bonding: prevent potential infinite loop in bond_header_parse()

bond_header_parse() can loop if a stack of two bonding devices is setup,
because skb->dev always points to the hierarchy top.

Add new "const struct net_device *dev" parameter to
(struct header_ops)->parse() method to make sure the recursion
is bounded, and that the final leaf parse method is called.

Fixes: 950803f725 ("bonding: fix type confusion in bond_setup_by_slave()")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Jiayuan Chen <jiayuan.chen@shopee.com>
Tested-by: Jiayuan Chen <jiayuan.chen@shopee.com>
Cc: Jay Vosburgh <jv@jvosburgh.net>
Cc: Andrew Lunn <andrew+netdev@lunn.ch>
Link: https://patch.msgid.link/20260315104152.1436867-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Eric Dumazet
2026-03-15 10:41:52 +00:00
committed by Jakub Kicinski
parent 43d222fbcd
commit b7405dcf73
9 changed files with 28 additions and 18 deletions

View File

@@ -257,9 +257,10 @@ static void fwnet_header_cache_update(struct hh_cache *hh,
memcpy((u8 *)hh->hh_data + HH_DATA_OFF(FWNET_HLEN), haddr, net->addr_len);
}
static int fwnet_header_parse(const struct sk_buff *skb, unsigned char *haddr)
static int fwnet_header_parse(const struct sk_buff *skb, const struct net_device *dev,
unsigned char *haddr)
{
memcpy(haddr, skb->dev->dev_addr, FWNET_ALEN);
memcpy(haddr, dev->dev_addr, FWNET_ALEN);
return FWNET_ALEN;
}

View File

@@ -1530,9 +1530,11 @@ static int bond_header_create(struct sk_buff *skb, struct net_device *bond_dev,
return ret;
}
static int bond_header_parse(const struct sk_buff *skb, unsigned char *haddr)
static int bond_header_parse(const struct sk_buff *skb,
const struct net_device *dev,
unsigned char *haddr)
{
struct bonding *bond = netdev_priv(skb->dev);
struct bonding *bond = netdev_priv(dev);
const struct header_ops *slave_ops;
struct slave *slave;
int ret = 0;
@@ -1542,7 +1544,7 @@ static int bond_header_parse(const struct sk_buff *skb, unsigned char *haddr)
if (slave) {
slave_ops = READ_ONCE(slave->dev->header_ops);
if (slave_ops && slave_ops->parse)
ret = slave_ops->parse(skb, haddr);
ret = slave_ops->parse(skb, slave->dev, haddr);
}
rcu_read_unlock();
return ret;

View File

@@ -42,7 +42,8 @@ extern const struct header_ops eth_header_ops;
int eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
const void *daddr, const void *saddr, unsigned len);
int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr);
int eth_header_parse(const struct sk_buff *skb, const struct net_device *dev,
unsigned char *haddr);
int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh,
__be16 type);
void eth_header_cache_update(struct hh_cache *hh, const struct net_device *dev,

View File

@@ -40,7 +40,8 @@ static inline struct ethhdr *inner_eth_hdr(const struct sk_buff *skb)
return (struct ethhdr *)skb_inner_mac_header(skb);
}
int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr);
int eth_header_parse(const struct sk_buff *skb, const struct net_device *dev,
unsigned char *haddr);
extern ssize_t sysfs_format_mac(char *buf, const unsigned char *addr, int len);

View File

@@ -311,7 +311,9 @@ struct header_ops {
int (*create) (struct sk_buff *skb, struct net_device *dev,
unsigned short type, const void *daddr,
const void *saddr, unsigned int len);
int (*parse)(const struct sk_buff *skb, unsigned char *haddr);
int (*parse)(const struct sk_buff *skb,
const struct net_device *dev,
unsigned char *haddr);
int (*cache)(const struct neighbour *neigh, struct hh_cache *hh, __be16 type);
void (*cache_update)(struct hh_cache *hh,
const struct net_device *dev,
@@ -3445,7 +3447,7 @@ static inline int dev_parse_header(const struct sk_buff *skb,
if (!dev->header_ops || !dev->header_ops->parse)
return 0;
return dev->header_ops->parse(skb, haddr);
return dev->header_ops->parse(skb, dev, haddr);
}
static inline __be16 dev_parse_header_protocol(const struct sk_buff *skb)

View File

@@ -193,14 +193,11 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
}
EXPORT_SYMBOL(eth_type_trans);
/**
* eth_header_parse - extract hardware address from packet
* @skb: packet to extract header from
* @haddr: destination buffer
*/
int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr)
int eth_header_parse(const struct sk_buff *skb, const struct net_device *dev,
unsigned char *haddr)
{
const struct ethhdr *eth = eth_hdr(skb);
memcpy(haddr, eth->h_source, ETH_ALEN);
return ETH_ALEN;
}

View File

@@ -919,7 +919,8 @@ static int ipgre_header(struct sk_buff *skb, struct net_device *dev,
return -(t->hlen + sizeof(*iph));
}
static int ipgre_header_parse(const struct sk_buff *skb, unsigned char *haddr)
static int ipgre_header_parse(const struct sk_buff *skb, const struct net_device *dev,
unsigned char *haddr)
{
const struct iphdr *iph = (const struct iphdr *) skb_mac_header(skb);
memcpy(haddr, &iph->saddr, 4);

View File

@@ -469,7 +469,9 @@ static int mac802154_header_create(struct sk_buff *skb,
}
static int
mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
mac802154_header_parse(const struct sk_buff *skb,
const struct net_device *dev,
unsigned char *haddr)
{
struct ieee802154_hdr hdr;

View File

@@ -129,9 +129,12 @@ static int pn_header_create(struct sk_buff *skb, struct net_device *dev,
return 1;
}
static int pn_header_parse(const struct sk_buff *skb, unsigned char *haddr)
static int pn_header_parse(const struct sk_buff *skb,
const struct net_device *dev,
unsigned char *haddr)
{
const u8 *media = skb_mac_header(skb);
*haddr = *media;
return 1;
}