Skip to contentSkip to conversation

ipv6: fix NULL pointer deref in ip6_rt_get_dev_rcu()

PATCH Applied Jakub Kicinski 3 participants 5 messages Mar 1 – Mar 4

Subsystems (3)
lore
Share & tools
lore

Jakub Kicinski

author maintainer
#0 lore raw

PATCH 1/1 ipv6: fix NULL pointer deref in ip6_rt_get_dev_rcu()

l3mdev_master_dev_rcu() can return NULL when the slave device is being un-slaved from a VRF. All other callers deal with this, but we lost the fallback to loopback in ip6_rt_pcpu_alloc() -> ip6_rt_get_dev_rcu() with commit 4832c30d5458 ("net: ipv6: put host and anycast routes on device with address").

  KASAN: null-ptr-deref in range [0x0000000000000108-0x000000000000010f]
  RIP: 0010:ip6_rt_pcpu_alloc (net/ipv6/route.c:1418)
  Call Trace:
   ip6_pol_route (net/ipv6/route.c:2318)
   fib6_rule_lookup (net/ipv6/fib6_rules.c:115)
   ip6_route_output_flags (net/ipv6/route.c:2607)
   vrf_process_v6_outbound (drivers/net/vrf.c:437)

I was tempted to rework the un-slaving code to clear the flag first and insert synchronize_rcu() before we remove the upper. But looks like the explicit fallback to loopback_dev is an established pattern. And I guess avoiding the synchronize_rcu() is nice, too.

Fixes 4832c30d5458 ("net: ipv6: put host and anycast routes on device with address")

---
now that NIPA is not full of the tc-action.sh crashes this 1 in ~30 runs crash in fcnal_ipv6.sh pops out quite clearly.

---
 net/ipv6/route.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
net/ipv6/route.c +2 -1
index 85df25c36409..7db0c837196c 100644
@@ -1063,7 +1063,8 @@ static struct net_device *ip6_rt_get_dev_rcu(const struct fib6_result *res)
 		 */
 		if (netif_is_l3_slave(dev) &&
 		    !rt6_need_strict(&res->f6i->fib6_dst.addr))
-			dev = l3mdev_master_dev_rcu(dev);
+			dev = l3mdev_master_dev_rcu(dev) ? :
+			      dev_net(dev)->loopback_dev;
 		else if (!netif_is_l3_master(dev))
 			dev = dev_net(dev)->loopback_dev;
 		/* last case is netif_is_l3_master(dev) is true in which
Signature · 1 line
2.53.0
maintainer
#1 lore raw

net/ipv6/route.c static struct net_device *ip6_rt_get_dev_rcu(const struct fib6_result *res) · line 1067

Quoted 11 lines · Jakub Kicinski
net/ipv6/route.c +2 -1
index 85df25c36409..7db0c837196c 100644
@@ -1063,7 +1063,8 @@ static struct net_device *ip6_rt_get_dev_rcu(const struct fib6_result *res)
 		 */
 		if (netif_is_l3_slave(dev) &&
 		    !rt6_need_strict(&res->f6i->fib6_dst.addr))
-			dev = l3mdev_master_dev_rcu(dev);
+			dev = l3mdev_master_dev_rcu(dev) ? :
+			      dev_net(dev)->loopback_dev;

how can the flag on the netdev say there is L3 master, yet the device not be there within an rcu window?

Quoted 3 lines
net/ipv6/route.c +0 -0
 		else if (!netif_is_l3_master(dev))
 			dev = dev_net(dev)->loopback_dev;
 		/* last case is netif_is_l3_master(dev) is true in which
author maintainer
#2 lore raw
Quoted 10 lines · On Sun, 1 Mar 2026 18:38:53 -0700 David Ahern wrote:

net/ipv6/route.c static struct net_device *ip6_rt_get_dev_rcu(const struct fib6_result *res) · line 1067

Quoted 7 lines
net/ipv6/route.c +2 -1
@@ -1063,7 +1063,8 @@ static struct net_device *ip6_rt_get_dev_rcu(const struct fib6_result *res)
 		 */
 		if (netif_is_l3_slave(dev) &&
 		    !rt6_need_strict(&res->f6i->fib6_dst.addr))
-			dev = l3mdev_master_dev_rcu(dev);
+			dev = l3mdev_master_dev_rcu(dev) ? :
+			      dev_net(dev)->loopback_dev;  

how can the flag on the netdev say there is L3 master, yet the device not be there within an rcu window?

1) We call netif_is_l3_slave() twice, once here and then again in l3mdev_master_dev_rcu(), the flag may get cleared in between the two.

2)

static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev)

{
        netdev_upper_dev_unlink(port_dev, dev);
	// No sync RCU here, also I'd say the order is inverted?
        port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE;
maintainer Reviewed-by on v1 1/1
#3 lore raw
In reply to Jakub Kicinski
Quoted 21 lines · Jakub Kicinski

l3mdev_master_dev_rcu() can return NULL when the slave device is being un-slaved from a VRF. All other callers deal with this, but we lost the fallback to loopback in ip6_rt_pcpu_alloc() -> ip6_rt_get_dev_rcu() with commit 4832c30d5458 ("net: ipv6: put host and anycast routes on device with address").

  KASAN: null-ptr-deref in range [0x0000000000000108-0x000000000000010f]
  RIP: 0010:ip6_rt_pcpu_alloc (net/ipv6/route.c:1418)
  Call Trace:
   ip6_pol_route (net/ipv6/route.c:2318)
   fib6_rule_lookup (net/ipv6/fib6_rules.c:115)
   ip6_route_output_flags (net/ipv6/route.c:2607)
   vrf_process_v6_outbound (drivers/net/vrf.c:437)

I was tempted to rework the un-slaving code to clear the flag first and insert synchronize_rcu() before we remove the upper. But looks like the explicit fallback to loopback_dev is an established pattern. And I guess avoiding the synchronize_rcu() is nice, too.

Fixes 4832c30d5458 ("net: ipv6: put host and anycast routes on device with address")
Quoted 9 lines

---
now that NIPA is not full of the tc-action.sh crashes this 1 in ~30 runs crash in fcnal_ipv6.sh pops out quite clearly.

---
 net/ipv6/route.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
#4 lore raw
In reply to Jakub Kicinski
This patch was applied to netdev/net.git (main) by Jakub Kicinski <[email protected]>:

Hello:

This patch was applied to netdev/net.git (main)
by Jakub Kicinski <[email protected]>:

Quoted 14 lines · On Sun, 1 Mar 2026 11:45:48 -0800 you wrote:

l3mdev_master_dev_rcu() can return NULL when the slave device is being un-slaved from a VRF. All other callers deal with this, but we lost the fallback to loopback in ip6_rt_pcpu_alloc() -> ip6_rt_get_dev_rcu() with commit 4832c30d5458 ("net: ipv6: put host and anycast routes on device with address").

  KASAN: null-ptr-deref in range [0x0000000000000108-0x000000000000010f]
  RIP: 0010:ip6_rt_pcpu_alloc (net/ipv6/route.c:1418)
  Call Trace:
   ip6_pol_route (net/ipv6/route.c:2318)
   fib6_rule_lookup (net/ipv6/fib6_rules.c:115)
   ip6_route_output_flags (net/ipv6/route.c:2607)
   vrf_process_v6_outbound (drivers/net/vrf.c:437)
Quoted 1 line

[...]

Here is the summary with links:
- [net] ipv6: fix NULL pointer deref in ip6_rt_get_dev_rcu()

    https://git.kernel.org/netdev/net/c/2ffb4f5c2ccb

You are awesome, thank you!

Signature · 2 lines
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html