net/udp: fix d_len corruption for 2nd+ SO_REUSEADDR listener

In udp_input()'s broadcast/multicast fan-out loop, each iteration
calls netdev_iob_replace(dev, iob) to swap in a freshly cloned iob
before handing the packet to the next matching connection. That
function unconditionally sets dev->d_len = iob->io_pktlen, which is
the full frame length (IP + UDP headers + payload), undoing the
'dev->d_len -= udpiplen' done once before the loop to strip the
headers off for udp_input_conn().

As a result, every connection after the first sees a d_len that is
udpiplen (IP+UDP header length, eg 28 bytes for IPv4) too large.
This value flows into udp_datahandler() as buflen (it reads
dev->d_len directly) and is stored as the queued packet's declared
length in the connection's read-ahead iob chain. Once more than one
such oversized entry has queued up in the same chain, the consumer
(udp_readahead() in udp_recvfrom.c) parses the following entry's
metadata starting at the wrong offset, so whatever byte happens to
land on src_addr_size is trusted as-is. That single byte (0-255) is
then used as the length in iob_copyout(srcaddr, iob, src_addr_size,
...), which fills a fixed-size stack buffer with no bounds check
outside a DEBUGASSERT - compiled out in release builds - so an
oversized value overflows that stack buffer.

Re-apply the same '-= udpiplen' header-stripping after each
netdev_iob_replace() call in the loop, matching what's already done
once before the loop for the first connection.

Fixes #14743

Signed-off-by: yi chen <94xhn1@gmail.com>
This commit is contained in:
yi chen 2026-07-10 04:49:08 +08:00
parent d75d713165
commit b3f3985084

View file

@ -336,6 +336,7 @@ static int udp_input(FAR struct net_driver_s *dev, unsigned int iplen)
}
netdev_iob_replace(dev, iob);
dev->d_len -= udpiplen;
udp = IPBUF(iplen);
conn = nextconn;
}