Repository navigation
net/packet: don't panic in Payload when dataofs is past length - #21232
Conversation
jentfoo
left a comment
There was a problem hiding this comment.
Thank you for the report and PR @Dev-next-gen
Now that #21123 has been merged, can you please change (or add to) your testing to validate through fuzz targets which includes both valid and invalid inputs as the seeds. Also include adding the new fuzz target to fuzz/oss-fuzz.sh.
The goal is for these protocol issues to be validated through fuzzing.
(FWIW, the only reason this was not included is that I intended to include fuzz targets that needed code changes in a separate PR like this, you just beat me to it)
Payload guards a truncated packet by comparing both length and dataofs against len(b), but the slice it returns is b[dataofs:length], so what actually has to hold is dataofs <= length. Those are independent: length comes from the IPv4 total length header field, while dataofs is derived from the sub-protocol header, and decode4 never checks that the declared total length covers the transport header. A 28-byte IPv4/UDP packet declaring a total length of 20 decodes to length=20, dataofs=28, len(b)=28, and Payload then evaluates b[28:20]. ICMPv4 and TCP reach the same state with 24- and 40-byte packets. I found this by fuzzing Decode and then calling the accessors on the result. I did not find a caller that can be driven into it from the network: wireguard-go truncates decrypted packets to the declared IP length, so on the inbound path dataofs > length implies dataofs > len(b) and the existing guard already catches it. Add a FuzzParsedPayload target that decodes and then calls Payload, seeded with valid IPv4/IPv6 packets and with invalid ones, including the three short total length packets above, and build it in fuzz/oss-fuzz.sh. Fixes tailscale#21231 Change-Id: Ie5d2100464b79750626b1bfefbe4020c4a42ca91 Signed-off-by: leoca <[email protected]>
cbd95ea to
48d54e2
Compare
|
Thanks @jentfoo. Now that #21123 is in, I rebased onto main and moved the testing into a fuzz target.
With the change reverted, I kept the target to |
|
Thank you @Dev-next-gen ! |
|
@jentfoo With pleasure, I use Tailscale every day! |
Fixes #21231
(*Parsed).Payloadsays it returns nothing rather than crashing on a truncated packet, but its guard compares bothlengthanddataofsagainstlen(b)while the slice it returns isb[dataofs:length]. What actually has to hold isdataofs <= length, and those two are independent:lengthis the IPv4 total length header field,dataofsis derived from the sub-protocol header, anddecode4never checks that the declared total length covers the transport header.A 28-byte IPv4/UDP packet declaring a total length of 20 decodes to
length=20 dataofs=28 len(b)=28, andPayloadthen evaluatesb[28:20]:ICMPv4 and TCP get there the same way, with 24- and 40-byte packets. The test I added covers all three; it panics on main and passes with the extra condition.
I found this by fuzzing
Decodeand then calling the accessors on the parsed result. Thenet/packetfuzz target in #21123 only callsDecode, which is why it did not surface.I went looking for a caller that can be driven into this from the network and could not prove one.
filterPacketInboundFromWireGuardreachesPayloadthroughisSelfDiscofor every inbound UDP packet, but wireguard-go truncates decrypted packets to the declared IP length first, so on that pathdataofs > lengthimpliesdataofs > len(b)and the existing guard already catches it. So I am sending this as a robustness fix for an exported parser whose documented contract is that it will not crash, not as a fix for a reachable crash. If you would rather have the invariant enforced indecode4(rejecting the packet asUnknownwhen the declared length does not cover the sub-protocol header) than repaired at the accessor, I am happy to redo it that way.While I was there I also noticed that
(*Parsed).Transportcan panic the same way, becausedecode4assignsq.subofsbefore validating it againstq.length, so the rejection path leavessubofs > len(b)withIPVersionstill set. I left that out of this change to keep it to one thing; tell me if you want it here or in its own issue.AI tools used