Skip to content

Commit 837c038

Browse files
tammelasmb49
authored andcommitted
net/sched: tcindex: update imperfect hash filters respecting rcu
The imperfect hash area can be updated while packets are traversing, which will cause a use-after-free when 'tcf_exts_exec()' is called with the destroyed tcf_ext. CPU 0: CPU 1: tcindex_set_parms tcindex_classify tcindex_lookup tcindex_lookup tcf_exts_change tcf_exts_exec [UAF] Stop operating on the shared area directly, by using a local copy, and update the filter with 'rcu_replace_pointer()'. Delete the old filter version only after a rcu grace period elapsed. Fixes: 9b0d444 ("net: sched: avoid atomic swap in tcf_exts_change") Reported-by: valis <sec@valis.email> Suggested-by: valis <sec@valis.email> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Link: https://lore.kernel.org/r/20230209143739.279867-1-pctammela@mojatatu.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> (cherry picked from commit ee05917) CVE-2023-1281 Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com> Acked-by: Stefan Bader <stefan.bader@canonical.com> Acked-by: Cengiz Can <cengiz.can@canonical.com> Signed-off-by: Luke Nowakowski-Krijger <luke.nowakowskikrijger@canonical.com>
1 parent 0bdc478 commit 837c038

File tree

1 file changed

+30
-4
lines changed

1 file changed

+30
-4
lines changed

net/sched/cls_tcindex.c

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/errno.h>
1313
#include <linux/slab.h>
1414
#include <linux/refcount.h>
15+
#include <linux/rcupdate.h>
1516
#include <net/act_api.h>
1617
#include <net/netlink.h>
1718
#include <net/pkt_cls.h>
@@ -338,6 +339,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
338339
struct tcf_result cr = {};
339340
int err, balloc = 0;
340341
struct tcf_exts e;
342+
bool update_h = false;
341343

342344
err = tcf_exts_init(&e, net, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
343345
if (err < 0)
@@ -455,10 +457,13 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
455457
}
456458
}
457459

458-
if (cp->perfect)
460+
if (cp->perfect) {
459461
r = cp->perfect + handle;
460-
else
461-
r = tcindex_lookup(cp, handle) ? : &new_filter_result;
462+
} else {
463+
/* imperfect area is updated in-place using rcu */
464+
update_h = !!tcindex_lookup(cp, handle);
465+
r = &new_filter_result;
466+
}
462467

463468
if (r == &new_filter_result) {
464469
f = kzalloc(sizeof(*f), GFP_KERNEL);
@@ -484,7 +489,28 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
484489

485490
rcu_assign_pointer(tp->root, cp);
486491

487-
if (r == &new_filter_result) {
492+
if (update_h) {
493+
struct tcindex_filter __rcu **fp;
494+
struct tcindex_filter *cf;
495+
496+
f->result.res = r->res;
497+
tcf_exts_change(&f->result.exts, &r->exts);
498+
499+
/* imperfect area bucket */
500+
fp = cp->h + (handle % cp->hash);
501+
502+
/* lookup the filter, guaranteed to exist */
503+
for (cf = rcu_dereference_bh_rtnl(*fp); cf;
504+
fp = &cf->next, cf = rcu_dereference_bh_rtnl(*fp))
505+
if (cf->key == handle)
506+
break;
507+
508+
f->next = cf->next;
509+
510+
cf = rcu_replace_pointer(*fp, f, 1);
511+
tcf_exts_get_net(&cf->result.exts);
512+
tcf_queue_work(&cf->rwork, tcindex_destroy_fexts_work);
513+
} else if (r == &new_filter_result) {
488514
struct tcindex_filter *nfp;
489515
struct tcindex_filter __rcu **fp;
490516

0 commit comments

Comments
 (0)