diff options
author | Florian Westphal <fw@strlen.de> | 2023-12-21 11:25:14 +0100 |
---|---|---|
committer | Florian Westphal <fw@strlen.de> | 2023-12-22 11:57:46 +0100 |
commit | dcb199544563ded462cb7151134278f82a9e6cfd (patch) | |
tree | b1a1937e32bf5eda4126fb288751fdddb9fbda42 /src/evaluate.c | |
parent | b9e19cc396347df8c7f8cf5d14ba1d6172040f16 (diff) |
src: do not allow to chain more than 16 binops
netlink_linearize.c has never supported more than 16 chained binops.
Adding more is possible but overwrites the stack in
netlink_gen_bitwise().
Add a recursion counter to catch this at eval stage.
Its not enough to just abort once the counter hits
NFT_MAX_EXPR_RECURSION.
This is because there are valid test cases that exceed this.
For example, evaluation of 1 | 2 will merge the constans, so even
if there are a dozen recursive eval calls this will not end up
with large binop chain post-evaluation.
v2: allow more than 16 binops iff the evaluation function
did constant-merging.
Signed-off-by: Florian Westphal <fw@strlen.de>
Diffstat (limited to 'src/evaluate.c')
-rw-r--r-- | src/evaluate.c | 39 |
1 files changed, 37 insertions, 2 deletions
diff --git a/src/evaluate.c b/src/evaluate.c index 26f0110f..41524eef 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -1418,6 +1418,13 @@ static int expr_evaluate_binop(struct eval_ctx *ctx, struct expr **expr) struct expr *op = *expr, *left, *right; const char *sym = expr_op_symbols[op->op]; unsigned int max_shift_len = ctx->ectx.len; + int ret = -1; + + if (ctx->recursion >= USHRT_MAX) + return expr_binary_error(ctx->msgs, op, NULL, + "Binary operation limit %u reached ", + ctx->recursion); + ctx->recursion++; if (expr_evaluate(ctx, &op->left) < 0) return -1; @@ -1472,14 +1479,42 @@ static int expr_evaluate_binop(struct eval_ctx *ctx, struct expr **expr) switch (op->op) { case OP_LSHIFT: case OP_RSHIFT: - return expr_evaluate_shift(ctx, expr); + ret = expr_evaluate_shift(ctx, expr); + break; case OP_AND: case OP_XOR: case OP_OR: - return expr_evaluate_bitwise(ctx, expr); + ret = expr_evaluate_bitwise(ctx, expr); + break; default: BUG("invalid binary operation %u\n", op->op); } + + + if (ctx->recursion == 0) + BUG("recursion counter underflow"); + + /* can't check earlier: evaluate functions might do constant-merging + expr_free. + * + * So once we've evaluate everything check for remaining length of the + * binop chain. + */ + if (--ctx->recursion == 0) { + unsigned int to_linearize = 0; + + op = *expr; + while (op && op->etype == EXPR_BINOP && op->left != NULL) { + to_linearize++; + op = op->left; + + if (to_linearize >= NFT_MAX_EXPR_RECURSION) + return expr_binary_error(ctx->msgs, op, NULL, + "Binary operation limit %u reached ", + NFT_MAX_EXPR_RECURSION); + } + } + + return ret; } static int list_member_evaluate(struct eval_ctx *ctx, struct expr **expr) |