Skip to content

Commit d7c199e

Browse files
author
Paolo Abeni
committedJul 11, 2024
Merge tag 'nf-24-07-11' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf
Pablo Neira Ayuso says: ==================== Netfilter fixes for net The following batch contains Netfilter fixes for net: Patch #1 fixes a bogus WARN_ON splat in nfnetlink_queue. Patch #2 fixes a crash due to stack overflow in chain loop detection by using the existing chain validation routines Both patches from Florian Westphal. netfilter pull request 24-07-11 * tag 'nf-24-07-11' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf: netfilter: nf_tables: prefer nft_chain_validate netfilter: nfnetlink_queue: drop bogus WARN_ON ==================== Link: https://patch.msgid.link/20240711093948.3816-1-pablo@netfilter.org Signed-off-by: Paolo Abeni <pabeni@redhat.com>

File tree

2 files changed

+14
-146
lines changed

2 files changed

+14
-146
lines changed
 

‎net/netfilter/nf_tables_api.c

+13-145
Original file line numberDiff line numberDiff line change
@@ -3823,6 +3823,15 @@ static void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *r
38233823
nf_tables_rule_destroy(ctx, rule);
38243824
}
38253825

3826+
/** nft_chain_validate - loop detection and hook validation
3827+
*
3828+
* @ctx: context containing call depth and base chain
3829+
* @chain: chain to validate
3830+
*
3831+
* Walk through the rules of the given chain and chase all jumps/gotos
3832+
* and set lookups until either the jump limit is hit or all reachable
3833+
* chains have been validated.
3834+
*/
38263835
int nft_chain_validate(const struct nft_ctx *ctx, const struct nft_chain *chain)
38273836
{
38283837
struct nft_expr *expr, *last;
@@ -3844,6 +3853,9 @@ int nft_chain_validate(const struct nft_ctx *ctx, const struct nft_chain *chain)
38443853
if (!expr->ops->validate)
38453854
continue;
38463855

3856+
/* This may call nft_chain_validate() recursively,
3857+
* callers that do so must increment ctx->level.
3858+
*/
38473859
err = expr->ops->validate(ctx, expr, &data);
38483860
if (err < 0)
38493861
return err;
@@ -10809,150 +10821,6 @@ int nft_chain_validate_hooks(const struct nft_chain *chain,
1080910821
}
1081010822
EXPORT_SYMBOL_GPL(nft_chain_validate_hooks);
1081110823

10812-
/*
10813-
* Loop detection - walk through the ruleset beginning at the destination chain
10814-
* of a new jump until either the source chain is reached (loop) or all
10815-
* reachable chains have been traversed.
10816-
*
10817-
* The loop check is performed whenever a new jump verdict is added to an
10818-
* expression or verdict map or a verdict map is bound to a new chain.
10819-
*/
10820-
10821-
static int nf_tables_check_loops(const struct nft_ctx *ctx,
10822-
const struct nft_chain *chain);
10823-
10824-
static int nft_check_loops(const struct nft_ctx *ctx,
10825-
const struct nft_set_ext *ext)
10826-
{
10827-
const struct nft_data *data;
10828-
int ret;
10829-
10830-
data = nft_set_ext_data(ext);
10831-
switch (data->verdict.code) {
10832-
case NFT_JUMP:
10833-
case NFT_GOTO:
10834-
ret = nf_tables_check_loops(ctx, data->verdict.chain);
10835-
break;
10836-
default:
10837-
ret = 0;
10838-
break;
10839-
}
10840-
10841-
return ret;
10842-
}
10843-
10844-
static int nf_tables_loop_check_setelem(const struct nft_ctx *ctx,
10845-
struct nft_set *set,
10846-
const struct nft_set_iter *iter,
10847-
struct nft_elem_priv *elem_priv)
10848-
{
10849-
const struct nft_set_ext *ext = nft_set_elem_ext(set, elem_priv);
10850-
10851-
if (!nft_set_elem_active(ext, iter->genmask))
10852-
return 0;
10853-
10854-
if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
10855-
*nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END)
10856-
return 0;
10857-
10858-
return nft_check_loops(ctx, ext);
10859-
}
10860-
10861-
static int nft_set_catchall_loops(const struct nft_ctx *ctx,
10862-
struct nft_set *set)
10863-
{
10864-
u8 genmask = nft_genmask_next(ctx->net);
10865-
struct nft_set_elem_catchall *catchall;
10866-
struct nft_set_ext *ext;
10867-
int ret = 0;
10868-
10869-
list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
10870-
ext = nft_set_elem_ext(set, catchall->elem);
10871-
if (!nft_set_elem_active(ext, genmask))
10872-
continue;
10873-
10874-
ret = nft_check_loops(ctx, ext);
10875-
if (ret < 0)
10876-
return ret;
10877-
}
10878-
10879-
return ret;
10880-
}
10881-
10882-
static int nf_tables_check_loops(const struct nft_ctx *ctx,
10883-
const struct nft_chain *chain)
10884-
{
10885-
const struct nft_rule *rule;
10886-
const struct nft_expr *expr, *last;
10887-
struct nft_set *set;
10888-
struct nft_set_binding *binding;
10889-
struct nft_set_iter iter;
10890-
10891-
if (ctx->chain == chain)
10892-
return -ELOOP;
10893-
10894-
if (fatal_signal_pending(current))
10895-
return -EINTR;
10896-
10897-
list_for_each_entry(rule, &chain->rules, list) {
10898-
nft_rule_for_each_expr(expr, last, rule) {
10899-
struct nft_immediate_expr *priv;
10900-
const struct nft_data *data;
10901-
int err;
10902-
10903-
if (strcmp(expr->ops->type->name, "immediate"))
10904-
continue;
10905-
10906-
priv = nft_expr_priv(expr);
10907-
if (priv->dreg != NFT_REG_VERDICT)
10908-
continue;
10909-
10910-
data = &priv->data;
10911-
switch (data->verdict.code) {
10912-
case NFT_JUMP:
10913-
case NFT_GOTO:
10914-
err = nf_tables_check_loops(ctx,
10915-
data->verdict.chain);
10916-
if (err < 0)
10917-
return err;
10918-
break;
10919-
default:
10920-
break;
10921-
}
10922-
}
10923-
}
10924-
10925-
list_for_each_entry(set, &ctx->table->sets, list) {
10926-
if (!nft_is_active_next(ctx->net, set))
10927-
continue;
10928-
if (!(set->flags & NFT_SET_MAP) ||
10929-
set->dtype != NFT_DATA_VERDICT)
10930-
continue;
10931-
10932-
list_for_each_entry(binding, &set->bindings, list) {
10933-
if (!(binding->flags & NFT_SET_MAP) ||
10934-
binding->chain != chain)
10935-
continue;
10936-
10937-
iter.genmask = nft_genmask_next(ctx->net);
10938-
iter.type = NFT_ITER_UPDATE;
10939-
iter.skip = 0;
10940-
iter.count = 0;
10941-
iter.err = 0;
10942-
iter.fn = nf_tables_loop_check_setelem;
10943-
10944-
set->ops->walk(ctx, set, &iter);
10945-
if (!iter.err)
10946-
iter.err = nft_set_catchall_loops(ctx, set);
10947-
10948-
if (iter.err < 0)
10949-
return iter.err;
10950-
}
10951-
}
10952-
10953-
return 0;
10954-
}
10955-
1095610824
/**
1095710825
* nft_parse_u32_check - fetch u32 attribute and check for maximum value
1095810826
*
@@ -11065,7 +10933,7 @@ static int nft_validate_register_store(const struct nft_ctx *ctx,
1106510933
if (data != NULL &&
1106610934
(data->verdict.code == NFT_GOTO ||
1106710935
data->verdict.code == NFT_JUMP)) {
11068-
err = nf_tables_check_loops(ctx, data->verdict.chain);
10936+
err = nft_chain_validate(ctx, data->verdict.chain);
1106910937
if (err < 0)
1107010938
return err;
1107110939
}

‎net/netfilter/nfnetlink_queue.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ static void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
325325
hooks = nf_hook_entries_head(net, pf, entry->state.hook);
326326

327327
i = entry->hook_index;
328-
if (WARN_ON_ONCE(!hooks || i >= hooks->num_hook_entries)) {
328+
if (!hooks || i >= hooks->num_hook_entries) {
329329
kfree_skb_reason(skb, SKB_DROP_REASON_NETFILTER_DROP);
330330
nf_queue_entry_free(entry);
331331
return;

0 commit comments

Comments
 (0)
Please sign in to comment.