Skip to content

Commit 179d9ba

Browse files
committed
netfilter: nf_tables: fix table flag updates
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 0ce7cf4 ("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: [email protected] Fixes: 0ce7cf4 ("netfilter: nftables: update table flags from the commit phase") Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 983c4fc commit 179d9ba

File tree

2 files changed

+40
-25
lines changed

2 files changed

+40
-25
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,16 +1506,10 @@ struct nft_trans_chain {
15061506

15071507
struct nft_trans_table {
15081508
bool update;
1509-
u8 state;
1510-
u32 flags;
15111509
};
15121510

15131511
#define nft_trans_table_update(trans) \
15141512
(((struct nft_trans_table *)trans->data)->update)
1515-
#define nft_trans_table_state(trans) \
1516-
(((struct nft_trans_table *)trans->data)->state)
1517-
#define nft_trans_table_flags(trans) \
1518-
(((struct nft_trans_table *)trans->data)->flags)
15191513

15201514
struct nft_trans_elem {
15211515
struct nft_set *set;

net/netfilter/nf_tables_api.c

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,8 @@ static int nf_tables_fill_table_info(struct sk_buff *skb, struct net *net,
736736
goto nla_put_failure;
737737

738738
if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) ||
739-
nla_put_be32(skb, NFTA_TABLE_FLAGS, htonl(table->flags)) ||
739+
nla_put_be32(skb, NFTA_TABLE_FLAGS,
740+
htonl(table->flags & NFT_TABLE_F_MASK)) ||
740741
nla_put_be32(skb, NFTA_TABLE_USE, htonl(table->use)) ||
741742
nla_put_be64(skb, NFTA_TABLE_HANDLE, cpu_to_be64(table->handle),
742743
NFTA_TABLE_PAD))
@@ -947,20 +948,22 @@ static int nf_tables_table_enable(struct net *net, struct nft_table *table)
947948

948949
static void nf_tables_table_disable(struct net *net, struct nft_table *table)
949950
{
951+
table->flags &= ~NFT_TABLE_F_DORMANT;
950952
nft_table_disable(net, table, 0);
953+
table->flags |= NFT_TABLE_F_DORMANT;
951954
}
952955

953-
enum {
954-
NFT_TABLE_STATE_UNCHANGED = 0,
955-
NFT_TABLE_STATE_DORMANT,
956-
NFT_TABLE_STATE_WAKEUP
957-
};
956+
#define __NFT_TABLE_F_INTERNAL (NFT_TABLE_F_MASK + 1)
957+
#define __NFT_TABLE_F_WAS_DORMANT (__NFT_TABLE_F_INTERNAL << 0)
958+
#define __NFT_TABLE_F_WAS_AWAKEN (__NFT_TABLE_F_INTERNAL << 1)
959+
#define __NFT_TABLE_F_UPDATE (__NFT_TABLE_F_WAS_DORMANT | \
960+
__NFT_TABLE_F_WAS_AWAKEN)
958961

959962
static int nf_tables_updtable(struct nft_ctx *ctx)
960963
{
961964
struct nft_trans *trans;
962965
u32 flags;
963-
int ret = 0;
966+
int ret;
964967

965968
if (!ctx->nla[NFTA_TABLE_FLAGS])
966969
return 0;
@@ -985,21 +988,27 @@ static int nf_tables_updtable(struct nft_ctx *ctx)
985988

986989
if ((flags & NFT_TABLE_F_DORMANT) &&
987990
!(ctx->table->flags & NFT_TABLE_F_DORMANT)) {
988-
nft_trans_table_state(trans) = NFT_TABLE_STATE_DORMANT;
991+
ctx->table->flags |= NFT_TABLE_F_DORMANT;
992+
if (!(ctx->table->flags & __NFT_TABLE_F_UPDATE))
993+
ctx->table->flags |= __NFT_TABLE_F_WAS_AWAKEN;
989994
} else if (!(flags & NFT_TABLE_F_DORMANT) &&
990995
ctx->table->flags & NFT_TABLE_F_DORMANT) {
991-
ret = nf_tables_table_enable(ctx->net, ctx->table);
992-
if (ret >= 0)
993-
nft_trans_table_state(trans) = NFT_TABLE_STATE_WAKEUP;
996+
ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
997+
if (!(ctx->table->flags & __NFT_TABLE_F_UPDATE)) {
998+
ret = nf_tables_table_enable(ctx->net, ctx->table);
999+
if (ret < 0)
1000+
goto err_register_hooks;
1001+
1002+
ctx->table->flags |= __NFT_TABLE_F_WAS_DORMANT;
1003+
}
9941004
}
995-
if (ret < 0)
996-
goto err;
9971005

998-
nft_trans_table_flags(trans) = flags;
9991006
nft_trans_table_update(trans) = true;
10001007
nft_trans_commit_list_add_tail(ctx->net, trans);
1008+
10011009
return 0;
1002-
err:
1010+
1011+
err_register_hooks:
10031012
nft_trans_destroy(trans);
10041013
return ret;
10051014
}
@@ -8556,10 +8565,14 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
85568565
switch (trans->msg_type) {
85578566
case NFT_MSG_NEWTABLE:
85588567
if (nft_trans_table_update(trans)) {
8559-
if (nft_trans_table_state(trans) == NFT_TABLE_STATE_DORMANT)
8568+
if (!(trans->ctx.table->flags & __NFT_TABLE_F_UPDATE)) {
8569+
nft_trans_destroy(trans);
8570+
break;
8571+
}
8572+
if (trans->ctx.table->flags & NFT_TABLE_F_DORMANT)
85608573
nf_tables_table_disable(net, trans->ctx.table);
85618574

8562-
trans->ctx.table->flags = nft_trans_table_flags(trans);
8575+
trans->ctx.table->flags &= ~__NFT_TABLE_F_UPDATE;
85638576
} else {
85648577
nft_clear(net, trans->ctx.table);
85658578
}
@@ -8777,9 +8790,17 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
87778790
switch (trans->msg_type) {
87788791
case NFT_MSG_NEWTABLE:
87798792
if (nft_trans_table_update(trans)) {
8780-
if (nft_trans_table_state(trans) == NFT_TABLE_STATE_WAKEUP)
8793+
if (!(trans->ctx.table->flags & __NFT_TABLE_F_UPDATE)) {
8794+
nft_trans_destroy(trans);
8795+
break;
8796+
}
8797+
if (trans->ctx.table->flags & __NFT_TABLE_F_WAS_DORMANT) {
87818798
nf_tables_table_disable(net, trans->ctx.table);
8782-
8799+
trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
8800+
} else if (trans->ctx.table->flags & __NFT_TABLE_F_WAS_AWAKEN) {
8801+
trans->ctx.table->flags &= ~NFT_TABLE_F_DORMANT;
8802+
}
8803+
trans->ctx.table->flags &= ~__NFT_TABLE_F_UPDATE;
87838804
nft_trans_destroy(trans);
87848805
} else {
87858806
list_del_rcu(&trans->ctx.table->list);

0 commit comments

Comments
 (0)