From: Kuniyuki Iwashima Date: Tue, 16 Jun 2026 19:13:48 +0000 (+0000) Subject: ipv4: fib_rule: Move fib4_rules_exit() to ->exit(). X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d954a67a7dfa58b7a9b3194322d321b940eb60c8;p=thirdparty%2Flinux.git ipv4: fib_rule: Move fib4_rules_exit() to ->exit(). syzbot reported use-after-free of net->ipv4.rules_ops. [0] It can be reproduced with these commands: while true; do ip netns add ns1 ip -n ns1 link set dev lo up ip -n ns1 address add 192.0.2.1/24 dev lo ip -n ns1 link add name dummy1 up type dummy ip -n ns1 address add 198.51.100.1/24 dev dummy1 ip -n ns1 rule add ipproto tcp sport 12345 table 12345 ip -n ns1 fou add port 5555 ipproto 47 local 192.0.2.1 peer 198.51.100.2 peer_port 54321 ip netns del ns1 done The cited commit moved fib4_rules_exit() earlier to ->exit_rtnl(), but the kernel socket destroyed in ->exit() could eventually reach __fib_lookup(). I left fib4_rules_exit() in ->exit_rtnl() because fib4_rule_delete() calls fib_unmerge(), which requires RTNL. However, when ->delete() is called, ->configure() has already been called, thus fib_unmerge() in ->delete() has no effect. Let's remove fib_unmerge() in fib4_rule_delete() and move fib4_rules_exit() to ->exit(). Many thanks to Ido Schimmel for providing the nice repro very quickly. Note that we can make fib_rules_ops.delete() return void once net-next opens. [0]: BUG: KASAN: slab-use-after-free in fib_rules_lookup+0x15e/0xeb0 net/core/fib_rules.c:321 Read of size 8 at addr ffff88804ec4c680 by task kworker/u8:21/12641 CPU: 0 UID: 0 PID: 12641 Comm: kworker/u8:21 Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/09/2026 Workqueue: netns cleanup_net Call Trace: dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 print_address_description+0x55/0x1e0 mm/kasan/report.c:378 print_report+0x58/0x70 mm/kasan/report.c:482 kasan_report+0x117/0x150 mm/kasan/report.c:595 fib_rules_lookup+0x15e/0xeb0 net/core/fib_rules.c:321 __fib_lookup+0x106/0x210 net/ipv4/fib_rules.c:96 ip_route_output_key_hash_rcu+0x294/0x2720 net/ipv4/route.c:2811 ip_route_output_key_hash+0x18d/0x2a0 net/ipv4/route.c:2702 __ip_route_output_key include/net/route.h:169 [inline] ip_route_output_flow+0x2a/0x150 net/ipv4/route.c:2929 ip4_datagram_release_cb+0x89d/0xbe0 net/ipv4/datagram.c:118 release_sock+0x206/0x260 net/core/sock.c:3861 inet_shutdown+0x2b1/0x390 net/ipv4/af_inet.c:950 udp_tunnel_sock_release+0x6d/0x80 net/ipv4/udp_tunnel_core.c:197 fou_release net/ipv4/fou_core.c:562 [inline] fou_exit_net+0x17d/0x1f0 net/ipv4/fou_core.c:1230 ops_exit_list net/core/net_namespace.c:199 [inline] ops_undo_list+0x43d/0x8d0 net/core/net_namespace.c:252 cleanup_net+0x572/0x810 net/core/net_namespace.c:702 process_one_work kernel/workqueue.c:3314 [inline] process_scheduled_works+0xa8e/0x14e0 kernel/workqueue.c:3397 worker_thread+0xa47/0xfb0 kernel/workqueue.c:3478 kthread+0x389/0x470 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 Fixes: 759923cf03b0 ("ipv4: fib: Convert fib_net_exit_batch() to ->exit_rtnl().") Reported-by: syzbot+965506b59a2de0b6905c@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/6a315824.b0403584.28d0ff.0000.GAE@google.com/ Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260616191359.4142661-1-kuniyu@google.com Signed-off-by: Jakub Kicinski --- diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index c7d1f31650d7b..42212970d7354 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1612,10 +1612,6 @@ static void ip_fib_net_exit(struct net *net) fib_free_table(tb); } } - -#ifdef CONFIG_IP_MULTIPLE_TABLES - fib4_rules_exit(net); -#endif } static int __net_init fib_net_init(struct net *net) @@ -1652,6 +1648,9 @@ out_semantics: ip_fib_net_exit(net); rtnl_net_unlock(net); +#ifdef CONFIG_IP_MULTIPLE_TABLES + fib4_rules_exit(net); +#endif kfree(net->ipv4.fib_table_hash); fib4_notifier_exit(net); goto out; @@ -1671,6 +1670,9 @@ static void __net_exit fib_net_exit_rtnl(struct net *net, static void __net_exit fib_net_exit(struct net *net) { +#ifdef CONFIG_IP_MULTIPLE_TABLES + fib4_rules_exit(net); +#endif kfree(net->ipv4.fib_table_hash); fib4_notifier_exit(net); fib4_semantics_exit(net); diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 51f0193092f03..e068a5bace73f 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -352,24 +352,17 @@ errout: static int fib4_rule_delete(struct fib_rule *rule) { struct net *net = rule->fr_net; - int err; - - /* split local/main if they are not already split */ - err = fib_unmerge(net); - if (err) - goto errout; #ifdef CONFIG_IP_ROUTE_CLASSID if (((struct fib4_rule *)rule)->tclassid) atomic_dec(&net->ipv4.fib_num_tclassid_users); #endif - net->ipv4.fib_has_custom_rules = true; if (net->ipv4.fib_rules_require_fldissect && fib_rule_requires_fldissect(rule)) net->ipv4.fib_rules_require_fldissect--; -errout: - return err; + + return 0; } static int fib4_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,