Merge branch 'net-sched-init-failure-fixes'

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 87b60cfacf, 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>
This commit is contained in:
David S. Miller 2017-08-30 15:26:12 -07:00
commit f5c3dd4b53
9 changed files with 26 additions and 28 deletions

View File

@ -1139,6 +1139,13 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt)
struct tc_ratespec *r; struct tc_ratespec *r;
int err; 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); err = nla_parse_nested(tb, TCA_CBQ_MAX, opt, cbq_policy, NULL);
if (err < 0) if (err < 0)
return err; 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.avpkt = q->link.allot/2;
q->link.minidle = -0x7FFFFFFF; 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->toplevel = TC_CBQ_MAXLEVEL;
q->now = psched_get_time(); q->now = psched_get_time();

View File

@ -491,10 +491,8 @@ static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt)
if (!q->flows) if (!q->flows)
return -ENOMEM; return -ENOMEM;
q->backlogs = kvzalloc(q->flows_cnt * sizeof(u32), GFP_KERNEL); q->backlogs = kvzalloc(q->flows_cnt * sizeof(u32), GFP_KERNEL);
if (!q->backlogs) { if (!q->backlogs)
kvfree(q->flows);
return -ENOMEM; return -ENOMEM;
}
for (i = 0; i < q->flows_cnt; i++) { for (i = 0; i < q->flows_cnt; i++) {
struct fq_codel_flow *flow = q->flows + i; struct fq_codel_flow *flow = q->flows + i;

View File

@ -1418,6 +1418,8 @@ hfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt)
struct tc_hfsc_qopt *qopt; struct tc_hfsc_qopt *qopt;
int err; int err;
qdisc_watchdog_init(&q->watchdog, sch);
if (opt == NULL || nla_len(opt) < sizeof(*qopt)) if (opt == NULL || nla_len(opt) < sizeof(*qopt))
return -EINVAL; return -EINVAL;
qopt = nla_data(opt); 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); err = tcf_block_get(&q->root.block, &q->root.filter_list);
if (err) if (err)
goto err_tcf; return err;
q->root.cl_common.classid = sch->handle; q->root.cl_common.classid = sch->handle;
q->root.refcnt = 1; 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_insert(&q->clhash, &q->root.cl_common);
qdisc_class_hash_grow(sch, &q->clhash); qdisc_class_hash_grow(sch, &q->clhash);
qdisc_watchdog_init(&q->watchdog, sch);
return 0; return 0;
err_tcf:
qdisc_class_hash_destroy(&q->clhash);
return err;
} }
static int static int

View File

@ -477,6 +477,9 @@ static void hhf_destroy(struct Qdisc *sch)
kvfree(q->hhf_valid_bits[i]); kvfree(q->hhf_valid_bits[i]);
} }
if (!q->hh_flows)
return;
for (i = 0; i < HH_FLOWS_CNT; i++) { for (i = 0; i < HH_FLOWS_CNT; i++) {
struct hh_flow_state *flow, *next; struct hh_flow_state *flow, *next;
struct list_head *head = &q->hh_flows[i]; struct list_head *head = &q->hh_flows[i];

View File

@ -1017,6 +1017,9 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt)
int err; int err;
int i; int i;
qdisc_watchdog_init(&q->watchdog, sch);
INIT_WORK(&q->work, htb_work_func);
if (!opt) if (!opt)
return -EINVAL; return -EINVAL;
@ -1041,8 +1044,6 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt)
for (i = 0; i < TC_HTB_NUMPRIO; i++) for (i = 0; i < TC_HTB_NUMPRIO; i++)
INIT_LIST_HEAD(q->drops + 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); qdisc_skb_head_init(&q->direct_queue);
if (tb[TCA_HTB_DIRECT_QLEN]) if (tb[TCA_HTB_DIRECT_QLEN])

View File

@ -257,12 +257,7 @@ static int multiq_init(struct Qdisc *sch, struct nlattr *opt)
for (i = 0; i < q->max_bands; i++) for (i = 0; i < q->max_bands; i++)
q->queues[i] = &noop_qdisc; q->queues[i] = &noop_qdisc;
err = multiq_tune(sch, opt); return multiq_tune(sch, opt);
if (err)
kfree(q->queues);
return err;
} }
static int multiq_dump(struct Qdisc *sch, struct sk_buff *skb) static int multiq_dump(struct Qdisc *sch, struct sk_buff *skb)

View File

@ -933,11 +933,11 @@ static int netem_init(struct Qdisc *sch, struct nlattr *opt)
struct netem_sched_data *q = qdisc_priv(sch); struct netem_sched_data *q = qdisc_priv(sch);
int ret; int ret;
qdisc_watchdog_init(&q->watchdog, sch);
if (!opt) if (!opt)
return -EINVAL; return -EINVAL;
qdisc_watchdog_init(&q->watchdog, sch);
q->loss_model = CLG_RANDOM; q->loss_model = CLG_RANDOM;
ret = netem_change(sch, opt); ret = netem_change(sch, opt);
if (ret) if (ret)

View File

@ -716,13 +716,13 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt)
int i; int i;
int err; int err;
setup_deferrable_timer(&q->perturb_timer, sfq_perturbation,
(unsigned long)sch);
err = tcf_block_get(&q->block, &q->filter_list); err = tcf_block_get(&q->block, &q->filter_list);
if (err) if (err)
return err; return err;
setup_deferrable_timer(&q->perturb_timer, sfq_perturbation,
(unsigned long)sch);
for (i = 0; i < SFQ_MAX_DEPTH + 1; i++) { for (i = 0; i < SFQ_MAX_DEPTH + 1; i++) {
q->dep[i].next = i + SFQ_MAX_FLOWS; q->dep[i].next = i + SFQ_MAX_FLOWS;
q->dep[i].prev = i + SFQ_MAX_FLOWS; q->dep[i].prev = i + SFQ_MAX_FLOWS;

View File

@ -425,12 +425,13 @@ static int tbf_init(struct Qdisc *sch, struct nlattr *opt)
{ {
struct tbf_sched_data *q = qdisc_priv(sch); struct tbf_sched_data *q = qdisc_priv(sch);
qdisc_watchdog_init(&q->watchdog, sch);
q->qdisc = &noop_qdisc;
if (opt == NULL) if (opt == NULL)
return -EINVAL; return -EINVAL;
q->t_c = ktime_get_ns(); q->t_c = ktime_get_ns();
qdisc_watchdog_init(&q->watchdog, sch);
q->qdisc = &noop_qdisc;
return tbf_change(sch, opt); return tbf_change(sch, opt);
} }