Skip to content

Commit

Permalink
Merge tag 'nf-23-08-23' of ssh://gitolite.kernel.org/pub/scm/linux/ke…
Browse files Browse the repository at this point in the history
…rnel/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/[email protected]
Signed-off-by: Paolo Abeni <[email protected]>
  • Loading branch information
Paolo Abeni committed Aug 24, 2023
2 parents b251610 + 8e51830 commit 8938fc0
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 11 deletions.
6 changes: 6 additions & 0 deletions include/net/netfilter/nf_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,11 @@ static inline void *nft_set_priv(const struct nft_set *set)
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)
{
return (void *)priv - offsetof(struct nft_set, data);
Expand Down Expand Up @@ -1729,6 +1734,7 @@ struct nftables_pernet {
u64 table_handle;
unsigned int base_seq;
unsigned int gc_seq;
u8 validate_state;
};

extern unsigned int nf_tables_net_id;
Expand Down
23 changes: 15 additions & 8 deletions net/netfilter/nf_tables_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -1373,7 +1373,7 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info,
if (table == NULL)
goto err_kzalloc;

table->validate_state = NFT_VALIDATE_SKIP;
table->validate_state = nft_net->validate_state;
table->name = nla_strdup(attr, GFP_KERNEL_ACCOUNT);
if (table->name == NULL)
goto err_strdup;
Expand Down Expand Up @@ -9051,9 +9051,8 @@ static int nf_tables_validate(struct net *net)
return -EAGAIN;

nft_validate_state_update(table, NFT_VALIDATE_SKIP);
break;
}

break;
}

return 0;
Expand Down Expand Up @@ -9457,9 +9456,9 @@ static void nft_trans_gc_work(struct work_struct *work)
struct nft_trans_gc *trans, *next;
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);
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_del(&trans->list);
Expand Down Expand Up @@ -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. */
if (nf_tables_validate(net) < 0)
if (nf_tables_validate(net) < 0) {
nft_net->validate_state = NFT_VALIDATE_DO;
return -EAGAIN;
}

err = nft_flow_rule_offload_commit(net);
if (err < 0)
Expand Down Expand Up @@ -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);

nft_gc_seq_end(nft_net, gc_seq);
nft_net->validate_state = NFT_VALIDATE_SKIP;
nf_tables_commit_release(net);

return 0;
Expand Down Expand Up @@ -10335,8 +10337,12 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb,
enum nfnl_abort_action action)
{
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);

return ret;
Expand Down Expand Up @@ -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);

if (!list_empty(&nf_tables_destroy_list))
rcu_barrier();
nf_tables_trans_destroy_flush_work();
again:
list_for_each_entry(table, &nft_net->tables, list) {
if (nft_table_has_owner(table) &&
Expand Down Expand Up @@ -11115,6 +11121,7 @@ static int __net_init nf_tables_init_net(struct net *net)
mutex_init(&nft_net->commit_mutex);
nft_net->base_seq = 1;
nft_net->gc_seq = 0;
nft_net->validate_state = NFT_VALIDATE_SKIP;

return 0;
}
Expand Down
3 changes: 3 additions & 0 deletions net/netfilter/nft_set_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,9 @@ static void nft_rhash_gc(struct work_struct *work)
nft_net = nft_pernet(net);
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);
if (!gc)
goto done;
Expand Down
13 changes: 10 additions & 3 deletions net/netfilter/nft_set_pipapo.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
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)
return ret;

f->rules++;

for (group = 0; group < f->groups; group++) {
int i, v;
u8 mask;
Expand Down Expand Up @@ -1052,7 +1054,9 @@ static int pipapo_expand(struct nft_pipapo_field *f,
step++;
if (step >= len) {
if (!masks) {
pipapo_insert(f, base, 0);
err = pipapo_insert(f, base, 0);
if (err < 0)
return err;
masks = 1;
}
goto out;
Expand Down Expand Up @@ -1235,6 +1239,9 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set,
else
ret = pipapo_expand(f, start, end, f->groups * f->bb);

if (ret < 0)
return ret;

if (f->bsize > bsize_max)
bsize_max = f->bsize;

Expand Down
3 changes: 3 additions & 0 deletions net/netfilter/nft_set_rbtree.c
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,9 @@ static void nft_rbtree_gc(struct work_struct *work)
nft_net = nft_pernet(net);
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);
if (!gc)
goto done;
Expand Down

0 comments on commit 8938fc0

Please sign in to comment.