CVE-2025-21702
Severity CVSS v4.0:
Pending analysis
Type:
Unavailable / Other
Publication date:
18/02/2025
Last modified:
13/03/2025
Description
In the Linux kernel, the following vulnerability has been resolved:<br />
<br />
pfifo_tail_enqueue: Drop new packet when sch->limit == 0<br />
<br />
Expected behaviour:<br />
In case we reach scheduler&#39;s limit, pfifo_tail_enqueue() will drop a<br />
packet in scheduler&#39;s queue and decrease scheduler&#39;s qlen by one.<br />
Then, pfifo_tail_enqueue() enqueue new packet and increase<br />
scheduler&#39;s qlen by one. Finally, pfifo_tail_enqueue() return<br />
`NET_XMIT_CN` status code.<br />
<br />
Weird behaviour:<br />
In case we set `sch->limit == 0` and trigger pfifo_tail_enqueue() on a<br />
scheduler that has no packet, the &#39;drop a packet&#39; step will do nothing.<br />
This means the scheduler&#39;s qlen still has value equal 0.<br />
Then, we continue to enqueue new packet and increase scheduler&#39;s qlen by<br />
one. In summary, we can leverage pfifo_tail_enqueue() to increase qlen by<br />
one and return `NET_XMIT_CN` status code.<br />
<br />
The problem is:<br />
Let&#39;s say we have two qdiscs: Qdisc_A and Qdisc_B.<br />
- Qdisc_A&#39;s type must have &#39;->graft()&#39; function to create parent/child relationship.<br />
Let&#39;s say Qdisc_A&#39;s type is `hfsc`. Enqueue packet to this qdisc will trigger `hfsc_enqueue`.<br />
- Qdisc_B&#39;s type is pfifo_head_drop. Enqueue packet to this qdisc will trigger `pfifo_tail_enqueue`.<br />
- Qdisc_B is configured to have `sch->limit == 0`.<br />
- Qdisc_A is configured to route the enqueued&#39;s packet to Qdisc_B.<br />
<br />
Enqueue packet through Qdisc_A will lead to:<br />
- hfsc_enqueue(Qdisc_A) -> pfifo_tail_enqueue(Qdisc_B)<br />
- Qdisc_B->q.qlen += 1<br />
- pfifo_tail_enqueue() return `NET_XMIT_CN`<br />
- hfsc_enqueue() check for `NET_XMIT_SUCCESS` and see `NET_XMIT_CN` => hfsc_enqueue() don&#39;t increase qlen of Qdisc_A.<br />
<br />
The whole process lead to a situation where Qdisc_A->q.qlen == 0 and Qdisc_B->q.qlen == 1.<br />
Replace &#39;hfsc&#39; with other type (for example: &#39;drr&#39;) still lead to the same problem.<br />
This violate the design where parent&#39;s qlen should equal to the sum of its childrens&#39;qlen.<br />
<br />
Bug impact: This issue can be used for user->kernel privilege escalation when it is reachable.
Impact
References to Advisories, Solutions, and Tools
- https://git.kernel.org/stable/c/020ecb76812a0526f4130ab5aeb6dc7c773e7ab9
- https://git.kernel.org/stable/c/647cef20e649c576dff271e018d5d15d998b629d
- https://git.kernel.org/stable/c/78285b53266d6d51fa4ff504a23df03852eba84e
- https://git.kernel.org/stable/c/79a955ea4a2e5ddf4a36328959de0de496419888
- https://git.kernel.org/stable/c/7a9723ec27aff5674f1fd4934608937f1d650980
- https://git.kernel.org/stable/c/a56a6e8589a9b98d8171611fbcc1e45a15fd2455
- https://git.kernel.org/stable/c/b6a079c3b6f95378f26e2aeda520cb3176f7067b
- https://git.kernel.org/stable/c/e40cb34b7f247fe2e366fd192700d1b4f38196ca