]> www.infradead.org Git - users/hch/misc.git/commitdiff
netfilter: nft_objref: validate objref and objrefmap expressions
authorFernando Fernandez Mancera <fmancera@suse.de>
Wed, 8 Oct 2025 10:08:16 +0000 (12:08 +0200)
committerFlorian Westphal <fw@strlen.de>
Wed, 8 Oct 2025 11:17:25 +0000 (13:17 +0200)
Referencing a synproxy stateful object from OUTPUT hook causes kernel
crash due to infinite recursive calls:

BUG: TASK stack guard page was hit at 000000008bda5b8c (stack is 000000003ab1c4a5..00000000494d8b12)
[...]
Call Trace:
 __find_rr_leaf+0x99/0x230
 fib6_table_lookup+0x13b/0x2d0
 ip6_pol_route+0xa4/0x400
 fib6_rule_lookup+0x156/0x240
 ip6_route_output_flags+0xc6/0x150
 __nf_ip6_route+0x23/0x50
 synproxy_send_tcp_ipv6+0x106/0x200
 synproxy_send_client_synack_ipv6+0x1aa/0x1f0
 nft_synproxy_do_eval+0x263/0x310
 nft_do_chain+0x5a8/0x5f0 [nf_tables
 nft_do_chain_inet+0x98/0x110
 nf_hook_slow+0x43/0xc0
 __ip6_local_out+0xf0/0x170
 ip6_local_out+0x17/0x70
 synproxy_send_tcp_ipv6+0x1a2/0x200
 synproxy_send_client_synack_ipv6+0x1aa/0x1f0
[...]

Implement objref and objrefmap expression validate functions.

Currently, only NFT_OBJECT_SYNPROXY object type requires validation.
This will also handle a jump to a chain using a synproxy object from the
OUTPUT hook.

Now when trying to reference a synproxy object in the OUTPUT hook, nft
will produce the following error:

synproxy_crash.nft: Error: Could not process rule: Operation not supported
  synproxy name mysynproxy
  ^^^^^^^^^^^^^^^^^^^^^^^^

Fixes: ee394f96ad75 ("netfilter: nft_synproxy: add synproxy stateful object support")
Reported-by: Georg Pfuetzenreuter <georg.pfuetzenreuter@suse.com>
Closes: https://bugzilla.suse.com/1250237
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Florian Westphal <fw@strlen.de>
net/netfilter/nft_objref.c

index 8ee66a86c3bc752f0a882e6d138133bed9308386..1a62e384766a76c80a189488ef570c34bd9b28c4 100644 (file)
@@ -22,6 +22,35 @@ void nft_objref_eval(const struct nft_expr *expr,
        obj->ops->eval(obj, regs, pkt);
 }
 
+static int nft_objref_validate_obj_type(const struct nft_ctx *ctx, u32 type)
+{
+       unsigned int hooks;
+
+       switch (type) {
+       case NFT_OBJECT_SYNPROXY:
+               if (ctx->family != NFPROTO_IPV4 &&
+                   ctx->family != NFPROTO_IPV6 &&
+                   ctx->family != NFPROTO_INET)
+                       return -EOPNOTSUPP;
+
+               hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD);
+
+               return nft_chain_validate_hooks(ctx->chain, hooks);
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int nft_objref_validate(const struct nft_ctx *ctx,
+                              const struct nft_expr *expr)
+{
+       struct nft_object *obj = nft_objref_priv(expr);
+
+       return nft_objref_validate_obj_type(ctx, obj->ops->type->type);
+}
+
 static int nft_objref_init(const struct nft_ctx *ctx,
                           const struct nft_expr *expr,
                           const struct nlattr * const tb[])
@@ -93,6 +122,7 @@ static const struct nft_expr_ops nft_objref_ops = {
        .activate       = nft_objref_activate,
        .deactivate     = nft_objref_deactivate,
        .dump           = nft_objref_dump,
+       .validate       = nft_objref_validate,
        .reduce         = NFT_REDUCE_READONLY,
 };
 
@@ -197,6 +227,14 @@ static void nft_objref_map_destroy(const struct nft_ctx *ctx,
        nf_tables_destroy_set(ctx, priv->set);
 }
 
+static int nft_objref_map_validate(const struct nft_ctx *ctx,
+                                  const struct nft_expr *expr)
+{
+       const struct nft_objref_map *priv = nft_expr_priv(expr);
+
+       return nft_objref_validate_obj_type(ctx, priv->set->objtype);
+}
+
 static const struct nft_expr_ops nft_objref_map_ops = {
        .type           = &nft_objref_type,
        .size           = NFT_EXPR_SIZE(sizeof(struct nft_objref_map)),
@@ -206,6 +244,7 @@ static const struct nft_expr_ops nft_objref_map_ops = {
        .deactivate     = nft_objref_map_deactivate,
        .destroy        = nft_objref_map_destroy,
        .dump           = nft_objref_map_dump,
+       .validate       = nft_objref_map_validate,
        .reduce         = NFT_REDUCE_READONLY,
 };