Received: by 2002:a05:6a10:d5a5:0:0:0:0 with SMTP id gn37csp3805651pxb; Mon, 4 Oct 2021 09:59:57 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxlJFjkKk/wwFFEtkGFIUnnOgftBI0jKUYJCbJWHKCUNlT/mgI8d5nIJ6HhZqJhiz3uYcjn X-Received: by 2002:a17:90a:4:: with SMTP id 4mr31230093pja.221.1633366797370; Mon, 04 Oct 2021 09:59:57 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1633366797; cv=none; d=google.com; s=arc-20160816; b=JhC30vFPiRTd7hLRz6VAGcNzbAaL9qQx+0Mto3qg8K1xTfS87qRfBBcjvIppzi+0/B wAR1dtFtGXOlou6d7Ke9aAUHxSLYdk41XH43EPkInG+z1TAEFYc2LwneppSKNlf4LFx1 LYohVqv/taoIHcIei6EpbDwX3rsB8ksAVb3nAqW8hb8+jZg5ryQRNuPtkg2dl7UxlQvz HW9COz6mLln5hj6XDj6Ul3+RxPKFXfAVBsE2XwbBi0lqMaTyynlh31yu3zctjNx8hFIE +yFYZNPQM8ZrWzeHYDylAcAbLFZvnlmotgfkJhPT0UwpJOX3kWbmUBv4mJodbylTPH3Z DUBg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=ZDs4qXFz6tr+tBmeYW6zxpoPryDl56/wKbjnTWnKjyQ=; b=o3fAxtBWFFzCTOPZn9CI5H3ISxvF4HRCN8I5CxI6Foxru6Si2Lo1EgmYaTvbuyNhP/ vmdzjPKhUESAOZB2enpDr0vU2FxHnO5r/S+ECXbDcinRsW+VewP4O976p1FHKOcnOvk4 urlWIG5wcntQbhIDFYr7zK8NAgy5ZzZWnPLPPZQL0d9Hl13dGGaIzlnQ30FQl2zIq9zz jpXcgtR07gOm8P3PYW/tJd4FZdkMLulAfwExez3zTf99QbA7PNARTi+a1nqQjzrtiKu2 J1bGvwkI6PoOHGusiGBbLQYbl6JdAsP26AjVRos2kUJ3eqOVIYIDb8z6XS0oRTMV/VgM StAA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=MtXOZmu9; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id y139si2156163pfb.222.2021.10.04.09.59.45; Mon, 04 Oct 2021 09:59:57 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=MtXOZmu9; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237462AbhJDNnI (ORCPT + 99 others); Mon, 4 Oct 2021 09:43:08 -0400 Received: from mail.kernel.org ([198.145.29.99]:55964 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237843AbhJDNl0 (ORCPT ); Mon, 4 Oct 2021 09:41:26 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 6AE196324E; Mon, 4 Oct 2021 13:18:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1633353538; bh=pqqQrhnj5xoZGD+GvFFvCJKJuXDEOF2Whz/VMVPU0xI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=MtXOZmu9b1YvfaiRorMBmrYWI/STC+0RqfeQNLRRpRQ+ZbsUVqyrPuzvF0hM5Qdse xQpUxWjkWdNrzNSGtYttOxpwmqIsJ9iizLwgh09J7jTjyOCV3nXcjKA96NcYWvwKh+ iPqwHRecJSFsQyhIl2cb92he0XoOJ6sk8sPujjuY= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Eric Dumazet , Jann Horn , "Eric W. Biederman" , Luiz Augusto von Dentz , Marcel Holtmann , "David S. Miller" , Sasha Levin Subject: [PATCH 5.14 136/172] af_unix: fix races in sk_peer_pid and sk_peer_cred accesses Date: Mon, 4 Oct 2021 14:53:06 +0200 Message-Id: <20211004125049.358368087@linuxfoundation.org> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20211004125044.945314266@linuxfoundation.org> References: <20211004125044.945314266@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Eric Dumazet [ Upstream commit 35306eb23814444bd4021f8a1c3047d3cb0c8b2b ] Jann Horn reported that SO_PEERCRED and SO_PEERGROUPS implementations are racy, as af_unix can concurrently change sk_peer_pid and sk_peer_cred. In order to fix this issue, this patch adds a new spinlock that needs to be used whenever these fields are read or written. Jann also pointed out that l2cap_sock_get_peer_pid_cb() is currently reading sk->sk_peer_pid which makes no sense, as this field is only possibly set by AF_UNIX sockets. We will have to clean this in a separate patch. This could be done by reverting b48596d1dc25 "Bluetooth: L2CAP: Add get_peer_pid callback" or implementing what was truly expected. Fixes: 109f6e39fa07 ("af_unix: Allow SO_PEERCRED to work across namespaces.") Signed-off-by: Eric Dumazet Reported-by: Jann Horn Cc: Eric W. Biederman Cc: Luiz Augusto von Dentz Cc: Marcel Holtmann Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- include/net/sock.h | 2 ++ net/core/sock.c | 32 ++++++++++++++++++++++++++------ net/unix/af_unix.c | 34 ++++++++++++++++++++++++++++------ 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/include/net/sock.h b/include/net/sock.h index 980b471b569d..db0cb8aa591f 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -487,8 +487,10 @@ struct sock { u8 sk_prefer_busy_poll; u16 sk_busy_poll_budget; #endif + spinlock_t sk_peer_lock; struct pid *sk_peer_pid; const struct cred *sk_peer_cred; + long sk_rcvtimeo; ktime_t sk_stamp; #if BITS_PER_LONG==32 diff --git a/net/core/sock.c b/net/core/sock.c index 1cf0edc79f37..bd1b34b3b778 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1366,6 +1366,16 @@ int sock_setsockopt(struct socket *sock, int level, int optname, } EXPORT_SYMBOL(sock_setsockopt); +static const struct cred *sk_get_peer_cred(struct sock *sk) +{ + const struct cred *cred; + + spin_lock(&sk->sk_peer_lock); + cred = get_cred(sk->sk_peer_cred); + spin_unlock(&sk->sk_peer_lock); + + return cred; +} static void cred_to_ucred(struct pid *pid, const struct cred *cred, struct ucred *ucred) @@ -1542,7 +1552,11 @@ int sock_getsockopt(struct socket *sock, int level, int optname, struct ucred peercred; if (len > sizeof(peercred)) len = sizeof(peercred); + + spin_lock(&sk->sk_peer_lock); cred_to_ucred(sk->sk_peer_pid, sk->sk_peer_cred, &peercred); + spin_unlock(&sk->sk_peer_lock); + if (copy_to_user(optval, &peercred, len)) return -EFAULT; goto lenout; @@ -1550,20 +1564,23 @@ int sock_getsockopt(struct socket *sock, int level, int optname, case SO_PEERGROUPS: { + const struct cred *cred; int ret, n; - if (!sk->sk_peer_cred) + cred = sk_get_peer_cred(sk); + if (!cred) return -ENODATA; - n = sk->sk_peer_cred->group_info->ngroups; + n = cred->group_info->ngroups; if (len < n * sizeof(gid_t)) { len = n * sizeof(gid_t); + put_cred(cred); return put_user(len, optlen) ? -EFAULT : -ERANGE; } len = n * sizeof(gid_t); - ret = groups_to_user((gid_t __user *)optval, - sk->sk_peer_cred->group_info); + ret = groups_to_user((gid_t __user *)optval, cred->group_info); + put_cred(cred); if (ret) return ret; goto lenout; @@ -1921,9 +1938,10 @@ static void __sk_destruct(struct rcu_head *head) sk->sk_frag.page = NULL; } - if (sk->sk_peer_cred) - put_cred(sk->sk_peer_cred); + /* We do not need to acquire sk->sk_peer_lock, we are the last user. */ + put_cred(sk->sk_peer_cred); put_pid(sk->sk_peer_pid); + if (likely(sk->sk_net_refcnt)) put_net(sock_net(sk)); sk_prot_free(sk->sk_prot_creator, sk); @@ -3124,6 +3142,8 @@ void sock_init_data(struct socket *sock, struct sock *sk) sk->sk_peer_pid = NULL; sk->sk_peer_cred = NULL; + spin_lock_init(&sk->sk_peer_lock); + sk->sk_write_pending = 0; sk->sk_rcvlowat = 1; sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 91ff09d833e8..f96ee27d9ff2 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -600,20 +600,42 @@ static void unix_release_sock(struct sock *sk, int embrion) static void init_peercred(struct sock *sk) { - put_pid(sk->sk_peer_pid); - if (sk->sk_peer_cred) - put_cred(sk->sk_peer_cred); + const struct cred *old_cred; + struct pid *old_pid; + + spin_lock(&sk->sk_peer_lock); + old_pid = sk->sk_peer_pid; + old_cred = sk->sk_peer_cred; sk->sk_peer_pid = get_pid(task_tgid(current)); sk->sk_peer_cred = get_current_cred(); + spin_unlock(&sk->sk_peer_lock); + + put_pid(old_pid); + put_cred(old_cred); } static void copy_peercred(struct sock *sk, struct sock *peersk) { - put_pid(sk->sk_peer_pid); - if (sk->sk_peer_cred) - put_cred(sk->sk_peer_cred); + const struct cred *old_cred; + struct pid *old_pid; + + if (sk < peersk) { + spin_lock(&sk->sk_peer_lock); + spin_lock_nested(&peersk->sk_peer_lock, SINGLE_DEPTH_NESTING); + } else { + spin_lock(&peersk->sk_peer_lock); + spin_lock_nested(&sk->sk_peer_lock, SINGLE_DEPTH_NESTING); + } + old_pid = sk->sk_peer_pid; + old_cred = sk->sk_peer_cred; sk->sk_peer_pid = get_pid(peersk->sk_peer_pid); sk->sk_peer_cred = get_cred(peersk->sk_peer_cred); + + spin_unlock(&sk->sk_peer_lock); + spin_unlock(&peersk->sk_peer_lock); + + put_pid(old_pid); + put_cred(old_cred); } static int unix_listen(struct socket *sock, int backlog) -- 2.33.0