net/udp: fix d_len corruption for 2nd+ SO_REUSEADDR listener#19390
Open
94xhn wants to merge 1 commit into
Open
net/udp: fix d_len corruption for 2nd+ SO_REUSEADDR listener#1939094xhn wants to merge 1 commit into
94xhn wants to merge 1 commit into
Conversation
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 apache#14743 Signed-off-by: yi chen <94xhn1@gmail.com>
xiaoxiang781216
approved these changes
Jul 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #14743. In
udp_input()'s broadcast/multicast fan-out loop (net/udp/udp_input.c), each iteration callsnetdev_iob_replace(dev, iob)to swap in a freshly cloned iob before handing the packet to the next matchingSO_REUSEADDRconnection.netdev_iob_replace()unconditionally setsdev->d_len = iob->io_pktlen(net/netdev/netdev_iob.c:149), which is the full frame length (IP + UDP headers + payload) — undoing thedev->d_len -= udpiplendone once before the loop to strip the headers off forudp_input_conn().As a result, every connection after the first sees a
d_lenthat isudpiplen(IP+UDP header length, e.g. 28 bytes for IPv4) too large. This value flows straight intoudp_datahandler()asbuflen(net/udp/udp_callback.c:256,int buflen = dev->d_len;) and gets 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()innet/udp/udp_recvfrom.c) parses the following entry's metadata starting at the wrong offset, so whatever byte happens to land onsrc_addr_sizegets trusted as-is. That single byte (0-255) is then used as the length iniob_copyout(srcaddr, iob, src_addr_size, offset)(udp_recvfrom.c:218), which fills a fixed-size stack buffer (uint8_t srcaddr[sizeof(struct sockaddr_in6)]) with no bounds check outside aDEBUGASSERT— compiled out in release builds. An oversized value overflows that stack buffer.Impact
Affects any target with
CONFIG_NET_SOCKOPTS+CONFIG_NET_BROADCASTwhere two or moreSO_REUSEADDRUDP sockets are bound to the same port and receive broadcast/multicast traffic — a normal, non-adversarial configuration (the issue reporter hit it without any malicious input). Security-relevant: stack buffer overflow in the network receive path.Fix
Re-apply the same
-= udpiplenheader-stripping after eachnetdev_iob_replace()call in the loop, matching what's already done once before the loop for the first connection — a 1-line change.Testing
I don't have a NuttX
simbuild environment set up in my sandbox (no WSL/Linux host available to me), so I couldn't producesim:nsh/sim:netnshexecution logs. What I did instead:net/udp/udp_input.c→net/netdev/netdev_iob.c→net/udp/udp_callback.c→net/udp/udp_recvfrom.cagainst currentmaster, confirming every intermediate read/write ofd_len/buflen/datalen/src_addr_sizecited above by line number.d_lentracking across the fan-out loop,netdev_iob_replace()copied verbatim) into a standalone host-C reproduction, compiled and run with plaingcc(no NuttX build needed since this part has no OS dependency):d_len= 1014 (correct), conn2 and conn3 both seed_len= 1042 — exactly 28 (udpiplen) too large.-DFIXED, i.e. this patch's one added line): all three connections see the correctd_len= 1014.DEBUGASSERT()toudp_readahead()that fires, and observed "the value of dev->d_len matched the size of the application data before udp_input_conn() was called the 1st time... The second time (for the second [connection])" — the same first-vs-second-connectiond_lenmismatch my trace and repro reproduce.Happy to run the real
sim:netnshreproduction and post logs if a maintainer can point me to (or confirm) the right multi-SO_REUSEADDR-socket test setup, or if this needs stronger evidence before merge.