From: Greg Kroah-Hartman Date: Sun, 16 Jul 2023 19:22:47 +0000 (+0200) Subject: 5.10-stable patches X-Git-Tag: v6.1.39~29 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=802eb3f50a8483db0c429be697ac865de8ed4941;p=thirdparty%2Fkernel%2Fstable-queue.git 5.10-stable patches added patches: netfilter-nf_tables-add-nft_trans_prepare_error-to-deal-with-bound-set-chain.patch netfilter-nf_tables-add-rescheduling-points-during-loop-detection-walks.patch netfilter-nf_tables-drop-map-element-references-from-preparation-phase.patch netfilter-nf_tables-fix-chain-binding-transaction-logic.patch netfilter-nf_tables-fix-scheduling-while-atomic-splat.patch netfilter-nf_tables-incorrect-error-path-handling-with-nft_msg_newrule.patch netfilter-nf_tables-reject-unbound-anonymous-set-before-commit-phase.patch netfilter-nf_tables-reject-unbound-chain-set-before-commit-phase.patch netfilter-nf_tables-unbind-non-anonymous-set-if-rule-construction-fails.patch netfilter-nf_tables-use-net_generic-infra-for-transaction-data.patch netfilter-nftables-rename-set-element-data-activation-deactivation-functions.patch --- diff --git a/queue-5.10/netfilter-nf_tables-add-nft_trans_prepare_error-to-deal-with-bound-set-chain.patch b/queue-5.10/netfilter-nf_tables-add-nft_trans_prepare_error-to-deal-with-bound-set-chain.patch new file mode 100644 index 00000000000..1fdba2a577b --- /dev/null +++ b/queue-5.10/netfilter-nf_tables-add-nft_trans_prepare_error-to-deal-with-bound-set-chain.patch @@ -0,0 +1,170 @@ +From stable-owner@vger.kernel.org Thu Jul 13 10:49:52 2023 +From: Pablo Neira Ayuso +Date: Thu, 13 Jul 2023 10:48:53 +0200 +Subject: netfilter: nf_tables: add NFT_TRANS_PREPARE_ERROR to deal with bound set/chain +To: netfilter-devel@vger.kernel.org +Cc: gregkh@linuxfoundation.org, stable@vger.kernel.org, sashal@kernel.org +Message-ID: <20230713084859.71541-6-pablo@netfilter.org> + +From: Pablo Neira Ayuso + +[ Upstream commit 26b5a5712eb85e253724e56a54c17f8519bd8e4e ] + +Add a new state to deal with rule expressions deactivation from the +newrule error path, otherwise the anonymous set remains in the list in +inactive state for the next generation. Mark the set/chain transaction +as unbound so the abort path releases this object, set it as inactive in +the next generation so it is not reachable anymore from this transaction +and reference counter is dropped. + +Fixes: 1240eb93f061 ("netfilter: nf_tables: incorrect error path handling with NFT_MSG_NEWRULE") +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + include/net/netfilter/nf_tables.h | 2 + + net/netfilter/nf_tables_api.c | 45 ++++++++++++++++++++++++++++++++------ + net/netfilter/nft_immediate.c | 3 ++ + 3 files changed, 43 insertions(+), 7 deletions(-) + +--- a/include/net/netfilter/nf_tables.h ++++ b/include/net/netfilter/nf_tables.h +@@ -777,6 +777,7 @@ struct nft_expr_type { + + enum nft_trans_phase { + NFT_TRANS_PREPARE, ++ NFT_TRANS_PREPARE_ERROR, + NFT_TRANS_ABORT, + NFT_TRANS_COMMIT, + NFT_TRANS_RELEASE +@@ -970,6 +971,7 @@ struct nft_chain { + + int nft_chain_validate(const struct nft_ctx *ctx, const struct nft_chain *chain); + int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain); ++void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain); + + enum nft_chain_types { + NFT_CHAIN_T_DEFAULT = 0, +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -173,7 +173,8 @@ static void nft_trans_destroy(struct nft + kfree(trans); + } + +-static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set) ++static void __nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set, ++ bool bind) + { + struct nftables_pernet *nft_net; + struct net *net = ctx->net; +@@ -187,17 +188,28 @@ static void nft_set_trans_bind(const str + switch (trans->msg_type) { + case NFT_MSG_NEWSET: + if (nft_trans_set(trans) == set) +- nft_trans_set_bound(trans) = true; ++ nft_trans_set_bound(trans) = bind; + break; + case NFT_MSG_NEWSETELEM: + if (nft_trans_elem_set(trans) == set) +- nft_trans_elem_set_bound(trans) = true; ++ nft_trans_elem_set_bound(trans) = bind; + break; + } + } + } + +-static void nft_chain_trans_bind(const struct nft_ctx *ctx, struct nft_chain *chain) ++static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set) ++{ ++ return __nft_set_trans_bind(ctx, set, true); ++} ++ ++static void nft_set_trans_unbind(const struct nft_ctx *ctx, struct nft_set *set) ++{ ++ return __nft_set_trans_bind(ctx, set, false); ++} ++ ++static void __nft_chain_trans_bind(const struct nft_ctx *ctx, ++ struct nft_chain *chain, bool bind) + { + struct nftables_pernet *nft_net; + struct net *net = ctx->net; +@@ -211,16 +223,22 @@ static void nft_chain_trans_bind(const s + switch (trans->msg_type) { + case NFT_MSG_NEWCHAIN: + if (nft_trans_chain(trans) == chain) +- nft_trans_chain_bound(trans) = true; ++ nft_trans_chain_bound(trans) = bind; + break; + case NFT_MSG_NEWRULE: + if (trans->ctx.chain == chain) +- nft_trans_rule_bound(trans) = true; ++ nft_trans_rule_bound(trans) = bind; + break; + } + } + } + ++static void nft_chain_trans_bind(const struct nft_ctx *ctx, ++ struct nft_chain *chain) ++{ ++ __nft_chain_trans_bind(ctx, chain, true); ++} ++ + int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain) + { + if (!nft_chain_binding(chain)) +@@ -239,6 +257,11 @@ int nf_tables_bind_chain(const struct nf + return 0; + } + ++void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain) ++{ ++ __nft_chain_trans_bind(ctx, chain, false); ++} ++ + static int nft_netdev_register_hooks(struct net *net, + struct list_head *hook_list) + { +@@ -3449,7 +3472,7 @@ static int nf_tables_newrule(struct net + + return 0; + err2: +- nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE); ++ nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE_ERROR); + nf_tables_rule_destroy(&ctx, rule); + err1: + for (i = 0; i < n; i++) { +@@ -4585,6 +4608,13 @@ void nf_tables_deactivate_set(const stru + enum nft_trans_phase phase) + { + switch (phase) { ++ case NFT_TRANS_PREPARE_ERROR: ++ nft_set_trans_unbind(ctx, set); ++ if (nft_set_is_anonymous(set)) ++ nft_deactivate_next(ctx->net, set); ++ ++ set->use--; ++ break; + case NFT_TRANS_PREPARE: + if (nft_set_is_anonymous(set)) + nft_deactivate_next(ctx->net, set); +@@ -6525,6 +6555,7 @@ void nf_tables_deactivate_flowtable(cons + enum nft_trans_phase phase) + { + switch (phase) { ++ case NFT_TRANS_PREPARE_ERROR: + case NFT_TRANS_PREPARE: + case NFT_TRANS_ABORT: + case NFT_TRANS_RELEASE: +--- a/net/netfilter/nft_immediate.c ++++ b/net/netfilter/nft_immediate.c +@@ -150,6 +150,9 @@ static void nft_immediate_deactivate(con + nft_rule_expr_deactivate(&chain_ctx, rule, phase); + + switch (phase) { ++ case NFT_TRANS_PREPARE_ERROR: ++ nf_tables_unbind_chain(ctx, chain); ++ fallthrough; + case NFT_TRANS_PREPARE: + nft_deactivate_next(ctx->net, chain); + break; diff --git a/queue-5.10/netfilter-nf_tables-add-rescheduling-points-during-loop-detection-walks.patch b/queue-5.10/netfilter-nf_tables-add-rescheduling-points-during-loop-detection-walks.patch new file mode 100644 index 00000000000..e003123cf61 --- /dev/null +++ b/queue-5.10/netfilter-nf_tables-add-rescheduling-points-during-loop-detection-walks.patch @@ -0,0 +1,50 @@ +From stable-owner@vger.kernel.org Thu Jul 13 10:49:47 2023 +From: Pablo Neira Ayuso +Date: Thu, 13 Jul 2023 10:48:50 +0200 +Subject: netfilter: nf_tables: add rescheduling points during loop detection walks +To: netfilter-devel@vger.kernel.org +Cc: gregkh@linuxfoundation.org, stable@vger.kernel.org, sashal@kernel.org +Message-ID: <20230713084859.71541-3-pablo@netfilter.org> + +From: Florian Westphal + +[ Upstream commit 81ea010667417ef3f218dfd99b69769fe66c2b67 ] + +Add explicit rescheduling points during ruleset walk. + +Switching to a faster algorithm is possible but this is a much +smaller change, suitable for nf tree. + +Link: https://bugzilla.netfilter.org/show_bug.cgi?id=1460 +Signed-off-by: Florian Westphal +Acked-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + net/netfilter/nf_tables_api.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -3164,6 +3164,8 @@ int nft_chain_validate(const struct nft_ + if (err < 0) + return err; + } ++ ++ cond_resched(); + } + + return 0; +@@ -8506,9 +8508,13 @@ static int nf_tables_check_loops(const s + break; + } + } ++ ++ cond_resched(); + } + + list_for_each_entry(set, &ctx->table->sets, list) { ++ cond_resched(); ++ + if (!nft_is_active_next(ctx->net, set)) + continue; + if (!(set->flags & NFT_SET_MAP) || diff --git a/queue-5.10/netfilter-nf_tables-drop-map-element-references-from-preparation-phase.patch b/queue-5.10/netfilter-nf_tables-drop-map-element-references-from-preparation-phase.patch new file mode 100644 index 00000000000..e4f4bfe1f20 --- /dev/null +++ b/queue-5.10/netfilter-nf_tables-drop-map-element-references-from-preparation-phase.patch @@ -0,0 +1,376 @@ +From stable-owner@vger.kernel.org Thu Jul 13 10:49:53 2023 +From: Pablo Neira Ayuso +Date: Thu, 13 Jul 2023 10:48:57 +0200 +Subject: netfilter: nf_tables: drop map element references from preparation phase +To: netfilter-devel@vger.kernel.org +Cc: gregkh@linuxfoundation.org, stable@vger.kernel.org, sashal@kernel.org +Message-ID: <20230713084859.71541-10-pablo@netfilter.org> + +From: Pablo Neira Ayuso + +[ Upstream commit 628bd3e49cba1c066228e23d71a852c23e26da73 ] + +set .destroy callback releases the references to other objects in maps. +This is very late and it results in spurious EBUSY errors. Drop refcount +from the preparation phase instead, update set backend not to drop +reference counter from set .destroy path. + +Exceptions: NFT_TRANS_PREPARE_ERROR does not require to drop the +reference counter because the transaction abort path releases the map +references for each element since the set is unbound. The abort path +also deals with releasing reference counter for new elements added to +unbound sets. + +Fixes: 591054469b3e ("netfilter: nf_tables: revisit chain/object refcounting from elements") +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + include/net/netfilter/nf_tables.h | 5 +- + net/netfilter/nf_tables_api.c | 89 ++++++++++++++++++++++++++++++++++---- + net/netfilter/nft_set_bitmap.c | 5 +- + net/netfilter/nft_set_hash.c | 23 +++++++-- + net/netfilter/nft_set_pipapo.c | 14 +++-- + net/netfilter/nft_set_rbtree.c | 5 +- + 6 files changed, 117 insertions(+), 24 deletions(-) + +--- a/include/net/netfilter/nf_tables.h ++++ b/include/net/netfilter/nf_tables.h +@@ -382,7 +382,8 @@ struct nft_set_ops { + int (*init)(const struct nft_set *set, + const struct nft_set_desc *desc, + const struct nlattr * const nla[]); +- void (*destroy)(const struct nft_set *set); ++ void (*destroy)(const struct nft_ctx *ctx, ++ const struct nft_set *set); + void (*gc_init)(const struct nft_set *set); + + unsigned int elemsize; +@@ -686,6 +687,8 @@ void *nft_set_elem_init(const struct nft + u64 timeout, u64 expiration, gfp_t gfp); + void nft_set_elem_destroy(const struct nft_set *set, void *elem, + bool destroy_expr); ++void nf_tables_set_elem_destroy(const struct nft_ctx *ctx, ++ const struct nft_set *set, void *elem); + + /** + * struct nft_set_gc_batch_head - nf_tables set garbage collection batch +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -557,6 +557,31 @@ static int nft_trans_set_add(const struc + return 0; + } + ++static void nft_setelem_data_deactivate(const struct net *net, ++ const struct nft_set *set, ++ struct nft_set_elem *elem); ++ ++static int nft_mapelem_deactivate(const struct nft_ctx *ctx, ++ struct nft_set *set, ++ const struct nft_set_iter *iter, ++ struct nft_set_elem *elem) ++{ ++ nft_setelem_data_deactivate(ctx->net, set, elem); ++ ++ return 0; ++} ++ ++static void nft_map_deactivate(const struct nft_ctx *ctx, struct nft_set *set) ++{ ++ struct nft_set_iter iter = { ++ .genmask = nft_genmask_next(ctx->net), ++ .fn = nft_mapelem_deactivate, ++ }; ++ ++ set->ops->walk(ctx, set, &iter); ++ WARN_ON_ONCE(iter.err); ++} ++ + static int nft_delset(const struct nft_ctx *ctx, struct nft_set *set) + { + int err; +@@ -565,6 +590,9 @@ static int nft_delset(const struct nft_c + if (err < 0) + return err; + ++ if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) ++ nft_map_deactivate(ctx, set); ++ + nft_deactivate_next(ctx->net, set); + ctx->table->use--; + +@@ -4474,7 +4502,7 @@ err_set_expr_alloc: + if (set->expr) + nft_expr_destroy(&ctx, set->expr); + +- ops->destroy(set); ++ ops->destroy(&ctx, set); + err_set_init: + kfree(set->name); + err_set_name: +@@ -4490,7 +4518,7 @@ static void nft_set_destroy(const struct + if (set->expr) + nft_expr_destroy(ctx, set->expr); + +- set->ops->destroy(set); ++ set->ops->destroy(ctx, set); + kfree(set->name); + kvfree(set); + } +@@ -4614,10 +4642,39 @@ static void nf_tables_unbind_set(const s + } + } + ++static void nft_setelem_data_activate(const struct net *net, ++ const struct nft_set *set, ++ struct nft_set_elem *elem); ++ ++static int nft_mapelem_activate(const struct nft_ctx *ctx, ++ struct nft_set *set, ++ const struct nft_set_iter *iter, ++ struct nft_set_elem *elem) ++{ ++ nft_setelem_data_activate(ctx->net, set, elem); ++ ++ return 0; ++} ++ ++static void nft_map_activate(const struct nft_ctx *ctx, struct nft_set *set) ++{ ++ struct nft_set_iter iter = { ++ .genmask = nft_genmask_next(ctx->net), ++ .fn = nft_mapelem_activate, ++ }; ++ ++ set->ops->walk(ctx, set, &iter); ++ WARN_ON_ONCE(iter.err); ++} ++ + void nf_tables_activate_set(const struct nft_ctx *ctx, struct nft_set *set) + { +- if (nft_set_is_anonymous(set)) ++ if (nft_set_is_anonymous(set)) { ++ if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) ++ nft_map_activate(ctx, set); ++ + nft_clear(ctx->net, set); ++ } + + set->use++; + } +@@ -4636,13 +4693,20 @@ void nf_tables_deactivate_set(const stru + set->use--; + break; + case NFT_TRANS_PREPARE: +- if (nft_set_is_anonymous(set)) +- nft_deactivate_next(ctx->net, set); ++ if (nft_set_is_anonymous(set)) { ++ if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) ++ nft_map_deactivate(ctx, set); + ++ nft_deactivate_next(ctx->net, set); ++ } + set->use--; + return; + case NFT_TRANS_ABORT: + case NFT_TRANS_RELEASE: ++ if (nft_set_is_anonymous(set) && ++ set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) ++ nft_map_deactivate(ctx, set); ++ + set->use--; + fallthrough; + default: +@@ -5249,6 +5313,7 @@ static void nft_set_elem_expr_destroy(co + } + } + ++/* Drop references and destroy. Called from gc, dynset and abort path. */ + void nft_set_elem_destroy(const struct nft_set *set, void *elem, + bool destroy_expr) + { +@@ -5270,11 +5335,11 @@ void nft_set_elem_destroy(const struct n + } + EXPORT_SYMBOL_GPL(nft_set_elem_destroy); + +-/* Only called from commit path, nft_setelem_data_deactivate() already deals +- * with the refcounting from the preparation phase. ++/* Destroy element. References have been already dropped in the preparation ++ * path via nft_setelem_data_deactivate(). + */ +-static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx, +- const struct nft_set *set, void *elem) ++void nf_tables_set_elem_destroy(const struct nft_ctx *ctx, ++ const struct nft_set *set, void *elem) + { + struct nft_set_ext *ext = nft_set_elem_ext(set, elem); + +@@ -8399,6 +8464,9 @@ static int __nf_tables_abort(struct net + case NFT_MSG_DELSET: + trans->ctx.table->use++; + nft_clear(trans->ctx.net, nft_trans_set(trans)); ++ if (nft_trans_set(trans)->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) ++ nft_map_activate(&trans->ctx, nft_trans_set(trans)); ++ + nft_trans_destroy(trans); + break; + case NFT_MSG_NEWSETELEM: +@@ -9128,6 +9196,9 @@ static void __nft_release_table(struct n + list_for_each_entry_safe(set, ns, &table->sets, list) { + list_del(&set->list); + table->use--; ++ if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) ++ nft_map_deactivate(&ctx, set); ++ + nft_set_destroy(&ctx, set); + } + list_for_each_entry_safe(obj, ne, &table->objects, list) { +--- a/net/netfilter/nft_set_bitmap.c ++++ b/net/netfilter/nft_set_bitmap.c +@@ -270,13 +270,14 @@ static int nft_bitmap_init(const struct + return 0; + } + +-static void nft_bitmap_destroy(const struct nft_set *set) ++static void nft_bitmap_destroy(const struct nft_ctx *ctx, ++ const struct nft_set *set) + { + struct nft_bitmap *priv = nft_set_priv(set); + struct nft_bitmap_elem *be, *n; + + list_for_each_entry_safe(be, n, &priv->list, head) +- nft_set_elem_destroy(set, be, true); ++ nf_tables_set_elem_destroy(ctx, set, be); + } + + static bool nft_bitmap_estimate(const struct nft_set_desc *desc, u32 features, +--- a/net/netfilter/nft_set_hash.c ++++ b/net/netfilter/nft_set_hash.c +@@ -380,19 +380,31 @@ static int nft_rhash_init(const struct n + return 0; + } + ++struct nft_rhash_ctx { ++ const struct nft_ctx ctx; ++ const struct nft_set *set; ++}; ++ + static void nft_rhash_elem_destroy(void *ptr, void *arg) + { +- nft_set_elem_destroy(arg, ptr, true); ++ struct nft_rhash_ctx *rhash_ctx = arg; ++ ++ nf_tables_set_elem_destroy(&rhash_ctx->ctx, rhash_ctx->set, ptr); + } + +-static void nft_rhash_destroy(const struct nft_set *set) ++static void nft_rhash_destroy(const struct nft_ctx *ctx, ++ const struct nft_set *set) + { + struct nft_rhash *priv = nft_set_priv(set); ++ struct nft_rhash_ctx rhash_ctx = { ++ .ctx = *ctx, ++ .set = set, ++ }; + + cancel_delayed_work_sync(&priv->gc_work); + rcu_barrier(); + rhashtable_free_and_destroy(&priv->ht, nft_rhash_elem_destroy, +- (void *)set); ++ (void *)&rhash_ctx); + } + + /* Number of buckets is stored in u32, so cap our result to 1U<<31 */ +@@ -621,7 +633,8 @@ static int nft_hash_init(const struct nf + return 0; + } + +-static void nft_hash_destroy(const struct nft_set *set) ++static void nft_hash_destroy(const struct nft_ctx *ctx, ++ const struct nft_set *set) + { + struct nft_hash *priv = nft_set_priv(set); + struct nft_hash_elem *he; +@@ -631,7 +644,7 @@ static void nft_hash_destroy(const struc + for (i = 0; i < priv->buckets; i++) { + hlist_for_each_entry_safe(he, next, &priv->table[i], node) { + hlist_del_rcu(&he->node); +- nft_set_elem_destroy(set, he, true); ++ nf_tables_set_elem_destroy(ctx, set, he); + } + } + } +--- a/net/netfilter/nft_set_pipapo.c ++++ b/net/netfilter/nft_set_pipapo.c +@@ -2127,10 +2127,12 @@ out_scratch: + + /** + * nft_set_pipapo_match_destroy() - Destroy elements from key mapping array ++ * @ctx: context + * @set: nftables API set representation + * @m: matching data pointing to key mapping array + */ +-static void nft_set_pipapo_match_destroy(const struct nft_set *set, ++static void nft_set_pipapo_match_destroy(const struct nft_ctx *ctx, ++ const struct nft_set *set, + struct nft_pipapo_match *m) + { + struct nft_pipapo_field *f; +@@ -2147,15 +2149,17 @@ static void nft_set_pipapo_match_destroy + + e = f->mt[r].e; + +- nft_set_elem_destroy(set, e, true); ++ nf_tables_set_elem_destroy(ctx, set, e); + } + } + + /** + * nft_pipapo_destroy() - Free private data for set and all committed elements ++ * @ctx: context + * @set: nftables API set representation + */ +-static void nft_pipapo_destroy(const struct nft_set *set) ++static void nft_pipapo_destroy(const struct nft_ctx *ctx, ++ const struct nft_set *set) + { + struct nft_pipapo *priv = nft_set_priv(set); + struct nft_pipapo_match *m; +@@ -2165,7 +2169,7 @@ static void nft_pipapo_destroy(const str + if (m) { + rcu_barrier(); + +- nft_set_pipapo_match_destroy(set, m); ++ nft_set_pipapo_match_destroy(ctx, set, m); + + #ifdef NFT_PIPAPO_ALIGN + free_percpu(m->scratch_aligned); +@@ -2182,7 +2186,7 @@ static void nft_pipapo_destroy(const str + m = priv->clone; + + if (priv->dirty) +- nft_set_pipapo_match_destroy(set, m); ++ nft_set_pipapo_match_destroy(ctx, set, m); + + #ifdef NFT_PIPAPO_ALIGN + free_percpu(priv->clone->scratch_aligned); +--- a/net/netfilter/nft_set_rbtree.c ++++ b/net/netfilter/nft_set_rbtree.c +@@ -657,7 +657,8 @@ static int nft_rbtree_init(const struct + return 0; + } + +-static void nft_rbtree_destroy(const struct nft_set *set) ++static void nft_rbtree_destroy(const struct nft_ctx *ctx, ++ const struct nft_set *set) + { + struct nft_rbtree *priv = nft_set_priv(set); + struct nft_rbtree_elem *rbe; +@@ -668,7 +669,7 @@ static void nft_rbtree_destroy(const str + while ((node = priv->root.rb_node) != NULL) { + rb_erase(node, &priv->root); + rbe = rb_entry(node, struct nft_rbtree_elem, node); +- nft_set_elem_destroy(set, rbe, true); ++ nf_tables_set_elem_destroy(ctx, set, rbe); + } + } + diff --git a/queue-5.10/netfilter-nf_tables-fix-chain-binding-transaction-logic.patch b/queue-5.10/netfilter-nf_tables-fix-chain-binding-transaction-logic.patch new file mode 100644 index 00000000000..039be7394c5 --- /dev/null +++ b/queue-5.10/netfilter-nf_tables-fix-chain-binding-transaction-logic.patch @@ -0,0 +1,432 @@ +From stable-owner@vger.kernel.org Thu Jul 13 10:49:52 2023 +From: Pablo Neira Ayuso +Date: Thu, 13 Jul 2023 10:48:52 +0200 +Subject: netfilter: nf_tables: fix chain binding transaction logic +To: netfilter-devel@vger.kernel.org +Cc: gregkh@linuxfoundation.org, stable@vger.kernel.org, sashal@kernel.org +Message-ID: <20230713084859.71541-5-pablo@netfilter.org> + +From: Pablo Neira Ayuso + +[ Upstream commit 4bedf9eee016286c835e3d8fa981ddece5338795 ] + +Add bound flag to rule and chain transactions as in 6a0a8d10a366 +("netfilter: nf_tables: use-after-free in failing rule with bound set") +to skip them in case that the chain is already bound from the abort +path. + +This patch fixes an imbalance in the chain use refcnt that triggers a +WARN_ON on the table and chain destroy path. + +This patch also disallows nested chain bindings, which is not +supported from userspace. + +The logic to deal with chain binding in nft_data_hold() and +nft_data_release() is not correct. The NFT_TRANS_PREPARE state needs a +special handling in case a chain is bound but next expressions in the +same rule fail to initialize as described by 1240eb93f061 ("netfilter: +nf_tables: incorrect error path handling with NFT_MSG_NEWRULE"). + +The chain is left bound if rule construction fails, so the objects +stored in this chain (and the chain itself) are released by the +transaction records from the abort path, follow up patch ("netfilter: +nf_tables: add NFT_TRANS_PREPARE_ERROR to deal with bound set/chain") +completes this error handling. + +When deleting an existing rule, chain bound flag is set off so the +rule expression .destroy path releases the objects. + +Fixes: d0e2c7de92c7 ("netfilter: nf_tables: add NFT_CHAIN_BINDING") +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + include/net/netfilter/nf_tables.h | 21 ++++++++- + net/netfilter/nf_tables_api.c | 86 ++++++++++++++++++++++++------------- + net/netfilter/nft_immediate.c | 87 ++++++++++++++++++++++++++++++++++---- + 3 files changed, 153 insertions(+), 41 deletions(-) + +--- a/include/net/netfilter/nf_tables.h ++++ b/include/net/netfilter/nf_tables.h +@@ -907,7 +907,10 @@ static inline struct nft_userdata *nft_u + return (void *)&rule->data[rule->dlen]; + } + +-void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule); ++void nft_rule_expr_activate(const struct nft_ctx *ctx, struct nft_rule *rule); ++void nft_rule_expr_deactivate(const struct nft_ctx *ctx, struct nft_rule *rule, ++ enum nft_trans_phase phase); ++void nf_tables_rule_destroy(const struct nft_ctx *ctx, struct nft_rule *rule); + + static inline void nft_set_elem_update_expr(const struct nft_set_ext *ext, + struct nft_regs *regs, +@@ -966,6 +969,7 @@ struct nft_chain { + }; + + int nft_chain_validate(const struct nft_ctx *ctx, const struct nft_chain *chain); ++int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain); + + enum nft_chain_types { + NFT_CHAIN_T_DEFAULT = 0, +@@ -1002,11 +1006,17 @@ int nft_chain_validate_dependency(const + int nft_chain_validate_hooks(const struct nft_chain *chain, + unsigned int hook_flags); + ++static inline bool nft_chain_binding(const struct nft_chain *chain) ++{ ++ return chain->flags & NFT_CHAIN_BINDING; ++} ++ + static inline bool nft_chain_is_bound(struct nft_chain *chain) + { + return (chain->flags & NFT_CHAIN_BINDING) && chain->bound; + } + ++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); + +@@ -1431,6 +1441,7 @@ struct nft_trans_rule { + struct nft_rule *rule; + struct nft_flow_rule *flow; + u32 rule_id; ++ bool bound; + }; + + #define nft_trans_rule(trans) \ +@@ -1439,6 +1450,8 @@ struct nft_trans_rule { + (((struct nft_trans_rule *)trans->data)->flow) + #define nft_trans_rule_id(trans) \ + (((struct nft_trans_rule *)trans->data)->rule_id) ++#define nft_trans_rule_bound(trans) \ ++ (((struct nft_trans_rule *)trans->data)->bound) + + struct nft_trans_set { + struct nft_set *set; +@@ -1454,13 +1467,17 @@ struct nft_trans_set { + (((struct nft_trans_set *)trans->data)->bound) + + struct nft_trans_chain { ++ struct nft_chain *chain; + bool update; + char *name; + struct nft_stats __percpu *stats; + u8 policy; ++ bool bound; + u32 chain_id; + }; + ++#define nft_trans_chain(trans) \ ++ (((struct nft_trans_chain *)trans->data)->chain) + #define nft_trans_chain_update(trans) \ + (((struct nft_trans_chain *)trans->data)->update) + #define nft_trans_chain_name(trans) \ +@@ -1469,6 +1486,8 @@ struct nft_trans_chain { + (((struct nft_trans_chain *)trans->data)->stats) + #define nft_trans_chain_policy(trans) \ + (((struct nft_trans_chain *)trans->data)->policy) ++#define nft_trans_chain_bound(trans) \ ++ (((struct nft_trans_chain *)trans->data)->bound) + #define nft_trans_chain_id(trans) \ + (((struct nft_trans_chain *)trans->data)->chain_id) + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -197,6 +197,48 @@ static void nft_set_trans_bind(const str + } + } + ++static void nft_chain_trans_bind(const struct nft_ctx *ctx, struct nft_chain *chain) ++{ ++ struct nftables_pernet *nft_net; ++ struct net *net = ctx->net; ++ struct nft_trans *trans; ++ ++ if (!nft_chain_binding(chain)) ++ return; ++ ++ nft_net = net_generic(net, nf_tables_net_id); ++ list_for_each_entry_reverse(trans, &nft_net->commit_list, list) { ++ switch (trans->msg_type) { ++ case NFT_MSG_NEWCHAIN: ++ if (nft_trans_chain(trans) == chain) ++ nft_trans_chain_bound(trans) = true; ++ break; ++ case NFT_MSG_NEWRULE: ++ if (trans->ctx.chain == chain) ++ nft_trans_rule_bound(trans) = true; ++ break; ++ } ++ } ++} ++ ++int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain) ++{ ++ if (!nft_chain_binding(chain)) ++ return 0; ++ ++ if (nft_chain_binding(ctx->chain)) ++ return -EOPNOTSUPP; ++ ++ if (chain->bound) ++ return -EBUSY; ++ ++ chain->bound = true; ++ chain->use++; ++ nft_chain_trans_bind(ctx, chain); ++ ++ return 0; ++} ++ + static int nft_netdev_register_hooks(struct net *net, + struct list_head *hook_list) + { +@@ -328,8 +370,9 @@ static struct nft_trans *nft_trans_chain + ntohl(nla_get_be32(ctx->nla[NFTA_CHAIN_ID])); + } + } +- ++ nft_trans_chain(trans) = ctx->chain; + nft_trans_commit_list_add_tail(ctx->net, trans); ++ + return trans; + } + +@@ -347,8 +390,7 @@ static int nft_delchain(struct nft_ctx * + return 0; + } + +-static void nft_rule_expr_activate(const struct nft_ctx *ctx, +- struct nft_rule *rule) ++void nft_rule_expr_activate(const struct nft_ctx *ctx, struct nft_rule *rule) + { + struct nft_expr *expr; + +@@ -361,9 +403,8 @@ static void nft_rule_expr_activate(const + } + } + +-static void nft_rule_expr_deactivate(const struct nft_ctx *ctx, +- struct nft_rule *rule, +- enum nft_trans_phase phase) ++void nft_rule_expr_deactivate(const struct nft_ctx *ctx, struct nft_rule *rule, ++ enum nft_trans_phase phase) + { + struct nft_expr *expr; + +@@ -2017,7 +2058,7 @@ static int nft_basechain_init(struct nft + return 0; + } + +-static int nft_chain_add(struct nft_table *table, struct nft_chain *chain) ++int nft_chain_add(struct nft_table *table, struct nft_chain *chain) + { + int err; + +@@ -3118,8 +3159,7 @@ err_fill_rule_info: + return err; + } + +-static void nf_tables_rule_destroy(const struct nft_ctx *ctx, +- struct nft_rule *rule) ++void nf_tables_rule_destroy(const struct nft_ctx *ctx, struct nft_rule *rule) + { + struct nft_expr *expr, *next; + +@@ -3136,7 +3176,7 @@ static void nf_tables_rule_destroy(const + kfree(rule); + } + +-void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule) ++static void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule) + { + nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_RELEASE); + nf_tables_rule_destroy(ctx, rule); +@@ -5547,7 +5587,6 @@ static int nf_tables_newsetelem(struct n + void nft_data_hold(const struct nft_data *data, enum nft_data_types type) + { + struct nft_chain *chain; +- struct nft_rule *rule; + + if (type == NFT_DATA_VERDICT) { + switch (data->verdict.code) { +@@ -5555,15 +5594,6 @@ void nft_data_hold(const struct nft_data + case NFT_GOTO: + chain = data->verdict.chain; + chain->use++; +- +- if (!nft_chain_is_bound(chain)) +- break; +- +- chain->table->use++; +- list_for_each_entry(rule, &chain->rules, list) +- chain->use++; +- +- nft_chain_add(chain->table, chain); + break; + } + } +@@ -8254,7 +8284,7 @@ static int __nf_tables_abort(struct net + kfree(nft_trans_chain_name(trans)); + nft_trans_destroy(trans); + } else { +- if (nft_chain_is_bound(trans->ctx.chain)) { ++ if (nft_trans_chain_bound(trans)) { + nft_trans_destroy(trans); + break; + } +@@ -8271,6 +8301,10 @@ static int __nf_tables_abort(struct net + nft_trans_destroy(trans); + break; + case NFT_MSG_NEWRULE: ++ if (nft_trans_rule_bound(trans)) { ++ nft_trans_destroy(trans); ++ break; ++ } + trans->ctx.chain->use--; + list_del_rcu(&nft_trans_rule(trans)->list); + nft_rule_expr_deactivate(&trans->ctx, +@@ -8796,22 +8830,12 @@ static int nft_verdict_init(const struct + static void nft_verdict_uninit(const struct nft_data *data) + { + struct nft_chain *chain; +- struct nft_rule *rule; + + switch (data->verdict.code) { + case NFT_JUMP: + case NFT_GOTO: + chain = data->verdict.chain; + chain->use--; +- +- if (!nft_chain_is_bound(chain)) +- break; +- +- chain->table->use--; +- list_for_each_entry(rule, &chain->rules, list) +- chain->use--; +- +- nft_chain_del(chain); + break; + } + } +--- a/net/netfilter/nft_immediate.c ++++ b/net/netfilter/nft_immediate.c +@@ -76,11 +76,9 @@ static int nft_immediate_init(const stru + switch (priv->data.verdict.code) { + case NFT_JUMP: + case NFT_GOTO: +- if (nft_chain_is_bound(chain)) { +- err = -EBUSY; +- goto err1; +- } +- chain->bound = true; ++ err = nf_tables_bind_chain(ctx, chain); ++ if (err < 0) ++ return err; + break; + default: + break; +@@ -98,6 +96,31 @@ static void nft_immediate_activate(const + const struct nft_expr *expr) + { + const struct nft_immediate_expr *priv = nft_expr_priv(expr); ++ const struct nft_data *data = &priv->data; ++ struct nft_ctx chain_ctx; ++ struct nft_chain *chain; ++ struct nft_rule *rule; ++ ++ if (priv->dreg == NFT_REG_VERDICT) { ++ switch (data->verdict.code) { ++ case NFT_JUMP: ++ case NFT_GOTO: ++ chain = data->verdict.chain; ++ if (!nft_chain_binding(chain)) ++ break; ++ ++ chain_ctx = *ctx; ++ chain_ctx.chain = chain; ++ ++ list_for_each_entry(rule, &chain->rules, list) ++ nft_rule_expr_activate(&chain_ctx, rule); ++ ++ nft_clear(ctx->net, chain); ++ break; ++ default: ++ break; ++ } ++ } + + return nft_data_hold(&priv->data, nft_dreg_to_type(priv->dreg)); + } +@@ -107,6 +130,40 @@ static void nft_immediate_deactivate(con + enum nft_trans_phase phase) + { + const struct nft_immediate_expr *priv = nft_expr_priv(expr); ++ const struct nft_data *data = &priv->data; ++ struct nft_ctx chain_ctx; ++ struct nft_chain *chain; ++ struct nft_rule *rule; ++ ++ if (priv->dreg == NFT_REG_VERDICT) { ++ switch (data->verdict.code) { ++ case NFT_JUMP: ++ case NFT_GOTO: ++ chain = data->verdict.chain; ++ if (!nft_chain_binding(chain)) ++ break; ++ ++ chain_ctx = *ctx; ++ chain_ctx.chain = chain; ++ ++ list_for_each_entry(rule, &chain->rules, list) ++ nft_rule_expr_deactivate(&chain_ctx, rule, phase); ++ ++ switch (phase) { ++ case NFT_TRANS_PREPARE: ++ nft_deactivate_next(ctx->net, chain); ++ break; ++ default: ++ nft_chain_del(chain); ++ chain->bound = false; ++ chain->table->use--; ++ break; ++ } ++ break; ++ default: ++ break; ++ } ++ } + + if (phase == NFT_TRANS_COMMIT) + return; +@@ -131,15 +188,27 @@ static void nft_immediate_destroy(const + case NFT_GOTO: + chain = data->verdict.chain; + +- if (!nft_chain_is_bound(chain)) ++ if (!nft_chain_binding(chain)) ++ break; ++ ++ /* Rule construction failed, but chain is already bound: ++ * let the transaction records release this chain and its rules. ++ */ ++ if (chain->bound) { ++ chain->use--; + break; ++ } + ++ /* Rule has been deleted, release chain and its rules. */ + chain_ctx = *ctx; + chain_ctx.chain = chain; + +- list_for_each_entry_safe(rule, n, &chain->rules, list) +- nf_tables_rule_release(&chain_ctx, rule); +- ++ chain->use--; ++ list_for_each_entry_safe(rule, n, &chain->rules, list) { ++ chain->use--; ++ list_del(&rule->list); ++ nf_tables_rule_destroy(&chain_ctx, rule); ++ } + nf_tables_chain_destroy(&chain_ctx); + break; + default: diff --git a/queue-5.10/netfilter-nf_tables-fix-scheduling-while-atomic-splat.patch b/queue-5.10/netfilter-nf_tables-fix-scheduling-while-atomic-splat.patch new file mode 100644 index 00000000000..8193a3c767e --- /dev/null +++ b/queue-5.10/netfilter-nf_tables-fix-scheduling-while-atomic-splat.patch @@ -0,0 +1,39 @@ +From stable-owner@vger.kernel.org Thu Jul 13 10:49:58 2023 +From: Pablo Neira Ayuso +Date: Thu, 13 Jul 2023 10:48:59 +0200 +Subject: netfilter: nf_tables: fix scheduling-while-atomic splat +To: netfilter-devel@vger.kernel.org +Cc: gregkh@linuxfoundation.org, stable@vger.kernel.org, sashal@kernel.org +Message-ID: <20230713084859.71541-12-pablo@netfilter.org> + +From: Florian Westphal + +[ Upstream commit 2024439bd5ceb145eeeb428b2a59e9b905153ac3 ] + +nf_tables_check_loops() can be called from rhashtable list +walk so cond_resched() cannot be used here. + +Fixes: 81ea01066741 ("netfilter: nf_tables: add rescheduling points during loop detection walks") +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + net/netfilter/nf_tables_api.c | 4 ---- + 1 file changed, 4 deletions(-) + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -8684,13 +8684,9 @@ static int nf_tables_check_loops(const s + break; + } + } +- +- cond_resched(); + } + + list_for_each_entry(set, &ctx->table->sets, list) { +- cond_resched(); +- + if (!nft_is_active_next(ctx->net, set)) + continue; + if (!(set->flags & NFT_SET_MAP) || diff --git a/queue-5.10/netfilter-nf_tables-incorrect-error-path-handling-with-nft_msg_newrule.patch b/queue-5.10/netfilter-nf_tables-incorrect-error-path-handling-with-nft_msg_newrule.patch new file mode 100644 index 00000000000..17c6b4af69d --- /dev/null +++ b/queue-5.10/netfilter-nf_tables-incorrect-error-path-handling-with-nft_msg_newrule.patch @@ -0,0 +1,73 @@ +From stable-owner@vger.kernel.org Thu Jul 13 10:49:52 2023 +From: Pablo Neira Ayuso +Date: Thu, 13 Jul 2023 10:48:51 +0200 +Subject: netfilter: nf_tables: incorrect error path handling with NFT_MSG_NEWRULE +To: netfilter-devel@vger.kernel.org +Cc: gregkh@linuxfoundation.org, stable@vger.kernel.org, sashal@kernel.org +Message-ID: <20230713084859.71541-4-pablo@netfilter.org> + +From: Pablo Neira Ayuso + +[ Upstream commit 1240eb93f0616b21c675416516ff3d74798fdc97 ] + +In case of error when adding a new rule that refers to an anonymous set, +deactivate expressions via NFT_TRANS_PREPARE state, not NFT_TRANS_RELEASE. +Thus, the lookup expression marks anonymous sets as inactive in the next +generation to ensure it is not reachable in this transaction anymore and +decrement the set refcount as introduced by c1592a89942e ("netfilter: +nf_tables: deactivate anonymous set from preparation phase"). The abort +step takes care of undoing the anonymous set. + +This is also consistent with rule deletion, where NFT_TRANS_PREPARE is +used. Note that this error path is exercised in the preparation step of +the commit protocol. This patch replaces nf_tables_rule_release() by the +deactivate and destroy calls, this time with NFT_TRANS_PREPARE. + +Due to this incorrect error handling, it is possible to access a +dangling pointer to the anonymous set that remains in the transaction +list. + +[1009.379054] BUG: KASAN: use-after-free in nft_set_lookup_global+0x147/0x1a0 [nf_tables] +[1009.379106] Read of size 8 at addr ffff88816c4c8020 by task nft-rule-add/137110 +[1009.379116] CPU: 7 PID: 137110 Comm: nft-rule-add Not tainted 6.4.0-rc4+ #256 +[1009.379128] Call Trace: +[1009.379132] +[1009.379135] dump_stack_lvl+0x33/0x50 +[1009.379146] ? nft_set_lookup_global+0x147/0x1a0 [nf_tables] +[1009.379191] print_address_description.constprop.0+0x27/0x300 +[1009.379201] kasan_report+0x107/0x120 +[1009.379210] ? nft_set_lookup_global+0x147/0x1a0 [nf_tables] +[1009.379255] nft_set_lookup_global+0x147/0x1a0 [nf_tables] +[1009.379302] nft_lookup_init+0xa5/0x270 [nf_tables] +[1009.379350] nf_tables_newrule+0x698/0xe50 [nf_tables] +[1009.379397] ? nf_tables_rule_release+0xe0/0xe0 [nf_tables] +[1009.379441] ? kasan_unpoison+0x23/0x50 +[1009.379450] nfnetlink_rcv_batch+0x97c/0xd90 [nfnetlink] +[1009.379470] ? nfnetlink_rcv_msg+0x480/0x480 [nfnetlink] +[1009.379485] ? __alloc_skb+0xb8/0x1e0 +[1009.379493] ? __alloc_skb+0xb8/0x1e0 +[1009.379502] ? entry_SYSCALL_64_after_hwframe+0x46/0xb0 +[1009.379509] ? unwind_get_return_address+0x2a/0x40 +[1009.379517] ? write_profile+0xc0/0xc0 +[1009.379524] ? avc_lookup+0x8f/0xc0 +[1009.379532] ? __rcu_read_unlock+0x43/0x60 + +Fixes: 958bee14d071 ("netfilter: nf_tables: use new transaction infrastructure to handle sets") +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + net/netfilter/nf_tables_api.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -3409,7 +3409,8 @@ static int nf_tables_newrule(struct net + + return 0; + err2: +- nf_tables_rule_release(&ctx, rule); ++ nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE); ++ nf_tables_rule_destroy(&ctx, rule); + err1: + for (i = 0; i < n; i++) { + if (info[i].ops) { diff --git a/queue-5.10/netfilter-nf_tables-reject-unbound-anonymous-set-before-commit-phase.patch b/queue-5.10/netfilter-nf_tables-reject-unbound-anonymous-set-before-commit-phase.patch new file mode 100644 index 00000000000..08a0f52413e --- /dev/null +++ b/queue-5.10/netfilter-nf_tables-reject-unbound-anonymous-set-before-commit-phase.patch @@ -0,0 +1,139 @@ +From stable-owner@vger.kernel.org Thu Jul 13 10:49:53 2023 +From: Pablo Neira Ayuso +Date: Thu, 13 Jul 2023 10:48:54 +0200 +Subject: netfilter: nf_tables: reject unbound anonymous set before commit phase +To: netfilter-devel@vger.kernel.org +Cc: gregkh@linuxfoundation.org, stable@vger.kernel.org, sashal@kernel.org +Message-ID: <20230713084859.71541-7-pablo@netfilter.org> + +From: Pablo Neira Ayuso + +[ Upstream commit 938154b93be8cd611ddfd7bafc1849f3c4355201 ] + +Add a new list to track set transaction and to check for unbound +anonymous sets before entering the commit phase. + +Bail out at the end of the transaction handling if an anonymous set +remains unbound. + +Fixes: 96518518cc41 ("netfilter: add nftables") +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 | 34 +++++++++++++++++++++++++++++++--- + 2 files changed, 34 insertions(+), 3 deletions(-) + +--- a/include/net/netfilter/nf_tables.h ++++ b/include/net/netfilter/nf_tables.h +@@ -1426,6 +1426,7 @@ static inline void nft_set_elem_clear_bu + * struct nft_trans - nf_tables object update in transaction + * + * @list: used internally ++ * @binding_list: list of objects with possible bindings + * @msg_type: message type + * @put_net: ctx->net needs to be put + * @ctx: transaction context +@@ -1433,6 +1434,7 @@ static inline void nft_set_elem_clear_bu + */ + struct nft_trans { + struct list_head list; ++ struct list_head binding_list; + int msg_type; + bool put_net; + struct nft_ctx ctx; +@@ -1559,6 +1561,7 @@ __be64 nf_jiffies64_to_msecs(u64 input); + struct nftables_pernet { + struct list_head tables; + struct list_head commit_list; ++ struct list_head binding_list; + struct list_head module_list; + struct list_head notify_list; + struct mutex commit_mutex; +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -155,6 +155,7 @@ static struct nft_trans *nft_trans_alloc + return NULL; + + INIT_LIST_HEAD(&trans->list); ++ INIT_LIST_HEAD(&trans->binding_list); + trans->msg_type = msg_type; + trans->ctx = *ctx; + +@@ -167,9 +168,15 @@ static struct nft_trans *nft_trans_alloc + return nft_trans_alloc_gfp(ctx, msg_type, size, GFP_KERNEL); + } + +-static void nft_trans_destroy(struct nft_trans *trans) ++static void nft_trans_list_del(struct nft_trans *trans) + { + list_del(&trans->list); ++ list_del(&trans->binding_list); ++} ++ ++static void nft_trans_destroy(struct nft_trans *trans) ++{ ++ nft_trans_list_del(trans); + kfree(trans); + } + +@@ -347,6 +354,14 @@ static void nft_trans_commit_list_add_ta + struct nftables_pernet *nft_net; + + nft_net = net_generic(net, nf_tables_net_id); ++ ++ switch (trans->msg_type) { ++ case NFT_MSG_NEWSET: ++ if (nft_set_is_anonymous(nft_trans_set(trans))) ++ list_add_tail(&trans->binding_list, &nft_net->binding_list); ++ break; ++ } ++ + list_add_tail(&trans->list, &nft_net->commit_list); + } + +@@ -7717,7 +7732,7 @@ static void nf_tables_trans_destroy_work + synchronize_rcu(); + + list_for_each_entry_safe(trans, next, &head, list) { +- list_del(&trans->list); ++ nft_trans_list_del(trans); + nft_commit_release(trans); + } + } +@@ -8019,6 +8034,18 @@ static int nf_tables_commit(struct net * + return 0; + } + ++ list_for_each_entry(trans, &nft_net->binding_list, binding_list) { ++ switch (trans->msg_type) { ++ case NFT_MSG_NEWSET: ++ if (nft_set_is_anonymous(nft_trans_set(trans)) && ++ !nft_trans_set_bound(trans)) { ++ pr_warn_once("nftables ruleset with unbound set\n"); ++ return -EINVAL; ++ } ++ break; ++ } ++ } ++ + /* 0. Validate ruleset, otherwise roll back for error reporting. */ + if (nf_tables_validate(net) < 0) + return -EAGAIN; +@@ -8421,7 +8448,7 @@ static int __nf_tables_abort(struct net + + list_for_each_entry_safe_reverse(trans, next, + &nft_net->commit_list, list) { +- list_del(&trans->list); ++ nft_trans_list_del(trans); + nf_tables_abort_release(trans); + } + +@@ -9120,6 +9147,7 @@ static int __net_init nf_tables_init_net + + INIT_LIST_HEAD(&nft_net->tables); + INIT_LIST_HEAD(&nft_net->commit_list); ++ INIT_LIST_HEAD(&nft_net->binding_list); + INIT_LIST_HEAD(&nft_net->module_list); + INIT_LIST_HEAD(&nft_net->notify_list); + mutex_init(&nft_net->commit_mutex); diff --git a/queue-5.10/netfilter-nf_tables-reject-unbound-chain-set-before-commit-phase.patch b/queue-5.10/netfilter-nf_tables-reject-unbound-chain-set-before-commit-phase.patch new file mode 100644 index 00000000000..22f706a8bcc --- /dev/null +++ b/queue-5.10/netfilter-nf_tables-reject-unbound-chain-set-before-commit-phase.patch @@ -0,0 +1,54 @@ +From stable-owner@vger.kernel.org Thu Jul 13 10:49:52 2023 +From: Pablo Neira Ayuso +Date: Thu, 13 Jul 2023 10:48:55 +0200 +Subject: netfilter: nf_tables: reject unbound chain set before commit phase +To: netfilter-devel@vger.kernel.org +Cc: gregkh@linuxfoundation.org, stable@vger.kernel.org, sashal@kernel.org +Message-ID: <20230713084859.71541-8-pablo@netfilter.org> + +From: Pablo Neira Ayuso + +[ Upstream commit 62e1e94b246e685d89c3163aaef4b160e42ceb02 ] + +Use binding list to track set transaction and to check for unbound +chains before entering the commit phase. + +Bail out if chain binding remain unused before entering the commit +step. + +Fixes: d0e2c7de92c7 ("netfilter: nf_tables: add NFT_CHAIN_BINDING") +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + net/netfilter/nf_tables_api.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -360,6 +360,11 @@ static void nft_trans_commit_list_add_ta + if (nft_set_is_anonymous(nft_trans_set(trans))) + list_add_tail(&trans->binding_list, &nft_net->binding_list); + break; ++ case NFT_MSG_NEWCHAIN: ++ if (!nft_trans_chain_update(trans) && ++ nft_chain_binding(nft_trans_chain(trans))) ++ list_add_tail(&trans->binding_list, &nft_net->binding_list); ++ break; + } + + list_add_tail(&trans->list, &nft_net->commit_list); +@@ -8043,6 +8048,14 @@ static int nf_tables_commit(struct net * + return -EINVAL; + } + break; ++ case NFT_MSG_NEWCHAIN: ++ if (!nft_trans_chain_update(trans) && ++ nft_chain_binding(nft_trans_chain(trans)) && ++ !nft_trans_chain_bound(trans)) { ++ pr_warn_once("nftables ruleset with unbound chain\n"); ++ return -EINVAL; ++ } ++ break; + } + } + diff --git a/queue-5.10/netfilter-nf_tables-unbind-non-anonymous-set-if-rule-construction-fails.patch b/queue-5.10/netfilter-nf_tables-unbind-non-anonymous-set-if-rule-construction-fails.patch new file mode 100644 index 00000000000..aee6afbe4d8 --- /dev/null +++ b/queue-5.10/netfilter-nf_tables-unbind-non-anonymous-set-if-rule-construction-fails.patch @@ -0,0 +1,33 @@ +From stable-owner@vger.kernel.org Thu Jul 13 10:49:58 2023 +From: Pablo Neira Ayuso +Date: Thu, 13 Jul 2023 10:48:58 +0200 +Subject: netfilter: nf_tables: unbind non-anonymous set if rule construction fails +To: netfilter-devel@vger.kernel.org +Cc: gregkh@linuxfoundation.org, stable@vger.kernel.org, sashal@kernel.org +Message-ID: <20230713084859.71541-11-pablo@netfilter.org> + +From: Pablo Neira Ayuso + +[ Upstream commit 3e70489721b6c870252c9082c496703677240f53 ] + +Otherwise a dangling reference to a rule object that is gone remains +in the set binding list. + +Fixes: 26b5a5712eb8 ("netfilter: nf_tables: add NFT_TRANS_PREPARE_ERROR to deal with bound set/chain") +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + net/netfilter/nf_tables_api.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -4689,6 +4689,8 @@ void nf_tables_deactivate_set(const stru + nft_set_trans_unbind(ctx, set); + if (nft_set_is_anonymous(set)) + nft_deactivate_next(ctx->net, set); ++ else ++ list_del_rcu(&binding->list); + + set->use--; + break; diff --git a/queue-5.10/netfilter-nf_tables-use-net_generic-infra-for-transaction-data.patch b/queue-5.10/netfilter-nf_tables-use-net_generic-infra-for-transaction-data.patch new file mode 100644 index 00000000000..769fada9476 --- /dev/null +++ b/queue-5.10/netfilter-nf_tables-use-net_generic-infra-for-transaction-data.patch @@ -0,0 +1,1443 @@ +From stable-owner@vger.kernel.org Thu Jul 13 10:49:52 2023 +From: Pablo Neira Ayuso +Date: Thu, 13 Jul 2023 10:48:49 +0200 +Subject: netfilter: nf_tables: use net_generic infra for transaction data +To: netfilter-devel@vger.kernel.org +Cc: gregkh@linuxfoundation.org, stable@vger.kernel.org, sashal@kernel.org +Message-ID: <20230713084859.71541-2-pablo@netfilter.org> + +From: Florian Westphal + +[ Upstream commit 0854db2aaef3fcdd3498a9d299c60adea2aa3dc6 ] + +This moves all nf_tables pernet data from struct net to a net_generic +extension, with the exception of the gencursor. + +The latter is used in the data path and also outside of the nf_tables +core. All others are only used from the configuration plane. + +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + include/net/netfilter/nf_tables.h | 10 + include/net/netns/nftables.h | 7 + net/netfilter/nf_tables_api.c | 382 +++++++++++++++++++++++--------------- + net/netfilter/nf_tables_offload.c | 30 +- + net/netfilter/nft_chain_filter.c | 11 - + net/netfilter/nft_dynset.c | 6 + 6 files changed, 279 insertions(+), 167 deletions(-) + +--- a/include/net/netfilter/nf_tables.h ++++ b/include/net/netfilter/nf_tables.h +@@ -1535,4 +1535,14 @@ void nf_tables_trans_destroy_flush_work( + int nf_msecs_to_jiffies64(const struct nlattr *nla, u64 *result); + __be64 nf_jiffies64_to_msecs(u64 input); + ++struct nftables_pernet { ++ struct list_head tables; ++ struct list_head commit_list; ++ struct list_head module_list; ++ struct list_head notify_list; ++ struct mutex commit_mutex; ++ unsigned int base_seq; ++ u8 validate_state; ++}; ++ + #endif /* _NET_NF_TABLES_H */ +--- a/include/net/netns/nftables.h ++++ b/include/net/netns/nftables.h +@@ -5,14 +5,7 @@ + #include + + struct netns_nftables { +- struct list_head tables; +- struct list_head commit_list; +- struct list_head module_list; +- struct list_head notify_list; +- struct mutex commit_mutex; +- unsigned int base_seq; + u8 gencursor; +- u8 validate_state; + }; + + #endif +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -21,10 +21,13 @@ + #include + #include + #include ++#include + #include + + #define NFT_MODULE_AUTOLOAD_LIMIT (MODULE_NAME_LEN - sizeof("nft-expr-255-")) + ++unsigned int nf_tables_net_id __read_mostly; ++ + static LIST_HEAD(nf_tables_expressions); + static LIST_HEAD(nf_tables_objects); + static LIST_HEAD(nf_tables_flowtables); +@@ -103,7 +106,9 @@ static const u8 nft2audit_op[NFT_MSG_MAX + + static void nft_validate_state_update(struct net *net, u8 new_validate_state) + { +- switch (net->nft.validate_state) { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); ++ ++ switch (nft_net->validate_state) { + case NFT_VALIDATE_SKIP: + WARN_ON_ONCE(new_validate_state == NFT_VALIDATE_DO); + break; +@@ -114,7 +119,7 @@ static void nft_validate_state_update(st + return; + } + +- net->nft.validate_state = new_validate_state; ++ nft_net->validate_state = new_validate_state; + } + static void nf_tables_trans_destroy_work(struct work_struct *w); + static DECLARE_WORK(trans_destroy_work, nf_tables_trans_destroy_work); +@@ -170,13 +175,15 @@ static void nft_trans_destroy(struct nft + + static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set) + { ++ struct nftables_pernet *nft_net; + struct net *net = ctx->net; + struct nft_trans *trans; + + if (!nft_set_is_anonymous(set)) + return; + +- list_for_each_entry_reverse(trans, &net->nft.commit_list, list) { ++ nft_net = net_generic(net, nf_tables_net_id); ++ list_for_each_entry_reverse(trans, &nft_net->commit_list, list) { + switch (trans->msg_type) { + case NFT_MSG_NEWSET: + if (nft_trans_set(trans) == set) +@@ -270,6 +277,14 @@ static void nf_tables_unregister_hook(st + nf_unregister_net_hook(net, &basechain->ops); + } + ++static void nft_trans_commit_list_add_tail(struct net *net, struct nft_trans *trans) ++{ ++ struct nftables_pernet *nft_net; ++ ++ nft_net = net_generic(net, nf_tables_net_id); ++ list_add_tail(&trans->list, &nft_net->commit_list); ++} ++ + static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type) + { + struct nft_trans *trans; +@@ -281,7 +296,7 @@ static int nft_trans_table_add(struct nf + if (msg_type == NFT_MSG_NEWTABLE) + nft_activate_next(ctx->net, ctx->table); + +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + return 0; + } + +@@ -314,7 +329,7 @@ static struct nft_trans *nft_trans_chain + } + } + +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + return trans; + } + +@@ -387,7 +402,7 @@ static struct nft_trans *nft_trans_rule_ + ntohl(nla_get_be32(ctx->nla[NFTA_RULE_ID])); + } + nft_trans_rule(trans) = rule; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + + return trans; + } +@@ -453,7 +468,7 @@ static int nft_trans_set_add(const struc + nft_activate_next(ctx->net, set); + } + nft_trans_set(trans) = set; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + + return 0; + } +@@ -485,7 +500,7 @@ static int nft_trans_obj_add(struct nft_ + nft_activate_next(ctx->net, obj); + + nft_trans_obj(trans) = obj; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + + return 0; + } +@@ -519,7 +534,7 @@ static int nft_trans_flowtable_add(struc + + INIT_LIST_HEAD(&nft_trans_flowtable_hooks(trans)); + nft_trans_flowtable(trans) = flowtable; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + + return 0; + } +@@ -547,13 +562,15 @@ static struct nft_table *nft_table_looku + const struct nlattr *nla, + u8 family, u8 genmask) + { ++ struct nftables_pernet *nft_net; + struct nft_table *table; + + if (nla == NULL) + return ERR_PTR(-EINVAL); + +- list_for_each_entry_rcu(table, &net->nft.tables, list, +- lockdep_is_held(&net->nft.commit_mutex)) { ++ nft_net = net_generic(net, nf_tables_net_id); ++ list_for_each_entry_rcu(table, &nft_net->tables, list, ++ lockdep_is_held(&nft_net->commit_mutex)) { + if (!nla_strcmp(nla, table->name) && + table->family == family && + nft_active_genmask(table, genmask)) +@@ -567,9 +584,11 @@ static struct nft_table *nft_table_looku + const struct nlattr *nla, + u8 genmask) + { ++ struct nftables_pernet *nft_net; + struct nft_table *table; + +- list_for_each_entry(table, &net->nft.tables, list) { ++ nft_net = net_generic(net, nf_tables_net_id); ++ list_for_each_entry(table, &nft_net->tables, list) { + if (be64_to_cpu(nla_get_be64(nla)) == table->handle && + nft_active_genmask(table, genmask)) + return table; +@@ -621,6 +640,7 @@ struct nft_module_request { + static int nft_request_module(struct net *net, const char *fmt, ...) + { + char module_name[MODULE_NAME_LEN]; ++ struct nftables_pernet *nft_net; + struct nft_module_request *req; + va_list args; + int ret; +@@ -631,7 +651,8 @@ static int nft_request_module(struct net + if (ret >= MODULE_NAME_LEN) + return 0; + +- list_for_each_entry(req, &net->nft.module_list, list) { ++ nft_net = net_generic(net, nf_tables_net_id); ++ list_for_each_entry(req, &nft_net->module_list, list) { + if (!strcmp(req->module, module_name)) { + if (req->done) + return 0; +@@ -647,7 +668,7 @@ static int nft_request_module(struct net + + req->done = false; + strlcpy(req->module, module_name, MODULE_NAME_LEN); +- list_add_tail(&req->list, &net->nft.module_list); ++ list_add_tail(&req->list, &nft_net->module_list); + + return -EAGAIN; + } +@@ -685,7 +706,9 @@ nf_tables_chain_type_lookup(struct net * + + static __be16 nft_base_seq(const struct net *net) + { +- return htons(net->nft.base_seq & 0xffff); ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); ++ ++ return htons(nft_net->base_seq & 0xffff); + } + + static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = { +@@ -743,6 +766,7 @@ static void nft_notify_enqueue(struct sk + + static void nf_tables_table_notify(const struct nft_ctx *ctx, int event) + { ++ struct nftables_pernet *nft_net; + struct sk_buff *skb; + int err; + +@@ -761,7 +785,8 @@ static void nf_tables_table_notify(const + goto err; + } + +- nft_notify_enqueue(skb, ctx->report, &ctx->net->nft.notify_list); ++ nft_net = net_generic(ctx->net, nf_tables_net_id); ++ nft_notify_enqueue(skb, ctx->report, &nft_net->notify_list); + return; + err: + nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS); +@@ -771,15 +796,17 @@ static int nf_tables_dump_tables(struct + struct netlink_callback *cb) + { + const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); ++ struct nftables_pernet *nft_net; + const struct nft_table *table; + unsigned int idx = 0, s_idx = cb->args[0]; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; + + rcu_read_lock(); +- cb->seq = net->nft.base_seq; ++ nft_net = net_generic(net, nf_tables_net_id); ++ cb->seq = nft_net->base_seq; + +- list_for_each_entry_rcu(table, &net->nft.tables, list) { ++ list_for_each_entry_rcu(table, &nft_net->tables, list) { + if (family != NFPROTO_UNSPEC && family != table->family) + continue; + +@@ -954,7 +981,7 @@ static int nf_tables_updtable(struct nft + goto err; + + nft_trans_table_update(trans) = true; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + return 0; + err: + nft_trans_destroy(trans); +@@ -1017,6 +1044,7 @@ static int nf_tables_newtable(struct net + const struct nlattr * const nla[], + struct netlink_ext_ack *extack) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + u8 genmask = nft_genmask_next(net); + int family = nfmsg->nfgen_family; +@@ -1026,7 +1054,7 @@ static int nf_tables_newtable(struct net + u32 flags = 0; + int err; + +- lockdep_assert_held(&net->nft.commit_mutex); ++ lockdep_assert_held(&nft_net->commit_mutex); + attr = nla[NFTA_TABLE_NAME]; + table = nft_table_lookup(net, attr, family, genmask); + if (IS_ERR(table)) { +@@ -1084,7 +1112,7 @@ static int nf_tables_newtable(struct net + if (err < 0) + goto err_trans; + +- list_add_tail_rcu(&table->list, &net->nft.tables); ++ list_add_tail_rcu(&table->list, &nft_net->tables); + return 0; + err_trans: + rhltable_destroy(&table->chains_ht); +@@ -1172,11 +1200,12 @@ out: + + static int nft_flush(struct nft_ctx *ctx, int family) + { ++ struct nftables_pernet *nft_net = net_generic(ctx->net, nf_tables_net_id); + struct nft_table *table, *nt; + const struct nlattr * const *nla = ctx->nla; + int err = 0; + +- list_for_each_entry_safe(table, nt, &ctx->net->nft.tables, list) { ++ list_for_each_entry_safe(table, nt, &nft_net->tables, list) { + if (family != AF_UNSPEC && table->family != family) + continue; + +@@ -1291,7 +1320,9 @@ nft_chain_lookup_byhandle(const struct n + static bool lockdep_commit_lock_is_held(const struct net *net) + { + #ifdef CONFIG_PROVE_LOCKING +- return lockdep_is_held(&net->nft.commit_mutex); ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); ++ ++ return lockdep_is_held(&nft_net->commit_mutex); + #else + return true; + #endif +@@ -1494,6 +1525,7 @@ nla_put_failure: + + static void nf_tables_chain_notify(const struct nft_ctx *ctx, int event) + { ++ struct nftables_pernet *nft_net; + struct sk_buff *skb; + int err; + +@@ -1513,7 +1545,8 @@ static void nf_tables_chain_notify(const + goto err; + } + +- nft_notify_enqueue(skb, ctx->report, &ctx->net->nft.notify_list); ++ nft_net = net_generic(ctx->net, nf_tables_net_id); ++ nft_notify_enqueue(skb, ctx->report, &nft_net->notify_list); + return; + err: + nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS); +@@ -1528,11 +1561,13 @@ static int nf_tables_dump_chains(struct + unsigned int idx = 0, s_idx = cb->args[0]; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; ++ struct nftables_pernet *nft_net; + + rcu_read_lock(); +- cb->seq = net->nft.base_seq; ++ nft_net = net_generic(net, nf_tables_net_id); ++ cb->seq = nft_net->base_seq; + +- list_for_each_entry_rcu(table, &net->nft.tables, list) { ++ list_for_each_entry_rcu(table, &nft_net->tables, list) { + if (family != NFPROTO_UNSPEC && family != table->family) + continue; + +@@ -1847,11 +1882,12 @@ static int nft_chain_parse_hook(struct n + struct nft_chain_hook *hook, u8 family, + bool autoload) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nlattr *ha[NFTA_HOOK_MAX + 1]; + const struct nft_chain_type *type; + int err; + +- lockdep_assert_held(&net->nft.commit_mutex); ++ lockdep_assert_held(&nft_net->commit_mutex); + lockdep_nfnl_nft_mutex_not_held(); + + err = nla_parse_nested_deprecated(ha, NFTA_HOOK_MAX, +@@ -2244,6 +2280,7 @@ static int nf_tables_updchain(struct nft + + if (nla[NFTA_CHAIN_HANDLE] && + nla[NFTA_CHAIN_NAME]) { ++ struct nftables_pernet *nft_net = net_generic(ctx->net, nf_tables_net_id); + struct nft_trans *tmp; + char *name; + +@@ -2253,7 +2290,7 @@ static int nf_tables_updchain(struct nft + goto err; + + err = -EEXIST; +- list_for_each_entry(tmp, &ctx->net->nft.commit_list, list) { ++ list_for_each_entry(tmp, &nft_net->commit_list, list) { + if (tmp->msg_type == NFT_MSG_NEWCHAIN && + tmp->ctx.table == table && + nft_trans_chain_update(tmp) && +@@ -2267,7 +2304,7 @@ static int nf_tables_updchain(struct nft + + nft_trans_chain_name(trans) = name; + } +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + + return 0; + err: +@@ -2280,10 +2317,11 @@ static struct nft_chain *nft_chain_looku + const struct nft_table *table, + const struct nlattr *nla) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + u32 id = ntohl(nla_get_be32(nla)); + struct nft_trans *trans; + +- list_for_each_entry(trans, &net->nft.commit_list, list) { ++ list_for_each_entry(trans, &nft_net->commit_list, list) { + struct nft_chain *chain = trans->ctx.chain; + + if (trans->msg_type == NFT_MSG_NEWCHAIN && +@@ -2299,6 +2337,7 @@ static int nf_tables_newchain(struct net + const struct nlattr * const nla[], + struct netlink_ext_ack *extack) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + u8 genmask = nft_genmask_next(net); + int family = nfmsg->nfgen_family; +@@ -2310,7 +2349,7 @@ static int nf_tables_newchain(struct net + u64 handle = 0; + u32 flags = 0; + +- lockdep_assert_held(&net->nft.commit_mutex); ++ lockdep_assert_held(&nft_net->commit_mutex); + + table = nft_table_lookup(net, nla[NFTA_CHAIN_TABLE], family, genmask); + if (IS_ERR(table)) { +@@ -2848,6 +2887,7 @@ nla_put_failure: + static void nf_tables_rule_notify(const struct nft_ctx *ctx, + const struct nft_rule *rule, int event) + { ++ struct nftables_pernet *nft_net = net_generic(ctx->net, nf_tables_net_id); + struct sk_buff *skb; + int err; + +@@ -2867,7 +2907,7 @@ static void nf_tables_rule_notify(const + goto err; + } + +- nft_notify_enqueue(skb, ctx->report, &ctx->net->nft.notify_list); ++ nft_notify_enqueue(skb, ctx->report, &nft_net->notify_list); + return; + err: + nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS); +@@ -2925,11 +2965,13 @@ static int nf_tables_dump_rules(struct s + unsigned int idx = 0; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; ++ struct nftables_pernet *nft_net; + + rcu_read_lock(); +- cb->seq = net->nft.base_seq; ++ nft_net = net_generic(net, nf_tables_net_id); ++ cb->seq = nft_net->base_seq; + +- list_for_each_entry_rcu(table, &net->nft.tables, list) { ++ list_for_each_entry_rcu(table, &nft_net->tables, list) { + if (family != NFPROTO_UNSPEC && family != table->family) + continue; + +@@ -3161,6 +3203,7 @@ static int nf_tables_newrule(struct net + const struct nlattr * const nla[], + struct netlink_ext_ack *extack) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + u8 genmask = nft_genmask_next(net); + struct nft_expr_info *info = NULL; +@@ -3178,7 +3221,7 @@ static int nf_tables_newrule(struct net + int err, rem; + u64 handle, pos_handle; + +- lockdep_assert_held(&net->nft.commit_mutex); ++ lockdep_assert_held(&nft_net->commit_mutex); + + table = nft_table_lookup(net, nla[NFTA_RULE_TABLE], family, genmask); + if (IS_ERR(table)) { +@@ -3351,7 +3394,7 @@ static int nf_tables_newrule(struct net + kvfree(info); + chain->use++; + +- if (net->nft.validate_state == NFT_VALIDATE_DO) ++ if (nft_net->validate_state == NFT_VALIDATE_DO) + return nft_table_validate(net, table); + + if (chain->flags & NFT_CHAIN_HW_OFFLOAD) { +@@ -3381,10 +3424,11 @@ static struct nft_rule *nft_rule_lookup_ + const struct nft_chain *chain, + const struct nlattr *nla) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + u32 id = ntohl(nla_get_be32(nla)); + struct nft_trans *trans; + +- list_for_each_entry(trans, &net->nft.commit_list, list) { ++ list_for_each_entry(trans, &nft_net->commit_list, list) { + struct nft_rule *rule = nft_trans_rule(trans); + + if (trans->msg_type == NFT_MSG_NEWRULE && +@@ -3497,13 +3541,14 @@ nft_select_set_ops(const struct nft_ctx + const struct nft_set_desc *desc, + enum nft_set_policies policy) + { ++ struct nftables_pernet *nft_net = net_generic(ctx->net, nf_tables_net_id); + const struct nft_set_ops *ops, *bops; + struct nft_set_estimate est, best; + const struct nft_set_type *type; + u32 flags = 0; + int i; + +- lockdep_assert_held(&ctx->net->nft.commit_mutex); ++ lockdep_assert_held(&nft_net->commit_mutex); + lockdep_nfnl_nft_mutex_not_held(); + + if (nla[NFTA_SET_FLAGS] != NULL) +@@ -3641,10 +3686,11 @@ static struct nft_set *nft_set_lookup_by + const struct nft_table *table, + const struct nlattr *nla, u8 genmask) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_trans *trans; + u32 id = ntohl(nla_get_be32(nla)); + +- list_for_each_entry(trans, &net->nft.commit_list, list) { ++ list_for_each_entry(trans, &nft_net->commit_list, list) { + if (trans->msg_type == NFT_MSG_NEWSET) { + struct nft_set *set = nft_trans_set(trans); + +@@ -3867,6 +3913,7 @@ static void nf_tables_set_notify(const s + const struct nft_set *set, int event, + gfp_t gfp_flags) + { ++ struct nftables_pernet *nft_net = net_generic(ctx->net, nf_tables_net_id); + struct sk_buff *skb; + u32 portid = ctx->portid; + int err; +@@ -3885,7 +3932,7 @@ static void nf_tables_set_notify(const s + goto err; + } + +- nft_notify_enqueue(skb, ctx->report, &ctx->net->nft.notify_list); ++ nft_notify_enqueue(skb, ctx->report, &nft_net->notify_list); + return; + err: + nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, -ENOBUFS); +@@ -3898,14 +3945,16 @@ static int nf_tables_dump_sets(struct sk + struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2]; + struct net *net = sock_net(skb->sk); + struct nft_ctx *ctx = cb->data, ctx_set; ++ struct nftables_pernet *nft_net; + + if (cb->args[1]) + return skb->len; + + rcu_read_lock(); +- cb->seq = net->nft.base_seq; ++ nft_net = net_generic(net, nf_tables_net_id); ++ cb->seq = nft_net->base_seq; + +- list_for_each_entry_rcu(table, &net->nft.tables, list) { ++ list_for_each_entry_rcu(table, &nft_net->tables, list) { + if (ctx->family != NFPROTO_UNSPEC && + ctx->family != table->family) + continue; +@@ -4706,6 +4755,7 @@ static int nf_tables_dump_set(struct sk_ + { + struct nft_set_dump_ctx *dump_ctx = cb->data; + struct net *net = sock_net(skb->sk); ++ struct nftables_pernet *nft_net; + struct nft_table *table; + struct nft_set *set; + struct nft_set_dump_args args; +@@ -4716,7 +4766,8 @@ static int nf_tables_dump_set(struct sk_ + int event; + + rcu_read_lock(); +- list_for_each_entry_rcu(table, &net->nft.tables, list) { ++ nft_net = net_generic(net, nf_tables_net_id); ++ list_for_each_entry_rcu(table, &nft_net->tables, list) { + if (dump_ctx->ctx.family != NFPROTO_UNSPEC && + dump_ctx->ctx.family != table->family) + continue; +@@ -4995,6 +5046,7 @@ static void nf_tables_setelem_notify(con + const struct nft_set_elem *elem, + int event, u16 flags) + { ++ struct nftables_pernet *nft_net; + struct net *net = ctx->net; + u32 portid = ctx->portid; + struct sk_buff *skb; +@@ -5014,7 +5066,8 @@ static void nf_tables_setelem_notify(con + goto err; + } + +- nft_notify_enqueue(skb, ctx->report, &ctx->net->nft.notify_list); ++ nft_net = net_generic(net, nf_tables_net_id); ++ nft_notify_enqueue(skb, ctx->report, &nft_net->notify_list); + return; + err: + nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, -ENOBUFS); +@@ -5410,7 +5463,7 @@ static int nft_add_set_elem(struct nft_c + } + + nft_trans_elem(trans) = elem; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + return 0; + + err_set_full: +@@ -5441,6 +5494,7 @@ static int nf_tables_newsetelem(struct n + const struct nlattr * const nla[], + struct netlink_ext_ack *extack) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + u8 genmask = nft_genmask_next(net); + const struct nlattr *attr; + struct nft_set *set; +@@ -5470,7 +5524,7 @@ static int nf_tables_newsetelem(struct n + return err; + } + +- if (net->nft.validate_state == NFT_VALIDATE_DO) ++ if (nft_net->validate_state == NFT_VALIDATE_DO) + return nft_table_validate(net, ctx.table); + + return 0; +@@ -5606,7 +5660,7 @@ static int nft_del_setelem(struct nft_ct + nft_set_elem_deactivate(ctx->net, set, &elem); + + nft_trans_elem(trans) = elem; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + return 0; + + fail_ops: +@@ -5640,7 +5694,7 @@ static int nft_flush_set(const struct nf + nft_set_elem_deactivate(ctx->net, set, elem); + nft_trans_elem_set(trans) = set; + nft_trans_elem(trans) = *elem; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + + return 0; + err1: +@@ -5939,7 +5993,7 @@ static int nf_tables_updobj(const struct + nft_trans_obj(trans) = obj; + nft_trans_obj_update(trans) = true; + nft_trans_obj_newobj(trans) = newobj; +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + + return 0; + +@@ -6102,6 +6156,7 @@ static int nf_tables_dump_obj(struct sk_ + struct nft_obj_filter *filter = cb->data; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; ++ struct nftables_pernet *nft_net; + struct nft_object *obj; + bool reset = false; + +@@ -6109,9 +6164,10 @@ static int nf_tables_dump_obj(struct sk_ + reset = true; + + rcu_read_lock(); +- cb->seq = net->nft.base_seq; ++ nft_net = net_generic(net, nf_tables_net_id); ++ cb->seq = nft_net->base_seq; + +- list_for_each_entry_rcu(table, &net->nft.tables, list) { ++ list_for_each_entry_rcu(table, &nft_net->tables, list) { + if (family != NFPROTO_UNSPEC && family != table->family) + continue; + +@@ -6134,7 +6190,7 @@ static int nf_tables_dump_obj(struct sk_ + char *buf = kasprintf(GFP_ATOMIC, + "%s:%u", + table->name, +- net->nft.base_seq); ++ nft_net->base_seq); + + audit_log_nfcfg(buf, + family, +@@ -6255,8 +6311,11 @@ static int nf_tables_getobj(struct net * + reset = true; + + if (reset) { +- char *buf = kasprintf(GFP_ATOMIC, "%s:%u", +- table->name, net->nft.base_seq); ++ const struct nftables_pernet *nft_net; ++ char *buf; ++ ++ nft_net = net_generic(net, nf_tables_net_id); ++ buf = kasprintf(GFP_ATOMIC, "%s:%u", table->name, nft_net->base_seq); + + audit_log_nfcfg(buf, + family, +@@ -6341,10 +6400,11 @@ void nft_obj_notify(struct net *net, con + struct nft_object *obj, u32 portid, u32 seq, int event, + int family, int report, gfp_t gfp) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct sk_buff *skb; + int err; + char *buf = kasprintf(gfp, "%s:%u", +- table->name, net->nft.base_seq); ++ table->name, nft_net->base_seq); + + audit_log_nfcfg(buf, + family, +@@ -6370,7 +6430,7 @@ void nft_obj_notify(struct net *net, con + goto err; + } + +- nft_notify_enqueue(skb, report, &net->nft.notify_list); ++ nft_notify_enqueue(skb, report, &nft_net->notify_list); + return; + err: + nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, -ENOBUFS); +@@ -6706,7 +6766,7 @@ static int nft_flowtable_update(struct n + INIT_LIST_HEAD(&nft_trans_flowtable_hooks(trans)); + list_splice(&flowtable_hook.list, &nft_trans_flowtable_hooks(trans)); + +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + + return 0; + +@@ -6896,7 +6956,7 @@ static int nft_delflowtable_hook(struct + list_splice(&flowtable_del_list, &nft_trans_flowtable_hooks(trans)); + nft_flowtable_hook_release(&flowtable_hook); + +- list_add_tail(&trans->list, &ctx->net->nft.commit_list); ++ nft_trans_commit_list_add_tail(ctx->net, trans); + + return 0; + +@@ -7022,12 +7082,14 @@ static int nf_tables_dump_flowtable(stru + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; + struct nft_flowtable *flowtable; ++ struct nftables_pernet *nft_net; + const struct nft_table *table; + + rcu_read_lock(); +- cb->seq = net->nft.base_seq; ++ nft_net = net_generic(net, nf_tables_net_id); ++ cb->seq = nft_net->base_seq; + +- list_for_each_entry_rcu(table, &net->nft.tables, list) { ++ list_for_each_entry_rcu(table, &nft_net->tables, list) { + if (family != NFPROTO_UNSPEC && family != table->family) + continue; + +@@ -7162,6 +7224,7 @@ static void nf_tables_flowtable_notify(s + struct list_head *hook_list, + int event) + { ++ struct nftables_pernet *nft_net = net_generic(ctx->net, nf_tables_net_id); + struct sk_buff *skb; + int err; + +@@ -7181,7 +7244,7 @@ static void nf_tables_flowtable_notify(s + goto err; + } + +- nft_notify_enqueue(skb, ctx->report, &ctx->net->nft.notify_list); ++ nft_notify_enqueue(skb, ctx->report, &nft_net->notify_list); + return; + err: + nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS); +@@ -7206,6 +7269,7 @@ static void nf_tables_flowtable_destroy( + static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net, + u32 portid, u32 seq) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nlmsghdr *nlh; + char buf[TASK_COMM_LEN]; + int event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWGEN); +@@ -7215,7 +7279,7 @@ static int nf_tables_fill_gen_info(struc + if (!nlh) + goto nla_put_failure; + +- if (nla_put_be32(skb, NFTA_GEN_ID, htonl(net->nft.base_seq)) || ++ if (nla_put_be32(skb, NFTA_GEN_ID, htonl(nft_net->base_seq)) || + nla_put_be32(skb, NFTA_GEN_PROC_PID, htonl(task_pid_nr(current))) || + nla_put_string(skb, NFTA_GEN_PROC_NAME, get_task_comm(buf, current))) + goto nla_put_failure; +@@ -7250,6 +7314,7 @@ static int nf_tables_flowtable_event(str + { + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct nft_flowtable *flowtable; ++ struct nftables_pernet *nft_net; + struct nft_table *table; + struct net *net; + +@@ -7257,13 +7322,14 @@ static int nf_tables_flowtable_event(str + return 0; + + net = dev_net(dev); +- mutex_lock(&net->nft.commit_mutex); +- list_for_each_entry(table, &net->nft.tables, list) { ++ nft_net = net_generic(net, nf_tables_net_id); ++ mutex_lock(&nft_net->commit_mutex); ++ list_for_each_entry(table, &nft_net->tables, list) { + list_for_each_entry(flowtable, &table->flowtables, list) { + nft_flowtable_event(event, dev, flowtable); + } + } +- mutex_unlock(&net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + + return NOTIFY_DONE; + } +@@ -7444,16 +7510,17 @@ static const struct nfnl_callback nf_tab + + static int nf_tables_validate(struct net *net) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_table *table; + +- switch (net->nft.validate_state) { ++ switch (nft_net->validate_state) { + case NFT_VALIDATE_SKIP: + break; + case NFT_VALIDATE_NEED: + nft_validate_state_update(net, NFT_VALIDATE_DO); + fallthrough; + case NFT_VALIDATE_DO: +- list_for_each_entry(table, &net->nft.tables, list) { ++ list_for_each_entry(table, &nft_net->tables, list) { + if (nft_table_validate(net, table) < 0) + return -EAGAIN; + } +@@ -7630,9 +7697,10 @@ static int nf_tables_commit_chain_prepar + + static void nf_tables_commit_chain_prepare_cancel(struct net *net) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_trans *trans, *next; + +- list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { ++ list_for_each_entry_safe(trans, next, &nft_net->commit_list, list) { + struct nft_chain *chain = trans->ctx.chain; + + if (trans->msg_type == NFT_MSG_NEWRULE || +@@ -7730,10 +7798,11 @@ void nft_chain_del(struct nft_chain *cha + + static void nf_tables_module_autoload_cleanup(struct net *net) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_module_request *req, *next; + +- WARN_ON_ONCE(!list_empty(&net->nft.commit_list)); +- list_for_each_entry_safe(req, next, &net->nft.module_list, list) { ++ WARN_ON_ONCE(!list_empty(&nft_net->commit_list)); ++ list_for_each_entry_safe(req, next, &nft_net->module_list, list) { + WARN_ON_ONCE(!req->done); + list_del(&req->list); + kfree(req); +@@ -7742,6 +7811,7 @@ static void nf_tables_module_autoload_cl + + static void nf_tables_commit_release(struct net *net) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_trans *trans; + + /* all side effects have to be made visible. +@@ -7751,35 +7821,36 @@ static void nf_tables_commit_release(str + * Memory reclaim happens asynchronously from work queue + * to prevent expensive synchronize_rcu() in commit phase. + */ +- if (list_empty(&net->nft.commit_list)) { ++ if (list_empty(&nft_net->commit_list)) { + nf_tables_module_autoload_cleanup(net); +- mutex_unlock(&net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + return; + } + +- trans = list_last_entry(&net->nft.commit_list, ++ trans = list_last_entry(&nft_net->commit_list, + struct nft_trans, list); + get_net(trans->ctx.net); + WARN_ON_ONCE(trans->put_net); + + trans->put_net = true; + spin_lock(&nf_tables_destroy_list_lock); +- list_splice_tail_init(&net->nft.commit_list, &nf_tables_destroy_list); ++ list_splice_tail_init(&nft_net->commit_list, &nf_tables_destroy_list); + spin_unlock(&nf_tables_destroy_list_lock); + + nf_tables_module_autoload_cleanup(net); + schedule_work(&trans_destroy_work); + +- mutex_unlock(&net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + } + + static void nft_commit_notify(struct net *net, u32 portid) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct sk_buff *batch_skb = NULL, *nskb, *skb; + unsigned char *data; + int len; + +- list_for_each_entry_safe(skb, nskb, &net->nft.notify_list, list) { ++ list_for_each_entry_safe(skb, nskb, &nft_net->notify_list, list) { + if (!batch_skb) { + new_batch: + batch_skb = skb; +@@ -7805,7 +7876,7 @@ new_batch: + NFT_CB(batch_skb).report, GFP_KERNEL); + } + +- WARN_ON_ONCE(!list_empty(&net->nft.notify_list)); ++ WARN_ON_ONCE(!list_empty(&nft_net->notify_list)); + } + + static int nf_tables_commit_audit_alloc(struct list_head *adl, +@@ -7871,6 +7942,7 @@ static void nf_tables_commit_audit_log(s + + static int nf_tables_commit(struct net *net, struct sk_buff *skb) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_trans *trans, *next; + struct nft_trans_elem *te; + struct nft_chain *chain; +@@ -7878,8 +7950,8 @@ static int nf_tables_commit(struct net * + LIST_HEAD(adl); + int err; + +- if (list_empty(&net->nft.commit_list)) { +- mutex_unlock(&net->nft.commit_mutex); ++ if (list_empty(&nft_net->commit_list)) { ++ mutex_unlock(&nft_net->commit_mutex); + return 0; + } + +@@ -7892,7 +7964,7 @@ static int nf_tables_commit(struct net * + return err; + + /* 1. Allocate space for next generation rules_gen_X[] */ +- list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { ++ list_for_each_entry_safe(trans, next, &nft_net->commit_list, list) { + int ret; + + ret = nf_tables_commit_audit_alloc(&adl, trans->ctx.table); +@@ -7915,7 +7987,7 @@ static int nf_tables_commit(struct net * + } + + /* step 2. Make rules_gen_X visible to packet path */ +- list_for_each_entry(table, &net->nft.tables, list) { ++ list_for_each_entry(table, &nft_net->tables, list) { + list_for_each_entry(chain, &table->chains, list) + nf_tables_commit_chain(net, chain); + } +@@ -7924,12 +7996,13 @@ static int nf_tables_commit(struct net * + * Bump generation counter, invalidate any dump in progress. + * Cannot fail after this point. + */ +- while (++net->nft.base_seq == 0); ++ while (++nft_net->base_seq == 0) ++ ; + + /* step 3. Start new generation, rules_gen_X now in use. */ + net->nft.gencursor = nft_gencursor_next(net); + +- list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { ++ list_for_each_entry_safe(trans, next, &nft_net->commit_list, list) { + nf_tables_commit_audit_collect(&adl, trans->ctx.table, + trans->msg_type); + switch (trans->msg_type) { +@@ -8089,7 +8162,7 @@ static int nf_tables_commit(struct net * + + nft_commit_notify(net, NETLINK_CB(skb).portid); + nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN); +- nf_tables_commit_audit_log(&adl, net->nft.base_seq); ++ nf_tables_commit_audit_log(&adl, nft_net->base_seq); + nf_tables_commit_release(net); + + return 0; +@@ -8097,17 +8170,18 @@ static int nf_tables_commit(struct net * + + static void nf_tables_module_autoload(struct net *net) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_module_request *req, *next; + LIST_HEAD(module_list); + +- list_splice_init(&net->nft.module_list, &module_list); +- mutex_unlock(&net->nft.commit_mutex); ++ list_splice_init(&nft_net->module_list, &module_list); ++ mutex_unlock(&nft_net->commit_mutex); + list_for_each_entry_safe(req, next, &module_list, list) { + request_module("%s", req->module); + req->done = true; + } +- mutex_lock(&net->nft.commit_mutex); +- list_splice(&module_list, &net->nft.module_list); ++ mutex_lock(&nft_net->commit_mutex); ++ list_splice(&module_list, &nft_net->module_list); + } + + static void nf_tables_abort_release(struct nft_trans *trans) +@@ -8144,6 +8218,7 @@ static void nf_tables_abort_release(stru + + static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_trans *trans, *next; + struct nft_trans_elem *te; + +@@ -8151,7 +8226,7 @@ static int __nf_tables_abort(struct net + nf_tables_validate(net) < 0) + return -EAGAIN; + +- list_for_each_entry_safe_reverse(trans, next, &net->nft.commit_list, ++ list_for_each_entry_safe_reverse(trans, next, &nft_net->commit_list, + list) { + switch (trans->msg_type) { + case NFT_MSG_NEWTABLE: +@@ -8277,7 +8352,7 @@ static int __nf_tables_abort(struct net + synchronize_rcu(); + + list_for_each_entry_safe_reverse(trans, next, +- &net->nft.commit_list, list) { ++ &nft_net->commit_list, list) { + list_del(&trans->list); + nf_tables_abort_release(trans); + } +@@ -8293,22 +8368,24 @@ static int __nf_tables_abort(struct net + static int nf_tables_abort(struct net *net, struct sk_buff *skb, + enum nfnl_abort_action action) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + int ret = __nf_tables_abort(net, action); + +- mutex_unlock(&net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + + return ret; + } + + static bool nf_tables_valid_genid(struct net *net, u32 genid) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + bool genid_ok; + +- mutex_lock(&net->nft.commit_mutex); ++ mutex_lock(&nft_net->commit_mutex); + +- genid_ok = genid == 0 || net->nft.base_seq == genid; ++ genid_ok = genid == 0 || nft_net->base_seq == genid; + if (!genid_ok) +- mutex_unlock(&net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + + /* else, commit mutex has to be released by commit or abort function */ + return genid_ok; +@@ -8909,19 +8986,19 @@ EXPORT_SYMBOL_GPL(__nft_release_basechai + + 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, &net->nft.tables, list) { ++ list_for_each_entry(table, &nft_net->tables, list) { + list_for_each_entry(chain, &table->chains, list) + nf_tables_unregister_hook(net, table, chain); + } + } + +-static void __nft_release_tables(struct net *net) ++static void __nft_release_table(struct net *net, struct nft_table *table) + { + struct nft_flowtable *flowtable, *nf; +- struct nft_table *table, *nt; + struct nft_chain *chain, *nc; + struct nft_object *obj, *ne; + struct nft_rule *rule, *nr; +@@ -8931,79 +9008,94 @@ static void __nft_release_tables(struct + .family = NFPROTO_NETDEV, + }; + +- list_for_each_entry_safe(table, nt, &net->nft.tables, list) { +- ctx.family = table->family; +- ctx.table = table; +- list_for_each_entry(chain, &table->chains, list) { +- ctx.chain = chain; +- list_for_each_entry_safe(rule, nr, &chain->rules, list) { +- list_del(&rule->list); +- chain->use--; +- nf_tables_rule_release(&ctx, rule); +- } +- } +- list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) { +- list_del(&flowtable->list); +- table->use--; +- nf_tables_flowtable_destroy(flowtable); +- } +- list_for_each_entry_safe(set, ns, &table->sets, list) { +- list_del(&set->list); +- table->use--; +- nft_set_destroy(&ctx, set); +- } +- list_for_each_entry_safe(obj, ne, &table->objects, list) { +- nft_obj_del(obj); +- table->use--; +- nft_obj_destroy(&ctx, obj); +- } +- list_for_each_entry_safe(chain, nc, &table->chains, list) { +- ctx.chain = chain; +- nft_chain_del(chain); +- table->use--; +- nf_tables_chain_destroy(&ctx); ++ ctx.family = table->family; ++ ctx.table = table; ++ list_for_each_entry(chain, &table->chains, list) { ++ ctx.chain = chain; ++ list_for_each_entry_safe(rule, nr, &chain->rules, list) { ++ list_del(&rule->list); ++ chain->use--; ++ nf_tables_rule_release(&ctx, rule); + } +- list_del(&table->list); +- nf_tables_table_destroy(&ctx); + } ++ list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) { ++ list_del(&flowtable->list); ++ table->use--; ++ nf_tables_flowtable_destroy(flowtable); ++ } ++ list_for_each_entry_safe(set, ns, &table->sets, list) { ++ list_del(&set->list); ++ table->use--; ++ nft_set_destroy(&ctx, set); ++ } ++ list_for_each_entry_safe(obj, ne, &table->objects, list) { ++ nft_obj_del(obj); ++ table->use--; ++ nft_obj_destroy(&ctx, obj); ++ } ++ list_for_each_entry_safe(chain, nc, &table->chains, list) { ++ ctx.chain = chain; ++ nft_chain_del(chain); ++ table->use--; ++ nf_tables_chain_destroy(&ctx); ++ } ++ list_del(&table->list); ++ nf_tables_table_destroy(&ctx); ++} ++ ++static void __nft_release_tables(struct net *net) ++{ ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); ++ struct nft_table *table, *nt; ++ ++ list_for_each_entry_safe(table, nt, &nft_net->tables, list) ++ __nft_release_table(net, table); + } + + static int __net_init nf_tables_init_net(struct net *net) + { +- INIT_LIST_HEAD(&net->nft.tables); +- INIT_LIST_HEAD(&net->nft.commit_list); +- INIT_LIST_HEAD(&net->nft.module_list); +- INIT_LIST_HEAD(&net->nft.notify_list); +- mutex_init(&net->nft.commit_mutex); +- net->nft.base_seq = 1; +- net->nft.validate_state = NFT_VALIDATE_SKIP; ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); ++ ++ INIT_LIST_HEAD(&nft_net->tables); ++ INIT_LIST_HEAD(&nft_net->commit_list); ++ INIT_LIST_HEAD(&nft_net->module_list); ++ INIT_LIST_HEAD(&nft_net->notify_list); ++ mutex_init(&nft_net->commit_mutex); ++ nft_net->base_seq = 1; ++ nft_net->validate_state = NFT_VALIDATE_SKIP; + + return 0; + } + + static void __net_exit nf_tables_pre_exit_net(struct net *net) + { +- mutex_lock(&net->nft.commit_mutex); ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); ++ ++ mutex_lock(&nft_net->commit_mutex); + __nft_release_hooks(net); +- mutex_unlock(&net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + } + + static void __net_exit nf_tables_exit_net(struct net *net) + { +- mutex_lock(&net->nft.commit_mutex); +- if (!list_empty(&net->nft.commit_list)) ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); ++ ++ mutex_lock(&nft_net->commit_mutex); ++ if (!list_empty(&nft_net->commit_list)) + __nf_tables_abort(net, NFNL_ABORT_NONE); + __nft_release_tables(net); +- mutex_unlock(&net->nft.commit_mutex); +- WARN_ON_ONCE(!list_empty(&net->nft.tables)); +- WARN_ON_ONCE(!list_empty(&net->nft.module_list)); +- WARN_ON_ONCE(!list_empty(&net->nft.notify_list)); ++ mutex_unlock(&nft_net->commit_mutex); ++ WARN_ON_ONCE(!list_empty(&nft_net->tables)); ++ WARN_ON_ONCE(!list_empty(&nft_net->module_list)); ++ WARN_ON_ONCE(!list_empty(&nft_net->notify_list)); + } + + static struct pernet_operations nf_tables_net_ops = { + .init = nf_tables_init_net, + .pre_exit = nf_tables_pre_exit_net, + .exit = nf_tables_exit_net, ++ .id = &nf_tables_net_id, ++ .size = sizeof(struct nftables_pernet), + }; + + static int __init nf_tables_module_init(void) +--- a/net/netfilter/nf_tables_offload.c ++++ b/net/netfilter/nf_tables_offload.c +@@ -7,6 +7,8 @@ + #include + #include + ++extern unsigned int nf_tables_net_id; ++ + static struct nft_flow_rule *nft_flow_rule_alloc(int num_actions) + { + struct nft_flow_rule *flow; +@@ -371,16 +373,18 @@ static void nft_indr_block_cleanup(struc + struct nft_base_chain *basechain = block_cb->indr.data; + struct net_device *dev = block_cb->indr.dev; + struct netlink_ext_ack extack = {}; ++ struct nftables_pernet *nft_net; + struct net *net = dev_net(dev); + struct flow_block_offload bo; + + nft_flow_block_offload_init(&bo, dev_net(dev), FLOW_BLOCK_UNBIND, + basechain, &extack); +- mutex_lock(&net->nft.commit_mutex); ++ nft_net = net_generic(net, nf_tables_net_id); ++ mutex_lock(&nft_net->commit_mutex); + list_del(&block_cb->driver_list); + list_move(&block_cb->list, &bo.cb_list); + nft_flow_offload_unbind(&bo, basechain); +- mutex_unlock(&net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + } + + static int nft_indr_block_offload_cmd(struct nft_base_chain *basechain, +@@ -476,9 +480,10 @@ static int nft_flow_offload_chain(struct + static void nft_flow_rule_offload_abort(struct net *net, + struct nft_trans *trans) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + int err = 0; + +- list_for_each_entry_continue_reverse(trans, &net->nft.commit_list, list) { ++ list_for_each_entry_continue_reverse(trans, &nft_net->commit_list, list) { + if (trans->ctx.family != NFPROTO_NETDEV) + continue; + +@@ -524,11 +529,12 @@ static void nft_flow_rule_offload_abort( + + int nft_flow_rule_offload_commit(struct net *net) + { ++ struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); + struct nft_trans *trans; + int err = 0; + u8 policy; + +- list_for_each_entry(trans, &net->nft.commit_list, list) { ++ list_for_each_entry(trans, &nft_net->commit_list, list) { + if (trans->ctx.family != NFPROTO_NETDEV) + continue; + +@@ -580,7 +586,7 @@ int nft_flow_rule_offload_commit(struct + } + } + +- list_for_each_entry(trans, &net->nft.commit_list, list) { ++ list_for_each_entry(trans, &nft_net->commit_list, list) { + if (trans->ctx.family != NFPROTO_NETDEV) + continue; + +@@ -600,15 +606,15 @@ int nft_flow_rule_offload_commit(struct + return err; + } + +-static struct nft_chain *__nft_offload_get_chain(struct net_device *dev) ++static struct nft_chain *__nft_offload_get_chain(const struct nftables_pernet *nft_net, ++ struct net_device *dev) + { + struct nft_base_chain *basechain; +- struct net *net = dev_net(dev); + struct nft_hook *hook, *found; + const struct nft_table *table; + struct nft_chain *chain; + +- list_for_each_entry(table, &net->nft.tables, list) { ++ list_for_each_entry(table, &nft_net->tables, list) { + if (table->family != NFPROTO_NETDEV) + continue; + +@@ -640,19 +646,21 @@ static int nft_offload_netdev_event(stru + unsigned long event, void *ptr) + { + struct net_device *dev = netdev_notifier_info_to_dev(ptr); ++ struct nftables_pernet *nft_net; + struct net *net = dev_net(dev); + struct nft_chain *chain; + + if (event != NETDEV_UNREGISTER) + return NOTIFY_DONE; + +- mutex_lock(&net->nft.commit_mutex); +- chain = __nft_offload_get_chain(dev); ++ nft_net = net_generic(net, nf_tables_net_id); ++ mutex_lock(&nft_net->commit_mutex); ++ chain = __nft_offload_get_chain(nft_net, dev); + if (chain) + nft_flow_block_chain(nft_base_chain(chain), dev, + FLOW_BLOCK_UNBIND); + +- mutex_unlock(&net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + + return NOTIFY_DONE; + } +--- a/net/netfilter/nft_chain_filter.c ++++ b/net/netfilter/nft_chain_filter.c +@@ -2,6 +2,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -10,6 +11,8 @@ + #include + #include + ++extern unsigned int nf_tables_net_id; ++ + #ifdef CONFIG_NF_TABLES_IPV4 + static unsigned int nft_do_chain_ipv4(void *priv, + struct sk_buff *skb, +@@ -355,6 +358,7 @@ static int nf_tables_netdev_event(struct + unsigned long event, void *ptr) + { + struct net_device *dev = netdev_notifier_info_to_dev(ptr); ++ struct nftables_pernet *nft_net; + struct nft_table *table; + struct nft_chain *chain, *nr; + struct nft_ctx ctx = { +@@ -365,8 +369,9 @@ static int nf_tables_netdev_event(struct + event != NETDEV_CHANGENAME) + return NOTIFY_DONE; + +- mutex_lock(&ctx.net->nft.commit_mutex); +- list_for_each_entry(table, &ctx.net->nft.tables, list) { ++ nft_net = net_generic(ctx.net, nf_tables_net_id); ++ mutex_lock(&nft_net->commit_mutex); ++ list_for_each_entry(table, &nft_net->tables, list) { + if (table->family != NFPROTO_NETDEV) + continue; + +@@ -380,7 +385,7 @@ static int nf_tables_netdev_event(struct + nft_netdev_event(event, dev, &ctx); + } + } +- mutex_unlock(&ctx.net->nft.commit_mutex); ++ mutex_unlock(&nft_net->commit_mutex); + + return NOTIFY_DONE; + } +--- a/net/netfilter/nft_dynset.c ++++ b/net/netfilter/nft_dynset.c +@@ -11,6 +11,9 @@ + #include + #include + #include ++#include ++ ++extern unsigned int nf_tables_net_id; + + struct nft_dynset { + struct nft_set *set; +@@ -106,13 +109,14 @@ static int nft_dynset_init(const struct + const struct nft_expr *expr, + const struct nlattr * const tb[]) + { ++ struct nftables_pernet *nft_net = net_generic(ctx->net, nf_tables_net_id); + struct nft_dynset *priv = nft_expr_priv(expr); + u8 genmask = nft_genmask_next(ctx->net); + struct nft_set *set; + u64 timeout; + int err; + +- lockdep_assert_held(&ctx->net->nft.commit_mutex); ++ lockdep_assert_held(&nft_net->commit_mutex); + + if (tb[NFTA_DYNSET_SET_NAME] == NULL || + tb[NFTA_DYNSET_OP] == NULL || diff --git a/queue-5.10/netfilter-nftables-rename-set-element-data-activation-deactivation-functions.patch b/queue-5.10/netfilter-nftables-rename-set-element-data-activation-deactivation-functions.patch new file mode 100644 index 00000000000..8c9da8e66f2 --- /dev/null +++ b/queue-5.10/netfilter-nftables-rename-set-element-data-activation-deactivation-functions.patch @@ -0,0 +1,92 @@ +From stable-owner@vger.kernel.org Thu Jul 13 10:49:53 2023 +From: Pablo Neira Ayuso +Date: Thu, 13 Jul 2023 10:48:56 +0200 +Subject: netfilter: nftables: rename set element data activation/deactivation functions +To: netfilter-devel@vger.kernel.org +Cc: gregkh@linuxfoundation.org, stable@vger.kernel.org, sashal@kernel.org +Message-ID: <20230713084859.71541-9-pablo@netfilter.org> + +From: Pablo Neira Ayuso + +[ Upstream commit f8bb7889af58d8e74d2d61c76b1418230f1610fa ] + +Rename: + +- nft_set_elem_activate() to nft_set_elem_data_activate(). +- nft_set_elem_deactivate() to nft_set_elem_data_deactivate(). + +To prepare for updates in the set element infrastructure to add support +for the special catch-all element. + +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + net/netfilter/nf_tables_api.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -5270,8 +5270,8 @@ void nft_set_elem_destroy(const struct n + } + EXPORT_SYMBOL_GPL(nft_set_elem_destroy); + +-/* Only called from commit path, nft_set_elem_deactivate() already deals with +- * the refcounting from the preparation phase. ++/* Only called from commit path, nft_setelem_data_deactivate() already deals ++ * with the refcounting from the preparation phase. + */ + static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx, + const struct nft_set *set, void *elem) +@@ -5649,9 +5649,9 @@ void nft_data_hold(const struct nft_data + } + } + +-static void nft_set_elem_activate(const struct net *net, +- const struct nft_set *set, +- struct nft_set_elem *elem) ++static void nft_setelem_data_activate(const struct net *net, ++ const struct nft_set *set, ++ struct nft_set_elem *elem) + { + const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv); + +@@ -5661,9 +5661,9 @@ static void nft_set_elem_activate(const + (*nft_set_ext_obj(ext))->use++; + } + +-static void nft_set_elem_deactivate(const struct net *net, +- const struct nft_set *set, +- struct nft_set_elem *elem) ++static void nft_setelem_data_deactivate(const struct net *net, ++ const struct nft_set *set, ++ struct nft_set_elem *elem) + { + const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv); + +@@ -5740,7 +5740,7 @@ static int nft_del_setelem(struct nft_ct + kfree(elem.priv); + elem.priv = priv; + +- nft_set_elem_deactivate(ctx->net, set, &elem); ++ nft_setelem_data_deactivate(ctx->net, set, &elem); + + nft_trans_elem(trans) = elem; + nft_trans_commit_list_add_tail(ctx->net, trans); +@@ -5774,7 +5774,7 @@ static int nft_flush_set(const struct nf + } + set->ndeact++; + +- nft_set_elem_deactivate(ctx->net, set, elem); ++ nft_setelem_data_deactivate(ctx->net, set, elem); + nft_trans_elem_set(trans) = set; + nft_trans_elem(trans) = *elem; + nft_trans_commit_list_add_tail(ctx->net, trans); +@@ -8413,7 +8413,7 @@ static int __nf_tables_abort(struct net + case NFT_MSG_DELSETELEM: + te = (struct nft_trans_elem *)trans->data; + +- nft_set_elem_activate(net, te->set, &te->elem); ++ nft_setelem_data_activate(net, te->set, &te->elem); + te->set->ops->activate(net, te->set, &te->elem); + te->set->ndeact--; + diff --git a/queue-5.10/series b/queue-5.10/series index 3aedddf3611..bd6df43263a 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -313,3 +313,14 @@ block-add-overflow-checks-for-amiga-partition-support.patch mips-dts-ci20-raise-vddcore-voltage-to-1.125-volts.patch io_uring-use-io_schedule-in-cqring-wait.patch sh-pgtable-3level-fix-cast-to-pointer-from-integer-of-different-size.patch +netfilter-nf_tables-use-net_generic-infra-for-transaction-data.patch +netfilter-nf_tables-add-rescheduling-points-during-loop-detection-walks.patch +netfilter-nf_tables-incorrect-error-path-handling-with-nft_msg_newrule.patch +netfilter-nf_tables-fix-chain-binding-transaction-logic.patch +netfilter-nf_tables-add-nft_trans_prepare_error-to-deal-with-bound-set-chain.patch +netfilter-nf_tables-reject-unbound-anonymous-set-before-commit-phase.patch +netfilter-nf_tables-reject-unbound-chain-set-before-commit-phase.patch +netfilter-nftables-rename-set-element-data-activation-deactivation-functions.patch +netfilter-nf_tables-drop-map-element-references-from-preparation-phase.patch +netfilter-nf_tables-unbind-non-anonymous-set-if-rule-construction-fails.patch +netfilter-nf_tables-fix-scheduling-while-atomic-splat.patch