]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
net_sched: drr: Fix double list add in class with netem as child qdisc
authorVictor Nogueira <victor@mojatatu.com>
Fri, 25 Apr 2025 22:07:05 +0000 (19:07 -0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 9 May 2025 07:43:59 +0000 (09:43 +0200)
[ Upstream commit f99a3fbf023e20b626be4b0f042463d598050c9a ]

As described in Gerrard's report [1], there are use cases where a netem
child qdisc will make the parent qdisc's enqueue callback reentrant.
In the case of drr, there won't be a UAF, but the code will add the same
classifier to the list twice, which will cause memory corruption.

In addition to checking for qlen being zero, this patch checks whether the
class was already added to the active_list (cl_is_active) before adding
to the list to cover for the reentrant case.

[1] https://lore.kernel.org/netdev/CAHcdcOm+03OD2j6R0=YHKqmy=VgJ8xEOKuP6c7mSgnp-TEJJbw@mail.gmail.com/

Fixes: 37d9cf1a3ce3 ("sched: Fix detection of empty queues in child qdiscs")
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: Victor Nogueira <victor@mojatatu.com>
Link: https://patch.msgid.link/20250425220710.3964791-2-victor@mojatatu.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
net/sched/sch_drr.c

index 19901e77cd3b7f1aab28da8b2469de310b24d920..edadb3a7bd1429101bf3cb6940a1f4b947c48943 100644 (file)
@@ -35,6 +35,11 @@ struct drr_sched {
        struct Qdisc_class_hash         clhash;
 };
 
+static bool cl_is_active(struct drr_class *cl)
+{
+       return !list_empty(&cl->alist);
+}
+
 static struct drr_class *drr_find_class(struct Qdisc *sch, u32 classid)
 {
        struct drr_sched *q = qdisc_priv(sch);
@@ -336,7 +341,6 @@ static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch,
        struct drr_sched *q = qdisc_priv(sch);
        struct drr_class *cl;
        int err = 0;
-       bool first;
 
        cl = drr_classify(skb, sch, &err);
        if (cl == NULL) {
@@ -346,7 +350,6 @@ static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch,
                return err;
        }
 
-       first = !cl->qdisc->q.qlen;
        err = qdisc_enqueue(skb, cl->qdisc, to_free);
        if (unlikely(err != NET_XMIT_SUCCESS)) {
                if (net_xmit_drop_count(err)) {
@@ -356,7 +359,7 @@ static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch,
                return err;
        }
 
-       if (first) {
+       if (!cl_is_active(cl)) {
                list_add_tail(&cl->alist, &q->active);
                cl->deficit = cl->quantum;
        }