* [PATCH nf 1/2] netfilter: nft_fib_ipv6: handle routes via external nexthop
@ 2026-05-19 4:14 Jiayuan Chen
2026-05-19 4:14 ` [PATCH nf 2/2] selftests: netfilter: add nft_fib_nexthop test Jiayuan Chen
2026-05-19 10:08 ` [PATCH nf 1/2] netfilter: nft_fib_ipv6: handle routes via external nexthop Phil Sutter
0 siblings, 2 replies; 8+ messages in thread
From: Jiayuan Chen @ 2026-05-19 4:14 UTC (permalink / raw)
To: netfilter-devel; +Cc: pablo, fw, phil, coreteam, Jiayuan Chen
fib6_info has a union:
union {
struct list_head fib6_siblings;
struct list_head nh_list;
};
Old-style multipath (ip -6 route add ... nexthop ... nexthop ...) uses
fib6_siblings. External nexthop (ip -6 route add ... nhid N) uses
nh_list, linked into &nh->f6i_list.
nft_fib6_info_nh_uses_dev() blindly walks &rt->fib6_siblings, causing
an OOB read past the struct nexthop slab when rt->nh is set:
==================================================================
BUG: KASAN: slab-out-of-bounds in nft_fib6_eval+0x1362/0x16c0
Read of size 8 at addr ffff888103a099d0 by task ping/386
CPU: 2 UID: 0 PID: 386 Comm: ping Not tainted 7.1.0-rc3+ #251 PREEMPT
Call Trace:
<IRQ>
dump_stack_lvl+0x76/0xa0
print_report+0xd1/0x5f0
kasan_report+0xe7/0x130
__asan_report_load8_noabort+0x14/0x30
nft_fib6_eval+0x1362/0x16c0
nft_do_chain+0x279/0x18c0
nft_do_chain_ipv6+0x1a8/0x230
nf_hook_slow+0xad/0x200
ipv6_rcv+0x152/0x380
__netif_receive_skb_one_core+0x118/0x1c0
==================================================================
Branch by route shape: when rt->nh is set, walk via
nexthop_for_each_fib6_nh() (also covers nh groups, which the original
code missed); otherwise walk fib6_siblings, guarded by fib6_nsiblings.
Fixes: 1c32b24c234b ("netfilter: nft_fib_ipv6: switch to fib6_lookup")
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
---
net/ipv6/netfilter/nft_fib_ipv6.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c
index 8b2dba88ee96..a44919f46de9 100644
--- a/net/ipv6/netfilter/nft_fib_ipv6.c
+++ b/net/ipv6/netfilter/nft_fib_ipv6.c
@@ -160,16 +160,32 @@ static bool nft_fib6_info_nh_dev_match(const struct net_device *nh_dev,
l3mdev_master_ifindex_rcu(nh_dev) == dev->ifindex;
}
+static int nft_fib6_nh_match_dev_cb(struct fib6_nh *nh, void *arg)
+{
+ const struct net_device *dev = arg;
+
+ return nft_fib6_info_nh_dev_match(nh->fib_nh_dev, dev) ? 1 : 0;
+}
+
static bool nft_fib6_info_nh_uses_dev(struct fib6_info *rt,
const struct net_device *dev)
{
const struct net_device *nh_dev;
struct fib6_info *iter;
+ /* External nexthop: fib6_siblings slot aliases nh_list, walk via nh. */
+ if (rt->nh)
+ return nexthop_for_each_fib6_nh(rt->nh,
+ nft_fib6_nh_match_dev_cb,
+ (void *)dev) != 0;
+
nh_dev = fib6_info_nh_dev(rt);
if (nft_fib6_info_nh_dev_match(nh_dev, dev))
return true;
+ if (!rt->fib6_nsiblings)
+ return false;
+
list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
nh_dev = fib6_info_nh_dev(iter);
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH nf 2/2] selftests: netfilter: add nft_fib_nexthop test 2026-05-19 4:14 [PATCH nf 1/2] netfilter: nft_fib_ipv6: handle routes via external nexthop Jiayuan Chen @ 2026-05-19 4:14 ` Jiayuan Chen 2026-05-19 18:53 ` Florian Westphal 2026-05-19 10:08 ` [PATCH nf 1/2] netfilter: nft_fib_ipv6: handle routes via external nexthop Phil Sutter 1 sibling, 1 reply; 8+ messages in thread From: Jiayuan Chen @ 2026-05-19 4:14 UTC (permalink / raw) To: netfilter-devel; +Cc: pablo, fw, phil, coreteam, Jiayuan Chen Cover nft_fib6_eval() over three route shapes and reproduce the OOB caused by the blind &rt->fib6_siblings walk: 1) single external nexthop (nhid) 2) external nexthop group (nhid -> group) 3) old-style multipath (nexthop ... nexthop ...) After the fix: ./nft_fib_nexthop.sh Nothing to flush PASS: single external nexthop (nhid) Flushed 1 nexthops PASS: nexthop group (nhid group 1/2) Flushed 2 nexthops PASS: old-style multipath (fib6_siblings) Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev> --- .../testing/selftests/net/netfilter/Makefile | 1 + .../net/netfilter/nft_fib_nexthop.sh | 104 ++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100755 tools/testing/selftests/net/netfilter/nft_fib_nexthop.sh diff --git a/tools/testing/selftests/net/netfilter/Makefile b/tools/testing/selftests/net/netfilter/Makefile index ee2d1a5254f8..d953ee218c0f 100644 --- a/tools/testing/selftests/net/netfilter/Makefile +++ b/tools/testing/selftests/net/netfilter/Makefile @@ -26,6 +26,7 @@ TEST_PROGS := \ nft_concat_range.sh \ nft_conntrack_helper.sh \ nft_fib.sh \ + nft_fib_nexthop.sh \ nft_flowtable.sh \ nft_interface_stress.sh \ nft_meta.sh \ diff --git a/tools/testing/selftests/net/netfilter/nft_fib_nexthop.sh b/tools/testing/selftests/net/netfilter/nft_fib_nexthop.sh new file mode 100755 index 000000000000..76f934156c8c --- /dev/null +++ b/tools/testing/selftests/net/netfilter/nft_fib_nexthop.sh @@ -0,0 +1,104 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# shellcheck disable=SC2154 +# +# Exercise nft_fib6_eval()'s sibling/nh enumeration on three route shapes: +# 1) route via a single external nexthop (nhid) +# 2) route via an external nexthop group (nhid -> group) +# 3) route via old-style multipath (nexthop ... nexthop ...) +# +# Topology similar to nft_fib.sh, without ns2; two dummy interfaces on +# nsrouter host the nh devices: +# +# dead:1::99 dead:1::1 dummy0 dead:2::1 +# ns1 <-----veth-----> nsrouter +# dummy1 dead:9::1 + +source lib.sh + +ret=0 + +checktool "nft --version" "run test without nft" +checktool "ip -V" "run test without iproute2" + +setup_ns nsrouter ns1 +trap cleanup_all_ns EXIT + +if ! ip link add veth0 netns "$nsrouter" type veth peer name eth0 netns "$ns1" \ + > /dev/null 2>&1; then + echo "SKIP: No virtual ethernet pair device support in kernel" + exit $ksft_skip +fi + +ip -net "$ns1" link set lo up +ip -net "$ns1" link set eth0 up +ip -net "$ns1" -6 addr add dead:1::99/64 dev eth0 nodad +ip -net "$ns1" -6 route add default via dead:1::1 + +ip -net "$nsrouter" link set lo up +ip -net "$nsrouter" link set veth0 up +ip -net "$nsrouter" -6 addr add dead:1::1/64 dev veth0 nodad + +if ! ip -net "$nsrouter" link add dummy0 type dummy 2>/dev/null; then + echo "SKIP: dummy netdev not available" + exit $ksft_skip +fi +ip -net "$nsrouter" link set dummy0 up +ip -net "$nsrouter" -6 addr add dead:2::1/64 dev dummy0 nodad + +ip -net "$nsrouter" link add dummy1 type dummy +ip -net "$nsrouter" link set dummy1 up +ip -net "$nsrouter" -6 addr add dead:9::1/64 dev dummy1 nodad + +ip netns exec "$nsrouter" sysctl -q net.ipv6.conf.all.forwarding=1 + +load_fib_rule() { + ip netns exec "$nsrouter" nft -f /dev/stdin <<EOF +flush ruleset +table ip6 t { + chain c { + type filter hook prerouting priority 0; policy accept; + fib daddr . iif oif missing counter + } +} +EOF +} + +run_scenario() { + local what="$1"; shift + + ip -net "$nsrouter" -6 route del dead:dead::/64 2>/dev/null || true + ip -net "$nsrouter" -6 nexthop flush 2>/dev/null || true + + "$@" || { echo "SKIP ($what): could not configure route"; return; } + + load_fib_rule || { echo "FAIL ($what): nft load"; ret=1; return; } + + ip netns exec "$ns1" ping -6 -c 1 -W 1 dead:dead::1 \ + > /dev/null 2>&1 || true + + echo "PASS: $what" +} + +scenario_single_nh() { + ip -net "$nsrouter" nexthop add id 1 via dead:2::2 dev dummy0 + ip -net "$nsrouter" -6 route add dead:dead::/64 nhid 1 +} +run_scenario "single external nexthop (nhid)" scenario_single_nh + +scenario_nh_group() { + ip -net "$nsrouter" nexthop add id 1 via dead:2::2 dev dummy0 + ip -net "$nsrouter" nexthop add id 2 via dead:9::2 dev dummy1 + ip -net "$nsrouter" nexthop add id 100 group 1/2 + ip -net "$nsrouter" -6 route add dead:dead::/64 nhid 100 +} +run_scenario "nexthop group (nhid group 1/2)" scenario_nh_group + +scenario_old_multipath() { + ip -net "$nsrouter" -6 route add dead:dead::/64 \ + nexthop via dead:2::2 dev dummy0 \ + nexthop via dead:9::2 dev dummy1 +} +run_scenario "old-style multipath (fib6_siblings)" scenario_old_multipath + +exit $ret -- 2.43.0 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH nf 2/2] selftests: netfilter: add nft_fib_nexthop test 2026-05-19 4:14 ` [PATCH nf 2/2] selftests: netfilter: add nft_fib_nexthop test Jiayuan Chen @ 2026-05-19 18:53 ` Florian Westphal 2026-05-20 1:26 ` Jiayuan Chen 0 siblings, 1 reply; 8+ messages in thread From: Florian Westphal @ 2026-05-19 18:53 UTC (permalink / raw) To: Jiayuan Chen; +Cc: netfilter-devel, pablo, phil, coreteam Jiayuan Chen <jiayuan.chen@linux.dev> wrote: > Cover nft_fib6_eval() over three route shapes and reproduce the OOB > caused by the blind &rt->fib6_siblings walk: > > 1) single external nexthop (nhid) > 2) external nexthop group (nhid -> group) > 3) old-style multipath (nexthop ... nexthop ...) https://sashiko.dev/#/patchset/20260519041431.396218-1-jiayuan.chen%40linux.dev Specifially: 'Does the test need to verify the datapath actually evaluated correctly?' and 'It looks like the test also sets up an nftables counter rule but doesn't verify if the rule actually matched any packets.' I prefer functionality test over 'bug trap' tests. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH nf 2/2] selftests: netfilter: add nft_fib_nexthop test 2026-05-19 18:53 ` Florian Westphal @ 2026-05-20 1:26 ` Jiayuan Chen 0 siblings, 0 replies; 8+ messages in thread From: Jiayuan Chen @ 2026-05-20 1:26 UTC (permalink / raw) To: Florian Westphal; +Cc: netfilter-devel, pablo, phil, coreteam On 5/20/26 2:53 AM, Florian Westphal wrote: > Specifially: 'Does the test need to verify the datapath actually > evaluated correctly?' and > 'It looks like the test also sets up an nftables counter rule but doesn't > verify if the rule actually matched any packets.' > > I prefer functionality test over 'bug trap' tests. Agreed -- functional verification is the right approach here. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH nf 1/2] netfilter: nft_fib_ipv6: handle routes via external nexthop 2026-05-19 4:14 [PATCH nf 1/2] netfilter: nft_fib_ipv6: handle routes via external nexthop Jiayuan Chen 2026-05-19 4:14 ` [PATCH nf 2/2] selftests: netfilter: add nft_fib_nexthop test Jiayuan Chen @ 2026-05-19 10:08 ` Phil Sutter 2026-05-19 10:50 ` Jiayuan Chen 1 sibling, 1 reply; 8+ messages in thread From: Phil Sutter @ 2026-05-19 10:08 UTC (permalink / raw) To: Jiayuan Chen; +Cc: netfilter-devel, pablo, fw, coreteam Hi, On Tue, May 19, 2026 at 12:14:30PM +0800, Jiayuan Chen wrote: > fib6_info has a union: > > union { > struct list_head fib6_siblings; > struct list_head nh_list; > }; > > Old-style multipath (ip -6 route add ... nexthop ... nexthop ...) uses > fib6_siblings. External nexthop (ip -6 route add ... nhid N) uses > nh_list, linked into &nh->f6i_list. > > nft_fib6_info_nh_uses_dev() blindly walks &rt->fib6_siblings, causing > an OOB read past the struct nexthop slab when rt->nh is set: > > ================================================================== > BUG: KASAN: slab-out-of-bounds in nft_fib6_eval+0x1362/0x16c0 > Read of size 8 at addr ffff888103a099d0 by task ping/386 > > CPU: 2 UID: 0 PID: 386 Comm: ping Not tainted 7.1.0-rc3+ #251 PREEMPT > Call Trace: > <IRQ> > dump_stack_lvl+0x76/0xa0 > print_report+0xd1/0x5f0 > kasan_report+0xe7/0x130 > __asan_report_load8_noabort+0x14/0x30 > nft_fib6_eval+0x1362/0x16c0 > nft_do_chain+0x279/0x18c0 > nft_do_chain_ipv6+0x1a8/0x230 > nf_hook_slow+0xad/0x200 > ipv6_rcv+0x152/0x380 > __netif_receive_skb_one_core+0x118/0x1c0 > ================================================================== > > Branch by route shape: when rt->nh is set, walk via > nexthop_for_each_fib6_nh() (also covers nh groups, which the original > code missed); otherwise walk fib6_siblings, guarded by fib6_nsiblings. > > Fixes: 1c32b24c234b ("netfilter: nft_fib_ipv6: switch to fib6_lookup") > Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev> > --- > net/ipv6/netfilter/nft_fib_ipv6.c | 16 ++++++++++++++++ > 1 file changed, 16 insertions(+) > > diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c > index 8b2dba88ee96..a44919f46de9 100644 > --- a/net/ipv6/netfilter/nft_fib_ipv6.c > +++ b/net/ipv6/netfilter/nft_fib_ipv6.c > @@ -160,16 +160,32 @@ static bool nft_fib6_info_nh_dev_match(const struct net_device *nh_dev, > l3mdev_master_ifindex_rcu(nh_dev) == dev->ifindex; > } > > +static int nft_fib6_nh_match_dev_cb(struct fib6_nh *nh, void *arg) > +{ > + const struct net_device *dev = arg; > + > + return nft_fib6_info_nh_dev_match(nh->fib_nh_dev, dev) ? 1 : 0; Why the ternary here? The function returns bool, but the iterator merely checks the value for 0 and caller returns the value as bool as well. > +} > + > static bool nft_fib6_info_nh_uses_dev(struct fib6_info *rt, > const struct net_device *dev) > { > const struct net_device *nh_dev; > struct fib6_info *iter; > > + /* External nexthop: fib6_siblings slot aliases nh_list, walk via nh. */ > + if (rt->nh) > + return nexthop_for_each_fib6_nh(rt->nh, > + nft_fib6_nh_match_dev_cb, > + (void *)dev) != 0; As above, the '!= 0' is not needed, is it? > + > nh_dev = fib6_info_nh_dev(rt); > if (nft_fib6_info_nh_dev_match(nh_dev, dev)) > return true; > > + if (!rt->fib6_nsiblings) Should this access using READ_ONCE() as per commit 31d7d67ba127 ("ipv6: annotate data-races around rt->fib6_nsiblings")? > + return false; > + > list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) { > nh_dev = fib6_info_nh_dev(iter); I thought about open-coding this to void the need for the callback wrapper, but it's not worth it. Cheers, Phil ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH nf 1/2] netfilter: nft_fib_ipv6: handle routes via external nexthop 2026-05-19 10:08 ` [PATCH nf 1/2] netfilter: nft_fib_ipv6: handle routes via external nexthop Phil Sutter @ 2026-05-19 10:50 ` Jiayuan Chen 2026-05-19 14:15 ` Phil Sutter 0 siblings, 1 reply; 8+ messages in thread From: Jiayuan Chen @ 2026-05-19 10:50 UTC (permalink / raw) To: Phil Sutter; +Cc: netfilter-devel, pablo, fw, coreteam Hi Phil, Thanks for your review. On 5/19/26 6:08 PM, Phil Sutter wrote: > Hi, > > On Tue, May 19, 2026 at 12:14:30PM +0800, Jiayuan Chen wrote: >> fib6_info has a union: >> >> union { >> struct list_head fib6_siblings; >> struct list_head nh_list; >> }; >> >> Old-style multipath (ip -6 route add ... nexthop ... nexthop ...) uses >> fib6_siblings. External nexthop (ip -6 route add ... nhid N) uses >> nh_list, linked into &nh->f6i_list. >> >> nft_fib6_info_nh_uses_dev() blindly walks &rt->fib6_siblings, causing >> an OOB read past the struct nexthop slab when rt->nh is set: >> >> ================================================================== >> BUG: KASAN: slab-out-of-bounds in nft_fib6_eval+0x1362/0x16c0 >> Read of size 8 at addr ffff888103a099d0 by task ping/386 >> >> CPU: 2 UID: 0 PID: 386 Comm: ping Not tainted 7.1.0-rc3+ #251 PREEMPT >> Call Trace: >> <IRQ> >> dump_stack_lvl+0x76/0xa0 >> print_report+0xd1/0x5f0 >> kasan_report+0xe7/0x130 >> __asan_report_load8_noabort+0x14/0x30 >> nft_fib6_eval+0x1362/0x16c0 >> nft_do_chain+0x279/0x18c0 >> nft_do_chain_ipv6+0x1a8/0x230 >> nf_hook_slow+0xad/0x200 >> ipv6_rcv+0x152/0x380 >> __netif_receive_skb_one_core+0x118/0x1c0 >> ================================================================== >> >> Branch by route shape: when rt->nh is set, walk via >> nexthop_for_each_fib6_nh() (also covers nh groups, which the original >> code missed); otherwise walk fib6_siblings, guarded by fib6_nsiblings. >> >> Fixes: 1c32b24c234b ("netfilter: nft_fib_ipv6: switch to fib6_lookup") >> Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev> >> --- >> net/ipv6/netfilter/nft_fib_ipv6.c | 16 ++++++++++++++++ >> 1 file changed, 16 insertions(+) >> >> diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c >> index 8b2dba88ee96..a44919f46de9 100644 >> --- a/net/ipv6/netfilter/nft_fib_ipv6.c >> +++ b/net/ipv6/netfilter/nft_fib_ipv6.c >> @@ -160,16 +160,32 @@ static bool nft_fib6_info_nh_dev_match(const struct net_device *nh_dev, >> l3mdev_master_ifindex_rcu(nh_dev) == dev->ifindex; >> } >> >> +static int nft_fib6_nh_match_dev_cb(struct fib6_nh *nh, void *arg) >> +{ >> + const struct net_device *dev = arg; >> + >> + return nft_fib6_info_nh_dev_match(nh->fib_nh_dev, dev) ? 1 : 0; > Why the ternary here? The function returns bool, but the iterator merely > checks the value for 0 and caller returns the value as bool as well. > >> +} >> + >> static bool nft_fib6_info_nh_uses_dev(struct fib6_info *rt, >> const struct net_device *dev) >> { >> const struct net_device *nh_dev; >> struct fib6_info *iter; >> >> + /* External nexthop: fib6_siblings slot aliases nh_list, walk via nh. */ >> + if (rt->nh) >> + return nexthop_for_each_fib6_nh(rt->nh, >> + nft_fib6_nh_match_dev_cb, >> + (void *)dev) != 0; All make sense ! >> + >> nh_dev = fib6_info_nh_dev(rt); >> if (nft_fib6_info_nh_dev_match(nh_dev, dev)) >> return true; >> >> + if (!rt->fib6_nsiblings) > Should this access using READ_ONCE() as per commit 31d7d67ba127 ("ipv6: > annotate data-races around rt->fib6_nsiblings")? You are right, we need READ_ONCE since fib6_add_rt2node will modify @fib6_nsiblings . >> + return false; >> + >> list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) { Now I think we should also change list_for_each_entry into list_for_each_entry_rcu for the same reason. But I'm not sure whether it is appropriate or not since this patch target to commit in Fiexes tag. May be a followup patch is necessary. >> nh_dev = fib6_info_nh_dev(iter); > I thought about open-coding this to void the need for the callback > wrapper, but it's not worth it. > > Cheers, Phil ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH nf 1/2] netfilter: nft_fib_ipv6: handle routes via external nexthop 2026-05-19 10:50 ` Jiayuan Chen @ 2026-05-19 14:15 ` Phil Sutter 2026-05-19 14:33 ` Jiayuan Chen 0 siblings, 1 reply; 8+ messages in thread From: Phil Sutter @ 2026-05-19 14:15 UTC (permalink / raw) To: Jiayuan Chen; +Cc: netfilter-devel, pablo, fw, coreteam On Tue, May 19, 2026 at 06:50:55PM +0800, Jiayuan Chen wrote: [...] > On 5/19/26 6:08 PM, Phil Sutter wrote: > > Hi, > > > > On Tue, May 19, 2026 at 12:14:30PM +0800, Jiayuan Chen wrote: > >> fib6_info has a union: > >> > >> union { > >> struct list_head fib6_siblings; > >> struct list_head nh_list; > >> }; > >> > >> Old-style multipath (ip -6 route add ... nexthop ... nexthop ...) uses > >> fib6_siblings. External nexthop (ip -6 route add ... nhid N) uses > >> nh_list, linked into &nh->f6i_list. > >> > >> nft_fib6_info_nh_uses_dev() blindly walks &rt->fib6_siblings, causing > >> an OOB read past the struct nexthop slab when rt->nh is set: > >> > >> ================================================================== > >> BUG: KASAN: slab-out-of-bounds in nft_fib6_eval+0x1362/0x16c0 > >> Read of size 8 at addr ffff888103a099d0 by task ping/386 > >> > >> CPU: 2 UID: 0 PID: 386 Comm: ping Not tainted 7.1.0-rc3+ #251 PREEMPT > >> Call Trace: > >> <IRQ> > >> dump_stack_lvl+0x76/0xa0 > >> print_report+0xd1/0x5f0 > >> kasan_report+0xe7/0x130 > >> __asan_report_load8_noabort+0x14/0x30 > >> nft_fib6_eval+0x1362/0x16c0 > >> nft_do_chain+0x279/0x18c0 > >> nft_do_chain_ipv6+0x1a8/0x230 > >> nf_hook_slow+0xad/0x200 > >> ipv6_rcv+0x152/0x380 > >> __netif_receive_skb_one_core+0x118/0x1c0 > >> ================================================================== > >> > >> Branch by route shape: when rt->nh is set, walk via > >> nexthop_for_each_fib6_nh() (also covers nh groups, which the original > >> code missed); otherwise walk fib6_siblings, guarded by fib6_nsiblings. > >> > >> Fixes: 1c32b24c234b ("netfilter: nft_fib_ipv6: switch to fib6_lookup") > >> Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev> > >> --- > >> net/ipv6/netfilter/nft_fib_ipv6.c | 16 ++++++++++++++++ > >> 1 file changed, 16 insertions(+) > >> > >> diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c > >> index 8b2dba88ee96..a44919f46de9 100644 > >> --- a/net/ipv6/netfilter/nft_fib_ipv6.c > >> +++ b/net/ipv6/netfilter/nft_fib_ipv6.c > >> @@ -160,16 +160,32 @@ static bool nft_fib6_info_nh_dev_match(const struct net_device *nh_dev, > >> l3mdev_master_ifindex_rcu(nh_dev) == dev->ifindex; > >> } > >> > >> +static int nft_fib6_nh_match_dev_cb(struct fib6_nh *nh, void *arg) > >> +{ > >> + const struct net_device *dev = arg; > >> + > >> + return nft_fib6_info_nh_dev_match(nh->fib_nh_dev, dev) ? 1 : 0; > > Why the ternary here? The function returns bool, but the iterator merely > > checks the value for 0 and caller returns the value as bool as well. > > > >> +} > >> + > >> static bool nft_fib6_info_nh_uses_dev(struct fib6_info *rt, > >> const struct net_device *dev) > >> { > >> const struct net_device *nh_dev; > >> struct fib6_info *iter; > >> > >> + /* External nexthop: fib6_siblings slot aliases nh_list, walk via nh. */ > >> + if (rt->nh) > >> + return nexthop_for_each_fib6_nh(rt->nh, > >> + nft_fib6_nh_match_dev_cb, > >> + (void *)dev) != 0; > > > All make sense ! > > > >> + > >> nh_dev = fib6_info_nh_dev(rt); > >> if (nft_fib6_info_nh_dev_match(nh_dev, dev)) > >> return true; > >> > >> + if (!rt->fib6_nsiblings) > > Should this access using READ_ONCE() as per commit 31d7d67ba127 ("ipv6: > > annotate data-races around rt->fib6_nsiblings")? > > > You are right, we need READ_ONCE since fib6_add_rt2node will modify > @fib6_nsiblings . > > > >> + return false; > >> + > >> list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) { > > > Now I think we should also change list_for_each_entry into > list_for_each_entry_rcu for the same reason. Seems legit! I missed that one because most examples in net/ipv6/route.c use a non-RCU variant, I guess because exclusive access is ensured via other means. > But I'm not sure whether it is appropriate or not since this patch > target to commit in Fiexes tag. > > May be a followup patch is necessary. I'd submit the _rcu fix in an initial patch of the series, so the one introducing the fib6_nsiblings check extends correct code (using _rcu) with a correct pattern (READ_ONCE). Thanks, Phil ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH nf 1/2] netfilter: nft_fib_ipv6: handle routes via external nexthop 2026-05-19 14:15 ` Phil Sutter @ 2026-05-19 14:33 ` Jiayuan Chen 0 siblings, 0 replies; 8+ messages in thread From: Jiayuan Chen @ 2026-05-19 14:33 UTC (permalink / raw) To: Phil Sutter; +Cc: netfilter-devel, pablo, fw, coreteam On 5/19/26 10:15 PM, Phil Sutter wrote: > On Tue, May 19, 2026 at 06:50:55PM +0800, Jiayuan Chen wrote: > [...] >> On 5/19/26 6:08 PM, Phil Sutter wrote: >>> Hi, >>> >>> On Tue, May 19, 2026 at 12:14:30PM +0800, Jiayuan Chen wrote: >>>> fib6_info has a union: >>>> >>>> union { >>>> struct list_head fib6_siblings; >>>> struct list_head nh_list; >>>> }; >>>> >>>> Old-style multipath (ip -6 route add ... nexthop ... nexthop ...) uses >>>> fib6_siblings. External nexthop (ip -6 route add ... nhid N) uses >>>> nh_list, linked into &nh->f6i_list. >>>> >>>> nft_fib6_info_nh_uses_dev() blindly walks &rt->fib6_siblings, causing >>>> an OOB read past the struct nexthop slab when rt->nh is set: >>>> >>>> ================================================================== >>>> BUG: KASAN: slab-out-of-bounds in nft_fib6_eval+0x1362/0x16c0 >>>> Read of size 8 at addr ffff888103a099d0 by task ping/386 >>>> >>>> CPU: 2 UID: 0 PID: 386 Comm: ping Not tainted 7.1.0-rc3+ #251 PREEMPT >>>> Call Trace: >>>> <IRQ> >>>> dump_stack_lvl+0x76/0xa0 >>>> print_report+0xd1/0x5f0 >>>> kasan_report+0xe7/0x130 >>>> __asan_report_load8_noabort+0x14/0x30 >>>> nft_fib6_eval+0x1362/0x16c0 >>>> nft_do_chain+0x279/0x18c0 >>>> nft_do_chain_ipv6+0x1a8/0x230 >>>> nf_hook_slow+0xad/0x200 >>>> ipv6_rcv+0x152/0x380 >>>> __netif_receive_skb_one_core+0x118/0x1c0 >>>> ================================================================== >>>> >>>> Branch by route shape: when rt->nh is set, walk via >>>> nexthop_for_each_fib6_nh() (also covers nh groups, which the original >>>> code missed); otherwise walk fib6_siblings, guarded by fib6_nsiblings. >>>> >>>> Fixes: 1c32b24c234b ("netfilter: nft_fib_ipv6: switch to fib6_lookup") >>>> Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev> >>>> --- >>>> net/ipv6/netfilter/nft_fib_ipv6.c | 16 ++++++++++++++++ >>>> 1 file changed, 16 insertions(+) >>>> >>>> diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c >>>> index 8b2dba88ee96..a44919f46de9 100644 >>>> --- a/net/ipv6/netfilter/nft_fib_ipv6.c >>>> +++ b/net/ipv6/netfilter/nft_fib_ipv6.c >>>> @@ -160,16 +160,32 @@ static bool nft_fib6_info_nh_dev_match(const struct net_device *nh_dev, >>>> l3mdev_master_ifindex_rcu(nh_dev) == dev->ifindex; >>>> } >>>> >>>> +static int nft_fib6_nh_match_dev_cb(struct fib6_nh *nh, void *arg) >>>> +{ >>>> + const struct net_device *dev = arg; >>>> + >>>> + return nft_fib6_info_nh_dev_match(nh->fib_nh_dev, dev) ? 1 : 0; >>> Why the ternary here? The function returns bool, but the iterator merely >>> checks the value for 0 and caller returns the value as bool as well. >>> >>>> +} >>>> + >>>> static bool nft_fib6_info_nh_uses_dev(struct fib6_info *rt, >>>> const struct net_device *dev) >>>> { >>>> const struct net_device *nh_dev; >>>> struct fib6_info *iter; >>>> >>>> + /* External nexthop: fib6_siblings slot aliases nh_list, walk via nh. */ >>>> + if (rt->nh) >>>> + return nexthop_for_each_fib6_nh(rt->nh, >>>> + nft_fib6_nh_match_dev_cb, >>>> + (void *)dev) != 0; >> >> All make sense ! >> >> >>>> + >>>> nh_dev = fib6_info_nh_dev(rt); >>>> if (nft_fib6_info_nh_dev_match(nh_dev, dev)) >>>> return true; >>>> >>>> + if (!rt->fib6_nsiblings) >>> Should this access using READ_ONCE() as per commit 31d7d67ba127 ("ipv6: >>> annotate data-races around rt->fib6_nsiblings")? >> >> You are right, we need READ_ONCE since fib6_add_rt2node will modify >> @fib6_nsiblings . >> >> >>>> + return false; >>>> + >>>> list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) { >> >> Now I think we should also change list_for_each_entry into >> list_for_each_entry_rcu for the same reason. > Seems legit! I missed that one because most examples in net/ipv6/route.c > use a non-RCU variant, I guess because exclusive access is ensured via > other means. > >> But I'm not sure whether it is appropriate or not since this patch >> target to commit in Fiexes tag. >> >> May be a followup patch is necessary. > I'd submit the _rcu fix in an initial patch of the series, so the one > introducing the fib6_nsiblings check extends correct code (using _rcu) > with a correct pattern (READ_ONCE). > > Thanks, Phil Got it - will respin as v2. Sending after a short cool-down. Respect ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-05-20 1:26 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-19 4:14 [PATCH nf 1/2] netfilter: nft_fib_ipv6: handle routes via external nexthop Jiayuan Chen 2026-05-19 4:14 ` [PATCH nf 2/2] selftests: netfilter: add nft_fib_nexthop test Jiayuan Chen 2026-05-19 18:53 ` Florian Westphal 2026-05-20 1:26 ` Jiayuan Chen 2026-05-19 10:08 ` [PATCH nf 1/2] netfilter: nft_fib_ipv6: handle routes via external nexthop Phil Sutter 2026-05-19 10:50 ` Jiayuan Chen 2026-05-19 14:15 ` Phil Sutter 2026-05-19 14:33 ` Jiayuan Chen
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.