Blame SOURCES/0020-evaluate-set-eval-ctx-for-add-update-statements-with.patch

1d03cd
From 5a23da04e253a012adc877a82fcdec5e2f4a1534 Mon Sep 17 00:00:00 2001
1d03cd
From: Phil Sutter <psutter@redhat.com>
1d03cd
Date: Thu, 9 Feb 2023 12:45:30 +0100
1d03cd
Subject: [PATCH] evaluate: set eval ctx for add/update statements with integer
1d03cd
 constants
1d03cd
1d03cd
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2094894
1d03cd
Upstream Status: nftables commit 4cc6b20d31498
1d03cd
1d03cd
commit 4cc6b20d31498d90e90ff574ce8b70276afcee8f
1d03cd
Author: Florian Westphal <fw@strlen.de>
1d03cd
Date:   Mon Jan 23 19:03:28 2023 +0100
1d03cd
1d03cd
    evaluate: set eval ctx for add/update statements with integer constants
1d03cd
1d03cd
    Eric reports that nft asserts when using integer basetype constants with
1d03cd
    'typeof' sets.  Example:
1d03cd
    table netdev t {
1d03cd
            set s {
1d03cd
                    typeof ether saddr . vlan id
1d03cd
                    flags dynamic,timeout
1d03cd
            }
1d03cd
1d03cd
            chain c { }
1d03cd
    }
1d03cd
1d03cd
    loads fine.  But adding a rule with add/update statement fails:
1d03cd
    nft 'add rule netdev t c set update ether saddr . 0 @s'
1d03cd
    nft: netlink_linearize.c:867: netlink_gen_expr: Assertion `dreg < ctx->reg_low' failed.
1d03cd
1d03cd
    When the 'ether saddr . 0' concat expression is processed, there is
1d03cd
    no set definition available anymore to deduce the required size of the
1d03cd
    integer constant.
1d03cd
1d03cd
    nft eval step then derives the required length using the data types.
1d03cd
    '0' has integer basetype, so the deduced length is 0.
1d03cd
1d03cd
    The assertion triggers because serialization step finds that it
1d03cd
    needs one more register.
1d03cd
1d03cd
    2 are needed to store the ethernet address, another register is
1d03cd
    needed for the vlan id.
1d03cd
1d03cd
    Update eval step to make the expression context store the set key
1d03cd
    information when processing the preceeding set reference, then
1d03cd
    let stmt_evaluate_set() preserve the  existing context instead of
1d03cd
    zeroing it again via stmt_evaluate_arg().
1d03cd
1d03cd
    This makes concat expression evaluation compute the total size
1d03cd
    needed based on the sets key definition.
1d03cd
1d03cd
    Reported-by: Eric Garver <eric@garver.life>
1d03cd
    Signed-off-by: Florian Westphal <fw@strlen.de>
1d03cd
1d03cd
Signed-off-by: Phil Sutter <psutter@redhat.com>
1d03cd
---
1d03cd
 src/evaluate.c                                | 32 +++++++++++++++++--
1d03cd
 .../maps/dumps/typeof_maps_concat.nft         | 11 +++++++
1d03cd
 tests/shell/testcases/maps/typeof_maps_concat |  6 ++++
1d03cd
 .../sets/dumps/typeof_sets_concat.nft         | 12 +++++++
1d03cd
 tests/shell/testcases/sets/typeof_sets_concat |  6 ++++
1d03cd
 5 files changed, 65 insertions(+), 2 deletions(-)
1d03cd
 create mode 100644 tests/shell/testcases/maps/dumps/typeof_maps_concat.nft
1d03cd
 create mode 100755 tests/shell/testcases/maps/typeof_maps_concat
1d03cd
 create mode 100644 tests/shell/testcases/sets/dumps/typeof_sets_concat.nft
1d03cd
 create mode 100755 tests/shell/testcases/sets/typeof_sets_concat
1d03cd
1d03cd
diff --git a/src/evaluate.c b/src/evaluate.c
1d03cd
index d67f915..7f81411 100644
1d03cd
--- a/src/evaluate.c
1d03cd
+++ b/src/evaluate.c
1d03cd
@@ -1526,6 +1526,14 @@ static int interval_set_eval(struct eval_ctx *ctx, struct set *set,
1d03cd
 	return ret;
1d03cd
 }
1d03cd
 
1d03cd
+static void expr_evaluate_set_ref(struct eval_ctx *ctx, struct expr *expr)
1d03cd
+{
1d03cd
+	struct set *set = expr->set;
1d03cd
+
1d03cd
+	expr_set_context(&ctx->ectx, set->key->dtype, set->key->len);
1d03cd
+	ctx->ectx.key = set->key;
1d03cd
+}
1d03cd
+
1d03cd
 static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
1d03cd
 {
1d03cd
 	struct expr *set = *expr, *i, *next;
1d03cd
@@ -2388,6 +2396,7 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
1d03cd
 	case EXPR_VARIABLE:
1d03cd
 		return expr_evaluate_variable(ctx, expr);
1d03cd
 	case EXPR_SET_REF:
1d03cd
+		expr_evaluate_set_ref(ctx, *expr);
1d03cd
 		return 0;
1d03cd
 	case EXPR_VALUE:
1d03cd
 		return expr_evaluate_value(ctx, expr);
1d03cd
@@ -2550,6 +2559,25 @@ static int stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt,
1d03cd
 	return __stmt_evaluate_arg(ctx, stmt, dtype, len, byteorder, expr);
1d03cd
 }
1d03cd
 
1d03cd
+/* like stmt_evaluate_arg, but keep existing context created
1d03cd
+ * by previous expr_evaluate().
1d03cd
+ *
1d03cd
+ * This is needed for add/update statements:
1d03cd
+ * ctx->ectx.key has the set key, which may be needed for 'typeof'
1d03cd
+ * sets: the 'add/update' expression might contain integer data types.
1d03cd
+ *
1d03cd
+ * Without the key we cannot derive the element size.
1d03cd
+ */
1d03cd
+static int stmt_evaluate_key(struct eval_ctx *ctx, struct stmt *stmt,
1d03cd
+			     const struct datatype *dtype, unsigned int len,
1d03cd
+			     enum byteorder byteorder, struct expr **expr)
1d03cd
+{
1d03cd
+	if (expr_evaluate(ctx, expr) < 0)
1d03cd
+		return -1;
1d03cd
+
1d03cd
+	return __stmt_evaluate_arg(ctx, stmt, dtype, len, byteorder, expr);
1d03cd
+}
1d03cd
+
1d03cd
 static int stmt_evaluate_verdict(struct eval_ctx *ctx, struct stmt *stmt)
1d03cd
 {
1d03cd
 	if (stmt_evaluate_arg(ctx, stmt, &verdict_type, 0, 0, &stmt->expr) < 0)
1d03cd
@@ -3762,7 +3790,7 @@ static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
1d03cd
 		return expr_error(ctx->msgs, stmt->set.set,
1d03cd
 				  "Expression does not refer to a set");
1d03cd
 
1d03cd
-	if (stmt_evaluate_arg(ctx, stmt,
1d03cd
+	if (stmt_evaluate_key(ctx, stmt,
1d03cd
 			      stmt->set.set->set->key->dtype,
1d03cd
 			      stmt->set.set->set->key->len,
1d03cd
 			      stmt->set.set->set->key->byteorder,
1d03cd
@@ -3805,7 +3833,7 @@ static int stmt_evaluate_map(struct eval_ctx *ctx, struct stmt *stmt)
1d03cd
 		return expr_error(ctx->msgs, stmt->map.set,
1d03cd
 				  "Expression does not refer to a set");
1d03cd
 
1d03cd
-	if (stmt_evaluate_arg(ctx, stmt,
1d03cd
+	if (stmt_evaluate_key(ctx, stmt,
1d03cd
 			      stmt->map.set->set->key->dtype,
1d03cd
 			      stmt->map.set->set->key->len,
1d03cd
 			      stmt->map.set->set->key->byteorder,
1d03cd
diff --git a/tests/shell/testcases/maps/dumps/typeof_maps_concat.nft b/tests/shell/testcases/maps/dumps/typeof_maps_concat.nft
1d03cd
new file mode 100644
1d03cd
index 0000000..1ca98d8
1d03cd
--- /dev/null
1d03cd
+++ b/tests/shell/testcases/maps/dumps/typeof_maps_concat.nft
1d03cd
@@ -0,0 +1,11 @@
1d03cd
+table netdev t {
1d03cd
+	map m {
1d03cd
+		typeof ether saddr . vlan id : meta mark
1d03cd
+		size 1234
1d03cd
+		flags dynamic,timeout
1d03cd
+	}
1d03cd
+
1d03cd
+	chain c {
1d03cd
+		ether type != 8021q update @m { ether daddr . 123 timeout 1m : 0x0000002a } counter packets 0 bytes 0 return
1d03cd
+	}
1d03cd
+}
1d03cd
diff --git a/tests/shell/testcases/maps/typeof_maps_concat b/tests/shell/testcases/maps/typeof_maps_concat
1d03cd
new file mode 100755
1d03cd
index 0000000..07820b7
1d03cd
--- /dev/null
1d03cd
+++ b/tests/shell/testcases/maps/typeof_maps_concat
1d03cd
@@ -0,0 +1,6 @@
1d03cd
+#!/bin/bash
1d03cd
+
1d03cd
+set -e
1d03cd
+dumpfile=$(dirname $0)/dumps/$(basename $0).nft
1d03cd
+
1d03cd
+$NFT -f "$dumpfile"
1d03cd
diff --git a/tests/shell/testcases/sets/dumps/typeof_sets_concat.nft b/tests/shell/testcases/sets/dumps/typeof_sets_concat.nft
1d03cd
new file mode 100644
1d03cd
index 0000000..dbaf7cd
1d03cd
--- /dev/null
1d03cd
+++ b/tests/shell/testcases/sets/dumps/typeof_sets_concat.nft
1d03cd
@@ -0,0 +1,12 @@
1d03cd
+table netdev t {
1d03cd
+	set s {
1d03cd
+		typeof ether saddr . vlan id
1d03cd
+		size 2048
1d03cd
+		flags dynamic,timeout
1d03cd
+	}
1d03cd
+
1d03cd
+	chain c {
1d03cd
+		ether type != 8021q add @s { ether saddr . 0 timeout 5s } counter packets 0 bytes 0 return
1d03cd
+		ether type != 8021q update @s { ether daddr . 123 timeout 1m } counter packets 0 bytes 0 return
1d03cd
+	}
1d03cd
+}
1d03cd
diff --git a/tests/shell/testcases/sets/typeof_sets_concat b/tests/shell/testcases/sets/typeof_sets_concat
1d03cd
new file mode 100755
1d03cd
index 0000000..07820b7
1d03cd
--- /dev/null
1d03cd
+++ b/tests/shell/testcases/sets/typeof_sets_concat
1d03cd
@@ -0,0 +1,6 @@
1d03cd
+#!/bin/bash
1d03cd
+
1d03cd
+set -e
1d03cd
+dumpfile=$(dirname $0)/dumps/$(basename $0).nft
1d03cd
+
1d03cd
+$NFT -f "$dumpfile"
1d03cd
-- 
1d03cd
2.39.1
1d03cd