OSDN Git Service

net: sched: sch_qfq: Fix UAF in qfq_dequeue()
[tomoyo/tomoyo-test1.git] / net / sched / sch_qfq.c
index 1a25752..546c10a 100644 (file)
@@ -974,10 +974,13 @@ static void qfq_update_eligible(struct qfq_sched *q)
 }
 
 /* Dequeue head packet of the head class in the DRR queue of the aggregate. */
-static void agg_dequeue(struct qfq_aggregate *agg,
-                       struct qfq_class *cl, unsigned int len)
+static struct sk_buff *agg_dequeue(struct qfq_aggregate *agg,
+                                  struct qfq_class *cl, unsigned int len)
 {
-       qdisc_dequeue_peeked(cl->qdisc);
+       struct sk_buff *skb = qdisc_dequeue_peeked(cl->qdisc);
+
+       if (!skb)
+               return NULL;
 
        cl->deficit -= (int) len;
 
@@ -987,6 +990,8 @@ static void agg_dequeue(struct qfq_aggregate *agg,
                cl->deficit += agg->lmax;
                list_move_tail(&cl->alist, &agg->active);
        }
+
+       return skb;
 }
 
 static inline struct sk_buff *qfq_peek_skb(struct qfq_aggregate *agg,
@@ -1132,11 +1137,18 @@ static struct sk_buff *qfq_dequeue(struct Qdisc *sch)
        if (!skb)
                return NULL;
 
-       qdisc_qstats_backlog_dec(sch, skb);
        sch->q.qlen--;
+
+       skb = agg_dequeue(in_serv_agg, cl, len);
+
+       if (!skb) {
+               sch->q.qlen++;
+               return NULL;
+       }
+
+       qdisc_qstats_backlog_dec(sch, skb);
        qdisc_bstats_update(sch, skb);
 
-       agg_dequeue(in_serv_agg, cl, len);
        /* If lmax is lowered, through qfq_change_class, for a class
         * owning pending packets with larger size than the new value
         * of lmax, then the following condition may hold.