sch_cbq: validate TCA_CBQ_WRROPT to avoid crash
[linux-2.6-block.git] / net / sched / sch_multiq.c
index e1087746f6a29f83efcc09444b83c8f0f967f52d..b2b7fdb06fc62a7325ed573dd856ef81cde9159a 100644 (file)
@@ -174,7 +174,8 @@ static int multiq_tune(struct Qdisc *sch, struct nlattr *opt,
 {
        struct multiq_sched_data *q = qdisc_priv(sch);
        struct tc_multiq_qopt *qopt;
-       int i;
+       struct Qdisc **removed;
+       int i, n_removed = 0;
 
        if (!netif_is_multiqueue(qdisc_dev(sch)))
                return -EOPNOTSUPP;
@@ -185,6 +186,11 @@ static int multiq_tune(struct Qdisc *sch, struct nlattr *opt,
 
        qopt->bands = qdisc_dev(sch)->real_num_tx_queues;
 
+       removed = kmalloc(sizeof(*removed) * (q->max_bands - q->bands),
+                         GFP_KERNEL);
+       if (!removed)
+               return -ENOMEM;
+
        sch_tree_lock(sch);
        q->bands = qopt->bands;
        for (i = q->bands; i < q->max_bands; i++) {
@@ -192,13 +198,17 @@ static int multiq_tune(struct Qdisc *sch, struct nlattr *opt,
                        struct Qdisc *child = q->queues[i];
 
                        q->queues[i] = &noop_qdisc;
-                       qdisc_tree_flush_backlog(child);
-                       qdisc_put(child);
+                       qdisc_purge_queue(child);
+                       removed[n_removed++] = child;
                }
        }
 
        sch_tree_unlock(sch);
 
+       for (i = 0; i < n_removed; i++)
+               qdisc_put(removed[i]);
+       kfree(removed);
+
        for (i = 0; i < q->bands; i++) {
                if (q->queues[i] == &noop_qdisc) {
                        struct Qdisc *child, *old;
@@ -213,11 +223,10 @@ static int multiq_tune(struct Qdisc *sch, struct nlattr *opt,
                                if (child != &noop_qdisc)
                                        qdisc_hash_add(child, true);
 
-                               if (old != &noop_qdisc) {
-                                       qdisc_tree_flush_backlog(old);
-                                       qdisc_put(old);
-                               }
+                               if (old != &noop_qdisc)
+                                       qdisc_purge_queue(old);
                                sch_tree_unlock(sch);
+                               qdisc_put(old);
                        }
                }
        }