From: Pablo Neira Ayuso <pablo@netfilter.org> mainline inclusion from mainline-v7.2-rc6 commit f4f699790590bd0896c48a71e9232a65198f92f0 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/17332 CVE: CVE-2026-74565 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- The nft_object rhltable is global, this allows for accessing objects that are being dismangled from lookup path by other existing netns. Given the nft_obj_destroy() releases the object inmediately, this might lead to use-after-free of these objects that are being released. Make the existing rhltable per table to address this issue to deal with with the nft_rcv_nl_event() path too. Update nft_obj_lookup() to take the table as non-const, otherwise, compiler complains when passing the objname_ht to rhltable_lookup(). Fixes: 4d44175aa5bb ("netfilter: nf_tables: handle nft_object lookups via rhltable") Suggested-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Conflicts: include/net/netfilter/nf_tables.h net/netfilter/nf_tables_api.c [commit f4f699790590bd0896c48a71e9232a65198f92f0 ("netfilter: nf_tables: make nft_object rhltable per table") is not backport, which lead to conflicts] [Structural conflict, no upstream prerequisite found in Fixes..CVE range: all flagged deviations are 5.10 branch code shape, not missing logic. classification: Class B forced-minimal adaptation; target control flow preserved; only CVE-related per-object-hashtable changes applied.] [Deviations: (1) 5.10 nf_tables_table_destroy() takes struct nft_ctx *ctx instead of struct nft_table *table, so objname_ht is destroyed via ctx->table->objname_ht, equivalent to upstream table->objname_ht; (2) 5.10 commit loop has no loop-local table variable (upstream has table = trans->table), so nft_obj_del() is called with trans->ctx.table, which is the same table the object was inserted into; (3) 5.10 merged nf_tables_getobj() (upstream split into nf_tables_getobj_single), the const removal on table applies there; (4) 5.10 module_init uses positional err1..err6 labels (upstream uses named labels); removing the global rhltable_init block requires renumbering so that nft_offload_init failure unwinds through err4 (unregister_netdevice_notifier) and nfnetlink_subsys_register failure through err5 (nft_offload_exit), preserving the exact upstream cleanup order and removing only the global rhltable_destroy for the object-name hashtable.] [Refcount/lifetime safety: object name-hashtable insertion and removal are paired at the table level (rhltable_insert in nf_tables_newobj vs rhltable_remove in nft_obj_del); the per-table hashtable is initialized in nf_tables_newtable and torn down in nf_tables_table_destroy / err_trans path; no dev_hold/refcount/lock operations were added, removed, or reordered relative to upstream; the CVE fix (isolating object lookup from concurrently-released objects across netns) is preserved verbatim.] Signed-off-by: Dong Chenchen <dongchenchen2@huawei.com> --- include/net/netfilter/nf_tables.h | 4 +++- net/netfilter/nf_tables_api.c | 40 +++++++++++++++---------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 9168a6a8934c..fb9de90d00e7 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -1103,6 +1103,7 @@ static inline void nft_use_inc_restore(u32 *use) * @sets: sets in the table * @objects: stateful objects in the table * @flowtables: flow tables in the table + * @objname_ht: hashtable for objects lookup by name * @hgenerator: handle generator state * @handle: table handle * @use: number of chain references to this table @@ -1118,6 +1119,7 @@ struct nft_table { struct list_head sets; struct list_head objects; struct list_head flowtables; + struct rhltable objname_ht; u64 hgenerator; u64 handle; u32 use; @@ -1190,7 +1192,7 @@ static inline void *nft_obj_data(const struct nft_object *obj) #define nft_expr_obj(expr) *((struct nft_object **)nft_expr_priv(expr)) struct nft_object *nft_obj_lookup(const struct net *net, - const struct nft_table *table, + struct nft_table *table, const struct nlattr *nla, u32 objtype, u8 genmask); diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 2beef989cced..fd498aecdd13 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -44,8 +44,6 @@ enum { NFT_VALIDATE_DO, }; -static struct rhltable nft_objname_ht; - static u32 nft_chain_hash(const void *data, u32 len, u32 seed); static u32 nft_chain_hash_obj(const void *data, u32 len, u32 seed); static int nft_chain_hash_cmp(struct rhashtable_compare_arg *, const void *); @@ -1300,6 +1298,10 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk, if (err) goto err_chain_ht; + err = rhltable_init(&table->objname_ht, &nft_objname_ht_params); + if (err < 0) + goto err_obj_ht; + INIT_LIST_HEAD(&table->chains); INIT_LIST_HEAD(&table->sets); INIT_LIST_HEAD(&table->objects); @@ -1316,6 +1318,8 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk, list_add_tail_rcu(&table->list, &nft_net->tables); return 0; err_trans: + rhltable_destroy(&table->objname_ht); +err_obj_ht: rhltable_destroy(&table->chains_ht); err_chain_ht: kfree(table->udata); @@ -1474,6 +1478,7 @@ static void nf_tables_table_destroy(struct nft_ctx *ctx) return; rhltable_destroy(&ctx->table->chains_ht); + rhltable_destroy(&ctx->table->objname_ht); kfree(ctx->table->name); kfree(ctx->table->udata); kfree(ctx->table); @@ -6150,7 +6155,7 @@ void nft_unregister_obj(struct nft_object_type *obj_type) EXPORT_SYMBOL_GPL(nft_unregister_obj); struct nft_object *nft_obj_lookup(const struct net *net, - const struct nft_table *table, + struct nft_table *table, const struct nlattr *nla, u32 objtype, u8 genmask) { @@ -6166,7 +6171,7 @@ struct nft_object *nft_obj_lookup(const struct net *net, !lockdep_commit_lock_is_held(net)); rcu_read_lock(); - list = rhltable_lookup(&nft_objname_ht, &k, nft_objname_ht_params); + list = rhltable_lookup(&table->objname_ht, &k, nft_objname_ht_params); if (!list) goto out; @@ -6436,7 +6441,7 @@ static int nf_tables_newobj(struct net *net, struct sock *nlsk, if (err < 0) goto err_trans; - err = rhltable_insert(&nft_objname_ht, &obj->rhlhead, + err = rhltable_insert(&table->objname_ht, &obj->rhlhead, nft_objname_ht_params); if (err < 0) goto err_obj_ht; @@ -6622,7 +6627,7 @@ static int nf_tables_getobj(struct net *net, struct sock *nlsk, const struct nfgenmsg *nfmsg = nlmsg_data(nlh); u8 genmask = nft_genmask_cur(net); int family = nfmsg->nfgen_family; - const struct nft_table *table; + struct nft_table *table; struct nft_object *obj; struct sk_buff *skb2; bool reset = false; @@ -8159,9 +8164,9 @@ static void nf_tables_commit_chain(struct net *net, struct nft_chain *chain) nf_tables_commit_chain_free_rules_old(g0); } -static void nft_obj_del(struct nft_object *obj) +static void nft_obj_del(struct nft_table *table, struct nft_object *obj) { - rhltable_remove(&nft_objname_ht, &obj->rhlhead, nft_objname_ht_params); + rhltable_remove(&table->objname_ht, &obj->rhlhead, nft_objname_ht_params); list_del_rcu(&obj->list); } @@ -8741,7 +8746,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) } break; case NFT_MSG_DELOBJ: - nft_obj_del(nft_trans_obj(trans)); + nft_obj_del(trans->ctx.table, nft_trans_obj(trans)); nf_tables_obj_notify(&trans->ctx, nft_trans_obj(trans), NFT_MSG_DELOBJ); break; @@ -8988,7 +8993,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) nft_trans_destroy(trans); } else { nft_use_dec_restore(&trans->ctx.table->use); - nft_obj_del(nft_trans_obj(trans)); + nft_obj_del(trans->ctx.table, nft_trans_obj(trans)); } break; case NFT_MSG_DELOBJ: @@ -9633,7 +9638,7 @@ static void __nft_release_table(struct net *net, struct nft_table *table) nft_set_destroy(&ctx, set); } list_for_each_entry_safe(obj, ne, &table->objects, list) { - nft_obj_del(obj); + nft_obj_del(table, obj); nft_use_dec(&table->use); nft_obj_destroy(&ctx, obj); } @@ -9734,26 +9739,20 @@ static int __init nf_tables_module_init(void) if (err < 0) goto err3; - err = rhltable_init(&nft_objname_ht, &nft_objname_ht_params); - if (err < 0) - goto err4; - err = nft_offload_init(); if (err < 0) - goto err5; + goto err4; /* must be last */ err = nfnetlink_subsys_register(&nf_tables_subsys); if (err < 0) - goto err6; + goto err5; nft_chain_route_init(); return err; -err6: - nft_offload_exit(); err5: - rhltable_destroy(&nft_objname_ht); + nft_offload_exit(); err4: unregister_netdevice_notifier(&nf_tables_flowtable_notifier); err3: @@ -9777,7 +9776,6 @@ static void __exit nf_tables_module_exit(void) cancel_work_sync(&trans_gc_work); cancel_work_sync(&trans_destroy_work); rcu_barrier(); - rhltable_destroy(&nft_objname_ht); nf_tables_core_module_exit(); } -- 2.43.0