Netdev List
 help / color / mirror / Atom feed
* [PATCH net] ipv6: ndisc: fix NULL deref in accept_untracked_na()
@ 2026-06-17  6:55 Weiming Shi
  2026-06-17  8:32 ` Jiayuan Chen
  2026-06-19  6:24 ` Jiayuan Chen
  0 siblings, 2 replies; 6+ messages in thread
From: Weiming Shi @ 2026-06-17  6:55 UTC (permalink / raw)
  To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: Simon Horman, netdev, linux-kernel, Xiang Mei, Weiming Shi

accept_untracked_na() re-fetches the inet6_dev with __in6_dev_get(dev)
and dereferences idev->cnf.accept_untracked_na without a NULL check,
even though its only caller ndisc_recv_na() already fetched and
NULL-checked idev for the same device.

Both reads of dev->ip6_ptr run in the same RCU read-side critical
section, but a concurrent addrconf_ifdown() can clear dev->ip6_ptr
between them: lowering the MTU below IPV6_MIN_MTU calls addrconf_ifdown()
without the synchronize_net() that orders the unregister path, so the
re-fetch returns NULL and oopses:

 BUG: KASAN: null-ptr-deref in ndisc_recv_na (net/ipv6/ndisc.c:974)
 Read of size 4 at addr 0000000000000364
 Call Trace:
  <IRQ>
  ndisc_recv_na (net/ipv6/ndisc.c:974)
  icmpv6_rcv (net/ipv6/icmp.c:1193)
  ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:479)
  ip6_input_finish (net/ipv6/ip6_input.c:534)
  ip6_input (net/ipv6/ip6_input.c:545)
  ip6_mc_input (net/ipv6/ip6_input.c:635)
  ipv6_rcv (net/ipv6/ip6_input.c:351)
  </IRQ>

It is reachable by an unprivileged user via a network namespace.

Pass the caller's already validated idev instead of re-fetching it; the
idev stays alive for the whole RCU critical section, so it is safe even
after dev->ip6_ptr has been cleared.

Fixes: aaa5f515b16b ("net: ipv6: new accept_untracked_na option to accept na only if in-network")
Assisted-by: Claude:claude-opus-4-8
Reported-by: Xiang Mei <xmei5@asu.edu>
Signed-off-by: Weiming Shi <bestswngs@gmail.com>

---
 net/ipv6/ndisc.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index e7ad13c5bd267..f867ec8d3d905 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -967,10 +967,8 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
 	return reason;
 }
 
-static int accept_untracked_na(struct net_device *dev, struct in6_addr *saddr)
+static int accept_untracked_na(struct inet6_dev *idev, struct in6_addr *saddr)
 {
-	struct inet6_dev *idev = __in6_dev_get(dev);
-
 	switch (READ_ONCE(idev->cnf.accept_untracked_na)) {
 	case 0: /* Don't accept untracked na (absent in neighbor cache) */
 		return 0;
@@ -980,7 +978,7 @@ static int accept_untracked_na(struct net_device *dev, struct in6_addr *saddr)
 		 * same subnet as an address configured on the interface that
 		 * received the na
 		 */
-		return !!ipv6_chk_prefix(saddr, dev);
+		return !!ipv6_chk_prefix(saddr, idev->dev);
 	default:
 		return 0;
 	}
@@ -1078,7 +1076,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
 	 */
 	new_state = msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE;
 	if (!neigh && lladdr && idev && READ_ONCE(idev->cnf.forwarding)) {
-		if (accept_untracked_na(dev, saddr)) {
+		if (accept_untracked_na(idev, saddr)) {
 			neigh = neigh_create(&nd_tbl, &msg->target, dev);
 			new_state = NUD_STALE;
 		}
-- 
2.43.0


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

* Re: [PATCH net] ipv6: ndisc: fix NULL deref in accept_untracked_na()
  2026-06-17  6:55 [PATCH net] ipv6: ndisc: fix NULL deref in accept_untracked_na() Weiming Shi
@ 2026-06-17  8:32 ` Jiayuan Chen
  2026-06-17 13:38   ` Weiming Shi
  2026-06-19  6:24 ` Jiayuan Chen
  1 sibling, 1 reply; 6+ messages in thread
From: Jiayuan Chen @ 2026-06-17  8:32 UTC (permalink / raw)
  To: Weiming Shi, David S . Miller, David Ahern, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, netdev, linux-kernel, Xiang Mei


On 6/17/26 2:55 PM, Weiming Shi wrote:
> accept_untracked_na() re-fetches the inet6_dev with __in6_dev_get(dev)
> and dereferences idev->cnf.accept_untracked_na without a NULL check,


Does ipv6_rpl_srh_rcv have same problem?


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

* Re: [PATCH net] ipv6: ndisc: fix NULL deref in accept_untracked_na()
  2026-06-17  8:32 ` Jiayuan Chen
@ 2026-06-17 13:38   ` Weiming Shi
  2026-06-18  4:08     ` Jiayuan Chen
  0 siblings, 1 reply; 6+ messages in thread
From: Weiming Shi @ 2026-06-17 13:38 UTC (permalink / raw)
  To: Jiayuan Chen, Weiming Shi, David S . Miller, David Ahern,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, netdev, linux-kernel, Xiang Mei

On Wed Jun 17, 2026 at 4:32 PM CST, Jiayuan Chen wrote:
>
> On 6/17/26 2:55 PM, Weiming Shi wrote:
>> accept_untracked_na() re-fetches the inet6_dev with __in6_dev_get(dev)
>> and dereferences idev->cnf.accept_untracked_na without a NULL check,
>
>
> Does ipv6_rpl_srh_rcv have same problem?

Hi,

Yes, ipv6_rpl_srh_rcv() has the same missing check. It reads
idev->cnf.rpl_seg_enabled right after __in6_dev_get(skb->dev) with no
NULL check, while seg6 and ioam6 in the same file both check it.

But I tried to trigger it and couldn't. With a guard added as an instrument,
idev never came back NULL over tens of millions of RPL packets while
flapping the MTU, so I can't say it's actually reachable.

Still, it's the only one of the three without the check. Want me to send
a patch adding it there too, for consistency?

Thanks,
Weiming Shi


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

* Re: [PATCH net] ipv6: ndisc: fix NULL deref in accept_untracked_na()
  2026-06-17 13:38   ` Weiming Shi
@ 2026-06-18  4:08     ` Jiayuan Chen
  2026-06-19  4:05       ` Xiang Mei
  0 siblings, 1 reply; 6+ messages in thread
From: Jiayuan Chen @ 2026-06-18  4:08 UTC (permalink / raw)
  To: Weiming Shi, David S . Miller, David Ahern, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, netdev, linux-kernel, Xiang Mei


On 6/17/26 9:38 PM, Weiming Shi wrote:
> On Wed Jun 17, 2026 at 4:32 PM CST, Jiayuan Chen wrote:
>> On 6/17/26 2:55 PM, Weiming Shi wrote:
>>> accept_untracked_na() re-fetches the inet6_dev with __in6_dev_get(dev)
>>> and dereferences idev->cnf.accept_untracked_na without a NULL check,
>>
>> Does ipv6_rpl_srh_rcv have same problem?
> Hi,
>
> Yes, ipv6_rpl_srh_rcv() has the same missing check. It reads
> idev->cnf.rpl_seg_enabled right after __in6_dev_get(skb->dev) with no
> NULL check, while seg6 and ioam6 in the same file both check it.
>
> But I tried to trigger it and couldn't. With a guard added as an instrument,
> idev never came back NULL over tens of millions of RPL packets while
> flapping the MTU, so I can't say it's actually reachable.


Can you need to add mdelay to enlarge the race window to reproduce it?

I believe we need more precise traffic and timing control, instead of
aggressively ramping up traffic and load in an attempt to reproduce the 
issue.


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

* Re: [PATCH net] ipv6: ndisc: fix NULL deref in accept_untracked_na()
  2026-06-18  4:08     ` Jiayuan Chen
@ 2026-06-19  4:05       ` Xiang Mei
  0 siblings, 0 replies; 6+ messages in thread
From: Xiang Mei @ 2026-06-19  4:05 UTC (permalink / raw)
  To: Jiayuan Chen
  Cc: Weiming Shi, David S . Miller, David Ahern, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, netdev, linux-kernel

On Wed, Jun 17, 2026 at 9:09 PM Jiayuan Chen <jiayuan.chen@linux.dev> wrote:
>
>
> On 6/17/26 9:38 PM, Weiming Shi wrote:
> > On Wed Jun 17, 2026 at 4:32 PM CST, Jiayuan Chen wrote:
> >> On 6/17/26 2:55 PM, Weiming Shi wrote:
> >>> accept_untracked_na() re-fetches the inet6_dev with __in6_dev_get(dev)
> >>> and dereferences idev->cnf.accept_untracked_na without a NULL check,
> >>
> >> Does ipv6_rpl_srh_rcv have same problem?
> > Hi,
> >
> > Yes, ipv6_rpl_srh_rcv() has the same missing check. It reads
> > idev->cnf.rpl_seg_enabled right after __in6_dev_get(skb->dev) with no
> > NULL check, while seg6 and ioam6 in the same file both check it.
> >
> > But I tried to trigger it and couldn't. With a guard added as an instrument,
> > idev never came back NULL over tens of millions of RPL packets while
> > flapping the MTU, so I can't say it's actually reachable.
>
>
> Can you need to add mdelay to enlarge the race window to reproduce it?
>
The delay works; we insert one at

@@ -1078,6 +1079,7 @@ static enum skb_drop_reason ndisc_recv_na(struct
sk_buff *skb)
         */
        new_state = msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE;
        if (!neigh && lladdr && idev && READ_ONCE(idev->cnf.forwarding)) {
+               mdelay(100); /* AI-POC: widen race window vs
concurrent addrconf_ifdown() NULLing dev->ip6_ptr */
                if (accept_untracked_na(dev, saddr)) {
                        neigh = neigh_create(&nd_tbl, &msg->target, dev);
                        new_state = NUD_STALE;

And our poc triggers:
```
[    0.887292] Oops: general protection fault, probably for
non-canonical address 0xdffffc0000000068: 0000 [#1] SMP KASAN NOPTI
[    0.887791] KASAN: null-ptr-deref in range
[0x0000000000000340-0x0000000000000347]
[    0.888101] CPU: 1 UID: 0 PID: 146 Comm: exploit Not tainted 7.1.0+
#9 PREEMPTLAZY
[    0.888419] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996),
BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/4
[    0.888880] RIP: 0010:ndisc_recv_na+0x67b/0xf30
[    0.889072] Code: 03 80 3c 02 00 0f 85 5f 07 00 00 49 8b af c0 00
00 00 48 b8 00 00 00 00 00 fc ff df 48 8d bd 40 03 00 00 e
[    0.889829] RSP: 0018:ffff8881029084f8 EFLAGS: 00010202
[    0.890043] RAX: dffffc0000000000 RBX: 1ffff110205210a8 RCX: 0000000000000001
[    0.890340] RDX: 0000000000000068 RSI: 0000000000004d9a RDI: 0000000000000340
[    0.890633] RBP: 0000000000000000 R08: 0000000000000004 R09: ffff888102908570
[    0.890921] R10: ffff88800ee7f792 R11: ffff888102908580 R12: ffff88800ee7f780
[    0.891207] R13: ffff88800ee7f778 R14: ffff88800ee7f792 R15: ffff88800f2d6000
[    0.891511] FS:  00007b8516aec6c0(0000) GS:ffff888177d3d000(0000)
knlGS:0000000000000000
[    0.891853] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    0.892094] CR2: 00007b8516aebb30 CR3: 000000000f164006 CR4: 0000000000772ef0
[    0.892402] PKRU: 55555554
[    0.892517] Call Trace:
[    0.892627]  <IRQ>
[    0.892718]  ? stack_trace_save+0x93/0xd0
[    0.892895]  ? __pfx_ndisc_recv_na+0x10/0x10
[    0.893087]  ? _raw_spin_unlock_irqrestore+0xe/0x40
[    0.893295]  ? stack_depot_save_flags+0x488/0x810
[    0.893506]  ? fib6_rule_lookup+0x40a/0x5a0
[    0.893689]  ? __napi_poll+0xa1/0x560
[    0.893852]  ? net_rx_action+0x401/0xd80
[    0.894025]  icmpv6_rcv+0x10a4/0x14e0
[    0.894181]  ip6_protocol_deliver_rcu+0xbd4/0x1340
[    0.894391]  ip6_input_finish+0x1d1/0x3a0
[    0.894559]  ip6_input+0x195/0x1d0
[    0.894709]  ? __pfx_ip6_input+0x10/0x10
[    0.894873]  ? __asan_memset+0x23/0x50
[    0.895034]  ip6_mc_input+0x26c/0x3a0
[    0.895188]  ipv6_rcv+0x31b/0x390
[    0.895340]  ? __pfx_ipv6_rcv+0x10/0x10
[    0.895501]  ? get_stack_info+0x2f/0x90
[    0.895665]  ? stack_access_ok+0xde/0x200
[    0.895833]  ? get_stack_info_noinstr+0x18/0x110
[    0.896036]  __netif_receive_skb_core.constprop.0+0xd78/0x2f00
[    0.896286]  ? common_startup_64+0x13e/0x151
[    0.896475]  ? common_startup_64+0x13e/0x151
[    0.896655]  ? __pfx_stack_trace_consume_entry+0x10/0x10
[    0.896875]  ? arch_stack_walk+0x99/0x100
[    0.897049]  ? __pfx___netif_receive_skb_core.constprop.0+0x10/0x10
[    0.897310]  ? common_startup_64+0x13e/0x151
[    0.897498]  ? stack_trace_save+0x93/0xd0
[    0.897668]  ? __pfx_stack_trace_save+0x10/0x10
[    0.897855]  ? stack_depot_save_flags+0x29/0x810
[    0.898046]  ? kasan_save_stack+0x33/0x60
[    0.898213]  ? kasan_save_track+0x14/0x30
[    0.898389]  ? kasan_save_free_info+0x3b/0x60
...
```

Weiming's patch works with a delay. The poc doesn't crash the kernel.
I noticed that you mentioned ipv6_rpl_srh_rcv. We actually have sent
one, but we later found that someone (Andrea Mayer) had sent the issue
earlier, so we just let them fix it:

https://lore.kernel.org/all/20260521200859.816b8923b5f27bba6124461e@uniroma2.it/

Xiang
> I believe we need more precise traffic and timing control, instead of
> aggressively ramping up traffic and load in an attempt to reproduce the
> issue.
>

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

* Re: [PATCH net] ipv6: ndisc: fix NULL deref in accept_untracked_na()
  2026-06-17  6:55 [PATCH net] ipv6: ndisc: fix NULL deref in accept_untracked_na() Weiming Shi
  2026-06-17  8:32 ` Jiayuan Chen
@ 2026-06-19  6:24 ` Jiayuan Chen
  1 sibling, 0 replies; 6+ messages in thread
From: Jiayuan Chen @ 2026-06-19  6:24 UTC (permalink / raw)
  To: Weiming Shi, David S . Miller, David Ahern, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, netdev, linux-kernel, Xiang Mei


On 6/17/26 2:55 PM, Weiming Shi wrote:
> accept_untracked_na() re-fetches the inet6_dev with __in6_dev_get(dev)
> and dereferences idev->cnf.accept_untracked_na without a NULL check,
> even though its only caller ndisc_recv_na() already fetched and
> NULL-checked idev for the same device.
>
> Both reads of dev->ip6_ptr run in the same RCU read-side critical
> section, but a concurrent addrconf_ifdown() can clear dev->ip6_ptr
> between them: lowering the MTU below IPV6_MIN_MTU calls addrconf_ifdown()
> without the synchronize_net() that orders the unregister path, so the
> re-fetch returns NULL and oopses:
>
>   BUG: KASAN: null-ptr-deref in ndisc_recv_na (net/ipv6/ndisc.c:974)
>   Read of size 4 at addr 0000000000000364
>   Call Trace:
>    <IRQ>
>    ndisc_recv_na (net/ipv6/ndisc.c:974)
>    icmpv6_rcv (net/ipv6/icmp.c:1193)
>    ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:479)
>    ip6_input_finish (net/ipv6/ip6_input.c:534)
>    ip6_input (net/ipv6/ip6_input.c:545)
>    ip6_mc_input (net/ipv6/ip6_input.c:635)
>    ipv6_rcv (net/ipv6/ip6_input.c:351)
>    </IRQ>
>
> It is reachable by an unprivileged user via a network namespace.
>
> Pass the caller's already validated idev instead of re-fetching it; the
> idev stays alive for the whole RCU critical section, so it is safe even
> after dev->ip6_ptr has been cleared.
>
> Fixes: aaa5f515b16b ("net: ipv6: new accept_untracked_na option to accept na only if in-network")
> Assisted-by: Claude:claude-opus-4-8
> Reported-by: Xiang Mei <xmei5@asu.edu>
> Signed-off-by: Weiming Shi <bestswngs@gmail.com>


Reviewed-by: Jiayuan Chen <jiayuan.chen@linux.dev>


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

end of thread, other threads:[~2026-06-19  6:24 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-17  6:55 [PATCH net] ipv6: ndisc: fix NULL deref in accept_untracked_na() Weiming Shi
2026-06-17  8:32 ` Jiayuan Chen
2026-06-17 13:38   ` Weiming Shi
2026-06-18  4:08     ` Jiayuan Chen
2026-06-19  4:05       ` Xiang Mei
2026-06-19  6:24 ` Jiayuan Chen

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