Skip to content

Commit f139f37

Browse files
vbnogueirakuba-moo
authored andcommitted
net_sched: qfq: Fix double list add in class with netem as child qdisc
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 qfq, there won't be a UAF, but the code will add the same classifier to the list twice, which will cause memory corruption. This patch checks whether the class was already added to the agg->active list (cl_is_active) before doing the addition to cater for the reentrant case. [1] https://lore.kernel.org/netdev/CAHcdcOm+03OD2j6R0=YHKqmy=VgJ8xEOKuP6c7mSgnp-TEJJbw@mail.gmail.com/ Fixes: 37d9cf1 ("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-5-victor@mojatatu.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1 parent 1a6d0c0 commit f139f37

File tree

1 file changed

+7
-4
lines changed

1 file changed

+7
-4
lines changed

net/sched/sch_qfq.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,11 @@ struct qfq_sched {
202202
*/
203203
enum update_reason {enqueue, requeue};
204204

205+
static bool cl_is_active(struct qfq_class *cl)
206+
{
207+
return !list_empty(&cl->alist);
208+
}
209+
205210
static struct qfq_class *qfq_find_class(struct Qdisc *sch, u32 classid)
206211
{
207212
struct qfq_sched *q = qdisc_priv(sch);
@@ -1215,7 +1220,6 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch,
12151220
struct qfq_class *cl;
12161221
struct qfq_aggregate *agg;
12171222
int err = 0;
1218-
bool first;
12191223

12201224
cl = qfq_classify(skb, sch, &err);
12211225
if (cl == NULL) {
@@ -1237,7 +1241,6 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch,
12371241
}
12381242

12391243
gso_segs = skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 1;
1240-
first = !cl->qdisc->q.qlen;
12411244
err = qdisc_enqueue(skb, cl->qdisc, to_free);
12421245
if (unlikely(err != NET_XMIT_SUCCESS)) {
12431246
pr_debug("qfq_enqueue: enqueue failed %d\n", err);
@@ -1253,8 +1256,8 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch,
12531256
++sch->q.qlen;
12541257

12551258
agg = cl->agg;
1256-
/* if the queue was not empty, then done here */
1257-
if (!first) {
1259+
/* if the class is active, then done here */
1260+
if (cl_is_active(cl)) {
12581261
if (unlikely(skb == cl->qdisc->ops->peek(cl->qdisc)) &&
12591262
list_first_entry(&agg->active, struct qfq_class, alist)
12601263
== cl && cl->deficit < len)

0 commit comments

Comments
 (0)