All of lore.kernel.org
 help / color / mirror / Atom feed
From: netdev-bot+sashiko@kernel.org
To: edumazet@google.com
Cc: davem@davemloft.net, kuba@kernel.org, pabeni@redhat.com,
	horms@kernel.org, andrew+netdev@lunn.ch, netdev@vger.kernel.org,
	eric.dumazet@gmail.com, jv@jvosburgh.net
Subject: Re: [net] bonding: use skb_cow_head() in bond_do_alb_xmit() and rlb_arp_xmit()
Date: Thu, 03 Sep 2026 14:09:35 +0000	[thread overview]
Message-ID: <178844457550.3394541.1019645216778443244@kernel.org> (raw)
In-Reply-To: <20260902125809.2910366-1-edumazet@google.com>

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

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

- [Medium] In `rlb_arp_xmit()` (drivers/net/bonding/bond_alb.c:681) the
  patch adds `tx_slave != rcu_access_pointer(bond->curr_active_slave)`…

--

bonding: use skb_cow_head() in bond_do_alb_xmit() and rlb_arp_xmit()

This adds skb_cow_head() before rewriting the source MAC in the Ethernet
header and in the ARP payload, stops caching header pointers across the
cow, and switches to skb_eth_hdr() so the mac header offset is not assumed
to be set in the output path.  It also adds a new condition so that
arp->mac_src is only rewritten when tx_slave is not the current active
slave.

> diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
> index 654f051d0023..5f750f08a144 100644
> --- a/drivers/net/bonding/bond_alb.c
> +++ b/drivers/net/bonding/bond_alb.c
> @@ -678,9 +678,13 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond)
>  	if (arp->op_code == htons(ARPOP_REPLY)) {
>  		/* the arp must be sent on the selected rx channel */
>  		tx_slave = rlb_choose_channel(skb, bond, arp);
> -		if (tx_slave)
> +		if (tx_slave && tx_slave != rcu_access_pointer(bond->curr_active_slave)) {
> +			if (unlikely(skb_cow_head(skb, 0)))
> +				return NULL;
> +			arp = (struct arp_pkt *)skb_network_header(skb);
>  			bond_hw_addr_copy(arp->mac_src, tx_slave->dev->dev_addr,
>  					  tx_slave->dev->addr_len);
> +		}

[Severity: Medium]
The changelog states:

  "When tx_slave is the active slave, arp->mac_src already matches its
  hardware address, so we can avoid an unnecessary copy and head
  reallocation."

Is that invariant actually established anywhere in rlb_arp_xmit()?  The only
check applied to arp->mac_src earlier in the function is:

  if (!bond_slave_has_mac_rcu(bond, arp->mac_src))
          return NULL;

and bond_slave_has_mac_rcu() in include/net/bonding.h matches any slave, not
just the active one:

  bond_for_each_slave_rcu(bond, tmp, iter)
          if (ether_addr_equal_64bits(mac, tmp->dev->dev_addr))
                  return true;
  return false;

In balance-alb each slave has a distinct MAC, so an ARP reply whose sender
MAC is a non-active slave's MAC passes this filter (for example an injected
reply via AF_PACKET with CAP_NET_RAW, or an upper VLAN whose MAC was set to a
slave MAC).

Can rlb_choose_channel() then return curr_active_slave for such a packet?  It
picks the channel independently of arp->mac_src, either via the existing
client entry:

  assigned_slave = client_info->slave;
  if (assigned_slave) {
          spin_unlock(&bond->mode_lock);
          return assigned_slave;
  }

or via __rlb_next_rx_slave(), which walks all transmitting slaves including
the active one:

  bond_for_each_slave_rcu(bond, slave, iter) {
          if (!bond_slave_can_tx(slave))
                  continue;

With the new gate, the rewrite is skipped in that case, so the ARP reply goes
out advertising a MAC that is not the RX channel just recorded in
client_info->slave.  Does the peer then steer return traffic to a slave the
RLB accounting believes is unassigned?  The early return above also leaves
client_info->ntt unset, so no corrective rlb_update_client() ARP is queued.

Note that rlb_update_client(), the other emitter of this field,
unconditionally advertises the assigned slave's address:

  skb = arp_create(ARPOP_REPLY, ETH_P_ARP,
                   client_info->ip_dst,
                   client_info->slave->dev,
                   client_info->ip_src,
                   client_info->mac_dst,
                   client_info->slave->dev->dev_addr,
                   client_info->mac_dst);

so the two paths now disagree about what mac_src should contain.

There is also a failover window.  The comparison is done under
rcu_read_lock() only, since bond->mode_lock is dropped inside
rlb_choose_channel(), while bond_alb_handle_active_change() publishes the new
active slave before the MAC swap:

  /* curr_active_slave must be set before calling alb_swap_mac_addr */
  if (swap_slave) {
          /* swap mac address */
          alb_swap_mac_addr(swap_slave, new_slave);

During that window, can tx_slave == curr_active_slave hold while the slave
MACs have not been swapped yet, making the skipped rewrite wrong even for a
normally generated ARP reply?

Would a value comparison be a safer way to express this optimization, for
example:

  if (tx_slave &&
      !ether_addr_equal_64bits(arp->mac_src, tx_slave->dev->dev_addr)) {

That keeps the fast path when the field already holds the right address
without relying on the pointer identity of curr_active_slave.

[ ... ]

> @@ -1351,7 +1354,9 @@ static netdev_tx_t bond_do_alb_xmit(struct sk_buff *skb, struct bonding *bond,
>  
>  	if (tx_slave && bond_slave_can_tx(tx_slave)) {
>  		if (tx_slave != rcu_access_pointer(bond->curr_active_slave)) {
> -			ether_addr_copy(eth_data->h_source,
> +			if (unlikely(skb_cow_head(skb, 0)))
> +				return bond_tx_drop(bond->dev, skb);
> +			ether_addr_copy(skb_eth_hdr(skb)->h_source,
>  					tx_slave->dev->dev_addr);
>  		}

Related to the above: this is the same pointer comparison, evaluated later in
the transmit path for the same skb.  If curr_active_slave changes between the
check in rlb_arp_xmit() and this one, can the Ethernet source be rewritten
here while arp->mac_src was left untouched (or the reverse), leaving the two
headers inconsistent with each other?

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260902125809.2910366-1-edumazet%40google.com

  parent reply	other threads:[~2026-09-03 14:09 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-02 12:58 [PATCH net] bonding: use skb_cow_head() in bond_do_alb_xmit() and rlb_arp_xmit() Eric Dumazet
2026-09-03  9:42 ` Hangbin Liu
2026-09-03 14:09 ` netdev-bot+sashiko [this message]
2026-09-03 14:17   ` [net] " Eric Dumazet

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=178844457550.3394541.1019645216778443244@kernel.org \
    --to=netdev-bot+sashiko@kernel.org \
    --cc=andrew+netdev@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=eric.dumazet@gmail.com \
    --cc=horms@kernel.org \
    --cc=jv@jvosburgh.net \
    --cc=kuba@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.