]> git.ipfire.org Git - people/arne_f/kernel.git/commitdiff
Merge branch 'net-sched-init-failure-fixes'
authorDavid S. Miller <davem@davemloft.net>
Wed, 30 Aug 2017 22:26:12 +0000 (15:26 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 30 Aug 2017 22:26:12 +0000 (15:26 -0700)
Nikolay Aleksandrov says:

====================
net/sched: init failure fixes

I went over all qdiscs' init, destroy and reset callbacks and found the
issues fixed in each patch. Mostly they are null pointer dereferences due
to uninitialized timer (qdisc watchdog) or double frees due to ->destroy
cleaning up a second time. There's more information in each patch.
I've tested these by either sending wrong attributes from user-spaces, no
attributes or by simulating memory alloc failure where applicable. Also
tried all of the qdiscs as a default qdisc.

Most of these bugs were present before commit 87b60cfacf9f, I've tried to
include proper fixes tags in each patch.

I haven't included individual patch acks in the set, I'd appreciate it if
you take another look and resend them.
====================

Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/sched/sch_cbq.c
net/sched/sch_fq_codel.c
net/sched/sch_hfsc.c
net/sched/sch_hhf.c
net/sched/sch_htb.c
net/sched/sch_multiq.c
net/sched/sch_netem.c
net/sched/sch_sfq.c
net/sched/sch_tbf.c

index 780db43300b16284192b24006b0ae8677adbe505..156c8a33c6777a644c77b1adec9057b482bac109 100644 (file)
@@ -1139,6 +1139,13 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt)
        struct tc_ratespec *r;
        int err;
 
+       qdisc_watchdog_init(&q->watchdog, sch);
+       hrtimer_init(&q->delay_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
+       q->delay_timer.function = cbq_undelay;
+
+       if (!opt)
+               return -EINVAL;
+
        err = nla_parse_nested(tb, TCA_CBQ_MAX, opt, cbq_policy, NULL);
        if (err < 0)
                return err;
@@ -1177,9 +1184,6 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt)
        q->link.avpkt = q->link.allot/2;
        q->link.minidle = -0x7FFFFFFF;
 
-       qdisc_watchdog_init(&q->watchdog, sch);
-       hrtimer_init(&q->delay_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
-       q->delay_timer.function = cbq_undelay;
        q->toplevel = TC_CBQ_MAXLEVEL;
        q->now = psched_get_time();
 
index 337f2d6d81e42e278b63443d904955e2c6692f03..2c0c05f2cc34a9de51390c45f29dd8db810075c7 100644 (file)
@@ -491,10 +491,8 @@ static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt)
                if (!q->flows)
                        return -ENOMEM;
                q->backlogs = kvzalloc(q->flows_cnt * sizeof(u32), GFP_KERNEL);
-               if (!q->backlogs) {
-                       kvfree(q->flows);
+               if (!q->backlogs)
                        return -ENOMEM;
-               }
                for (i = 0; i < q->flows_cnt; i++) {
                        struct fq_codel_flow *flow = q->flows + i;
 
index fd15200f86273add7d6c8c4a18aaef912aba7411..11ab8dace901534b23b8f376ac704f995dc6b66b 100644 (file)
@@ -1418,6 +1418,8 @@ hfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt)
        struct tc_hfsc_qopt *qopt;
        int err;
 
+       qdisc_watchdog_init(&q->watchdog, sch);
+
        if (opt == NULL || nla_len(opt) < sizeof(*qopt))
                return -EINVAL;
        qopt = nla_data(opt);
@@ -1430,7 +1432,7 @@ hfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt)
 
        err = tcf_block_get(&q->root.block, &q->root.filter_list);
        if (err)
-               goto err_tcf;
+               return err;
 
        q->root.cl_common.classid = sch->handle;
        q->root.refcnt  = 1;
@@ -1448,13 +1450,7 @@ hfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt)
        qdisc_class_hash_insert(&q->clhash, &q->root.cl_common);
        qdisc_class_hash_grow(sch, &q->clhash);
 
-       qdisc_watchdog_init(&q->watchdog, sch);
-
        return 0;
-
-err_tcf:
-       qdisc_class_hash_destroy(&q->clhash);
-       return err;
 }
 
 static int
index 51d3ba682af9ba0f69a3f3c3036519bf527cdee0..73a53c08091baafde3ef776ce9ea99cac9edfd9d 100644 (file)
@@ -477,6 +477,9 @@ static void hhf_destroy(struct Qdisc *sch)
                kvfree(q->hhf_valid_bits[i]);
        }
 
+       if (!q->hh_flows)
+               return;
+
        for (i = 0; i < HH_FLOWS_CNT; i++) {
                struct hh_flow_state *flow, *next;
                struct list_head *head = &q->hh_flows[i];
index 5d65ec5207e91202d501a83c983793f2e923f075..5bf5177b2bd3f6aa1b0ba9e4e59a946e1c739e0a 100644 (file)
@@ -1017,6 +1017,9 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt)
        int err;
        int i;
 
+       qdisc_watchdog_init(&q->watchdog, sch);
+       INIT_WORK(&q->work, htb_work_func);
+
        if (!opt)
                return -EINVAL;
 
@@ -1041,8 +1044,6 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt)
        for (i = 0; i < TC_HTB_NUMPRIO; i++)
                INIT_LIST_HEAD(q->drops + i);
 
-       qdisc_watchdog_init(&q->watchdog, sch);
-       INIT_WORK(&q->work, htb_work_func);
        qdisc_skb_head_init(&q->direct_queue);
 
        if (tb[TCA_HTB_DIRECT_QLEN])
index f143b7bbaa0d5b00d01d36e9f6eefb54bf677e8f..9c454f5d6c38820512485cceecbc06c9fa86f634 100644 (file)
@@ -257,12 +257,7 @@ static int multiq_init(struct Qdisc *sch, struct nlattr *opt)
        for (i = 0; i < q->max_bands; i++)
                q->queues[i] = &noop_qdisc;
 
-       err = multiq_tune(sch, opt);
-
-       if (err)
-               kfree(q->queues);
-
-       return err;
+       return multiq_tune(sch, opt);
 }
 
 static int multiq_dump(struct Qdisc *sch, struct sk_buff *skb)
index 1b3dd6190e9386c6f8104153eb9fdc0255e03ac5..14d1724e0dc436f49da643be8606be273ce22ebd 100644 (file)
@@ -933,11 +933,11 @@ static int netem_init(struct Qdisc *sch, struct nlattr *opt)
        struct netem_sched_data *q = qdisc_priv(sch);
        int ret;
 
+       qdisc_watchdog_init(&q->watchdog, sch);
+
        if (!opt)
                return -EINVAL;
 
-       qdisc_watchdog_init(&q->watchdog, sch);
-
        q->loss_model = CLG_RANDOM;
        ret = netem_change(sch, opt);
        if (ret)
index 82469ef9655eb65431477215bb2df0fb2737f905..fc69fc5956e9d4d2dbfe645e4c25e83328517371 100644 (file)
@@ -716,13 +716,13 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt)
        int i;
        int err;
 
+       setup_deferrable_timer(&q->perturb_timer, sfq_perturbation,
+                              (unsigned long)sch);
+
        err = tcf_block_get(&q->block, &q->filter_list);
        if (err)
                return err;
 
-       setup_deferrable_timer(&q->perturb_timer, sfq_perturbation,
-                              (unsigned long)sch);
-
        for (i = 0; i < SFQ_MAX_DEPTH + 1; i++) {
                q->dep[i].next = i + SFQ_MAX_FLOWS;
                q->dep[i].prev = i + SFQ_MAX_FLOWS;
index b2e4b6ad241a8e538c654ef4e8417ab756740a45..493270f0d5b055fa07d4dee2b35ec9d40bddc3d0 100644 (file)
@@ -425,12 +425,13 @@ static int tbf_init(struct Qdisc *sch, struct nlattr *opt)
 {
        struct tbf_sched_data *q = qdisc_priv(sch);
 
+       qdisc_watchdog_init(&q->watchdog, sch);
+       q->qdisc = &noop_qdisc;
+
        if (opt == NULL)
                return -EINVAL;
 
        q->t_c = ktime_get_ns();
-       qdisc_watchdog_init(&q->watchdog, sch);
-       q->qdisc = &noop_qdisc;
 
        return tbf_change(sch, opt);
 }