Commit 8938fc0c authored by Paolo Abeni's avatar Paolo Abeni

Merge tag 'nf-23-08-23' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/netfilter/nf

Florian Westphal says:

====================
netfilter updates for net

This PR contains nf_tables updates for your *net* tree.

First patch fixes table validation, I broke this in 6.4 when tracking
validation state per table, reported by Pablo, fixup from myself.

Second patch makes sure objects waiting for memory release have been
released, this was broken in 6.1, patch from Pablo Neira Ayuso.

Patch three is a fix-for-fix from previous PR: In case a transaction
gets aborted, gc sequence counter needs to be incremented so pending
gc requests are invalidated, from Pablo.

Same for patch 4: gc list needs to use gc list lock, not destroy lock,
also from Pablo.

Patch 5 fixes a UaF in a set backend, but this should only occur when
failslab is enabled for GFP_KERNEL allocations, broken since feature
was added in 5.6, from myself.

Patch 6 fixes a double-free bug that was also added via previous PR:
We must not schedule gc work if the previous batch is still queued.

netfilter pull request 2023-08-23

* tag 'nf-23-08-23' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/netfilter/nf:
  netfilter: nf_tables: defer gc run if previous batch is still pending
  netfilter: nf_tables: fix out of memory error handling
  netfilter: nf_tables: use correct lock to protect gc_list
  netfilter: nf_tables: GC transaction race with abort path
  netfilter: nf_tables: flush pending destroy work before netlink notifier
  netfilter: nf_tables: validate all pending tables
====================

Link: https://lore.kernel.org/r/20230823152711.15279-1-fw@strlen.deSigned-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parents b251610c 8e51830e
...@@ -587,6 +587,11 @@ static inline void *nft_set_priv(const struct nft_set *set) ...@@ -587,6 +587,11 @@ static inline void *nft_set_priv(const struct nft_set *set)
return (void *)set->data; return (void *)set->data;
} }
static inline bool nft_set_gc_is_pending(const struct nft_set *s)
{
return refcount_read(&s->refs) != 1;
}
static inline struct nft_set *nft_set_container_of(const void *priv) static inline struct nft_set *nft_set_container_of(const void *priv)
{ {
return (void *)priv - offsetof(struct nft_set, data); return (void *)priv - offsetof(struct nft_set, data);
...@@ -1729,6 +1734,7 @@ struct nftables_pernet { ...@@ -1729,6 +1734,7 @@ struct nftables_pernet {
u64 table_handle; u64 table_handle;
unsigned int base_seq; unsigned int base_seq;
unsigned int gc_seq; unsigned int gc_seq;
u8 validate_state;
}; };
extern unsigned int nf_tables_net_id; extern unsigned int nf_tables_net_id;
......
...@@ -1373,7 +1373,7 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info, ...@@ -1373,7 +1373,7 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info,
if (table == NULL) if (table == NULL)
goto err_kzalloc; goto err_kzalloc;
table->validate_state = NFT_VALIDATE_SKIP; table->validate_state = nft_net->validate_state;
table->name = nla_strdup(attr, GFP_KERNEL_ACCOUNT); table->name = nla_strdup(attr, GFP_KERNEL_ACCOUNT);
if (table->name == NULL) if (table->name == NULL)
goto err_strdup; goto err_strdup;
...@@ -9051,10 +9051,9 @@ static int nf_tables_validate(struct net *net) ...@@ -9051,10 +9051,9 @@ static int nf_tables_validate(struct net *net)
return -EAGAIN; return -EAGAIN;
nft_validate_state_update(table, NFT_VALIDATE_SKIP); nft_validate_state_update(table, NFT_VALIDATE_SKIP);
}
break; break;
} }
}
return 0; return 0;
} }
...@@ -9457,9 +9456,9 @@ static void nft_trans_gc_work(struct work_struct *work) ...@@ -9457,9 +9456,9 @@ static void nft_trans_gc_work(struct work_struct *work)
struct nft_trans_gc *trans, *next; struct nft_trans_gc *trans, *next;
LIST_HEAD(trans_gc_list); LIST_HEAD(trans_gc_list);
spin_lock(&nf_tables_destroy_list_lock); spin_lock(&nf_tables_gc_list_lock);
list_splice_init(&nf_tables_gc_list, &trans_gc_list); list_splice_init(&nf_tables_gc_list, &trans_gc_list);
spin_unlock(&nf_tables_destroy_list_lock); spin_unlock(&nf_tables_gc_list_lock);
list_for_each_entry_safe(trans, next, &trans_gc_list, list) { list_for_each_entry_safe(trans, next, &trans_gc_list, list) {
list_del(&trans->list); list_del(&trans->list);
...@@ -9799,8 +9798,10 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) ...@@ -9799,8 +9798,10 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
} }
/* 0. Validate ruleset, otherwise roll back for error reporting. */ /* 0. Validate ruleset, otherwise roll back for error reporting. */
if (nf_tables_validate(net) < 0) if (nf_tables_validate(net) < 0) {
nft_net->validate_state = NFT_VALIDATE_DO;
return -EAGAIN; return -EAGAIN;
}
err = nft_flow_rule_offload_commit(net); err = nft_flow_rule_offload_commit(net);
if (err < 0) if (err < 0)
...@@ -10059,6 +10060,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) ...@@ -10059,6 +10060,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
nf_tables_commit_audit_log(&adl, nft_net->base_seq); nf_tables_commit_audit_log(&adl, nft_net->base_seq);
nft_gc_seq_end(nft_net, gc_seq); nft_gc_seq_end(nft_net, gc_seq);
nft_net->validate_state = NFT_VALIDATE_SKIP;
nf_tables_commit_release(net); nf_tables_commit_release(net);
return 0; return 0;
...@@ -10335,8 +10337,12 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb, ...@@ -10335,8 +10337,12 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb,
enum nfnl_abort_action action) enum nfnl_abort_action action)
{ {
struct nftables_pernet *nft_net = nft_pernet(net); struct nftables_pernet *nft_net = nft_pernet(net);
int ret = __nf_tables_abort(net, action); unsigned int gc_seq;
int ret;
gc_seq = nft_gc_seq_begin(nft_net);
ret = __nf_tables_abort(net, action);
nft_gc_seq_end(nft_net, gc_seq);
mutex_unlock(&nft_net->commit_mutex); mutex_unlock(&nft_net->commit_mutex);
return ret; return ret;
...@@ -11071,7 +11077,7 @@ static int nft_rcv_nl_event(struct notifier_block *this, unsigned long event, ...@@ -11071,7 +11077,7 @@ static int nft_rcv_nl_event(struct notifier_block *this, unsigned long event,
gc_seq = nft_gc_seq_begin(nft_net); gc_seq = nft_gc_seq_begin(nft_net);
if (!list_empty(&nf_tables_destroy_list)) if (!list_empty(&nf_tables_destroy_list))
rcu_barrier(); nf_tables_trans_destroy_flush_work();
again: again:
list_for_each_entry(table, &nft_net->tables, list) { list_for_each_entry(table, &nft_net->tables, list) {
if (nft_table_has_owner(table) && if (nft_table_has_owner(table) &&
...@@ -11115,6 +11121,7 @@ static int __net_init nf_tables_init_net(struct net *net) ...@@ -11115,6 +11121,7 @@ static int __net_init nf_tables_init_net(struct net *net)
mutex_init(&nft_net->commit_mutex); mutex_init(&nft_net->commit_mutex);
nft_net->base_seq = 1; nft_net->base_seq = 1;
nft_net->gc_seq = 0; nft_net->gc_seq = 0;
nft_net->validate_state = NFT_VALIDATE_SKIP;
return 0; return 0;
} }
......
...@@ -326,6 +326,9 @@ static void nft_rhash_gc(struct work_struct *work) ...@@ -326,6 +326,9 @@ static void nft_rhash_gc(struct work_struct *work)
nft_net = nft_pernet(net); nft_net = nft_pernet(net);
gc_seq = READ_ONCE(nft_net->gc_seq); gc_seq = READ_ONCE(nft_net->gc_seq);
if (nft_set_gc_is_pending(set))
goto done;
gc = nft_trans_gc_alloc(set, gc_seq, GFP_KERNEL); gc = nft_trans_gc_alloc(set, gc_seq, GFP_KERNEL);
if (!gc) if (!gc)
goto done; goto done;
......
...@@ -902,12 +902,14 @@ static void pipapo_lt_bits_adjust(struct nft_pipapo_field *f) ...@@ -902,12 +902,14 @@ static void pipapo_lt_bits_adjust(struct nft_pipapo_field *f)
static int pipapo_insert(struct nft_pipapo_field *f, const uint8_t *k, static int pipapo_insert(struct nft_pipapo_field *f, const uint8_t *k,
int mask_bits) int mask_bits)
{ {
int rule = f->rules++, group, ret, bit_offset = 0; int rule = f->rules, group, ret, bit_offset = 0;
ret = pipapo_resize(f, f->rules - 1, f->rules); ret = pipapo_resize(f, f->rules, f->rules + 1);
if (ret) if (ret)
return ret; return ret;
f->rules++;
for (group = 0; group < f->groups; group++) { for (group = 0; group < f->groups; group++) {
int i, v; int i, v;
u8 mask; u8 mask;
...@@ -1052,7 +1054,9 @@ static int pipapo_expand(struct nft_pipapo_field *f, ...@@ -1052,7 +1054,9 @@ static int pipapo_expand(struct nft_pipapo_field *f,
step++; step++;
if (step >= len) { if (step >= len) {
if (!masks) { if (!masks) {
pipapo_insert(f, base, 0); err = pipapo_insert(f, base, 0);
if (err < 0)
return err;
masks = 1; masks = 1;
} }
goto out; goto out;
...@@ -1235,6 +1239,9 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set, ...@@ -1235,6 +1239,9 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set,
else else
ret = pipapo_expand(f, start, end, f->groups * f->bb); ret = pipapo_expand(f, start, end, f->groups * f->bb);
if (ret < 0)
return ret;
if (f->bsize > bsize_max) if (f->bsize > bsize_max)
bsize_max = f->bsize; bsize_max = f->bsize;
......
...@@ -611,6 +611,9 @@ static void nft_rbtree_gc(struct work_struct *work) ...@@ -611,6 +611,9 @@ static void nft_rbtree_gc(struct work_struct *work)
nft_net = nft_pernet(net); nft_net = nft_pernet(net);
gc_seq = READ_ONCE(nft_net->gc_seq); gc_seq = READ_ONCE(nft_net->gc_seq);
if (nft_set_gc_is_pending(set))
goto done;
gc = nft_trans_gc_alloc(set, gc_seq, GFP_KERNEL); gc = nft_trans_gc_alloc(set, gc_seq, GFP_KERNEL);
if (!gc) if (!gc)
goto done; goto done;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment