[PATCH nf v2] netfilter: ip6t_rpfilter: reject routes without inet6_dev

From: Weiming Shi

Date: Sun Sep 06 2026 - 03:57:23 EST


ip6_route_lookup() can return an error-free route whose rt6i_idev is
NULL. Lowering an external nexthop device's MTU below IPV6_MIN_MTU tears
down its inet6_dev while fib6_ifdown() leaves routes using nexthop objects
in the FIB. An unprivileged user can construct this state with rtnetlink
in a private user and network namespace, then trigger a NULL dereference
through an IPv6 rpfilter lookup:

Oops: general protection fault, probably for non-canonical address
0xdffffc0000000000
KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007]
RIP: rpfilter_mt (net/ipv6/netfilter/ip6t_rpfilter.c:75)
Call Trace:
ip6t_do_table (net/ipv6/netfilter/ip6_tables.c:316)
nf_hook_slow (net/netfilter/core.c:619)
ipv6_rcv (net/ipv6/ip6_input.c:351)
__netif_receive_skb_one_core (net/core/dev.c:6216)
process_backlog (net/core/dev.c:6680)
__napi_poll (net/core/dev.c:7739)
net_rx_action (net/core/dev.c:7959)
handle_softirqs (kernel/softirq.c:622)
do_softirq.part.0 (kernel/softirq.c:523)
__local_bh_enable_ip (kernel/softirq.c:450)
__dev_queue_xmit (net/core/dev.c:4913)
packet_sendmsg (net/packet/af_packet.c:3139)
__sys_sendto (net/socket.c:2252)
__x64_sys_sendto (net/socket.c:2259)
do_syscall_64 (arch/x86/entry/syscall_64.c:94)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
Kernel panic - not syncing: Fatal exception in interrupt

Reject routes without an inet6_dev immediately after lookup. Such routes
are not eligible for reverse-path filtering, and the check protects all
later rt6i_idev dereferences.

Fixes: e26f9a480fb6 ("netfilter: add ipv6 reverse path filter match")
Reported-by: co+459f67f4d8af8ce6@xxxxxxx
Closes: https://lore.kernel.org/all/VtWUkE8QzJt5CroTj2V2v3ZQ0gwbXZ7nq7I3@xxxxxxx/
Suggested-by: Florian Westphal <fw@xxxxxxxxx>
Assisted-by: Claude:gpt-5
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Weiming Shi <bestswngs@xxxxxxxxx>
---
v2:
- Reject routes without inet6_dev in both loose and strict modes, as
suggested by Florian.
v1: https://lore.kernel.org/all/20260906034939.3746540-1-bestswngs@xxxxxxxxx/
---
net/ipv6/netfilter/ip6t_rpfilter.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/ipv6/netfilter/ip6t_rpfilter.c b/net/ipv6/netfilter/ip6t_rpfilter.c
index 67c87a88cde4f..b5def30c3127e 100644
--- a/net/ipv6/netfilter/ip6t_rpfilter.c
+++ b/net/ipv6/netfilter/ip6t_rpfilter.c
@@ -61,7 +61,7 @@ static bool rpfilter_lookup_reverse6(struct net *net, const struct sk_buff *skb,
fl6.flowi6_oif = dev->ifindex;

rt = (void *)ip6_route_lookup(net, &fl6, skb, lookup_flags);
- if (rt->dst.error)
+ if (rt->dst.error || !rt->rt6i_idev)
goto out;

if (rt->rt6i_flags & (RTF_REJECT|RTF_ANYCAST))
--
2.55.0