From nobody Mon May 25 04:35:59 2026 Received: from mail-wm1-f47.google.com (mail-wm1-f47.google.com [209.85.128.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 292CB49252B for ; Mon, 18 May 2026 16:51:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779123103; cv=none; b=E+hUpvZN1bzsaRCE++iAK/VDAl53Ah7qcCuMeS974oN9Ai7vOHTlwumXioJUTj/D92QP9aJnAFVM/A0h29Go8TdNynqaNecGaLXH+Rr+u2biyci+40KrKxMdmW1EriHR64SvBsqXzmS0bfEHr2Yzmzk1ZpuOkU2kkU22S4ukTqs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779123103; c=relaxed/simple; bh=54HD6WkoU9E2L7AOGhuR8sDuLpt8IOaDZS6a/rdTjMw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc; b=I85uHiz/ow2BNLbJntmTcSHN7okKsWkj883dY7o44MNg1hgdibdtOvfanDQWjfrNp4G+YrTgjEk9WpiFvOuYZ33KGNWbYKW16w2K0bDJJv9bv91lsBiZ0FcCbmKuIVoE1fkhB1nPltVHhUfpRM7301rtdK4p2/YpNcDOA7Ck7g0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=p/2CR2u3; arc=none smtp.client-ip=209.85.128.47 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="p/2CR2u3" Received: by mail-wm1-f47.google.com with SMTP id 5b1f17b1804b1-48d1c670255so3805e9.0 for ; Mon, 18 May 2026 09:51:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1779123100; x=1779727900; darn=vger.kernel.org; h=cc:to:message-id:content-transfer-encoding:mime-version:subject :date:from:from:to:cc:subject:date:message-id:reply-to; bh=QUGzTv1DHoDq83W4njAaCPU00dav/zccykg7KzVApdE=; b=p/2CR2u3oXVtLSO7FQsNftLmRV3+JGfLC+kAL3QBN/DLZf+iNMGb7ZZXfh0Pp9Hube v0joDRbs0TvU7yOMRe3gvCQdKZgSIRdi+ArXILcEQCkgGRsbSIQ8mVT1jaW44PS9AKoL jHqnPot69ni3rGqzcQdQxi/ne88dBCjfDgg5MXP0xh7/rotywNJpgX4YKntvDg+4LrW4 nGoa+HWH7WzLIeGG3NF8sgzlR9I/MlpNgTE8aLh0fQTR0pnItIUzb8D9mu9k1XV5Gc0N uZ3gDH111CvQIs+cbVrK1qnhsryR9EBqPzBJ6+fAArTYBdix1RGVNMroRSXbJcxjk5bE Cn0A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779123100; x=1779727900; h=cc:to:message-id:content-transfer-encoding:mime-version:subject :date:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=QUGzTv1DHoDq83W4njAaCPU00dav/zccykg7KzVApdE=; b=OEczls/ExA1YjfOjrqd6lU6PWzgq3Iwu0Ft2v12BT9O0lHFKZIRzwWE1grQF6fjCzf S4A96I7EjTXwJayWFVfc5IkSn77p+smU/GMWrvaa3d2h9+km79g8O2J8EaLCs7rdKGcb 34XgkwUhg6rJw0SKnlymYMKRfGJcAJ9RYy5qNXWhx5YZdjBHZNu24Vc5HLrPkrX4Y1jg 3lZABsTc1rLWPX7s5NtWNQFKCZEqz3PrR1x+Qt+JhlB9YD+cMYOXy3WUaHBAfMiqiqXj MEf7L7UFFkH/Leny8EvTtf094IBXzF+AK2z+jKqeR/5rnX5Pcu+CqTImQJKzBnfsO7N2 C10Q== X-Forwarded-Encrypted: i=1; AFNElJ8lUlInuRNHt9VMNIzgaQWawcn36WsewkhID0XM02MCWWNATh8AXvtgmlG4zLzq2oPDtKNfCrj/qmonFE8=@vger.kernel.org X-Gm-Message-State: AOJu0Yw9otauKS43DPnNw3BAsVAnuiCa51IbOip4M1eXmZWbU8MrKo8W hP30efsBDgoKWThWthQ12NVdBmwTmkh2qELkgT7k1oToMVuvVmBtd3QLfDbQNo02XQ== X-Gm-Gg: Acq92OGCJe9Tn3AeOflZB5VB3IE3xZm46BwcHxrr/XQxdQ1IP/7mDpw+YIv0GPm36H3 4FwFnNTm01adiOdOlCSHiDEdoRb4whyVcyk+t8o+G0ieQuXwLXqmKbJvA+MawseYHKo5xfePqtP R8jBw7KzdLMtCcFEEhdbBytPHuYt56pnbb0XLlkzX05TyY60n4HTZrBCyH2bYLZPjZvAGGc9Md5 BnrzJDHzqpTayRd87NqimxN/MAKnKfOeiKGFwL3z9ZFxiN4eBKPllvQXfGiEasDtnlLj28SGkSb rxXRY1dULUbeCIDebt19hZAQkD3zTWnfNp2c43S6fy3Wtnuvn/xhVty5ZnwP+Nz2MDLCqJ63rT3 QWhypi+V9Qssg8SMKt6whIBCE9xt9jOPQX8wKshwAMouVbS9O9t82W5FhcJ8VkfSSFfre1XEOtG 4V0M3vRMEkooeR/bd+kHxMgbnI3LOee278/LTb6twVMp35UaohZGvOnatiI2LG X-Received: by 2002:a05:600c:418b:b0:488:960f:60b8 with SMTP id 5b1f17b1804b1-48ffa5e1272mr1613485e9.6.1779123099979; Mon, 18 May 2026 09:51:39 -0700 (PDT) Received: from localhost ([2a00:79e0:288a:8:866a:e549:273b:bc0]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48fe5694fcasm247128945e9.5.2026.05.18.09.51.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 18 May 2026 09:51:39 -0700 (PDT) From: Jann Horn Date: Mon, 18 May 2026 18:51:30 +0200 Subject: [PATCH net v2] af_unix: Fix UAF read of tail->len in unix_stream_data_wait() Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260518-b4-unix-recv-wait-hotfix-v2-1-83e29ce8ad31@google.com> X-B4-Tracking: v=1; b=H4sIAJFDC2oC/x2MSQqAMAwAvyI5G2hLFfUr4sEl2lyqtHUB6d8NH mdg5oVIgSlCV7wQ6OLIuxcwZQGzG/1GyIswGGVqVekGJ4un5wcDzRfeIyd0e1pFUKutUpPVptI g+RFI9L/uwVOCIecPb39W6W8AAAA= X-Change-ID: 20260518-b4-unix-recv-wait-hotfix-e91400b41251 To: Kuniyuki Iwashima , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman Cc: Hannes Frederic Sowa , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, stable@vger.kernel.org, Jann Horn X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=ed25519-sha256; t=1779123098; l=6494; i=jannh@google.com; s=20240730; h=from:subject:message-id; bh=54HD6WkoU9E2L7AOGhuR8sDuLpt8IOaDZS6a/rdTjMw=; b=KFcmSeJwQW72Yr5iXq8iiTqf0xvrDeRaC+IySSWIzUO7MBc8V9G+gugvf3YybFObLQQ/YC4H3 Cu1aD4RIY1kDOwIpmP43dr9KNLldxQfJ3abSQh9d/rBRzsjIP5s8htd X-Developer-Key: i=jannh@google.com; a=ed25519; pk=AljNtGOzXeF6khBXDJVVvwSEkVDGnnZZYqfWhP1V+C8= unix_stream_data_wait() does skb_peek_tail(&sk->sk_receive_queue) without holding any lock that prevents SKBs on that queue from being dequeued and freed. This has been the case since commit 79f632c71bea ("unix/stream: fix peeking with an offset larger than data in queue"). The first consequence of this is that the pointer comparison `tail !=3D last` can be false even if `last` semantically refers to an already-freed SKB while `tail` is a new SKB allocated at the same address; which can cause unix_stream_data_wait() to wrongly keep blocking after new data has arrived, but only in a weird scenario where a peeking recv() and a normal recv() on the same socket are racing, which is probably not a real problem. But since commit 2b514574f7e8 ("net: af_unix: implement splice for stream af_unix sockets"), `tail` is actually dereferenced, which can cause UAF in the following race scenario (where test_setup() runs single-threaded, and afterwards, test_thread1() and test_thread2() run concurrently in two threads: ``` static int socks[2]; void test_setup(void) { socketpair(AF_UNIX, SOCK_STREAM, 0, socks); send(socks[1], "A", 1, 0); int peekoff =3D 1; setsockopt(socks[0], SOL_SOCKET, SO_PEEK_OFF, &peekoff, sizeof(peekoff)); } void test_thread1(void) { char dummy; recv(socks[0], &dummy, 1, MSG_PEEK); } void test_thread2(void) { char dummy; recv(socks[0], &dummy, 1, 0); shutdown(socks[1], SHUT_WR); } ``` when racing like this: ``` thread1 thread2 unix_stream_read_generic mutex_lock(&u->iolock) skb_peek(&sk->sk_receive_queue) skb_peek_next(skb, &sk->sk_receive_queue) mutex_unlock(&u->iolock) unix_stream_read_generic unix_state_lock(sk) skb_peek(&sk->sk_receive_queue) unix_state_unlock(sk) unix_stream_data_wait unix_state_lock(sk) tail =3D skb_peek_tail(&sk->sk_receive_queue) spin_lock(&sk->sk_receive_queue.lock) __skb_unlink(skb, &sk->sk_receive_queue) spin_unlock(&sk->sk_receive_queue.lock) consume_skb(skb) [frees the SKB] `tail !=3D last`: false `tail`: true `tail->len !=3D last_len` ***UAF*** ``` Fix the UAF by removing the read of tail->len; checking tail->len would only make sense if SKBs in the receive queue of a UNIX socket could grow, which can no longer happen. Kuniyuki explained: > When commit 869e7c62486e ("net: af_unix: implement stream sendpage > support") added sendpage() support, data could be appended to the last > skb in the receiver's queue. > > That's why we needed to check if the length of the last skb was changed > while waiting for new data in unix_stream_data_wait(). > > However, commit a0dbf5f818f9 ("af_unix: Support MSG_SPLICE_PAGES") and > commit 57d44a354a43 ("unix: Convert unix_stream_sendpage() to use > MSG_SPLICE_PAGES") refactored sendmsg(), and now data is always added > to a new skb. That means this fix is not suitable for kernels before 6.5. Fixes: 2b514574f7e8 ("net: af_unix: implement splice for stream af_unix soc= kets") Cc: stable@vger.kernel.org # 6.5.x Signed-off-by: Jann Horn Reviewed-by: Kuniyuki Iwashima --- sending as a separate patch as requested at . Based on the context provided there, I have changed the CC: stable line to mark the patch as only being suitable for kernel 6.5+. If we want to fix older kernels, I guess we'll need something different for those... --- Changes in v2: - limit stable backport range to 6.5+ - Link to v1: https://lore.kernel.org/r/20260515-unix-recv-wait-v1-0-76adb5= f063d5@google.com --- net/unix/af_unix.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 1cbf36ea043b..dc71ed79be4a 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2711,8 +2711,7 @@ static int unix_read_skb(struct sock *sk, skb_read_ac= tor_t recv_actor) * Sleep until more data has arrived. But check for races.. */ static long unix_stream_data_wait(struct sock *sk, long timeo, - struct sk_buff *last, unsigned int last_len, - bool freezable) + struct sk_buff *last, bool freezable) { unsigned int state =3D TASK_INTERRUPTIBLE | freezable * TASK_FREEZABLE; struct sk_buff *tail; @@ -2725,7 +2724,6 @@ static long unix_stream_data_wait(struct sock *sk, lo= ng timeo, =20 tail =3D skb_peek_tail(&sk->sk_receive_queue); if (tail !=3D last || - (tail && tail->len !=3D last_len) || sk->sk_err || (sk->sk_shutdown & RCV_SHUTDOWN) || signal_pending(current) || @@ -2921,7 +2919,6 @@ static int unix_stream_read_generic(struct unix_strea= m_read_state *state, int flags =3D state->flags; bool check_creds =3D false; struct scm_cookie scm; - unsigned int last_len; struct unix_sock *u; int copied =3D 0; int err =3D 0; @@ -2967,7 +2964,6 @@ static int unix_stream_read_generic(struct unix_strea= m_read_state *state, goto unlock; } last =3D skb =3D skb_peek(&sk->sk_receive_queue); - last_len =3D last ? last->len : 0; =20 again: #if IS_ENABLED(CONFIG_AF_UNIX_OOB) @@ -3001,8 +2997,7 @@ static int unix_stream_read_generic(struct unix_strea= m_read_state *state, =20 mutex_unlock(&u->iolock); =20 - timeo =3D unix_stream_data_wait(sk, timeo, last, - last_len, freezable); + timeo =3D unix_stream_data_wait(sk, timeo, last, freezable); =20 if (signal_pending(current)) { err =3D sock_intr_errno(timeo); @@ -3019,7 +3014,6 @@ static int unix_stream_read_generic(struct unix_strea= m_read_state *state, while (skip >=3D unix_skb_len(skb)) { skip -=3D unix_skb_len(skb); last =3D skb; - last_len =3D skb->len; skb =3D skb_peek_next(skb, &sk->sk_receive_queue); if (!skb) goto again; @@ -3094,7 +3088,6 @@ static int unix_stream_read_generic(struct unix_strea= m_read_state *state, =20 skip =3D 0; last =3D skb; - last_len =3D skb->len; unix_state_lock(sk); skb =3D skb_peek_next(skb, &sk->sk_receive_queue); if (skb) --- base-commit: aaec7096f9961eb223b5b149abe9495525c205d9 change-id: 20260518-b4-unix-recv-wait-hotfix-e91400b41251 -- =20 Jann Horn