--- /dev/null
+From 1689f25924ada8fe14a4a82c38925d04994c7142 Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 28 Jun 2023 16:24:27 +0200
+Subject: netfilter: nf_tables: report use refcount overflow
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+commit 1689f25924ada8fe14a4a82c38925d04994c7142 upstream.
+
+Overflow use refcount checks are not complete.
+
+Add helper function to deal with object reference counter tracking.
+Report -EMFILE in case UINT_MAX is reached.
+
+nft_use_dec() splats in case that reference counter underflows,
+which should not ever happen.
+
+Add nft_use_inc_restore() and nft_use_dec_restore() which are used
+to restore reference counter from error and abort paths.
+
+Use u32 in nft_flowtable and nft_object since helper functions cannot
+work on bitfields.
+
+Remove the few early incomplete checks now that the helper functions
+are in place and used to check for refcount overflow.
+
+Fixes: 96518518cc41 ("netfilter: add nftables")
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/netfilter/nf_tables.h | 31 ++++++-
+ net/netfilter/nf_tables_api.c | 163 +++++++++++++++++++++++---------------
+ net/netfilter/nft_flow_offload.c | 6 -
+ net/netfilter/nft_immediate.c | 8 -
+ net/netfilter/nft_objref.c | 8 +
+ 5 files changed, 141 insertions(+), 75 deletions(-)
+
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -1192,6 +1192,29 @@ int __nft_release_basechain(struct nft_c
+
+ unsigned int nft_do_chain(struct nft_pktinfo *pkt, void *priv);
+
++static inline bool nft_use_inc(u32 *use)
++{
++ if (*use == UINT_MAX)
++ return false;
++
++ (*use)++;
++
++ return true;
++}
++
++static inline void nft_use_dec(u32 *use)
++{
++ WARN_ON_ONCE((*use)-- == 0);
++}
++
++/* For error and abort path: restore use counter to previous state. */
++static inline void nft_use_inc_restore(u32 *use)
++{
++ WARN_ON_ONCE(!nft_use_inc(use));
++}
++
++#define nft_use_dec_restore nft_use_dec
++
+ /**
+ * struct nft_table - nf_tables table
+ *
+@@ -1275,8 +1298,8 @@ struct nft_object {
+ struct list_head list;
+ struct rhlist_head rhlhead;
+ struct nft_object_hash_key key;
+- u32 genmask:2,
+- use:30;
++ u32 genmask:2;
++ u32 use;
+ u64 handle;
+ u16 udlen;
+ u8 *udata;
+@@ -1378,8 +1401,8 @@ struct nft_flowtable {
+ char *name;
+ int hooknum;
+ int ops_len;
+- u32 genmask:2,
+- use:30;
++ u32 genmask:2;
++ u32 use;
+ u64 handle;
+ /* runtime data below here */
+ struct list_head hook_list ____cacheline_aligned;
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -255,8 +255,10 @@ int nf_tables_bind_chain(const struct nf
+ if (chain->bound)
+ return -EBUSY;
+
++ if (!nft_use_inc(&chain->use))
++ return -EMFILE;
++
+ chain->bound = true;
+- chain->use++;
+ nft_chain_trans_bind(ctx, chain);
+
+ return 0;
+@@ -439,7 +441,7 @@ static int nft_delchain(struct nft_ctx *
+ if (IS_ERR(trans))
+ return PTR_ERR(trans);
+
+- ctx->table->use--;
++ nft_use_dec(&ctx->table->use);
+ nft_deactivate_next(ctx->net, ctx->chain);
+
+ return 0;
+@@ -478,7 +480,7 @@ nf_tables_delrule_deactivate(struct nft_
+ /* You cannot delete the same rule twice */
+ if (nft_is_active_next(ctx->net, rule)) {
+ nft_deactivate_next(ctx->net, rule);
+- ctx->chain->use--;
++ nft_use_dec(&ctx->chain->use);
+ return 0;
+ }
+ return -ENOENT;
+@@ -645,7 +647,7 @@ static int nft_delset(const struct nft_c
+ nft_map_deactivate(ctx, set);
+
+ nft_deactivate_next(ctx->net, set);
+- ctx->table->use--;
++ nft_use_dec(&ctx->table->use);
+
+ return err;
+ }
+@@ -677,7 +679,7 @@ static int nft_delobj(struct nft_ctx *ct
+ return err;
+
+ nft_deactivate_next(ctx->net, obj);
+- ctx->table->use--;
++ nft_use_dec(&ctx->table->use);
+
+ return err;
+ }
+@@ -712,7 +714,7 @@ static int nft_delflowtable(struct nft_c
+ return err;
+
+ nft_deactivate_next(ctx->net, flowtable);
+- ctx->table->use--;
++ nft_use_dec(&ctx->table->use);
+
+ return err;
+ }
+@@ -2358,9 +2360,6 @@ static int nf_tables_addchain(struct nft
+ unsigned int data_size;
+ int err;
+
+- if (table->use == UINT_MAX)
+- return -EOVERFLOW;
+-
+ if (nla[NFTA_CHAIN_HOOK]) {
+ struct nft_stats __percpu *stats = NULL;
+ struct nft_chain_hook hook;
+@@ -2457,6 +2456,11 @@ static int nf_tables_addchain(struct nft
+ if (err < 0)
+ goto err_destroy_chain;
+
++ if (!nft_use_inc(&table->use)) {
++ err = -EMFILE;
++ goto err_use;
++ }
++
+ trans = nft_trans_chain_add(ctx, NFT_MSG_NEWCHAIN);
+ if (IS_ERR(trans)) {
+ err = PTR_ERR(trans);
+@@ -2473,10 +2477,11 @@ static int nf_tables_addchain(struct nft
+ goto err_unregister_hook;
+ }
+
+- table->use++;
+-
+ return 0;
++
+ err_unregister_hook:
++ nft_use_dec_restore(&table->use);
++err_use:
+ nf_tables_unregister_hook(net, table, chain);
+ err_destroy_chain:
+ nf_tables_chain_destroy(ctx);
+@@ -3663,9 +3668,6 @@ static int nf_tables_newrule(struct sk_b
+ return -EINVAL;
+ handle = nf_tables_alloc_handle(table);
+
+- if (chain->use == UINT_MAX)
+- return -EOVERFLOW;
+-
+ if (nla[NFTA_RULE_POSITION]) {
+ pos_handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_POSITION]));
+ old_rule = __nft_rule_lookup(chain, pos_handle);
+@@ -3759,6 +3761,11 @@ static int nf_tables_newrule(struct sk_b
+ }
+ }
+
++ if (!nft_use_inc(&chain->use)) {
++ err = -EMFILE;
++ goto err_release_rule;
++ }
++
+ if (info->nlh->nlmsg_flags & NLM_F_REPLACE) {
+ err = nft_delrule(&ctx, old_rule);
+ if (err < 0)
+@@ -3790,7 +3797,6 @@ static int nf_tables_newrule(struct sk_b
+ }
+ }
+ kvfree(expr_info);
+- chain->use++;
+
+ if (flow)
+ nft_trans_flow_rule(trans) = flow;
+@@ -3801,6 +3807,7 @@ static int nf_tables_newrule(struct sk_b
+ return 0;
+
+ err_destroy_flow_rule:
++ nft_use_dec_restore(&chain->use);
+ if (flow)
+ nft_flow_rule_destroy(flow);
+ err_release_rule:
+@@ -4818,9 +4825,15 @@ static int nf_tables_newset(struct sk_bu
+ alloc_size = sizeof(*set) + size + udlen;
+ if (alloc_size < size || alloc_size > INT_MAX)
+ return -ENOMEM;
++
++ if (!nft_use_inc(&table->use))
++ return -EMFILE;
++
+ set = kvzalloc(alloc_size, GFP_KERNEL_ACCOUNT);
+- if (!set)
+- return -ENOMEM;
++ if (!set) {
++ err = -ENOMEM;
++ goto err_alloc;
++ }
+
+ name = nla_strdup(nla[NFTA_SET_NAME], GFP_KERNEL_ACCOUNT);
+ if (!name) {
+@@ -4878,7 +4891,7 @@ static int nf_tables_newset(struct sk_bu
+ goto err_set_expr_alloc;
+
+ list_add_tail_rcu(&set->list, &table->sets);
+- table->use++;
++
+ return 0;
+
+ err_set_expr_alloc:
+@@ -4890,6 +4903,9 @@ err_set_init:
+ kfree(set->name);
+ err_set_name:
+ kvfree(set);
++err_alloc:
++ nft_use_dec_restore(&table->use);
++
+ return err;
+ }
+
+@@ -5024,9 +5040,6 @@ int nf_tables_bind_set(const struct nft_
+ struct nft_set_binding *i;
+ struct nft_set_iter iter;
+
+- if (set->use == UINT_MAX)
+- return -EOVERFLOW;
+-
+ if (!list_empty(&set->bindings) && nft_set_is_anonymous(set))
+ return -EBUSY;
+
+@@ -5054,10 +5067,12 @@ int nf_tables_bind_set(const struct nft_
+ return iter.err;
+ }
+ bind:
++ if (!nft_use_inc(&set->use))
++ return -EMFILE;
++
+ binding->chain = ctx->chain;
+ list_add_tail_rcu(&binding->list, &set->bindings);
+ nft_set_trans_bind(ctx, set);
+- set->use++;
+
+ return 0;
+ }
+@@ -5131,7 +5146,7 @@ void nf_tables_activate_set(const struct
+ nft_clear(ctx->net, set);
+ }
+
+- set->use++;
++ nft_use_inc_restore(&set->use);
+ }
+ EXPORT_SYMBOL_GPL(nf_tables_activate_set);
+
+@@ -5147,7 +5162,7 @@ void nf_tables_deactivate_set(const stru
+ else
+ list_del_rcu(&binding->list);
+
+- set->use--;
++ nft_use_dec(&set->use);
+ break;
+ case NFT_TRANS_PREPARE:
+ if (nft_set_is_anonymous(set)) {
+@@ -5156,7 +5171,7 @@ void nf_tables_deactivate_set(const stru
+
+ nft_deactivate_next(ctx->net, set);
+ }
+- set->use--;
++ nft_use_dec(&set->use);
+ return;
+ case NFT_TRANS_ABORT:
+ case NFT_TRANS_RELEASE:
+@@ -5164,7 +5179,7 @@ void nf_tables_deactivate_set(const stru
+ set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
+ nft_map_deactivate(ctx, set);
+
+- set->use--;
++ nft_use_dec(&set->use);
+ fallthrough;
+ default:
+ nf_tables_unbind_set(ctx, set, binding,
+@@ -5937,7 +5952,7 @@ void nft_set_elem_destroy(const struct n
+ nft_set_elem_expr_destroy(&ctx, nft_set_ext_expr(ext));
+
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF))
+- (*nft_set_ext_obj(ext))->use--;
++ nft_use_dec(&(*nft_set_ext_obj(ext))->use);
+ kfree(elem);
+ }
+ EXPORT_SYMBOL_GPL(nft_set_elem_destroy);
+@@ -6439,8 +6454,16 @@ static int nft_add_set_elem(struct nft_c
+ set->objtype, genmask);
+ if (IS_ERR(obj)) {
+ err = PTR_ERR(obj);
++ obj = NULL;
+ goto err_parse_key_end;
+ }
++
++ if (!nft_use_inc(&obj->use)) {
++ err = -EMFILE;
++ obj = NULL;
++ goto err_parse_key_end;
++ }
++
+ err = nft_set_ext_add(&tmpl, NFT_SET_EXT_OBJREF);
+ if (err < 0)
+ goto err_parse_key_end;
+@@ -6509,10 +6532,9 @@ static int nft_add_set_elem(struct nft_c
+ if (flags)
+ *nft_set_ext_flags(ext) = flags;
+
+- if (obj) {
++ if (obj)
+ *nft_set_ext_obj(ext) = obj;
+- obj->use++;
+- }
++
+ if (ulen > 0) {
+ if (nft_set_ext_check(&tmpl, NFT_SET_EXT_USERDATA, ulen) < 0) {
+ err = -EINVAL;
+@@ -6577,12 +6599,13 @@ err_element_clash:
+ kfree(trans);
+ err_elem_free:
+ nf_tables_set_elem_destroy(ctx, set, elem.priv);
+- if (obj)
+- obj->use--;
+ err_parse_data:
+ if (nla[NFTA_SET_ELEM_DATA] != NULL)
+ nft_data_release(&elem.data.val, desc.type);
+ err_parse_key_end:
++ if (obj)
++ nft_use_dec_restore(&obj->use);
++
+ nft_data_release(&elem.key_end.val, NFT_DATA_VALUE);
+ err_parse_key:
+ nft_data_release(&elem.key.val, NFT_DATA_VALUE);
+@@ -6663,7 +6686,7 @@ void nft_data_hold(const struct nft_data
+ case NFT_JUMP:
+ case NFT_GOTO:
+ chain = data->verdict.chain;
+- chain->use++;
++ nft_use_inc_restore(&chain->use);
+ break;
+ }
+ }
+@@ -6678,7 +6701,7 @@ static void nft_setelem_data_activate(co
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
+ nft_data_hold(nft_set_ext_data(ext), set->dtype);
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF))
+- (*nft_set_ext_obj(ext))->use++;
++ nft_use_inc_restore(&(*nft_set_ext_obj(ext))->use);
+ }
+
+ static void nft_setelem_data_deactivate(const struct net *net,
+@@ -6690,7 +6713,7 @@ static void nft_setelem_data_deactivate(
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
+ nft_data_release(nft_set_ext_data(ext), set->dtype);
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF))
+- (*nft_set_ext_obj(ext))->use--;
++ nft_use_dec(&(*nft_set_ext_obj(ext))->use);
+ }
+
+ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
+@@ -7229,9 +7252,14 @@ static int nf_tables_newobj(struct sk_bu
+
+ nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
+
++ if (!nft_use_inc(&table->use))
++ return -EMFILE;
++
+ type = nft_obj_type_get(net, objtype);
+- if (IS_ERR(type))
+- return PTR_ERR(type);
++ if (IS_ERR(type)) {
++ err = PTR_ERR(type);
++ goto err_type;
++ }
+
+ obj = nft_obj_init(&ctx, type, nla[NFTA_OBJ_DATA]);
+ if (IS_ERR(obj)) {
+@@ -7265,7 +7293,7 @@ static int nf_tables_newobj(struct sk_bu
+ goto err_obj_ht;
+
+ list_add_tail_rcu(&obj->list, &table->objects);
+- table->use++;
++
+ return 0;
+ err_obj_ht:
+ /* queued in transaction log */
+@@ -7281,6 +7309,9 @@ err_strdup:
+ kfree(obj);
+ err_init:
+ module_put(type->owner);
++err_type:
++ nft_use_dec_restore(&table->use);
++
+ return err;
+ }
+
+@@ -7671,7 +7702,7 @@ void nf_tables_deactivate_flowtable(cons
+ case NFT_TRANS_PREPARE:
+ case NFT_TRANS_ABORT:
+ case NFT_TRANS_RELEASE:
+- flowtable->use--;
++ nft_use_dec(&flowtable->use);
+ fallthrough;
+ default:
+ return;
+@@ -8019,9 +8050,14 @@ static int nf_tables_newflowtable(struct
+
+ nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
+
++ if (!nft_use_inc(&table->use))
++ return -EMFILE;
++
+ flowtable = kzalloc(sizeof(*flowtable), GFP_KERNEL_ACCOUNT);
+- if (!flowtable)
+- return -ENOMEM;
++ if (!flowtable) {
++ err = -ENOMEM;
++ goto flowtable_alloc;
++ }
+
+ flowtable->table = table;
+ flowtable->handle = nf_tables_alloc_handle(table);
+@@ -8076,7 +8112,6 @@ static int nf_tables_newflowtable(struct
+ goto err5;
+
+ list_add_tail_rcu(&flowtable->list, &table->flowtables);
+- table->use++;
+
+ return 0;
+ err5:
+@@ -8093,6 +8128,9 @@ err2:
+ kfree(flowtable->name);
+ err1:
+ kfree(flowtable);
++flowtable_alloc:
++ nft_use_dec_restore(&table->use);
++
+ return err;
+ }
+
+@@ -9378,7 +9416,7 @@ static int nf_tables_commit(struct net *
+ */
+ if (nft_set_is_anonymous(nft_trans_set(trans)) &&
+ !list_empty(&nft_trans_set(trans)->bindings))
+- trans->ctx.table->use--;
++ nft_use_dec(&trans->ctx.table->use);
+ }
+ nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
+ NFT_MSG_NEWSET, GFP_KERNEL);
+@@ -9597,7 +9635,7 @@ static int __nf_tables_abort(struct net
+ nft_trans_destroy(trans);
+ break;
+ }
+- trans->ctx.table->use--;
++ nft_use_dec_restore(&trans->ctx.table->use);
+ nft_chain_del(trans->ctx.chain);
+ nf_tables_unregister_hook(trans->ctx.net,
+ trans->ctx.table,
+@@ -9605,7 +9643,7 @@ static int __nf_tables_abort(struct net
+ }
+ break;
+ case NFT_MSG_DELCHAIN:
+- trans->ctx.table->use++;
++ nft_use_inc_restore(&trans->ctx.table->use);
+ nft_clear(trans->ctx.net, trans->ctx.chain);
+ nft_trans_destroy(trans);
+ break;
+@@ -9614,7 +9652,7 @@ static int __nf_tables_abort(struct net
+ nft_trans_destroy(trans);
+ break;
+ }
+- trans->ctx.chain->use--;
++ nft_use_dec_restore(&trans->ctx.chain->use);
+ list_del_rcu(&nft_trans_rule(trans)->list);
+ nft_rule_expr_deactivate(&trans->ctx,
+ nft_trans_rule(trans),
+@@ -9623,7 +9661,7 @@ static int __nf_tables_abort(struct net
+ nft_flow_rule_destroy(nft_trans_flow_rule(trans));
+ break;
+ case NFT_MSG_DELRULE:
+- trans->ctx.chain->use++;
++ nft_use_inc_restore(&trans->ctx.chain->use);
+ nft_clear(trans->ctx.net, nft_trans_rule(trans));
+ nft_rule_expr_activate(&trans->ctx, nft_trans_rule(trans));
+ if (trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)
+@@ -9636,7 +9674,7 @@ static int __nf_tables_abort(struct net
+ nft_trans_destroy(trans);
+ break;
+ }
+- trans->ctx.table->use--;
++ nft_use_dec_restore(&trans->ctx.table->use);
+ if (nft_trans_set_bound(trans)) {
+ nft_trans_destroy(trans);
+ break;
+@@ -9644,7 +9682,7 @@ static int __nf_tables_abort(struct net
+ list_del_rcu(&nft_trans_set(trans)->list);
+ break;
+ case NFT_MSG_DELSET:
+- trans->ctx.table->use++;
++ nft_use_inc_restore(&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));
+@@ -9687,12 +9725,12 @@ static int __nf_tables_abort(struct net
+ nft_obj_destroy(&trans->ctx, nft_trans_obj_newobj(trans));
+ nft_trans_destroy(trans);
+ } else {
+- trans->ctx.table->use--;
++ nft_use_dec_restore(&trans->ctx.table->use);
+ nft_obj_del(nft_trans_obj(trans));
+ }
+ break;
+ case NFT_MSG_DELOBJ:
+- trans->ctx.table->use++;
++ nft_use_inc_restore(&trans->ctx.table->use);
+ nft_clear(trans->ctx.net, nft_trans_obj(trans));
+ nft_trans_destroy(trans);
+ break;
+@@ -9701,7 +9739,7 @@ static int __nf_tables_abort(struct net
+ nft_unregister_flowtable_net_hooks(net,
+ &nft_trans_flowtable_hooks(trans));
+ } else {
+- trans->ctx.table->use--;
++ nft_use_dec_restore(&trans->ctx.table->use);
+ list_del_rcu(&nft_trans_flowtable(trans)->list);
+ nft_unregister_flowtable_net_hooks(net,
+ &nft_trans_flowtable(trans)->hook_list);
+@@ -9712,7 +9750,7 @@ static int __nf_tables_abort(struct net
+ list_splice(&nft_trans_flowtable_hooks(trans),
+ &nft_trans_flowtable(trans)->hook_list);
+ } else {
+- trans->ctx.table->use++;
++ nft_use_inc_restore(&trans->ctx.table->use);
+ nft_clear(trans->ctx.net, nft_trans_flowtable(trans));
+ }
+ nft_trans_destroy(trans);
+@@ -10165,8 +10203,9 @@ static int nft_verdict_init(const struct
+ if (desc->flags & NFT_DATA_DESC_SETELEM &&
+ chain->flags & NFT_CHAIN_BINDING)
+ return -EINVAL;
++ if (!nft_use_inc(&chain->use))
++ return -EMFILE;
+
+- chain->use++;
+ data->verdict.chain = chain;
+ break;
+ }
+@@ -10184,7 +10223,7 @@ static void nft_verdict_uninit(const str
+ case NFT_JUMP:
+ case NFT_GOTO:
+ chain = data->verdict.chain;
+- chain->use--;
++ nft_use_dec(&chain->use);
+ break;
+ }
+ }
+@@ -10353,11 +10392,11 @@ int __nft_release_basechain(struct nft_c
+ nf_tables_unregister_hook(ctx->net, ctx->chain->table, ctx->chain);
+ list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) {
+ list_del(&rule->list);
+- ctx->chain->use--;
++ nft_use_dec(&ctx->chain->use);
+ nf_tables_rule_release(ctx, rule);
+ }
+ nft_chain_del(ctx->chain);
+- ctx->table->use--;
++ nft_use_dec(&ctx->table->use);
+ nf_tables_chain_destroy(ctx);
+
+ return 0;
+@@ -10410,18 +10449,18 @@ static void __nft_release_table(struct n
+ ctx.chain = chain;
+ list_for_each_entry_safe(rule, nr, &chain->rules, list) {
+ list_del(&rule->list);
+- chain->use--;
++ nft_use_dec(&chain->use);
+ nf_tables_rule_release(&ctx, rule);
+ }
+ }
+ list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) {
+ list_del(&flowtable->list);
+- table->use--;
++ nft_use_dec(&table->use);
+ nf_tables_flowtable_destroy(flowtable);
+ }
+ list_for_each_entry_safe(set, ns, &table->sets, list) {
+ list_del(&set->list);
+- table->use--;
++ nft_use_dec(&table->use);
+ if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
+ nft_map_deactivate(&ctx, set);
+
+@@ -10429,13 +10468,13 @@ static void __nft_release_table(struct n
+ }
+ list_for_each_entry_safe(obj, ne, &table->objects, list) {
+ nft_obj_del(obj);
+- table->use--;
++ nft_use_dec(&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--;
++ nft_use_dec(&table->use);
+ nf_tables_chain_destroy(&ctx);
+ }
+ nf_tables_table_destroy(&ctx);
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -404,8 +404,10 @@ static int nft_flow_offload_init(const s
+ if (IS_ERR(flowtable))
+ return PTR_ERR(flowtable);
+
++ if (!nft_use_inc(&flowtable->use))
++ return -EMFILE;
++
+ priv->flowtable = flowtable;
+- flowtable->use++;
+
+ return nf_ct_netns_get(ctx->net, ctx->family);
+ }
+@@ -424,7 +426,7 @@ static void nft_flow_offload_activate(co
+ {
+ struct nft_flow_offload *priv = nft_expr_priv(expr);
+
+- priv->flowtable->use++;
++ nft_use_inc_restore(&priv->flowtable->use);
+ }
+
+ static void nft_flow_offload_destroy(const struct nft_ctx *ctx,
+--- a/net/netfilter/nft_immediate.c
++++ b/net/netfilter/nft_immediate.c
+@@ -168,7 +168,7 @@ static void nft_immediate_deactivate(con
+ nft_immediate_chain_deactivate(ctx, chain, phase);
+ nft_chain_del(chain);
+ chain->bound = false;
+- chain->table->use--;
++ nft_use_dec(&chain->table->use);
+ break;
+ }
+ break;
+@@ -207,7 +207,7 @@ static void nft_immediate_destroy(const
+ * let the transaction records release this chain and its rules.
+ */
+ if (chain->bound) {
+- chain->use--;
++ nft_use_dec(&chain->use);
+ break;
+ }
+
+@@ -215,9 +215,9 @@ static void nft_immediate_destroy(const
+ chain_ctx = *ctx;
+ chain_ctx.chain = chain;
+
+- chain->use--;
++ nft_use_dec(&chain->use);
+ list_for_each_entry_safe(rule, n, &chain->rules, list) {
+- chain->use--;
++ nft_use_dec(&chain->use);
+ list_del(&rule->list);
+ nf_tables_rule_destroy(&chain_ctx, rule);
+ }
+--- a/net/netfilter/nft_objref.c
++++ b/net/netfilter/nft_objref.c
+@@ -41,8 +41,10 @@ static int nft_objref_init(const struct
+ if (IS_ERR(obj))
+ return -ENOENT;
+
++ if (!nft_use_inc(&obj->use))
++ return -EMFILE;
++
+ nft_objref_priv(expr) = obj;
+- obj->use++;
+
+ return 0;
+ }
+@@ -71,7 +73,7 @@ static void nft_objref_deactivate(const
+ if (phase == NFT_TRANS_COMMIT)
+ return;
+
+- obj->use--;
++ nft_use_dec(&obj->use);
+ }
+
+ static void nft_objref_activate(const struct nft_ctx *ctx,
+@@ -79,7 +81,7 @@ static void nft_objref_activate(const st
+ {
+ struct nft_object *obj = nft_objref_priv(expr);
+
+- obj->use++;
++ nft_use_inc_restore(&obj->use);
+ }
+
+ static struct nft_expr_type nft_objref_type;