* [PATCH v2 1/2] net: fix out-of-bounds write in IP fragment reassembly
2026-07-28 6:50 [PATCH v2 0/2] net: fix out-of-bounds write in IP fragment reassembly Shahriyar Jalayeri
@ 2026-07-28 6:50 ` Shahriyar Jalayeri
2026-07-30 13:28 ` Jerome Forissier
2026-07-28 6:50 ` [PATCH v2 2/2] test: net: add regression test for IP reassembly overflow Shahriyar Jalayeri
1 sibling, 1 reply; 4+ messages in thread
From: Shahriyar Jalayeri @ 2026-07-28 6:50 UTC (permalink / raw)
To: u-boot
Cc: Jerome Forissier, Tom Rini, Wayen Yan, Simon Glass, Argus,
Shahriyar Jalayeri
__net_defragment() reassembles IP fragments into the static buffer
pkt_buff[CONFIG_NET_MAXDEFRAG]. The bounds check
if (start + len > IP_MAXUDP)
return NULL;
only covers the fragment data copy. The split-hole and move-hole
branches additionally write an 8-byte struct hole via "*newh = *h" at
newh = thisfrag + len / 8, which can land up to sizeof(struct hole)
bytes past the end of pkt_buff. A single fragment with a non-zero
fragment offset and the More-Fragments flag set reaches this path, so
a crafted fragment received during netboot overflows the buffer.
Reject any fragment whose trailing hole descriptor would fall outside
pkt_buff.
Signed-off-by: Shahriyar Jalayeri <shahriyar@byteray.co.uk>
Acked-by: Jerome Forissier <jerome.forissier@arm.com>
---
net/net.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/net.c b/net/net.c
index 61c5a6ef6c4..71666eb1113 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1076,6 +1076,8 @@ static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
} else if (h >= thisfrag) {
/* overlaps with initial part of the hole: move this hole */
newh = thisfrag + (len / 8);
+ if ((uchar *)(newh + 1) > pkt_buff + IP_PKTSIZE)
+ return NULL; /* hole descriptor would overflow pkt_buff */
*newh = *h;
h = newh;
if (h->next_hole)
@@ -1088,6 +1090,8 @@ static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
} else {
/* fragment sits in the middle: split the hole */
newh = thisfrag + (len / 8);
+ if ((uchar *)(newh + 1) > pkt_buff + IP_PKTSIZE)
+ return NULL; /* hole descriptor would overflow pkt_buff */
*newh = *h;
h->last_byte = start;
h->next_hole = (newh - payload);
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH v2 2/2] test: net: add regression test for IP reassembly overflow
2026-07-28 6:50 [PATCH v2 0/2] net: fix out-of-bounds write in IP fragment reassembly Shahriyar Jalayeri
2026-07-28 6:50 ` [PATCH v2 1/2] " Shahriyar Jalayeri
@ 2026-07-28 6:50 ` Shahriyar Jalayeri
1 sibling, 0 replies; 4+ messages in thread
From: Shahriyar Jalayeri @ 2026-07-28 6:50 UTC (permalink / raw)
To: u-boot
Cc: Jerome Forissier, Tom Rini, Wayen Yan, Simon Glass, Argus,
Shahriyar Jalayeri
Add a DM test that feeds __net_defragment() a single crafted fragment
whose trailing hole descriptor lands just past pkt_buff. Without the
preceding fix the 8-byte hole write goes out of bounds; with it the
fragment is dropped and no datagram is delivered.
Signed-off-by: Shahriyar Jalayeri <shahriyar@byteray.co.uk>
Acked-by: Jerome Forissier <jerome.forissier@arm.com>
---
test/dm/net_defrag.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/test/dm/net_defrag.c b/test/dm/net_defrag.c
index 3fd40de90cd..7501b252db9 100644
--- a/test/dm/net_defrag.c
+++ b/test/dm/net_defrag.c
@@ -80,3 +80,39 @@ static int dm_test_net_ip_defrag_dup_last(struct unit_test_state *uts)
}
DM_TEST(dm_test_net_ip_defrag_dup_last, 0);
+
+/*
+ * A fragment placed at the very top of the reassembly buffer takes the
+ * split-hole branch, which writes an 8-byte "struct hole" at
+ * pkt_buff + IP_HDR_SIZE + (offset8 + len / 8) * 8. With start + len equal to
+ * IP_MAXUDP that write reaches the end of pkt_buff and spills past it. pkt_buff
+ * is a static array, so this is flagged under AddressSanitizer; the fix rejects
+ * such a fragment instead. The datagram is incomplete, so nothing is delivered
+ * either way.
+ */
+static int dm_test_net_ip_defrag_oob(struct unit_test_state *uts)
+{
+ rxhand_f *saved_handler = net_get_udp_handler();
+ uchar frame[FRAME_LEN];
+ struct ip_udp_hdr *ip = (struct ip_udp_hdr *)(frame + ETHER_HDR_SIZE);
+ u16 payload[4] = { 0, 0, 0, 0 };
+ /* Offset (8-byte units) so that start + FRAG_LEN == IP_MAXUDP. */
+ u16 off8 = (CONFIG_NET_MAXDEFRAG - IP_HDR_SIZE - FRAG_LEN) / 8;
+
+ udp_rx_count = 0;
+ net_set_udp_handler(defrag_udp_handler);
+
+ build_frag(frame, IP_FLAGS_MFRAG | off8, payload);
+ /* A distinct id forces a fresh reassembly independent of earlier tests. */
+ ip->ip_id = htons(0x7abc);
+ ip->ip_sum = 0;
+ ip->ip_sum = compute_ip_checksum(ip, IP_HDR_SIZE);
+ net_process_received_packet(frame, FRAME_LEN);
+
+ ut_asserteq(0, udp_rx_count);
+
+ net_set_udp_handler(saved_handler);
+
+ return 0;
+}
+DM_TEST(dm_test_net_ip_defrag_oob, 0);
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread