Commit cc8e58f8 authored by Cong Wang's avatar Cong Wang Committed by Jakub Kicinski
Browse files

act_ife: load meta modules before tcf_idr_check_alloc()



The following deadlock scenario is triggered by syzbot:

Thread A:				Thread B:
tcf_idr_check_alloc()
...
populate_metalist()
  rtnl_unlock()
					rtnl_lock()
					...
  request_module()			tcf_idr_check_alloc()
  rtnl_lock()

At this point, thread A is waiting for thread B to release RTNL
lock, while thread B is waiting for thread A to commit the IDR
change with tcf_idr_insert() later.

Break this deadlock situation by preloading ife modules earlier,
before tcf_idr_check_alloc(), this is fine because we only need
to load modules we need potentially.

Reported-and-tested-by: default avatar <syzbot+80e32b5d1f9923f8ace6@syzkaller.appspotmail.com>
Fixes: 0190c1d4 ("net: sched: atomically check-allocate action")
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: Vlad Buslov <vladbu@mellanox.com>
Cc: Jiri Pirko <jiri@resnulli.us>
Signed-off-by: default avatarCong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent c2b94787
Loading
Loading
Loading
Loading
+34 −10
Original line number Diff line number Diff line
@@ -436,6 +436,25 @@ static void tcf_ife_cleanup(struct tc_action *a)
		kfree_rcu(p, rcu);
}

static int load_metalist(struct nlattr **tb, bool rtnl_held)
{
	int i;

	for (i = 1; i < max_metacnt; i++) {
		if (tb[i]) {
			void *val = nla_data(tb[i]);
			int len = nla_len(tb[i]);
			int rc;

			rc = load_metaops_and_vet(i, val, len, rtnl_held);
			if (rc != 0)
				return rc;
		}
	}

	return 0;
}

static int populate_metalist(struct tcf_ife_info *ife, struct nlattr **tb,
			     bool exists, bool rtnl_held)
{
@@ -449,10 +468,6 @@ static int populate_metalist(struct tcf_ife_info *ife, struct nlattr **tb,
			val = nla_data(tb[i]);
			len = nla_len(tb[i]);

			rc = load_metaops_and_vet(i, val, len, rtnl_held);
			if (rc != 0)
				return rc;

			rc = add_metainfo(ife, i, val, len, exists);
			if (rc)
				return rc;
@@ -509,6 +524,21 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
	if (!p)
		return -ENOMEM;

	if (tb[TCA_IFE_METALST]) {
		err = nla_parse_nested_deprecated(tb2, IFE_META_MAX,
						  tb[TCA_IFE_METALST], NULL,
						  NULL);
		if (err) {
			kfree(p);
			return err;
		}
		err = load_metalist(tb2, rtnl_held);
		if (err) {
			kfree(p);
			return err;
		}
	}

	index = parm->index;
	err = tcf_idr_check_alloc(tn, &index, a, bind);
	if (err < 0) {
@@ -570,15 +600,9 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
	}

	if (tb[TCA_IFE_METALST]) {
		err = nla_parse_nested_deprecated(tb2, IFE_META_MAX,
						  tb[TCA_IFE_METALST], NULL,
						  NULL);
		if (err)
			goto metadata_parse_err;
		err = populate_metalist(ife, tb2, exists, rtnl_held);
		if (err)
			goto metadata_parse_err;

	} else {
		/* if no passed metadata allow list or passed allow-all
		 * then here we process by adding as many supported metadatum