CVE-2025-22058
Severity CVSS v4.0:
Pending analysis
Type:
Unavailable / Other
Publication date:
16/04/2025
Last modified:
17/04/2025
Description
In the Linux kernel, the following vulnerability has been resolved:<br />
<br />
udp: Fix memory accounting leak.<br />
<br />
Matt Dowling reported a weird UDP memory usage issue.<br />
<br />
Under normal operation, the UDP memory usage reported in /proc/net/sockstat<br />
remains close to zero. However, it occasionally spiked to 524,288 pages<br />
and never dropped. Moreover, the value doubled when the application was<br />
terminated. Finally, it caused intermittent packet drops.<br />
<br />
We can reproduce the issue with the script below [0]:<br />
<br />
1. /proc/net/sockstat reports 0 pages<br />
<br />
# cat /proc/net/sockstat | grep UDP:<br />
UDP: inuse 1 mem 0<br />
<br />
2. Run the script till the report reaches 524,288<br />
<br />
# python3 test.py & sleep 5<br />
# cat /proc/net/sockstat | grep UDP:<br />
UDP: inuse 3 mem 524288 > PAGE_SHIFT<br />
<br />
3. Kill the socket and confirm the number never drops<br />
<br />
# pkill python3 && sleep 5<br />
# cat /proc/net/sockstat | grep UDP:<br />
UDP: inuse 1 mem 524288<br />
<br />
4. (necessary since v6.0) Trigger proto_memory_pcpu_drain()<br />
<br />
# python3 test.py & sleep 1 && pkill python3<br />
<br />
5. The number doubles<br />
<br />
# cat /proc/net/sockstat | grep UDP:<br />
UDP: inuse 1 mem 1048577<br />
<br />
The application set INT_MAX to SO_RCVBUF, which triggered an integer<br />
overflow in udp_rmem_release().<br />
<br />
When a socket is close()d, udp_destruct_common() purges its receive<br />
queue and sums up skb->truesize in the queue. This total is calculated<br />
and stored in a local unsigned integer variable.<br />
<br />
The total size is then passed to udp_rmem_release() to adjust memory<br />
accounting. However, because the function takes a signed integer<br />
argument, the total size can wrap around, causing an overflow.<br />
<br />
Then, the released amount is calculated as follows:<br />
<br />
1) Add size to sk->sk_forward_alloc.<br />
2) Round down sk->sk_forward_alloc to the nearest lower multiple of<br />
PAGE_SIZE and assign it to amount.<br />
3) Subtract amount from sk->sk_forward_alloc.<br />
4) Pass amount >> PAGE_SHIFT to __sk_mem_reduce_allocated().<br />
<br />
When the issue occurred, the total in udp_destruct_common() was 2147484480<br />
(INT_MAX + 833), which was cast to -2147482816 in udp_rmem_release().<br />
<br />
At 1) sk->sk_forward_alloc is changed from 3264 to -2147479552, and<br />
2) sets -2147479552 to amount. 3) reverts the wraparound, so we don&#39;t<br />
see a warning in inet_sock_destruct(). However, udp_memory_allocated<br />
ends up doubling at 4).<br />
<br />
Since commit 3cd3399dd7a8 ("net: implement per-cpu reserves for<br />
memory_allocated"), memory usage no longer doubles immediately after<br />
a socket is close()d because __sk_mem_reduce_allocated() caches the<br />
amount in udp_memory_per_cpu_fw_alloc. However, the next time a UDP<br />
socket receives a packet, the subtraction takes effect, causing UDP<br />
memory usage to double.<br />
<br />
This issue makes further memory allocation fail once the socket&#39;s<br />
sk->sk_rmem_alloc exceeds net.ipv4.udp_rmem_min, resulting in packet<br />
drops.<br />
<br />
To prevent this issue, let&#39;s use unsigned int for the calculation and<br />
call sk_forward_alloc_add() only once for the small delta.<br />
<br />
Note that first_packet_length() also potentially has the same problem.<br />
<br />
[0]:<br />
from socket import *<br />
<br />
SO_RCVBUFFORCE = 33<br />
INT_MAX = (2 ** 31) - 1<br />
<br />
s = socket(AF_INET, SOCK_DGRAM)<br />
s.bind((&#39;&#39;, 0))<br />
s.setsockopt(SOL_SOCKET, SO_RCVBUFFORCE, INT_MAX)<br />
<br />
c = socket(AF_INET, SOCK_DGRAM)<br />
c.connect(s.getsockname())<br />
<br />
data = b&#39;a&#39; * 100<br />
<br />
while True:<br />
c.send(data)
Impact
References to Advisories, Solutions, and Tools
- https://git.kernel.org/stable/c/3836029448e76c1e6f77cc5fe0adc09b018b5fa8
- https://git.kernel.org/stable/c/9122fec396950cc866137af7154b1d0d989be52e
- https://git.kernel.org/stable/c/a116b271bf3cb72c8155b6b7f39083c1b80dcd00
- https://git.kernel.org/stable/c/aeef6456692c6f11ae53d278df64f1316a2a405a
- https://git.kernel.org/stable/c/c4bac6c398118fba79e32b1cd01db22dbfe29fbf
- https://git.kernel.org/stable/c/df207de9d9e7a4d92f8567e2c539d9c8c12fd99d