* [PATCH net] net/sched: Abort __tc_modify_qdisc if parent class does not exist
@ 2025-07-04 16:34 Victor Nogueira
2025-07-04 23:54 ` Cong Wang
2025-07-07 13:59 ` Jakub Kicinski
0 siblings, 2 replies; 4+ messages in thread
From: Victor Nogueira @ 2025-07-04 16:34 UTC (permalink / raw)
To: davem, kuba, pabeni, edumazet, jhs, xiyou.wangcong, jiri
Cc: netdev, pctammela, syzbot+d8b58d7b0ad89a678a16,
syzbot+5eccb463fa89309d8bdc, syzbot+1261670bbdefc5485a06,
syzbot+4dadc5aecf80324d5a51, syzbot+15b96fc3aac35468fe77
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/d912cbd7-193b-4269-9857-525bee8bbb6a@gmail.com/
Fixes: 5e50da01d0ce ("[NET_SCHED]: Fix endless loops (part 2): "simple" qdiscs")
Reported-by: syzbot+d8b58d7b0ad89a678a16@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/netdev/68663c93.a70a0220.5d25f.0857.GAE@google.com/
Reported-by: syzbot+5eccb463fa89309d8bdc@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/netdev/68663c94.a70a0220.5d25f.0858.GAE@google.com/
Reported-by: syzbot+1261670bbdefc5485a06@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/netdev/686764a5.a00a0220.c7b3.0013.GAE@google.com/
Reported-by: syzbot+15b96fc3aac35468fe77@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/netdev/686764a5.a00a0220.c7b3.0014.GAE@google.com/
Reported-by: syzbot+4dadc5aecf80324d5a51@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/netdev/68679e81.a70a0220.29cf51.0016.GAE@google.com/
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: Victor Nogueira <victor@mojatatu.com>
---
net/sched/sch_api.c | 31 +++++++++++++++++++++----------
1 file changed, 21 insertions(+), 10 deletions(-)
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index d8a33486c511..659ab1c38518 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -336,17 +336,22 @@ struct Qdisc *qdisc_lookup_rcu(struct net_device *dev, u32 handle)
return q;
}
-static struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid)
+static struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid,
+ struct netlink_ext_ack *extack)
{
unsigned long cl;
const struct Qdisc_class_ops *cops = p->ops->cl_ops;
- if (cops == NULL)
- return NULL;
+ if (cops == NULL) {
+ NL_SET_ERR_MSG(extack, "Parent qdisc is not classful");
+ return ERR_PTR(-EOPNOTSUPP);
+ }
cl = cops->find(p, classid);
- if (cl == 0)
- return NULL;
+ if (cl == 0) {
+ NL_SET_ERR_MSG(extack, "Specified class not found");
+ return ERR_PTR(-ENOENT);
+ }
return cops->leaf(p, cl);
}
@@ -1490,16 +1495,20 @@ static int __tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
NL_SET_ERR_MSG(extack, "Failed to find qdisc with specified classid");
return -ENOENT;
}
- q = qdisc_leaf(p, clid);
+ q = qdisc_leaf(p, clid, extack);
} else if (dev_ingress_queue(dev)) {
q = rtnl_dereference(dev_ingress_queue(dev)->qdisc_sleeping);
}
} else {
q = rtnl_dereference(dev->qdisc);
}
- if (!q) {
- NL_SET_ERR_MSG(extack, "Cannot find specified qdisc on specified device");
- return -ENOENT;
+ if (IS_ERR_OR_NULL(q)) {
+ if (!q) {
+ NL_SET_ERR_MSG(extack,
+ "Cannot find specified qdisc on specified device");
+ return -ENOENT;
+ }
+ return PTR_ERR(q);
}
if (tcm->tcm_handle && q->handle != tcm->tcm_handle) {
@@ -1602,7 +1611,9 @@ static int __tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
NL_SET_ERR_MSG(extack, "Failed to find specified qdisc");
return -ENOENT;
}
- q = qdisc_leaf(p, clid);
+ q = qdisc_leaf(p, clid, extack);
+ if (IS_ERR(q))
+ return PTR_ERR(q);
} else if (dev_ingress_queue_create(dev)) {
q = rtnl_dereference(dev_ingress_queue(dev)->qdisc_sleeping);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH net] net/sched: Abort __tc_modify_qdisc if parent class does not exist
2025-07-04 16:34 [PATCH net] net/sched: Abort __tc_modify_qdisc if parent class does not exist Victor Nogueira
@ 2025-07-04 23:54 ` Cong Wang
2025-07-05 14:03 ` Jamal Hadi Salim
2025-07-07 13:59 ` Jakub Kicinski
1 sibling, 1 reply; 4+ messages in thread
From: Cong Wang @ 2025-07-04 23:54 UTC (permalink / raw)
To: Victor Nogueira
Cc: davem, kuba, pabeni, edumazet, jhs, jiri, netdev, pctammela,
syzbot+d8b58d7b0ad89a678a16, syzbot+5eccb463fa89309d8bdc,
syzbot+1261670bbdefc5485a06, syzbot+4dadc5aecf80324d5a51,
syzbot+15b96fc3aac35468fe77
On Fri, Jul 04, 2025 at 01:34:22PM -0300, Victor Nogueira wrote:
> 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.
>
Thanks for your quick and excellent catch!
Reviewed-by: Cong Wang <xiyou.wangcong@gmail.com>
Just one additional question, do we still need to safe-guard
qdisc_tree_reduce_backlog()? Below is actually what pops on my mind
immediately after reading your above description (before reading your
code):
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index c5e3673aadbe..5a32af623aa4 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -817,6 +817,8 @@ void qdisc_tree_reduce_backlog(struct Qdisc *sch, int n, int len)
cops = sch->ops->cl_ops;
if (notify && cops->qlen_notify) {
cl = cops->find(sch, parentid);
+ if (!cl)
+ break;
cops->qlen_notify(sch, cl);
}
sch->q.qlen -= n;
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH net] net/sched: Abort __tc_modify_qdisc if parent class does not exist
2025-07-04 23:54 ` Cong Wang
@ 2025-07-05 14:03 ` Jamal Hadi Salim
0 siblings, 0 replies; 4+ messages in thread
From: Jamal Hadi Salim @ 2025-07-05 14:03 UTC (permalink / raw)
To: Cong Wang
Cc: Victor Nogueira, davem, kuba, pabeni, edumazet, jiri, netdev,
pctammela, syzbot+d8b58d7b0ad89a678a16,
syzbot+5eccb463fa89309d8bdc, syzbot+1261670bbdefc5485a06,
syzbot+4dadc5aecf80324d5a51, syzbot+15b96fc3aac35468fe77
On Fri, Jul 4, 2025 at 7:54 PM Cong Wang <xiyou.wangcong@gmail.com> wrote:
>
> On Fri, Jul 04, 2025 at 01:34:22PM -0300, Victor Nogueira wrote:
> > 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.
> >
>
> Thanks for your quick and excellent catch!
>
> Reviewed-by: Cong Wang <xiyou.wangcong@gmail.com>
>
> Just one additional question, do we still need to safe-guard
> qdisc_tree_reduce_backlog()? Below is actually what pops on my mind
> immediately after reading your above description (before reading your
> code):
>
>
> diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
> index c5e3673aadbe..5a32af623aa4 100644
> --- a/net/sched/sch_api.c
> +++ b/net/sched/sch_api.c
> @@ -817,6 +817,8 @@ void qdisc_tree_reduce_backlog(struct Qdisc *sch, int n, int len)
> cops = sch->ops->cl_ops;
> if (notify && cops->qlen_notify) {
> cl = cops->find(sch, parentid);
> + if (!cl)
> + break;
> cops->qlen_notify(sch, cl);
> }
> sch->q.qlen -= n;
>
Sorry, meant to respond to this in last email: there's no need for
this extra piece because with the patch posted by Victor this code
will not be hit anymore.
cheers,
jamal
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net] net/sched: Abort __tc_modify_qdisc if parent class does not exist
2025-07-04 16:34 [PATCH net] net/sched: Abort __tc_modify_qdisc if parent class does not exist Victor Nogueira
2025-07-04 23:54 ` Cong Wang
@ 2025-07-07 13:59 ` Jakub Kicinski
1 sibling, 0 replies; 4+ messages in thread
From: Jakub Kicinski @ 2025-07-07 13:59 UTC (permalink / raw)
To: Victor Nogueira
Cc: davem, pabeni, edumazet, jhs, xiyou.wangcong, jiri, netdev,
pctammela, syzbot+d8b58d7b0ad89a678a16,
syzbot+5eccb463fa89309d8bdc, syzbot+1261670bbdefc5485a06,
syzbot+4dadc5aecf80324d5a51, syzbot+15b96fc3aac35468fe77
On Fri, 4 Jul 2025 13:34:22 -0300 Victor Nogueira wrote:
> - if (!q) {
> - NL_SET_ERR_MSG(extack, "Cannot find specified qdisc on specified device");
> - return -ENOENT;
> + if (IS_ERR_OR_NULL(q)) {
> + if (!q) {
> + NL_SET_ERR_MSG(extack,
> + "Cannot find specified qdisc on specified device");
> + return -ENOENT;
> + }
> + return PTR_ERR(q);
> }
nit: could you leave the existing handling as is?
if (!q) {
NL_SET_ERR_MSG(extack, "Cannot find specified qdisc on specified device");
return -ENOENT;
}
if (IS_ERR(q))
return PTR_ERR(q);
the double conditions increase indentation and confuse static analyzers
--
pw-bot: cr
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2025-07-07 13:59 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-04 16:34 [PATCH net] net/sched: Abort __tc_modify_qdisc if parent class does not exist Victor Nogueira
2025-07-04 23:54 ` Cong Wang
2025-07-05 14:03 ` Jamal Hadi Salim
2025-07-07 13:59 ` Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).