mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] ieee802154: 6lowpan: validate fragment offset against datagram size
@ 2026-11-07 22:32 Ryan Prinz
  2026-11-08 13:05 ` [PATCH RESEND] " Ryan Prinz
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Ryan Prinz @ 2026-11-07 22:32 UTC (permalink / raw)
  To: linux-wpan, netdev
  Cc: Alexander Aring, Stefan Schmidt, Miquel Raynal, David S . Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	linux-kernel, Ryan Prinz


[-- Attachment #1.1: Type: text/plain, Size: 3342 bytes --]

A 6LoWPAN fragment announces the total size of the datagram it belongs
to (d_size) and its own offset within that datagram. The reassembly
code never checks that the offset actually fits inside d_size, so a
fragment claiming to start far past the end of the datagram is
accepted the same as a legitimate one, allowing an attacker to forge
data of another sender.

lowpan_frag_queue() considers a datagram complete by counting bytes
received (fq->q.meat) against the declared size (fq->q.len), not by
verifying the bytes form a contiguous range. A fragment with an
out-of-range offset still has its length counted toward that total, so
a legitimate fragment exchange plus an attacker fragment can sum to
d_size, marking the datagram complete while a gap in the data was
never filled, and appending the out-of-range fragment's bytes to the
end of the reassembled buffer as if they were part of the message.

IPv6's reassembly (net/ipv6/reassembly.c, ip6_frag_queue()) computes
end = of
fset + payload length for every fragment and checks end
against IPV6_MAXPLEN, which bounds offset by construction. 6LoWPAN's
d_offset and d_size are independent wire fields with no such coupling,
and no separate bounds check was added for it.

The same class of bug was assigned CVE-2019-8359 (CVSS 9.8) in
Contiki-NG's 6LoWPAN stack before a 2019 fix; RIOT-OS's implementation
(gnrc_sixlowpan_frag_rb_add) rejects a fragment once offset + size
exceeds the reassembly buffer's datagram_size. Linux's 6LoWPAN
reassembly is the only one of these implementations without an
equivalent check.

Fix by rejecting any fragment whose offset, or offset plus length,
falls outside [0, d_size).

Fixes: 7240cdec60b1 ("6lowpan: handling 6lowpan fragmentation via inet_frag api")
Signed-off-by: Ryan Prinz <rc4d5a@protonmail.com>
---
Reproduced two ways: with a local kernel module calling
lowpan_frag_rcv() directly, and over mac802154_hwsim with three
independent processes standing in for s
ender, receiver and attacker.
An attacker in radio range who observes one fragment of an
in-progress transmission reads d_tag, d_size and the addressing in the
clear, then sends a single out-of-range fragment of its own. The
receiver's reassembly completes with the attacker's bytes appended to
the legitimate data. Both reproducers were re-run against this fix to
confirm the out-of-range fragment is now rejected and the datagram no
longer completes with it included.

AI Disclosure: An AI assistant (Claude) was used to assist with the
analysis and testing. I have verified the vulnerability, the fix, and
the test results myself and take responsibility for this contribution,
per Documentation/process/coding-assistants.rst.

 net/ieee802154/6lowpan/reassembly.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c
index ddb6a5817d09..905ba5f86844 100644
--- a/net/ieee802154/6lowpan/reassembly.c
++
+ b/net/ieee802154/6lowpan/reassembly.c
@@ -103,6 +103,9 @@ static int lowpan_frag_queue(struct lowpan_frag_queue *fq,
 	offset = lowpan_802154_cb(skb)->d_offset << 3;
 	end = lowpan_802154_cb(skb)->d_size;

+	if (offset > end || skb->len > end - offset)
+		goto err;
+
 	/* Is this the final fragment? */
 	if (offset + skb->len == end) {
 		/* If we already have some bits beyond end
--
2.51.2

[-- Attachment #1.2: publickey - rc4d5a@protonmail.com - 0xE775BF83.asc --]
[-- Type: application/pgp-keys, Size: 897 bytes --]

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 322 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH RESEND] ieee802154: 6lowpan: validate fragment offset against datagram size
  2026-11-07 22:32 [PATCH] ieee802154: 6lowpan: validate fragment offset against datagram size Ryan Prinz
@ 2026-11-08 13:05 ` Ryan Prinz
  2026-11-08 14:20 ` [PATCH net " Ryan Prinz
  2026-11-09  9:03 ` Ryan Prinz
  2 siblings, 0 replies; 7+ messages in thread
From: Ryan Prinz @ 2026-11-08 13:05 UTC (permalink / raw)
  To: linux-wpan, netdev
  Cc: Alexander Aring, Stefan Schmidt, Miquel Raynal, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	linux-kernel, Ryan Prinz


[-- Attachment #1.1: Type: text/plain, Size: 3407 bytes --]

A 6LoWPAN fragment announces the total size of the datagram it belongs
to (d_size) and its own offset within that datagram. The reassembly
code never checks that the offset actually fits inside d_size, so a
fragment claiming to start far past the end of the datagram is
accepted the same as a legitimate one, allowing an attacker to forge
data of another sender.

lowpan_frag_queue() considers a datagram complete by counting bytes
received (fq->q.meat) against the declared size (fq->q.len), not by
verifying the bytes form a contiguous range. A fragment with an
out-of-range offset still has its length counted toward that total, so
a legitimate fragment exchange plus an attacker fragment can sum to
d_size, marking the datagram complete while a gap in the data was
never filled, and appending the out-of-range fragment's bytes to the
end of the reassembled buffer as if they were part of the message.

IPv6's reassembly (net/ipv6/reassembly.c, ip6_frag_queue()) computes
end = of
fset + payload length for every fragment and checks end
against IPV6_MAXPLEN, which bounds offset by construction. 6LoWPAN's
d_offset and d_size are independent wire fields with no such coupling,
and no separate bounds check was added for it.

The same class of bug was assigned CVE-2019-8359 (CVSS 9.8) in
Contiki-NG's 6LoWPAN stack before a 2019 fix; RIOT-OS's implementation
(gnrc_sixlowpan_frag_rb_add) rejects a fragment once offset + size
exceeds the reassembly buffer's datagram_size. Linux's 6LoWPAN
reassembly is the only one of these implementations without an
equivalent check.

Fix by rejecting any fragment whose offset, or offset plus length,
falls outside [0, d_size).

Reproduced two ways: with a local kernel module calling
lowpan_frag_rcv() directly, and over mac802154_hwsim with three
independent processes standing in for sender, receiver and attacker.
An attacker in radio range who observes one fragment of an
in-progress transmission reads d_tag, d_size and
 the addressing in the
clear, then sends a single out-of-range fragment of its own. The
receiver's reassembly completes with the attacker's bytes appended to
the legitimate data. Both reproducers were re-run against this fix to
confirm the out-of-range fragment is now rejected and the datagram no
longer completes with it included.

AI Disclosure: An AI assistant (Claude) was used to assist with the
analysis and testing. I have verified the vulnerability, the fix, and
the test results myself and take responsibility for this contribution,
per Documentation/process/coding-assistants.rst.

Fixes: 7240cdec60b1 ("6lowpan: handling 6lowpan fragmentation via inet_frag api")
Signed-off-by: Ryan Prinz <rc4d5a@protonmail.com>
---
Resending to fix a diff formatting issue in my previous mail.

 net/ieee802154/6lowpan/reassembly.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c
index ddb6a5817d09..9
05ba5f86844 100644
--- a/net/ieee802154/6lowpan/reassembly.c
+++ b/net/ieee802154/6lowpan/reassembly.c
@@ -103,6 +103,9 @@ static int lowpan_frag_queue(struct lowpan_frag_queue *fq,
 	offset = lowpan_802154_cb(skb)->d_offset << 3;
 	end = lowpan_802154_cb(skb)->d_size;
 
+	if (offset > end || skb->len > end - offset)
+		goto err;
+
 	/* Is this the final fragment? */
 	if (offset + skb->len == end) {
 		/* If we already have some bits beyond end
-- 
2.51.2

[-- Attachment #1.2: publickey - rc4d5a@protonmail.com - 0xE775BF83.asc --]
[-- Type: application/pgp-keys, Size: 897 bytes --]

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 322 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH net RESEND] ieee802154: 6lowpan: validate fragment offset against datagram size
  2026-11-07 22:32 [PATCH] ieee802154: 6lowpan: validate fragment offset against datagram size Ryan Prinz
  2026-11-08 13:05 ` [PATCH RESEND] " Ryan Prinz
@ 2026-11-08 14:20 ` Ryan Prinz
  2026-11-09  9:03 ` Ryan Prinz
  2 siblings, 0 replies; 7+ messages in thread
From: Ryan Prinz @ 2026-11-08 14:20 UTC (permalink / raw)
  To: linux-wpan, netdev
  Cc: Alexander Aring, Stefan Schmidt, Miquel Raynal, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	linux-kernel, Ryan Prinz


[-- Attachment #1.1: Type: text/plain, Size: 3399 bytes --]

A 6LoWPAN fragment announces the total size of the datagram it belongs
to (d_size) and its own offset within that datagram. The reassembly
code never checks that the offset actually fits inside d_size, so a
fragment claiming to start far past the end of the datagram is
accepted the same as a legitimate one, allowing an attacker to forge
data of another sender.

lowpan_frag_queue() considers a datagram complete by counting bytes
received (fq->q.meat) against the declared size (fq->q.len), not by
verifying the bytes form a contiguous range. A fragment with an
out-of-range offset still has its length counted toward that total, so
a legitimate fragment exchange plus an attacker fragment can sum to
d_size, marking the datagram complete while a gap in the data was
never filled, and appending the out-of-range fragment's bytes to the
end of the reassembled buffer as if they were part of the message.

IPv6's reassembly (net/ipv6/reassembly.c, ip6_frag_queue()) computes
end = of
fset + payload length for every fragment and checks end
against IPV6_MAXPLEN, which bounds offset by construction. 6LoWPAN's
d_offset and d_size are independent wire fields with no such coupling,
and no separate bounds check was added for it.

The same class of bug was assigned CVE-2019-8359 (CVSS 9.8) in
Contiki-NG's 6LoWPAN stack before a 2019 fix; RIOT-OS's implementation
(gnrc_sixlowpan_frag_rb_add) rejects a fragment once offset + size
exceeds the reassembly buffer's datagram_size. Linux's 6LoWPAN
reassembly is the only one of these implementations without an
equivalent check.

Fix by rejecting any fragment whose offset, or offset plus length,
falls outside [0, d_size).

Reproduced two ways: with a local kernel module calling
lowpan_frag_rcv() directly, and over mac802154_hwsim with three
independent processes standing in for sender, receiver and attacker.
An attacker in radio range who observes one fragment of an
in-progress transmission reads d_tag, d_size and
 the addressing in the
clear, then sends a single out-of-range fragment of its own. The
receiver's reassembly completes with the attacker's bytes appended to
the legitimate data. Both reproducers were re-run against this fix to
confirm the out-of-range fragment is now rejected and the datagram no
longer completes with it included.

AI Disclosure: An AI assistant (Claude) was used to assist with the
analysis and testing. I have verified the vulnerability, the fix, and
the test results myself and take responsibility for this contribution,
per Documentation/process/coding-assistants.rst.

Fixes: 7240cdec60b1 ("6lowpan: handling 6lowpan fragmentation via inet_frag api")
Signed-off-by: Ryan Prinz <rc4d5a@protonmail.com>
---
Resending with correct tree designator in the subject

 net/ieee802154/6lowpan/reassembly.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c
index ddb6a5817d09..905ba5f86
844 100644
--- a/net/ieee802154/6lowpan/reassembly.c
+++ b/net/ieee802154/6lowpan/reassembly.c
@@ -103,6 +103,9 @@ static int lowpan_frag_queue(struct lowpan_frag_queue *fq,
 	offset = lowpan_802154_cb(skb)->d_offset << 3;
 	end = lowpan_802154_cb(skb)->d_size;
 
+	if (offset > end || skb->len > end - offset)
+		goto err;
+
 	/* Is this the final fragment? */
 	if (offset + skb->len == end) {
 		/* If we already have some bits beyond end
-- 
2.51.2

[-- Attachment #1.2: publickey - rc4d5a@protonmail.com - 0xE775BF83.asc --]
[-- Type: application/pgp-keys, Size: 897 bytes --]

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 322 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH net RESEND] ieee802154: 6lowpan: validate fragment offset against datagram size
  2026-11-07 22:32 [PATCH] ieee802154: 6lowpan: validate fragment offset against datagram size Ryan Prinz
  2026-11-08 13:05 ` [PATCH RESEND] " Ryan Prinz
  2026-11-08 14:20 ` [PATCH net " Ryan Prinz
@ 2026-11-09  9:03 ` Ryan Prinz
  2026-11-12  9:05   ` netdev-bot+sashiko
  2026-11-12 15:31   ` [PATCH net v2] " Ryan Prinz
  2 siblings, 2 replies; 7+ messages in thread
From: Ryan Prinz @ 2026-11-09  9:03 UTC (permalink / raw)
  To: linux-wpan, netdev
  Cc: Alexander Aring, Stefan Schmidt, Miquel Raynal, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	linux-kernel, Ryan Prinz

A 6LoWPAN fragment announces the total size of the datagram it belongs
to (d_size) and its own offset within that datagram. The reassembly
code never checks that the offset actually fits inside d_size, so a
fragment claiming to start far past the end of the datagram is
accepted the same as a legitimate one, allowing an attacker to forge
data of another sender.

lowpan_frag_queue() considers a datagram complete by counting bytes
received (fq->q.meat) against the declared size (fq->q.len), not by
verifying the bytes form a contiguous range. A fragment with an
out-of-range offset still has its length counted toward that total, so
a legitimate fragment exchange plus an attacker fragment can sum to
d_size, marking the datagram complete while a gap in the data was
never filled, and appending the out-of-range fragment's bytes to the
end of the reassembled buffer as if they were part of the message.

IPv6's reassembly (net/ipv6/reassembly.c, ip6_frag_queue()) computes
end = offset + payload length for every fragment and checks end
against IPV6_MAXPLEN, which bounds offset by construction. 6LoWPAN's
d_offset and d_size are independent wire fields with no such coupling,
and no separate bounds check was added for it.

The same class of bug was assigned CVE-2019-8359 (CVSS 9.8) in
Contiki-NG's 6LoWPAN stack before a 2019 fix; RIOT-OS's implementation
(gnrc_sixlowpan_frag_rb_add) rejects a fragment once offset + size
exceeds the reassembly buffer's datagram_size. Linux's 6LoWPAN
reassembly is the only one of these implementations without an
equivalent check.

Fix by rejecting any fragment whose offset, or offset plus length,
falls outside [0, d_size).

Reproduced two ways: with a local kernel module calling
lowpan_frag_rcv() directly, and over mac802154_hwsim with three
independent processes standing in for sender, receiver and attacker.
An attacker in radio range who observes one fragment of an
in-progress transmission reads d_tag, d_size and the addressing in the
clear, then sends a single out-of-range fragment of its own. The
receiver's reassembly completes with the attacker's bytes appended to
the legitimate data. Both reproducers were re-run against this fix to
confirm the out-of-range fragment is now rejected and the datagram no
longer completes with it included.

AI Disclosure: An AI assistant (Claude) was used to assist with the
analysis and testing. I have verified the vulnerability, the fix, and
the test results myself and take responsibility for this contribution,
per Documentation/process/coding-assistants.rst.

Fixes: 7240cdec60b1 ("6lowpan: handling 6lowpan fragmentation via inet_frag api")
Signed-off-by: Ryan Prinz <rc4d5a@protonmail.com>
---
Resending with correct tree designator in the subject

 net/ieee802154/6lowpan/reassembly.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c
index ddb6a5817d09..905ba5f86844 100644
--- a/net/ieee802154/6lowpan/reassembly.c
+++ b/net/ieee802154/6lowpan/reassembly.c
@@ -103,6 +103,9 @@ static int lowpan_frag_queue(struct lowpan_frag_queue *fq,
 	offset = lowpan_802154_cb(skb)->d_offset << 3;
 	end = lowpan_802154_cb(skb)->d_size;
 
+	if (offset > end || skb->len > end - offset)
+		goto err;
+
 	/* Is this the final fragment? */
 	if (offset + skb->len == end) {
 		/* If we already have some bits beyond end
-- 
2.51.2


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH net RESEND] ieee802154: 6lowpan: validate fragment offset against datagram size
  2026-11-09  9:03 ` Ryan Prinz
@ 2026-11-12  9:05   ` netdev-bot+sashiko
  2026-11-12 15:29     ` Ryan Prinz
  2026-11-12 15:31   ` [PATCH net v2] " Ryan Prinz
  1 sibling, 1 reply; 7+ messages in thread
From: netdev-bot+sashiko @ 2026-11-12  9:05 UTC (permalink / raw)
  To: rc4d5a
  Cc: linux-wpan, netdev, alex.aring, stefan, miquel.raynal, davem,
	edumazet, kuba, pabeni, horms, linux-kernel

Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 1 · Low: 0

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] The commit message overstates the security impact and
  misstates the check it adds.

--

ieee802154: 6lowpan: validate fragment offset against datagram size

This commit adds a bounds check to lowpan_frag_queue() that rejects a
6LoWPAN fragment when its offset, or its offset plus length, goes past
the declared datagram size (d_size). The commit message says this stops
an attacker from forging another sender's data. It also compares the
bug to CVE-2019-8359 in Contiki-NG.

> fragment claiming to start far past the end of the datagram is
> accepted the same as a legitimate one, allowing an attacker to forge
> data of another sender.

[ ... ]

> The same class of bug was assigned CVE-2019-8359 (CVSS 9.8) in
> Contiki-NG's 6LoWPAN stack before a 2019 fix; RIOT-OS's implementation

[ ... ]

> Fix by rejecting any fragment whose offset, or offset plus length,
> falls outside [0, d_size).

[ ... ]

> diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c
> index ddb6a5817d098..905ba5f868442 100644
> --- a/net/ieee802154/6lowpan/reassembly.c
> +++ b/net/ieee802154/6lowpan/reassembly.c
> @@ -103,6 +103,9 @@ static int lowpan_frag_queue(struct lowpan_frag_queue *fq,
>  	offset = lowpan_802154_cb(skb)->d_offset << 3;
>  	end = lowpan_802154_cb(skb)->d_size;
>  
> +	if (offset > end || skb->len > end - offset)
> +		goto err;
> +

[Severity: Medium]
Does the commit message match what this check rejects and what it
protects against?

The check accepts offset + skb->len == end, which it has to for the
final fragment. It also accepts a zero-length FRAGN with offset ==
d_size. So the fragment end has a closed upper bound, not the
"[0, d_size)" range the message gives.

ip6_frag_queue() drops zero-length fragments:

	if (end == offset)
		goto discard_fq;

lowpan_frag_queue() still accepts them. They add nothing to fq->q.meat,
so they can't append any data.

About "allowing an attacker to forge data of another sender": the
message assumes the attacker can read d_tag, d_size and the addressing
in the clear. With that, can't the attacker still send an in-range
fragment at the next expected offset? For example, with d_size = 200:

  FRAG1 [0, 64)       legitimate, queued
  FRAGN [64, 136)     attacker, passes the new check
  FRAGN [64, 136)     legitimate, rejected as an overlap
  FRAGN [136, 200)    legitimate, completes the datagram

The attacker's fragment takes the append path in
inet_frag_queue_insert():

	else if (FRAG_CB(last)->ip_defrag_offset + last->len < end) {
		/* This is the common case: skb goes to the end. */
		/* Detect and discard overlaps. */
		if (offset < FRAG_CB(last)->ip_defrag_offset + last->len)
			return IPFRAG_OVERLAP;
		if (offset == FRAG_CB(last)->ip_defrag_offset + last->len)
			fragrun_append_to_last(q, skb);

The legitimate [64, 136) fragment then gets IPFRAG_OVERLAP and is freed
through goto err in lowpan_frag_queue(). The reassembled datagram ends
up holding the attacker's bytes.

Also, 802.15.4 has no source authentication without llsec, so an
attacker can just spoof a whole datagram. This bug doesn't really enable
forging another sender's data, and the patch doesn't prevent it.

About "The same class of bug was assigned CVE-2019-8359 (CVSS 9.8)": the
Contiki-NG bug was an out-of-bounds write into a fixed reassembly
buffer. Linux never copies payload to a computed offset.
inet_frag_reasm_finish() links the fragment skbs to the head:

				head->data_len += fp->len;
				head->len += fp->len;
				head->truesize += fp->truesize;

				*nextp = fp;

So before this patch, an out-of-range offset only changed the contents
of the reassembled payload. It never touched memory outside it. Could
the comparison and the 9.8 score come out of the message? They may
mislead stable and CVE triage.

The change itself looks like a reasonable integrity and robustness fix:
a reassembled datagram can no longer contain bytes past the declared
d_size. Could the message describe it that way, and give the fragment
end's upper bound as closed?

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261109090226.799973-1-rc4d5a%40protonmail.com

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH net RESEND] ieee802154: 6lowpan: validate fragment offset against datagram size
  2026-11-12  9:05   ` netdev-bot+sashiko
@ 2026-11-12 15:29     ` Ryan Prinz
  0 siblings, 0 replies; 7+ messages in thread
From: Ryan Prinz @ 2026-11-12 15:29 UTC (permalink / raw)
  To: netdev-bot+sashiko
  Cc: Alexander Aring, David S . Miller, Eric Dumazet, Simon Horman,
	Jakub Kicinski, linux-kernel, linux-wpan, Miquel Raynal, netdev,
	Paolo Abeni, Ryan Prinz, Stefan Schmidt

Thanks for the review! These are fair points, I'll respin.

- The accepted offset range has a closed upper bound (offset + length
  == d_size is allowed, as needed for the final fragment, including a
  zero-length one at offset == d_size), not the open [0, d_size) I
  described.
- 802.15.4 has no source authentication without llsec, so this bug
  doesn't let an attacker impersonate another sender as the message
  implied. The real effect is an attacker's fragment getting spliced
  into the reassembled datagram in place of a legitimate sender's
  bytes for a given span, via the overlap-eviction path you described.
- The CVE-2019-8359/Contiki-NG comparison doesn't match. That was an
  out-of-bounds write into a fixed buffer. This is a reassembly
  data-integrity issue only, since inet_frag_reasm_finish() links skbs
  by pointer rather than copying into a computed offset. Dropping that
  comparison.

Sending v2 with the commit message corrected; the fix itself is
unchanged.

pw-bot: cr


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH net v2] ieee802154: 6lowpan: validate fragment offset against datagram size
  2026-11-09  9:03 ` Ryan Prinz
  2026-11-12  9:05   ` netdev-bot+sashiko
@ 2026-11-12 15:31   ` Ryan Prinz
  1 sibling, 0 replies; 7+ messages in thread
From: Ryan Prinz @ 2026-11-12 15:31 UTC (permalink / raw)
  To: linux-wpan, netdev
  Cc: Alexander Aring, Stefan Schmidt, Miquel Raynal, David S . Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	linux-kernel, Ryan Prinz

A 6LoWPAN fragment announces the total size of the datagram it belongs
to (d_size) and its own offset within that datagram. The reassembly
code never checks that the offset actually fits inside d_size, so a
fragment claiming to start past the end of the datagram is accepted
the same as a legitimate one.

lowpan_frag_queue() considers a datagram complete by counting bytes
received (fq->q.meat) against the declared size (fq->q.len), not by
verifying the bytes form a contiguous range. A fragment with an
out-of-range offset still has its length counted toward that total.
If an attacker's out-of-range fragment lands in a gap before the
legitimate fragment for that span arrives, inet_frag_queue_insert()
accepts the attacker's bytes into that span; when the legitimate
fragment for the same span then arrives, it collides with the
attacker's skb and is dropped as an overlap (IPFRAG_OVERLAP). The
datagram still completes, but with the attacker's bytes in the
reassembled buffer instead of the legitimate sender's.

IPv6's reassembly (net/ipv6/reassembly.c, ip6_frag_queue()) computes
end = offset + payload length for every fragment and checks end
against IPV6_MAXPLEN, which bounds offset by construction. 6LoWPAN's
d_offset and d_size are independent wire fields with no such coupling,
and no separate bounds check was added for it. RIOT-OS's 6LoWPAN
implementation (gnrc_sixlowpan_frag_rb_add) already rejects a fragment
once offset + size exceeds the reassembly buffer's datagram_size;
Linux's 6LoWPAN reassembly had no equivalent check.

Fixed by rejecting any fragment whose offset exceeds d_size, or whose
offset plus length would exceed d_size. A final fragment ending
exactly at d_size, including a zero-length one at offset == d_size, is
still accepted, matching the completion check right below it.

Fixes: 7240cdec60b1 ("6lowpan: handling 6lowpan fragmentation via inet_frag api")
Signed-off-by: Ryan Prinz <rc4d5a@protonmail.com>
---
v2:
 - Reworded the impact description. 802.15.4 has no source
   authentication without llsec, so this bug doesn't let an attacker
   impersonate another sender; the real effect is that an attacker's
   fragment can get spliced into the reassembled datagram in place of
   a legitimate sender's bytes, with the legitimate fragment for that
   span evicted as an overlap.
 - Dropped the CVE-2019-8359/Contiki-NG comparison. That bug was an
   out-of-bounds write into a fixed buffer; this one only corrupts the
   reassembled payload's contents, since inet_frag_reasm_finish()
   links fragment skbs by pointer rather than copying into a computed
   offset. The two aren't the same severity class.
 - Corrected the described accepted range: the check allows
   offset + length == d_size, which it must for the final fragment
   (including a zero-length one at offset == d_size), so the accepted
   range has a closed upper bound, not the open "[0, d_size)" the v1
   message stated.

No change to the fix itself; the diff in reassembly.c is unchanged
from v1.

Reproduced two ways: with a local kernel module calling
lowpan_frag_rcv() directly, and over mac802154_hwsim with three
independent processes standing in for sender, receiver and attacker.
An attacker in radio range who observes one fragment of an
in-progress transmission reads d_tag, d_size and the addressing in the
clear, then sends a single out-of-range fragment of its own before the
legitimate fragment for that span arrives. The receiver's reassembly
completes with the attacker's bytes in place of the legitimate
sender's for that span, and the legitimate fragment is dropped as an
overlap. Both reproducers were re-run against this fix to confirm the
out-of-range fragment is now rejected outright.

AI Disclosure: An AI assistant (Claude) was used to assist with the
analysis and testing. I have verified the vulnerability, the fix, and
the test results myself and take responsibility for this contribution,
per Documentation/process/coding-assistants.rst.

 net/ieee802154/6lowpan/reassembly.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c
index ddb6a5817d09..905ba5f86844 100644
--- a/net/ieee802154/6lowpan/reassembly.c
+++ b/net/ieee802154/6lowpan/reassembly.c
@@ -103,6 +103,9 @@ static int lowpan_frag_queue(struct lowpan_frag_queue *fq,
 	offset = lowpan_802154_cb(skb)->d_offset << 3;
 	end = lowpan_802154_cb(skb)->d_size;

+	if (offset > end || skb->len > end - offset)
+		goto err;
+
 	/* Is this the final fragment? */
 	if (offset + skb->len == end) {
 		/* If we already have some bits beyond end
--
2.51.2


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-11-12 15:31 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-07 22:32 [PATCH] ieee802154: 6lowpan: validate fragment offset against datagram size Ryan Prinz
2026-11-08 13:05 ` [PATCH RESEND] " Ryan Prinz
2026-11-08 14:20 ` [PATCH net " Ryan Prinz
2026-11-09  9:03 ` Ryan Prinz
2026-11-12  9:05   ` netdev-bot+sashiko
2026-11-12 15:29     ` Ryan Prinz
2026-11-12 15:31   ` [PATCH net v2] " Ryan Prinz

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®