Introduction
Recently, I audited the golang.org/x/net/ipv4 package and found two bounds-checking bugs in how the library parses incoming IPv4 packets. I reported them to the Go Security Team, the bugs were confirmed, and fixes were merged upstream.
Here is a breakdown of exactly what the bugs were, what their impact is, and how the Go team fixed them.
Bug 1: The slicePacket Out-of-Bounds Panic
The slicePacket function is responsible for taking a raw byte buffer and slicing it into the IP header and the payload.
go func slicePacket(b []byte) (h, p []byte, err error) { if len(b) < HeaderLen { // HeaderLen is 20 bytes return nil, nil, errHeaderTooShort } hdrlen := int(b[0]&0x0f) << 2 // IHL (Internet Header Length) can be up to 60 return b[:hdrlen], b[hdrlen:], nil }
The logic checks if the incoming buffer is at least 20 bytes long. However, an IPv4 header can be up to 60 bytes long if it contains options (IHL = 15). The code blindly slices the buffer up to hdrlen without verifying that the buffer is actually that large.
If an attacker sends a packet with a 60-byte header and the application calls ReadFrom with a buffer smaller than 60 bytes, the slice expression b[:hdrlen] causes a runtime panic: slice bounds out of range [:60] with capacity 20.
Bug 2: The payloadHandler.ReadFrom Off-by-20
The second bug was hiding in payloadHandler.ReadFrom. When parsing packets with IP options, the code attempted to shift the payload data over the header options.
go if hdrlen > len(h) { d := hdrlen - len(h) // d is the length of the options copy(b, b[d:]) // Panic if d > len(b) m.N -= d // Logic error } else { m.N -= hdrlen }
This introduced two distinct issues: 1. A Panic: If the size of the options (d) was larger than the caller's buffer (len(b)), the copy function would trigger a slice out-of-bounds panic. 2. Information Disclosure: The calculation m.N -= d was mathematically wrong. It should have been subtracting the full header length. As a result, the returned length n was exactly 20 bytes larger than the actual payload. A caller doing b[:n] would inadvertently read 20 bytes of stale memory left over from previous packets.
Impact
These bugs primarily affect applications that use raw IP sockets (such as custom ICMP tools, routing daemons, or network diagnostics) running on Linux, macOS, and BSD variants. Windows is not affected as it uses a different code path.
1. Denial of Service (Remote) For both bugs, a single crafted packet from a remote, unauthenticated attacker is enough to trigger a fatal runtime panic. If an application uses a 20-byte buffer to peek at the IPv4 header, an attacker can send a packet with IHL=15 (a 60-byte header). The resulting panic will crash the process unless the application explicitly recovers from it.
2. Information Disclosure (Logic Error) The off-by-20 bug in payloadHandler.ReadFrom exposes stale buffer memory. If an application processes b[:n] as the payload and forwards it, logs it, or parses it as protocol data, it will inadvertently leak 20 bytes of uninitialized memory. This memory could contain data from previous network packets or sensitive internal state.
The Fixes
The Go Security Team reviewed the PoCs and merged the fixes upstream in Gerrit CL 852745.
For slicePacket, the fix adds a check to ensure the buffer is large enough to hold the actual header length, not just the base 20 bytes:
go hdrlen := int(b[0]&0x0f) << 2 if len(b) < hdrlen { return nil, nil, errHeaderTooShort } return b[:hdrlen], b[hdrlen:], nil
For payloadHandler.ReadFrom, the fix adds a guard clause to prevent the panic and corrects the length subtraction:
go if hdrlen > len(h) { d := hdrlen - len(h) if d > len(b) { return 0, nil, nil, errHeaderTooShort // Guard clause } copy(b, b[d:]) m.N -= hdrlen // Corrected calculation } else { m.N -= hdrlen }
Proof of Concept
I wrote comprehensive Go tests to prove both the panic thresholds and the incorrect length returns. The tests require root on Linux because they use raw sockets.
You can view the full PoC code and test logs here: - PoC: slicePacket Panic - PoC: payloadHandler OOB - Gerrit CL 852745
Thanks to the Go Security Team for the quick triage and for hardening golang.org/x/net/ipv4.