From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793211596; b=4SQGt6b1Xlg5cY1pIbuXYZismnOmqyJ0L6mK+dzLtXzO32ogvUcwS+Ny/ZF2Up8QGDJS hss6Z2DPnHR1reiRh0i0Z6bp6zoK35g1Qkw6luQx4CD2u37Ecwy2meQksdObSXU1I6P5s ++SmkVeWmDbQb/oS9dSGiuhlci9kpU2QyrQFF2+2HIcbXl3Yj7Uo2K4iXj4/IajE0m++x mRd0IZffMRbPCqn/ksw26oKarmvnzsNco4b6+Y1UVl9OkIMJ5jxBH0KZmQbv6383JER76 Ss9M2Mr1jTqmAB6+NtCKp1fsJB42iQuy02qRLJMm93gXOrl+4skXZyd/hi4sHr82Osw== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793211596; h=Received:DKIM-Signature:From:To:Cc:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:User-Agent:X-stable:X-Patchwork-Hint: MIME-Version:Content-Transfer-Encoding; bh=V7tubR7BIFzyRH6TOfQdiUJbiA/F1y2WpmMkJ07KmVY=; b=TnjCUn0mxwsvN2GvMuuFWXgTYfMROTP1ftqXnsEA5SFkMlG+8y9lRSM4t9Y7l8gOV7NN ErqsyGhn6dZGnpy1FCDRjBXS+6FaqZ6IrJ5AR07bvo1P4B8qIQ5yY6fz+E7+F79OSN4nh W6Vp37ZTtEFbn4WE2F5U8LToM+4cp1sooSGqiobg31tCHWlF9E8wt8awL1ytvUiPOBn6Z iXeoT+4IY3Z1DIPHFAiUeprZGW/XstZjzdoJT+cktxdzjK4rxS+9Fw0zRKldQNWxWLeL8 5SLyx6LOgVh8w9EbWaUwt3uObzEgkkZmbc1B6IuqEchXvieLVw2m9xDZTd/U2zC+VEQ== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=linuxfoundation.org header.i=@linuxfoundation.org header.a=rsa-sha256 header.s=korg header.b="RjUycs/Y"; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DF9D8606A31D; Wed, 28 Oct 2026 18:19:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key, unprotected) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.a=rsa-sha256 header.s=korg header.b=RjUycs/Y Received: by smtp.kernel.org (Postfix) with ESMTPSA id E4E381F01825; Wed, 28 Oct 2026 18:19:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1793211595; bh=V7tubR7BIFzyRH6TOfQdiUJbiA/F1y2WpmMkJ07KmVY=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=RjUycs/YLFMHcapvjNkrSKZcVSeWiG8MrdgzH7ZN4sANc6tCeSgd6z+COrUNOjtTS EjAV1pwMdVx9euOJoggdCu4iliuBgZb70fqT8rTGPYA6Ny++CGwmG4F3PNLZqNHU2H LHB0EMImTlTbHRD7JkJpAW+sZHV6FXLgdhwIuzec= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Xiang Mei , Weiming Shi , Jiayuan Chen , Paolo Abeni , Sasha Levin Subject: [PATCH 5.15 433/571] net: team: fix NULL pointer dereference in team_xmit during mode change Date: Wed, 28 Oct 2026 17:59:40 +0100 Message-ID: <20261028170018.294173072@linuxfoundation.org> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261028170008.009143722@linuxfoundation.org> References: <20261028170008.009143722@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore X-Mailing-List: patches@lists.linux.dev List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 5.15-stable review patch. If anyone has any objections, please let me know. ------------------ From: Weiming Shi [ Upstream commit 25fe708bbc59289d3d1ea4b126fbc1b460a072a5 ] __team_change_mode() clears team->ops with memset() before restoring safe dummy handlers via team_adjust_ops(). A concurrent team_xmit() running under RCU on another CPU can read team->ops.transmit during this window and call a NULL function pointer, crashing the kernel. The race requires a mode change (CAP_NET_ADMIN) concurrent with transmit on the team device. BUG: kernel NULL pointer dereference, address: 0000000000000000 Oops: 0010 [#1] SMP KASAN NOPTI RIP: 0010:0x0 Call Trace: team_xmit (drivers/net/team/team_core.c:1853) dev_hard_start_xmit (net/core/dev.c:3904) __dev_queue_xmit (net/core/dev.c:4871) packet_sendmsg (net/packet/af_packet.c:3109) __sys_sendto (net/socket.c:2265) The original code assumed that no ports means no traffic, so mode changes could freely memset()/memcpy() the ops. AF_PACKET with forced carrier breaks that assumption. Prevent the race instead of making it safe: replace memset()/memcpy() with per-field updates that never touch transmit or receive. Those two handlers are managed solely by team_adjust_ops(), which already installs dummies when tx_en_port_count == 0 (always true during mode change since no ports are present). WRITE_ONCE/READ_ONCE prevent store/load tearing on the handler pointers. synchronize_net() before exit_op() drains in-flight readers that may still reference old mode state from before port removal switched the handlers to dummies. Fixes: 3d249d4ca7d0 ("net: introduce ethernet teaming device") Reported-by: Xiang Mei Signed-off-by: Weiming Shi Reviewed-by: Jiayuan Chen Link: https://patch.msgid.link/20260521081159.1491563-3-bestswngs@gmail.com Signed-off-by: Paolo Abeni Stable-dep-of: b7454f1940c5 ("net: team: stop reusing skb after queue override") Signed-off-by: Sasha Levin --- drivers/net/team/team.c | 45 +++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 0d2fd43fd00a4..367ef5dd6205a 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -535,21 +535,23 @@ static void team_adjust_ops(struct team *team) if (!team->en_port_count || !team_is_mode_set(team) || !team->mode->ops->transmit) - team->ops.transmit = team_dummy_transmit; + WRITE_ONCE(team->ops.transmit, team_dummy_transmit); else - team->ops.transmit = team->mode->ops->transmit; + WRITE_ONCE(team->ops.transmit, team->mode->ops->transmit); if (!team->en_port_count || !team_is_mode_set(team) || !team->mode->ops->receive) - team->ops.receive = team_dummy_receive; + WRITE_ONCE(team->ops.receive, team_dummy_receive); else - team->ops.receive = team->mode->ops->receive; + WRITE_ONCE(team->ops.receive, team->mode->ops->receive); } /* - * We can benefit from the fact that it's ensured no port is present - * at the time of mode change. Therefore no packets are in fly so there's no - * need to set mode operations in any special way. + * team_change_mode() ensures no ports are present during mode change, + * but lockless readers can still reach team_xmit(). Avoid touching + * transmit/receive -- they are already set to dummies by + * team_adjust_ops() since no ports are enabled. synchronize_net() + * drains in-flight readers before destroying old mode state. */ static int __team_change_mode(struct team *team, const struct team_mode *new_mode) @@ -558,9 +560,21 @@ static int __team_change_mode(struct team *team, if (team_is_mode_set(team)) { void (*exit_op)(struct team *team) = team->ops.exit; - /* Clear ops area so no callback is called any longer */ - memset(&team->ops, 0, sizeof(struct team_mode_ops)); - team_adjust_ops(team); + /* Clear cold-path ops used only under RTNL. transmit and + * receive are already dummies (no ports) so leave them + * alone -- overwriting them is the source of the race. + */ + team->ops.init = NULL; + team->ops.exit = NULL; + team->ops.port_enter = NULL; + team->ops.port_leave = NULL; + team->ops.port_change_dev_addr = NULL; + team->ops.port_tx_disabled = NULL; + + /* Wait for in-flight readers before tearing down mode + * state they may reference. + */ + synchronize_net(); if (exit_op) exit_op(team); @@ -583,7 +597,12 @@ static int __team_change_mode(struct team *team, } team->mode = new_mode; - memcpy(&team->ops, new_mode->ops, sizeof(struct team_mode_ops)); + team->ops.init = new_mode->ops->init; + team->ops.exit = new_mode->ops->exit; + team->ops.port_enter = new_mode->ops->port_enter; + team->ops.port_leave = new_mode->ops->port_leave; + team->ops.port_change_dev_addr = new_mode->ops->port_change_dev_addr; + team->ops.port_tx_disabled = new_mode->ops->port_tx_disabled; team_adjust_ops(team); return 0; @@ -739,7 +758,7 @@ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb) /* allow exact match delivery for disabled ports */ res = RX_HANDLER_EXACT; } else { - res = team->ops.receive(team, port, skb); + res = READ_ONCE(team->ops.receive)(team, port, skb); } if (res == RX_HANDLER_ANOTHER) { struct team_pcpu_stats *pcpu_stats; @@ -1737,7 +1756,7 @@ static netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev) tx_success = team_queue_override_transmit(team, skb); if (!tx_success) - tx_success = team->ops.transmit(team, skb); + tx_success = READ_ONCE(team->ops.transmit)(team, skb); if (tx_success) { struct team_pcpu_stats *pcpu_stats; -- 2.53.0