CVE-2026-64109
Publication date:
19/07/2026
In the Linux kernel, the following vulnerability has been resolved:<br />
<br />
af_unix: Fix UAF read of tail->len in unix_stream_data_wait()<br />
<br />
unix_stream_data_wait() does skb_peek_tail(&sk->sk_receive_queue) without<br />
holding any lock that prevents SKBs on that queue from being dequeued and<br />
freed.<br />
This has been the case since commit 79f632c71bea ("unix/stream: fix<br />
peeking with an offset larger than data in queue").<br />
The first consequence of this is that the pointer comparison<br />
`tail != last` can be false even if `last` semantically refers to an<br />
already-freed SKB while `tail` is a new SKB allocated at the same address;<br />
which can cause unix_stream_data_wait() to wrongly keep blocking after new<br />
data has arrived, but only in a weird scenario where a peeking recv() and<br />
a normal recv() on the same socket are racing, which is probably not a<br />
real problem.<br />
<br />
But since commit 2b514574f7e8 ("net: af_unix: implement splice for stream<br />
af_unix sockets"), `tail` is actually dereferenced, which can cause UAF in<br />
the following race scenario (where test_setup() runs single-threaded,<br />
and afterwards, test_thread1() and test_thread2() run concurrently in<br />
two threads:<br />
```<br />
static int socks[2];<br />
void test_setup(void) {<br />
socketpair(AF_UNIX, SOCK_STREAM, 0, socks);<br />
send(socks[1], "A", 1, 0);<br />
int peekoff = 1;<br />
setsockopt(socks[0], SOL_SOCKET, SO_PEEK_OFF, &peekoff, sizeof(peekoff));<br />
}<br />
void test_thread1(void) {<br />
char dummy;<br />
recv(socks[0], &dummy, 1, MSG_PEEK);<br />
}<br />
void test_thread2(void) {<br />
char dummy;<br />
recv(socks[0], &dummy, 1, 0);<br />
shutdown(socks[1], SHUT_WR);<br />
}<br />
```<br />
<br />
when racing like this:<br />
```<br />
thread1 thread2<br />
unix_stream_read_generic<br />
mutex_lock(&u->iolock)<br />
skb_peek(&sk->sk_receive_queue)<br />
skb_peek_next(skb, &sk->sk_receive_queue)<br />
mutex_unlock(&u->iolock)<br />
unix_stream_read_generic<br />
unix_state_lock(sk)<br />
skb_peek(&sk->sk_receive_queue)<br />
unix_state_unlock(sk)<br />
unix_stream_data_wait<br />
unix_state_lock(sk)<br />
tail = skb_peek_tail(&sk->sk_receive_queue)<br />
spin_lock(&sk->sk_receive_queue.lock)<br />
__skb_unlink(skb, &sk->sk_receive_queue)<br />
spin_unlock(&sk->sk_receive_queue.lock)<br />
consume_skb(skb) [frees the SKB]<br />
`tail != last`: false<br />
`tail`: true<br />
`tail->len != last_len` ***UAF***<br />
```<br />
<br />
Fix the UAF by removing the read of tail->len; checking tail->len would<br />
only make sense if SKBs in the receive queue of a UNIX socket could grow,<br />
which can no longer happen.<br />
<br />
Kuniyuki explained:<br />
<br />
> When commit 869e7c62486e ("net: af_unix: implement stream sendpage<br />
> support") added sendpage() support, data could be appended to the last<br />
> skb in the receiver&#39;s queue.<br />
><br />
> That&#39;s why we needed to check if the length of the last skb was changed<br />
> while waiting for new data in unix_stream_data_wait().<br />
><br />
> However, commit a0dbf5f818f9 ("af_unix: Support MSG_SPLICE_PAGES") and<br />
> commit 57d44a354a43 ("unix: Convert unix_stream_sendpage() to use<br />
> MSG_SPLICE_PAGES") refactored sendmsg(), and now data is always added<br />
> to a new skb.<br />
<br />
That means this fix is not suitable for kernels before 6.5.
Severity CVSS v4.0: Pending analysis
Last modification:
19/07/2026