mirror of
https://github.com/tailscale/tailscale.git
synced 2026-10-09 11:52:00 -04:00
`decode4` assigned `q.subofs` before validating it against the declared IP total length. A packet with an IHL past the end of the buffer was rejected but left `subofs` dangling there, so a later Transport call would panic. This change only store `subofs` once validated. As defense in depth an additional bounds check is added in Transport. Fuzzing was expanded and improved to get better coverage in `packet.go`. Credit to @Dev-next-gen for finding and reporting. Fixes tailscale/corp#48322 Updates tailscale/corp#46608 Change-Id: I198d06b921add9b188daad79d4ca473aed1e3b66 Signed-off-by: Mike Jensen <mikej@tailscale.com>
389 lines
17 KiB
Go
389 lines
17 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package packet
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"tailscale.com/types/ipproto"
|
|
)
|
|
|
|
func FuzzParsedDecode(f *testing.F) {
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x14})
|
|
f.Add([]byte{0x60, 0x00, 0x12, 0x34, 0x56, 0x78})
|
|
f.Add([]byte{0x44, 0x00, 0x01, 0x02})
|
|
// A complete minimal IPv4 UDP packet (20-byte header + 8-byte UDP header)
|
|
f.Add([]byte{
|
|
0x45, 0x00, 0x00, 0x1c, 0x12, 0x34, 0x40, 0x00, 0x40, byte(ipproto.UDP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2,
|
|
0x30, 0x39, 0x04, 0xd2, 0x00, 0x08, 0x00, 0x00,
|
|
})
|
|
// A complete minimal IPv6 UDP packet (40-byte header + 8-byte UDP header)
|
|
f.Add(append(append([]byte{
|
|
0x60, 0x00, 0x00, 0x00, 0x00, 0x08, byte(ipproto.UDP), 0x40,
|
|
}, make([]byte, 32)...), 0x30, 0x39, 0x04, 0xd2, 0x00, 0x08, 0x00, 0x00))
|
|
// A complete IPv4 ICMP packet
|
|
f.Add([]byte{
|
|
0x45, 0x00, 0x00, 0x1c, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.ICMPv4),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2,
|
|
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
})
|
|
// A complete IPv6 ICMPv6 packet
|
|
f.Add(append(append([]byte{
|
|
0x60, 0x00, 0x00, 0x00, 0x00, 0x04, byte(ipproto.ICMPv6), 0x40,
|
|
}, make([]byte, 32)...), 0x80, 0x00, 0x00, 0x00))
|
|
// A TSMP packet (Tailscale's inter-node message protocol)
|
|
f.Add([]byte{
|
|
0x45, 0x00, 0x00, 0x1c, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.TSMP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2,
|
|
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x00,
|
|
})
|
|
// A later (non-first) IPv4 fragment with a high offset
|
|
f.Add([]byte{
|
|
0x45, 0x00, 0x00, 0x1c, 0x12, 0x34, 0x20, 0x10, 0x40, byte(ipproto.TCP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
})
|
|
// A bad IP version nibble, hitting the default (unknown) case
|
|
f.Add([]byte{0x50, 0x00, 0x00, 0x14, 0x12, 0x34, 0x00, 0x00, 0x40, 0x11,
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2})
|
|
// IPv4 declared length (0x0100) larger than the buffer
|
|
f.Add([]byte{0x45, 0x00, 0x01, 0x00, 0x12, 0x34, 0x00, 0x00, 0x40, 0x11,
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2})
|
|
// IHL 15: sub-proto offset (60) beyond the packet end
|
|
f.Add([]byte{0x4f, 0x00, 0x00, 0x14, 0x12, 0x34, 0x00, 0x00, 0x40, 0x11,
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2})
|
|
// IHL 6: 4 option bytes, UDP sub-header at offset 24
|
|
f.Add([]byte{0x46, 0x00, 0x00, 0x20, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.UDP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2, 0x01, 0x02, 0x03, 0x04,
|
|
0x30, 0x39, 0x04, 0xd2, 0x00, 0x08, 0x00, 0x00})
|
|
// IHL 3: sub-header read from inside the IP addresses (IHL < 5 is not validated)
|
|
f.Add([]byte{0x43, 0x00, 0x00, 0x1c, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.UDP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
|
// Truncated sub-headers: TCP, UDP, ICMPv4, IGMP, SCTP
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x18, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.TCP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2, 0x30, 0x39, 0x04, 0xd2})
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x16, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.UDP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2, 0x30, 0x39})
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x16, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.ICMPv4),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2, 0x08, 0x00})
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x18, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.IGMP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2, 0x11, 0x64, 0x00, 0x00})
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x1c, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.SCTP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2, 0x30, 0x39, 0x04, 0xd2, 0x00, 0x00, 0x00, 0x01})
|
|
// A fragmented TSMP, strictly disallowed
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x1c, 0x12, 0x34, 0x20, 0x00, 0x40, byte(ipproto.TSMP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2,
|
|
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x00})
|
|
// A TSMP sub-header shorter than the minimum 7 bytes
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x1a, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.TSMP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60})
|
|
// A later fragment with an offset below minFragBlks (8 < 10), rejected
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x14, 0x12, 0x34, 0x20, 0x08, 0x40, byte(ipproto.TCP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2})
|
|
// A protocol byte of 0xff, the internal Fragment sentinel
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x14, 0x12, 0x34, 0x00, 0x00, 0x40, 0xff,
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2})
|
|
// IPv6 declared payload length (16) larger than the buffer provides
|
|
f.Add(append(append([]byte{0x60, 0x00, 0x00, 0x00, 0x00, 0x10, byte(ipproto.UDP), 0x40},
|
|
make([]byte, 32)...), 0x30))
|
|
// A truncated IPv6 fragment header (payload length 4, frag header cut off)
|
|
f.Add(append(append([]byte{0x60, 0x00, 0x00, 0x00, 0x00, 0x04, byte(ip6FragHeader), 0x40},
|
|
make([]byte, 32)...), 0x11, 0x00, 0x00, 0x00))
|
|
// IPv6 with truncated sub-headers: ICMPv6, UDP, TCP
|
|
f.Add(append(append([]byte{0x60, 0x00, 0x00, 0x00, 0x00, 0x02, byte(ipproto.ICMPv6), 0x40},
|
|
make([]byte, 32)...), 0x80, 0x00))
|
|
f.Add(append(append([]byte{0x60, 0x00, 0x00, 0x00, 0x00, 0x04, byte(ipproto.UDP), 0x40},
|
|
make([]byte, 32)...), 0x30, 0x39, 0x04, 0xd2))
|
|
f.Add(append(append([]byte{0x60, 0x00, 0x00, 0x00, 0x00, 0x08, byte(ipproto.TCP), 0x40},
|
|
make([]byte, 32)...), 0x30, 0x39, 0x04, 0xd2, 0x00, 0x00, 0x00, 0x01))
|
|
// IPv6 with the internal Fragment sentinel as the next-header byte
|
|
f.Add(append(append([]byte{0x60, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0x40},
|
|
make([]byte, 32)...), 0x00, 0x00, 0x00, 0x00))
|
|
// A complete minimal IPv4 UDP packet with a 4-byte payload
|
|
f.Add([]byte{
|
|
0x45, 0x00, 0x00, 0x20, 0x12, 0x34, 0x40, 0x00, 0x40, byte(ipproto.UDP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2,
|
|
0x30, 0x39, 0x04, 0xd2, 0x00, 0x0c, 0x00, 0x00,
|
|
0xde, 0xad, 0xbe, 0xef,
|
|
})
|
|
// A complete minimal IPv4 TCP packet (20-byte header + 20-byte TCP header)
|
|
f.Add([]byte{
|
|
0x45, 0x00, 0x00, 0x28, 0x12, 0x34, 0x40, 0x00, 0x40, byte(ipproto.TCP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2,
|
|
0x30, 0x39, 0x04, 0xd2, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x50, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
})
|
|
// A complete minimal IPv6 UDP packet with a 4-byte payload
|
|
f.Add(append(append([]byte{
|
|
0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, byte(ipproto.UDP), 0x40,
|
|
}, make([]byte, 32)...), 0x30, 0x39, 0x04, 0xd2, 0x00, 0x0c, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef))
|
|
// IPv4 declared length (0x0100) larger than the buffer, with sub-header
|
|
f.Add([]byte{0x45, 0x00, 0x01, 0x00, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.UDP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2,
|
|
0x30, 0x39, 0x04, 0xd2, 0x00, 0x08, 0x00, 0x00})
|
|
// IPv4 declared total length of 20 (the IP header alone) followed by a
|
|
// full sub-protocol header, so dataofs lands past length while both stay
|
|
// inside the buffer: ICMPv4, UDP, TCP
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x40, byte(ipproto.ICMPv4),
|
|
0x00, 0x00, 1, 2, 3, 4, 5, 6, 7, 8,
|
|
0x08, 0x00, 0x00, 0x00})
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x40, byte(ipproto.UDP),
|
|
0x00, 0x00, 1, 2, 3, 4, 5, 6, 7, 8,
|
|
0x30, 0x39, 0x04, 0xd2, 0x00, 0x08, 0x00, 0x00})
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x40, byte(ipproto.TCP),
|
|
0x00, 0x00, 1, 2, 3, 4, 5, 6, 7, 8,
|
|
0x30, 0x39, 0x04, 0xd2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
|
// A TCP data offset (15 words) pointing past the end of the packet
|
|
f.Add([]byte{
|
|
0x45, 0x00, 0x00, 0x28, 0x12, 0x34, 0x40, 0x00, 0x40, byte(ipproto.TCP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2,
|
|
0x30, 0x39, 0x04, 0xd2, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0xf0, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
})
|
|
// subofs = 28 past the end of a 20-byte packet
|
|
f.Add([]byte{0x47, 0x00, 0x00, 0x14, 0x12, 0x34, 0x00, 0x00, 0x40, 0x11,
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2})
|
|
|
|
f.Fuzz(func(t *testing.T, b []byte) {
|
|
var q Parsed
|
|
q.Decode(b)
|
|
if q.IPVersion == 4 {
|
|
_ = q.IP4Header()
|
|
if q.IPProto == ipproto.ICMPv4 {
|
|
_ = q.ICMP4Header()
|
|
} else if q.IPProto == ipproto.UDP {
|
|
_ = q.UDP4Header()
|
|
}
|
|
} else if q.IPVersion == 6 {
|
|
_ = q.IP6Header()
|
|
if q.IPProto == ipproto.ICMPv6 {
|
|
_ = q.ICMP6Header()
|
|
}
|
|
}
|
|
_ = q.Buffer()
|
|
_ = q.Transport()
|
|
_ = q.Payload()
|
|
_ = q.String()
|
|
_ = q.IsTCPSyn()
|
|
_ = q.IsError()
|
|
_ = q.IsEchoRequest()
|
|
_ = q.IsEchoResponse()
|
|
_ = q.EchoIDSeq()
|
|
})
|
|
}
|
|
|
|
func FuzzDecode4(f *testing.F) {
|
|
f.Add(make([]byte, ip4HeaderLength))
|
|
f.Add([]byte{0x45, 0x00, 0x04, 0x1e, 0x12, 0x34, 0x40, 0x00, 0x80, 0x11})
|
|
// A complete minimal IPv4 TCP packet (20-byte header + 20-byte TCP header)
|
|
f.Add([]byte{
|
|
0x45, 0x00, 0x00, 0x28, 0x12, 0x34, 0x40, 0x00, 0x40, byte(ipproto.TCP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2,
|
|
0x30, 0x39, 0x04, 0xd2, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x50, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
})
|
|
// A complete minimal IPv4 UDP packet
|
|
f.Add([]byte{
|
|
0x45, 0x00, 0x00, 0x1c, 0x12, 0x34, 0x40, 0x00, 0x40, byte(ipproto.UDP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2,
|
|
0x30, 0x39, 0x04, 0xd2, 0x00, 0x08, 0x00, 0x00,
|
|
})
|
|
// A complete IPv4 ICMP packet
|
|
f.Add([]byte{
|
|
0x45, 0x00, 0x00, 0x1c, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.ICMPv4),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2,
|
|
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
})
|
|
// A complete IPv4 IGMP packet (8-byte IGMP header)
|
|
f.Add([]byte{
|
|
0x45, 0x00, 0x00, 0x1c, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.IGMP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2,
|
|
0x11, 0x64, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x01,
|
|
})
|
|
// A complete IPv4 SCTP packet (12-byte SCTP header)
|
|
f.Add([]byte{
|
|
0x45, 0x00, 0x00, 0x20, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.SCTP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2,
|
|
0x30, 0x39, 0x04, 0xd2, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
|
})
|
|
// A TSMP packet
|
|
f.Add([]byte{
|
|
0x45, 0x00, 0x00, 0x2b, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.TSMP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2,
|
|
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x00,
|
|
})
|
|
// IHL 15: sub-proto offset (60) beyond the packet end
|
|
f.Add([]byte{0x4f, 0x00, 0x00, 0x14, 0x12, 0x34, 0x00, 0x00, 0x40, 0x11,
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2})
|
|
// IHL 6: 4 option bytes, UDP sub-header at offset 24
|
|
f.Add([]byte{0x46, 0x00, 0x00, 0x20, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.UDP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2, 0x01, 0x02, 0x03, 0x04,
|
|
0x30, 0x39, 0x04, 0xd2, 0x00, 0x08, 0x00, 0x00})
|
|
// Truncated sub-headers: TCP, UDP, ICMPv4, IGMP, SCTP
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x18, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.TCP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2, 0x30, 0x39, 0x04, 0xd2})
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x16, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.UDP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2, 0x30, 0x39})
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x16, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.ICMPv4),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2, 0x08, 0x00})
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x18, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.IGMP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2, 0x11, 0x64, 0x00, 0x00})
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x1c, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.SCTP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2, 0x30, 0x39, 0x04, 0xd2, 0x00, 0x00, 0x00, 0x01})
|
|
// A fragmented TSMP, strictly disallowed
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x1c, 0x12, 0x34, 0x20, 0x00, 0x40, byte(ipproto.TSMP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2,
|
|
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x00})
|
|
// A TSMP sub-header shorter than the minimum 7 bytes
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x1a, 0x12, 0x34, 0x00, 0x00, 0x40, byte(ipproto.TSMP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60})
|
|
// A later fragment with an offset below minFragBlks (8 < 10), rejected
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x14, 0x12, 0x34, 0x20, 0x08, 0x40, byte(ipproto.TCP),
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2})
|
|
// A protocol byte of 0xff, the internal Fragment sentinel
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x14, 0x12, 0x34, 0x00, 0x00, 0x40, 0xff,
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2})
|
|
|
|
f.Fuzz(func(t *testing.T, b []byte) {
|
|
var q Parsed
|
|
q.b = b
|
|
q.decode4(b)
|
|
_ = q.Transport()
|
|
_ = q.Payload()
|
|
_ = q.IsError()
|
|
_ = q.IsEchoRequest()
|
|
_ = q.IsEchoResponse()
|
|
_ = q.EchoIDSeq()
|
|
})
|
|
}
|
|
|
|
func FuzzDecode6(f *testing.F) {
|
|
f.Add(make([]byte, ip6HeaderLength))
|
|
// Base header Next Header = Fragment (44), with a fragment header claiming
|
|
// a first fragment (offset 0) but no sub-header bytes behind it
|
|
f.Add(append(append([]byte{
|
|
0x60, 0x00, 0x00, 0x00, 0x00, 0x08, byte(ip6FragHeader), 0x40,
|
|
}, make([]byte, 32)...), byte(ipproto.UDP), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))
|
|
// A fragment header cut off mid-way (4 of 8 bytes)
|
|
f.Add(append(append([]byte{
|
|
0x60, 0x00, 0x00, 0x00, 0x00, 0x04, byte(ip6FragHeader), 0x40,
|
|
}, make([]byte, 32)...), byte(ipproto.UDP), 0x00, 0x00, 0x00))
|
|
// A complete minimal IPv6 UDP packet (40-byte header + 8-byte UDP header)
|
|
f.Add(append(append([]byte{
|
|
0x60, 0x00, 0x00, 0x00, 0x00, 0x08, byte(ipproto.UDP), 0x40,
|
|
}, make([]byte, 32)...), 0x30, 0x39, 0x04, 0xd2, 0x00, 0x08, 0x00, 0x00))
|
|
// A first fragment: base header Next Header 44, then a fragment header
|
|
// with offset 0 and the real Next Header (UDP), then a UDP header
|
|
f.Add(append(append([]byte{
|
|
0x60, 0x00, 0x00, 0x00, 0x00, 0x10, byte(ip6FragHeader), 0x40,
|
|
}, make([]byte, 32)...),
|
|
byte(ipproto.UDP), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
0x30, 0x39, 0x04, 0xd2, 0x00, 0x08, 0x00, 0x00))
|
|
// A first fragment whose UDP sub-header is truncated past the fragment header
|
|
f.Add(append(append([]byte{
|
|
0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, byte(ip6FragHeader), 0x40,
|
|
}, make([]byte, 32)...),
|
|
byte(ipproto.UDP), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
0x30, 0x39, 0x04, 0xd2))
|
|
// A later fragment with a block offset below minFragBlks (1 < 10), rejected
|
|
f.Add(append(append([]byte{
|
|
0x60, 0x00, 0x00, 0x00, 0x00, 0x08, byte(ip6FragHeader), 0x40,
|
|
}, make([]byte, 32)...),
|
|
byte(ipproto.UDP), 0x00, 0x00, 0x08, 0x03, 0x04, 0x05, 0x06))
|
|
// A later fragment with a block offset of 32, passed through as ipproto.Fragment
|
|
f.Add(append(append([]byte{
|
|
0x60, 0x00, 0x00, 0x00, 0x00, 0x0a, byte(ip6FragHeader), 0x40,
|
|
}, make([]byte, 32)...),
|
|
byte(ipproto.UDP), 0x00, 0x01, 0x00, 0x03, 0x04, 0x05, 0x06, 0xaa, 0xbb))
|
|
// A complete minimal IPv6 TCP packet (40-byte header + 20-byte TCP header)
|
|
f.Add(append(append([]byte{
|
|
0x60, 0x00, 0x00, 0x00, 0x00, 0x14, byte(ipproto.TCP), 0x40,
|
|
}, make([]byte, 32)...),
|
|
0x30, 0x39, 0x04, 0xd2, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x50, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00))
|
|
// A complete IPv6 ICMPv6 packet
|
|
f.Add(append(append([]byte{
|
|
0x60, 0x00, 0x00, 0x00, 0x00, 0x04, byte(ipproto.ICMPv6), 0x40,
|
|
}, make([]byte, 32)...), 0x80, 0x00, 0x00, 0x00))
|
|
// A complete IPv6 SCTP packet (12-byte SCTP header)
|
|
f.Add(append(append([]byte{
|
|
0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, byte(ipproto.SCTP), 0x40,
|
|
}, make([]byte, 32)...),
|
|
0x30, 0x39, 0x04, 0xd2, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00))
|
|
// A TSMP packet
|
|
f.Add(append(append([]byte{
|
|
0x60, 0x00, 0x00, 0x00, 0x00, 0x07, byte(ipproto.TSMP), 0x40,
|
|
}, make([]byte, 32)...), 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70))
|
|
|
|
f.Fuzz(func(t *testing.T, b []byte) {
|
|
var q Parsed
|
|
q.b = b
|
|
q.decode6(b)
|
|
_ = q.Transport()
|
|
_ = q.Payload()
|
|
_ = q.IsError()
|
|
_ = q.IsEchoRequest()
|
|
_ = q.IsEchoResponse()
|
|
_ = q.EchoIDSeq()
|
|
})
|
|
}
|
|
|
|
func FuzzIP4Checksum(f *testing.F) {
|
|
f.Add([]byte{0x45, 0x00, 0x04, 0x1e})
|
|
// A full 20-byte IPv4 header
|
|
f.Add([]byte{0x45, 0x00, 0x00, 0x14, 0x12, 0x34, 0x40, 0x00, 0x40, 0x11,
|
|
0x00, 0x00, 10, 0, 0, 1, 10, 0, 0, 2})
|
|
// An odd-length input to exercise the trailing-byte path
|
|
f.Add([]byte{0x45, 0x00, 0x04, 0x1e, 0xab})
|
|
// An empty header, skipping the loop entirely
|
|
f.Add([]byte{})
|
|
|
|
f.Fuzz(func(t *testing.T, b []byte) {
|
|
_ = ip4Checksum(b)
|
|
})
|
|
}
|
|
|
|
func FuzzChecksumBytes(f *testing.F) {
|
|
f.Add([]byte{0x12, 0x34, 0x56}, uint16(0))
|
|
f.Add([]byte{}, uint16(1))
|
|
// All-ones inputs to exercise the checksum fold/carry path
|
|
f.Add([]byte{0xff, 0xff, 0xff, 0xff}, uint16(0xffff))
|
|
f.Add([]byte{0x80}, uint16(0x8000))
|
|
|
|
f.Fuzz(func(t *testing.T, b []byte, initial uint16) {
|
|
_ = checksumBytes(b, initial)
|
|
})
|
|
}
|
|
|
|
func FuzzICMP6Checksum(f *testing.F) {
|
|
var src, dst [16]byte
|
|
f.Add([]byte{0x80, 0x00, 0x12, 0x34}, []byte("ping"))
|
|
// A real 8-byte ICMPv6 echo header with a zeroed checksum field
|
|
f.Add([]byte{0x80, 0x00, 0x00, 0x00, 0x12, 0x34, 0x00, 0x01}, []byte("payload"))
|
|
// Partially-copied headers and empty payload/header
|
|
f.Add([]byte{0x80}, []byte("payload"))
|
|
f.Add([]byte{}, []byte("payload"))
|
|
f.Add([]byte{0x80, 0x00, 0x12, 0x34}, []byte{})
|
|
// Variable-length header and payload
|
|
f.Fuzz(func(t *testing.T, header, payload []byte) {
|
|
_ = icmp6Checksum(header, src, dst, payload)
|
|
})
|
|
}
|
|
|
|
func FuzzGeneveDecode(f *testing.F) {
|
|
f.Add([]byte{0x00, 0x40, 0x65, 0x58, 0x00, 0x01, 0x02, 0x03})
|
|
f.Add(make([]byte, GeneveFixedHeaderLength))
|
|
// A full valid Geneve header: v0, options-length 2 (8 option bytes)
|
|
f.Add(append([]byte{0x02, 0x40, 0x65, 0x58, 0x12, 0x34, 0x56, 0x00},
|
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08))
|
|
// One byte short of the fixed header
|
|
f.Add([]byte{0x00, 0x40, 0x65, 0x58, 0x00, 0x01, 0x02})
|
|
// Control bit set, and a non-zero version nibble
|
|
f.Add([]byte{0x00, 0xc0, 0x65, 0x58, 0x12, 0x34, 0x56, 0x00})
|
|
f.Add([]byte{0xc0, 0x40, 0x65, 0x58, 0x12, 0x34, 0x56, 0x00})
|
|
|
|
f.Fuzz(func(t *testing.T, b []byte) {
|
|
var h GeneveHeader
|
|
_ = h.Decode(b)
|
|
})
|
|
}
|