Skip to content

Commit 164936b

Browse files
ummakynesgregkh
authored andcommitted
netfilter: nf_tables: restore set elements when delete set fails
>From abort path, nft_mapelem_activate() needs to restore refcounters to the original state. Currently, it uses the set->ops->walk() to iterate over these set elements. The existing set iterator skips inactive elements in the next generation, this does not work from the abort path to restore the original state since it has to skip active elements instead (not inactive ones). This patch moves the check for inactive elements to the set iterator callback, then it reverses the logic for the .activate case which needs to skip active elements. Toggle next generation bit for elements when delete set command is invoked and call nft_clear() from .activate (abort) path to restore the next generation bit. The splat below shows an object in mappings memleak: [43929.457523] ------------[ cut here ]------------ [43929.457532] WARNING: CPU: 0 PID: 1139 at include/net/netfilter/nf_tables.h:1237 nft_setelem_data_deactivate+0xe4/0xf0 [nf_tables] [...] [43929.458014] RIP: 0010:nft_setelem_data_deactivate+0xe4/0xf0 [nf_tables] [43929.458076] Code: 83 f8 01 77 ab 49 8d 7c 24 08 e8 37 5e d0 de 49 8b 6c 24 08 48 8d 7d 50 e8 e9 5c d0 de 8b 45 50 8d 50 ff 89 55 50 85 c0 75 86 <0f> 0b eb 82 0f 0b eb b3 0f 1f 40 00 90 90 90 90 90 90 90 90 90 90 [43929.458081] RSP: 0018:ffff888140f9f4b0 EFLAGS: 00010246 [43929.458086] RAX: 0000000000000000 RBX: ffff8881434f5288 RCX: dffffc0000000000 [43929.458090] RDX: 00000000ffffffff RSI: ffffffffa26d28a7 RDI: ffff88810ecc9550 [43929.458093] RBP: ffff88810ecc9500 R08: 0000000000000001 R09: ffffed10281f3e8f [43929.458096] R10: 0000000000000003 R11: ffff0000ffff0000 R12: ffff8881434f52a0 [43929.458100] R13: ffff888140f9f5f4 R14: ffff888151c7a800 R15: 0000000000000002 [43929.458103] FS: 00007f0c687c4740(0000) GS:ffff888390800000(0000) knlGS:0000000000000000 [43929.458107] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [43929.458111] CR2: 00007f58dbe5b008 CR3: 0000000123602005 CR4: 00000000001706f0 [43929.458114] Call Trace: [43929.458118] <TASK> [43929.458121] ? __warn+0x9f/0x1a0 [43929.458127] ? nft_setelem_data_deactivate+0xe4/0xf0 [nf_tables] [43929.458188] ? report_bug+0x1b1/0x1e0 [43929.458196] ? handle_bug+0x3c/0x70 [43929.458200] ? exc_invalid_op+0x17/0x40 [43929.458211] ? nft_setelem_data_deactivate+0xd7/0xf0 [nf_tables] [43929.458271] ? nft_setelem_data_deactivate+0xe4/0xf0 [nf_tables] [43929.458332] nft_mapelem_deactivate+0x24/0x30 [nf_tables] [43929.458392] nft_rhash_walk+0xdd/0x180 [nf_tables] [43929.458453] ? __pfx_nft_rhash_walk+0x10/0x10 [nf_tables] [43929.458512] ? rb_insert_color+0x2e/0x280 [43929.458520] nft_map_deactivate+0xdc/0x1e0 [nf_tables] [43929.458582] ? __pfx_nft_map_deactivate+0x10/0x10 [nf_tables] [43929.458642] ? __pfx_nft_mapelem_deactivate+0x10/0x10 [nf_tables] [43929.458701] ? __rcu_read_unlock+0x46/0x70 [43929.458709] nft_delset+0xff/0x110 [nf_tables] [43929.458769] nft_flush_table+0x16f/0x460 [nf_tables] [43929.458830] nf_tables_deltable+0x501/0x580 [nf_tables] Fixes: 628bd3e ("netfilter: nf_tables: drop map element references from preparation phase") Signed-off-by: Pablo Neira Ayuso <[email protected]> (cherry picked from commit e79b47a) [Vegard: CVE-2024-27012; fixed conflicts due to missing commits 0e1ea65 ("netfilter: nf_tables: shrink memory consumption of set elements") and 9dad402 ("netfilter: nf_tables: expose opaque set element as struct nft_elem_priv") so we pass the correct types and values to nft_setelem_data_deactivate(), nft_setelem_validate(), nft_set_elem_ext(), etc.] Signed-off-by: Vegard Nossum <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent a1bd2a3 commit 164936b

File tree

5 files changed

+42
-20
lines changed

5 files changed

+42
-20
lines changed

net/netfilter/nf_tables_api.c

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,12 @@ static int nft_mapelem_deactivate(const struct nft_ctx *ctx,
594594
const struct nft_set_iter *iter,
595595
struct nft_set_elem *elem)
596596
{
597+
struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
598+
599+
if (!nft_set_elem_active(ext, iter->genmask))
600+
return 0;
601+
602+
nft_set_elem_change_active(ctx->net, set, ext);
597603
nft_setelem_data_deactivate(ctx->net, set, elem);
598604

599605
return 0;
@@ -619,6 +625,7 @@ static void nft_map_catchall_deactivate(const struct nft_ctx *ctx,
619625
continue;
620626

621627
elem.priv = catchall->elem;
628+
nft_set_elem_change_active(ctx->net, set, ext);
622629
nft_setelem_data_deactivate(ctx->net, set, &elem);
623630
break;
624631
}
@@ -3820,6 +3827,9 @@ int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set,
38203827
const struct nft_data *data;
38213828
int err;
38223829

3830+
if (!nft_set_elem_active(ext, iter->genmask))
3831+
return 0;
3832+
38233833
if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
38243834
*nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END)
38253835
return 0;
@@ -3843,19 +3853,22 @@ int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set,
38433853

38443854
int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set)
38453855
{
3846-
u8 genmask = nft_genmask_next(ctx->net);
3856+
struct nft_set_iter dummy_iter = {
3857+
.genmask = nft_genmask_next(ctx->net),
3858+
};
38473859
struct nft_set_elem_catchall *catchall;
38483860
struct nft_set_elem elem;
3861+
38493862
struct nft_set_ext *ext;
38503863
int ret = 0;
38513864

38523865
list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
38533866
ext = nft_set_elem_ext(set, catchall->elem);
3854-
if (!nft_set_elem_active(ext, genmask))
3867+
if (!nft_set_elem_active(ext, dummy_iter.genmask))
38553868
continue;
38563869

38573870
elem.priv = catchall->elem;
3858-
ret = nft_setelem_validate(ctx, set, NULL, &elem);
3871+
ret = nft_setelem_validate(ctx, set, &dummy_iter, &elem);
38593872
if (ret < 0)
38603873
return ret;
38613874
}
@@ -5347,6 +5360,11 @@ static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx,
53475360
const struct nft_set_iter *iter,
53485361
struct nft_set_elem *elem)
53495362
{
5363+
const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
5364+
5365+
if (!nft_set_elem_active(ext, iter->genmask))
5366+
return 0;
5367+
53505368
return nft_setelem_data_validate(ctx, set, elem);
53515369
}
53525370

@@ -5441,6 +5459,13 @@ static int nft_mapelem_activate(const struct nft_ctx *ctx,
54415459
const struct nft_set_iter *iter,
54425460
struct nft_set_elem *elem)
54435461
{
5462+
struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
5463+
5464+
/* called from abort path, reverse check to undo changes. */
5465+
if (nft_set_elem_active(ext, iter->genmask))
5466+
return 0;
5467+
5468+
nft_clear(ctx->net, ext);
54445469
nft_setelem_data_activate(ctx->net, set, elem);
54455470

54465471
return 0;
@@ -5459,6 +5484,7 @@ static void nft_map_catchall_activate(const struct nft_ctx *ctx,
54595484
if (!nft_set_elem_active(ext, genmask))
54605485
continue;
54615486

5487+
nft_clear(ctx->net, ext);
54625488
elem.priv = catchall->elem;
54635489
nft_setelem_data_activate(ctx->net, set, &elem);
54645490
break;
@@ -5733,6 +5759,9 @@ static int nf_tables_dump_setelem(const struct nft_ctx *ctx,
57335759
const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
57345760
struct nft_set_dump_args *args;
57355761

5762+
if (!nft_set_elem_active(ext, iter->genmask))
5763+
return 0;
5764+
57365765
if (nft_set_elem_expired(ext) || nft_set_elem_is_dead(ext))
57375766
return 0;
57385767

@@ -6500,7 +6529,7 @@ static void nft_setelem_activate(struct net *net, struct nft_set *set,
65006529
struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
65016530

65026531
if (nft_setelem_is_catchall(set, elem)) {
6503-
nft_set_elem_change_active(net, set, ext);
6532+
nft_clear(net, ext);
65046533
} else {
65056534
set->ops->activate(net, set, elem);
65066535
}
@@ -7194,9 +7223,13 @@ static int nft_setelem_flush(const struct nft_ctx *ctx,
71947223
const struct nft_set_iter *iter,
71957224
struct nft_set_elem *elem)
71967225
{
7226+
const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
71977227
struct nft_trans *trans;
71987228
int err;
71997229

7230+
if (!nft_set_elem_active(ext, iter->genmask))
7231+
return 0;
7232+
72007233
trans = nft_trans_alloc_gfp(ctx, NFT_MSG_DELSETELEM,
72017234
sizeof(struct nft_trans_elem), GFP_ATOMIC);
72027235
if (!trans)

net/netfilter/nft_set_bitmap.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ static void nft_bitmap_activate(const struct net *net,
171171
nft_bitmap_location(set, nft_set_ext_key(&be->ext), &idx, &off);
172172
/* Enter 11 state. */
173173
priv->bitmap[idx] |= (genmask << off);
174-
nft_set_elem_change_active(net, set, &be->ext);
174+
nft_clear(net, &be->ext);
175175
}
176176

177177
static bool nft_bitmap_flush(const struct net *net,
@@ -223,8 +223,6 @@ static void nft_bitmap_walk(const struct nft_ctx *ctx,
223223
list_for_each_entry_rcu(be, &priv->list, head) {
224224
if (iter->count < iter->skip)
225225
goto cont;
226-
if (!nft_set_elem_active(&be->ext, iter->genmask))
227-
goto cont;
228226

229227
elem.priv = be;
230228

net/netfilter/nft_set_hash.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ static void nft_rhash_activate(const struct net *net, const struct nft_set *set,
189189
{
190190
struct nft_rhash_elem *he = elem->priv;
191191

192-
nft_set_elem_change_active(net, set, &he->ext);
192+
nft_clear(net, &he->ext);
193193
}
194194

195195
static bool nft_rhash_flush(const struct net *net,
@@ -277,8 +277,6 @@ static void nft_rhash_walk(const struct nft_ctx *ctx, struct nft_set *set,
277277

278278
if (iter->count < iter->skip)
279279
goto cont;
280-
if (!nft_set_elem_active(&he->ext, iter->genmask))
281-
goto cont;
282280

283281
elem.priv = he;
284282

@@ -587,7 +585,7 @@ static void nft_hash_activate(const struct net *net, const struct nft_set *set,
587585
{
588586
struct nft_hash_elem *he = elem->priv;
589587

590-
nft_set_elem_change_active(net, set, &he->ext);
588+
nft_clear(net, &he->ext);
591589
}
592590

593591
static bool nft_hash_flush(const struct net *net,
@@ -641,8 +639,6 @@ static void nft_hash_walk(const struct nft_ctx *ctx, struct nft_set *set,
641639
hlist_for_each_entry_rcu(he, &priv->table[i], node) {
642640
if (iter->count < iter->skip)
643641
goto cont;
644-
if (!nft_set_elem_active(&he->ext, iter->genmask))
645-
goto cont;
646642

647643
elem.priv = he;
648644

net/netfilter/nft_set_pipapo.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1766,7 +1766,7 @@ static void nft_pipapo_activate(const struct net *net,
17661766
{
17671767
struct nft_pipapo_elem *e = elem->priv;
17681768

1769-
nft_set_elem_change_active(net, set, &e->ext);
1769+
nft_clear(net, &e->ext);
17701770
}
17711771

17721772
/**
@@ -2068,9 +2068,6 @@ static void nft_pipapo_walk(const struct nft_ctx *ctx, struct nft_set *set,
20682068

20692069
e = f->mt[r].e;
20702070

2071-
if (!nft_set_elem_active(&e->ext, iter->genmask))
2072-
goto cont;
2073-
20742071
elem.priv = e;
20752072

20762073
iter->err = iter->fn(ctx, set, iter, &elem);

net/netfilter/nft_set_rbtree.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ static void nft_rbtree_activate(const struct net *net,
527527
{
528528
struct nft_rbtree_elem *rbe = elem->priv;
529529

530-
nft_set_elem_change_active(net, set, &rbe->ext);
530+
nft_clear(net, &rbe->ext);
531531
}
532532

533533
static bool nft_rbtree_flush(const struct net *net,
@@ -596,8 +596,6 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx,
596596

597597
if (iter->count < iter->skip)
598598
goto cont;
599-
if (!nft_set_elem_active(&rbe->ext, iter->genmask))
600-
goto cont;
601599

602600
elem.priv = rbe;
603601

0 commit comments

Comments
 (0)