Skip to content

Commit

Permalink
net_sched: fix estimator lock selection for mq child qdiscs
Browse files Browse the repository at this point in the history
When new child qdiscs are attached to the mq qdisc, they are actually
attached as root qdiscs to the device queues. The lock selection for
new estimators incorrectly picks the root lock of the existing and
to be replaced qdisc, which results in a use-after-free once the old
qdisc has been destroyed.

Mark mq qdisc instances with a new flag and treat qdiscs attached to
mq as children similar to regular root qdiscs.

Additionally prevent estimators from being attached to the mq qdisc
itself since it only updates its byte and packet counters during dumps.

Signed-off-by: Patrick McHardy <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
kaber authored and davem330 committed Sep 10, 2009
1 parent ea6a634 commit 23bcf63
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 16 deletions.
1 change: 1 addition & 0 deletions include/net/sch_generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ struct Qdisc
#define TCQ_F_THROTTLED 2
#define TCQ_F_INGRESS 4
#define TCQ_F_CAN_BYPASS 8
#define TCQ_F_MQROOT 16
#define TCQ_F_WARN_NONWC (1 << 16)
int padded;
struct Qdisc_ops *ops;
Expand Down
42 changes: 26 additions & 16 deletions net/sched/sch_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,8 @@ static struct lock_class_key qdisc_rx_lock;

static struct Qdisc *
qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,
u32 parent, u32 handle, struct nlattr **tca, int *errp)
struct Qdisc *p, u32 parent, u32 handle,
struct nlattr **tca, int *errp)
{
int err;
struct nlattr *kind = tca[TCA_KIND];
Expand Down Expand Up @@ -810,24 +811,21 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,
if (tca[TCA_RATE]) {
spinlock_t *root_lock;

err = -EOPNOTSUPP;
if (sch->flags & TCQ_F_MQROOT)
goto err_out4;

if ((sch->parent != TC_H_ROOT) &&
!(sch->flags & TCQ_F_INGRESS))
!(sch->flags & TCQ_F_INGRESS) &&
(!p || !(p->flags & TCQ_F_MQROOT)))
root_lock = qdisc_root_sleeping_lock(sch);
else
root_lock = qdisc_lock(sch);

err = gen_new_estimator(&sch->bstats, &sch->rate_est,
root_lock, tca[TCA_RATE]);
if (err) {
/*
* Any broken qdiscs that would require
* a ops->reset() here? The qdisc was never
* in action so it shouldn't be necessary.
*/
if (ops->destroy)
ops->destroy(sch);
goto err_out3;
}
if (err)
goto err_out4;
}

qdisc_list_add(sch);
Expand All @@ -843,6 +841,15 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,
err_out:
*errp = err;
return NULL;

err_out4:
/*
* Any broken qdiscs that would require a ops->reset() here?
* The qdisc was never in action so it shouldn't be necessary.
*/
if (ops->destroy)
ops->destroy(sch);
goto err_out3;
}

static int qdisc_change(struct Qdisc *sch, struct nlattr **tca)
Expand All @@ -867,13 +874,16 @@ static int qdisc_change(struct Qdisc *sch, struct nlattr **tca)
qdisc_put_stab(sch->stab);
sch->stab = stab;

if (tca[TCA_RATE])
if (tca[TCA_RATE]) {
/* NB: ignores errors from replace_estimator
because change can't be undone. */
if (sch->flags & TCQ_F_MQROOT)
goto out;
gen_replace_estimator(&sch->bstats, &sch->rate_est,
qdisc_root_sleeping_lock(sch),
tca[TCA_RATE]);

}
out:
return 0;
}

Expand Down Expand Up @@ -1097,7 +1107,7 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
if (!(n->nlmsg_flags&NLM_F_CREATE))
return -ENOENT;
if (clid == TC_H_INGRESS)
q = qdisc_create(dev, &dev->rx_queue,
q = qdisc_create(dev, &dev->rx_queue, p,
tcm->tcm_parent, tcm->tcm_parent,
tca, &err);
else {
Expand All @@ -1106,7 +1116,7 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
if (p && p->ops->cl_ops && p->ops->cl_ops->select_queue)
ntx = p->ops->cl_ops->select_queue(p, tcm);

q = qdisc_create(dev, netdev_get_tx_queue(dev, ntx),
q = qdisc_create(dev, netdev_get_tx_queue(dev, ntx), p,
tcm->tcm_parent, tcm->tcm_handle,
tca, &err);
}
Expand Down
1 change: 1 addition & 0 deletions net/sched/sch_mq.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ static int mq_init(struct Qdisc *sch, struct nlattr *opt)
priv->qdiscs[ntx] = qdisc;
}

sch->flags |= TCQ_F_MQROOT;
return 0;

err:
Expand Down

0 comments on commit 23bcf63

Please sign in to comment.