Hello U-Boot maintainers,

I found an integer-overflow bounds-check issue in U-Boot's WGET receive path.

No CVE is assigned at report time.

Summary
-------

`net/wget.c` checks whether a received HTTP response block fits the configured 
download buffer by evaluating:

    wget_info->buffer_size < offset + len

The addition is performed before the bounds check. Since `offset` and `len` are 
unsigned inputs to `store_block()`, a large offset can wrap `offset + len` to a 
small value and bypass the intended `buffer_size` guard before `memcpy()` 
writes response bytes to `image_load_addr + offset`.

Affected version
----------------

Observed in current master:

    38dbe637c9dfcadbd1bc201bfbb27f96b2ad525a

Affected file:

    net/wget.c

Details
-------

The vulnerable bounds check is in `store_block()`:

    net/wget.c:48-55
        static inline int store_block(uchar *src, unsigned int offset,
                                      unsigned int len)
        {
                ulong store_addr = image_load_addr + offset;
                ...
                if (wget_info->buffer_size &&
                    wget_info->buffer_size < offset + len)
                        return -1;

If `offset + len` wraps, the comparison can allow a write whose starting offset 
is already outside the intended buffer.

The data is then copied to the computed load address:

    net/wget.c:67-69
        ptr = map_sysmem(store_addr, len);
        memcpy(ptr, src, len);
        unmap_sysmem(ptr);

The offset passed into `store_block()` is derived in the TCP receive callback:

    net/wget.c:234-240
        static int tcp_stream_rx(struct tcp_stream *tcp, u32 rx_offs,
                                 void *buf, int len)
        {
                if ((max_rx_pos == (u32)(-1)) ||
                    (max_rx_pos < rx_offs + len - 1))
                        max_rx_pos = rx_offs + len - 1;

                if (store_block(buf, rx_offs - http_hdr_size, len) < 0)
                        return -1;

The same callback also computes `rx_offs + len - 1` before checking for 
overflow.

There is an additional adjacent issue while parsing headers:

    net/wget.c:136-140
        ptr = map_sysmem(image_load_addr, rx_bytes + 1);
        saved = ptr[rx_bytes];
        ptr[rx_bytes] = '\0';
        pos = strstr((char *)ptr, http_eom);
        ptr[rx_bytes] = saved;

This temporarily touches one byte past the received byte count in order to run 
C-string parsing. If `rx_bytes` is at the configured buffer boundary, this 
touches one byte beyond the caller's intended receive buffer.

Minimal host-side PoC
---------------------

The attached/referenced harness uses:

    buffer_size = 64
    offset      = 0xfffffff0
    len         = 0x40

In unsigned 32-bit arithmetic:

    offset + len == 48

The current check therefore sees:

    buffer_size < wrapped_sum
    64 < 48  // false

So the guard allows the write even though the starting offset is far outside 
the 64-byte buffer.

Observed harness output:

    BA2-T6-CAND-006: wrapped_sum=48 allows=1 terminator_one_past=1

PoC source to attach:

    poc-wget-bounds.c

Build and run:

    gcc -std=c11 -Wall -Wextra -O0 poc-wget-bounds.c -o poc-wget-bounds
    ./poc-wget-bounds

Observed output:

    buffer_size=64
    offset=0xfffffff0
    len=64
    wrapped_sum=48
    current_check_allows=1
    VULNERABLE: wrapped offset+len bypasses buffer_size check

Impact
------

This affects systems using U-Boot WGET/HTTP boot with a configured receive 
buffer size. A malicious HTTP server on the boot network controls response 
bytes reaching the WGET receive path. If the TCP stream layer presents a large 
receive offset, the current arithmetic can bypass the buffer-size check and 
copy attacker-controlled bytes outside the intended image buffer. The impact is 
pre-OS denial of service or memory corruption in the bootloader context.

Expected invariant
------------------

The WGET receive path should reject any `(offset, len)` range that does not fit 
inside `wget_info->buffer_size` without first computing `offset + len` in a 
wrapping expression. The usual form is to reject when:

    offset > buffer_size || len > buffer_size - offset

Similar non-wrapping checks should be used for `rx_offs + len - 1` and for the 
temporary HTTP header terminator.

I can prepare a separate patch or sandbox test if that would be useful, but I 
am sending this first as a vulnerability report so the security impact can be 
triaged independently of the fix shape.

Regards,
Zhenyu Liu
#include <stdint.h>
#include <stdio.h>

int main(void)
{
    const uint32_t buffer_size = 64u;
    const uint32_t offset = 0xfffffff0u;
    const uint32_t len = 0x40u;
    const uint32_t wrapped_sum = offset + len;
    const int current_check_allows = !(buffer_size < wrapped_sum);

    /*
     * This models net/wget.c:
     *
     *   if (wget_info->buffer_size &&
     *       wget_info->buffer_size < offset + len)
     *       return -1;
     *
     * offset + len wraps to 48, so a 64-byte buffer does not reject it.
     */
    printf("buffer_size=%u\n", buffer_size);
    printf("offset=0x%08x\n", offset);
    printf("len=%u\n", len);
    printf("wrapped_sum=%u\n", wrapped_sum);
    printf("current_check_allows=%d\n", current_check_allows);

    if (offset > buffer_size && current_check_allows) {
        puts("VULNERABLE: wrapped offset+len bypasses buffer_size check");
        return 0;
    }

    puts("not reproduced");
    return 1;
}
ÿþbuffer_size=64

offset=0xfffffff0

len=64

wrapped_sum=48

current_check_allows=1

VULNERABLE: wrapped offset+len bypasses 
buffer_size check

Reply via email to