]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
evaluate: support shifts larger than the width of the left operand
authorPablo Neira Ayuso <pablo@netfilter.org>
Fri, 17 Mar 2023 09:16:40 +0000 (10:16 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 22 Jan 2025 21:10:05 +0000 (22:10 +0100)
commit edecd58755a84dbe8f36795f619189490eeb3ef1 upstream.

If we want to left-shift a value of narrower type and assign the result
to a variable of a wider type, we are constrained to only shifting up to
the width of the narrower type.  Thus:

  add rule t c meta mark set ip dscp << 2

works, but:

  add rule t c meta mark set ip dscp << 8

does not, even though the lvalue is large enough to accommodate the
result.

Upgrade the maximum length based on the statement datatype length, which
is provided via context, if it is larger than expression lvalue.

Update netlink_delinearize.c to handle the case where the length of a
shift expression does not match that of its left-hand operand.

Based on patch from Jeremy Sowden.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/rule.h
src/evaluate.c
src/netlink_delinearize.c

index 7625addfd91984458a787b6e9a8f5d54d350652f..c4d8166cb332a6c03862c0b048becc76ccd90e73 100644 (file)
@@ -769,6 +769,7 @@ struct eval_ctx {
        struct rule             *rule;
        struct set              *set;
        struct stmt             *stmt;
+       uint32_t                stmt_len;
        struct expr_ctx         ectx;
        struct proto_ctx        pctx;
 };
index 5d682a7f3624b3e0aca5889f64614eaf1f157c08..9cad7890ef9aa66f3bc726246e6afd5306a40880 100644 (file)
@@ -1194,14 +1194,19 @@ static int constant_binop_simplify(struct eval_ctx *ctx, struct expr **expr)
 static int expr_evaluate_shift(struct eval_ctx *ctx, struct expr **expr)
 {
        struct expr *op = *expr, *left = op->left, *right = op->right;
+       unsigned int shift = mpz_get_uint32(right->value);
+       unsigned int max_shift_len;
 
-       if (mpz_get_uint32(right->value) >= left->len)
+       if (ctx->stmt_len > left->len)
+               max_shift_len = ctx->stmt_len;
+       else
+               max_shift_len = left->len;
+
+       if (shift >= max_shift_len)
                return expr_binary_error(ctx->msgs, right, left,
-                                        "%s shift of %u bits is undefined "
-                                        "for type of %u bits width",
+                                        "%s shift of %u bits is undefined for type of %u bits width",
                                         op->op == OP_LSHIFT ? "Left" : "Right",
-                                        mpz_get_uint32(right->value),
-                                        left->len);
+                                        shift, max_shift_len);
 
        /* Both sides need to be in host byte order */
        if (byteorder_conversion(ctx, &op->left, BYTEORDER_HOST_ENDIAN) < 0)
@@ -1211,7 +1216,7 @@ static int expr_evaluate_shift(struct eval_ctx *ctx, struct expr **expr)
                return -1;
 
        op->byteorder = BYTEORDER_HOST_ENDIAN;
-       op->len       = left->len;
+       op->len       = max_shift_len;
 
        if (expr_is_constant(left))
                return constant_binop_simplify(ctx, expr);
@@ -1244,14 +1249,20 @@ 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;
 
        if (expr_evaluate(ctx, &op->left) < 0)
                return -1;
        left = op->left;
 
-       if (op->op == OP_LSHIFT || op->op == OP_RSHIFT)
+       if (op->op == OP_LSHIFT || op->op == OP_RSHIFT) {
+               if (left->len > max_shift_len)
+                       max_shift_len = left->len;
+
                __expr_set_context(&ctx->ectx, &integer_type,
-                                  left->byteorder, ctx->ectx.len, 0);
+                                  left->byteorder, max_shift_len, 0);
+       }
+
        if (expr_evaluate(ctx, &op->right) < 0)
                return -1;
        right = op->right;
@@ -1764,6 +1775,7 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
        }
 
        expr_set_context(&ctx->ectx, NULL, 0);
+       ctx->stmt_len = 0;
        if (expr_evaluate(ctx, &map->map) < 0)
                return -1;
        if (expr_is_constant(map->map))
@@ -2966,20 +2978,34 @@ static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt)
 
 static int stmt_evaluate_meta(struct eval_ctx *ctx, struct stmt *stmt)
 {
-       return stmt_evaluate_arg(ctx, stmt,
-                                stmt->meta.tmpl->dtype,
-                                stmt->meta.tmpl->len,
-                                stmt->meta.tmpl->byteorder,
-                                &stmt->meta.expr);
+       int ret;
+
+       ctx->stmt_len = stmt->meta.tmpl->len;
+
+       ret = stmt_evaluate_arg(ctx, stmt,
+                               stmt->meta.tmpl->dtype,
+                               stmt->meta.tmpl->len,
+                               stmt->meta.tmpl->byteorder,
+                               &stmt->meta.expr);
+       ctx->stmt_len = 0;
+
+       return ret;
 }
 
 static int stmt_evaluate_ct(struct eval_ctx *ctx, struct stmt *stmt)
 {
-       if (stmt_evaluate_arg(ctx, stmt,
-                             stmt->ct.tmpl->dtype,
-                             stmt->ct.tmpl->len,
-                             stmt->ct.tmpl->byteorder,
-                             &stmt->ct.expr) < 0)
+       int ret;
+
+       ctx->stmt_len = stmt->ct.tmpl->len;
+
+       ret = stmt_evaluate_arg(ctx, stmt,
+                               stmt->ct.tmpl->dtype,
+                               stmt->ct.tmpl->len,
+                               stmt->ct.tmpl->byteorder,
+                               &stmt->ct.expr);
+       ctx->stmt_len = 0;
+
+       if (ret < 0)
                return -1;
 
        if (stmt->ct.key == NFT_CT_SECMARK && expr_is_constant(stmt->ct.expr))
index c4eefe1bee3521689a571295e607464a316d65e9..a132020c92d7056836659e855129957f7b57e675 100644 (file)
@@ -479,7 +479,7 @@ static struct expr *netlink_parse_bitwise_bool(struct netlink_parse_ctx *ctx,
                mpz_ior(m, m, o);
        }
 
-       if (left->len > 0 && mpz_scan0(m, 0) == left->len) {
+       if (left->len > 0 && mpz_scan0(m, 0) >= left->len) {
                /* mask encompasses the entire value */
                expr_free(mask);
        } else {
@@ -527,7 +527,7 @@ static struct expr *netlink_parse_bitwise_shift(struct netlink_parse_ctx *ctx,
        right->byteorder = BYTEORDER_HOST_ENDIAN;
 
        expr = binop_expr_alloc(loc, op, left, right);
-       expr->len = left->len;
+       expr->len = nftnl_expr_get_u32(nle, NFTNL_EXPR_BITWISE_LEN) * BITS_PER_BYTE;
 
        return expr;
 }