]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 5.10
authorSasha Levin <sashal@kernel.org>
Thu, 28 Sep 2023 11:59:01 +0000 (07:59 -0400)
committerSasha Levin <sashal@kernel.org>
Thu, 28 Sep 2023 11:59:01 +0000 (07:59 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
queue-5.10/netfilter-nf_tables-double-hook-unregistration-in-ne.patch [new file with mode: 0644]
queue-5.10/netfilter-nf_tables-unregister-flowtable-hooks-on-ne.patch [new file with mode: 0644]
queue-5.10/series

diff --git a/queue-5.10/netfilter-nf_tables-double-hook-unregistration-in-ne.patch b/queue-5.10/netfilter-nf_tables-double-hook-unregistration-in-ne.patch
new file mode 100644 (file)
index 0000000..1b74188
--- /dev/null
@@ -0,0 +1,145 @@
+From a44991add96b467ad61a4b2c6434b527d7f34a22 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 27 Sep 2023 17:30:07 +0200
+Subject: netfilter: nf_tables: double hook unregistration in netns path
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+commit f9a43007d3f7ba76d5e7f9421094f00f2ef202f8 upstream.
+
+[ This backport includes ab5e5c062f67 ("netfilter: nf_tables: use
+  kfree_rcu(ptr, rcu) to release hooks in clean_net path") ]
+
+__nft_release_hooks() is called from pre_netns exit path which
+unregisters the hooks, then the NETDEV_UNREGISTER event is triggered
+which unregisters the hooks again.
+
+[  565.221461] WARNING: CPU: 18 PID: 193 at net/netfilter/core.c:495 __nf_unregister_net_hook+0x247/0x270
+[...]
+[  565.246890] CPU: 18 PID: 193 Comm: kworker/u64:1 Tainted: G            E     5.18.0-rc7+ #27
+[  565.253682] Workqueue: netns cleanup_net
+[  565.257059] RIP: 0010:__nf_unregister_net_hook+0x247/0x270
+[...]
+[  565.297120] Call Trace:
+[  565.300900]  <TASK>
+[  565.304683]  nf_tables_flowtable_event+0x16a/0x220 [nf_tables]
+[  565.308518]  raw_notifier_call_chain+0x63/0x80
+[  565.312386]  unregister_netdevice_many+0x54f/0xb50
+
+Unregister and destroy netdev hook from netns pre_exit via kfree_rcu
+so the NETDEV_UNREGISTER path see unregistered hooks.
+
+Fixes: 767d1216bff8 ("netfilter: nftables: fix possible UAF over chains from packet path in netns")
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/nf_tables_api.c | 54 ++++++++++++++++++++++++++---------
+ 1 file changed, 41 insertions(+), 13 deletions(-)
+
+diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
+index efbcf85cd6b7a..16e2500e8590b 100644
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -303,12 +303,18 @@ static int nft_netdev_register_hooks(struct net *net,
+ }
+ static void nft_netdev_unregister_hooks(struct net *net,
+-                                      struct list_head *hook_list)
++                                      struct list_head *hook_list,
++                                      bool release_netdev)
+ {
+-      struct nft_hook *hook;
++      struct nft_hook *hook, *next;
+-      list_for_each_entry(hook, hook_list, list)
++      list_for_each_entry_safe(hook, next, hook_list, list) {
+               nf_unregister_net_hook(net, &hook->ops);
++              if (release_netdev) {
++                      list_del(&hook->list);
++                      kfree_rcu(hook, rcu);
++              }
++      }
+ }
+ static int nf_tables_register_hook(struct net *net,
+@@ -334,9 +340,10 @@ static int nf_tables_register_hook(struct net *net,
+       return nf_register_net_hook(net, &basechain->ops);
+ }
+-static void nf_tables_unregister_hook(struct net *net,
+-                                    const struct nft_table *table,
+-                                    struct nft_chain *chain)
++static void __nf_tables_unregister_hook(struct net *net,
++                                      const struct nft_table *table,
++                                      struct nft_chain *chain,
++                                      bool release_netdev)
+ {
+       struct nft_base_chain *basechain;
+       const struct nf_hook_ops *ops;
+@@ -351,11 +358,19 @@ static void nf_tables_unregister_hook(struct net *net,
+               return basechain->type->ops_unregister(net, ops);
+       if (nft_base_chain_netdev(table->family, basechain->ops.hooknum))
+-              nft_netdev_unregister_hooks(net, &basechain->hook_list);
++              nft_netdev_unregister_hooks(net, &basechain->hook_list,
++                                          release_netdev);
+       else
+               nf_unregister_net_hook(net, &basechain->ops);
+ }
++static void nf_tables_unregister_hook(struct net *net,
++                                    const struct nft_table *table,
++                                    struct nft_chain *chain)
++{
++      return __nf_tables_unregister_hook(net, table, chain, false);
++}
++
+ static void nft_trans_commit_list_add_tail(struct net *net, struct nft_trans *trans)
+ {
+       struct nftables_pernet *nft_net;
+@@ -6822,13 +6837,25 @@ static void nft_unregister_flowtable_hook(struct net *net,
+                                   FLOW_BLOCK_UNBIND);
+ }
+-static void nft_unregister_flowtable_net_hooks(struct net *net,
+-                                             struct list_head *hook_list)
++static void __nft_unregister_flowtable_net_hooks(struct net *net,
++                                               struct list_head *hook_list,
++                                               bool release_netdev)
+ {
+-      struct nft_hook *hook;
++      struct nft_hook *hook, *next;
+-      list_for_each_entry(hook, hook_list, list)
++      list_for_each_entry_safe(hook, next, hook_list, list) {
+               nf_unregister_net_hook(net, &hook->ops);
++              if (release_netdev) {
++                      list_del(&hook->list);
++                      kfree_rcu(hook, rcu);
++              }
++      }
++}
++
++static void nft_unregister_flowtable_net_hooks(struct net *net,
++                                             struct list_head *hook_list)
++{
++      __nft_unregister_flowtable_net_hooks(net, hook_list, false);
+ }
+ static int nft_register_flowtable_net_hooks(struct net *net,
+@@ -9472,9 +9499,10 @@ static void __nft_release_hook(struct net *net, struct nft_table *table)
+       struct nft_chain *chain;
+       list_for_each_entry(chain, &table->chains, list)
+-              nf_tables_unregister_hook(net, table, chain);
++              __nf_tables_unregister_hook(net, table, chain, true);
+       list_for_each_entry(flowtable, &table->flowtables, list)
+-              nft_unregister_flowtable_net_hooks(net, &flowtable->hook_list);
++              __nft_unregister_flowtable_net_hooks(net, &flowtable->hook_list,
++                                                   true);
+ }
+ static void __nft_release_hooks(struct net *net)
+-- 
+2.40.1
+
diff --git a/queue-5.10/netfilter-nf_tables-unregister-flowtable-hooks-on-ne.patch b/queue-5.10/netfilter-nf_tables-unregister-flowtable-hooks-on-ne.patch
new file mode 100644 (file)
index 0000000..da8e1c5
--- /dev/null
@@ -0,0 +1,90 @@
+From d4e0e230dc64b104adbbc089f94d8f09e086d95d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 27 Sep 2023 17:30:06 +0200
+Subject: netfilter: nf_tables: unregister flowtable hooks on netns exit
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+commit 6069da443bf65f513bb507bb21e2f87cfb1ad0b6 upstream.
+
+Unregister flowtable hooks before they are releases via
+nf_tables_flowtable_destroy() otherwise hook core reports UAF.
+
+BUG: KASAN: use-after-free in nf_hook_entries_grow+0x5a7/0x700 net/netfilter/core.c:142 net/netfilter/core.c:142
+Read of size 4 at addr ffff8880736f7438 by task syz-executor579/3666
+
+CPU: 0 PID: 3666 Comm: syz-executor579 Not tainted 5.16.0-rc5-syzkaller #0
+Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
+Call Trace:
+ <TASK>
+ __dump_stack lib/dump_stack.c:88 [inline]
+ __dump_stack lib/dump_stack.c:88 [inline] lib/dump_stack.c:106
+ dump_stack_lvl+0x1dc/0x2d8 lib/dump_stack.c:106 lib/dump_stack.c:106
+ print_address_description+0x65/0x380 mm/kasan/report.c:247 mm/kasan/report.c:247
+ __kasan_report mm/kasan/report.c:433 [inline]
+ __kasan_report mm/kasan/report.c:433 [inline] mm/kasan/report.c:450
+ kasan_report+0x19a/0x1f0 mm/kasan/report.c:450 mm/kasan/report.c:450
+ nf_hook_entries_grow+0x5a7/0x700 net/netfilter/core.c:142 net/netfilter/core.c:142
+ __nf_register_net_hook+0x27e/0x8d0 net/netfilter/core.c:429 net/netfilter/core.c:429
+ nf_register_net_hook+0xaa/0x180 net/netfilter/core.c:571 net/netfilter/core.c:571
+ nft_register_flowtable_net_hooks+0x3c5/0x730 net/netfilter/nf_tables_api.c:7232 net/netfilter/nf_tables_api.c:7232
+ nf_tables_newflowtable+0x2022/0x2cf0 net/netfilter/nf_tables_api.c:7430 net/netfilter/nf_tables_api.c:7430
+ nfnetlink_rcv_batch net/netfilter/nfnetlink.c:513 [inline]
+ nfnetlink_rcv_skb_batch net/netfilter/nfnetlink.c:634 [inline]
+ nfnetlink_rcv_batch net/netfilter/nfnetlink.c:513 [inline] net/netfilter/nfnetlink.c:652
+ nfnetlink_rcv_skb_batch net/netfilter/nfnetlink.c:634 [inline] net/netfilter/nfnetlink.c:652
+ nfnetlink_rcv+0x10e6/0x2550 net/netfilter/nfnetlink.c:652 net/netfilter/nfnetlink.c:652
+
+__nft_release_hook() calls nft_unregister_flowtable_net_hooks() which
+only unregisters the hooks, then after RCU grace period, it is
+guaranteed that no packets add new entries to the flowtable (no flow
+offload rules and flowtable hooks are reachable from packet path), so it
+is safe to call nf_flow_table_free() which cleans up the remaining
+entries from the flowtable (both software and hardware) and it unbinds
+the flow_block.
+
+Fixes: ff4bf2f42a40 ("netfilter: nf_tables: add nft_unregister_flowtable_hook()")
+Reported-by: syzbot+e918523f77e62790d6d9@syzkaller.appspotmail.com
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/nf_tables_api.c | 18 +++++++++++++-----
+ 1 file changed, 13 insertions(+), 5 deletions(-)
+
+diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
+index 52c776b5967ef..efbcf85cd6b7a 100644
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -9466,16 +9466,24 @@ int __nft_release_basechain(struct nft_ctx *ctx)
+ }
+ EXPORT_SYMBOL_GPL(__nft_release_basechain);
++static void __nft_release_hook(struct net *net, struct nft_table *table)
++{
++      struct nft_flowtable *flowtable;
++      struct nft_chain *chain;
++
++      list_for_each_entry(chain, &table->chains, list)
++              nf_tables_unregister_hook(net, table, chain);
++      list_for_each_entry(flowtable, &table->flowtables, list)
++              nft_unregister_flowtable_net_hooks(net, &flowtable->hook_list);
++}
++
+ static void __nft_release_hooks(struct net *net)
+ {
+       struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
+       struct nft_table *table;
+-      struct nft_chain *chain;
+-      list_for_each_entry(table, &nft_net->tables, list) {
+-              list_for_each_entry(chain, &table->chains, list)
+-                      nf_tables_unregister_hook(net, table, chain);
+-      }
++      list_for_each_entry(table, &nft_net->tables, list)
++              __nft_release_hook(net, table);
+ }
+ static void __nft_release_table(struct net *net, struct nft_table *table)
+-- 
+2.40.1
+
index d5304e7c73e4c252d130ac8536ceb55c8a9bdaa4..342302491d9e3a5c9f208a0aac8cb5c4880ef508 100644 (file)
@@ -59,3 +59,5 @@ bnxt_en-flush-xdp-for-bnxt_poll_nitroa0-s-napi.patch
 net-rds-fix-possible-null-pointer-dereference.patch
 gpio-tb10x-fix-an-error-handling-path-in-tb10x_gpio_.patch
 i2c-mux-demux-pinctrl-check-the-return-value-of-devm.patch
+netfilter-nf_tables-unregister-flowtable-hooks-on-ne.patch
+netfilter-nf_tables-double-hook-unregistration-in-ne.patch