mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net] seg6: fix tun_src RCU lifetime on namespace teardown
@ 2026-10-23 19:52 Tristan Madani
  2026-10-23 19:54 ` netdev-bot+sinfo
  2026-10-27  7:53 ` netdev-bot+sashiko
  0 siblings, 2 replies; 5+ messages in thread
From: Tristan Madani @ 2026-10-23 19:52 UTC (permalink / raw)
  To: Andrea Mayer, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: Simon Horman, David Lebrun, netdev, linux-kernel, stable, Tristan Madani

From: Tristan Madani <tristan@talencesecurity.com>

seg6_net_exit() frees tun_src directly, but set_tun_src() in
seg6_iptunnel.c reads it under rcu_read_lock(). The pointer may
be freed while a concurrent reader is still accessing it.

Add a pre_exit callback to clear the tun_src pointer before the
implied synchronize_rcu_expedited(), so all RCU readers complete
before the actual kfree in the exit callback. Save the original
pointer in tun_src_old so exit can free it after the grace period,
since the RCU pointer is already NULL at that point. The init
error path frees tun_src directly since pre_exit has not run.

Fixes: 915d7e5e5930 ("ipv6: sr: add code base for control plane support of SR-IPv6")
Cc: stable@vger.kernel.org
Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
---
 include/net/seg6.h |  1 +
 net/ipv6/seg6.c    | 12 +++++++++++-
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/include/net/seg6.h b/include/net/seg6.h
index 82b3fbbcbb935..210f4fa0d4dd7 100644
--- a/include/net/seg6.h
+++ b/include/net/seg6.h
@@ -36,6 +36,7 @@ static inline void update_csum_diff16(struct sk_buff *skb, __be32 *from,
 struct seg6_pernet_data {
 	struct mutex lock;
 	struct in6_addr __rcu *tun_src;
+	struct in6_addr *tun_src_old;
 #ifdef CONFIG_IPV6_SEG6_HMAC
 	struct rhashtable hmac_infos;
 #endif
diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c
index 8c2b156c227a7..670052290e43c 100644
--- a/net/ipv6/seg6.c
+++ b/net/ipv6/seg6.c
@@ -447,18 +447,28 @@ static int __net_init seg6_net_init(struct net *net)
 	return 0;
 }
 
+static void __net_exit seg6_net_pre_exit(struct net *net)
+{
+	struct seg6_pernet_data *sdata = seg6_pernet(net);
+
+	/* Save tun_src for exit to free after the RCU grace period. */
+	sdata->tun_src_old = rcu_dereference_raw(sdata->tun_src);
+	RCU_INIT_POINTER(sdata->tun_src, NULL);
+}
+
 static void __net_exit seg6_net_exit(struct net *net)
 {
 	struct seg6_pernet_data *sdata = seg6_pernet(net);
 
 	seg6_hmac_net_exit(net);
 
-	kfree(rcu_dereference_raw(sdata->tun_src));
+	kfree(sdata->tun_src_old);
 	kfree(sdata);
 }
 
 static struct pernet_operations ip6_segments_ops = {
 	.init = seg6_net_init,
+	.pre_exit = seg6_net_pre_exit,
 	.exit = seg6_net_exit,
 };
 
-- 
2.47.3


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

* Re: [PATCH net] seg6: fix tun_src RCU lifetime on namespace teardown
  2026-10-23 19:52 [PATCH net] seg6: fix tun_src RCU lifetime on namespace teardown Tristan Madani
@ 2026-10-23 19:54 ` netdev-bot+sinfo
       [not found]   ` <20261025222222.1064464-1-tristan@talencesecurity.com>
  2026-10-27  7:53 ` netdev-bot+sashiko
  1 sibling, 1 reply; 5+ messages in thread
From: netdev-bot+sinfo @ 2026-10-23 19:54 UTC (permalink / raw)
  To: Tristan Madani
  Cc: Andrea Mayer, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, David Lebrun, netdev, linux-kernel,
	stable, Tristan Madani

Hi!

This is an automated message. This series looks like a fix, but its
commit messages seem to be missing some information:

 - How the issue was discovered, e.g. hit in production, hit during
   development, syzbot report, manual code inspection, LLM or static
   analysis tool scan.

 - Whether the issue was actually triggered, or is only theoretical
   (e.g. found by code inspection). If it was triggered please include
   the symptoms, like the stack trace or error messages.

Please do not repost the series just to address the above. Instead,
reply to this email with the missing information, so that reviewers
can take it into account. If the series needs another revision for
other reasons, please include the information in the commit messages
then.

The evaluation is done by an LLM so it may be wrong, if you think
that is the case please reply and explain.

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

* Re: [PATCH net] seg6: fix tun_src RCU lifetime on namespace teardown
       [not found]   ` <20261025222222.1064464-1-tristan@talencesecurity.com>
@ 2026-10-26 14:05     ` Andrea Mayer
  2026-10-26 14:20       ` Eric Dumazet
  0 siblings, 1 reply; 5+ messages in thread
From: Andrea Mayer @ 2026-10-26 14:05 UTC (permalink / raw)
  To: Tristan Madani
  Cc: netdev-bot+sinfo, davem, edumazet, kuba, pabeni, horms, tristan,
	david.lebrun, linux-kernel, netdev, stable, stefano.salsano,
	Andrea Mayer

On Sun, 25 Oct 2026 22:22:22 +0000
Tristan Madani <tristmd@gmail.com> wrote:

> Found by manual code inspection while auditing RCU lifetime
> patterns in the seg6 subsystem.  The issue is theoretical --
> found by reading the source, not triggered in production.

Hi Tristan,
thanks for the info.

The pre_exit in this patch introduces a NULL deref. It sets tun_src to
NULL before the netns devices are removed, while set_tun_src() can
still read it.

I ran a stress test on net with KASAN. A userspace program sends UDP
packets in a loop from init_net through a veth into a test netns, where
a seg6 encap route without a per-route tunsrc forwards them. Meanwhile,
the test netns is created and deleted repeatedly.

With this patch applied, KASAN reported a null-ptr-deref in
set_tun_src() and the kernel panicked:

 BUG: KASAN: null-ptr-deref in set_tun_src+0xd1/0x240
 Read of size 8 at addr 0000000000000000 by task udp_flood_user/379
 Call Trace:
  <IRQ>
  set_tun_src+0xd1/0x240
  __seg6_do_srh_encap+0x410/0x710
  seg6_do_srh+0x65e/0x6a0
  seg6_input_core+0x87/0x4c0
  lwtunnel_input+0x18d/0x420
  ipv6_rcv+0x44f/0x460
  __netif_receive_skb_one_core+0xd6/0x130
  process_backlog+0x2cc/0xa00
  __napi_poll.constprop.0+0x56/0x270
  net_rx_action+0x327/0x730
  handle_softirqs+0x15a/0x6c0
  do_softirq+0xb4/0xf0
  </IRQ>
  <TASK>
  __local_bh_enable_ip+0xdd/0x100
  __dev_queue_xmit+0x538/0x1030
  ip6_finish_output+0x280/0x640
  ip6_output+0x118/0x380
  ip6_send_skb+0x99/0x210
  udp_v6_send_skb+0x397/0x760
  udpv6_sendmsg+0x1100/0x14d0
  __sys_sendto+0x219/0x2d0
  ...
 Kernel panic - not syncing: Fatal exception in interrupt

Without this patch, I ran the same test with 1000 netns deletions and
KASAN reported nothing.

Ciao,
Andrea

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

* Re: [PATCH net] seg6: fix tun_src RCU lifetime on namespace teardown
  2026-10-26 14:05     ` Andrea Mayer
@ 2026-10-26 14:20       ` Eric Dumazet
  0 siblings, 0 replies; 5+ messages in thread
From: Eric Dumazet @ 2026-10-26 14:20 UTC (permalink / raw)
  To: Andrea Mayer
  Cc: Tristan Madani, netdev-bot+sinfo, davem, kuba, pabeni, horms,
	tristan, david.lebrun, linux-kernel, netdev, stable,
	stefano.salsano

Le lun. 26 oct. 2026 à 15:05, Andrea Mayer <andrea.mayer@uniroma2.it> a écrit :
>
> On Sun, 25 Oct 2026 22:22:22 +0000
> Tristan Madani <tristmd@gmail.com> wrote:
>
> > Found by manual code inspection while auditing RCU lifetime
> > patterns in the seg6 subsystem.  The issue is theoretical --
> > found by reading the source, not triggered in production.
>
> Hi Tristan,
> thanks for the info.
>
> The pre_exit in this patch introduces a NULL deref. It sets tun_src to
> NULL before the netns devices are removed, while set_tun_src() can
> still read it.
>
> I ran a stress test on net with KASAN. A userspace program sends UDP
> packets in a loop from init_net through a veth into a test netns, where
> a seg6 encap route without a per-route tunsrc forwards them. Meanwhile,
> the test netns is created and deleted repeatedly.
>
> With this patch applied, KASAN reported a null-ptr-deref in
> set_tun_src() and the kernel panicked:
>
>  BUG: KASAN: null-ptr-deref in set_tun_src+0xd1/0x240
>  Read of size 8 at addr 0000000000000000 by task udp_flood_user/379
>  Call Trace:
>   <IRQ>
>   set_tun_src+0xd1/0x240
>   __seg6_do_srh_encap+0x410/0x710
>   seg6_do_srh+0x65e/0x6a0
>   seg6_input_core+0x87/0x4c0
>   lwtunnel_input+0x18d/0x420
>   ipv6_rcv+0x44f/0x460
>   __netif_receive_skb_one_core+0xd6/0x130
>   process_backlog+0x2cc/0xa00
>   __napi_poll.constprop.0+0x56/0x270
>   net_rx_action+0x327/0x730
>   handle_softirqs+0x15a/0x6c0
>   do_softirq+0xb4/0xf0
>   </IRQ>
>   <TASK>
>   __local_bh_enable_ip+0xdd/0x100
>   __dev_queue_xmit+0x538/0x1030
>   ip6_finish_output+0x280/0x640
>   ip6_output+0x118/0x380
>   ip6_send_skb+0x99/0x210
>   udp_v6_send_skb+0x397/0x760
>   udpv6_sendmsg+0x1100/0x14d0
>   __sys_sendto+0x219/0x2d0
>   ...
>  Kernel panic - not syncing: Fatal exception in interrupt
>
> Without this patch, I ran the same test with 1000 netns deletions and
> KASAN reported nothing.
>
> Ciao,
> Andrea

Right, and there is no bug to fix in the first place.

ip6_segments_ops is a pernet subsys, so seg6_net_exit() runs after
all netns devices have been unregistered (default_device_exit_batch()
and the RCU grace periods in netdev_run_todo()). At that point no
packet can reach set_tun_src() for this netns, and no socket can
issue SEG6_CMD_GET_TUNSRC either.

pre_exit runs before devices are dismantled, hence the crash Andrea
reported.

pw-bot: rejected

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

* Re: [PATCH net] seg6: fix tun_src RCU lifetime on namespace teardown
  2026-10-23 19:52 [PATCH net] seg6: fix tun_src RCU lifetime on namespace teardown Tristan Madani
  2026-10-23 19:54 ` netdev-bot+sinfo
@ 2026-10-27  7:53 ` netdev-bot+sashiko
  1 sibling, 0 replies; 5+ messages in thread
From: netdev-bot+sashiko @ 2026-10-27  7:53 UTC (permalink / raw)
  To: tristmd
  Cc: andrea.mayer, davem, edumazet, kuba, pabeni, horms, david.lebrun,
	netdev, linux-kernel, stable, tristan

Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider.

Critical: 1 · High: 0 · Medium: 0 · Low: 0

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Critical] The patch adds a reachable NULL pointer dereference on the
  seg6 datapath during netns teardown.

--

seg6: fix tun_src RCU lifetime on namespace teardown

This patch adds a pre_exit callback to seg6 pernet ops. The callback
saves the per-netns tun_src pointer in a new tun_src_old field and then
clears the RCU pointer. The exit callback frees tun_src_old after the
grace period that runs between pre_exit and exit.

> diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c
> index 8c2b156c227a7..670052290e43c 100644
> --- a/net/ipv6/seg6.c
> +++ b/net/ipv6/seg6.c
> @@ -447,18 +447,28 @@ static int __net_init seg6_net_init(struct net *net)
>  	return 0;
>  }
>  
> +static void __net_exit seg6_net_pre_exit(struct net *net)
> +{
> +	struct seg6_pernet_data *sdata = seg6_pernet(net);
> +
> +	/* Save tun_src for exit to free after the RCU grace period. */
> +	sdata->tun_src_old = rcu_dereference_raw(sdata->tun_src);
> +	RCU_INIT_POINTER(sdata->tun_src, NULL);

[Severity: Critical]
Can this cause a NULL pointer dereference in set_tun_src() while the
netns is being torn down?

set_tun_src() in net/ipv6/seg6_iptunnel.c assumes tun_src is never NULL.
If the route has no per-route tunsrc configured (the default), it does:

	rcu_read_lock();
	tun_src = rcu_dereference(sdata->tun_src);

	if (!ipv6_addr_any(tun_src)) {
		memcpy(saddr, tun_src, sizeof(struct in6_addr));

ipv6_addr_any() dereferences its argument in both config variants, so a
NULL tun_src faults there.

In ops_undo_list(), all pre_exit callbacks run first. After that comes
synchronize_rcu_expedited(), then ops_exit_rtnl_list(), then
ops_exit_list(). The netns devices are unregistered later, in
default_device_exit_batch() during the exit phase. That means the dying
netns keeps its registered devices and seg6 lwtunnel routes after
seg6_net_pre_exit() has published NULL.

A packet that arrives in that window, for example from a veth peer in a
live netns or from a physical NIC, would follow:

seg6_input_core() / seg6_output_core()
  -> seg6_do_srh()
    -> seg6_do_srh_encap() / seg6_do_srh_encap_red() / seg6_do_srh_inline()
      -> set_tun_src()
        -> ipv6_addr_any(NULL)

This runs in softirq or xmit context. The grace period does not close
the window, because it only waits for readers that already exist.
Readers that load the pointer after the clear still see NULL.

Before this patch, tun_src was never NULL between init and exit. Would
it be better to leave the pointer in place and keep the kfree() in
seg6_net_exit()? By the time exit runs, a grace period has already
passed.

seg6_genl_get_tunsrc() also passes the pointer to nla_put() without a
NULL check. That path is probably not reachable on a dying netns, since
a genl socket holds a netns reference.

> +}
> +
>  static void __net_exit seg6_net_exit(struct net *net)
>  {
>  	struct seg6_pernet_data *sdata = seg6_pernet(net);
>  
>  	seg6_hmac_net_exit(net);
>  
> -	kfree(rcu_dereference_raw(sdata->tun_src));
> +	kfree(sdata->tun_src_old);
>  	kfree(sdata);
>  }

[ ... ]

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261023195234.3524462-1-tristmd%40gmail.com

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

end of thread, other threads:[~2026-10-27  7:53 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-23 19:52 [PATCH net] seg6: fix tun_src RCU lifetime on namespace teardown Tristan Madani
2026-10-23 19:54 ` netdev-bot+sinfo
     [not found]   ` <20261025222222.1064464-1-tristan@talencesecurity.com>
2026-10-26 14:05     ` Andrea Mayer
2026-10-26 14:20       ` Eric Dumazet
2026-10-27  7:53 ` netdev-bot+sashiko

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

all inboxes | Powered by JetHome®