From f9bee62e27ac6d32e40701e9cb2d46e8074e1a35 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 7 Oct 2021 19:19:30 +0200 Subject: [PATCH] 4.4-stable patches added patches: af_unix-fix-races-in-sk_peer_pid-and-sk_peer_cred-accesses.patch --- ...k_peer_pid-and-sk_peer_cred-accesses.patch | 147 ++++++++++++++++++ queue-4.4/series | 1 + 2 files changed, 148 insertions(+) create mode 100644 queue-4.4/af_unix-fix-races-in-sk_peer_pid-and-sk_peer_cred-accesses.patch create mode 100644 queue-4.4/series diff --git a/queue-4.4/af_unix-fix-races-in-sk_peer_pid-and-sk_peer_cred-accesses.patch b/queue-4.4/af_unix-fix-races-in-sk_peer_pid-and-sk_peer_cred-accesses.patch new file mode 100644 index 00000000000..bd3c6686195 --- /dev/null +++ b/queue-4.4/af_unix-fix-races-in-sk_peer_pid-and-sk_peer_cred-accesses.patch @@ -0,0 +1,147 @@ +From 35306eb23814444bd4021f8a1c3047d3cb0c8b2b Mon Sep 17 00:00:00 2001 +From: Eric Dumazet +Date: Wed, 29 Sep 2021 15:57:50 -0700 +Subject: af_unix: fix races in sk_peer_pid and sk_peer_cred accesses + +From: Eric Dumazet + +commit 35306eb23814444bd4021f8a1c3047d3cb0c8b2b upstream. + +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 +[backport note: 4.4 and 4.9 don't have SO_PEERGROUPS, only SO_PEERCRED] +[backport note: got rid of sk_get_peer_cred(), no users in 4.4/4.9] +Signed-off-by: Jann Horn +Signed-off-by: Greg Kroah-Hartman +--- + include/net/sock.h | 2 ++ + net/core/sock.c | 12 +++++++++--- + net/unix/af_unix.c | 34 ++++++++++++++++++++++++++++------ + 3 files changed, 39 insertions(+), 9 deletions(-) + +--- a/include/net/sock.h ++++ b/include/net/sock.h +@@ -429,8 +429,10 @@ struct sock { + #if IS_ENABLED(CONFIG_CGROUP_NET_PRIO) + __u32 sk_cgrp_prioidx; + #endif ++ spinlock_t sk_peer_lock; + struct pid *sk_peer_pid; + const struct cred *sk_peer_cred; ++ + long sk_rcvtimeo; + long sk_sndtimeo; + struct timer_list sk_timer; +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -1014,7 +1014,6 @@ set_rcvbuf: + } + EXPORT_SYMBOL(sock_setsockopt); + +- + static void cred_to_ucred(struct pid *pid, const struct cred *cred, + struct ucred *ucred) + { +@@ -1174,7 +1173,11 @@ int sock_getsockopt(struct socket *sock, + 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; +@@ -1467,9 +1470,10 @@ void sk_destruct(struct sock *sk) + 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); +@@ -2442,6 +2446,8 @@ void sock_init_data(struct socket *sock, + + 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; +--- a/net/unix/af_unix.c ++++ b/net/unix/af_unix.c +@@ -594,20 +594,42 @@ static void unix_release_sock(struct soc + + 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) diff --git a/queue-4.4/series b/queue-4.4/series new file mode 100644 index 00000000000..49c28fb29a2 --- /dev/null +++ b/queue-4.4/series @@ -0,0 +1 @@ +af_unix-fix-races-in-sk_peer_pid-and-sk_peer_cred-accesses.patch -- 2.47.3