Commit 79803f66 authored by Pedro Tammela's avatar Pedro Tammela Committed by Jialin Zhang
Browse files

net/sched: tcindex: update imperfect hash filters respecting rcu

stable inclusion
from stable-v5.10.168
commit eb8e9d8572d1d9df17272783ad8a84843ce559d4
category: bugfix
bugzilla: 188576, https://gitee.com/src-openeuler/kernel/issues/I6OP9S
CVE: CVE-2023-1281

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=eb8e9d8572d1d9df17272783ad8a84843ce559d4



---------------------------

commit ee059170 upstream.

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: 9b0d4446 ("net: sched: avoid atomic swap in tcf_exts_change")
Reported-by: default avatarvalis <sec@valis.email>
Suggested-by: default avatarvalis <sec@valis.email>
Signed-off-by: default avatarJamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: default avatarPedro Tammela <pctammela@mojatatu.com>
Link: https://lore.kernel.org/r/20230209143739.279867-1-pctammela@mojatatu.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarDong Chenchen <dongchenchen2@huawei.com>
Reviewed-by: default avatarYue Haibing <yuehaibing@huawei.com>
Reviewed-by: default avatarWang Weiyang <wangweiyang2@huawei.com>
Signed-off-by: default avatarJialin Zhang <zhangjialin11@huawei.com>
parent 6fa43629
Loading
Loading
Loading
Loading
+30 −4
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/refcount.h>
#include <linux/rcupdate.h>
#include <net/act_api.h>
#include <net/netlink.h>
#include <net/pkt_cls.h>
@@ -338,6 +339,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
	struct tcf_result cr = {};
	int err, balloc = 0;
	struct tcf_exts e;
	bool update_h = false;

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

	if (cp->perfect)
	if (cp->perfect) {
		r = cp->perfect + handle;
	else
		r = tcindex_lookup(cp, handle) ? : &new_filter_result;
	} else {
		/* imperfect area is updated in-place using rcu */
		update_h = !!tcindex_lookup(cp, handle);
		r = &new_filter_result;
	}

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

	rcu_assign_pointer(tp->root, cp);

	if (r == &new_filter_result) {
	if (update_h) {
		struct tcindex_filter __rcu **fp;
		struct tcindex_filter *cf;

		f->result.res = r->res;
		tcf_exts_change(&f->result.exts, &r->exts);

		/* imperfect area bucket */
		fp = cp->h + (handle % cp->hash);

		/* lookup the filter, guaranteed to exist */
		for (cf = rcu_dereference_bh_rtnl(*fp); cf;
		     fp = &cf->next, cf = rcu_dereference_bh_rtnl(*fp))
			if (cf->key == handle)
				break;

		f->next = cf->next;

		cf = rcu_replace_pointer(*fp, f, 1);
		tcf_exts_get_net(&cf->result.exts);
		tcf_queue_work(&cf->rwork, tcindex_destroy_fexts_work);
	} else if (r == &new_filter_result) {
		struct tcindex_filter *nfp;
		struct tcindex_filter __rcu **fp;