public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH net v1] team: fix header_ops type confusion with non-Ethernet ports
@ 2026-03-14  6:23 Jiayuan Chen
  2026-03-14 10:19 ` Eric Dumazet
  0 siblings, 1 reply; 9+ messages in thread
From: Jiayuan Chen @ 2026-03-14  6:23 UTC (permalink / raw)
  To: netdev
  Cc: Jiayuan Chen, syzbot+3d8bc31c45e11450f24c, Jiayuan Chen,
	Jiri Pirko, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, linux-kernel

From: Jiayuan Chen <jiayuan.chen@shopee.com>

Similar to commit 950803f72547 ("bonding: fix type confusion in bond_setup_by_slave()"),
team has the same class of header_ops type confusion.

For non-Ethernet ports, team_setup_by_port() copies port_dev->header_ops
directly. When the team device later calls dev_hard_header()/dev_parse_header(),
these callbacks can run with the team net_device instead of the real lower
device, so netdev_priv(dev) is interpreted as the wrong private type and
can crash.

Fix this by introducing team header_ops wrappers for create/parse, selecting
a team port under RCU, and calling the lower device callbacks with port->dev,
so each callback always sees the correct net_device context.

Reproducer:
set -euxo pipefail
for d in t0 b0 g0 d0; do
ip link del "$d" 2>/dev/null || true
done

modprobe bonding || true
modprobe team || true
modprobe ip_gre || true
modprobe dummy || true
ip link add d0 type dummy
ip addr add 10.10.10.1/24 dev d0
ip link set d0 up
ip link add g0 type gre local 10.10.10.1
ip link add b0 type bond mode active-backup
ip link add t0 type team
ip link set g0 master b0
ip link set b0 master t0
ip link set g0 up
ip link set b0 up
ip link set t0 up

Fixes: 8ff5105a2b9d ("team: add support for queue override by setting queue_id for port")
Reported-by: syzbot+3d8bc31c45e11450f24c@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/all/69b46af7.050a0220.36eb34.000e.GAE@google.com/T/
Cc: Jiayuan Chen <jiayuan.chen@linux.dev>
Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
---
 drivers/net/team/team_core.c | 61 +++++++++++++++++++++++++++++++++++-
 1 file changed, 60 insertions(+), 1 deletion(-)

diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c
index b7282f5c9632..737bff5bf5ae 100644
--- a/drivers/net/team/team_core.c
+++ b/drivers/net/team/team_core.c
@@ -2058,6 +2058,65 @@ static const struct ethtool_ops team_ethtool_ops = {
  * rt netlink interface
  ***********************/
 
+/* Caller must hold rcu_read_lock(). */
+static struct team_port *team_header_port_get_rcu(struct team *team, bool txable)
+{
+	struct team_port *port;
+
+	list_for_each_entry_rcu(port, &team->port_list, list) {
+		if (!txable || team_port_txable(port))
+			return port;
+	}
+
+	return NULL;
+}
+
+static int team_header_create(struct sk_buff *skb, struct net_device *team_dev,
+			      unsigned short type, const void *daddr,
+			      const void *saddr, unsigned int len)
+{
+	struct team *team = netdev_priv(team_dev);
+	const struct header_ops *port_ops;
+	struct team_port *port;
+	int ret = 0;
+
+	rcu_read_lock();
+	port = team_header_port_get_rcu(team, true);
+	if (port) {
+		port_ops = READ_ONCE(port->dev->header_ops);
+		if (port_ops && port_ops->create)
+			ret = port_ops->create(skb, port->dev,
+					       type, daddr, saddr, len);
+	}
+	rcu_read_unlock();
+	return ret;
+}
+
+static int team_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+{
+	struct team *team = netdev_priv(skb->dev);
+	const struct header_ops *port_ops;
+	struct team_port *port;
+	int ret = 0;
+
+	rcu_read_lock();
+	port = team_header_port_get_rcu(team, true);
+	if (!port)
+		port = team_header_port_get_rcu(team, false);
+	if (port) {
+		port_ops = READ_ONCE(port->dev->header_ops);
+		if (port_ops && port_ops->parse)
+			ret = port_ops->parse(skb, haddr);
+	}
+	rcu_read_unlock();
+	return ret;
+}
+
+static const struct header_ops team_header_ops = {
+	.create		= team_header_create,
+	.parse		= team_header_parse,
+};
+
 static void team_setup_by_port(struct net_device *dev,
 			       struct net_device *port_dev)
 {
@@ -2066,7 +2125,7 @@ static void team_setup_by_port(struct net_device *dev,
 	if (port_dev->type == ARPHRD_ETHER)
 		dev->header_ops	= team->header_ops_cache;
 	else
-		dev->header_ops	= port_dev->header_ops;
+		dev->header_ops	= port_dev->header_ops ? &team_header_ops : NULL;
 	dev->type = port_dev->type;
 	dev->hard_header_len = port_dev->hard_header_len;
 	dev->needed_headroom = port_dev->needed_headroom;
-- 
2.43.0


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

end of thread, other threads:[~2026-03-14 12:07 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-14  6:23 [PATCH net v1] team: fix header_ops type confusion with non-Ethernet ports Jiayuan Chen
2026-03-14 10:19 ` Eric Dumazet
2026-03-14 11:14   ` Eric Dumazet
2026-03-14 11:41     ` Jiayuan Chen
2026-03-14 11:46       ` Eric Dumazet
2026-03-14 11:51         ` Jiayuan Chen
2026-03-14 11:53           ` Eric Dumazet
2026-03-14 11:57             ` Eric Dumazet
2026-03-14 12:05             ` Jiayuan Chen

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