mirror of
https://github.com/torvalds/linux.git
synced 2026-06-03 12:03:54 +02:00
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>
This commit is contained in:
commit
25f5463c91
|
|
@ -2058,6 +2058,68 @@ static const struct ethtool_ops team_ethtool_ops = {
|
|||
* rt netlink interface
|
||||
***********************/
|
||||
|
||||
/* For tx path we need a linkup && enabled port and for parse any port
|
||||
* suffices.
|
||||
*/
|
||||
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,
|
||||
const struct net_device *team_dev,
|
||||
unsigned char *haddr)
|
||||
{
|
||||
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, false);
|
||||
if (port) {
|
||||
port_ops = READ_ONCE(port->dev->header_ops);
|
||||
if (port_ops && port_ops->parse)
|
||||
ret = port_ops->parse(skb, port->dev, 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 +2128,8 @@ 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;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
TEST_PROGS := \
|
||||
dev_addr_lists.sh \
|
||||
non_ether_header_ops.sh \
|
||||
options.sh \
|
||||
propagation.sh \
|
||||
refleak.sh \
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
CONFIG_BONDING=y
|
||||
CONFIG_DUMMY=y
|
||||
CONFIG_IPV6=y
|
||||
CONFIG_MACVLAN=y
|
||||
CONFIG_NETDEVSIM=m
|
||||
CONFIG_NET_IPGRE=y
|
||||
CONFIG_NET_TEAM=y
|
||||
CONFIG_NET_TEAM_MODE_ACTIVEBACKUP=y
|
||||
CONFIG_NET_TEAM_MODE_LOADBALANCE=y
|
||||
|
|
|
|||
41
tools/testing/selftests/drivers/net/team/non_ether_header_ops.sh
Executable file
41
tools/testing/selftests/drivers/net/team/non_ether_header_ops.sh
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# shellcheck disable=SC2154
|
||||
#
|
||||
# Reproduce the non-Ethernet header_ops confusion scenario with:
|
||||
# g0 (gre) -> b0 (bond) -> t0 (team)
|
||||
#
|
||||
# Before the fix, direct header_ops inheritance in this stack could call
|
||||
# callbacks with the wrong net_device context and crash.
|
||||
|
||||
lib_dir=$(dirname "$0")
|
||||
source "$lib_dir"/../../../net/lib.sh
|
||||
|
||||
trap cleanup_all_ns EXIT
|
||||
|
||||
setup_ns ns1
|
||||
|
||||
ip -n "$ns1" link add d0 type dummy
|
||||
ip -n "$ns1" addr add 10.10.10.1/24 dev d0
|
||||
ip -n "$ns1" link set d0 up
|
||||
|
||||
ip -n "$ns1" link add g0 type gre local 10.10.10.1
|
||||
ip -n "$ns1" link add b0 type bond mode active-backup
|
||||
ip -n "$ns1" link add t0 type team
|
||||
|
||||
ip -n "$ns1" link set g0 master b0
|
||||
ip -n "$ns1" link set b0 master t0
|
||||
|
||||
ip -n "$ns1" link set g0 up
|
||||
ip -n "$ns1" link set b0 up
|
||||
ip -n "$ns1" link set t0 up
|
||||
|
||||
# IPv6 address assignment triggers MLD join reports that call
|
||||
# dev_hard_header() on t0, exercising the inherited header_ops path.
|
||||
ip -n "$ns1" -6 addr add 2001:db8:1::1/64 dev t0 nodad
|
||||
for i in $(seq 1 20); do
|
||||
ip netns exec "$ns1" ping -6 -I t0 ff02::1 -c1 -W1 &>/dev/null || true
|
||||
done
|
||||
|
||||
echo "PASS: non-Ethernet header_ops stacking did not crash"
|
||||
exit "$EXIT_STATUS"
|
||||
Loading…
Reference in New Issue
Block a user