[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1053816: marked as done (bullseye-pu: package nftables/0.9.8-3.1+deb11u2)



Your message dated Sat, 10 Feb 2024 13:02:55 +0000
with message-id <E1rYn0R-002xpN-Fb@coccia.debian.org>
and subject line Released with 11.9
has caused the Debian Bug report #1053816,
regarding bullseye-pu: package nftables/0.9.8-3.1+deb11u2
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
1053816: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1053816
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bullseye
User: release.debian.org@packages.debian.org
Usertags: pu
X-Debbugs-Cc: nftables@packages.debian.org,
Control: affects -1 + src:nftables

A version of this pu has already been accepted for Bookworm.  I have cribbed
liberally from the bookworm-pu bug report.

nftables bug: https://bugs.debian.org/1051592
bookworm-pu bug: https://bugs.debian.org/1052021

[ Reason ]
Timo Sigurdsson reported after the release of DSA 5492-1 for linux that
in his case nftables rules were not loaded anymore.

This was tracked down to a Linux change, 0ebc1064e487 ("netfilter:
nf_tables: disallow rule addition to bound chain via
NFTA_RULE_CHAIN_ID"), which is to address CVE-2023-4147, but uncovered
an issue with nftables releases before v1.0.7 upstream. nftables
generates incorrect bytecode, which is affected by this new kernel check
that rejects adding rules to bound chains.

Following https://lore.kernel.org/stable/ZP+bUpxJiFcmTWhy@calendula/ and
further discussion on the Linux kernel mailing-lists it seemed that this
had to be addressed in nftables itself.

[ Impact ]
Users which have such rules, running unpatched nftables but updated
the linux kernel due to address security fixes (and later to be
included in the point release as well) are left without loaded
nftables rules.

[ Tests ]
Explicit tests with the rules provided by Timo to verify they
correctly get loaded with updated nftables userland and the updated
kernel.

[ Risks ]
Pablo Neira Ayuso provided the series of commits required to address
the issue. They apply cleanly for the bullseye version.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in oldstable
  [x] the issue is verified as fixed in unstable

[ Changes ]
  * New patches to fix the problem described above.
  * I updated an existing patch to remove some fuzz (happy to drop this
    if you prefer).

[ Other info ]
diff -Nru nftables-0.9.8/debian/changelog nftables-0.9.8/debian/changelog
--- nftables-0.9.8/debian/changelog	2022-09-04 09:34:11.000000000 +0100
+++ nftables-0.9.8/debian/changelog	2023-10-10 21:28:38.000000000 +0100
@@ -1,3 +1,18 @@
+nftables (0.9.8-3.1+deb11u2) bullseye; urgency=medium
+
+  * d/p/rule_fix_for_potential_off-by-one_in_cmd_add_loc.patch: fix fuzz
+  * Fix incorrect bytecode generation hit with new kernel check that
+    rejects adding rules to bound chains
+
+    - cache: rename chain_htable to cache_chain_ht
+    - src: split chain list in table
+    - evaluate: init cmd pointer for new on-stack context
+    - rule: add helper function to expand chain rules into commands
+    - rule: expand standalone chain that contains rules
+    - src: expand table command before evaluation
+
+ -- Jeremy Sowden <jeremy@azazel.net>  Tue, 10 Oct 2023 21:28:38 +0100
+
 nftables (0.9.8-3.1+deb11u1) bullseye; urgency=medium
 
   * d/p/rule_fix_for_potential_off-by-one_in_cmd_add_loc.patch
diff -Nru nftables-0.9.8/debian/patches/cache-rename-chain_htable-to-cache_chain_ht.patch nftables-0.9.8/debian/patches/cache-rename-chain_htable-to-cache_chain_ht.patch
--- nftables-0.9.8/debian/patches/cache-rename-chain_htable-to-cache_chain_ht.patch	1970-01-01 01:00:00.000000000 +0100
+++ nftables-0.9.8/debian/patches/cache-rename-chain_htable-to-cache_chain_ht.patch	2023-10-10 21:28:38.000000000 +0100
@@ -0,0 +1,98 @@
+From 0a39091a75b6255422832126df4cbf73c86845cd Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Thu, 1 Apr 2021 22:18:29 +0200
+Subject: [PATCH nft 0.9.8] cache: rename chain_htable to cache_chain_ht
+
+upstream 3542e49cf539ecfcef6ef7c2d4befb7896ade2cd commit.
+
+Rename the hashtable chain that is used for fast cache lookups.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ include/rule.h | 4 ++--
+ src/cache.c    | 6 +++---
+ src/rule.c     | 6 +++---
+ 3 files changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/include/rule.h b/include/rule.h
+index 330a09aa77fa..43872db8947a 100644
+--- a/include/rule.h
++++ b/include/rule.h
+@@ -154,7 +154,7 @@ struct table {
+ 	struct handle		handle;
+ 	struct location		location;
+ 	struct scope		scope;
+-	struct list_head	*chain_htable;
++	struct list_head	*cache_chain_ht;
+ 	struct list_head	chains;
+ 	struct list_head	sets;
+ 	struct list_head	objs;
+@@ -220,7 +220,7 @@ struct hook_spec {
+  */
+ struct chain {
+ 	struct list_head	list;
+-	struct list_head	hlist;
++	struct list_head	cache_hlist;
+ 	struct handle		handle;
+ 	struct location		location;
+ 	unsigned int		refcnt;
+diff --git a/src/cache.c b/src/cache.c
+index ed2609008e22..7101b74160be 100644
+--- a/src/cache.c
++++ b/src/cache.c
+@@ -194,7 +194,7 @@ static int chain_cache_cb(struct nftnl_chain *nlc, void *arg)
+ 	if (chain->flags & CHAIN_F_BINDING) {
+ 		list_add_tail(&chain->list, &ctx->table->chain_bindings);
+ 	} else {
+-		list_add_tail(&chain->hlist, &ctx->table->chain_htable[hash]);
++		list_add_tail(&chain->cache_hlist, &ctx->table->cache_chain_ht[hash]);
+ 		list_add_tail(&chain->list, &ctx->table->chains);
+ 	}
+ 
+@@ -238,7 +238,7 @@ void chain_cache_add(struct chain *chain, struct table *table)
+ 	uint32_t hash;
+ 
+ 	hash = djb_hash(chain->handle.chain.name) % NFT_CACHE_HSIZE;
+-	list_add_tail(&chain->hlist, &table->chain_htable[hash]);
++	list_add_tail(&chain->cache_hlist, &table->cache_chain_ht[hash]);
+ 	list_add_tail(&chain->list, &table->chains);
+ }
+ 
+@@ -249,7 +249,7 @@ struct chain *chain_cache_find(const struct table *table,
+ 	uint32_t hash;
+ 
+ 	hash = djb_hash(handle->chain.name) % NFT_CACHE_HSIZE;
+-	list_for_each_entry(chain, &table->chain_htable[hash], hlist) {
++	list_for_each_entry(chain, &table->cache_chain_ht[hash], cache_hlist) {
+ 		if (!strcmp(chain->handle.chain.name, handle->chain.name))
+ 			return chain;
+ 	}
+diff --git a/src/rule.c b/src/rule.c
+index e4bb6bae276a..3b445851f657 100644
+--- a/src/rule.c
++++ b/src/rule.c
+@@ -1328,10 +1328,10 @@ struct table *table_alloc(void)
+ 	init_list_head(&table->scope.symbols);
+ 	table->refcnt = 1;
+ 
+-	table->chain_htable =
++	table->cache_chain_ht =
+ 		xmalloc(sizeof(struct list_head) * NFT_CACHE_HSIZE);
+ 	for (i = 0; i < NFT_CACHE_HSIZE; i++)
+-		init_list_head(&table->chain_htable[i]);
++		init_list_head(&table->cache_chain_ht[i]);
+ 
+ 	return table;
+ }
+@@ -1359,7 +1359,7 @@ void table_free(struct table *table)
+ 		obj_free(obj);
+ 	handle_free(&table->handle);
+ 	scope_release(&table->scope);
+-	xfree(table->chain_htable);
++	xfree(table->cache_chain_ht);
+ 	xfree(table);
+ }
+ 
+-- 
+2.30.2
+
diff -Nru nftables-0.9.8/debian/patches/evaluate-init-cmd-pointer-for-new-on-stack-context.patch nftables-0.9.8/debian/patches/evaluate-init-cmd-pointer-for-new-on-stack-context.patch
--- nftables-0.9.8/debian/patches/evaluate-init-cmd-pointer-for-new-on-stack-context.patch	1970-01-01 01:00:00.000000000 +0100
+++ nftables-0.9.8/debian/patches/evaluate-init-cmd-pointer-for-new-on-stack-context.patch	2023-10-10 21:28:38.000000000 +0100
@@ -0,0 +1,48 @@
+From 5af65a30a12396281c751e635509ab1d9363f4bc Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 4 Mar 2022 11:30:55 +0100
+Subject: [PATCH nft 0.9.8] evaluate: init cmd pointer for new on-stack
+ context
+
+upstream 4e718641397c876315a87db441afc53139863122 commit
+
+else, this will segfault when trying to print the
+"table 'x' doesn't exist" error message.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+---
+ src/evaluate.c                                   | 1 +
+ tests/shell/testcases/chains/0041chain_binding_0 | 6 ++++++
+ 2 files changed, 7 insertions(+)
+
+diff --git a/src/evaluate.c b/src/evaluate.c
+index c830dcdbd965..f546667131e1 100644
+--- a/src/evaluate.c
++++ b/src/evaluate.c
+@@ -3211,6 +3211,7 @@ static int stmt_evaluate_chain(struct eval_ctx *ctx, struct stmt *stmt)
+ 			struct eval_ctx rule_ctx = {
+ 				.nft	= ctx->nft,
+ 				.msgs	= ctx->msgs,
++				.cmd	= ctx->cmd,
+ 			};
+ 			struct handle h2 = {};
+ 
+diff --git a/tests/shell/testcases/chains/0041chain_binding_0 b/tests/shell/testcases/chains/0041chain_binding_0
+index 59bdbe9f0ba9..4b541bb55c30 100755
+--- a/tests/shell/testcases/chains/0041chain_binding_0
++++ b/tests/shell/testcases/chains/0041chain_binding_0
+@@ -1,5 +1,11 @@
+ #!/bin/bash
+ 
++# no table x, caused segfault in earlier nft releases
++$NFT insert rule inet x y handle 107 'goto { log prefix "MOO! "; }'
++if [ $? -ne 1 ]; then
++	exit 1
++fi
++
+ set -e
+ 
+ EXPECTED="table inet x {
+-- 
+2.30.2
+
diff -Nru nftables-0.9.8/debian/patches/rule-add-helper-function-to-expand-chain-rules-into-.patch nftables-0.9.8/debian/patches/rule-add-helper-function-to-expand-chain-rules-into-.patch
--- nftables-0.9.8/debian/patches/rule-add-helper-function-to-expand-chain-rules-into-.patch	1970-01-01 01:00:00.000000000 +0100
+++ nftables-0.9.8/debian/patches/rule-add-helper-function-to-expand-chain-rules-into-.patch	2023-10-10 21:28:38.000000000 +0100
@@ -0,0 +1,83 @@
+From 0f559011ee7e805df883be635b88396639fbb87e Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 12 Sep 2023 23:22:46 +0200
+Subject: [PATCH nft 0.9.8] rule: add helper function to expand chain rules
+ into commands
+
+upstream 784597a4ed63b9decb10d74fdb49a1b021e22728 commit.
+
+This patch adds a helper function to expand chain rules into commands.
+This comes in preparation for the follow up patch.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ src/rule.c | 39 ++++++++++++++++++++++-----------------
+ 1 file changed, 22 insertions(+), 17 deletions(-)
+
+diff --git a/src/rule.c b/src/rule.c
+index f76c27c9d091..3fbf4271accd 100644
+--- a/src/rule.c
++++ b/src/rule.c
+@@ -1503,6 +1503,25 @@ void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc)
+ 	cmd->num_attrs++;
+ }
+ 
++static void nft_cmd_expand_chain(struct chain *chain, struct list_head *new_cmds)
++{
++	struct rule *rule;
++	struct handle h;
++	struct cmd *new;
++
++	list_for_each_entry(rule, &chain->rules, list) {
++		memset(&h, 0, sizeof(h));
++		handle_merge(&h, &rule->handle);
++		if (chain->flags & CHAIN_F_BINDING) {
++			rule->handle.chain_id = chain->handle.chain_id;
++			rule->handle.chain.location = chain->location;
++		}
++		new = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &h,
++				&rule->location, rule_get(rule));
++		list_add_tail(&new->list, new_cmds);
++	}
++}
++
+ void nft_cmd_expand(struct cmd *cmd)
+ {
+ 	struct list_head new_cmds;
+@@ -1510,7 +1529,6 @@ void nft_cmd_expand(struct cmd *cmd)
+ 	struct flowtable *ft;
+ 	struct table *table;
+ 	struct chain *chain;
+-	struct rule *rule;
+ 	struct obj *obj;
+ 	struct cmd *new;
+ 	struct handle h;
+@@ -1555,22 +1573,9 @@ void nft_cmd_expand(struct cmd *cmd)
+ 					&ft->location, flowtable_get(ft));
+ 			list_add_tail(&new->list, &new_cmds);
+ 		}
+-		list_for_each_entry(chain, &table->chains, list) {
+-			list_for_each_entry(rule, &chain->rules, list) {
+-				memset(&h, 0, sizeof(h));
+-				handle_merge(&h, &rule->handle);
+-				if (chain->flags & CHAIN_F_BINDING) {
+-					rule->handle.chain_id =
+-						chain->handle.chain_id;
+-					rule->handle.chain.location =
+-						chain->location;
+-				}
+-				new = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &h,
+-						&rule->location,
+-						rule_get(rule));
+-				list_add_tail(&new->list, &new_cmds);
+-			}
+-		}
++		list_for_each_entry(chain, &table->chains, list)
++			nft_cmd_expand_chain(chain, &new_cmds);
++
+ 		list_splice(&new_cmds, &cmd->list);
+ 		break;
+ 	case CMD_OBJ_SET:
+-- 
+2.30.2
+
diff -Nru nftables-0.9.8/debian/patches/rule-expand-standalone-chain-that-contains-rules.patch nftables-0.9.8/debian/patches/rule-expand-standalone-chain-that-contains-rules.patch
--- nftables-0.9.8/debian/patches/rule-expand-standalone-chain-that-contains-rules.patch	1970-01-01 01:00:00.000000000 +0100
+++ nftables-0.9.8/debian/patches/rule-expand-standalone-chain-that-contains-rules.patch	2023-10-10 21:28:38.000000000 +0100
@@ -0,0 +1,116 @@
+From f03bc399d75ef724fcbed184f74fc306ca8ef324 Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Mon, 6 Feb 2023 15:28:41 +0100
+Subject: [PATCH nft 0.9.8] rule: expand standalone chain that contains
+ rules
+
+upstream 27c753e4a8d4744f479345e3f5e34cafef751602 commit.
+
+Otherwise rules that this chain contains are ignored when expressed
+using the following syntax:
+
+chain inet filter input2 {
+       type filter hook input priority filter; policy accept;
+       ip saddr 1.2.3.4 tcp dport { 22, 443, 123 } drop
+}
+
+When expanding the chain, remove the rule so the new CMD_OBJ_CHAIN
+case does not expand it again.
+
+Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1655
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ src/rule.c                                    | 15 +++++++++---
+ .../testcases/include/0020include_chain_0     | 23 +++++++++++++++++++
+ .../include/dumps/0020include_chain_0.nft     |  6 +++++
+ 3 files changed, 41 insertions(+), 3 deletions(-)
+ create mode 100755 tests/shell/testcases/include/0020include_chain_0
+ create mode 100644 tests/shell/testcases/include/dumps/0020include_chain_0.nft
+
+diff --git a/src/rule.c b/src/rule.c
+index 3fbf4271accd..9139418e1bf8 100644
+--- a/src/rule.c
++++ b/src/rule.c
+@@ -1505,11 +1505,12 @@ void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc)
+ 
+ static void nft_cmd_expand_chain(struct chain *chain, struct list_head *new_cmds)
+ {
+-	struct rule *rule;
++	struct rule *rule, *next;
+ 	struct handle h;
+ 	struct cmd *new;
+ 
+-	list_for_each_entry(rule, &chain->rules, list) {
++	list_for_each_entry_safe(rule, next, &chain->rules, list) {
++		list_del(&rule->list);
+ 		memset(&h, 0, sizeof(h));
+ 		handle_merge(&h, &rule->handle);
+ 		if (chain->flags & CHAIN_F_BINDING) {
+@@ -1517,7 +1518,7 @@ static void nft_cmd_expand_chain(struct chain *chain, struct list_head *new_cmds
+ 			rule->handle.chain.location = chain->location;
+ 		}
+ 		new = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &h,
+-				&rule->location, rule_get(rule));
++				&rule->location, rule);
+ 		list_add_tail(&new->list, new_cmds);
+ 	}
+ }
+@@ -1578,6 +1579,14 @@ void nft_cmd_expand(struct cmd *cmd)
+ 
+ 		list_splice(&new_cmds, &cmd->list);
+ 		break;
++	case CMD_OBJ_CHAIN:
++		chain = cmd->chain;
++		if (!chain || list_empty(&chain->rules))
++			break;
++
++		nft_cmd_expand_chain(chain, &new_cmds);
++		list_splice(&new_cmds, &cmd->list);
++		break;
+ 	case CMD_OBJ_SET:
+ 	case CMD_OBJ_MAP:
+ 		set = cmd->set;
+diff --git a/tests/shell/testcases/include/0020include_chain_0 b/tests/shell/testcases/include/0020include_chain_0
+new file mode 100755
+index 000000000000..2ff83c92fda8
+--- /dev/null
++++ b/tests/shell/testcases/include/0020include_chain_0
+@@ -0,0 +1,23 @@
++#!/bin/bash
++
++set -e
++
++tmpfile1=$(mktemp -p .)
++if [ ! -w $tmpfile1 ] ; then
++	echo "Failed to create tmp file" >&2
++	exit 0
++fi
++
++trap "rm -rf $tmpfile1" EXIT # cleanup if aborted
++
++RULESET="table inet filter { }
++include \"$tmpfile1\""
++
++RULESET2="chain inet filter input2 {
++	type filter hook input priority filter; policy accept;
++	ip saddr 1.2.3.4 tcp dport { 22, 443, 123 } drop
++}"
++
++echo "$RULESET2" > $tmpfile1
++
++$NFT -f - <<< $RULESET
+diff --git a/tests/shell/testcases/include/dumps/0020include_chain_0.nft b/tests/shell/testcases/include/dumps/0020include_chain_0.nft
+new file mode 100644
+index 000000000000..3ad6db14d2f5
+--- /dev/null
++++ b/tests/shell/testcases/include/dumps/0020include_chain_0.nft
+@@ -0,0 +1,6 @@
++table inet filter {
++	chain input2 {
++		type filter hook input priority filter; policy accept;
++		ip saddr 1.2.3.4 tcp dport { 22, 123, 443 } drop
++	}
++}
+-- 
+2.30.2
+
diff -Nru nftables-0.9.8/debian/patches/rule_fix_for_potential_off-by-one_in_cmd_add_loc.patch nftables-0.9.8/debian/patches/rule_fix_for_potential_off-by-one_in_cmd_add_loc.patch
--- nftables-0.9.8/debian/patches/rule_fix_for_potential_off-by-one_in_cmd_add_loc.patch	2022-09-04 09:34:11.000000000 +0100
+++ nftables-0.9.8/debian/patches/rule_fix_for_potential_off-by-one_in_cmd_add_loc.patch	2023-10-10 21:28:38.000000000 +0100
@@ -18,7 +18,7 @@
 index dbbe744e..92daf2f3 100644
 --- a/src/rule.c
 +++ b/src/rule.c
-@@ -1275,7 +1275,7 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
+@@ -1491,7 +1491,7 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
  
  void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc)
  {
diff -Nru nftables-0.9.8/debian/patches/series nftables-0.9.8/debian/patches/series
--- nftables-0.9.8/debian/patches/series	2022-09-04 09:34:11.000000000 +0100
+++ nftables-0.9.8/debian/patches/series	2023-10-10 21:28:38.000000000 +0100
@@ -1,2 +1,8 @@
 payload-check-icmp-dependency-before-removing-previo.patch
 rule_fix_for_potential_off-by-one_in_cmd_add_loc.patch
+cache-rename-chain_htable-to-cache_chain_ht.patch
+src-split-chain-list-in-table.patch
+evaluate-init-cmd-pointer-for-new-on-stack-context.patch
+rule-add-helper-function-to-expand-chain-rules-into-.patch
+rule-expand-standalone-chain-that-contains-rules.patch
+src-expand-table-command-before-evaluation.patch
diff -Nru nftables-0.9.8/debian/patches/src-expand-table-command-before-evaluation.patch nftables-0.9.8/debian/patches/src-expand-table-command-before-evaluation.patch
--- nftables-0.9.8/debian/patches/src-expand-table-command-before-evaluation.patch	1970-01-01 01:00:00.000000000 +0100
+++ nftables-0.9.8/debian/patches/src-expand-table-command-before-evaluation.patch	2023-10-10 21:28:38.000000000 +0100
@@ -0,0 +1,202 @@
+From 050e0b7a85016b733e1a59285df501d1c05eec0b Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 12 Sep 2023 23:25:27 +0200
+Subject: [PATCH nft 0.9.8] src: expand table command before evaluation
+
+upstream 3975430b12d97c92cdf03753342f2269153d5624 commit.
+
+The nested syntax notation results in one single table command which
+includes all other objects. This differs from the flat notation where
+there is usually one command per object.
+
+This patch adds a previous step to the evaluation phase to expand the
+objects that are contained in the table into independent commands, so
+both notations have similar representations.
+
+Remove the code to evaluate the nested representation in the evaluation
+phase since commands are independently evaluated after the expansion.
+
+The commands are expanded after the set element collapse step, in case
+that there is a long list of singleton element commands to be added to
+the set, to shorten the command list iteration.
+
+This approach also avoids interference with the object cache that is
+populated in the evaluation, which might refer to objects coming in the
+existing command list that is being processed.
+
+There is still a post_expand phase to detach the elements from the set
+which could be consolidated by updating the evaluation step to handle
+the CMD_OBJ_SETELEMS command type.
+
+This patch fixes 27c753e4a8d4 ("rule: expand standalone chain that
+contains rules") which broke rule addition/insertion by index because
+the expansion code after the evaluation messes up the cache.
+
+Fixes: 27c753e4a8d4 ("rule: expand standalone chain that contains rules")
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ include/rule.h    |  1 +
+ src/evaluate.c    | 39 ---------------------------------------
+ src/libnftables.c |  9 ++++++++-
+ src/rule.c        | 21 +++++++++++++++++++--
+ 4 files changed, 28 insertions(+), 42 deletions(-)
+
+--- a/include/rule.h
++++ b/include/rule.h
+@@ -717,6 +717,7 @@
+ 			     const struct handle *h, const struct location *loc,
+ 			     void *data);
+ extern void nft_cmd_expand(struct cmd *cmd);
++extern void nft_cmd_post_expand(struct cmd *cmd);
+ extern struct cmd *cmd_alloc_obj_ct(enum cmd_ops op, int type,
+ 				    const struct handle *h,
+ 				    const struct location *loc, struct obj *obj);
+--- a/src/evaluate.c
++++ b/src/evaluate.c
+@@ -4067,7 +4067,6 @@
+ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
+ {
+ 	struct table *table;
+-	struct rule *rule;
+ 
+ 	table = table_lookup_global(ctx);
+ 	if (table == NULL)
+@@ -4121,11 +4120,6 @@
+ 		}
+ 	}
+ 
+-	list_for_each_entry(rule, &chain->rules, list) {
+-		handle_merge(&rule->handle, &chain->handle);
+-		if (rule_evaluate(ctx, rule, CMD_INVALID) < 0)
+-			return -1;
+-	}
+ 	return 0;
+ }
+ 
+@@ -4182,11 +4176,6 @@
+ 
+ static int table_evaluate(struct eval_ctx *ctx, struct table *table)
+ {
+-	struct flowtable *ft;
+-	struct chain *chain;
+-	struct set *set;
+-	struct obj *obj;
+-
+ 	if (table_lookup(&ctx->cmd->handle, &ctx->nft->cache) == NULL) {
+ 		if (table == NULL) {
+ 			table = table_alloc();
+@@ -4197,34 +4186,6 @@
+ 		}
+ 	}
+ 
+-	if (ctx->cmd->table == NULL)
+-		return 0;
+-
+-	ctx->table = table;
+-	list_for_each_entry(set, &table->sets, list) {
+-		expr_set_context(&ctx->ectx, NULL, 0);
+-		handle_merge(&set->handle, &table->handle);
+-		if (set_evaluate(ctx, set) < 0)
+-			return -1;
+-	}
+-	list_for_each_entry(chain, &table->chains, list) {
+-		handle_merge(&chain->handle, &table->handle);
+-		ctx->cmd->handle.chain.location = chain->location;
+-		if (chain_evaluate(ctx, chain) < 0)
+-			return -1;
+-	}
+-	list_for_each_entry(ft, &table->flowtables, list) {
+-		handle_merge(&ft->handle, &table->handle);
+-		if (flowtable_evaluate(ctx, ft) < 0)
+-			return -1;
+-	}
+-	list_for_each_entry(obj, &table->objs, list) {
+-		handle_merge(&obj->handle, &table->handle);
+-		if (obj_evaluate(ctx, obj) < 0)
+-			return -1;
+-	}
+-
+-	ctx->table = NULL;
+ 	return 0;
+ }
+ 
+--- a/src/libnftables.c
++++ b/src/libnftables.c
+@@ -421,6 +421,13 @@
+ 		return -1;
+ 
+ 	list_for_each_entry(cmd, cmds, list) {
++		if (cmd->op != CMD_ADD)
++			continue;
++
++		nft_cmd_expand(cmd);
++	}
++
++	list_for_each_entry(cmd, cmds, list) {
+ 		struct eval_ctx ectx = {
+ 			.nft	= nft,
+ 			.msgs	= msgs,
+@@ -437,7 +444,7 @@
+ 		if (cmd->op != CMD_ADD)
+ 			continue;
+ 
+-		nft_cmd_expand(cmd);
++		nft_cmd_post_expand(cmd);
+ 	}
+ 
+ 	return 0;
+--- a/src/rule.c
++++ b/src/rule.c
+@@ -1511,8 +1511,9 @@
+ 
+ 	list_for_each_entry_safe(rule, next, &chain->rules, list) {
+ 		list_del(&rule->list);
++		handle_merge(&rule->handle, &chain->handle);
+ 		memset(&h, 0, sizeof(h));
+-		handle_merge(&h, &rule->handle);
++		handle_merge(&h, &chain->handle);
+ 		if (chain->flags & CHAIN_F_BINDING) {
+ 			rule->handle.chain_id = chain->handle.chain_id;
+ 			rule->handle.chain.location = chain->location;
+@@ -1526,10 +1527,10 @@
+ void nft_cmd_expand(struct cmd *cmd)
+ {
+ 	struct list_head new_cmds;
+-	struct set *set, *newset;
+ 	struct flowtable *ft;
+ 	struct table *table;
+ 	struct chain *chain;
++	struct set *set;
+ 	struct obj *obj;
+ 	struct cmd *new;
+ 	struct handle h;
+@@ -1543,6 +1544,7 @@
+ 			return;
+ 
+ 		list_for_each_entry(chain, &table->chains, list) {
++			handle_merge(&chain->handle, &table->handle);
+ 			memset(&h, 0, sizeof(h));
+ 			handle_merge(&h, &chain->handle);
+ 			h.chain_id = chain->handle.chain_id;
+@@ -1587,6 +1589,21 @@
+ 		nft_cmd_expand_chain(chain, &new_cmds);
+ 		list_splice(&new_cmds, &cmd->list);
+ 		break;
++	default:
++		break;
++	}
++}
++
++void nft_cmd_post_expand(struct cmd *cmd)
++{
++	struct list_head new_cmds;
++	struct set *set, *newset;
++	struct cmd *new;
++	struct handle h;
++
++	init_list_head(&new_cmds);
++
++	switch (cmd->obj) {
+ 	case CMD_OBJ_SET:
+ 	case CMD_OBJ_MAP:
+ 		set = cmd->set;
diff -Nru nftables-0.9.8/debian/patches/src-split-chain-list-in-table.patch nftables-0.9.8/debian/patches/src-split-chain-list-in-table.patch
--- nftables-0.9.8/debian/patches/src-split-chain-list-in-table.patch	1970-01-01 01:00:00.000000000 +0100
+++ nftables-0.9.8/debian/patches/src-split-chain-list-in-table.patch	2023-10-10 21:28:38.000000000 +0100
@@ -0,0 +1,188 @@
+From f37e4261130b021edf068e4d5c6ca062ce4e2ac1 Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Thu, 1 Apr 2021 22:19:30 +0200
+Subject: [PATCH nft 0.9.8] src: split chain list in table
+
+upstream a3ac2527724dd27628e12caaa55f731b109e4586 commit.
+
+This patch splits table->lists in two:
+
+- Chains that reside in the cache are stored in the new
+  tables->cache_chain and tables->cache_chain_ht. The hashtable chain
+  cache allows for fast chain lookups.
+
+- Chains that defined via command line / ruleset file reside in
+  tables->chains.
+
+Note that chains in the cache (already in the kernel) are not placed in
+the table->chains.
+
+By keeping separated lists, chains defined via command line / ruleset
+file can be added to cache.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ include/rule.h |  2 ++
+ src/cache.c    |  6 +++---
+ src/json.c     |  6 +++---
+ src/rule.c     | 18 +++++++++++-------
+ 4 files changed, 19 insertions(+), 13 deletions(-)
+
+diff --git a/include/rule.h b/include/rule.h
+index 43872db8947a..dde32367f48f 100644
+--- a/include/rule.h
++++ b/include/rule.h
+@@ -155,6 +155,7 @@ struct table {
+ 	struct location		location;
+ 	struct scope		scope;
+ 	struct list_head	*cache_chain_ht;
++	struct list_head	cache_chain;
+ 	struct list_head	chains;
+ 	struct list_head	sets;
+ 	struct list_head	objs;
+@@ -221,6 +222,7 @@ struct hook_spec {
+ struct chain {
+ 	struct list_head	list;
+ 	struct list_head	cache_hlist;
++	struct list_head	cache_list;
+ 	struct handle		handle;
+ 	struct location		location;
+ 	unsigned int		refcnt;
+diff --git a/src/cache.c b/src/cache.c
+index 7101b74160be..32e6eea4ac5c 100644
+--- a/src/cache.c
++++ b/src/cache.c
+@@ -192,10 +192,10 @@ static int chain_cache_cb(struct nftnl_chain *nlc, void *arg)
+ 	chain = netlink_delinearize_chain(ctx->nlctx, nlc);
+ 
+ 	if (chain->flags & CHAIN_F_BINDING) {
+-		list_add_tail(&chain->list, &ctx->table->chain_bindings);
++		list_add_tail(&chain->cache_list, &ctx->table->chain_bindings);
+ 	} else {
+ 		list_add_tail(&chain->cache_hlist, &ctx->table->cache_chain_ht[hash]);
+-		list_add_tail(&chain->list, &ctx->table->chains);
++		list_add_tail(&chain->cache_list, &ctx->table->cache_chain);
+ 	}
+ 
+ 	nftnl_chain_list_del(nlc);
+@@ -239,7 +239,7 @@ void chain_cache_add(struct chain *chain, struct table *table)
+ 
+ 	hash = djb_hash(chain->handle.chain.name) % NFT_CACHE_HSIZE;
+ 	list_add_tail(&chain->cache_hlist, &table->cache_chain_ht[hash]);
+-	list_add_tail(&chain->list, &table->chains);
++	list_add_tail(&chain->cache_list, &table->cache_chain);
+ }
+ 
+ struct chain *chain_cache_find(const struct table *table,
+diff --git a/src/json.c b/src/json.c
+index 585d35326ac0..737e73b08b5a 100644
+--- a/src/json.c
++++ b/src/json.c
+@@ -1594,7 +1594,7 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
+ 		tmp = flowtable_print_json(flowtable);
+ 		json_array_append_new(root, tmp);
+ 	}
+-	list_for_each_entry(chain, &table->chains, list) {
++	list_for_each_entry(chain, &table->cache_chain, cache_list) {
+ 		tmp = chain_print_json(chain);
+ 		json_array_append_new(root, tmp);
+ 
+@@ -1656,7 +1656,7 @@ static json_t *do_list_chain_json(struct netlink_ctx *ctx,
+ 	struct chain *chain;
+ 	struct rule *rule;
+ 
+-	list_for_each_entry(chain, &table->chains, list) {
++	list_for_each_entry(chain, &table->cache_chain, cache_list) {
+ 		if (chain->handle.family != cmd->handle.family ||
+ 		    strcmp(cmd->handle.chain.name, chain->handle.chain.name))
+ 			continue;
+@@ -1684,7 +1684,7 @@ static json_t *do_list_chains_json(struct netlink_ctx *ctx, struct cmd *cmd)
+ 		    cmd->handle.family != table->handle.family)
+ 			continue;
+ 
+-		list_for_each_entry(chain, &table->chains, list) {
++		list_for_each_entry(chain, &table->cache_chain, cache_list) {
+ 			json_t *tmp = chain_print_json(chain);
+ 
+ 			json_array_append_new(root, tmp);
+diff --git a/src/rule.c b/src/rule.c
+index 3b445851f657..f76c27c9d091 100644
+--- a/src/rule.c
++++ b/src/rule.c
+@@ -953,7 +953,7 @@ struct chain *chain_lookup(const struct table *table, const struct handle *h)
+ {
+ 	struct chain *chain;
+ 
+-	list_for_each_entry(chain, &table->chains, list) {
++	list_for_each_entry(chain, &table->cache_chain, cache_list) {
+ 		if (!strcmp(chain->handle.chain.name, h->chain.name))
+ 			return chain;
+ 	}
+@@ -965,7 +965,7 @@ struct chain *chain_binding_lookup(const struct table *table,
+ {
+ 	struct chain *chain;
+ 
+-	list_for_each_entry(chain, &table->chain_bindings, list) {
++	list_for_each_entry(chain, &table->chain_bindings, cache_list) {
+ 		if (!strcmp(chain->handle.chain.name, chain_name))
+ 			return chain;
+ 	}
+@@ -983,7 +983,7 @@ struct chain *chain_lookup_fuzzy(const struct handle *h,
+ 	string_misspell_init(&st);
+ 
+ 	list_for_each_entry(table, &cache->list, list) {
+-		list_for_each_entry(chain, &table->chains, list) {
++		list_for_each_entry(chain, &table->cache_chain, cache_list) {
+ 			if (!strcmp(chain->handle.chain.name, h->chain.name)) {
+ 				*t = table;
+ 				return chain;
+@@ -1321,6 +1321,7 @@ struct table *table_alloc(void)
+ 
+ 	table = xzalloc(sizeof(*table));
+ 	init_list_head(&table->chains);
++	init_list_head(&table->cache_chain);
+ 	init_list_head(&table->sets);
+ 	init_list_head(&table->objs);
+ 	init_list_head(&table->flowtables);
+@@ -1349,7 +1350,10 @@ void table_free(struct table *table)
+ 		xfree(table->comment);
+ 	list_for_each_entry_safe(chain, next, &table->chains, list)
+ 		chain_free(chain);
+-	list_for_each_entry_safe(chain, next, &table->chain_bindings, list)
++	list_for_each_entry_safe(chain, next, &table->chain_bindings, cache_list)
++		chain_free(chain);
++	/* this is implicitly releasing chains in the hashtable cache */
++	list_for_each_entry_safe(chain, next, &table->cache_chain, cache_list)
+ 		chain_free(chain);
+ 	list_for_each_entry_safe(set, nset, &table->sets, list)
+ 		set_free(set);
+@@ -1465,7 +1469,7 @@ static void table_print(const struct table *table, struct output_ctx *octx)
+ 		flowtable_print(flowtable, octx);
+ 		delim = "\n";
+ 	}
+-	list_for_each_entry(chain, &table->chains, list) {
++	list_for_each_entry(chain, &table->cache_chain, cache_list) {
+ 		nft_print(octx, "%s", delim);
+ 		chain_print(chain, octx);
+ 		delim = "\n";
+@@ -2555,7 +2559,7 @@ static int do_list_chain(struct netlink_ctx *ctx, struct cmd *cmd,
+ 
+ 	table_print_declaration(table, &ctx->nft->output);
+ 
+-	list_for_each_entry(chain, &table->chains, list) {
++	list_for_each_entry(chain, &table->cache_chain, cache_list) {
+ 		if (chain->handle.family != cmd->handle.family ||
+ 		    strcmp(cmd->handle.chain.name, chain->handle.chain.name) != 0)
+ 			continue;
+@@ -2580,7 +2584,7 @@ static int do_list_chains(struct netlink_ctx *ctx, struct cmd *cmd)
+ 
+ 		table_print_declaration(table, &ctx->nft->output);
+ 
+-		list_for_each_entry(chain, &table->chains, list) {
++		list_for_each_entry(chain, &table->cache_chain, cache_list) {
+ 			chain_print_declaration(chain, &ctx->nft->output);
+ 			nft_print(&ctx->nft->output, "\t}\n");
+ 		}
+-- 
+2.30.2
+

--- End Message ---
--- Begin Message ---
Version: 11.9

The upload requested in this bug has been released as part of 11.9.

--- End Message ---

Reply to: