From d64dcbb71ff14aa57a22973a0599b1268c3e8843 Mon Sep 17 00:00:00 2001 From: Nils Bernsdorf Date: Thu, 4 Dec 2025 15:27:02 +0100 Subject: [PATCH 1/4] gnrc_ipv6_ext_frag: fail on first fragment buffer is too small This case can happen if a second, but larger first fragment is send after a smaller. --- .../gnrc/network_layer/ipv6/ext/frag/gnrc_ipv6_ext_frag.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sys/net/gnrc/network_layer/ipv6/ext/frag/gnrc_ipv6_ext_frag.c b/sys/net/gnrc/network_layer/ipv6/ext/frag/gnrc_ipv6_ext_frag.c index c57407729e..deafa0826e 100644 --- a/sys/net/gnrc/network_layer/ipv6/ext/frag/gnrc_ipv6_ext_frag.c +++ b/sys/net/gnrc/network_layer/ipv6/ext/frag/gnrc_ipv6_ext_frag.c @@ -524,6 +524,11 @@ gnrc_pktsnip_t *gnrc_ipv6_ext_frag_reass(gnrc_pktsnip_t *pkt) DEBUG("ipv6_ext_frag: fragment length not divisible by 8"); goto error_exit; } + else if (rbuf->pkt != NULL && rbuf->pkt->size < pkt->size) { + DEBUG("ipv6_ext_frag: reassembly buffer too small to fit first " + "fragment\n"); + goto error_exit; + } _set_nh(fh_snip->next, nh); gnrc_pktbuf_remove_snip(pkt, fh_snip); /* TODO: RFC 8200 says "- 8"; determine if `sizeof(ipv6_ext_frag_t)` is From 0e4c7dcaf52b7d2d89818aab9cf65acf81ae6bc1 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Fri, 5 Dec 2025 17:53:54 +0100 Subject: [PATCH 2/4] tests: amend IPv6 reassembly test for 1st fragment repeats Co-authored-by: Nils Bernsdorf --- .../gnrc_ipv6_ext_frag/tests-as-root/01-run.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/net/gnrc_ipv6_ext_frag/tests-as-root/01-run.py b/tests/net/gnrc_ipv6_ext_frag/tests-as-root/01-run.py index 22642a3627..c215e0c08e 100755 --- a/tests/net/gnrc_ipv6_ext_frag/tests-as-root/01-run.py +++ b/tests/net/gnrc_ipv6_ext_frag/tests-as-root/01-run.py @@ -156,6 +156,23 @@ def test_reass_offset_too_large(child, iface, hw_dst, ll_dst, ll_src): pktbuf_empty(child) +def test_reass_first_fragment_repeat(child, iface, hw_dst, ll_dst, ll_src): + # Originally proposed by Nils Bernsdorf (Uni Saarland), adapted by Martine Lenders + # send the first packet to initialize the reassembly buffer + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrFragment(id=0xabcd, nh=0, m=1, offset=0) / + (b"A"*(24-8)), + iface=iface, verbose=0) + # send the a second larger packet (also with offset 0) to trigger the buffer + # overflow + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrFragment(id=0xabcd, nh=0, m=1, offset=0) / + (b"A"*(128-8)), + iface=iface, verbose=0) + # the fragments should be discarded + pktbuf_empty(child) + + def test_ipv6_ext_frag_shell_test_0(child, s, iface, ll_dst): child.sendline("test {} 0".format(ll_dst)) data, _ = s.recvfrom(RECV_BUFSIZE) @@ -422,6 +439,7 @@ def testfunc(child): run(test_reass_successful_udp) run(test_reass_too_short_header) run(test_reass_offset_too_large) + run(test_reass_first_fragment_repeat) print("SUCCESS") From 8af2f50a7d5afcc052edd9a14db5fd3ebb24af85 Mon Sep 17 00:00:00 2001 From: Nils Bernsdorf Date: Thu, 4 Dec 2025 15:38:35 +0100 Subject: [PATCH 3/4] gnrc_ipv6_ext_frag: disregard empty fragments --- sys/net/gnrc/network_layer/ipv6/ext/frag/gnrc_ipv6_ext_frag.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sys/net/gnrc/network_layer/ipv6/ext/frag/gnrc_ipv6_ext_frag.c b/sys/net/gnrc/network_layer/ipv6/ext/frag/gnrc_ipv6_ext_frag.c index deafa0826e..2cdefe2428 100644 --- a/sys/net/gnrc/network_layer/ipv6/ext/frag/gnrc_ipv6_ext_frag.c +++ b/sys/net/gnrc/network_layer/ipv6/ext/frag/gnrc_ipv6_ext_frag.c @@ -423,6 +423,10 @@ gnrc_pktsnip_t *gnrc_ipv6_ext_frag_reass(gnrc_pktsnip_t *pkt) DEBUG("ipv6_ext_frag: unable to mark fragmentation header\n"); goto error_release; } + else if (pkt->size == 0) { + DEBUG("ipv6_ext_frag: fragment empty after removing header\n"); + goto error_release; + } fh = fh_snip->data; /* search IPv6 header */ ipv6_snip = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6); From af2bf2527cabe4f68d183994d0c3c25180a1d75a Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Fri, 5 Dec 2025 17:43:56 +0100 Subject: [PATCH 4/4] tests: amend IPv6 reassembly test for empty fragments Co-authored-by: Nils Bernsdorf --- .../gnrc_ipv6_ext_frag/tests-as-root/01-run.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/net/gnrc_ipv6_ext_frag/tests-as-root/01-run.py b/tests/net/gnrc_ipv6_ext_frag/tests-as-root/01-run.py index c215e0c08e..b1fd464e24 100755 --- a/tests/net/gnrc_ipv6_ext_frag/tests-as-root/01-run.py +++ b/tests/net/gnrc_ipv6_ext_frag/tests-as-root/01-run.py @@ -156,6 +156,22 @@ def test_reass_offset_too_large(child, iface, hw_dst, ll_dst, ll_src): pktbuf_empty(child) +def test_reass_empty_fragment(child, iface, hw_dst, ll_dst, ll_src): + # Originally proposed by Nils Bernsdorf (Uni Saarland), adapted by Martine Lenders + # send the first packet (without payload) to initialize the reassembly buffer + # with a null pointer + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrFragment(id=0xabcd, nh=0, m=1, offset=0), + iface=iface, verbose=0) + # send the second packet to potentially trigger a memcpy + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrFragment(id=0xabcd, nh=0, m=1, offset=0) / + (b"A" * (24 - 8)), + iface=iface, verbose=0) + time.sleep(11) # let reassembly buffer garbage collect + pktbuf_empty(child) + + def test_reass_first_fragment_repeat(child, iface, hw_dst, ll_dst, ll_src): # Originally proposed by Nils Bernsdorf (Uni Saarland), adapted by Martine Lenders # send the first packet to initialize the reassembly buffer @@ -439,6 +455,7 @@ def testfunc(child): run(test_reass_successful_udp) run(test_reass_too_short_header) run(test_reass_offset_too_large) + run(test_reass_empty_fragment) run(test_reass_first_fragment_repeat) print("SUCCESS")