--- /dev/null
+From 039413a400387eb691901b4309fa8730ae776efc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 Sep 2023 08:22:33 +0200
+Subject: netfilter: nf_tables: disallow rule removal from chain binding
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ Upstream commit f15f29fd4779be8a418b66e9d52979bb6d6c2325 ]
+
+Chain binding only requires the rule addition/insertion command within
+the same transaction. Removal of rules from chain bindings within the
+same transaction makes no sense, userspace does not utilize this
+feature. Replace nft_chain_is_bound() check to nft_chain_binding() in
+rule deletion commands. Replace command implies a rule deletion, reject
+this command too.
+
+Rule flush command can also safely rely on this nft_chain_binding()
+check because unbound chains are not allowed since 62e1e94b246e
+("netfilter: nf_tables: reject unbound chain set before commit phase").
+
+Fixes: d0e2c7de92c7 ("netfilter: nf_tables: add NFT_CHAIN_BINDING")
+Reported-by: Kevin Rich <kevinrich1337@gmail.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/nf_tables_api.c | 18 +++++++++++++-----
+ 1 file changed, 13 insertions(+), 5 deletions(-)
+
+diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
+index 1d6a37430ff6b..52b81dc1fcf5b 100644
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -1427,7 +1427,7 @@ static int nft_flush_table(struct nft_ctx *ctx)
+ if (!nft_is_active_next(ctx->net, chain))
+ continue;
+
+- if (nft_chain_is_bound(chain))
++ if (nft_chain_binding(chain))
+ continue;
+
+ ctx->chain = chain;
+@@ -1471,7 +1471,7 @@ static int nft_flush_table(struct nft_ctx *ctx)
+ if (!nft_is_active_next(ctx->net, chain))
+ continue;
+
+- if (nft_chain_is_bound(chain))
++ if (nft_chain_binding(chain))
+ continue;
+
+ ctx->chain = chain;
+@@ -2792,6 +2792,9 @@ static int nf_tables_delchain(struct sk_buff *skb, const struct nfnl_info *info,
+ return PTR_ERR(chain);
+ }
+
++ if (nft_chain_binding(chain))
++ return -EOPNOTSUPP;
++
+ if (info->nlh->nlmsg_flags & NLM_F_NONREC &&
+ chain->use > 0)
+ return -EBUSY;
+@@ -3771,6 +3774,11 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
+ }
+
+ if (info->nlh->nlmsg_flags & NLM_F_REPLACE) {
++ if (nft_chain_binding(chain)) {
++ err = -EOPNOTSUPP;
++ goto err_destroy_flow_rule;
++ }
++
+ err = nft_delrule(&ctx, old_rule);
+ if (err < 0)
+ goto err_destroy_flow_rule;
+@@ -3874,7 +3882,7 @@ static int nf_tables_delrule(struct sk_buff *skb, const struct nfnl_info *info,
+ NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]);
+ return PTR_ERR(chain);
+ }
+- if (nft_chain_is_bound(chain))
++ if (nft_chain_binding(chain))
+ return -EOPNOTSUPP;
+ }
+
+@@ -3904,7 +3912,7 @@ static int nf_tables_delrule(struct sk_buff *skb, const struct nfnl_info *info,
+ list_for_each_entry(chain, &table->chains, list) {
+ if (!nft_is_active_next(net, chain))
+ continue;
+- if (nft_chain_is_bound(chain))
++ if (nft_chain_binding(chain))
+ continue;
+
+ ctx.chain = chain;
+@@ -10664,7 +10672,7 @@ static void __nft_release_table(struct net *net, struct nft_table *table)
+ ctx.family = table->family;
+ ctx.table = table;
+ list_for_each_entry(chain, &table->chains, list) {
+- if (nft_chain_is_bound(chain))
++ if (nft_chain_binding(chain))
+ continue;
+
+ ctx.chain = chain;
+--
+2.40.1
+