Skip to content

Commit b6290e1

Browse files
committed
netfilter: nf_tables: use timestamp to check for set element timeout
jira VULN-7047 cve CVE-2024-27397 commit-author Pablo Neira Ayuso <[email protected]> commit 7395dfa upstream-diff Significant code drift, fuzz, conflicts and every other dumb thing cherry-pick can do while pulling something new into something ancient. Tried to stay true to the resf_kernel-4.18.0-553.8.1.el8_10 tagged code. Add a timestamp field at the beginning of the transaction, store it in the nftables per-netns area. Update set backend .insert, .deactivate and sync gc path to use the timestamp, this avoids that an element expires while control plane transaction is still unfinished. .lookup and .update, which are used from packet path, still use the current time to check if the element has expired. And .get path and dump also since this runs lockless under rcu read size lock. Then, there is async gc which also needs to check the current time since it runs asynchronously from a workqueue. Fixes: c3e1b00 ("netfilter: nf_tables: add set element timeout support") Signed-off-by: Pablo Neira Ayuso <[email protected]> (cherry picked from commit 7395dfa) Signed-off-by: Greg Rose <[email protected]> Conflicts: include/net/netfilter/nf_tables.h net/netfilter/nf_tables_api.c net/netfilter/nft_set_pipapo.c net/netfilter/nft_set_rbtree.c
1 parent bfd71d8 commit b6290e1

File tree

5 files changed

+47
-18
lines changed

5 files changed

+47
-18
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -716,10 +716,16 @@ static inline struct nft_set_elem_expr *nft_set_ext_expr(const struct nft_set_ex
716716
return nft_set_ext(ext, NFT_SET_EXT_EXPRESSIONS);
717717
}
718718

719-
static inline bool nft_set_elem_expired(const struct nft_set_ext *ext)
719+
static inline bool __nft_set_elem_expired(const struct nft_set_ext *ext,
720+
u64 tstamp)
720721
{
721722
return nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION) &&
722-
time_is_before_eq_jiffies64(*nft_set_ext_expiration(ext));
723+
time_after_eq64(tstamp, *nft_set_ext_expiration(ext));
724+
}
725+
726+
static inline bool nft_set_elem_expired(const struct nft_set_ext *ext)
727+
{
728+
return __nft_set_elem_expired(ext, get_jiffies_64());
723729
}
724730

725731
static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
@@ -1534,6 +1540,7 @@ static inline int nft_request_module(struct net *net, const char *fmt, ...) { re
15341540
#endif
15351541

15361542
struct nftables_pernet {
1543+
u64 tstamp;
15371544
unsigned int gc_seq;
15381545
};
15391546

@@ -1544,4 +1551,9 @@ static inline struct nftables_pernet *nft_pernet(const struct net *net)
15441551
return net_generic(net, nf_tables_net_id);
15451552
}
15461553

1554+
static inline u64 nft_net_tstamp(const struct net *net)
1555+
{
1556+
return nft_pernet(net)->tstamp;
1557+
}
1558+
15471559
#endif /* _NET_NF_TABLES_H */

net/netfilter/nf_tables_api.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8643,9 +8643,11 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb,
86438643

86448644
static bool nf_tables_valid_genid(struct net *net, u32 genid)
86458645
{
8646+
struct nftables_pernet *nft_net = nft_pernet(net);
86468647
bool genid_ok;
86478648

86488649
mutex_lock(&net->nft_commit_mutex);
8650+
nft_net->tstamp = get_jiffies_64();
86498651

86508652
genid_ok = genid == 0 || net->nft.base_seq == genid;
86518653
if (!genid_ok)

net/netfilter/nft_set_hash.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ struct nft_rhash_cmp_arg {
3838
const struct nft_set *set;
3939
const u32 *key;
4040
u8 genmask;
41+
u64 tstamp;
4142
};
4243

4344
static inline u32 nft_rhash_key(const void *data, u32 len, u32 seed)
@@ -64,7 +65,7 @@ static inline int nft_rhash_cmp(struct rhashtable_compare_arg *arg,
6465
return 1;
6566
if (nft_set_elem_is_dead(&he->ext))
6667
return 1;
67-
if (nft_set_elem_expired(&he->ext))
68+
if (__nft_set_elem_expired(&he->ext, x->tstamp))
6869
return 1;
6970
if (!nft_set_elem_active(&he->ext, x->genmask))
7071
return 1;
@@ -88,6 +89,7 @@ static bool nft_rhash_lookup(const struct net *net, const struct nft_set *set,
8889
.genmask = nft_genmask_cur(net),
8990
.set = set,
9091
.key = key,
92+
.tstamp = get_jiffies_64(),
9193
};
9294

9395
he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params);
@@ -106,6 +108,7 @@ static void *nft_rhash_get(const struct net *net, const struct nft_set *set,
106108
.genmask = nft_genmask_cur(net),
107109
.set = set,
108110
.key = elem->key.val.data,
111+
.tstamp = get_jiffies_64(),
109112
};
110113

111114
he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params);
@@ -129,6 +132,7 @@ static bool nft_rhash_update(struct nft_set *set, const u32 *key,
129132
.genmask = NFT_GENMASK_ANY,
130133
.set = set,
131134
.key = key,
135+
.tstamp = get_jiffies_64(),
132136
};
133137

134138
he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params);
@@ -172,6 +176,7 @@ static int nft_rhash_insert(const struct net *net, const struct nft_set *set,
172176
.genmask = nft_genmask_next(net),
173177
.set = set,
174178
.key = elem->key.val.data,
179+
.tstamp = nft_net_tstamp(net),
175180
};
176181
struct nft_rhash_elem *prev;
177182

@@ -212,6 +217,7 @@ static void *nft_rhash_deactivate(const struct net *net,
212217
.genmask = nft_genmask_next(net),
213218
.set = set,
214219
.key = elem->key.val.data,
220+
.tstamp = nft_net_tstamp(net),
215221
};
216222

217223
rcu_read_lock();

net/netfilter/nft_set_pipapo.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,7 @@ static bool nft_pipapo_lookup(const struct net *net, const struct nft_set *set,
635635
* @set: nftables API set representation
636636
* @data: Key data to be matched against existing elements
637637
* @genmask: If set, check that element is active in given genmask
638+
* @tstamp: timestamp to check for expired elements
638639
*
639640
* This is essentially the same as the lookup function, except that it matches
640641
* key data against the uncommitted copy and doesn't use preallocated maps for
@@ -644,7 +645,8 @@ static bool nft_pipapo_lookup(const struct net *net, const struct nft_set *set,
644645
*/
645646
static struct nft_pipapo_elem *pipapo_get(const struct net *net,
646647
const struct nft_set *set,
647-
const u8 *data, u8 genmask)
648+
const u8 *data, u8 genmask,
649+
u64 tstamp)
648650
{
649651
struct nft_pipapo_elem *ret = ERR_PTR(-ENOENT);
650652
struct nft_pipapo *priv = nft_set_priv(set);
@@ -704,7 +706,7 @@ static struct nft_pipapo_elem *pipapo_get(const struct net *net,
704706
goto out;
705707

706708
if (last) {
707-
if (nft_set_elem_expired(&f->mt[b].e->ext))
709+
if (__nft_set_elem_expired(&f->mt[b].e->ext, tstamp))
708710
goto next_match;
709711
if ((genmask &&
710712
!nft_set_elem_active(&f->mt[b].e->ext, genmask)))
@@ -741,7 +743,7 @@ void *nft_pipapo_get(const struct net *net, const struct nft_set *set,
741743
const struct nft_set_elem *elem, unsigned int flags)
742744
{
743745
return pipapo_get(net, set, (const u8 *)elem->key.val.data,
744-
nft_genmask_cur(net));
746+
nft_genmask_cur(net), get_jiffies_64());
745747
}
746748

747749
/**
@@ -1100,14 +1102,15 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set,
11001102
struct nft_pipapo *priv = nft_set_priv(set);
11011103
struct nft_pipapo_match *m = priv->clone;
11021104
u8 genmask = nft_genmask_next(net);
1105+
u64 tstamp = nft_net_tstamp(net);
11031106
struct nft_pipapo_field *f;
11041107
int i, bsize_max, err = 0;
11051108

1106-
dup = pipapo_get(net, set, start, genmask);
1109+
dup = pipapo_get(net, set, start, genmask, tstamp);
11071110
if (PTR_ERR(dup) == -ENOENT) {
11081111
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END)) {
11091112
end = (const u8 *)nft_set_ext_key_end(ext)->data;
1110-
dup = pipapo_get(net, set, end, nft_genmask_next(net));
1113+
dup = pipapo_get(net, set, end, nft_genmask_next(net), tstamp);
11111114
} else {
11121115
end = start;
11131116
}
@@ -1457,6 +1460,7 @@ static void pipapo_gc(const struct nft_set *_set, struct nft_pipapo_match *m)
14571460
struct nft_set *set = (struct nft_set *) _set;
14581461
struct nft_pipapo *priv = nft_set_priv(set);
14591462
struct net *net = read_pnet(&set->net);
1463+
u64 tstamp = nft_net_tstamp(net);
14601464
int rules_f0, first_rule = 0;
14611465
struct nft_pipapo_elem *e;
14621466
struct nft_trans_gc *gc;
@@ -1491,7 +1495,7 @@ static void pipapo_gc(const struct nft_set *_set, struct nft_pipapo_match *m)
14911495
/* synchronous gc never fails, there is no need to set on
14921496
* NFT_SET_ELEM_DEAD_BIT.
14931497
*/
1494-
if (nft_set_elem_expired(&e->ext)) {
1498+
if (__nft_set_elem_expired(&e->ext, tstamp)) {
14951499
priv->dirty = true;
14961500

14971501
gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC);
@@ -1662,7 +1666,7 @@ static void *pipapo_deactivate(const struct net *net, const struct nft_set *set,
16621666
{
16631667
struct nft_pipapo_elem *e;
16641668

1665-
e = pipapo_get(net, set, data, nft_genmask_next(net));
1669+
e = pipapo_get(net, set, data, nft_genmask_next(net), nft_net_tstamp(net));
16661670
if (IS_ERR(e))
16671671
return NULL;
16681672

net/netfilter/nft_set_rbtree.c

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
229229
{
230230
struct nft_rbtree *priv = nft_set_priv(set);
231231
u8 genmask = nft_genmask_next(net);
232+
u64 tstamp = nft_net_tstamp(net);
232233
struct nft_rbtree_elem *rbe;
233234
struct rb_node *parent, **p;
234235
bool overlap = false;
@@ -287,13 +288,13 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
287288
if (nft_rbtree_interval_start(new)) {
288289
if (nft_rbtree_interval_end(rbe) &&
289290
nft_set_elem_active(&rbe->ext, genmask) &&
290-
!nft_set_elem_expired(&rbe->ext) && !*p)
291+
!__nft_set_elem_expired(&rbe->ext, tstamp) && !*p)
291292
overlap = false;
292293
} else {
293294
overlap = nft_rbtree_interval_end(rbe) &&
294295
nft_set_elem_active(&rbe->ext,
295296
genmask) &&
296-
!nft_set_elem_expired(&rbe->ext);
297+
!__nft_set_elem_expired(&rbe->ext, tstamp);
297298
}
298299
} else if (d > 0) {
299300
p = &parent->rb_right;
@@ -302,9 +303,9 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
302303
overlap = nft_rbtree_interval_end(rbe) &&
303304
nft_set_elem_active(&rbe->ext,
304305
genmask) &&
305-
!nft_set_elem_expired(&rbe->ext);
306+
!__nft_set_elem_expired(&rbe->ext, tstamp);
306307
} else if (nft_set_elem_active(&rbe->ext, genmask) &&
307-
!nft_set_elem_expired(&rbe->ext)) {
308+
!__nft_set_elem_expired(&rbe->ext, tstamp)) {
308309
overlap = nft_rbtree_interval_end(rbe);
309310
}
310311
} else {
@@ -313,17 +314,17 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
313314
p = &parent->rb_left;
314315

315316
if (nft_set_elem_active(&rbe->ext, genmask) &&
316-
!nft_set_elem_expired(&rbe->ext))
317+
!__nft_set_elem_expired(&rbe->ext, tstamp))
317318
overlap = false;
318319
} else if (nft_rbtree_interval_start(rbe) &&
319320
nft_rbtree_interval_end(new)) {
320321
p = &parent->rb_right;
321322

322323
if (nft_set_elem_active(&rbe->ext, genmask) &&
323-
!nft_set_elem_expired(&rbe->ext))
324+
!__nft_set_elem_expired(&rbe->ext, tstamp))
324325
overlap = false;
325326
} else if (nft_set_elem_active(&rbe->ext, genmask) &&
326-
!nft_set_elem_expired(&rbe->ext)) {
327+
!__nft_set_elem_expired(&rbe->ext, tstamp)) {
327328
*ext = &rbe->ext;
328329
return -EEXIST;
329330
} else {
@@ -400,6 +401,7 @@ static void *nft_rbtree_deactivate(const struct net *net,
400401
const struct rb_node *parent = priv->root.rb_node;
401402
struct nft_rbtree_elem *rbe, *this = elem->priv;
402403
u8 genmask = nft_genmask_next(net);
404+
u64 tstamp = nft_net_tstamp(net);
403405
int d;
404406

405407
while (parent != NULL) {
@@ -420,6 +422,8 @@ static void *nft_rbtree_deactivate(const struct net *net,
420422
nft_rbtree_interval_end(this)) {
421423
parent = parent->rb_right;
422424
continue;
425+
} else if (__nft_set_elem_expired(&rbe->ext, tstamp)) {
426+
break;
423427
} else if (!nft_set_elem_active(&rbe->ext, genmask)) {
424428
parent = parent->rb_left;
425429
continue;
@@ -472,6 +476,7 @@ static void nft_rbtree_gc(struct work_struct *work)
472476
struct nft_set *set;
473477
unsigned int gc_seq;
474478
struct net *net;
479+
u64 tstamp;
475480

476481
priv = container_of(work, struct nft_rbtree, gc_work.work);
477482
set = nft_set_container_of(priv);
@@ -510,7 +515,7 @@ static void nft_rbtree_gc(struct work_struct *work)
510515
rbe_end = rbe;
511516
continue;
512517
}
513-
if (!nft_set_elem_expired(&rbe->ext))
518+
if (!__nft_set_elem_expired(&rbe->ext, tstamp))
514519
continue;
515520

516521
nft_set_elem_dead(&rbe->ext);

0 commit comments

Comments
 (0)