[nsec] compute NSEC.len() the same way that we would do in packDataNsec (#967)

The byte sequence, when Unpack()-ed and subsequential Pack()-ed created a
panic: runtime error: slice bounds out of range
github.com/miekg/dns.(*Msg).packBufferWithCompressionMap(0xc0000d4000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100, 0x14, 0x14e80b, 0xbf2d4654d501a3c8, ...)
/Users/chantra/go/src/github.com/miekg/dns/msg.go:868 +0x13a8

Confirmed that Unpacking/Repacking payload described in TestCrashNSEC
did not raise a slice bound out of range panic, added unittests which
failed prior to this change.

```
go test -run TestCrashNSEC
--- FAIL: TestCrashNSEC (0.00s)
    types_test.go:135: expected length of 19, got 12
FAIL
exit status 1
FAIL	github.com/miekg/dns	0.067s
```
This commit is contained in:
chantra 2019-05-19 00:40:22 -07:00 committed by Miek Gieben
parent 2f1ea90356
commit 37f455fa04
2 changed files with 46 additions and 4 deletions

View File

@ -71,3 +71,37 @@ func TestPackDataOpt(t *testing.T) {
})
}
}
// TestCrashNSEC tests generated using fuzz.go and with a message pack
// containing the following bytes:
// "0000\x00\x00000000\x00\x00/00000" +
// "0\x00\v\x00#\b00000\x00\x00\x00\x00\x00\x1a000" +
// "000\x00\x00\x00\x00\x1a000000\x00\x00\x00\x00\x1a0" +
// "00000\x00\v00\a0000000\x00"
// That byte sequence, when Unpack() and subsequential Pack() created a
// panic: runtime error: slice bounds out of range
// which was attributed to the fact that NSEC RR length computation was different (and smaller)
// then when within packDataNsec.
func TestCrashNSEC(t *testing.T) {
compression := make(map[string]struct{})
nsec := &NSEC{
Hdr: RR_Header{
Name:".",
Rrtype:0x2f,
Class:0x3030,
Ttl:0x30303030,
Rdlength:0xb,
},
NextDomain:".",
TypeBitMap:[]uint16{
0x2302, 0x2303, 0x230a, 0x230b,
0x2312, 0x2313, 0x231a, 0x231b,
0x2322, 0x2323,
},
}
expectedLength := 19
l := nsec.len(0, compression)
if l != expectedLength {
t.Fatalf("expected length of %d, got %d", expectedLength, l)
}
}

View File

@ -854,14 +854,22 @@ func (rr *NSEC) String() string {
func (rr *NSEC) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression)
l += domainNameLen(rr.NextDomain, off+l, compression, false)
lastwindow := uint32(2 ^ 32 + 1)
var lastwindow, lastlength uint16
for _, t := range rr.TypeBitMap {
window := t / 256
if uint32(window) != lastwindow {
l += 1 + 32
length := (t-window*256)/8 + 1
if window > lastwindow && lastlength != 0 { // New window, jump to the new offset
l += int(lastlength) + 2
lastlength = 0
}
lastwindow = uint32(window)
if window < lastwindow || length < lastlength {
// packDataNsec would return Error{err: "nsec bits out of order"} here, but
// when computing the length, we want do be liberal.
continue
}
lastwindow, lastlength = window, length
}
l += int(lastlength) + 2
return l
}