From: Greg Kroah-Hartman Date: Tue, 20 May 2025 11:12:19 +0000 (+0200) Subject: 6.1-stable patches X-Git-Tag: v5.15.184~12 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4b54c93ea057c525764d53e34943a9066ce0b12e;p=thirdparty%2Fkernel%2Fstable-queue.git 6.1-stable patches 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 --- 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 index 0000000000..16f022830a --- /dev/null +++ b/queue-6.1/netfilter-nf_tables-do-not-defer-rule-destruction-via-call_rcu.patch @@ -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 +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 + +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: + + 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 +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..2abf1570b4 --- /dev/null +++ b/queue-6.1/netfilter-nf_tables-pass-nft_chain-to-destroy-function-not-nft_ctx.patch @@ -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 +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 + +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 +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: c03d278fdf35 ("netfilter: nf_tables: wait for rcu grace period on net_device removal") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..70da4757b9 --- /dev/null +++ b/queue-6.1/netfilter-nf_tables-wait-for-rcu-grace-period-on-net_device-removal.patch @@ -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 +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 + +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 +Signed-off-by: Greg Kroah-Hartman +--- + 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; + } diff --git a/queue-6.1/series b/queue-6.1/series index edea07bce4..eb8666f086 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -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