public inbox for stable@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 6.12.y] net: bonding: fix use-after-free in bond_xmit_broadcast()
@ 2026-04-13 21:34 Chenglong Tang
  2026-04-13 21:54 ` Xiang Mei
  0 siblings, 1 reply; 6+ messages in thread
From: Chenglong Tang @ 2026-04-13 21:34 UTC (permalink / raw)
  To: stable
  Cc: kpberry, rnj, joneslee, Chenglong Tang, Weiming Shi, Xiang Mei,
	Paolo Abeni

commit 2884bf72fb8f03409e423397319205de48adca16 upstream.

bond_xmit_broadcast() reuses the original skb for the last slave
(determined by bond_is_last_slave()) and clones it for others.
Concurrent slave enslave/release can mutate the slave list during
RCU-protected iteration, changing which slave is "last" mid-loop.
This causes the original skb to be double-consumed (double-freed).

Replace the racy bond_is_last_slave() check with a simple index
comparison (i + 1 == slaves_count) against the pre-snapshot slave
count taken via READ_ONCE() before the loop.  This preserves the
zero-copy optimization for the last slave while making the "last"
determination stable against concurrent list mutations.

The UAF can trigger the following crash:

==================================================================
BUG: KASAN: slab-use-after-free in skb_clone
Read of size 8 at addr ffff888100ef8d40 by task exploit/147

CPU: 1 UID: 0 PID: 147 Comm: exploit Not tainted 7.0.0-rc3+ #4 PREEMPTLAZY
Call Trace:
 <TASK>
 dump_stack_lvl (lib/dump_stack.c:123)
 print_report (mm/kasan/report.c:379 mm/kasan/report.c:482)
 kasan_report (mm/kasan/report.c:597)
 skb_clone (include/linux/skbuff.h:1724 include/linux/skbuff.h:1792 include/linux/skbuff.h:3396 net/core/skbuff.c:2108)
 bond_xmit_broadcast (drivers/net/bonding/bond_main.c:5334)
 bond_start_xmit (drivers/net/bonding/bond_main.c:5567 drivers/net/bonding/bond_main.c:5593)
 dev_hard_start_xmit (include/linux/netdevice.h:5325 include/linux/netdevice.h:5334 net/core/dev.c:3871 net/core/dev.c:3887)
 __dev_queue_xmit (include/linux/netdevice.h:3601 net/core/dev.c:4838)
 ip6_finish_output2 (include/net/neighbour.h:540 include/net/neighbour.h:554 net/ipv6/ip6_output.c:136)
 ip6_finish_output (net/ipv6/ip6_output.c:208 net/ipv6/ip6_output.c:219)
 ip6_output (net/ipv6/ip6_output.c:250)
 ip6_send_skb (net/ipv6/ip6_output.c:1985)
 udp_v6_send_skb (net/ipv6/udp.c:1442)
 udpv6_sendmsg (net/ipv6/udp.c:1733)
 __sys_sendto (net/socket.c:730 net/socket.c:742 net/socket.c:2206)
 __x64_sys_sendto (net/socket.c:2209)
 do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94)
 entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130)
 </TASK>

Allocated by task 147:

Freed by task 147:

The buggy address belongs to the object at ffff888100ef8c80
 which belongs to the cache skbuff_head_cache of size 224
The buggy address is located 192 bytes inside of
 freed 224-byte region [ffff888100ef8c80, ffff888100ef8d60)

Memory state around the buggy address:
 ffff888100ef8c00: fb fb fb fb fc fc fc fc fc fc fc fc fc fc fc fc
 ffff888100ef8c80: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>ffff888100ef8d00: fb fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc
                                                    ^
 ffff888100ef8d80: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb
 ffff888100ef8e00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================

Fixes: 4e5bd03ae346 ("net: bonding: fix bond_xmit_broadcast return value error bug")
Reported-by: Weiming Shi <bestswngs@gmail.com>
Change-Id: I2349f4953b5760b7f4a12da583aa779c19c4b59c
Signed-off-by: Xiang Mei <xmei5@asu.edu>
Link: https://patch.msgid.link/20260326075553.3960562-1-xmei5@asu.edu
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
[Kevin Berry <kpberry@google.com>: fixed merge conflicts and adapted
to 6.12 struct]
Signed-off-by: Chenglong Tang <chenglongtang@google.com>
---
 drivers/net/bonding/bond_main.c | 24 +++++++++++++++++-------
 1 file changed, 17 insertions(+), 7 deletions(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 2ac455a9d1bb..fb8d7fec27ee 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -5346,23 +5346,33 @@ static netdev_tx_t bond_3ad_xor_xmit(struct sk_buff *skb,
 	return bond_tx_drop(dev, skb);
 }
 
-/* in broadcast mode, we send everything to all usable interfaces. */
+/* in broadcast mode, we send everything to all or usable slave interfaces.
+ * under rcu_read_lock when this function is called.
+ */
 static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb,
-				       struct net_device *bond_dev)
+				       struct net_device *bond_dev,
+				       bool all_slaves)
 {
 	struct bonding *bond = netdev_priv(bond_dev);
-	struct slave *slave = NULL;
-	struct list_head *iter;
+	struct bond_up_slave *slaves;
 	bool xmit_suc = false;
 	bool skb_used = false;
+	int slaves_count, i;
 
-	bond_for_each_slave_rcu(bond, slave, iter) {
+	if (all_slaves)
+		slaves = rcu_dereference(bond->all_slaves);
+	else
+		slaves = rcu_dereference(bond->usable_slaves);
+
+	slaves_count = slaves ? READ_ONCE(slaves->count) : 0;
+	for (i = 0; i < slaves_count; i++) {
+		struct slave *slave = slaves->arr[i];
 		struct sk_buff *skb2;
 
 		if (!(bond_slave_is_up(slave) && slave->link == BOND_LINK_UP))
 			continue;
 
-		if (bond_is_last_slave(bond, slave)) {
+		if (i + 1 == slaves_count) {
 			skb2 = skb;
 			skb_used = true;
 		} else {
@@ -5597,7 +5607,7 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev
 	case BOND_MODE_XOR:
 		return bond_3ad_xor_xmit(skb, dev);
 	case BOND_MODE_BROADCAST:
-		return bond_xmit_broadcast(skb, dev);
+		return bond_xmit_broadcast(skb, dev, true);
 	case BOND_MODE_ALB:
 		return bond_alb_xmit(skb, dev);
 	case BOND_MODE_TLB:
-- 
2.54.0.rc0.605.g598a273b03-goog


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

* Re: [PATCH 6.12.y] net: bonding: fix use-after-free in bond_xmit_broadcast()
  2026-04-13 21:34 [PATCH 6.12.y] net: bonding: fix use-after-free in bond_xmit_broadcast() Chenglong Tang
@ 2026-04-13 21:54 ` Xiang Mei
  2026-04-27 18:42   ` Kevin Berry
  0 siblings, 1 reply; 6+ messages in thread
From: Xiang Mei @ 2026-04-13 21:54 UTC (permalink / raw)
  To: Chenglong Tang; +Cc: stable, kpberry, rnj, joneslee, Weiming Shi, Paolo Abeni

Hi Chenglong,

I’m not entirely clear on how PATCH 6.12.y works and had a question
regarding this patch.
Could you please clarify the need for the parameter bool all_slaves?
As this feature (was introduced in ce7a381697cb) is not introduced in
v6.12, I was wondering if it might be sufficient to apply my original
patch instead. I’ve also checked against 6.12.81 and didn’t encounter
any conflicts.

Thanks,
Xiang

On Mon, Apr 13, 2026 at 2:35 PM Chenglong Tang <chenglongtang@google.com> wrote:
>
> commit 2884bf72fb8f03409e423397319205de48adca16 upstream.
>
> bond_xmit_broadcast() reuses the original skb for the last slave
> (determined by bond_is_last_slave()) and clones it for others.
> Concurrent slave enslave/release can mutate the slave list during
> RCU-protected iteration, changing which slave is "last" mid-loop.
> This causes the original skb to be double-consumed (double-freed).
>
> Replace the racy bond_is_last_slave() check with a simple index
> comparison (i + 1 == slaves_count) against the pre-snapshot slave
> count taken via READ_ONCE() before the loop.  This preserves the
> zero-copy optimization for the last slave while making the "last"
> determination stable against concurrent list mutations.
>
> The UAF can trigger the following crash:
>
> ==================================================================
> BUG: KASAN: slab-use-after-free in skb_clone
> Read of size 8 at addr ffff888100ef8d40 by task exploit/147
>
> CPU: 1 UID: 0 PID: 147 Comm: exploit Not tainted 7.0.0-rc3+ #4 PREEMPTLAZY
> Call Trace:
>  <TASK>
>  dump_stack_lvl (lib/dump_stack.c:123)
>  print_report (mm/kasan/report.c:379 mm/kasan/report.c:482)
>  kasan_report (mm/kasan/report.c:597)
>  skb_clone (include/linux/skbuff.h:1724 include/linux/skbuff.h:1792 include/linux/skbuff.h:3396 net/core/skbuff.c:2108)
>  bond_xmit_broadcast (drivers/net/bonding/bond_main.c:5334)
>  bond_start_xmit (drivers/net/bonding/bond_main.c:5567 drivers/net/bonding/bond_main.c:5593)
>  dev_hard_start_xmit (include/linux/netdevice.h:5325 include/linux/netdevice.h:5334 net/core/dev.c:3871 net/core/dev.c:3887)
>  __dev_queue_xmit (include/linux/netdevice.h:3601 net/core/dev.c:4838)
>  ip6_finish_output2 (include/net/neighbour.h:540 include/net/neighbour.h:554 net/ipv6/ip6_output.c:136)
>  ip6_finish_output (net/ipv6/ip6_output.c:208 net/ipv6/ip6_output.c:219)
>  ip6_output (net/ipv6/ip6_output.c:250)
>  ip6_send_skb (net/ipv6/ip6_output.c:1985)
>  udp_v6_send_skb (net/ipv6/udp.c:1442)
>  udpv6_sendmsg (net/ipv6/udp.c:1733)
>  __sys_sendto (net/socket.c:730 net/socket.c:742 net/socket.c:2206)
>  __x64_sys_sendto (net/socket.c:2209)
>  do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94)
>  entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130)
>  </TASK>
>
> Allocated by task 147:
>
> Freed by task 147:
>
> The buggy address belongs to the object at ffff888100ef8c80
>  which belongs to the cache skbuff_head_cache of size 224
> The buggy address is located 192 bytes inside of
>  freed 224-byte region [ffff888100ef8c80, ffff888100ef8d60)
>
> Memory state around the buggy address:
>  ffff888100ef8c00: fb fb fb fb fc fc fc fc fc fc fc fc fc fc fc fc
>  ffff888100ef8c80: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
> >ffff888100ef8d00: fb fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc
>                                                     ^
>  ffff888100ef8d80: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb
>  ffff888100ef8e00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
> ==================================================================
>
> Fixes: 4e5bd03ae346 ("net: bonding: fix bond_xmit_broadcast return value error bug")
> Reported-by: Weiming Shi <bestswngs@gmail.com>
> Change-Id: I2349f4953b5760b7f4a12da583aa779c19c4b59c
> Signed-off-by: Xiang Mei <xmei5@asu.edu>
> Link: https://patch.msgid.link/20260326075553.3960562-1-xmei5@asu.edu
> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
> [Kevin Berry <kpberry@google.com>: fixed merge conflicts and adapted
> to 6.12 struct]
> Signed-off-by: Chenglong Tang <chenglongtang@google.com>
> ---
>  drivers/net/bonding/bond_main.c | 24 +++++++++++++++++-------
>  1 file changed, 17 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
> index 2ac455a9d1bb..fb8d7fec27ee 100644
> --- a/drivers/net/bonding/bond_main.c
> +++ b/drivers/net/bonding/bond_main.c
> @@ -5346,23 +5346,33 @@ static netdev_tx_t bond_3ad_xor_xmit(struct sk_buff *skb,
>         return bond_tx_drop(dev, skb);
>  }
>
> -/* in broadcast mode, we send everything to all usable interfaces. */
> +/* in broadcast mode, we send everything to all or usable slave interfaces.
> + * under rcu_read_lock when this function is called.
> + */
>  static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb,
> -                                      struct net_device *bond_dev)
> +                                      struct net_device *bond_dev,
> +                                      bool all_slaves)
>  {
>         struct bonding *bond = netdev_priv(bond_dev);
> -       struct slave *slave = NULL;
> -       struct list_head *iter;
> +       struct bond_up_slave *slaves;
>         bool xmit_suc = false;
>         bool skb_used = false;
> +       int slaves_count, i;
>
> -       bond_for_each_slave_rcu(bond, slave, iter) {
> +       if (all_slaves)
> +               slaves = rcu_dereference(bond->all_slaves);
> +       else
> +               slaves = rcu_dereference(bond->usable_slaves);
> +
> +       slaves_count = slaves ? READ_ONCE(slaves->count) : 0;
> +       for (i = 0; i < slaves_count; i++) {
> +               struct slave *slave = slaves->arr[i];
>                 struct sk_buff *skb2;
>
>                 if (!(bond_slave_is_up(slave) && slave->link == BOND_LINK_UP))
>                         continue;
>
> -               if (bond_is_last_slave(bond, slave)) {
> +               if (i + 1 == slaves_count) {
>                         skb2 = skb;
>                         skb_used = true;
>                 } else {
> @@ -5597,7 +5607,7 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev
>         case BOND_MODE_XOR:
>                 return bond_3ad_xor_xmit(skb, dev);
>         case BOND_MODE_BROADCAST:
> -               return bond_xmit_broadcast(skb, dev);
> +               return bond_xmit_broadcast(skb, dev, true);
>         case BOND_MODE_ALB:
>                 return bond_alb_xmit(skb, dev);
>         case BOND_MODE_TLB:
> --
> 2.54.0.rc0.605.g598a273b03-goog
>

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

* Re: [PATCH 6.12.y] net: bonding: fix use-after-free in bond_xmit_broadcast()
  2026-04-13 21:54 ` Xiang Mei
@ 2026-04-27 18:42   ` Kevin Berry
  2026-04-27 18:42     ` [PATCH] " Kevin Berry
  0 siblings, 1 reply; 6+ messages in thread
From: Kevin Berry @ 2026-04-27 18:42 UTC (permalink / raw)
  To: xmei5; +Cc: bestswngs, chenglongtang, joneslee, kpberry, pabeni, rnj, stable

Hi Xiang,

> I was wondering if it might be sufficient to apply my original
> patch instead. I’ve also checked against 6.12.81 and didn’t encounter
> any conflicts.

We tried applying your original patch first, but, while it does apply
cleanly, it doesn't compile because the version of bond_xmit_broadcast
in the 6.12 kernel doesn't have the slaves_count variable introduced
in ce7a381697cb.

> Could you please clarify the need for the parameter bool all_slaves?

We had included the all_slaves parameter to try to reduce the chance of
conflicts with any other potential backports from later kernel versions,
but I agree that omitting it is probably better.

I've sent an amended version of the patch which just adds slaves_count,
uses an explicit loop, and applies your original change. Tested the
patch by compiling it.


Thanks,

Kevin

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

* [PATCH] net: bonding: fix use-after-free in bond_xmit_broadcast()
  2026-04-27 18:42   ` Kevin Berry
@ 2026-04-27 18:42     ` Kevin Berry
  2026-04-27 20:07       ` Xiang Mei
  0 siblings, 1 reply; 6+ messages in thread
From: Kevin Berry @ 2026-04-27 18:42 UTC (permalink / raw)
  To: xmei5
  Cc: bestswngs, chenglongtang, joneslee, kpberry, pabeni, rnj, stable,
	Sasha Levin

From: Xiang Mei <xmei5@asu.edu>

[ Upstream commit 2884bf72fb8f03409e423397319205de48adca16 ]

bond_xmit_broadcast() reuses the original skb for the last slave
(determined by bond_is_last_slave()) and clones it for others.
Concurrent slave enslave/release can mutate the slave list during
RCU-protected iteration, changing which slave is "last" mid-loop.
This causes the original skb to be double-consumed (double-freed).

Replace the racy bond_is_last_slave() check with a simple index
comparison (i + 1 == slaves_count) against the pre-snapshot slave
count taken via READ_ONCE() before the loop.  This preserves the
zero-copy optimization for the last slave while making the "last"
determination stable against concurrent list mutations.

The UAF can trigger the following crash:

==================================================================
BUG: KASAN: slab-use-after-free in skb_clone
Read of size 8 at addr ffff888100ef8d40 by task exploit/147

CPU: 1 UID: 0 PID: 147 Comm: exploit Not tainted 7.0.0-rc3+ #4 PREEMPTLAZY
Call Trace:
 <TASK>
 dump_stack_lvl (lib/dump_stack.c:123)
 print_report (mm/kasan/report.c:379 mm/kasan/report.c:482)
 kasan_report (mm/kasan/report.c:597)
 skb_clone (include/linux/skbuff.h:1724 include/linux/skbuff.h:1792 include/linux/skbuff.h:3396 net/core/skbuff.c:2108)
 bond_xmit_broadcast (drivers/net/bonding/bond_main.c:5334)
 bond_start_xmit (drivers/net/bonding/bond_main.c:5567 drivers/net/bonding/bond_main.c:5593)
 dev_hard_start_xmit (include/linux/netdevice.h:5325 include/linux/netdevice.h:5334 net/core/dev.c:3871 net/core/dev.c:3887)
 __dev_queue_xmit (include/linux/netdevice.h:3601 net/core/dev.c:4838)
 ip6_finish_output2 (include/net/neighbour.h:540 include/net/neighbour.h:554 net/ipv6/ip6_output.c:136)
 ip6_finish_output (net/ipv6/ip6_output.c:208 net/ipv6/ip6_output.c:219)
 ip6_output (net/ipv6/ip6_output.c:250)
 ip6_send_skb (net/ipv6/ip6_output.c:1985)
 udp_v6_send_skb (net/ipv6/udp.c:1442)
 udpv6_sendmsg (net/ipv6/udp.c:1733)
 __sys_sendto (net/socket.c:730 net/socket.c:742 net/socket.c:2206)
 __x64_sys_sendto (net/socket.c:2209)
 do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94)
 entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130)
 </TASK>

Allocated by task 147:

Freed by task 147:

The buggy address belongs to the object at ffff888100ef8c80
 which belongs to the cache skbuff_head_cache of size 224
The buggy address is located 192 bytes inside of
 freed 224-byte region [ffff888100ef8c80, ffff888100ef8d60)

Memory state around the buggy address:
 ffff888100ef8c00: fb fb fb fb fc fc fc fc fc fc fc fc fc fc fc fc
 ffff888100ef8c80: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>ffff888100ef8d00: fb fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc
                                                    ^
 ffff888100ef8d80: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb
 ffff888100ef8e00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================

Fixes: 4e5bd03ae346 ("net: bonding: fix bond_xmit_broadcast return value error bug")
Reported-by: Weiming Shi <bestswngs@gmail.com>
Signed-off-by: Xiang Mei <xmei5@asu.edu>
Link: https://patch.msgid.link/20260326075553.3960562-1-xmei5@asu.edu
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Kevin Berry <kpberry@google.com>
---
 drivers/net/bonding/bond_main.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 5035cfa74f1a..20043f1094df 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -5322,18 +5322,22 @@ static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb,
 				       struct net_device *bond_dev)
 {
 	struct bonding *bond = netdev_priv(bond_dev);
-	struct slave *slave = NULL;
-	struct list_head *iter;
+	struct bond_up_slave *slaves;
 	bool xmit_suc = false;
 	bool skb_used = false;
+	int slaves_count, i;
 
-	bond_for_each_slave_rcu(bond, slave, iter) {
+	slaves = rcu_dereference(bond->all_slaves);
+
+	slaves_count = slaves ? READ_ONCE(slaves->count) : 0;
+	for (i = 0; i < slaves_count; i++) {
+		struct slave *slave = slaves->arr[i];
 		struct sk_buff *skb2;
 
 		if (!(bond_slave_is_up(slave) && slave->link == BOND_LINK_UP))
 			continue;
 
-		if (bond_is_last_slave(bond, slave)) {
+		if (i + 1 == slaves_count) {
 			skb2 = skb;
 			skb_used = true;
 		} else {

base-commit: eefc95626b5cb02ea6268d1ae58237768004a60d
-- 
2.54.0.545.g6539524ca2-goog


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

* Re: [PATCH] net: bonding: fix use-after-free in bond_xmit_broadcast()
  2026-04-27 18:42     ` [PATCH] " Kevin Berry
@ 2026-04-27 20:07       ` Xiang Mei
  2026-04-27 21:06         ` Kevin Berry
  0 siblings, 1 reply; 6+ messages in thread
From: Xiang Mei @ 2026-04-27 20:07 UTC (permalink / raw)
  To: Kevin Berry
  Cc: bestswngs, chenglongtang, joneslee, pabeni, rnj, stable,
	Sasha Levin

Hi Kevin,

Thanks for the explanation. I now understand that we are backporting
the patch to 6.12 (I didn't realize it couldn't be backported).

I have one concern about your patch. On 6.12, bond->all_slaves is only
set by bond_update_slave_arr(), which is gated by
bond_mode_can_use_xmit_hash() (i.e., 802.3ad, XOR, TLB, ALB). This
does not include BOND_MODE_BROADCAST, and nothing else initializes it.
As a result, in bond_xmit_broadcast() on 6.12,
rcu_dereference(bond->all_slaves) is NULL, slaves_count is 0, the loop
never executes, the skb is freed, and NET_XMIT_DROP is returned, so it
drops every packet.

This works in mainline because the array-based iteration was
introduced alongside additional changes that ensure all_slaves is
valid in broadcast mode. Those supporting changes are not present in
6.12.

To resolve this, we can either backport the prerequisite series or
apply a smaller, self-contained fix like the one below:
```
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -5326,14 +5326,21 @@ static netdev_tx_t bond_xmit_broadcast(struct
sk_buff *skb,
  struct list_head *iter;
  bool xmit_suc = false;
  bool skb_used = false;
+ int slaves_count, i = 0;

+ slaves_count = READ_ONCE(bond->slave_cnt);
  bond_for_each_slave_rcu(bond, slave, iter) {
  struct sk_buff *skb2;
+ bool is_last;
+
+ if (++i > slaves_count)
+ break;
+ is_last = (i == slaves_count);

  if (!(bond_slave_is_up(slave) && slave->link == BOND_LINK_UP))
  continue;

- if (bond_is_last_slave(bond, slave)) {
+ if (is_last) {
  skb2 = skb;
  skb_used = true;
  } else {
```

Please let me know your thoughts.

Thanks,
Xiang

On Mon, Apr 27, 2026 at 11:42 AM Kevin Berry <kpberry@google.com> wrote:
>
> From: Xiang Mei <xmei5@asu.edu>
>
> [ Upstream commit 2884bf72fb8f03409e423397319205de48adca16 ]
>
> bond_xmit_broadcast() reuses the original skb for the last slave
> (determined by bond_is_last_slave()) and clones it for others.
> Concurrent slave enslave/release can mutate the slave list during
> RCU-protected iteration, changing which slave is "last" mid-loop.
> This causes the original skb to be double-consumed (double-freed).
>
> Replace the racy bond_is_last_slave() check with a simple index
> comparison (i + 1 == slaves_count) against the pre-snapshot slave
> count taken via READ_ONCE() before the loop.  This preserves the
> zero-copy optimization for the last slave while making the "last"
> determination stable against concurrent list mutations.
>
> The UAF can trigger the following crash:
>
> ==================================================================
> BUG: KASAN: slab-use-after-free in skb_clone
> Read of size 8 at addr ffff888100ef8d40 by task exploit/147
>
> CPU: 1 UID: 0 PID: 147 Comm: exploit Not tainted 7.0.0-rc3+ #4 PREEMPTLAZY
> Call Trace:
>  <TASK>
>  dump_stack_lvl (lib/dump_stack.c:123)
>  print_report (mm/kasan/report.c:379 mm/kasan/report.c:482)
>  kasan_report (mm/kasan/report.c:597)
>  skb_clone (include/linux/skbuff.h:1724 include/linux/skbuff.h:1792 include/linux/skbuff.h:3396 net/core/skbuff.c:2108)
>  bond_xmit_broadcast (drivers/net/bonding/bond_main.c:5334)
>  bond_start_xmit (drivers/net/bonding/bond_main.c:5567 drivers/net/bonding/bond_main.c:5593)
>  dev_hard_start_xmit (include/linux/netdevice.h:5325 include/linux/netdevice.h:5334 net/core/dev.c:3871 net/core/dev.c:3887)
>  __dev_queue_xmit (include/linux/netdevice.h:3601 net/core/dev.c:4838)
>  ip6_finish_output2 (include/net/neighbour.h:540 include/net/neighbour.h:554 net/ipv6/ip6_output.c:136)
>  ip6_finish_output (net/ipv6/ip6_output.c:208 net/ipv6/ip6_output.c:219)
>  ip6_output (net/ipv6/ip6_output.c:250)
>  ip6_send_skb (net/ipv6/ip6_output.c:1985)
>  udp_v6_send_skb (net/ipv6/udp.c:1442)
>  udpv6_sendmsg (net/ipv6/udp.c:1733)
>  __sys_sendto (net/socket.c:730 net/socket.c:742 net/socket.c:2206)
>  __x64_sys_sendto (net/socket.c:2209)
>  do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94)
>  entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130)
>  </TASK>
>
> Allocated by task 147:
>
> Freed by task 147:
>
> The buggy address belongs to the object at ffff888100ef8c80
>  which belongs to the cache skbuff_head_cache of size 224
> The buggy address is located 192 bytes inside of
>  freed 224-byte region [ffff888100ef8c80, ffff888100ef8d60)
>
> Memory state around the buggy address:
>  ffff888100ef8c00: fb fb fb fb fc fc fc fc fc fc fc fc fc fc fc fc
>  ffff888100ef8c80: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
> >ffff888100ef8d00: fb fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc
>                                                     ^
>  ffff888100ef8d80: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb
>  ffff888100ef8e00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
> ==================================================================
>
> Fixes: 4e5bd03ae346 ("net: bonding: fix bond_xmit_broadcast return value error bug")
> Reported-by: Weiming Shi <bestswngs@gmail.com>
> Signed-off-by: Xiang Mei <xmei5@asu.edu>
> Link: https://patch.msgid.link/20260326075553.3960562-1-xmei5@asu.edu
> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
> Signed-off-by: Sasha Levin <sashal@kernel.org>
> Signed-off-by: Kevin Berry <kpberry@google.com>
> ---
>  drivers/net/bonding/bond_main.c | 12 ++++++++----
>  1 file changed, 8 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
> index 5035cfa74f1a..20043f1094df 100644
> --- a/drivers/net/bonding/bond_main.c
> +++ b/drivers/net/bonding/bond_main.c
> @@ -5322,18 +5322,22 @@ static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb,
>                                        struct net_device *bond_dev)
>  {
>         struct bonding *bond = netdev_priv(bond_dev);
> -       struct slave *slave = NULL;
> -       struct list_head *iter;
> +       struct bond_up_slave *slaves;
>         bool xmit_suc = false;
>         bool skb_used = false;
> +       int slaves_count, i;
>
> -       bond_for_each_slave_rcu(bond, slave, iter) {
> +       slaves = rcu_dereference(bond->all_slaves);
> +
> +       slaves_count = slaves ? READ_ONCE(slaves->count) : 0;
> +       for (i = 0; i < slaves_count; i++) {
> +               struct slave *slave = slaves->arr[i];
>                 struct sk_buff *skb2;
>
>                 if (!(bond_slave_is_up(slave) && slave->link == BOND_LINK_UP))
>                         continue;
>
> -               if (bond_is_last_slave(bond, slave)) {
> +               if (i + 1 == slaves_count) {
>                         skb2 = skb;
>                         skb_used = true;
>                 } else {
>
> base-commit: eefc95626b5cb02ea6268d1ae58237768004a60d
> --
> 2.54.0.545.g6539524ca2-goog
>

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

* Re: [PATCH] net: bonding: fix use-after-free in bond_xmit_broadcast()
  2026-04-27 20:07       ` Xiang Mei
@ 2026-04-27 21:06         ` Kevin Berry
  0 siblings, 0 replies; 6+ messages in thread
From: Kevin Berry @ 2026-04-27 21:06 UTC (permalink / raw)
  To: Xiang Mei
  Cc: bestswngs, chenglongtang, joneslee, pabeni, rnj, stable,
	Sasha Levin

Hi Xiang,

Thanks for the explanation! I was worried that bond->all_slaves might
have been uninitialized, thanks for confirming that.

Your patch looks good to me, minus the formatting.


Thanks,

Kevin

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

end of thread, other threads:[~2026-04-27 21:06 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-13 21:34 [PATCH 6.12.y] net: bonding: fix use-after-free in bond_xmit_broadcast() Chenglong Tang
2026-04-13 21:54 ` Xiang Mei
2026-04-27 18:42   ` Kevin Berry
2026-04-27 18:42     ` [PATCH] " Kevin Berry
2026-04-27 20:07       ` Xiang Mei
2026-04-27 21:06         ` Kevin Berry

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