Skip to content

Commit 8858320

Browse files
committed
netfilter: nf_tables: restore set elements when delete set fails
jira VULN-37622 cve CVE-2024-27012 commit-author Pablo Neira Ayuso <pablo@netfilter.org> commit e79b47a upstream-diff Accounted for the missing commit 0e1ea65. 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 <pablo@netfilter.org> (cherry picked from commit e79b47a) Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
1 parent f356abe commit 8858320

5 files changed

Lines changed: 44 additions & 20 deletions

File tree

net/netfilter/nf_tables_api.c

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,12 @@ static int nft_mapelem_deactivate(const struct nft_ctx *ctx,
591591
const struct nft_set_iter *iter,
592592
struct nft_set_elem *elem)
593593
{
594+
struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
595+
596+
if (!nft_set_elem_active(ext, iter->genmask))
597+
return 0;
598+
599+
nft_set_elem_change_active(ctx->net, set, ext);
594600
nft_setelem_data_deactivate(ctx->net, set, elem);
595601

596602
return 0;
@@ -615,6 +621,7 @@ static void nft_map_catchall_deactivate(const struct nft_ctx *ctx,
615621
if (!nft_set_elem_active(ext, genmask))
616622
continue;
617623

624+
nft_set_elem_change_active(ctx->net, set, ext);
618625
elem.priv = catchall->elem;
619626
nft_setelem_data_deactivate(ctx->net, set, &elem);
620627
break;
@@ -3484,6 +3491,9 @@ int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set,
34843491
const struct nft_data *data;
34853492
int err;
34863493

3494+
if (!nft_set_elem_active(ext, iter->genmask))
3495+
return 0;
3496+
34873497
if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
34883498
*nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END)
34893499
return 0;
@@ -3507,19 +3517,21 @@ int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set,
35073517

35083518
int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set)
35093519
{
3510-
u8 genmask = nft_genmask_next(ctx->net);
3520+
struct nft_set_iter dummy_iter = {
3521+
.genmask = nft_genmask_next(ctx->net),
3522+
};
35113523
struct nft_set_elem_catchall *catchall;
35123524
struct nft_set_elem elem;
35133525
struct nft_set_ext *ext;
35143526
int ret = 0;
35153527

35163528
list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
35173529
ext = nft_set_elem_ext(set, catchall->elem);
3518-
if (!nft_set_elem_active(ext, genmask))
3530+
if (!nft_set_elem_active(ext, dummy_iter.genmask))
35193531
continue;
35203532

35213533
elem.priv = catchall->elem;
3522-
ret = nft_setelem_validate(ctx, set, NULL, &elem);
3534+
ret = nft_setelem_validate(ctx, set, &dummy_iter, &elem);
35233535
if (ret < 0)
35243536
return ret;
35253537
}
@@ -4996,6 +5008,11 @@ static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx,
49965008
const struct nft_set_iter *iter,
49975009
struct nft_set_elem *elem)
49985010
{
5011+
const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
5012+
5013+
if (!nft_set_elem_active(ext, iter->genmask))
5014+
return 0;
5015+
49995016
return nft_setelem_data_validate(ctx, set, elem);
50005017
}
50015018

@@ -5090,6 +5107,13 @@ static int nft_mapelem_activate(const struct nft_ctx *ctx,
50905107
const struct nft_set_iter *iter,
50915108
struct nft_set_elem *elem)
50925109
{
5110+
struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
5111+
5112+
/* called from abort path, reverse check to undo changes. */
5113+
if (nft_set_elem_active(ext, iter->genmask))
5114+
return 0;
5115+
5116+
nft_clear(ctx->net, ext);
50935117
nft_setelem_data_activate(ctx->net, set, elem);
50945118

50955119
return 0;
@@ -5108,6 +5132,7 @@ static void nft_map_catchall_activate(const struct nft_ctx *ctx,
51085132
if (!nft_set_elem_active(ext, genmask))
51095133
continue;
51105134

5135+
nft_clear(ctx->net, ext);
51115136
elem.priv = catchall->elem;
51125137
nft_setelem_data_activate(ctx->net, set, &elem);
51135138
break;
@@ -5380,6 +5405,9 @@ static int nf_tables_dump_setelem(const struct nft_ctx *ctx,
53805405
const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
53815406
struct nft_set_dump_args *args;
53825407

5408+
if (!nft_set_elem_active(ext, iter->genmask))
5409+
return 0;
5410+
53835411
if (nft_set_elem_expired(ext) || nft_set_elem_is_dead(ext))
53845412
return 0;
53855413

@@ -6113,7 +6141,7 @@ static void nft_setelem_activate(struct net *net, struct nft_set *set,
61136141
struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
61146142

61156143
if (nft_setelem_is_catchall(set, elem)) {
6116-
nft_set_elem_change_active(net, set, ext);
6144+
nft_clear(net, ext);
61176145
} else {
61186146
set->ops->activate(net, set, elem);
61196147
}
@@ -6787,8 +6815,12 @@ static int nft_setelem_flush(const struct nft_ctx *ctx,
67876815
const struct nft_set_iter *iter,
67886816
struct nft_set_elem *elem)
67896817
{
6818+
const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
67906819
struct nft_trans *trans;
67916820

6821+
if (!nft_set_elem_active(ext, iter->genmask))
6822+
return 0;
6823+
67926824
trans = nft_trans_alloc_gfp(ctx, NFT_MSG_DELSETELEM,
67936825
sizeof(struct nft_trans_elem), GFP_ATOMIC);
67946826
if (!trans)
@@ -10056,6 +10088,9 @@ static int nf_tables_loop_check_setelem(const struct nft_ctx *ctx,
1005610088
{
1005710089
const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
1005810090

10091+
if (!nft_set_elem_active(ext, iter->genmask))
10092+
return 0;
10093+
1005910094
if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
1006010095
*nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END)
1006110096
return 0;

net/netfilter/nft_set_bitmap.c

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

179179
static void nft_bitmap_flush(const struct net *net,
@@ -224,8 +224,6 @@ static void nft_bitmap_walk(const struct nft_ctx *ctx,
224224
list_for_each_entry_rcu(be, &priv->list, head) {
225225
if (iter->count < iter->skip)
226226
goto cont;
227-
if (!nft_set_elem_active(&be->ext, iter->genmask))
228-
goto cont;
229227

230228
elem.priv = &be->priv;
231229

net/netfilter/nft_set_hash.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ static void nft_rhash_activate(const struct net *net, const struct nft_set *set,
196196
{
197197
struct nft_rhash_elem *he = nft_elem_priv_cast(elem->priv);
198198

199-
nft_set_elem_change_active(net, set, &he->ext);
199+
nft_clear(net, &he->ext);
200200
}
201201

202202
static void nft_rhash_flush(const struct net *net,
@@ -283,8 +283,6 @@ static void nft_rhash_walk(const struct nft_ctx *ctx, struct nft_set *set,
283283

284284
if (iter->count < iter->skip)
285285
goto cont;
286-
if (!nft_set_elem_active(&he->ext, iter->genmask))
287-
goto cont;
288286

289287
elem.priv = &he->priv;
290288

@@ -612,7 +610,7 @@ static void nft_hash_activate(const struct net *net, const struct nft_set *set,
612610
{
613611
struct nft_hash_elem *he = nft_elem_priv_cast(elem->priv);
614612

615-
nft_set_elem_change_active(net, set, &he->ext);
613+
nft_clear(net, &he->ext);
616614
}
617615

618616
static void nft_hash_flush(const struct net *net,
@@ -666,8 +664,6 @@ static void nft_hash_walk(const struct nft_ctx *ctx, struct nft_set *set,
666664
hlist_for_each_entry_rcu(he, &priv->table[i], node) {
667665
if (iter->count < iter->skip)
668666
goto cont;
669-
if (!nft_set_elem_active(&he->ext, iter->genmask))
670-
goto cont;
671667

672668
elem.priv = &he->priv;
673669

net/netfilter/nft_set_pipapo.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1754,7 +1754,7 @@ static void nft_pipapo_activate(const struct net *net,
17541754
{
17551755
struct nft_pipapo_elem *e = nft_elem_priv_cast(elem->priv);
17561756

1757-
nft_set_elem_change_active(net, set, &e->ext);
1757+
nft_clear(net, &e->ext);
17581758
}
17591759

17601760
/**
@@ -2052,9 +2052,6 @@ static void nft_pipapo_walk(const struct nft_ctx *ctx, struct nft_set *set,
20522052

20532053
e = f->mt[r].e;
20542054

2055-
if (!nft_set_elem_active(&e->ext, iter->genmask))
2056-
goto cont;
2057-
20582055
elem.priv = &e->priv;
20592056

20602057
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
@@ -530,7 +530,7 @@ static void nft_rbtree_activate(const struct net *net,
530530
{
531531
struct nft_rbtree_elem *rbe = nft_elem_priv_cast(elem->priv);
532532

533-
nft_set_elem_change_active(net, set, &rbe->ext);
533+
nft_clear(net, &rbe->ext);
534534
}
535535

536536
static void 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->priv;
603601

0 commit comments

Comments
 (0)