* [PATCH] tipc: bound reassembly size in tipc_buf_append()
@ 2026-11-08 22:25 Noah Weinberger
2026-11-09 9:45 ` Tung Quang Nguyen
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Noah Weinberger @ 2026-11-08 22:25 UTC (permalink / raw)
To: jmaloy, tung.quang.nguyen, davem, edumazet, kuba, pabeni
Cc: netdev, linux-kernel
Hello,
tipc_buf_append() accumulates fragments on the head skb without any
cumulative size or fragment-count limit: head->len and head->truesize
grow with every appended fragment, and the declared inner message size
is only checked by tipc_msg_validate() once the LAST_FRAGMENT arrives.
A peer that has established a TIPC link can send a FIRST_FRAGMENT
followed by an unbounded stream of in-sequence FRAGMENT packets. Each
fragment consumes a sequence number, so the link receive window slides
and every packet passes the window check; nothing bounds the reassembly
if the last fragment never arrives. The head skb's frag list therefore
grows by roughly one bearer-MTU skb per attacker packet - a remote
memory-exhaustion denial of service on any host with TIPC enabled and a
bearer up.
The patch bounds the accumulated size to TIPC_MAX_USER_MSG_SIZE. A
tighter variant would validate against the first fragment's declared
msg_size(); the simple bound is the minimal fix.
Affected version/commit: mainline fa4ad798f6f681031775266044ba1b0cf3c670bd (v7.4-rc1, 2026-11-07), net/tipc/msg.c:141-205 (tipc_buf_append), net/tipc/link.c:1297-1322, 1343-1397, 1778-1820 (callers).
Longstanding code.
This was found during an assisted (AI-supported) static source review of
mainline fa4ad798f6f681031775266044ba1b0cf3c670bd (v7.4-rc1, 2026-11-07); every code path cited below was re-verified by the
reporter against that tree. The patch below has NOT been compiled and has
NOT been runtime tested, and no reproducer was produced - the triggering
conditions are described above instead. Please treat this as a report for
verification rather than a battle-tested fix.
Reported-by: Noah Weinberger <noahweinberger2003@proton.me>
---
diff --git a/net/tipc/msg.c b/net/tipc/msg.c
index 39dfd33..b37db2c 100644
--- a/net/tipc/msg.c
+++ b/net/tipc/msg.c
@@ -145,6 +145,13 @@ int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf)
if (skb_has_frag_list(frag) && __skb_linearize(frag))
goto err;
*buf = NULL;
+
+ /* Bound the reassembled message size: a peer that never sends
+ * the last fragment could otherwise grow the head skb (and its
+ * frag list) without any limit.
+ */
+ if (head->len + frag->len > TIPC_MAX_USER_MSG_SIZE)
+ goto err;
frag = skb_unclone_full(frag, GFP_ATOMIC);
if (unlikely(!frag))
goto err;
^ permalink raw reply [flat|nested] 5+ messages in thread
* RE: [PATCH] tipc: bound reassembly size in tipc_buf_append()
2026-11-08 22:25 [PATCH] tipc: bound reassembly size in tipc_buf_append() Noah Weinberger
@ 2026-11-09 9:45 ` Tung Quang Nguyen
2026-11-09 22:55 ` netdev-bot+sashiko
2026-11-11 17:37 ` [syzbot ci] " syzbot ci
2 siblings, 0 replies; 5+ messages in thread
From: Tung Quang Nguyen @ 2026-11-09 9:45 UTC (permalink / raw)
To: Noah Weinberger
Cc: netdev, linux-kernel, jmaloy, davem, edumazet, kuba, pabeni
>Subject: [PATCH] tipc: bound reassembly size in tipc_buf_append()
>
>Hello,
>
>tipc_buf_append() accumulates fragments on the head skb without any
>cumulative size or fragment-count limit: head->len and head->truesize grow
>with every appended fragment, and the declared inner message size is only
>checked by tipc_msg_validate() once the LAST_FRAGMENT arrives.
>
>A peer that has established a TIPC link can send a FIRST_FRAGMENT followed
>by an unbounded stream of in-sequence FRAGMENT packets. Each fragment
>consumes a sequence number, so the link receive window slides and every
>packet passes the window check; nothing bounds the reassembly if the last
>fragment never arrives. The head skb's frag list therefore grows by roughly one
>bearer-MTU skb per attacker packet - a remote memory-exhaustion denial of
>service on any host with TIPC enabled and a bearer up.
>
This cannot occur because protocol messages are guaranteed to be sent/received
(TIPC must be configured using IPsec or Encryption so that no crafted/corrupt messages are sent/received).
Thus, LAST_FRAGMENT is always sent/received.
---
pw-bot: rejected
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] tipc: bound reassembly size in tipc_buf_append()
2026-11-08 22:25 [PATCH] tipc: bound reassembly size in tipc_buf_append() Noah Weinberger
2026-11-09 9:45 ` Tung Quang Nguyen
@ 2026-11-09 22:55 ` netdev-bot+sashiko
2026-11-09 23:21 ` Noah Weinberger
2026-11-11 17:37 ` [syzbot ci] " syzbot ci
2 siblings, 1 reply; 5+ messages in thread
From: netdev-bot+sashiko @ 2026-11-09 22:55 UTC (permalink / raw)
To: NoahWeinberger2003
Cc: jmaloy, tung.quang.nguyen, davem, edumazet, kuba, pabeni, netdev,
linux-kernel
Thank you for your contribution! Sashiko AI review found 6 potential
issue(s) to consider.
Critical: 1 · High: 1 · Medium: 2 · Low: 2
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).
- [Critical] In tipc_buf_append() (net/tipc/msg.c), the new size check is
placed in the FIRST_FRAGMENT branch and reads `head->len`.
- [High] The fix does not achieve its stated purpose.
- [Medium] The chosen limit does not match TIPC's own definition of a
maximum message.
- [Medium] Bounding only the byte length of the reassembled data does not
bound the memory a reassembly pins.
- [Low] The submission has form problems.
- [Low] The new error path in tipc_buf_append() leaks the fragment skb.
--
tipc: bound reassembly size in tipc_buf_append()
This patch adds a size check to tipc_buf_append() to limit the total size
of a TIPC message being reassembled from fragments. The aim is to stop a
remote peer from growing the reassembly skb without limit by sending a
first fragment and then an endless stream of in-sequence fragments.
> Reported-by: Noah Weinberger <noahweinberger2003@proton.me>
[Severity: Low]
This isn't a bug, but there's no Signed-off-by line, only a Reported-by
from the same person, so the patch can't be applied as is. Could a Fixes:
tag also be added, since this is meant to fix long-standing code?
The body also reads like an email report rather than a changelog
("Hello,", "Please treat this as a report for verification", and the
notes about AI-assisted review). It also says the patch hasn't been
compiled or runtime tested.
Could this be rewritten as a normal changelog?
> diff --git a/net/tipc/msg.c b/net/tipc/msg.c
> index 39dfd33c8eecc..b37db2cbcc8fb 100644
> --- a/net/tipc/msg.c
> +++ b/net/tipc/msg.c
> @@ -145,6 +145,13 @@ int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf)
> if (skb_has_frag_list(frag) && __skb_linearize(frag))
> goto err;
> *buf = NULL;
> +
> + /* Bound the reassembled message size: a peer that never sends
> + * the last fragment could otherwise grow the head skb (and its
> + * frag list) without any limit.
> + */
> + if (head->len + frag->len > TIPC_MAX_USER_MSG_SIZE)
[Severity: Critical]
Can head be anything other than NULL here? This is the FIRST_FRAGMENT
branch, and just above the hunk tipc_buf_append() already bails out if
head is set:
if (fragid == FIRST_FRAGMENT) {
if (unlikely(head))
goto err;
head is only assigned further down:
head = *headbuf = frag;
So it looks like every first fragment dereferences a NULL head. That
includes ordinary, legitimate fragmented messages.
On the receive side this runs in softirq with le->lock held:
tipc_rcv()
spin_lock_bh(&le->lock)
tipc_link_rcv()
tipc_link_input()
tipc_buf_append()
The tunnel reassembly in link.c hits it the same way. Local senders do
too, for example tipc_mcast_xmit()->tipc_msg_reassemble() for multicast
messages with local destinations that exceed the MTU, and
tipc_msg_build()->tipc_msg_assemble() or
tipc_lxc_xmit()->tipc_msg_assemble().
Would this oops on any peer that sends a message larger than the bearer
MTU?
[Severity: High]
Even with the NULL dereference fixed, does this check bound the
reassembly at all? Nothing has been accumulated yet in the
FIRST_FRAGMENT branch.
The growth described in the changelog happens later in tipc_buf_append(),
on the FRAGMENT/LAST_FRAGMENT path:
if (skb_try_coalesce(head, frag, &headstolen, &delta)) {
kfree_skb_partial(frag, headstolen);
} else {
...
head->truesize += frag->truesize;
head->data_len += frag->len;
head->len += frag->len;
...
}
That path still has no size check. A peer that never sends LAST_FRAGMENT
can still grow head without limit.
Should the check be done before appending on the non-first path instead,
perhaps against the first fragment's declared inner msg_size()?
[Severity: Medium]
Is TIPC_MAX_USER_MSG_SIZE the right limit to compare head->len against?
tipc_buf_append() only pulls the outer fragmenter header, so the
reassembled skb holds the inner header plus the payload.
TIPC_MAX_USER_MSG_SIZE is a payload-only limit everywhere else:
net/tipc/msg.c:tipc_msg_validate() {
...
if (unlikely((msz - hsz) > TIPC_MAX_USER_MSG_SIZE))
return false;
...
}
net/tipc/msg.h:
#define MAX_MSG_SIZE (MAX_H_SIZE + TIPC_MAX_USER_MSG_SIZE)
The socket send paths only reject dlen > TIPC_MAX_USER_MSG_SIZE, and
tipc_msg_build() fragments msz = mhsz + dsz.
If the check moves to the append path and keeps this constant, would
valid messages with a payload near 66000 bytes get rejected? On unicast
links, a reassembly failure makes tipc_link_input() reset the link:
} else if (!*reasm_skb && !link_is_bc_rcvlink(l)) {
pr_warn_ratelimited("Unable to build fragment list\n");
return tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
Would MAX_MSG_SIZE, or the first fragment's msg_size(), be a more
consistent bound?
[Severity: Medium]
Is a length-only bound enough to cap the memory one reassembly pins?
Once skb_try_coalesce() fails, each fragment skb is chained whole with
head->truesize += frag->truesize. Nothing limits the truesize or the
number of fragments.
A peer could send many in-sequence fragments that each carry about one
byte of payload. Each one passes the per-packet ratio check in
tipc_msg_validate():
if (unlikely(skb->truesize / buf_roundup_len(skb) >= 4)) {
but still costs roughly 1-4 KB of truesize.
With a 66000-byte length cap, one reassembly could hold tens of
thousands of skbs. That GFP_ATOMIC memory isn't charged to any memcg,
and the attack can be repeated per link and per tunnel reassembly. The
aggregate truesize is only checked when LAST_FRAGMENT arrives.
Should the accumulated truesize or the fragment count also be bounded?
> + goto err;
[Severity: Low]
Does this error path leak frag? *buf was already set to NULL above. In
the FIRST_FRAGMENT branch *headbuf is also NULL, so the err label frees
nothing:
err:
kfree_skb(*buf);
kfree_skb(*headbuf);
*buf = *headbuf = NULL;
The skb_unclone_full() failure path just below is fine, because
skb_unclone_full() frees the original skb itself. This new branch
doesn't free frag.
On MMU systems, the NULL dereference on the previous line hides this
today. A respin that only fixes the dereference would expose the leak,
unless the check moves before *buf = NULL or frag is freed explicitly.
> frag = skb_unclone_full(frag, GFP_ATOMIC);
> if (unlikely(!frag))
> goto err;
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/AoU-V5M5ZCKF37bLl8ILhgeNdsnCEaiaQNuWP4K56kRfJsnhkIexZ8tFRLq7uzGiA0qNqK6shXcSZ5E4hyV0-QI0ZWSuKiJzKvMrv3NfqCg%3D%40proton.me
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] tipc: bound reassembly size in tipc_buf_append()
2026-11-09 22:55 ` netdev-bot+sashiko
@ 2026-11-09 23:21 ` Noah Weinberger
0 siblings, 0 replies; 5+ messages in thread
From: Noah Weinberger @ 2026-11-09 23:21 UTC (permalink / raw)
To: netdev-bot+sashiko
Cc: jmaloy, tung.quang.nguyen, davem, edumazet, kuba, pabeni, netdev,
linux-kernel
Thanks for the review. Please disregard this version of the patch. Sashiko identifies a possible NULL dereference in the new check, and Tung has already rejected the original report's threat-model premise.
Noah
^ permalink raw reply [flat|nested] 5+ messages in thread
* [syzbot ci] Re: tipc: bound reassembly size in tipc_buf_append()
2026-11-08 22:25 [PATCH] tipc: bound reassembly size in tipc_buf_append() Noah Weinberger
2026-11-09 9:45 ` Tung Quang Nguyen
2026-11-09 22:55 ` netdev-bot+sashiko
@ 2026-11-11 17:37 ` syzbot ci
2 siblings, 0 replies; 5+ messages in thread
From: syzbot ci @ 2026-11-11 17:37 UTC (permalink / raw)
To: davem, edumazet, jmaloy, kuba, linux-kernel, netdev,
noahweinberger2003, pabeni, tung.quang.nguyen
Cc: syzbot, syzkaller-bugs
syzbot ci has tested the following series
[v1] tipc: bound reassembly size in tipc_buf_append()
https://lore.kernel.org/all/AoU-V5M5ZCKF37bLl8ILhgeNdsnCEaiaQNuWP4K56kRfJsnhkIexZ8tFRLq7uzGiA0qNqK6shXcSZ5E4hyV0-QI0ZWSuKiJzKvMrv3NfqCg=@proton.me
* [PATCH] tipc: bound reassembly size in tipc_buf_append()
and found the following issue:
general protection fault in tipc_buf_append
Full report is available here:
https://ci.syzbot.org/series/3856f5b1-55ca-4bc2-885e-034eb8af1bdf
***
general protection fault in tipc_buf_append
tree: net-next
URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/netdev/net-next.git
base: c4c470912125ec8a1a427a70f4085d8a731b00e4
arch: amd64
compiler: Debian clang version 22.1.8 (1~deb13u4), Debian LLD 22.1.8
config: https://ci.syzbot.org/builds/28264169-6c3f-405e-9f6a-339572b43e52/config
syz repro: https://ci.syzbot.org/findings/e9e2a2a5-41e6-4866-89fc-45c75bfe992b/syz_repro
Oops: general protection fault, probably for non-canonical address 0xdffffc000000000e: 0000 [#1] SMP KASAN PTI
KASAN: null-ptr-deref in range [0x0000000000000070-0x0000000000000077]
CPU: 1 UID: 0 PID: 5915 Comm: syz.1.18 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
RIP: 0010:tipc_buf_append+0x389/0xa20 net/tipc/msg.c:153
Code: e9 b5 01 00 00 e8 97 ea 29 f6 48 8b 44 24 20 42 80 3c 30 00 48 8b 5c 24 28 74 08 48 89 df e8 8e 74 a1 f6 48 c7 03 00 00 00 00 <41> 0f b6 46 0e 84 c0 0f 85 ea 04 00 00 8b 1c 25 70 00 00 00 4d 8d
RSP: 0018:ffffc900039defa0 EFLAGS: 00010246
RAX: 1ffff9200073be13 RBX: ffffc900039df098 RCX: ffff88810d335a00
RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff88816af130c0
RBP: ffffc900039df078 R08: ffff8881ba5f6ee3 R09: 1ffff110374beddc
R10: dffffc0000000000 R11: ffffed10374beddd R12: 1ffff1102d5e2617
R13: 1ffff9200073bdfc R14: dffffc0000000000 R15: ffff88816af12ff0
FS: 00007fbe193e76c0(0000) GS:ffff8882a7809000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fbe193e6ff8 CR3: 0000000117562000 CR4: 00000000000006f0
Call Trace:
<TASK>
tipc_msg_reassemble+0x230/0x3b0 net/tipc/msg.c:807
tipc_mcast_xmit+0x254/0x19e0 net/tipc/bcast.c:392
tipc_sendmcast net/tipc/socket.c:887 [inline]
__tipc_sendmsg+0x2074/0x2b60 net/tipc/socket.c:1470
tipc_sendmsg+0x55/0x70 net/tipc/socket.c:1404
sock_sendmsg_nosec+0x14e/0x190 net/socket.c:800
__sock_sendmsg net/socket.c:818 [inline]
____sys_sendmsg+0x54e/0x850 net/socket.c:2715
___sys_sendmsg+0x2a5/0x360 net/socket.c:2769
__sys_sendmmsg+0x273/0x4e0 net/socket.c:2858
__do_sys_sendmmsg net/socket.c:2885 [inline]
__se_sys_sendmmsg net/socket.c:2882 [inline]
__x64_sys_sendmmsg+0xa0/0xc0 net/socket.c:2882
do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fbe185a8f19
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fbe193e7028 EFLAGS: 00000246 ORIG_RAX: 0000000000000133
RAX: ffffffffffffffda RBX: 00007fbe18825fa0 RCX: 00007fbe185a8f19
RDX: 0000000000000300 RSI: 0000200000006740 RDI: 0000000000000003
RBP: 00007fbe18637f6d R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007fbe18826038 R14: 00007fbe18825fa0 R15: 00007ffeceeefb38
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:tipc_buf_append+0x389/0xa20 net/tipc/msg.c:153
Code: e9 b5 01 00 00 e8 97 ea 29 f6 48 8b 44 24 20 42 80 3c 30 00 48 8b 5c 24 28 74 08 48 89 df e8 8e 74 a1 f6 48 c7 03 00 00 00 00 <41> 0f b6 46 0e 84 c0 0f 85 ea 04 00 00 8b 1c 25 70 00 00 00 4d 8d
RSP: 0018:ffffc900039defa0 EFLAGS: 00010246
RAX: 1ffff9200073be13 RBX: ffffc900039df098 RCX: ffff88810d335a00
RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff88816af130c0
RBP: ffffc900039df078 R08: ffff8881ba5f6ee3 R09: 1ffff110374beddc
R10: dffffc0000000000 R11: ffffed10374beddd R12: 1ffff1102d5e2617
R13: 1ffff9200073bdfc R14: dffffc0000000000 R15: ffff88816af12ff0
FS: 00007fbe193e76c0(0000) GS:ffff8882a7809000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fbe193e6ff8 CR3: 0000000117562000 CR4: 00000000000006f0
----------------
Code disassembly (best guess):
0: e9 b5 01 00 00 jmp 0x1ba
5: e8 97 ea 29 f6 call 0xf629eaa1
a: 48 8b 44 24 20 mov 0x20(%rsp),%rax
f: 42 80 3c 30 00 cmpb $0x0,(%rax,%r14,1)
14: 48 8b 5c 24 28 mov 0x28(%rsp),%rbx
19: 74 08 je 0x23
1b: 48 89 df mov %rbx,%rdi
1e: e8 8e 74 a1 f6 call 0xf6a174b1
23: 48 c7 03 00 00 00 00 movq $0x0,(%rbx)
* 2a: 41 0f b6 46 0e movzbl 0xe(%r14),%eax <-- trapping instruction
2f: 84 c0 test %al,%al
31: 0f 85 ea 04 00 00 jne 0x521
37: 8b 1c 25 70 00 00 00 mov 0x70,%ebx
3e: 4d rex.WRB
3f: 8d .byte 0x8d
***
If these findings have caused you to resend the series or submit a
separate fix, please add the following tag to your commit message:
Tested-by: syzbot@syzkaller.appspotmail.com
---
This report is generated by a bot. It may contain errors.
syzbot ci engineers can be reached at syzkaller@googlegroups.com.
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-11 17:37 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-08 22:25 [PATCH] tipc: bound reassembly size in tipc_buf_append() Noah Weinberger
2026-11-09 9:45 ` Tung Quang Nguyen
2026-11-09 22:55 ` netdev-bot+sashiko
2026-11-09 23:21 ` Noah Weinberger
2026-11-11 17:37 ` [syzbot ci] " syzbot ci
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®