--- /dev/null
+From stable-owner@vger.kernel.org Tue Nov 21 12:14:05 2023
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 21 Nov 2023 13:13:32 +0100
+Subject: netfilter: nf_tables: disable toggling dormant table state more than once
+To: netfilter-devel@vger.kernel.org
+Cc: gregkh@linuxfoundation.org, sashal@kernel.org, stable@vger.kernel.org
+Message-ID: <20231121121333.294238-26-pablo@netfilter.org>
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+commit c9bd26513b3a11b3adb3c2ed8a31a01a87173ff1 upstream.
+
+nft -f -<<EOF
+add table ip t
+add table ip t { flags dormant; }
+add chain ip t c { type filter hook input priority 0; }
+add table ip t
+EOF
+
+Triggers a splat from nf core on next table delete because we lose
+track of right hook register state:
+
+WARNING: CPU: 2 PID: 1597 at net/netfilter/core.c:501 __nf_unregister_net_hook
+RIP: 0010:__nf_unregister_net_hook+0x41b/0x570
+ nf_unregister_net_hook+0xb4/0xf0
+ __nf_tables_unregister_hook+0x160/0x1d0
+[..]
+
+The above should have table in *active* state, but in fact no
+hooks were registered.
+
+Reject on/off/on games rather than attempting to fix this.
+
+Fixes: 179d9ba5559a ("netfilter: nf_tables: fix table flag updates")
+Reported-by: "Lee, Cherie-Anne" <cherie.lee@starlabs.sg>
+Cc: Bing-Jhong Billy Jheng <billy@starlabs.sg>
+Cc: info@starlabs.sg
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/netfilter/nf_tables_api.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -1099,6 +1099,10 @@ static int nf_tables_updtable(struct nft
+ if (flags == ctx->table->flags)
+ return 0;
+
++ /* No dormant off/on/off/on games in single transaction */
++ if (ctx->table->flags & __NFT_TABLE_F_UPDATE)
++ return -EINVAL;
++
+ trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE,
+ sizeof(struct nft_trans_table));
+ if (trans == NULL)
--- /dev/null
+From stable-owner@vger.kernel.org Tue Nov 21 12:14:05 2023
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 21 Nov 2023 13:13:31 +0100
+Subject: netfilter: nf_tables: fix table flag updates
+To: netfilter-devel@vger.kernel.org
+Cc: gregkh@linuxfoundation.org, sashal@kernel.org, stable@vger.kernel.org
+Message-ID: <20231121121333.294238-25-pablo@netfilter.org>
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+commit 179d9ba5559a756f4322583388b3213fe4e391b0 upstream.
+
+The dormant flag need to be updated from the preparation phase,
+otherwise, two consecutive requests to dorm a table in the same batch
+might try to remove the same hooks twice, resulting in the following
+warning:
+
+ hook not found, pf 3 num 0
+ WARNING: CPU: 0 PID: 334 at net/netfilter/core.c:480 __nf_unregister_net_hook+0x1eb/0x610 net/netfilter/core.c:480
+ Modules linked in:
+ CPU: 0 PID: 334 Comm: kworker/u4:5 Not tainted 5.12.0-syzkaller #0
+ Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
+ Workqueue: netns cleanup_net
+ RIP: 0010:__nf_unregister_net_hook+0x1eb/0x610 net/netfilter/core.c:480
+
+This patch is a partial revert of 0ce7cf4127f1 ("netfilter: nftables:
+update table flags from the commit phase") to restore the previous
+behaviour.
+
+However, there is still another problem: A batch containing a series of
+dorm-wakeup-dorm table and vice-versa also trigger the warning above
+since hook unregistration happens from the preparation phase, while hook
+registration occurs from the commit phase.
+
+To fix this problem, this patch adds two internal flags to annotate the
+original dormant flag status which are __NFT_TABLE_F_WAS_DORMANT and
+__NFT_TABLE_F_WAS_AWAKEN, to restore it from the abort path.
+
+The __NFT_TABLE_F_UPDATE bitmask allows to handle the dormant flag update
+with one single transaction.
+
+Reported-by: syzbot+7ad5cd1615f2d89c6e7e@syzkaller.appspotmail.com
+Fixes: 0ce7cf4127f1 ("netfilter: nftables: update table flags from the commit phase")
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/netfilter/nf_tables.h | 6 ---
+ include/uapi/linux/netfilter/nf_tables.h | 1
+ net/netfilter/nf_tables_api.c | 59 +++++++++++++++++++++----------
+ 3 files changed, 41 insertions(+), 25 deletions(-)
+
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -1479,16 +1479,10 @@ struct nft_trans_chain {
+
+ struct nft_trans_table {
+ bool update;
+- u8 state;
+- u32 flags;
+ };
+
+ #define nft_trans_table_update(trans) \
+ (((struct nft_trans_table *)trans->data)->update)
+-#define nft_trans_table_state(trans) \
+- (((struct nft_trans_table *)trans->data)->state)
+-#define nft_trans_table_flags(trans) \
+- (((struct nft_trans_table *)trans->data)->flags)
+
+ struct nft_trans_elem {
+ struct nft_set *set;
+--- a/include/uapi/linux/netfilter/nf_tables.h
++++ b/include/uapi/linux/netfilter/nf_tables.h
+@@ -165,6 +165,7 @@ enum nft_hook_attributes {
+ enum nft_table_flags {
+ NFT_TABLE_F_DORMANT = 0x1,
+ };
++#define NFT_TABLE_F_MASK (NFT_TABLE_F_DORMANT)
+
+ /**
+ * enum nft_table_attributes - nf_tables table netlink attributes
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -863,7 +863,8 @@ static int nf_tables_fill_table_info(str
+ goto nla_put_failure;
+
+ if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) ||
+- nla_put_be32(skb, NFTA_TABLE_FLAGS, htonl(table->flags)) ||
++ nla_put_be32(skb, NFTA_TABLE_FLAGS,
++ htonl(table->flags & NFT_TABLE_F_MASK)) ||
+ nla_put_be32(skb, NFTA_TABLE_USE, htonl(table->use)) ||
+ nla_put_be64(skb, NFTA_TABLE_HANDLE, cpu_to_be64(table->handle),
+ NFTA_TABLE_PAD))
+@@ -1071,20 +1072,22 @@ err_register_hooks:
+
+ static void nf_tables_table_disable(struct net *net, struct nft_table *table)
+ {
++ table->flags &= ~NFT_TABLE_F_DORMANT;
+ nft_table_disable(net, table, 0);
++ table->flags |= NFT_TABLE_F_DORMANT;
+ }
+
+-enum {
+- NFT_TABLE_STATE_UNCHANGED = 0,
+- NFT_TABLE_STATE_DORMANT,
+- NFT_TABLE_STATE_WAKEUP
+-};
++#define __NFT_TABLE_F_INTERNAL (NFT_TABLE_F_MASK + 1)
++#define __NFT_TABLE_F_WAS_DORMANT (__NFT_TABLE_F_INTERNAL << 0)
++#define __NFT_TABLE_F_WAS_AWAKEN (__NFT_TABLE_F_INTERNAL << 1)
++#define __NFT_TABLE_F_UPDATE (__NFT_TABLE_F_WAS_DORMANT | \
++ __NFT_TABLE_F_WAS_AWAKEN)
+
+ static int nf_tables_updtable(struct nft_ctx *ctx)
+ {
+ struct nft_trans *trans;
+ u32 flags;
+- int ret = 0;
++ int ret;
+
+ if (!ctx->nla[NFTA_TABLE_FLAGS])
+ return 0;
+@@ -1103,21 +1106,27 @@ static int nf_tables_updtable(struct nft
+
+ if ((flags & NFT_TABLE_F_DORMANT) &&
+ !(ctx->table->flags & NFT_TABLE_F_DORMANT)) {
+- nft_trans_table_state(trans) = NFT_TABLE_STATE_DORMANT;
++ ctx->table->flags |= NFT_TABLE_F_DORMANT;
++ if (!(ctx->table->flags & __NFT_TABLE_F_UPDATE))
++ ctx->table->flags |= __NFT_TABLE_F_WAS_AWAKEN;
+ } else if (!(flags & NFT_TABLE_F_DORMANT) &&
+ ctx->table->flags & NFT_TABLE_F_DORMANT) {
+- ret = nf_tables_table_enable(ctx->net, ctx->table);
+- if (ret >= 0)
+- nft_trans_table_state(trans) = NFT_TABLE_STATE_WAKEUP;
++ ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
++ if (!(ctx->table->flags & __NFT_TABLE_F_UPDATE)) {
++ ret = nf_tables_table_enable(ctx->net, ctx->table);
++ if (ret < 0)
++ goto err_register_hooks;
++
++ ctx->table->flags |= __NFT_TABLE_F_WAS_DORMANT;
++ }
+ }
+- if (ret < 0)
+- goto err;
+
+- nft_trans_table_flags(trans) = flags;
+ nft_trans_table_update(trans) = true;
+ nft_trans_commit_list_add_tail(ctx->net, trans);
++
+ return 0;
+-err:
++
++err_register_hooks:
+ nft_trans_destroy(trans);
+ return ret;
+ }
+@@ -8479,10 +8488,14 @@ static int nf_tables_commit(struct net *
+ switch (trans->msg_type) {
+ case NFT_MSG_NEWTABLE:
+ if (nft_trans_table_update(trans)) {
+- if (nft_trans_table_state(trans) == NFT_TABLE_STATE_DORMANT)
++ if (!(trans->ctx.table->flags & __NFT_TABLE_F_UPDATE)) {
++ nft_trans_destroy(trans);
++ break;
++ }
++ if (trans->ctx.table->flags & NFT_TABLE_F_DORMANT)
+ nf_tables_table_disable(net, trans->ctx.table);
+
+- trans->ctx.table->flags = nft_trans_table_flags(trans);
++ trans->ctx.table->flags &= ~__NFT_TABLE_F_UPDATE;
+ } else {
+ nft_clear(net, trans->ctx.table);
+ }
+@@ -8731,9 +8744,17 @@ static int __nf_tables_abort(struct net
+ switch (trans->msg_type) {
+ case NFT_MSG_NEWTABLE:
+ if (nft_trans_table_update(trans)) {
+- if (nft_trans_table_state(trans) == NFT_TABLE_STATE_WAKEUP)
++ if (!(trans->ctx.table->flags & __NFT_TABLE_F_UPDATE)) {
++ nft_trans_destroy(trans);
++ break;
++ }
++ if (trans->ctx.table->flags & __NFT_TABLE_F_WAS_DORMANT) {
+ nf_tables_table_disable(net, trans->ctx.table);
+-
++ trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
++ } else if (trans->ctx.table->flags & __NFT_TABLE_F_WAS_AWAKEN) {
++ trans->ctx.table->flags &= ~NFT_TABLE_F_DORMANT;
++ }
++ trans->ctx.table->flags &= ~__NFT_TABLE_F_UPDATE;
+ nft_trans_destroy(trans);
+ } else {
+ list_del_rcu(&trans->ctx.table->list);
--- /dev/null
+From stable-owner@vger.kernel.org Tue Nov 21 12:13:57 2023
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 21 Nov 2023 13:13:30 +0100
+Subject: netfilter: nftables: update table flags from the commit phase
+To: netfilter-devel@vger.kernel.org
+Cc: gregkh@linuxfoundation.org, sashal@kernel.org, stable@vger.kernel.org
+Message-ID: <20231121121333.294238-24-pablo@netfilter.org>
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+commit 0ce7cf4127f14078ca598ba9700d813178a59409 upstream.
+
+Do not update table flags from the preparation phase. Store the flags
+update into the transaction, then update the flags from the commit
+phase.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/netfilter/nf_tables.h | 9 ++++++---
+ net/netfilter/nf_tables_api.c | 31 ++++++++++++++++---------------
+ 2 files changed, 22 insertions(+), 18 deletions(-)
+
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -1479,13 +1479,16 @@ struct nft_trans_chain {
+
+ struct nft_trans_table {
+ bool update;
+- bool enable;
++ u8 state;
++ u32 flags;
+ };
+
+ #define nft_trans_table_update(trans) \
+ (((struct nft_trans_table *)trans->data)->update)
+-#define nft_trans_table_enable(trans) \
+- (((struct nft_trans_table *)trans->data)->enable)
++#define nft_trans_table_state(trans) \
++ (((struct nft_trans_table *)trans->data)->state)
++#define nft_trans_table_flags(trans) \
++ (((struct nft_trans_table *)trans->data)->flags)
+
+ struct nft_trans_elem {
+ struct nft_set *set;
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -1074,6 +1074,12 @@ static void nf_tables_table_disable(stru
+ nft_table_disable(net, table, 0);
+ }
+
++enum {
++ NFT_TABLE_STATE_UNCHANGED = 0,
++ NFT_TABLE_STATE_DORMANT,
++ NFT_TABLE_STATE_WAKEUP
++};
++
+ static int nf_tables_updtable(struct nft_ctx *ctx)
+ {
+ struct nft_trans *trans;
+@@ -1097,19 +1103,17 @@ static int nf_tables_updtable(struct nft
+
+ if ((flags & NFT_TABLE_F_DORMANT) &&
+ !(ctx->table->flags & NFT_TABLE_F_DORMANT)) {
+- nft_trans_table_enable(trans) = false;
++ nft_trans_table_state(trans) = NFT_TABLE_STATE_DORMANT;
+ } else if (!(flags & NFT_TABLE_F_DORMANT) &&
+ ctx->table->flags & NFT_TABLE_F_DORMANT) {
+- ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
+ ret = nf_tables_table_enable(ctx->net, ctx->table);
+ if (ret >= 0)
+- nft_trans_table_enable(trans) = true;
+- else
+- ctx->table->flags |= NFT_TABLE_F_DORMANT;
++ nft_trans_table_state(trans) = NFT_TABLE_STATE_WAKEUP;
+ }
+ if (ret < 0)
+ goto err;
+
++ nft_trans_table_flags(trans) = flags;
+ nft_trans_table_update(trans) = true;
+ nft_trans_commit_list_add_tail(ctx->net, trans);
+ return 0;
+@@ -8475,11 +8479,10 @@ static int nf_tables_commit(struct net *
+ switch (trans->msg_type) {
+ case NFT_MSG_NEWTABLE:
+ if (nft_trans_table_update(trans)) {
+- if (!nft_trans_table_enable(trans)) {
+- nf_tables_table_disable(net,
+- trans->ctx.table);
+- trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
+- }
++ if (nft_trans_table_state(trans) == NFT_TABLE_STATE_DORMANT)
++ nf_tables_table_disable(net, trans->ctx.table);
++
++ trans->ctx.table->flags = nft_trans_table_flags(trans);
+ } else {
+ nft_clear(net, trans->ctx.table);
+ }
+@@ -8728,11 +8731,9 @@ static int __nf_tables_abort(struct net
+ switch (trans->msg_type) {
+ case NFT_MSG_NEWTABLE:
+ if (nft_trans_table_update(trans)) {
+- if (nft_trans_table_enable(trans)) {
+- nf_tables_table_disable(net,
+- trans->ctx.table);
+- trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
+- }
++ if (nft_trans_table_state(trans) == NFT_TABLE_STATE_WAKEUP)
++ nf_tables_table_disable(net, trans->ctx.table);
++
+ nft_trans_destroy(trans);
+ } else {
+ list_del_rcu(&trans->ctx.table->list);