net: cdp: reject CDP TLVs with a length below the 4-byte header

cdp_receive() reads a 16-bit TLV length (tlen) from the packet and only
checks that it does not exceed the remaining buffer (tlen > len). It then
unconditionally does "tlen -= 4" to skip the TLV header. As tlen is a
u16, a crafted TLV with a length of 0..3 underflows tlen to ~65532-65535.

For a CDP_APPLIANCE_VLAN_TLV the underflowed length then drives the inner
"while (tlen > 0)" loop, which walks ~64KB past the receive buffer reading
*ss each step -> out-of-bounds read (crash / info-influence). A length of
0 additionally fails to advance pkt/len, hanging the parse loop.

Reject any TLV whose declared length is smaller than its own 4-byte
header. This is the same class of bug as the recent bootp/dhcpv6/sntp/nfs
fixes (unchecked length field), in a sibling LAN parser that was missed.

Verified with a standalone AddressSanitizer harness using the verbatim
cdp_receive()/cdp_compute_csum() routines: a 16-byte CDP frame with an
appliance-VLAN TLV of length 3 triggers a heap-buffer-overflow READ that
the check eliminates.

Fixes: f575ae1f7d ("net: Move CDP out of net.c")
Cc: stable@vger.kernel.org
Signed-off-by: Piyush Paliwal <piyushthepal@gmail.com>
Reviewed-by: Jerome Forissier <jerome.forissier@arm.com>
This commit is contained in:
Piyush Paliwal
2026-06-23 13:13:16 +02:00
committed by Jerome Forissier
parent 9f7906a58c
commit 91d5e0ee3e
+7 -1
View File
@@ -276,7 +276,13 @@ void cdp_receive(const uchar *pkt, unsigned len)
ss = (const ushort *)pkt;
type = ntohs(ss[0]);
tlen = ntohs(ss[1]);
if (tlen > len)
/*
* tlen includes the 4-byte TLV header, so it must be at
* least 4. Without this check a crafted tlen < 4 makes the
* "tlen -= 4" below underflow (tlen is a ushort), and a tlen
* of 0 also fails to advance pkt/len, hanging the loop.
*/
if (tlen < 4 || tlen > len)
goto pkt_short;
pkt += tlen;