diff options
| author | Florian Westphal <fw@strlen.de> | 2025-06-24 23:01:13 +0200 |
|---|---|---|
| committer | Florian Westphal <fw@strlen.de> | 2025-06-26 00:10:06 +0200 |
| commit | ca0c49d1bdb944534851c3dcb4c8ce16f1675074 (patch) | |
| tree | f2652ba1360a9fc5bf559d6c9871f58b0467a7b2 /src | |
| parent | bed99830c4c63eae205c28a7ff914737bedb199d (diff) | |
evaluate: make sure chain jump name comes with a null byte
There is a stack oob read access in netlink_gen_chain():
mpz_export_data(chain, expr->chain->value,
BYTEORDER_HOST_ENDIAN, len);
snprintf(data->chain, NFT_CHAIN_MAXNAMELEN, "%s", chain);
There is no guarantee that chain[] is null terminated, so snprintf
can read past chain[] array. ASAN report is:
AddressSanitizer: stack-buffer-overflow on address 0x7ffff5f00520 at ..
READ of size 257 at 0x7ffff5f00520 thread T0
#0 0x00000032ffb6 in printf_common(void*, char const*, __va_list_tag*) (src/nft+0x32ffb6)
#1 0x00000033055d in vsnprintf (src/nft+0x33055d)
#2 0x000000332071 in snprintf (src/nft+0x332071)
#3 0x0000004eef03 in netlink_gen_chain src/netlink.c:454:2
#4 0x0000004eef03 in netlink_gen_verdict src/netlink.c:467:4
Reject chain jumps that exceed 255 characters, which matches the netlink
policy on the kernel side.
The included reproducer fails without asan too because the kernel will
reject the too-long chain name. But that happens after the asan detected
bogus read.
Signed-off-by: Florian Westphal <fw@strlen.de>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'src')
| -rw-r--r-- | src/evaluate.c | 26 |
1 files changed, 21 insertions, 5 deletions
diff --git a/src/evaluate.c b/src/evaluate.c index 783a373b..ab50cb0a 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -3040,16 +3040,32 @@ static int expr_evaluate_xfrm(struct eval_ctx *ctx, struct expr **exprp) return expr_evaluate_primary(ctx, exprp); } -static int verdict_validate_chainlen(struct eval_ctx *ctx, +static int verdict_validate_chain(struct eval_ctx *ctx, struct expr *chain) { - if (chain->len > NFT_CHAIN_MAXNAMELEN * BITS_PER_BYTE) + char buf[NFT_CHAIN_MAXNAMELEN]; + unsigned int len; + + len = chain->len / BITS_PER_BYTE; + if (len > NFT_CHAIN_MAXNAMELEN) return expr_error(ctx->msgs, chain, "chain name too long (%u, max %u)", chain->len / BITS_PER_BYTE, NFT_CHAIN_MAXNAMELEN); - return 0; + if (!len) + return expr_error(ctx->msgs, chain, + "chain name length 0 not allowed"); + + memset(buf, 0, sizeof(buf)); + mpz_export_data(buf, chain->value, BYTEORDER_HOST_ENDIAN, len); + + if (strnlen(buf, sizeof(buf)) < sizeof(buf)) + return 0; + + return expr_error(ctx->msgs, chain, + "chain name must be smaller than %u", + NFT_CHAIN_MAXNAMELEN); } static int expr_evaluate_verdict(struct eval_ctx *ctx, struct expr **exprp) @@ -3060,7 +3076,7 @@ static int expr_evaluate_verdict(struct eval_ctx *ctx, struct expr **exprp) case NFT_GOTO: case NFT_JUMP: if (expr->chain->etype == EXPR_VALUE && - verdict_validate_chainlen(ctx, expr->chain)) + verdict_validate_chain(ctx, expr->chain)) return -1; break; @@ -3296,7 +3312,7 @@ static int stmt_evaluate_verdict(struct eval_ctx *ctx, struct stmt *stmt) "not a value expression"); } - if (verdict_validate_chainlen(ctx, stmt->expr->chain)) + if (verdict_validate_chain(ctx, stmt->expr->chain)) return -1; } break; |
