netfilter: nf_tables: deactivate anonymous set from preparation phase
authorPablo Neira Ayuso <pablo@netfilter.org>
Tue, 2 May 2023 08:25:24 +0000 (10:25 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 3 May 2023 06:24:32 +0000 (08:24 +0200)
Toggle deleted anonymous sets as inactive in the next generation, so
users cannot perform any update on it. Clear the generation bitmask
in case the transaction is aborted.

The following KASAN splat shows a set element deletion for a bound
anonymous set that has been already removed in the same transaction.

[   64.921510] ==================================================================
[   64.923123] BUG: KASAN: wild-memory-access in nf_tables_commit+0xa24/0x1490 [nf_tables]
[   64.924745] Write of size 8 at addr dead000000000122 by task test/890
[   64.927903] CPU: 3 PID: 890 Comm: test Not tainted 6.3.0+ #253
[   64.931120] Call Trace:
[   64.932699]  <TASK>
[   64.934292]  dump_stack_lvl+0x33/0x50
[   64.935908]  ? nf_tables_commit+0xa24/0x1490 [nf_tables]
[   64.937551]  kasan_report+0xda/0x120
[   64.939186]  ? nf_tables_commit+0xa24/0x1490 [nf_tables]
[   64.940814]  nf_tables_commit+0xa24/0x1490 [nf_tables]
[   64.942452]  ? __kasan_slab_alloc+0x2d/0x60
[   64.944070]  ? nf_tables_setelem_notify+0x190/0x190 [nf_tables]
[   64.945710]  ? kasan_set_track+0x21/0x30
[   64.947323]  nfnetlink_rcv_batch+0x709/0xd90 [nfnetlink]
[   64.948898]  ? nfnetlink_rcv_msg+0x480/0x480 [nfnetlink]

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/net/netfilter/nf_tables.h
net/netfilter/nf_tables_api.c
net/netfilter/nft_dynset.c
net/netfilter/nft_lookup.c
net/netfilter/nft_objref.c

index 3ed21d2d56590135c56995ead77bae276631aa17..2e24ea1d744c25e7dc5c3550d36f33d733c581a2 100644 (file)
@@ -619,6 +619,7 @@ struct nft_set_binding {
 };
 
 enum nft_trans_phase;
+void nf_tables_activate_set(const struct nft_ctx *ctx, struct nft_set *set);
 void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set,
                              struct nft_set_binding *binding,
                              enum nft_trans_phase phase);
index 8b6c61a2196cb2c2cd16c512e44cf99e7a12b65b..59fb8320ab4d7745ca08e5ffe58cf18c8c196487 100644 (file)
@@ -5127,12 +5127,24 @@ static void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
        }
 }
 
+void nf_tables_activate_set(const struct nft_ctx *ctx, struct nft_set *set)
+{
+       if (nft_set_is_anonymous(set))
+               nft_clear(ctx->net, set);
+
+       set->use++;
+}
+EXPORT_SYMBOL_GPL(nf_tables_activate_set);
+
 void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set,
                              struct nft_set_binding *binding,
                              enum nft_trans_phase phase)
 {
        switch (phase) {
        case NFT_TRANS_PREPARE:
+               if (nft_set_is_anonymous(set))
+                       nft_deactivate_next(ctx->net, set);
+
                set->use--;
                return;
        case NFT_TRANS_ABORT:
index 274579b1696e0c2ac0f73e2e6284c1f019b54a9f..bd19c7aec92ee7ca47dc0c9024bd33b82027c5a7 100644 (file)
@@ -342,7 +342,7 @@ static void nft_dynset_activate(const struct nft_ctx *ctx,
 {
        struct nft_dynset *priv = nft_expr_priv(expr);
 
-       priv->set->use++;
+       nf_tables_activate_set(ctx, priv->set);
 }
 
 static void nft_dynset_destroy(const struct nft_ctx *ctx,
index cecf8ab90e58f7e84dd73168f69c87a50811837c..03ef4fdaa460b65b485c252c1d2709bb4b0f5cf4 100644 (file)
@@ -167,7 +167,7 @@ static void nft_lookup_activate(const struct nft_ctx *ctx,
 {
        struct nft_lookup *priv = nft_expr_priv(expr);
 
-       priv->set->use++;
+       nf_tables_activate_set(ctx, priv->set);
 }
 
 static void nft_lookup_destroy(const struct nft_ctx *ctx,
index cb37169608babd8b514f7bce598aa94ea967b528..a48dd5b5d45b13256b0d30d4806ddca49bbcbc6f 100644 (file)
@@ -185,7 +185,7 @@ static void nft_objref_map_activate(const struct nft_ctx *ctx,
 {
        struct nft_objref_map *priv = nft_expr_priv(expr);
 
-       priv->set->use++;
+       nf_tables_activate_set(ctx, priv->set);
 }
 
 static void nft_objref_map_destroy(const struct nft_ctx *ctx,