pfifo_fast_init() and pfifo_fast_change_tx_queue_len() allocate skb ring arrays sized by dev->tx_queue_len with GFP_KERNEL and no upper bound. An unprivileged user (via unshare -Urn) can set a huge tx_queue_len and attach many pfifo_fast qdiscs to exhaust global memory, causing a system-wide OOM. Cap the ring size to 65535 (default tx_queue_len is 1000) and use GFP_KERNEL_ACCOUNT so the allocations are charged to the allocating process's memory cgroup. A pr_warn_ratelimited is emitted when the ring size is capped so the user is not silently clamped. Conditions to recreate the bug: - CONFIG_NET_SCHED=y, CONFIG_VETH=y, CONFIG_USER_NS=y, CONFIG_NET_NS=y. - Unprivileged user in a fresh user+net namespace (unshare -Urn). - Create a veth pair, set tx_queue_len to a huge value (e.g. 500000) while the devices are down. - Attach mq at root, then replace each child queue with pfifo_fast: tc qdisc replace dev veth0 root handle 1: mq tc qdisc replace dev veth0 parent 1:1 pfifo_fast tc qdisc replace dev veth0 parent 1:2 pfifo_fast ... - Repeat across many veth pairs. Each pfifo_fast allocates 3 skb_array rings of tx_queue_len entries (~12MB per qdisc at QLEN=500000). - On the unfixed kernel this exhausts global memory in ~28 iterations on a 2GB guest -> OOM panic. On the fixed kernel the ring is capped to 65535 entries (~1.5MB per qdisc) and charged to the memcg. Fixes: c5ad119fb6c0 ("net: sched: pfifo_fast use skb_array") Reported-by: vega@nebusec.ai Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim --- net/sched/sch_generic.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index ef2b4bf51564..e98fd236afa0 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -910,11 +910,17 @@ static int pfifo_fast_init(struct Qdisc *qdisc, struct nlattr *opt, if (!qlen) return -EINVAL; + if (qlen > 65535) { + pr_warn_ratelimited("pfifo_fast: capping ring size %u to 65535 for dev %s\n", + qlen, qdisc_dev(qdisc)->name); + qlen = 65535; + } + for (prio = 0; prio < PFIFO_FAST_BANDS; prio++) { struct skb_array *q = band2list(priv, prio); int err; - err = skb_array_init(q, qlen, GFP_KERNEL); + err = skb_array_init(q, qlen, GFP_KERNEL_ACCOUNT); if (err) return -ENOMEM; } @@ -957,8 +963,14 @@ static int pfifo_fast_change_tx_queue_len(struct Qdisc *sch, bands[prio] = q; } + if (new_len > 65535) { + pr_warn_ratelimited("pfifo_fast: capping ring size %u to 65535 for dev %s\n", + new_len, qdisc_dev(sch)->name); + new_len = 65535; + } + return skb_array_resize_multiple_bh(bands, PFIFO_FAST_BANDS, new_len, - GFP_KERNEL); + GFP_KERNEL_ACCOUNT); } struct Qdisc_ops pfifo_fast_ops __read_mostly = { -- 2.43.0