From: Vlad Buslov <vladbu@mellanox.com>
Date: Mon, 24 Sep 2018 19:22:53 +0300
Subject: net: sched: use Qdisc rcu API instead of relying on rtnl lock
Patch-mainline: v4.20-rc1
Git-commit: e368fdb61d8e7c67ac70791b23345b26d7bbc661
References: bsc#1196973 CVE-2021-39713
As a preparation from removing rtnl lock dependency from rules update path,
use Qdisc rcu and reference counting capabilities instead of relying on
rtnl lock while working with Qdiscs. Create new tcf_block_release()
function, and use it to free resources taken by tcf_block_find().
Currently, this function only releases Qdisc and it is extended in next
patches in this series.
Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Acked-by: Thomas Bogendoerfer <tbogendoerfer@suse.de>
---
net/sched/cls_api.c | 53 +++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 42 insertions(+), 11 deletions(-)
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -487,34 +487,63 @@ replay:
/* Find head of filter chain. */
+ rcu_read_lock();
+
/* Find link */
- dev = __dev_get_by_index(net, t->tcm_ifindex);
- if (dev == NULL)
+ dev = dev_get_by_index_rcu(net, t->tcm_ifindex);
+ if (dev == NULL) {
+ rcu_read_unlock();
return -ENODEV;
+ }
/* Find qdisc */
if (!parent) {
q = dev->qdisc;
parent = q->handle;
} else {
- q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent));
- if (q == NULL)
- return -EINVAL;
+ q = qdisc_lookup_rcu(dev, TC_H_MAJ(t->tcm_parent));
+ if (q == NULL) {
+ err = -EINVAL;
+ rcu_read_unlock();
+ goto errout;
+ }
+ }
+
+ q = qdisc_refcount_inc_nz(q);
+ if (!q) {
+ err = -EINVAL;
+ rcu_read_unlock();
+ goto errout;
}
/* Is it classful? */
cops = q->ops->cl_ops;
- if (!cops)
- return -EINVAL;
+ if (!cops) {
+ err = -EINVAL;
+ rcu_read_unlock();
+ goto errout;
+ }
- if (!cops->tcf_block)
- return -EOPNOTSUPP;
+ if (!cops->tcf_block) {
+ err = -EOPNOTSUPP;
+ rcu_read_unlock();
+ goto errout;
+ }
+
+ /* At this point we know that qdisc is not noop_qdisc,
+ * which means that qdisc holds a reference to net_device
+ * and we hold a reference to qdisc, so it is safe to release
+ * rcu read lock.
+ */
+ rcu_read_unlock();
/* Do we search for filter, attached to class? */
if (TC_H_MIN(parent)) {
cl = cops->get(q, parent);
- if (cl == 0)
- return -ENOENT;
+ if (cl == 0) {
+ err = -ENOENT;
+ goto errout;
+ }
}
/* And the last stroke */
@@ -645,6 +674,8 @@ errout:
tcf_chain_put(chain);
if (cl)
cops->put(q, cl);
+ if (q)
+ qdisc_put(q);
if (err == -EAGAIN)
/* Replay the request. */
goto replay;