]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.1-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 20 May 2025 11:12:19 +0000 (13:12 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 20 May 2025 11:12:19 +0000 (13:12 +0200)
added patches:
netfilter-nf_tables-do-not-defer-rule-destruction-via-call_rcu.patch
netfilter-nf_tables-pass-nft_chain-to-destroy-function-not-nft_ctx.patch
netfilter-nf_tables-wait-for-rcu-grace-period-on-net_device-removal.patch

queue-6.1/netfilter-nf_tables-do-not-defer-rule-destruction-via-call_rcu.patch [new file with mode: 0644]
queue-6.1/netfilter-nf_tables-pass-nft_chain-to-destroy-function-not-nft_ctx.patch [new file with mode: 0644]
queue-6.1/netfilter-nf_tables-wait-for-rcu-grace-period-on-net_device-removal.patch [new file with mode: 0644]
queue-6.1/series

diff --git a/queue-6.1/netfilter-nf_tables-do-not-defer-rule-destruction-via-call_rcu.patch b/queue-6.1/netfilter-nf_tables-do-not-defer-rule-destruction-via-call_rcu.patch
new file mode 100644 (file)
index 0000000..16f0228
--- /dev/null
@@ -0,0 +1,155 @@
+From stable+bounces-144982-greg=kroah.com@vger.kernel.org Tue May 20 01:35:15 2025
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 20 May 2025 01:34:38 +0200
+Subject: netfilter: nf_tables: do not defer rule destruction via call_rcu
+To: netfilter-devel@vger.kernel.org
+Cc: gregkh@linuxfoundation.org, sashal@kernel.org, stable@vger.kernel.org
+Message-ID: <20250519233438.22640-4-pablo@netfilter.org>
+
+From: Florian Westphal <fw@strlen.de>
+
+commit b04df3da1b5c6f6dc7cdccc37941740c078c4043 upstream.
+
+nf_tables_chain_destroy can sleep, it can't be used from call_rcu
+callbacks.
+
+Moreover, nf_tables_rule_release() is only safe for error unwinding,
+while transaction mutex is held and the to-be-desroyed rule was not
+exposed to either dataplane or dumps, as it deactives+frees without
+the required synchronize_rcu() in-between.
+
+nft_rule_expr_deactivate() callbacks will change ->use counters
+of other chains/sets, see e.g. nft_lookup .deactivate callback, these
+must be serialized via transaction mutex.
+
+Also add a few lockdep asserts to make this more explicit.
+
+Calling synchronize_rcu() isn't ideal, but fixing this without is hard
+and way more intrusive.  As-is, we can get:
+
+WARNING: .. net/netfilter/nf_tables_api.c:5515 nft_set_destroy+0x..
+Workqueue: events nf_tables_trans_destroy_work
+RIP: 0010:nft_set_destroy+0x3fe/0x5c0
+Call Trace:
+ <TASK>
+ nf_tables_trans_destroy_work+0x6b7/0xad0
+ process_one_work+0x64a/0xce0
+ worker_thread+0x613/0x10d0
+
+In case the synchronize_rcu becomes an issue, we can explore alternatives.
+
+One way would be to allocate nft_trans_rule objects + one nft_trans_chain
+object, deactivate the rules + the chain and then defer the freeing to the
+nft destroy workqueue.  We'd still need to keep the synchronize_rcu path as
+a fallback to handle -ENOMEM corner cases though.
+
+Reported-by: syzbot+b26935466701e56cfdc2@syzkaller.appspotmail.com
+Closes: https://lore.kernel.org/all/67478d92.050a0220.253251.0062.GAE@google.com/T/
+Fixes: c03d278fdf35 ("netfilter: nf_tables: wait for rcu grace period on net_device removal")
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/netfilter/nf_tables.h |    3 ---
+ net/netfilter/nf_tables_api.c     |   32 +++++++++++++++-----------------
+ 2 files changed, 15 insertions(+), 20 deletions(-)
+
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -1062,7 +1062,6 @@ struct nft_chain {
+       char                            *name;
+       u16                             udlen;
+       u8                              *udata;
+-      struct rcu_head                 rcu_head;
+       /* Only used during control plane commit phase: */
+       struct nft_rule_blob            *blob_next;
+@@ -1205,7 +1204,6 @@ static inline void nft_use_inc_restore(u
+  *    @sets: sets in the table
+  *    @objects: stateful objects in the table
+  *    @flowtables: flow tables in the table
+- *    @net: netnamespace this table belongs to
+  *    @hgenerator: handle generator state
+  *    @handle: table handle
+  *    @use: number of chain references to this table
+@@ -1221,7 +1219,6 @@ struct nft_table {
+       struct list_head                sets;
+       struct list_head                objects;
+       struct list_head                flowtables;
+-      possible_net_t                  net;
+       u64                             hgenerator;
+       u64                             handle;
+       u32                             use;
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -1413,7 +1413,6 @@ static int nf_tables_newtable(struct sk_
+       INIT_LIST_HEAD(&table->sets);
+       INIT_LIST_HEAD(&table->objects);
+       INIT_LIST_HEAD(&table->flowtables);
+-      write_pnet(&table->net, net);
+       table->family = family;
+       table->flags = flags;
+       table->handle = ++nft_net->table_handle;
+@@ -3511,8 +3510,11 @@ void nf_tables_rule_destroy(const struct
+       kfree(rule);
+ }
++/* can only be used if rule is no longer visible to dumps */
+ static void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule)
+ {
++      lockdep_commit_lock_is_held(ctx->net);
++
+       nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_RELEASE);
+       nf_tables_rule_destroy(ctx, rule);
+ }
+@@ -5248,6 +5250,8 @@ void nf_tables_deactivate_set(const stru
+                             struct nft_set_binding *binding,
+                             enum nft_trans_phase phase)
+ {
++      lockdep_commit_lock_is_held(ctx->net);
++
+       switch (phase) {
+       case NFT_TRANS_PREPARE_ERROR:
+               nft_set_trans_unbind(ctx, set);
+@@ -10674,19 +10678,6 @@ static void __nft_release_basechain_now(
+       nf_tables_chain_destroy(ctx->chain);
+ }
+-static void nft_release_basechain_rcu(struct rcu_head *head)
+-{
+-      struct nft_chain *chain = container_of(head, struct nft_chain, rcu_head);
+-      struct nft_ctx ctx = {
+-              .family = chain->table->family,
+-              .chain  = chain,
+-              .net    = read_pnet(&chain->table->net),
+-      };
+-
+-      __nft_release_basechain_now(&ctx);
+-      put_net(ctx.net);
+-}
+-
+ int __nft_release_basechain(struct nft_ctx *ctx)
+ {
+       struct nft_rule *rule;
+@@ -10701,11 +10692,18 @@ int __nft_release_basechain(struct nft_c
+       nft_chain_del(ctx->chain);
+       nft_use_dec(&ctx->table->use);
+-      if (maybe_get_net(ctx->net))
+-              call_rcu(&ctx->chain->rcu_head, nft_release_basechain_rcu);
+-      else
++      if (!maybe_get_net(ctx->net)) {
+               __nft_release_basechain_now(ctx);
++              return 0;
++      }
++
++      /* wait for ruleset dumps to complete.  Owning chain is no longer in
++       * lists, so new dumps can't find any of these rules anymore.
++       */
++      synchronize_rcu();
++      __nft_release_basechain_now(ctx);
++      put_net(ctx->net);
+       return 0;
+ }
+ EXPORT_SYMBOL_GPL(__nft_release_basechain);
diff --git a/queue-6.1/netfilter-nf_tables-pass-nft_chain-to-destroy-function-not-nft_ctx.patch b/queue-6.1/netfilter-nf_tables-pass-nft_chain-to-destroy-function-not-nft_ctx.patch
new file mode 100644 (file)
index 0000000..2abf157
--- /dev/null
@@ -0,0 +1,123 @@
+From stable+bounces-144980-greg=kroah.com@vger.kernel.org Tue May 20 01:34:59 2025
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 20 May 2025 01:34:36 +0200
+Subject: netfilter: nf_tables: pass nft_chain to destroy function, not nft_ctx
+To: netfilter-devel@vger.kernel.org
+Cc: gregkh@linuxfoundation.org, sashal@kernel.org, stable@vger.kernel.org
+Message-ID: <20250519233438.22640-2-pablo@netfilter.org>
+
+From: Florian Westphal <fw@strlen.de>
+
+commit 8965d42bcf54d42cbc72fe34a9d0ec3f8527debd upstream.
+
+It would be better to not store nft_ctx inside nft_trans object,
+the netlink ctx strucutre is huge and most of its information is
+never needed in places that use trans->ctx.
+
+Avoid/reduce its usage if possible, no runtime behaviour change
+intended.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: c03d278fdf35 ("netfilter: nf_tables: wait for rcu grace period on net_device removal")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/netfilter/nf_tables.h |    2 +-
+ net/netfilter/nf_tables_api.c     |   17 ++++++++---------
+ net/netfilter/nft_immediate.c     |    2 +-
+ 3 files changed, 10 insertions(+), 11 deletions(-)
+
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -1121,7 +1121,7 @@ static inline bool nft_chain_is_bound(st
+ int nft_chain_add(struct nft_table *table, struct nft_chain *chain);
+ void nft_chain_del(struct nft_chain *chain);
+-void nf_tables_chain_destroy(struct nft_ctx *ctx);
++void nf_tables_chain_destroy(struct nft_chain *chain);
+ struct nft_stats {
+       u64                     bytes;
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -2034,9 +2034,9 @@ static void nf_tables_chain_free_chain_r
+       kvfree(chain->blob_next);
+ }
+-void nf_tables_chain_destroy(struct nft_ctx *ctx)
++void nf_tables_chain_destroy(struct nft_chain *chain)
+ {
+-      struct nft_chain *chain = ctx->chain;
++      const struct nft_table *table = chain->table;
+       struct nft_hook *hook, *next;
+       if (WARN_ON(chain->use > 0))
+@@ -2048,7 +2048,7 @@ void nf_tables_chain_destroy(struct nft_
+       if (nft_is_base_chain(chain)) {
+               struct nft_base_chain *basechain = nft_base_chain(chain);
+-              if (nft_base_chain_netdev(ctx->family, basechain->ops.hooknum)) {
++              if (nft_base_chain_netdev(table->family, basechain->ops.hooknum)) {
+                       list_for_each_entry_safe(hook, next,
+                                                &basechain->hook_list, list) {
+                               list_del_rcu(&hook->list);
+@@ -2515,7 +2515,7 @@ err_chain_add:
+ err_trans:
+       nft_use_dec_restore(&table->use);
+ err_destroy_chain:
+-      nf_tables_chain_destroy(ctx);
++      nf_tables_chain_destroy(chain);
+       return err;
+ }
+@@ -8994,7 +8994,7 @@ static void nft_commit_release(struct nf
+               kfree(nft_trans_chain_name(trans));
+               break;
+       case NFT_MSG_DELCHAIN:
+-              nf_tables_chain_destroy(&trans->ctx);
++              nf_tables_chain_destroy(nft_trans_chain(trans));
+               break;
+       case NFT_MSG_DELRULE:
+               nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
+@@ -9955,7 +9955,7 @@ static void nf_tables_abort_release(stru
+               nf_tables_table_destroy(&trans->ctx);
+               break;
+       case NFT_MSG_NEWCHAIN:
+-              nf_tables_chain_destroy(&trans->ctx);
++              nf_tables_chain_destroy(nft_trans_chain(trans));
+               break;
+       case NFT_MSG_NEWRULE:
+               nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
+@@ -10677,7 +10677,7 @@ int __nft_release_basechain(struct nft_c
+       }
+       nft_chain_del(ctx->chain);
+       nft_use_dec(&ctx->table->use);
+-      nf_tables_chain_destroy(ctx);
++      nf_tables_chain_destroy(ctx->chain);
+       return 0;
+ }
+@@ -10753,10 +10753,9 @@ static void __nft_release_table(struct n
+               nft_obj_destroy(&ctx, obj);
+       }
+       list_for_each_entry_safe(chain, nc, &table->chains, list) {
+-              ctx.chain = chain;
+               nft_chain_del(chain);
+               nft_use_dec(&table->use);
+-              nf_tables_chain_destroy(&ctx);
++              nf_tables_chain_destroy(chain);
+       }
+       nf_tables_table_destroy(&ctx);
+ }
+--- a/net/netfilter/nft_immediate.c
++++ b/net/netfilter/nft_immediate.c
+@@ -221,7 +221,7 @@ static void nft_immediate_destroy(const
+                       list_del(&rule->list);
+                       nf_tables_rule_destroy(&chain_ctx, rule);
+               }
+-              nf_tables_chain_destroy(&chain_ctx);
++              nf_tables_chain_destroy(chain);
+               break;
+       default:
+               break;
diff --git a/queue-6.1/netfilter-nf_tables-wait-for-rcu-grace-period-on-net_device-removal.patch b/queue-6.1/netfilter-nf_tables-wait-for-rcu-grace-period-on-net_device-removal.patch
new file mode 100644 (file)
index 0000000..70da475
--- /dev/null
@@ -0,0 +1,157 @@
+From stable+bounces-144981-greg=kroah.com@vger.kernel.org Tue May 20 01:35:03 2025
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 20 May 2025 01:34:37 +0200
+Subject: netfilter: nf_tables: wait for rcu grace period on net_device removal
+To: netfilter-devel@vger.kernel.org
+Cc: gregkh@linuxfoundation.org, sashal@kernel.org, stable@vger.kernel.org
+Message-ID: <20250519233438.22640-3-pablo@netfilter.org>
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+commit c03d278fdf35e73dd0ec543b9b556876b9d9a8dc upstream.
+
+8c873e219970 ("netfilter: core: free hooks with call_rcu") removed
+synchronize_net() call when unregistering basechain hook, however,
+net_device removal event handler for the NFPROTO_NETDEV was not updated
+to wait for RCU grace period.
+
+Note that 835b803377f5 ("netfilter: nf_tables_netdev: unregister hooks
+on net_device removal") does not remove basechain rules on device
+removal, I was hinted to remove rules on net_device removal later, see
+5ebe0b0eec9d ("netfilter: nf_tables: destroy basechain and rules on
+netdevice removal").
+
+Although NETDEV_UNREGISTER event is guaranteed to be handled after
+synchronize_net() call, this path needs to wait for rcu grace period via
+rcu callback to release basechain hooks if netns is alive because an
+ongoing netlink dump could be in progress (sockets hold a reference on
+the netns).
+
+Note that nf_tables_pre_exit_net() unregisters and releases basechain
+hooks but it is possible to see NETDEV_UNREGISTER at a later stage in
+the netns exit path, eg. veth peer device in another netns:
+
+ cleanup_net()
+  default_device_exit_batch()
+   unregister_netdevice_many_notify()
+    notifier_call_chain()
+     nf_tables_netdev_event()
+      __nft_release_basechain()
+
+In this particular case, same rule of thumb applies: if netns is alive,
+then wait for rcu grace period because netlink dump in the other netns
+could be in progress. Otherwise, if the other netns is going away then
+no netlink dump can be in progress and basechain hooks can be released
+inmediately.
+
+While at it, turn WARN_ON() into WARN_ON_ONCE() for the basechain
+validation, which should not ever happen.
+
+Fixes: 835b803377f5 ("netfilter: nf_tables_netdev: unregister hooks on net_device removal")
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/netfilter/nf_tables.h |    4 +++
+ net/netfilter/nf_tables_api.c     |   41 +++++++++++++++++++++++++++++++-------
+ 2 files changed, 38 insertions(+), 7 deletions(-)
+
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -1045,6 +1045,7 @@ struct nft_rule_blob {
+  *    @use: number of jump references to this chain
+  *    @flags: bitmask of enum nft_chain_flags
+  *    @name: name of the chain
++ *    @rcu_head: rcu head for deferred release
+  */
+ struct nft_chain {
+       struct nft_rule_blob            __rcu *blob_gen_0;
+@@ -1061,6 +1062,7 @@ struct nft_chain {
+       char                            *name;
+       u16                             udlen;
+       u8                              *udata;
++      struct rcu_head                 rcu_head;
+       /* Only used during control plane commit phase: */
+       struct nft_rule_blob            *blob_next;
+@@ -1203,6 +1205,7 @@ static inline void nft_use_inc_restore(u
+  *    @sets: sets in the table
+  *    @objects: stateful objects in the table
+  *    @flowtables: flow tables in the table
++ *    @net: netnamespace this table belongs to
+  *    @hgenerator: handle generator state
+  *    @handle: table handle
+  *    @use: number of chain references to this table
+@@ -1218,6 +1221,7 @@ struct nft_table {
+       struct list_head                sets;
+       struct list_head                objects;
+       struct list_head                flowtables;
++      possible_net_t                  net;
+       u64                             hgenerator;
+       u64                             handle;
+       u32                             use;
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -1413,6 +1413,7 @@ static int nf_tables_newtable(struct sk_
+       INIT_LIST_HEAD(&table->sets);
+       INIT_LIST_HEAD(&table->objects);
+       INIT_LIST_HEAD(&table->flowtables);
++      write_pnet(&table->net, net);
+       table->family = family;
+       table->flags = flags;
+       table->handle = ++nft_net->table_handle;
+@@ -10662,22 +10663,48 @@ int nft_data_dump(struct sk_buff *skb, i
+ }
+ EXPORT_SYMBOL_GPL(nft_data_dump);
+-int __nft_release_basechain(struct nft_ctx *ctx)
++static void __nft_release_basechain_now(struct nft_ctx *ctx)
+ {
+       struct nft_rule *rule, *nr;
+-      if (WARN_ON(!nft_is_base_chain(ctx->chain)))
+-              return 0;
+-
+-      nf_tables_unregister_hook(ctx->net, ctx->chain->table, ctx->chain);
+       list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) {
+               list_del(&rule->list);
+-              nft_use_dec(&ctx->chain->use);
+               nf_tables_rule_release(ctx, rule);
+       }
++      nf_tables_chain_destroy(ctx->chain);
++}
++
++static void nft_release_basechain_rcu(struct rcu_head *head)
++{
++      struct nft_chain *chain = container_of(head, struct nft_chain, rcu_head);
++      struct nft_ctx ctx = {
++              .family = chain->table->family,
++              .chain  = chain,
++              .net    = read_pnet(&chain->table->net),
++      };
++
++      __nft_release_basechain_now(&ctx);
++      put_net(ctx.net);
++}
++
++int __nft_release_basechain(struct nft_ctx *ctx)
++{
++      struct nft_rule *rule;
++
++      if (WARN_ON_ONCE(!nft_is_base_chain(ctx->chain)))
++              return 0;
++
++      nf_tables_unregister_hook(ctx->net, ctx->chain->table, ctx->chain);
++      list_for_each_entry(rule, &ctx->chain->rules, list)
++              nft_use_dec(&ctx->chain->use);
++
+       nft_chain_del(ctx->chain);
+       nft_use_dec(&ctx->table->use);
+-      nf_tables_chain_destroy(ctx->chain);
++
++      if (maybe_get_net(ctx->net))
++              call_rcu(&ctx->chain->rcu_head, nft_release_basechain_rcu);
++      else
++              __nft_release_basechain_now(ctx);
+       return 0;
+ }
index edea07bce4b0661e110d9a2ffbb69dfb8dfcb482..eb8666f086ff71046942b5d2e107c5baa173bbf8 100644 (file)
@@ -85,3 +85,6 @@ loongarch-explicitly-specify-code-model-in-makefile.patch
 hwpoison-memory_hotplug-lock-folio-before-unmap-hwpoisoned-folio.patch
 sctp-add-mutual-exclusion-in-proc_sctp_do_udp_port.patch
 btrfs-don-t-bug_on-when-0-reference-count-at-btrfs_lookup_extent_info.patch
+netfilter-nf_tables-pass-nft_chain-to-destroy-function-not-nft_ctx.patch
+netfilter-nf_tables-wait-for-rcu-grace-period-on-net_device-removal.patch
+netfilter-nf_tables-do-not-defer-rule-destruction-via-call_rcu.patch