Skip to content

Commit

Permalink
Merge tag 'nf-23-09-13' of git://git.kernel.org/pub/scm/linux/kernel/…
Browse files Browse the repository at this point in the history
…git/netfilter/nf

netfilter pull request 23-09-13

====================

The following patchset contains Netfilter fixes for net:

1) Do not permit to remove rules from chain binding, otherwise
   double rule release is possible, triggering UaF. This rule
   deletion support does not make sense and userspace does not use
   this. Problem exists since the introduction of chain binding support.

2) rbtree GC worker only collects the elements that have expired.
   This operation is not destructive, therefore, turn write into
   read spinlock to avoid datapath contention due to GC worker run.
   This was not fixed in the recent GC fix batch in the 6.5 cycle.

3) pipapo set backend performs sync GC, therefore, catchall elements
   must use sync GC queue variant. This bug was introduced in the
   6.5 cycle with the recent GC fixes.

4) Stop GC run if memory allocation fails in pipapo set backend,
   otherwise access to NULL pointer to GC transaction object might
   occur. This bug was introduced in the 6.5 cycle with the recent
   GC fixes.

5) rhash GC run uses an iterator that might hit EAGAIN to rewind,
   triggering double-collection of the same element. This bug was
   introduced in the 6.5 cycle with the recent GC fixes.

6) Do not permit to remove elements in anonymous sets, this type of
   sets are populated once and then bound to rules. This fix is
   similar to the chain binding patch coming first in this batch.
   API permits since the very beginning but it has no use case from
   userspace.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Sep 15, 2023
2 parents 350db8a + e8dbde5 commit 615efed
Show file tree
Hide file tree
Showing 11 changed files with 338 additions and 38 deletions.
5 changes: 3 additions & 2 deletions include/net/netfilter/nf_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -1700,8 +1700,9 @@ void nft_trans_gc_queue_sync_done(struct nft_trans_gc *trans);

void nft_trans_gc_elem_add(struct nft_trans_gc *gc, void *priv);

struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,
unsigned int gc_seq);
struct nft_trans_gc *nft_trans_gc_catchall_async(struct nft_trans_gc *gc,
unsigned int gc_seq);
struct nft_trans_gc *nft_trans_gc_catchall_sync(struct nft_trans_gc *gc);

void nft_setelem_data_deactivate(const struct net *net,
const struct nft_set *set,
Expand Down
4 changes: 2 additions & 2 deletions net/netfilter/nf_conntrack_extend.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ static const u8 nf_ct_ext_type_len[NF_CT_EXT_NUM] = {
[NF_CT_EXT_ECACHE] = sizeof(struct nf_conntrack_ecache),
#endif
#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
[NF_CT_EXT_TSTAMP] = sizeof(struct nf_conn_acct),
[NF_CT_EXT_TSTAMP] = sizeof(struct nf_conn_tstamp),
#endif
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
[NF_CT_EXT_TIMEOUT] = sizeof(struct nf_conn_tstamp),
[NF_CT_EXT_TIMEOUT] = sizeof(struct nf_conn_timeout),
#endif
#ifdef CONFIG_NF_CONNTRACK_LABELS
[NF_CT_EXT_LABELS] = sizeof(struct nf_conn_labels),
Expand Down
65 changes: 47 additions & 18 deletions net/netfilter/nf_tables_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -1432,7 +1432,7 @@ static int nft_flush_table(struct nft_ctx *ctx)
if (!nft_is_active_next(ctx->net, chain))
continue;

if (nft_chain_is_bound(chain))
if (nft_chain_binding(chain))
continue;

ctx->chain = chain;
Expand All @@ -1446,8 +1446,7 @@ static int nft_flush_table(struct nft_ctx *ctx)
if (!nft_is_active_next(ctx->net, set))
continue;

if (nft_set_is_anonymous(set) &&
!list_empty(&set->bindings))
if (nft_set_is_anonymous(set))
continue;

err = nft_delset(ctx, set);
Expand Down Expand Up @@ -1477,7 +1476,7 @@ static int nft_flush_table(struct nft_ctx *ctx)
if (!nft_is_active_next(ctx->net, chain))
continue;

if (nft_chain_is_bound(chain))
if (nft_chain_binding(chain))
continue;

ctx->chain = chain;
Expand Down Expand Up @@ -2910,6 +2909,9 @@ static int nf_tables_delchain(struct sk_buff *skb, const struct nfnl_info *info,
return PTR_ERR(chain);
}

if (nft_chain_binding(chain))
return -EOPNOTSUPP;

nft_ctx_init(&ctx, net, skb, info->nlh, family, table, chain, nla);

if (nla[NFTA_CHAIN_HOOK]) {
Expand Down Expand Up @@ -3449,6 +3451,8 @@ static int __nf_tables_dump_rules(struct sk_buff *skb,
struct net *net = sock_net(skb->sk);
const struct nft_rule *rule, *prule;
unsigned int s_idx = cb->args[0];
unsigned int entries = 0;
int ret = 0;
u64 handle;

prule = NULL;
Expand All @@ -3471,20 +3475,22 @@ static int __nf_tables_dump_rules(struct sk_buff *skb,
NFT_MSG_NEWRULE,
NLM_F_MULTI | NLM_F_APPEND,
table->family,
table, chain, rule, handle, reset) < 0)
return 1;

table, chain, rule, handle, reset) < 0) {
ret = 1;
break;
}
entries++;
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
cont:
prule = rule;
cont_skip:
(*idx)++;
}

if (reset && *idx)
audit_log_rule_reset(table, cb->seq, *idx);
if (reset && entries)
audit_log_rule_reset(table, cb->seq, entries);

return 0;
return ret;
}

static int nf_tables_dump_rules(struct sk_buff *skb,
Expand Down Expand Up @@ -3971,6 +3977,11 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
}

if (info->nlh->nlmsg_flags & NLM_F_REPLACE) {
if (nft_chain_binding(chain)) {
err = -EOPNOTSUPP;
goto err_destroy_flow_rule;
}

err = nft_delrule(&ctx, old_rule);
if (err < 0)
goto err_destroy_flow_rule;
Expand Down Expand Up @@ -4078,7 +4089,7 @@ static int nf_tables_delrule(struct sk_buff *skb, const struct nfnl_info *info,
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]);
return PTR_ERR(chain);
}
if (nft_chain_is_bound(chain))
if (nft_chain_binding(chain))
return -EOPNOTSUPP;
}

Expand Down Expand Up @@ -4112,7 +4123,7 @@ static int nf_tables_delrule(struct sk_buff *skb, const struct nfnl_info *info,
list_for_each_entry(chain, &table->chains, list) {
if (!nft_is_active_next(net, chain))
continue;
if (nft_chain_is_bound(chain))
if (nft_chain_binding(chain))
continue;

ctx.chain = chain;
Expand Down Expand Up @@ -7183,8 +7194,10 @@ static int nf_tables_delsetelem(struct sk_buff *skb,
if (IS_ERR(set))
return PTR_ERR(set);

if (!list_empty(&set->bindings) &&
(set->flags & (NFT_SET_CONSTANT | NFT_SET_ANONYMOUS)))
if (nft_set_is_anonymous(set))
return -EOPNOTSUPP;

if (!list_empty(&set->bindings) && (set->flags & NFT_SET_CONSTANT))
return -EBUSY;

nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
Expand Down Expand Up @@ -9605,8 +9618,9 @@ void nft_trans_gc_queue_sync_done(struct nft_trans_gc *trans)
call_rcu(&trans->rcu, nft_trans_gc_trans_free);
}

struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,
unsigned int gc_seq)
static struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,
unsigned int gc_seq,
bool sync)
{
struct nft_set_elem_catchall *catchall;
const struct nft_set *set = gc->set;
Expand All @@ -9622,7 +9636,11 @@ struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,

nft_set_elem_dead(ext);
dead_elem:
gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC);
if (sync)
gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC);
else
gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC);

if (!gc)
return NULL;

Expand All @@ -9632,6 +9650,17 @@ struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,
return gc;
}

struct nft_trans_gc *nft_trans_gc_catchall_async(struct nft_trans_gc *gc,
unsigned int gc_seq)
{
return nft_trans_gc_catchall(gc, gc_seq, false);
}

struct nft_trans_gc *nft_trans_gc_catchall_sync(struct nft_trans_gc *gc)
{
return nft_trans_gc_catchall(gc, 0, true);
}

static void nf_tables_module_autoload_cleanup(struct net *net)
{
struct nftables_pernet *nft_net = nft_pernet(net);
Expand Down Expand Up @@ -11054,7 +11083,7 @@ static void __nft_release_table(struct net *net, struct nft_table *table)
ctx.family = table->family;
ctx.table = table;
list_for_each_entry(chain, &table->chains, list) {
if (nft_chain_is_bound(chain))
if (nft_chain_binding(chain))
continue;

ctx.chain = chain;
Expand Down
11 changes: 4 additions & 7 deletions net/netfilter/nft_set_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -338,12 +338,9 @@ static void nft_rhash_gc(struct work_struct *work)

while ((he = rhashtable_walk_next(&hti))) {
if (IS_ERR(he)) {
if (PTR_ERR(he) != -EAGAIN) {
nft_trans_gc_destroy(gc);
gc = NULL;
goto try_later;
}
continue;
nft_trans_gc_destroy(gc);
gc = NULL;
goto try_later;
}

/* Ruleset has been updated, try later. */
Expand Down Expand Up @@ -372,7 +369,7 @@ static void nft_rhash_gc(struct work_struct *work)
nft_trans_gc_elem_add(gc, he);
}

gc = nft_trans_gc_catchall(gc, gc_seq);
gc = nft_trans_gc_catchall_async(gc, gc_seq);

try_later:
/* catchall list iteration requires rcu read side lock. */
Expand Down
4 changes: 2 additions & 2 deletions net/netfilter/nft_set_pipapo.c
Original file line number Diff line number Diff line change
Expand Up @@ -1596,7 +1596,7 @@ static void pipapo_gc(const struct nft_set *_set, struct nft_pipapo_match *m)

gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC);
if (!gc)
break;
return;

nft_pipapo_gc_deactivate(net, set, e);
pipapo_drop(m, rulemap);
Expand All @@ -1610,7 +1610,7 @@ static void pipapo_gc(const struct nft_set *_set, struct nft_pipapo_match *m)
}
}

gc = nft_trans_gc_catchall(gc, 0);
gc = nft_trans_gc_catchall_sync(gc);
if (gc) {
nft_trans_gc_queue_sync_done(gc);
priv->last_gc = jiffies;
Expand Down
8 changes: 3 additions & 5 deletions net/netfilter/nft_set_rbtree.c
Original file line number Diff line number Diff line change
Expand Up @@ -622,8 +622,7 @@ static void nft_rbtree_gc(struct work_struct *work)
if (!gc)
goto done;

write_lock_bh(&priv->lock);
write_seqcount_begin(&priv->count);
read_lock_bh(&priv->lock);
for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) {

/* Ruleset has been updated, try later. */
Expand Down Expand Up @@ -670,11 +669,10 @@ static void nft_rbtree_gc(struct work_struct *work)
nft_trans_gc_elem_add(gc, rbe);
}

gc = nft_trans_gc_catchall(gc, gc_seq);
gc = nft_trans_gc_catchall_async(gc, gc_seq);

try_later:
write_seqcount_end(&priv->count);
write_unlock_bh(&priv->lock);
read_unlock_bh(&priv->lock);

if (gc)
nft_trans_gc_queue_async_done(gc);
Expand Down
1 change: 1 addition & 0 deletions tools/testing/selftests/netfilter/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
nf-queue
connect_close
audit_logread
4 changes: 2 additions & 2 deletions tools/testing/selftests/netfilter/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ TEST_PROGS := nft_trans_stress.sh nft_fib.sh nft_nat.sh bridge_brouter.sh \
nft_concat_range.sh nft_conntrack_helper.sh \
nft_queue.sh nft_meta.sh nf_nat_edemux.sh \
ipip-conntrack-mtu.sh conntrack_tcp_unreplied.sh \
conntrack_vrf.sh nft_synproxy.sh rpath.sh
conntrack_vrf.sh nft_synproxy.sh rpath.sh nft_audit.sh

HOSTPKG_CONFIG := pkg-config

CFLAGS += $(shell $(HOSTPKG_CONFIG) --cflags libmnl 2>/dev/null)
LDLIBS += $(shell $(HOSTPKG_CONFIG) --libs libmnl 2>/dev/null || echo -lmnl)

TEST_GEN_FILES = nf-queue connect_close
TEST_GEN_FILES = nf-queue connect_close audit_logread

include ../lib.mk
Loading

0 comments on commit 615efed

Please sign in to comment.