Skip to content

Commit 25f5463

Browse files
author
Paolo Abeni
committed
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/[email protected]/ v2: https://lore.kernel.org/netdev/[email protected]/ ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
2 parents 673bb63 + 5606382 commit 25f5463

4 files changed

Lines changed: 108 additions & 1 deletion

File tree

drivers/net/team/team_core.c

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2058,6 +2058,68 @@ static const struct ethtool_ops team_ethtool_ops = {
20582058
* rt netlink interface
20592059
***********************/
20602060

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+
20612123
static void team_setup_by_port(struct net_device *dev,
20622124
struct net_device *port_dev)
20632125
{
@@ -2066,7 +2128,8 @@ static void team_setup_by_port(struct net_device *dev,
20662128
if (port_dev->type == ARPHRD_ETHER)
20672129
dev->header_ops = team->header_ops_cache;
20682130
else
2069-
dev->header_ops = port_dev->header_ops;
2131+
dev->header_ops = port_dev->header_ops ?
2132+
&team_header_ops : NULL;
20702133
dev->type = port_dev->type;
20712134
dev->hard_header_len = port_dev->hard_header_len;
20722135
dev->needed_headroom = port_dev->needed_headroom;

tools/testing/selftests/drivers/net/team/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
TEST_PROGS := \
55
dev_addr_lists.sh \
6+
non_ether_header_ops.sh \
67
options.sh \
78
propagation.sh \
89
refleak.sh \
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
CONFIG_BONDING=y
12
CONFIG_DUMMY=y
23
CONFIG_IPV6=y
34
CONFIG_MACVLAN=y
45
CONFIG_NETDEVSIM=m
6+
CONFIG_NET_IPGRE=y
57
CONFIG_NET_TEAM=y
68
CONFIG_NET_TEAM_MODE_ACTIVEBACKUP=y
79
CONFIG_NET_TEAM_MODE_LOADBALANCE=y
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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"

0 commit comments

Comments
 (0)