Re: [PATCH v2 2/2] net: bootp: bound DHCP option parsing by the received packet length
Pranav R <[email protected]>
| Newsgroups | org.u-boot-project.lists.u-boot |
|---|---|
| Message-ID | <CANMPf=NLDUV=8o8e47kkYozRuVoJoROJU6hTC4GeO7c67uAfmA@mail.gmail.com> |
Hi Jerome, Good catch — you're right, the outer bound alone doesn't cover it. dhcp_process_options() trusted `popt < end` to mean the whole option (code + length + payload) was safe, but that only guarantees the code byte is readable. oplen = *(popt + 1) and the payload dereferences in the switch had no bound check of their own, so a packet truncated right after an option code byte, or with a declared length that overruns the buffer, still walked off the end even with the outer fix applied. Sent as v2 with a prerequisite patch (1/2) that fixes this: the pad option is now handled before touching a second byte, a length byte is required to exist before it's read, and the full declared option (header + payload) is required to fit before the switch runs on it — otherwise parsing stops instead of reading past the buffer. Your original outer-bound fix is 2/2, unchanged, rebased on top. Both commits are checkpatch --strict clean and build warning-free at W=1; a full sandbox build also passes. Thanks, Pranav On Thu, Aug 20, 2026 at 1:33 PM Pranav Rajendran <[email protected]> wrote: > dhcp_packet_process_options() derives the end of the option area from > BOOTP_HDR_SIZE, a compile-time constant, rather than from the length of > the packet that was actually received: > > uchar *popt = (uchar *)&bp->bp_vend[4]; > uchar *end = popt + BOOTP_HDR_SIZE; > > Since popt already starts near the end of the header, 'end' lands > sizeof(struct bootp_hdr) bytes beyond it, so a short reply leaves > dhcp_process_options() walking off the end of the received data and > into whatever the receive buffer held before - typically the remains of > earlier packets. > > That is not only a disclosure: the options found there are acted on > like any others, so stale bytes that happen to parse as an option can > influence the boot file name, the DNS server or the root path. > > The BOOTP path already gets this right and passes the real length to > bootp_process_vendor(), and both callers here have the length in scope > - they hand it to dhcp_message_type() on the lines above. Pass it in > and use it as the limit. The overloaded 'file' and 'sname' areas are > clamped the same way, as a truncated packet need not contain them > either. > > Fixes: 774c3e05ec0a ("net: parse DHCP options from overloaded file/sname > fields") > Signed-off-by: Pranav Rajendran <[email protected]> > --- > v2: No change, rebased on top of the new patch 1/2 which fixes the > inner option-length validation gap Jerome raised against v1. > > net/bootp.c | 27 +++++++++++++++++++-------- > 1 file changed, 19 insertions(+), 8 deletions(-) > > diff --git a/net/bootp.c b/net/bootp.c > index eafbe9e3bb4..06083092875 100644 > --- a/net/bootp.c > +++ b/net/bootp.c > @@ -977,32 +977,43 @@ static void dhcp_process_options(uchar *popt, uchar > *end) > } > } > > -static void dhcp_packet_process_options(struct bootp_hdr *bp) > +static void dhcp_packet_process_options(struct bootp_hdr *bp, unsigned > int len) > { > - uchar *popt = (uchar *)&bp->bp_vend[4]; > - uchar *end = popt + BOOTP_HDR_SIZE; > + uchar *pkt_end = (uchar *)bp + len; > + uchar *popt, *end; > + > + if (len < offsetof(struct bootp_hdr, bp_vend) + 4) > + return; > > if (net_read_u32((u32 *)&bp->bp_vend[0]) != > htonl(BOOTP_VENDOR_MAGIC)) > return; > > + popt = (uchar *)&bp->bp_vend[4]; > + > dhcp_option_overload = 0; > > /* > * The 'options' field MUST be interpreted first, 'file' next, > * 'sname' last. > */ > - dhcp_process_options(popt, end); > + dhcp_process_options(popt, pkt_end); > > if (dhcp_option_overload & OVERLOAD_FILE) { > popt = (uchar *)bp->bp_file; > end = popt + sizeof(bp->bp_file); > - dhcp_process_options(popt, end); > + if (end > pkt_end) > + end = pkt_end; > + if (popt < end) > + dhcp_process_options(popt, end); > } > > if (dhcp_option_overload & OVERLOAD_SNAME) { > popt = (uchar *)bp->bp_sname; > end = popt + sizeof(bp->bp_sname); > - dhcp_process_options(popt, end); > + if (end > pkt_end) > + end = pkt_end; > + if (popt < end) > + dhcp_process_options(popt, end); > } > } > > @@ -1133,7 +1144,7 @@ static void dhcp_handler(uchar *pkt, unsigned dest, > struct in_addr sip, > debug("got BOOTP response; transitioning > to BOUND\n"); > goto dhcp_got_bootp; > } > - dhcp_packet_process_options(bp); > + dhcp_packet_process_options(bp, len); > if (CONFIG_IS_ENABLED(EFI_LOADER) && > IS_ENABLED(CONFIG_NETDEVICES)) > efi_net_set_dhcp_ack(pkt, len); > @@ -1160,7 +1171,7 @@ static void dhcp_handler(uchar *pkt, unsigned dest, > struct in_addr sip, > > if (dhcp_message_type((u8 *)bp->bp_vend, (u8 *)pkt + len) > == DHCP_ACK) { > dhcp_got_bootp: > - dhcp_packet_process_options(bp); > + dhcp_packet_process_options(bp, len); > /* Store net params from reply */ > store_net_params(bp); > dhcp_state = BOUND; > -- > 2.50.1 (Apple Git-155) > >