mirror of
https://github.com/torvalds/linux.git
synced 2026-09-12 12:34:02 +02:00
net/sched: defer qdisc freeing after failed creation
An RTM_NEWQDISC request can make clsact bind a populated shared ingress
block during ->init(), publishing an embedded mini_Qdisc to lockless
readers. If the same request has an invalid TCA_RATE, estimator setup
fails after ->init(); the unwind removes the pointer but synchronously
frees its containing qdisc while tc_run() may still hold it.
Retire failed qdiscs through the same RCU helper as normal destruction.
Inline the synchronous free into the callback now that no direct callers
remain.
Fixes: 51ab2994c3 ("net: sched: allow ingress and clsact qdiscs to share filter blocks")
Reported-by: Xiang Mei <xmei5@asu.edu>
Link: https://lore.kernel.org/netdev/20260805102505.740806-1-david.lee@trailofbits.com/
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
Link: https://patch.msgid.link/20260902155231.2149915-2-bestswngs@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
2b4707a149
commit
e6662f2100
|
|
@ -793,7 +793,7 @@ void qdisc_offload_query_caps(struct net_device *dev,
|
|||
struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
|
||||
const struct Qdisc_ops *ops,
|
||||
struct netlink_ext_ack *extack);
|
||||
void qdisc_free(struct Qdisc *qdisc);
|
||||
void qdisc_free_rcu(struct Qdisc *qdisc);
|
||||
struct Qdisc *qdisc_create_dflt(struct netdev_queue *dev_queue,
|
||||
const struct Qdisc_ops *ops, u32 parentid,
|
||||
struct netlink_ext_ack *extack);
|
||||
|
|
|
|||
|
|
@ -1385,7 +1385,7 @@ static struct Qdisc *qdisc_create(struct net_device *dev,
|
|||
err_out3:
|
||||
qdisc_lock_uninit(sch, ops);
|
||||
netdev_put(dev, &sch->dev_tracker);
|
||||
qdisc_free(sch);
|
||||
qdisc_free_rcu(sch);
|
||||
err_out2:
|
||||
bpf_module_put(ops, ops->owner);
|
||||
err_out:
|
||||
|
|
|
|||
|
|
@ -1086,21 +1086,21 @@ void qdisc_reset(struct Qdisc *qdisc)
|
|||
}
|
||||
EXPORT_SYMBOL(qdisc_reset);
|
||||
|
||||
void qdisc_free(struct Qdisc *qdisc)
|
||||
{
|
||||
if (qdisc_is_percpu_stats(qdisc)) {
|
||||
free_percpu(qdisc->cpu_bstats);
|
||||
free_percpu(qdisc->cpu_qstats);
|
||||
}
|
||||
|
||||
kfree(qdisc);
|
||||
}
|
||||
|
||||
static void qdisc_free_cb(struct rcu_head *head)
|
||||
{
|
||||
struct Qdisc *q = container_of(head, struct Qdisc, rcu);
|
||||
|
||||
qdisc_free(q);
|
||||
if (qdisc_is_percpu_stats(q)) {
|
||||
free_percpu(q->cpu_bstats);
|
||||
free_percpu(q->cpu_qstats);
|
||||
}
|
||||
|
||||
kfree(q);
|
||||
}
|
||||
|
||||
void qdisc_free_rcu(struct Qdisc *qdisc)
|
||||
{
|
||||
call_rcu(&qdisc->rcu, qdisc_free_cb);
|
||||
}
|
||||
|
||||
static void __qdisc_destroy(struct Qdisc *qdisc)
|
||||
|
|
@ -1127,7 +1127,7 @@ static void __qdisc_destroy(struct Qdisc *qdisc)
|
|||
|
||||
trace_qdisc_destroy(qdisc);
|
||||
|
||||
call_rcu(&qdisc->rcu, qdisc_free_cb);
|
||||
qdisc_free_rcu(qdisc);
|
||||
}
|
||||
|
||||
void qdisc_destroy(struct Qdisc *qdisc)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user