Files
tailscale/net/packet/fuzz_test.go
T
Mike Jensen 408504af89 net/packet: fix Transport panic from a bad IPv4 IHL (#21331)
`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>
2026-09-17 10:00:01 -06:00

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)
})
}