netfilter: nf_tables: report use refcount overflow

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: 96518518cc ("netfilter: add nftables")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Pablo Neira Ayuso
2023-08-13 00:07:58 +02:00
committed by Greg Kroah-Hartman
parent 93b3195d37
commit 039ce5eb6b
5 changed files with 145 additions and 79 deletions

View File

@@ -1073,6 +1073,29 @@ int __nft_release_basechain(struct nft_ctx *ctx);
unsigned int nft_do_chain(struct nft_pktinfo *pkt, void *priv); 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 * struct nft_table - nf_tables table
* *
@@ -1150,8 +1173,8 @@ struct nft_object {
struct list_head list; struct list_head list;
struct rhlist_head rhlhead; struct rhlist_head rhlhead;
struct nft_object_hash_key key; struct nft_object_hash_key key;
u32 genmask:2, u32 genmask:2;
use:30; u32 use;
u64 handle; u64 handle;
u16 udlen; u16 udlen;
u8 *udata; u8 *udata;
@@ -1253,8 +1276,8 @@ struct nft_flowtable {
char *name; char *name;
int hooknum; int hooknum;
int ops_len; int ops_len;
u32 genmask:2, u32 genmask:2;
use:30; u32 use;
u64 handle; u64 handle;
/* runtime data below here */ /* runtime data below here */
struct list_head hook_list ____cacheline_aligned; struct list_head hook_list ____cacheline_aligned;

View File

@@ -257,8 +257,10 @@ int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain)
if (chain->bound) if (chain->bound)
return -EBUSY; return -EBUSY;
if (!nft_use_inc(&chain->use))
return -EMFILE;
chain->bound = true; chain->bound = true;
chain->use++;
nft_chain_trans_bind(ctx, chain); nft_chain_trans_bind(ctx, chain);
return 0; return 0;
@@ -427,7 +429,7 @@ static int nft_delchain(struct nft_ctx *ctx)
if (IS_ERR(trans)) if (IS_ERR(trans))
return PTR_ERR(trans); return PTR_ERR(trans);
ctx->table->use--; nft_use_dec(&ctx->table->use);
nft_deactivate_next(ctx->net, ctx->chain); nft_deactivate_next(ctx->net, ctx->chain);
return 0; return 0;
@@ -466,7 +468,7 @@ nf_tables_delrule_deactivate(struct nft_ctx *ctx, struct nft_rule *rule)
/* You cannot delete the same rule twice */ /* You cannot delete the same rule twice */
if (nft_is_active_next(ctx->net, rule)) { if (nft_is_active_next(ctx->net, rule)) {
nft_deactivate_next(ctx->net, rule); nft_deactivate_next(ctx->net, rule);
ctx->chain->use--; nft_use_dec(&ctx->chain->use);
return 0; return 0;
} }
return -ENOENT; return -ENOENT;
@@ -594,7 +596,7 @@ static int nft_delset(const struct nft_ctx *ctx, struct nft_set *set)
nft_map_deactivate(ctx, set); nft_map_deactivate(ctx, set);
nft_deactivate_next(ctx->net, set); nft_deactivate_next(ctx->net, set);
ctx->table->use--; nft_use_dec(&ctx->table->use);
return err; return err;
} }
@@ -626,7 +628,7 @@ static int nft_delobj(struct nft_ctx *ctx, struct nft_object *obj)
return err; return err;
nft_deactivate_next(ctx->net, obj); nft_deactivate_next(ctx->net, obj);
ctx->table->use--; nft_use_dec(&ctx->table->use);
return err; return err;
} }
@@ -661,7 +663,7 @@ static int nft_delflowtable(struct nft_ctx *ctx,
return err; return err;
nft_deactivate_next(ctx->net, flowtable); nft_deactivate_next(ctx->net, flowtable);
ctx->table->use--; nft_use_dec(&ctx->table->use);
return err; return err;
} }
@@ -2158,9 +2160,6 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
struct nft_rule **rules; struct nft_rule **rules;
int err; int err;
if (table->use == UINT_MAX)
return -EOVERFLOW;
if (nla[NFTA_CHAIN_HOOK]) { if (nla[NFTA_CHAIN_HOOK]) {
struct nft_stats __percpu *stats = NULL; struct nft_stats __percpu *stats = NULL;
struct nft_chain_hook hook; struct nft_chain_hook hook;
@@ -2256,6 +2255,11 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
if (err < 0) if (err < 0)
goto err_destroy_chain; goto err_destroy_chain;
if (!nft_use_inc(&table->use)) {
err = -EMFILE;
goto err_use;
}
trans = nft_trans_chain_add(ctx, NFT_MSG_NEWCHAIN); trans = nft_trans_chain_add(ctx, NFT_MSG_NEWCHAIN);
if (IS_ERR(trans)) { if (IS_ERR(trans)) {
err = PTR_ERR(trans); err = PTR_ERR(trans);
@@ -2272,10 +2276,11 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
goto err_unregister_hook; goto err_unregister_hook;
} }
table->use++;
return 0; return 0;
err_unregister_hook: err_unregister_hook:
nft_use_dec_restore(&table->use);
err_use:
nf_tables_unregister_hook(net, table, chain); nf_tables_unregister_hook(net, table, chain);
err_destroy_chain: err_destroy_chain:
nf_tables_chain_destroy(ctx); nf_tables_chain_destroy(ctx);
@@ -3387,9 +3392,6 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
return -EINVAL; return -EINVAL;
handle = nf_tables_alloc_handle(table); handle = nf_tables_alloc_handle(table);
if (chain->use == UINT_MAX)
return -EOVERFLOW;
if (nla[NFTA_RULE_POSITION]) { if (nla[NFTA_RULE_POSITION]) {
pos_handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_POSITION])); pos_handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_POSITION]));
old_rule = __nft_rule_lookup(chain, pos_handle); old_rule = __nft_rule_lookup(chain, pos_handle);
@@ -3475,16 +3477,21 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
expr = nft_expr_next(expr); expr = nft_expr_next(expr);
} }
if (!nft_use_inc(&chain->use)) {
err = -EMFILE;
goto err2;
}
if (nlh->nlmsg_flags & NLM_F_REPLACE) { if (nlh->nlmsg_flags & NLM_F_REPLACE) {
trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule); trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule);
if (trans == NULL) { if (trans == NULL) {
err = -ENOMEM; err = -ENOMEM;
goto err2; goto err_destroy_flow_rule;
} }
err = nft_delrule(&ctx, old_rule); err = nft_delrule(&ctx, old_rule);
if (err < 0) { if (err < 0) {
nft_trans_destroy(trans); nft_trans_destroy(trans);
goto err2; goto err_destroy_flow_rule;
} }
list_add_tail_rcu(&rule->list, &old_rule->list); list_add_tail_rcu(&rule->list, &old_rule->list);
@@ -3492,7 +3499,7 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule); trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule);
if (!trans) { if (!trans) {
err = -ENOMEM; err = -ENOMEM;
goto err2; goto err_destroy_flow_rule;
} }
if (nlh->nlmsg_flags & NLM_F_APPEND) { if (nlh->nlmsg_flags & NLM_F_APPEND) {
@@ -3508,7 +3515,6 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
} }
} }
kvfree(info); kvfree(info);
chain->use++;
if (nft_net->validate_state == NFT_VALIDATE_DO) if (nft_net->validate_state == NFT_VALIDATE_DO)
return nft_table_validate(net, table); return nft_table_validate(net, table);
@@ -3522,6 +3528,9 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
} }
return 0; return 0;
err_destroy_flow_rule:
nft_use_dec_restore(&chain->use);
err2: err2:
nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE_ERROR); nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE_ERROR);
nf_tables_rule_destroy(&ctx, rule); nf_tables_rule_destroy(&ctx, rule);
@@ -4437,9 +4446,15 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
alloc_size = sizeof(*set) + size + udlen; alloc_size = sizeof(*set) + size + udlen;
if (alloc_size < size || alloc_size > INT_MAX) if (alloc_size < size || alloc_size > INT_MAX)
return -ENOMEM; return -ENOMEM;
if (!nft_use_inc(&table->use))
return -EMFILE;
set = kvzalloc(alloc_size, GFP_KERNEL); set = kvzalloc(alloc_size, GFP_KERNEL);
if (!set) if (!set) {
return -ENOMEM; err = -ENOMEM;
goto err_alloc;
}
name = nla_strdup(nla[NFTA_SET_NAME], GFP_KERNEL); name = nla_strdup(nla[NFTA_SET_NAME], GFP_KERNEL);
if (!name) { if (!name) {
@@ -4500,7 +4515,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
goto err_set_expr_alloc; goto err_set_expr_alloc;
list_add_tail_rcu(&set->list, &table->sets); list_add_tail_rcu(&set->list, &table->sets);
table->use++;
return 0; return 0;
err_set_expr_alloc: err_set_expr_alloc:
@@ -4512,6 +4527,9 @@ err_set_init:
kfree(set->name); kfree(set->name);
err_set_name: err_set_name:
kvfree(set); kvfree(set);
err_alloc:
nft_use_dec_restore(&table->use);
return err; return err;
} }
@@ -4605,9 +4623,6 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
struct nft_set_binding *i; struct nft_set_binding *i;
struct nft_set_iter iter; struct nft_set_iter iter;
if (set->use == UINT_MAX)
return -EOVERFLOW;
if (!list_empty(&set->bindings) && nft_set_is_anonymous(set)) if (!list_empty(&set->bindings) && nft_set_is_anonymous(set))
return -EBUSY; return -EBUSY;
@@ -4632,10 +4647,12 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
return iter.err; return iter.err;
} }
bind: bind:
if (!nft_use_inc(&set->use))
return -EMFILE;
binding->chain = ctx->chain; binding->chain = ctx->chain;
list_add_tail_rcu(&binding->list, &set->bindings); list_add_tail_rcu(&binding->list, &set->bindings);
nft_set_trans_bind(ctx, set); nft_set_trans_bind(ctx, set);
set->use++;
return 0; return 0;
} }
@@ -4688,7 +4705,7 @@ void nf_tables_activate_set(const struct nft_ctx *ctx, struct nft_set *set)
nft_clear(ctx->net, set); nft_clear(ctx->net, set);
} }
set->use++; nft_use_inc_restore(&set->use);
} }
EXPORT_SYMBOL_GPL(nf_tables_activate_set); EXPORT_SYMBOL_GPL(nf_tables_activate_set);
@@ -4704,7 +4721,7 @@ void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set,
else else
list_del_rcu(&binding->list); list_del_rcu(&binding->list);
set->use--; nft_use_dec(&set->use);
break; break;
case NFT_TRANS_PREPARE: case NFT_TRANS_PREPARE:
if (nft_set_is_anonymous(set)) { if (nft_set_is_anonymous(set)) {
@@ -4713,7 +4730,7 @@ void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set,
nft_deactivate_next(ctx->net, set); nft_deactivate_next(ctx->net, set);
} }
set->use--; nft_use_dec(&set->use);
return; return;
case NFT_TRANS_ABORT: case NFT_TRANS_ABORT:
case NFT_TRANS_RELEASE: case NFT_TRANS_RELEASE:
@@ -4721,7 +4738,7 @@ void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set,
set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
nft_map_deactivate(ctx, set); nft_map_deactivate(ctx, set);
set->use--; nft_use_dec(&set->use);
fallthrough; fallthrough;
default: default:
nf_tables_unbind_set(ctx, set, binding, nf_tables_unbind_set(ctx, set, binding,
@@ -5344,7 +5361,7 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem,
nft_set_elem_expr_destroy(&ctx, nft_set_ext_expr(ext)); nft_set_elem_expr_destroy(&ctx, nft_set_ext_expr(ext));
if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF)) 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); kfree(elem);
} }
EXPORT_SYMBOL_GPL(nft_set_elem_destroy); EXPORT_SYMBOL_GPL(nft_set_elem_destroy);
@@ -5518,8 +5535,16 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
set->objtype, genmask); set->objtype, genmask);
if (IS_ERR(obj)) { if (IS_ERR(obj)) {
err = PTR_ERR(obj); err = PTR_ERR(obj);
obj = NULL;
goto err_parse_key_end; goto err_parse_key_end;
} }
if (!nft_use_inc(&obj->use)) {
err = -EMFILE;
obj = NULL;
goto err_parse_key_end;
}
nft_set_ext_add(&tmpl, NFT_SET_EXT_OBJREF); nft_set_ext_add(&tmpl, NFT_SET_EXT_OBJREF);
} }
@@ -5584,10 +5609,8 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
udata->len = ulen - 1; udata->len = ulen - 1;
nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen); nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen);
} }
if (obj) { if (obj)
*nft_set_ext_obj(ext) = obj; *nft_set_ext_obj(ext) = obj;
obj->use++;
}
err = nft_set_elem_expr_setup(ctx, ext, expr); err = nft_set_elem_expr_setup(ctx, ext, expr);
if (err < 0) if (err < 0)
@@ -5643,14 +5666,14 @@ err_set_full:
err_element_clash: err_element_clash:
kfree(trans); kfree(trans);
err_elem_expr: err_elem_expr:
if (obj)
obj->use--;
nf_tables_set_elem_destroy(ctx, set, elem.priv); nf_tables_set_elem_destroy(ctx, set, elem.priv);
err_parse_data: err_parse_data:
if (nla[NFTA_SET_ELEM_DATA] != NULL) if (nla[NFTA_SET_ELEM_DATA] != NULL)
nft_data_release(&elem.data.val, desc.type); nft_data_release(&elem.data.val, desc.type);
err_parse_key_end: err_parse_key_end:
if (obj)
nft_use_dec_restore(&obj->use);
nft_data_release(&elem.key_end.val, NFT_DATA_VALUE); nft_data_release(&elem.key_end.val, NFT_DATA_VALUE);
err_parse_key: err_parse_key:
nft_data_release(&elem.key.val, NFT_DATA_VALUE); nft_data_release(&elem.key.val, NFT_DATA_VALUE);
@@ -5722,7 +5745,7 @@ void nft_data_hold(const struct nft_data *data, enum nft_data_types type)
case NFT_JUMP: case NFT_JUMP:
case NFT_GOTO: case NFT_GOTO:
chain = data->verdict.chain; chain = data->verdict.chain;
chain->use++; nft_use_inc_restore(&chain->use);
break; break;
} }
} }
@@ -5737,7 +5760,7 @@ static void nft_setelem_data_activate(const struct net *net,
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
nft_data_hold(nft_set_ext_data(ext), set->dtype); nft_data_hold(nft_set_ext_data(ext), set->dtype);
if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF)) 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, static void nft_setelem_data_deactivate(const struct net *net,
@@ -5749,7 +5772,7 @@ static void nft_setelem_data_deactivate(const struct net *net,
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
nft_data_release(nft_set_ext_data(ext), set->dtype); nft_data_release(nft_set_ext_data(ext), set->dtype);
if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF)) 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, static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
@@ -6216,9 +6239,14 @@ static int nf_tables_newobj(struct net *net, struct sock *nlsk,
nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla); nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
if (!nft_use_inc(&table->use))
return -EMFILE;
type = nft_obj_type_get(net, objtype); type = nft_obj_type_get(net, objtype);
if (IS_ERR(type)) if (IS_ERR(type)) {
return PTR_ERR(type); err = PTR_ERR(type);
goto err_type;
}
obj = nft_obj_init(&ctx, type, nla[NFTA_OBJ_DATA]); obj = nft_obj_init(&ctx, type, nla[NFTA_OBJ_DATA]);
if (IS_ERR(obj)) { if (IS_ERR(obj)) {
@@ -6252,7 +6280,7 @@ static int nf_tables_newobj(struct net *net, struct sock *nlsk,
goto err_obj_ht; goto err_obj_ht;
list_add_tail_rcu(&obj->list, &table->objects); list_add_tail_rcu(&obj->list, &table->objects);
table->use++;
return 0; return 0;
err_obj_ht: err_obj_ht:
/* queued in transaction log */ /* queued in transaction log */
@@ -6268,6 +6296,9 @@ err_strdup:
kfree(obj); kfree(obj);
err_init: err_init:
module_put(type->owner); module_put(type->owner);
err_type:
nft_use_dec_restore(&table->use);
return err; return err;
} }
@@ -6658,7 +6689,7 @@ void nf_tables_deactivate_flowtable(const struct nft_ctx *ctx,
case NFT_TRANS_PREPARE: case NFT_TRANS_PREPARE:
case NFT_TRANS_ABORT: case NFT_TRANS_ABORT:
case NFT_TRANS_RELEASE: case NFT_TRANS_RELEASE:
flowtable->use--; nft_use_dec(&flowtable->use);
fallthrough; fallthrough;
default: default:
return; return;
@@ -6995,9 +7026,14 @@ static int nf_tables_newflowtable(struct net *net, struct sock *nlsk,
nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla); nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
if (!nft_use_inc(&table->use))
return -EMFILE;
flowtable = kzalloc(sizeof(*flowtable), GFP_KERNEL); flowtable = kzalloc(sizeof(*flowtable), GFP_KERNEL);
if (!flowtable) if (!flowtable) {
return -ENOMEM; err = -ENOMEM;
goto flowtable_alloc;
}
flowtable->table = table; flowtable->table = table;
flowtable->handle = nf_tables_alloc_handle(table); flowtable->handle = nf_tables_alloc_handle(table);
@@ -7052,7 +7088,6 @@ static int nf_tables_newflowtable(struct net *net, struct sock *nlsk,
goto err5; goto err5;
list_add_tail_rcu(&flowtable->list, &table->flowtables); list_add_tail_rcu(&flowtable->list, &table->flowtables);
table->use++;
return 0; return 0;
err5: err5:
@@ -7069,6 +7104,9 @@ err2:
kfree(flowtable->name); kfree(flowtable->name);
err1: err1:
kfree(flowtable); kfree(flowtable);
flowtable_alloc:
nft_use_dec_restore(&table->use);
return err; return err;
} }
@@ -8254,7 +8292,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
*/ */
if (nft_set_is_anonymous(nft_trans_set(trans)) && if (nft_set_is_anonymous(nft_trans_set(trans)) &&
!list_empty(&nft_trans_set(trans)->bindings)) !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), nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
NFT_MSG_NEWSET, GFP_KERNEL); NFT_MSG_NEWSET, GFP_KERNEL);
@@ -8438,7 +8476,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
nft_trans_destroy(trans); nft_trans_destroy(trans);
break; break;
} }
trans->ctx.table->use--; nft_use_dec_restore(&trans->ctx.table->use);
nft_chain_del(trans->ctx.chain); nft_chain_del(trans->ctx.chain);
nf_tables_unregister_hook(trans->ctx.net, nf_tables_unregister_hook(trans->ctx.net,
trans->ctx.table, trans->ctx.table,
@@ -8446,7 +8484,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
} }
break; break;
case NFT_MSG_DELCHAIN: 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_clear(trans->ctx.net, trans->ctx.chain);
nft_trans_destroy(trans); nft_trans_destroy(trans);
break; break;
@@ -8455,20 +8493,20 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
nft_trans_destroy(trans); nft_trans_destroy(trans);
break; break;
} }
trans->ctx.chain->use--; nft_use_dec_restore(&trans->ctx.chain->use);
list_del_rcu(&nft_trans_rule(trans)->list); list_del_rcu(&nft_trans_rule(trans)->list);
nft_rule_expr_deactivate(&trans->ctx, nft_rule_expr_deactivate(&trans->ctx,
nft_trans_rule(trans), nft_trans_rule(trans),
NFT_TRANS_ABORT); NFT_TRANS_ABORT);
break; break;
case NFT_MSG_DELRULE: 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_clear(trans->ctx.net, nft_trans_rule(trans));
nft_rule_expr_activate(&trans->ctx, nft_trans_rule(trans)); nft_rule_expr_activate(&trans->ctx, nft_trans_rule(trans));
nft_trans_destroy(trans); nft_trans_destroy(trans);
break; break;
case NFT_MSG_NEWSET: case NFT_MSG_NEWSET:
trans->ctx.table->use--; nft_use_dec_restore(&trans->ctx.table->use);
if (nft_trans_set_bound(trans)) { if (nft_trans_set_bound(trans)) {
nft_trans_destroy(trans); nft_trans_destroy(trans);
break; break;
@@ -8476,7 +8514,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
list_del_rcu(&nft_trans_set(trans)->list); list_del_rcu(&nft_trans_set(trans)->list);
break; break;
case NFT_MSG_DELSET: 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)); nft_clear(trans->ctx.net, nft_trans_set(trans));
if (nft_trans_set(trans)->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) if (nft_trans_set(trans)->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
nft_map_activate(&trans->ctx, nft_trans_set(trans)); nft_map_activate(&trans->ctx, nft_trans_set(trans));
@@ -8506,12 +8544,12 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
nft_obj_destroy(&trans->ctx, nft_trans_obj_newobj(trans)); nft_obj_destroy(&trans->ctx, nft_trans_obj_newobj(trans));
nft_trans_destroy(trans); nft_trans_destroy(trans);
} else { } else {
trans->ctx.table->use--; nft_use_dec_restore(&trans->ctx.table->use);
nft_obj_del(nft_trans_obj(trans)); nft_obj_del(nft_trans_obj(trans));
} }
break; break;
case NFT_MSG_DELOBJ: 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_clear(trans->ctx.net, nft_trans_obj(trans));
nft_trans_destroy(trans); nft_trans_destroy(trans);
break; break;
@@ -8520,7 +8558,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
nft_unregister_flowtable_net_hooks(net, nft_unregister_flowtable_net_hooks(net,
&nft_trans_flowtable_hooks(trans)); &nft_trans_flowtable_hooks(trans));
} else { } else {
trans->ctx.table->use--; nft_use_dec_restore(&trans->ctx.table->use);
list_del_rcu(&nft_trans_flowtable(trans)->list); list_del_rcu(&nft_trans_flowtable(trans)->list);
nft_unregister_flowtable_net_hooks(net, nft_unregister_flowtable_net_hooks(net,
&nft_trans_flowtable(trans)->hook_list); &nft_trans_flowtable(trans)->hook_list);
@@ -8531,7 +8569,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
list_splice(&nft_trans_flowtable_hooks(trans), list_splice(&nft_trans_flowtable_hooks(trans),
&nft_trans_flowtable(trans)->hook_list); &nft_trans_flowtable(trans)->hook_list);
} else { } else {
trans->ctx.table->use++; nft_use_inc_restore(&trans->ctx.table->use);
nft_clear(trans->ctx.net, nft_trans_flowtable(trans)); nft_clear(trans->ctx.net, nft_trans_flowtable(trans));
} }
nft_trans_destroy(trans); nft_trans_destroy(trans);
@@ -8969,8 +9007,9 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
if (desc->flags & NFT_DATA_DESC_SETELEM && if (desc->flags & NFT_DATA_DESC_SETELEM &&
chain->flags & NFT_CHAIN_BINDING) chain->flags & NFT_CHAIN_BINDING)
return -EINVAL; return -EINVAL;
if (!nft_use_inc(&chain->use))
return -EMFILE;
chain->use++;
data->verdict.chain = chain; data->verdict.chain = chain;
break; break;
} }
@@ -8988,7 +9027,7 @@ static void nft_verdict_uninit(const struct nft_data *data)
case NFT_JUMP: case NFT_JUMP:
case NFT_GOTO: case NFT_GOTO:
chain = data->verdict.chain; chain = data->verdict.chain;
chain->use--; nft_use_dec(&chain->use);
break; break;
} }
} }
@@ -9157,11 +9196,11 @@ int __nft_release_basechain(struct nft_ctx *ctx)
nf_tables_unregister_hook(ctx->net, ctx->chain->table, ctx->chain); nf_tables_unregister_hook(ctx->net, ctx->chain->table, ctx->chain);
list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) { list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) {
list_del(&rule->list); list_del(&rule->list);
ctx->chain->use--; nft_use_dec(&ctx->chain->use);
nf_tables_rule_release(ctx, rule); nf_tables_rule_release(ctx, rule);
} }
nft_chain_del(ctx->chain); nft_chain_del(ctx->chain);
ctx->table->use--; nft_use_dec(&ctx->table->use);
nf_tables_chain_destroy(ctx); nf_tables_chain_destroy(ctx);
return 0; return 0;
@@ -9201,18 +9240,18 @@ static void __nft_release_table(struct net *net, struct nft_table *table)
ctx.chain = chain; ctx.chain = chain;
list_for_each_entry_safe(rule, nr, &chain->rules, list) { list_for_each_entry_safe(rule, nr, &chain->rules, list) {
list_del(&rule->list); list_del(&rule->list);
chain->use--; nft_use_dec(&chain->use);
nf_tables_rule_release(&ctx, rule); nf_tables_rule_release(&ctx, rule);
} }
} }
list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) { list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) {
list_del(&flowtable->list); list_del(&flowtable->list);
table->use--; nft_use_dec(&table->use);
nf_tables_flowtable_destroy(flowtable); nf_tables_flowtable_destroy(flowtable);
} }
list_for_each_entry_safe(set, ns, &table->sets, list) { list_for_each_entry_safe(set, ns, &table->sets, list) {
list_del(&set->list); list_del(&set->list);
table->use--; nft_use_dec(&table->use);
if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
nft_map_deactivate(&ctx, set); nft_map_deactivate(&ctx, set);
@@ -9220,13 +9259,13 @@ static void __nft_release_table(struct net *net, struct nft_table *table)
} }
list_for_each_entry_safe(obj, ne, &table->objects, list) { list_for_each_entry_safe(obj, ne, &table->objects, list) {
nft_obj_del(obj); nft_obj_del(obj);
table->use--; nft_use_dec(&table->use);
nft_obj_destroy(&ctx, obj); nft_obj_destroy(&ctx, obj);
} }
list_for_each_entry_safe(chain, nc, &table->chains, list) { list_for_each_entry_safe(chain, nc, &table->chains, list) {
ctx.chain = chain; ctx.chain = chain;
nft_chain_del(chain); nft_chain_del(chain);
table->use--; nft_use_dec(&table->use);
nf_tables_chain_destroy(&ctx); nf_tables_chain_destroy(&ctx);
} }
list_del(&table->list); list_del(&table->list);

View File

@@ -174,8 +174,10 @@ static int nft_flow_offload_init(const struct nft_ctx *ctx,
if (IS_ERR(flowtable)) if (IS_ERR(flowtable))
return PTR_ERR(flowtable); return PTR_ERR(flowtable);
if (!nft_use_inc(&flowtable->use))
return -EMFILE;
priv->flowtable = flowtable; priv->flowtable = flowtable;
flowtable->use++;
return nf_ct_netns_get(ctx->net, ctx->family); return nf_ct_netns_get(ctx->net, ctx->family);
} }
@@ -194,7 +196,7 @@ static void nft_flow_offload_activate(const struct nft_ctx *ctx,
{ {
struct nft_flow_offload *priv = nft_expr_priv(expr); 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, static void nft_flow_offload_destroy(const struct nft_ctx *ctx,

View File

@@ -168,7 +168,7 @@ static void nft_immediate_deactivate(const struct nft_ctx *ctx,
nft_immediate_chain_deactivate(ctx, chain, phase); nft_immediate_chain_deactivate(ctx, chain, phase);
nft_chain_del(chain); nft_chain_del(chain);
chain->bound = false; chain->bound = false;
chain->table->use--; nft_use_dec(&chain->table->use);
break; break;
} }
break; break;
@@ -207,7 +207,7 @@ static void nft_immediate_destroy(const struct nft_ctx *ctx,
* let the transaction records release this chain and its rules. * let the transaction records release this chain and its rules.
*/ */
if (chain->bound) { if (chain->bound) {
chain->use--; nft_use_dec(&chain->use);
break; break;
} }
@@ -215,9 +215,9 @@ static void nft_immediate_destroy(const struct nft_ctx *ctx,
chain_ctx = *ctx; chain_ctx = *ctx;
chain_ctx.chain = chain; chain_ctx.chain = chain;
chain->use--; nft_use_dec(&chain->use);
list_for_each_entry_safe(rule, n, &chain->rules, list) { list_for_each_entry_safe(rule, n, &chain->rules, list) {
chain->use--; nft_use_dec(&chain->use);
list_del(&rule->list); list_del(&rule->list);
nf_tables_rule_destroy(&chain_ctx, rule); nf_tables_rule_destroy(&chain_ctx, rule);
} }

View File

@@ -41,8 +41,10 @@ static int nft_objref_init(const struct nft_ctx *ctx,
if (IS_ERR(obj)) if (IS_ERR(obj))
return -ENOENT; return -ENOENT;
if (!nft_use_inc(&obj->use))
return -EMFILE;
nft_objref_priv(expr) = obj; nft_objref_priv(expr) = obj;
obj->use++;
return 0; return 0;
} }
@@ -71,7 +73,7 @@ static void nft_objref_deactivate(const struct nft_ctx *ctx,
if (phase == NFT_TRANS_COMMIT) if (phase == NFT_TRANS_COMMIT)
return; return;
obj->use--; nft_use_dec(&obj->use);
} }
static void nft_objref_activate(const struct nft_ctx *ctx, static void nft_objref_activate(const struct nft_ctx *ctx,
@@ -79,7 +81,7 @@ static void nft_objref_activate(const struct nft_ctx *ctx,
{ {
struct nft_object *obj = nft_objref_priv(expr); struct nft_object *obj = nft_objref_priv(expr);
obj->use++; nft_use_inc_restore(&obj->use);
} }
static struct nft_expr_type nft_objref_type; static struct nft_expr_type nft_objref_type;