]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
evaluate: honor statement length in bitwise evaluation
authorPablo Neira Ayuso <pablo@netfilter.org>
Fri, 17 Mar 2023 09:16:45 +0000 (10:16 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 28 Mar 2023 08:26:34 +0000 (10:26 +0200)
Get length from statement, instead infering it from the expression that
is used to set the value. In the particular case of {ct|meta} mark, this
is 32 bits.

Otherwise, bytecode generation is not correct:

 # nft -c --debug=netlink 'add rule ip6 x y ct mark set ip6 dscp << 2 | 0x10'
  [ payload load 2b @ network header + 0 => reg 1 ]
  [ bitwise reg 1 = ( reg 1 & 0x0000c00f ) ^ 0x00000000 ]
  [ bitwise reg 1 = ( reg 1 >> 0x00000006 ) ]
  [ byteorder reg 1 = ntoh(reg 1, 2, 1) ]
  [ bitwise reg 1 = ( reg 1 << 0x00000002 ) ]
  [ bitwise reg 1 = ( reg 1 & 0x00000fef ) ^ 0x00000010 ]    <--- incorrect!
  [ ct set mark with reg 1 ]

the previous bitwise shift already upgraded to 32-bits (not visible from
the netlink debug output above).

After this patch, the last | 0x10 uses 32-bits:

 [ bitwise reg 1 = ( reg 1 & 0xffffffef ) ^ 0x00000010 ]

note that mask 0xffffffef is used instead of 0x00000fef.

Patch ("evaluate: support shifts larger than the width of the left operand")
provides the statement length through eval context. Use it to evaluate the
bitwise expression accordingly, otherwise bytecode is incorrect:

 # nft --debug=netlink add rule ip x y 'ct mark set ip dscp & 0x0f << 1 | 0xff000000'
 ip x y
  [ payload load 1b @ network header + 1 => reg 1 ]
  [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
  [ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
  [ bitwise reg 1 = ( reg 1 & 0x1e000000 ) ^ 0x000000ff ]  <-- incorrect byteorder for OR
  [ byteorder reg 1 = ntoh(reg 1, 4, 4) ]    <-- no needed for single ip dscp byte
  [ ct set mark with reg 1 ]

Correct bytecode:

 # nft --debug=netlink add rule ip x y 'ct mark set ip dscp & 0x0f << 1 | 0xff000000
 ip x y
  [ payload load 1b @ network header + 1 => reg 1 ]
  [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
  [ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
  [ bitwise reg 1 = ( reg 1 & 0x0000001e ) ^ 0xff000000 ]
  [ ct set mark with reg 1 ]

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

index 4178be4e1d6f422b04570756a0e3a6a0a9f2f067..c4ddb007ef4412bb7b7a55a338e0de84f1dff4fd 100644 (file)
@@ -1326,13 +1326,32 @@ static int expr_evaluate_shift(struct eval_ctx *ctx, struct expr **expr)
 static int expr_evaluate_bitwise(struct eval_ctx *ctx, struct expr **expr)
 {
        struct expr *op = *expr, *left = op->left;
+       const struct datatype *dtype;
+       unsigned int max_len;
+       int byteorder;
+
+       if (ctx->stmt_len > left->len) {
+               max_len = ctx->stmt_len;
+               byteorder = BYTEORDER_HOST_ENDIAN;
+               dtype = &integer_type;
+
+               /* Both sides need to be in host byte order */
+               if (byteorder_conversion(ctx, &op->left, BYTEORDER_HOST_ENDIAN) < 0)
+                       return -1;
+
+               left = op->left;
+       } else {
+               max_len = left->len;
+               byteorder = left->byteorder;
+               dtype = left->dtype;
+       }
 
-       if (byteorder_conversion(ctx, &op->right, left->byteorder) < 0)
+       if (byteorder_conversion(ctx, &op->right, byteorder) < 0)
                return -1;
 
-       op->dtype     = left->dtype;
-       op->byteorder = left->byteorder;
-       op->len       = left->len;
+       datatype_set(op, dtype);
+       op->byteorder = byteorder;
+       op->len       = max_len;
 
        if (expr_is_constant(left))
                return constant_binop_simplify(ctx, expr);