Received: by 2002:a05:6a10:d5a5:0:0:0:0 with SMTP id gn37csp3922609pxb; Mon, 4 Oct 2021 12:43:39 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyBYLlvLmG6lcePrh82zticFritfvsLwBg0Uem8Wc7L+5m78TewOpBK2tnJZEHnMHlK41ER X-Received: by 2002:a50:d841:: with SMTP id v1mr20051353edj.221.1633376619172; Mon, 04 Oct 2021 12:43:39 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1633376619; cv=none; d=google.com; s=arc-20160816; b=xKxMIIRUXc21QKI73zoOdT19ESWlrkbEDVQwdipa3TmmZwPIlG5PeqRRZlMpgarZ9z 8Yhl6d3BLJy1XM9+y0irIUfCQF0a5T/vYJtNJdwX9D9SGf33V5RJczo9kSRla7atmD5U /26FlPB/NTT+77AFsGowwguQe1NlwPT9AoXG5jmUQaMmMEM2xkkgHmovM+JyMFRkYcSL kbruKSqx20fr+auulhCn2Lh9+husJOxiPqntOKdAQAXWUAO89VS3xwN1OgXJs1W6VYDj rhY0pXx3OJbbUCZdtQ+H66A87r9QsO7osss2/vniCVc5UIQWGk9WVvNtp89GdWPNxMHq Bhkw== 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=V+mv1rBSRs6uyLDBsaSqSuEOqAlC27AY3QzxYfo9b9E=; b=pl7B6IJMURELGfl9kP4e8vfeslkrTwQtTEw2BBYVV4nQU20k3PXuj5Vk1+cGuTxZMc ckqfezpLErvyGRsDShBcS1CjD5J051SULzJT/h71xkei4OMvJtgYDWAkZz704pVeVCIy Ztss7hyH/9VnWS4NKeHfl/e83sS4e8LD0jfIE3Ii2lhnIIHh9Kl/pqu2NVCo19QQ1Udn tAfYtLkd/nKWGlskvm0aSno7pkK2YbmCb94UdqTB0eFwEQ1BrNZyE9jIlhHNvzuE15AY jjNaiYZFaANUvI77mzCC9LiWCmZvfGf/eU/Kgi+uNVmeU0hsjWEo2cCnGtCvxVJAm7YF ZDJA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=TXUVjj8s; 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 p4si21974945eju.502.2021.10.04.12.43.13; Mon, 04 Oct 2021 12:43:39 -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=TXUVjj8s; 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 S235247AbhJDNGB (ORCPT + 99 others); Mon, 4 Oct 2021 09:06:01 -0400 Received: from mail.kernel.org ([198.145.29.99]:38546 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234399AbhJDNEA (ORCPT ); Mon, 4 Oct 2021 09:04:00 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id C26C1613C8; Mon, 4 Oct 2021 13:00:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1633352419; bh=M+WXZ0rd37CTK7wFIWxL4ogRsIyFsFNKW0/TeZrKOGw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=TXUVjj8sRWrtiDnbfuTH31lbLxVAIob1dC/KWPSS7v6S+APxmcIP8F6cu83nJPHVa 7HVoc6G5jqoG7dX3vEnfncSDhb/e5bZYaYkvN+vt1L4JMP/aMwG9z5wWpBgYMbydZ7 68X8vveAcG5apmWU6Wo/mFou/ifagzUTdrlWHAx8= 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 4.14 54/75] af_unix: fix races in sk_peer_pid and sk_peer_cred accesses Date: Mon, 4 Oct 2021 14:52:29 +0200 Message-Id: <20211004125033.335733437@linuxfoundation.org> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20211004125031.530773667@linuxfoundation.org> References: <20211004125031.530773667@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 70fe85bee4e5..029df5cdeaf1 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -454,8 +454,10 @@ struct sock { u32 sk_ack_backlog; u32 sk_max_ack_backlog; kuid_t sk_uid; + 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 699bd3052c61..427024597204 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1069,6 +1069,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) @@ -1242,7 +1252,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; @@ -1250,20 +1264,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; @@ -1574,9 +1591,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); @@ -2753,6 +2771,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 f30509ff302e..0e494902fada 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -595,20 +595,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