Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'team-fix-header_ops-type-confusion-and-add-selftest'

Jiayuan Chen says:

====================
team: fix header_ops type confusion and add selftest

Hi,

This patch series fixes a panic reported by syzkaller in the team/bond/gre
stacked non-Ethernet configuration:
https://syzkaller.appspot.com/bug?extid=3d8bc31c45e11450f24c

The first patch fixes the header_ops type confusion / parse recursion
context issue in team. The second patch adds a selftest to reproduce the
reported scenario and prevent regressions in the future.

v1: https://lore.kernel.org/netdev/20260314062306.212765-1-jiayuan.chen@linux.dev/
v2: https://lore.kernel.org/netdev/20260317124606.157035-1-jiayuan.chen@linux.dev/
====================

Link: https://patch.msgid.link/20260320072139.134249-1-jiayuan.chen@linux.dev
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

+108 -1
+64 -1
drivers/net/team/team_core.c
··· 2058 2058 * rt netlink interface 2059 2059 ***********************/ 2060 2060 2061 + /* For tx path we need a linkup && enabled port and for parse any port 2062 + * suffices. 2063 + */ 2064 + static struct team_port *team_header_port_get_rcu(struct team *team, 2065 + bool txable) 2066 + { 2067 + struct team_port *port; 2068 + 2069 + list_for_each_entry_rcu(port, &team->port_list, list) { 2070 + if (!txable || team_port_txable(port)) 2071 + return port; 2072 + } 2073 + 2074 + return NULL; 2075 + } 2076 + 2077 + static int team_header_create(struct sk_buff *skb, struct net_device *team_dev, 2078 + unsigned short type, const void *daddr, 2079 + const void *saddr, unsigned int len) 2080 + { 2081 + struct team *team = netdev_priv(team_dev); 2082 + const struct header_ops *port_ops; 2083 + struct team_port *port; 2084 + int ret = 0; 2085 + 2086 + rcu_read_lock(); 2087 + port = team_header_port_get_rcu(team, true); 2088 + if (port) { 2089 + port_ops = READ_ONCE(port->dev->header_ops); 2090 + if (port_ops && port_ops->create) 2091 + ret = port_ops->create(skb, port->dev, 2092 + type, daddr, saddr, len); 2093 + } 2094 + rcu_read_unlock(); 2095 + return ret; 2096 + } 2097 + 2098 + static int team_header_parse(const struct sk_buff *skb, 2099 + const struct net_device *team_dev, 2100 + unsigned char *haddr) 2101 + { 2102 + struct team *team = netdev_priv(team_dev); 2103 + const struct header_ops *port_ops; 2104 + struct team_port *port; 2105 + int ret = 0; 2106 + 2107 + rcu_read_lock(); 2108 + port = team_header_port_get_rcu(team, false); 2109 + if (port) { 2110 + port_ops = READ_ONCE(port->dev->header_ops); 2111 + if (port_ops && port_ops->parse) 2112 + ret = port_ops->parse(skb, port->dev, haddr); 2113 + } 2114 + rcu_read_unlock(); 2115 + return ret; 2116 + } 2117 + 2118 + static const struct header_ops team_header_ops = { 2119 + .create = team_header_create, 2120 + .parse = team_header_parse, 2121 + }; 2122 + 2061 2123 static void team_setup_by_port(struct net_device *dev, 2062 2124 struct net_device *port_dev) 2063 2125 { ··· 2128 2066 if (port_dev->type == ARPHRD_ETHER) 2129 2067 dev->header_ops = team->header_ops_cache; 2130 2068 else 2131 - dev->header_ops = port_dev->header_ops; 2069 + dev->header_ops = port_dev->header_ops ? 2070 + &team_header_ops : NULL; 2132 2071 dev->type = port_dev->type; 2133 2072 dev->hard_header_len = port_dev->hard_header_len; 2134 2073 dev->needed_headroom = port_dev->needed_headroom;
+1
tools/testing/selftests/drivers/net/team/Makefile
··· 3 3 4 4 TEST_PROGS := \ 5 5 dev_addr_lists.sh \ 6 + non_ether_header_ops.sh \ 6 7 options.sh \ 7 8 propagation.sh \ 8 9 refleak.sh \
+2
tools/testing/selftests/drivers/net/team/config
··· 1 + CONFIG_BONDING=y 1 2 CONFIG_DUMMY=y 2 3 CONFIG_IPV6=y 3 4 CONFIG_MACVLAN=y 4 5 CONFIG_NETDEVSIM=m 6 + CONFIG_NET_IPGRE=y 5 7 CONFIG_NET_TEAM=y 6 8 CONFIG_NET_TEAM_MODE_ACTIVEBACKUP=y 7 9 CONFIG_NET_TEAM_MODE_LOADBALANCE=y
+41
tools/testing/selftests/drivers/net/team/non_ether_header_ops.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + # shellcheck disable=SC2154 4 + # 5 + # Reproduce the non-Ethernet header_ops confusion scenario with: 6 + # g0 (gre) -> b0 (bond) -> t0 (team) 7 + # 8 + # Before the fix, direct header_ops inheritance in this stack could call 9 + # callbacks with the wrong net_device context and crash. 10 + 11 + lib_dir=$(dirname "$0") 12 + source "$lib_dir"/../../../net/lib.sh 13 + 14 + trap cleanup_all_ns EXIT 15 + 16 + setup_ns ns1 17 + 18 + ip -n "$ns1" link add d0 type dummy 19 + ip -n "$ns1" addr add 10.10.10.1/24 dev d0 20 + ip -n "$ns1" link set d0 up 21 + 22 + ip -n "$ns1" link add g0 type gre local 10.10.10.1 23 + ip -n "$ns1" link add b0 type bond mode active-backup 24 + ip -n "$ns1" link add t0 type team 25 + 26 + ip -n "$ns1" link set g0 master b0 27 + ip -n "$ns1" link set b0 master t0 28 + 29 + ip -n "$ns1" link set g0 up 30 + ip -n "$ns1" link set b0 up 31 + ip -n "$ns1" link set t0 up 32 + 33 + # IPv6 address assignment triggers MLD join reports that call 34 + # dev_hard_header() on t0, exercising the inherited header_ops path. 35 + ip -n "$ns1" -6 addr add 2001:db8:1::1/64 dev t0 nodad 36 + for i in $(seq 1 20); do 37 + ip netns exec "$ns1" ping -6 -I t0 ff02::1 -c1 -W1 &>/dev/null || true 38 + done 39 + 40 + echo "PASS: non-Ethernet header_ops stacking did not crash" 41 + exit "$EXIT_STATUS"