CVE-2024-57974
Publication date:
27/02/2025
In the Linux kernel, the following vulnerability has been resolved:<br />
<br />
udp: Deal with race between UDP socket address change and rehash<br />
<br />
If a UDP socket changes its local address while it&#39;s receiving<br />
datagrams, as a result of connect(), there is a period during which<br />
a lookup operation might fail to find it, after the address is changed<br />
but before the secondary hash (port and address) and the four-tuple<br />
hash (local and remote ports and addresses) are updated.<br />
<br />
Secondary hash chains were introduced by commit 30fff9231fad ("udp:<br />
bind() optimisation") and, as a result, a rehash operation became<br />
needed to make a bound socket reachable again after a connect().<br />
<br />
This operation was introduced by commit 719f835853a9 ("udp: add<br />
rehash on connect()") which isn&#39;t however a complete fix: the<br />
socket will be found once the rehashing completes, but not while<br />
it&#39;s pending.<br />
<br />
This is noticeable with a socat(1) server in UDP4-LISTEN mode, and a<br />
client sending datagrams to it. After the server receives the first<br />
datagram (cf. _xioopen_ipdgram_listen()), it issues a connect() to<br />
the address of the sender, in order to set up a directed flow.<br />
<br />
Now, if the client, running on a different CPU thread, happens to<br />
send a (subsequent) datagram while the server&#39;s socket changes its<br />
address, but is not rehashed yet, this will result in a failed<br />
lookup and a port unreachable error delivered to the client, as<br />
apparent from the following reproducer:<br />
<br />
LEN=$(($(cat /proc/sys/net/core/wmem_default) / 4))<br />
dd if=/dev/urandom bs=1 count=${LEN} of=tmp.in<br />
<br />
while :; do<br />
taskset -c 1 socat UDP4-LISTEN:1337,null-eof OPEN:tmp.out,create,trunc &<br />
sleep 0.1 || sleep 1<br />
taskset -c 2 socat OPEN:tmp.in UDP4:localhost:1337,shut-null<br />
wait<br />
done<br />
<br />
where the client will eventually get ECONNREFUSED on a write()<br />
(typically the second or third one of a given iteration):<br />
<br />
2024/11/13 21:28:23 socat[46901] E write(6, 0x556db2e3c000, 8192): Connection refused<br />
<br />
This issue was first observed as a seldom failure in Podman&#39;s tests<br />
checking UDP functionality while using pasta(1) to connect the<br />
container&#39;s network namespace, which leads us to a reproducer with<br />
the lookup error resulting in an ICMP packet on a tap device:<br />
<br />
LOCAL_ADDR="$(ip -j -4 addr show|jq -rM &#39;.[] | .addr_info[0] | select(.scope == "global").local&#39;)"<br />
<br />
while :; do<br />
./pasta --config-net -p pasta.pcap -u 1337 socat UDP4-LISTEN:1337,null-eof OPEN:tmp.out,create,trunc &<br />
sleep 0.2 || sleep 1<br />
socat OPEN:tmp.in UDP4:${LOCAL_ADDR}:1337,shut-null<br />
wait<br />
cmp tmp.in tmp.out<br />
done<br />
<br />
Once this fails:<br />
<br />
tmp.in tmp.out differ: char 8193, line 29<br />
<br />
we can finally have a look at what&#39;s going on:<br />
<br />
$ tshark -r pasta.pcap<br />
1 0.000000 :: ? ff02::16 ICMPv6 110 Multicast Listener Report Message v2<br />
2 0.168690 88.198.0.161 ? 88.198.0.164 UDP 8234 60260 ? 1337 Len=8192<br />
3 0.168767 88.198.0.161 ? 88.198.0.164 UDP 8234 60260 ? 1337 Len=8192<br />
4 0.168806 88.198.0.161 ? 88.198.0.164 UDP 8234 60260 ? 1337 Len=8192<br />
5 0.168827 c6:47:05:8d:dc:04 ? Broadcast ARP 42 Who has 88.198.0.161? Tell 88.198.0.164<br />
6 0.168851 9a:55:9a:55:9a:55 ? c6:47:05:8d:dc:04 ARP 42 88.198.0.161 is at 9a:55:9a:55:9a:55<br />
7 0.168875 88.198.0.161 ? 88.198.0.164 UDP 8234 60260 ? 1337 Len=8192<br />
8 0.168896 88.198.0.164 ? 88.198.0.161 ICMP 590 Destination unreachable (Port unreachable)<br />
9 0.168926 88.198.0.161 ? 88.198.0.164 UDP 8234 60260 ? 1337 Len=8192<br />
10 0.168959 88.198.0.161 ? 88.198.0.164 UDP 8234 60260 ? 1337 Len=8192<br />
11 0.168989 88.198.0.161 ? 88.198.0.164 UDP 4138 60260 ? 1337 Len=4096<br />
12 0.169010 88.198.0.161 ? 88.198.0.164 UDP 42 60260 ? 1337 Len=0<br />
<br />
On the third datagram received, the network namespace of the container<br />
initiates an ARP lookup to deliver the ICMP message.<br />
<br />
In another variant of this reproducer, starting the client with:<br />
<br />
strace -f pasta --config-net -u 1337 socat UDP4-LISTEN:1337,null-eof OPEN:tmp.out,create,tru<br />
---truncated---
Severity CVSS v4.0: Pending analysis
Last modification:
27/02/2025