Skip to content

Commit ffdde7b

Browse files
vbnogueirakuba-moo
authored andcommitted
net/sched: Abort __tc_modify_qdisc if parent class does not exist
Lion's patch [1] revealed an ancient bug in the qdisc API. Whenever a user creates/modifies a qdisc specifying as a parent another qdisc, the qdisc API will, during grafting, detect that the user is not trying to attach to a class and reject. However grafting is performed after qdisc_create (and thus the qdiscs' init callback) is executed. In qdiscs that eventually call qdisc_tree_reduce_backlog during init or change (such as fq, hhf, choke, etc), an issue arises. For example, executing the following commands: sudo tc qdisc add dev lo root handle a: htb default 2 sudo tc qdisc add dev lo parent a: handle beef fq Qdiscs such as fq, hhf, choke, etc unconditionally invoke qdisc_tree_reduce_backlog() in their control path init() or change() which then causes a failure to find the child class; however, that does not stop the unconditional invocation of the assumed child qdisc's qlen_notify with a null class. All these qdiscs make the assumption that class is non-null. The solution is ensure that qdisc_leaf() which looks up the parent class, and is invoked prior to qdisc_create(), should return failure on not finding the class. In this patch, we leverage qdisc_leaf to return ERR_PTRs whenever the parentid doesn't correspond to a class, so that we can detect it earlier on and abort before qdisc_create is called. [1] https://lore.kernel.org/netdev/[email protected]/ Fixes: 5e50da0 ("[NET_SCHED]: Fix endless loops (part 2): "simple" qdiscs") Reported-by: [email protected] Closes: https://lore.kernel.org/netdev/[email protected]/ Reported-by: [email protected] Closes: https://lore.kernel.org/netdev/[email protected]/ Reported-by: [email protected] Closes: https://lore.kernel.org/netdev/[email protected]/ Reported-by: [email protected] Closes: https://lore.kernel.org/netdev/[email protected]/ Reported-by: [email protected] Closes: https://lore.kernel.org/netdev/[email protected]/ Acked-by: Jamal Hadi Salim <[email protected]> Reviewed-by: Cong Wang <[email protected]> Signed-off-by: Victor Nogueira <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 02c4d6c commit ffdde7b

File tree

1 file changed

+16
-7
lines changed

1 file changed

+16
-7
lines changed

net/sched/sch_api.c

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -336,17 +336,22 @@ struct Qdisc *qdisc_lookup_rcu(struct net_device *dev, u32 handle)
336336
return q;
337337
}
338338

339-
static struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid)
339+
static struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid,
340+
struct netlink_ext_ack *extack)
340341
{
341342
unsigned long cl;
342343
const struct Qdisc_class_ops *cops = p->ops->cl_ops;
343344

344-
if (cops == NULL)
345-
return NULL;
345+
if (cops == NULL) {
346+
NL_SET_ERR_MSG(extack, "Parent qdisc is not classful");
347+
return ERR_PTR(-EOPNOTSUPP);
348+
}
346349
cl = cops->find(p, classid);
347350

348-
if (cl == 0)
349-
return NULL;
351+
if (cl == 0) {
352+
NL_SET_ERR_MSG(extack, "Specified class not found");
353+
return ERR_PTR(-ENOENT);
354+
}
350355
return cops->leaf(p, cl);
351356
}
352357

@@ -1490,7 +1495,7 @@ static int __tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
14901495
NL_SET_ERR_MSG(extack, "Failed to find qdisc with specified classid");
14911496
return -ENOENT;
14921497
}
1493-
q = qdisc_leaf(p, clid);
1498+
q = qdisc_leaf(p, clid, extack);
14941499
} else if (dev_ingress_queue(dev)) {
14951500
q = rtnl_dereference(dev_ingress_queue(dev)->qdisc_sleeping);
14961501
}
@@ -1501,6 +1506,8 @@ static int __tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
15011506
NL_SET_ERR_MSG(extack, "Cannot find specified qdisc on specified device");
15021507
return -ENOENT;
15031508
}
1509+
if (IS_ERR(q))
1510+
return PTR_ERR(q);
15041511

15051512
if (tcm->tcm_handle && q->handle != tcm->tcm_handle) {
15061513
NL_SET_ERR_MSG(extack, "Invalid handle");
@@ -1602,7 +1609,9 @@ static int __tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
16021609
NL_SET_ERR_MSG(extack, "Failed to find specified qdisc");
16031610
return -ENOENT;
16041611
}
1605-
q = qdisc_leaf(p, clid);
1612+
q = qdisc_leaf(p, clid, extack);
1613+
if (IS_ERR(q))
1614+
return PTR_ERR(q);
16061615
} else if (dev_ingress_queue_create(dev)) {
16071616
q = rtnl_dereference(dev_ingress_queue(dev)->qdisc_sleeping);
16081617
}

0 commit comments

Comments
 (0)