* [PATCH net] tipc: reject invalid and unexpected GRP_ACK_MSG to prevent bc_ackers underflow
@ 2026-09-13 4:42 Eric Dumazet
2026-09-14 4:52 ` netdev-bot+sashiko
2026-09-18 22:50 ` patchwork-bot+netdevbpf
0 siblings, 2 replies; 4+ messages in thread
From: Eric Dumazet @ 2026-09-13 4:42 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, netdev, eric.dumazet, Eric Dumazet, James Burton,
stable, Oleh Konko, Tung Nguyen, Jon Maloy
Commit 48a5fe38772b ("tipc: fix bc_ackers underflow on duplicate
GRP_ACK_MSG") rejected duplicate/stale ACKs in tipc_group_proto_rcv()
by returning early when less_eq(acked, m->bc_acked).
However, that check remains incomplete in two ways:
1. When grp->bc_ackers is zero (e.g. on a quiet group, when replicast
ACKs were not requested, or after all expected members have already
acknowledged), an unexpected GRP_ACK_MSG with acked > m->bc_acked
passes less_eq() and unconditionally decrements grp->bc_ackers.
Because bc_ackers is a u16, this wraps to 65535, causing
tipc_group_bc_cong() to permanently report congestion and blocking
all future group broadcasts on the socket.
2. During an active broadcast round (grp->bc_ackers > 0), the sender
transmits packet S and advances grp->bc_snd_nxt to S + 1. Receivers
increment their expected counter to S + 1 upon consuming packet S,
so the only valid ACK value for the current round is strictly
acked == grp->bc_snd_nxt.
However, tipc_group_update_bc_members() initializes each member's
m->bc_acked to prev = grp->bc_snd_nxt - 1 (S - 1 before increment).
This leaves a 2-sequence gap (S - 1 to S + 1) in sequence space.
An incoming ACK is therefore neither rejected as duplicate nor
prevented from decrementing grp->bc_ackers if an unexpected or stale
value (such as S) is received. A member sending acked = S followed
by acked = S + 1 could decrement grp->bc_ackers twice in the same
round, prematurely clearing bc_ackers or underflowing it.
Fix this by:
- Dropping GRP_ACK_MSG immediately if grp->bc_ackers is zero.
- Requiring acked == grp->bc_snd_nxt and rejecting duplicates where
m->bc_acked == acked. Because replicast broadcast rounds are strictly
sequential, only grp->bc_snd_nxt can be acknowledged, and each member
can acknowledge at most once per round.
Note that a related pre-existing issue in tipc_group_delete_member()
(where grp->bc_ackers decrementing to zero upon member departure does
not restore *grp->open or trigger a socket wakeup) will be addressed
in a separate patch.
Fixes: 48a5fe38772b ("tipc: fix bc_ackers underflow on duplicate GRP_ACK_MSG")
Fixes: 2f487712b893 ("tipc: guarantee that group broadcast doesn't bypass group unicast")
Reported-by: James Burton <jamesburton@meta.com>
Cc: stable@vger.kernel.org
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
Cc: Oleh Konko <security@1seal.org>
Cc: Tung Nguyen <tung.quang.nguyen@est.tech>
Cc: Jon Maloy <jmaloy@redhat.com>
---
net/tipc/group.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/tipc/group.c b/net/tipc/group.c
index 14e6732624e28edc20ff80163787bca3018cb2dd..74f6d3dac0784d5a54db3e2202ead3878d8df9e2 100644
--- a/net/tipc/group.c
+++ b/net/tipc/group.c
@@ -797,10 +797,10 @@ void tipc_group_proto_rcv(struct tipc_group *grp, bool *usr_wakeup,
tipc_group_open(m, usr_wakeup);
return;
case GRP_ACK_MSG:
- if (!m)
+ if (!m || !grp->bc_ackers)
return;
acked = msg_grp_bc_acked(hdr);
- if (less_eq(acked, m->bc_acked))
+ if (acked != grp->bc_snd_nxt || m->bc_acked == acked)
return;
m->bc_acked = acked;
if (--grp->bc_ackers)
--
2.55.0.1007.g17ff1f9808-goog
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [PATCH net] tipc: reject invalid and unexpected GRP_ACK_MSG to prevent bc_ackers underflow 2026-09-13 4:42 [PATCH net] tipc: reject invalid and unexpected GRP_ACK_MSG to prevent bc_ackers underflow Eric Dumazet @ 2026-09-14 4:52 ` netdev-bot+sashiko 2026-09-17 8:09 ` Paolo Abeni 2026-09-18 22:50 ` patchwork-bot+netdevbpf 1 sibling, 1 reply; 4+ messages in thread From: netdev-bot+sashiko @ 2026-09-14 4:52 UTC (permalink / raw) To: edumazet Cc: davem, kuba, pabeni, horms, netdev, eric.dumazet, jamesburton, stable, security, tung.quang.nguyen, jmaloy Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: Critical: 0 · High: 0 · Medium: 1 (1) · Low: 0 - [Medium] Incomplete ACK accounting in tipc_group_proto_rcv() (net/tipc/group.c:799-808): the new validation gates the decrement of… Pre-existing issues: - [Medium] Pre-existing (not introduced or claimed by this patch): in tipc_send_group_bcast() (net/tipc/socket.c:1131-1137) the packets… -- tipc: reject invalid and unexpected GRP_ACK_MSG to prevent bc_ackers underflow This tightens the GRP_ACK_MSG path in tipc_group_proto_rcv() so that the message is dropped when grp->bc_ackers is zero, and so that only an ACK whose value equals grp->bc_snd_nxt (and which is not a repeat of the member's own last ACK) is allowed to decrement grp->bc_ackers. > diff --git a/net/tipc/group.c b/net/tipc/group.c > index 14e6732624e28..74f6d3dac0784 100644 > --- a/net/tipc/group.c > +++ b/net/tipc/group.c > @@ -797,10 +797,10 @@ void tipc_group_proto_rcv(struct tipc_group *grp, bool *usr_wakeup, > tipc_group_open(m, usr_wakeup); > return; > case GRP_ACK_MSG: > - if (!m) > + if (!m || !grp->bc_ackers) > return; > acked = msg_grp_bc_acked(hdr); > - if (less_eq(acked, m->bc_acked)) > + if (acked != grp->bc_snd_nxt || m->bc_acked == acked) > return; > m->bc_acked = acked; > if (--grp->bc_ackers) [Severity: Medium] The new test gates the decrement on the sequence value and on per-member duplication. Does it also need to check that the acking member was one of the members actually counted into grp->bc_ackers for the current round? tipc_group_update_bc_members() counts and re-arms receivers only: net/tipc/group.c:tipc_group_update_bc_members() { u16 prev = grp->bc_snd_nxt - 1; ... for (n = rb_first(&grp->members); n; n = rb_next(n)) { m = container_of(n, struct tipc_member, tree_node); if (tipc_group_is_receiver(m)) { tipc_group_update_member(m, len); m->bc_acked = prev; ackers++; } } /* Mark number of acknowledges to expect, if any */ if (ack) grp->bc_ackers = ackers; grp->bc_snd_nxt++; } A member in MBR_JOINING or MBR_LEAVING is therefore neither summed into ackers nor re-armed, so it keeps a stale m->bc_acked. The same holds for a member created after the round was published, since tipc_group_create_member() sets: m->bc_acked = grp->bc_snd_nxt - 1; In both cases m->bc_acked differs from grp->bc_snd_nxt, so a GRP_ACK_MSG from that member passes both new conditions and still executes --grp->bc_ackers although it contributed no increment. The lookup on this path, tipc_group_find_member(), applies no state filter. Can that let grp->bc_ackers reach zero while a counted receiver still owes an ACK? The number of accepted decrements per round looks bounded by grp->member_cnt rather than by the counted acker set. If bc_ackers is cleared early, tipc_group_bc_cong() stops reporting congestion: net/tipc/group.c:tipc_group_bc_cong() { /* If prev bcast was replicast, reject until all receivers have acked */ if (grp->bc_ackers) { *grp->open = false; return true; } so the sender releases the next group broadcast while the previous replicast round is still outstanding, which is the ordering guarantee from 2f487712b893, one of the Fixes tags on this patch. The zero-transition branch also runs list_del_init(&m->small_win) and *m->group->open = true for the acking member, which need not be a receiver, and the genuine acker's later ACK is then swallowed by the new !grp->bc_ackers guard, so its small_win / tipc_group_update_member() bookkeeping never runs. The commit message states that "each member can acknowledge at most once per round" - that part holds per member, but does it cover members that were never counted for the round? Would a per-round marker set in tipc_group_update_bc_members(), or accepting only members whose m->bc_acked == grp->bc_snd_nxt - 2, close the remaining gap? The u16 wrap to 65535 itself does look closed by the new !grp->bc_ackers guard, so what is left here is the premature clearing of the counter that item 2 of the commit message also mentions. [Severity: Medium] This is a pre-existing issue and was not introduced by this patch - the patch strictly improves the path - but the same permanent congestion is still reachable from tipc_send_group_bcast() in net/tipc/socket.c, where the packets are transmitted before the round state is published: net/tipc/socket.c:tipc_send_group_bcast() { /* Send message */ rc = tipc_mcast_xmit(net, &pkts, method, dsts, &tsk->cong_link_cnt); if (unlikely(rc)) return rc; /* Update broadcast sequence number and send windows */ tipc_group_update_bc_members(tsk->group, blks, ack); } The error return skips the publication entirely, and the failure can happen after partial delivery: net/tipc/bcast.c:tipc_rcast_xmit() { list_for_each_entry_safe(dst, tmp, &dests->list, list) { dnode = dst->node; if (!tipc_msg_pskb_copy(dnode, pkts, &_pkts)) return -ENOMEM; if (tipc_node_xmit(net, &_pkts, dnode, selector) == -ELINKCONG) (*cong_link_cnt)++; } } Peers that already received seqno S advance m->bc_rcv_nxt to S + 1 while the sender's grp->bc_snd_nxt stays at S. The next broadcast reuses S, and those peers drop it at the head of tipc_group_filter_msg(): if (less(msg_grp_bc_seqno(hdr), m->bc_rcv_nxt)) goto drop; which is before ack = msg_grp_bc_ack_req(hdr) is evaluated, so they can never emit the ACK the sender is waiting for and grp->bc_ackers never returns to zero. tipc_group_bc_cong() then keeps *grp->open false for the lifetime of that socket. Before this patch the same desynchronization additionally wrapped grp->bc_ackers through the unguarded --grp->bc_ackers, so this change does remove that part. Should the round state be published atomically with the transmission, or rolled back when tipc_mcast_xmit() fails after partial delivery? -- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260913044233.193927-1-edumazet%40google.com ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net] tipc: reject invalid and unexpected GRP_ACK_MSG to prevent bc_ackers underflow 2026-09-14 4:52 ` netdev-bot+sashiko @ 2026-09-17 8:09 ` Paolo Abeni 0 siblings, 0 replies; 4+ messages in thread From: Paolo Abeni @ 2026-09-17 8:09 UTC (permalink / raw) To: edumazet, tung.quang.nguyen Cc: davem, kuba, horms, netdev, eric.dumazet, jamesburton, stable, security, jmaloy, netdev-bot+sashiko On 9/14/26 06:52, netdev-bot+sashiko@kernel.org wrote: > Thank you for your contribution! Sashiko AI review found 1 potential > issue(s) to consider: > > Critical: 0 · High: 0 · Medium: 1 (1) · Low: 0 > > - [Medium] Incomplete ACK accounting in tipc_group_proto_rcv() > (net/tipc/group.c:799-808): the new validation gates the decrement of… > > Pre-existing issues: > - [Medium] Pre-existing (not introduced or claimed by this patch): in > tipc_send_group_bcast() (net/tipc/socket.c:1131-1137) the packets… > > -- > > tipc: reject invalid and unexpected GRP_ACK_MSG to prevent bc_ackers underflow > > This tightens the GRP_ACK_MSG path in tipc_group_proto_rcv() so that the > message is dropped when grp->bc_ackers is zero, and so that only an ACK > whose value equals grp->bc_snd_nxt (and which is not a repeat of the > member's own last ACK) is allowed to decrement grp->bc_ackers. > >> diff --git a/net/tipc/group.c b/net/tipc/group.c >> index 14e6732624e28..74f6d3dac0784 100644 >> --- a/net/tipc/group.c >> +++ b/net/tipc/group.c >> @@ -797,10 +797,10 @@ void tipc_group_proto_rcv(struct tipc_group *grp, bool *usr_wakeup, >> tipc_group_open(m, usr_wakeup); >> return; >> case GRP_ACK_MSG: >> - if (!m) >> + if (!m || !grp->bc_ackers) >> return; >> acked = msg_grp_bc_acked(hdr); >> - if (less_eq(acked, m->bc_acked)) >> + if (acked != grp->bc_snd_nxt || m->bc_acked == acked) >> return; >> m->bc_acked = acked; >> if (--grp->bc_ackers) > > [Severity: Medium] > The new test gates the decrement on the sequence value and on per-member > duplication. Does it also need to check that the acking member was one of > the members actually counted into grp->bc_ackers for the current round? > > tipc_group_update_bc_members() counts and re-arms receivers only: > > net/tipc/group.c:tipc_group_update_bc_members() { > u16 prev = grp->bc_snd_nxt - 1; > ... > for (n = rb_first(&grp->members); n; n = rb_next(n)) { > m = container_of(n, struct tipc_member, tree_node); > if (tipc_group_is_receiver(m)) { > tipc_group_update_member(m, len); > m->bc_acked = prev; > ackers++; > } > } > > /* Mark number of acknowledges to expect, if any */ > if (ack) > grp->bc_ackers = ackers; > grp->bc_snd_nxt++; > } > > A member in MBR_JOINING or MBR_LEAVING is therefore neither summed into > ackers nor re-armed, so it keeps a stale m->bc_acked. The same holds for a > member created after the round was published, since > tipc_group_create_member() sets: > > m->bc_acked = grp->bc_snd_nxt - 1; > > In both cases m->bc_acked differs from grp->bc_snd_nxt, so a GRP_ACK_MSG > from that member passes both new conditions and still executes > --grp->bc_ackers although it contributed no increment. The lookup on this > path, tipc_group_find_member(), applies no state filter. > > Can that let grp->bc_ackers reach zero while a counted receiver still owes > an ACK? The number of accepted decrements per round looks bounded by > grp->member_cnt rather than by the counted acker set. > > If bc_ackers is cleared early, tipc_group_bc_cong() stops reporting > congestion: > > net/tipc/group.c:tipc_group_bc_cong() { > /* If prev bcast was replicast, reject until all receivers have acked */ > if (grp->bc_ackers) { > *grp->open = false; > return true; > } > > so the sender releases the next group broadcast while the previous replicast > round is still outstanding, which is the ordering guarantee from > 2f487712b893, one of the Fixes tags on this patch. The zero-transition > branch also runs list_del_init(&m->small_win) and *m->group->open = true for > the acking member, which need not be a receiver, and the genuine acker's > later ACK is then swallowed by the new !grp->bc_ackers guard, so its > small_win / tipc_group_update_member() bookkeeping never runs. > > The commit message states that "each member can acknowledge at most once per > round" - that part holds per member, but does it cover members that were > never counted for the round? > > Would a per-round marker set in tipc_group_update_bc_members(), or accepting > only members whose m->bc_acked == grp->bc_snd_nxt - 2, close the remaining > gap? > > The u16 wrap to 65535 itself does look closed by the new !grp->bc_ackers > guard, so what is left here is the premature clearing of the counter that > item 2 of the commit message also mentions. My reading of the above is that it's a slightly different problem, that could/should deserve a separate patch. WDYT? /P ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net] tipc: reject invalid and unexpected GRP_ACK_MSG to prevent bc_ackers underflow 2026-09-13 4:42 [PATCH net] tipc: reject invalid and unexpected GRP_ACK_MSG to prevent bc_ackers underflow Eric Dumazet 2026-09-14 4:52 ` netdev-bot+sashiko @ 2026-09-18 22:50 ` patchwork-bot+netdevbpf 1 sibling, 0 replies; 4+ messages in thread From: patchwork-bot+netdevbpf @ 2026-09-18 22:50 UTC (permalink / raw) To: Eric Dumazet Cc: davem, kuba, pabeni, horms, netdev, eric.dumazet, jamesburton, stable, security, tung.quang.nguyen, jmaloy Hello: This patch was applied to netdev/net.git (main) by Jakub Kicinski <kuba@kernel.org>: On Sun, 13 Sep 2026 04:42:33 +0000 you wrote: > Commit 48a5fe38772b ("tipc: fix bc_ackers underflow on duplicate > GRP_ACK_MSG") rejected duplicate/stale ACKs in tipc_group_proto_rcv() > by returning early when less_eq(acked, m->bc_acked). > > However, that check remains incomplete in two ways: > > 1. When grp->bc_ackers is zero (e.g. on a quiet group, when replicast > ACKs were not requested, or after all expected members have already > acknowledged), an unexpected GRP_ACK_MSG with acked > m->bc_acked > passes less_eq() and unconditionally decrements grp->bc_ackers. > Because bc_ackers is a u16, this wraps to 65535, causing > tipc_group_bc_cong() to permanently report congestion and blocking > all future group broadcasts on the socket. > > [...] Here is the summary with links: - [net] tipc: reject invalid and unexpected GRP_ACK_MSG to prevent bc_ackers underflow https://git.kernel.org/netdev/net/c/99cc2a62e07a You are awesome, thank you! -- Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-18 22:51 UTC | newest] Thread overview: 4+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-09-13 4:42 [PATCH net] tipc: reject invalid and unexpected GRP_ACK_MSG to prevent bc_ackers underflow Eric Dumazet 2026-09-14 4:52 ` netdev-bot+sashiko 2026-09-17 8:09 ` Paolo Abeni 2026-09-18 22:50 ` patchwork-bot+netdevbpf
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox