Received: by 2002:ac0:bc90:0:0:0:0:0 with SMTP id a16csp264000img; Mon, 18 Mar 2019 02:34:55 -0700 (PDT) X-Google-Smtp-Source: APXvYqxas9uDyiDWMJIFgdTdwszFXwcw6OGUhK6W5ExP/OZpVgq+aoR1xQ4NsNJrBUsn8yQoqVAa X-Received: by 2002:a17:902:e50b:: with SMTP id ck11mr19208847plb.25.1552901695480; Mon, 18 Mar 2019 02:34:55 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1552901695; cv=none; d=google.com; s=arc-20160816; b=C5MoU2PkJo22COOHnlHuWjeQRmlM2U2+f5ToRC8S3+E96AH0YQoy4xqKNVecy64a0y LWCfAmoqjfzvb7PvQrl+eQOQrPFQTQnJ0e0S43bNR+8q+OqclSh6/6UWUTjBlEo14A6K +5aMNDOVHp+04iLYaAhozKVjUH6+hwkAtE2w1q1YbDs0ml5cYvmLBu+uK7uB6p0L9S8X 8mT43mxfuWnKFRjWWolt0VUoik5DjZA3G8qSvNqoWi9Ql0p3MZqbhGuzmbwReNDCOliC k+ZcdPBO6gigH+SpHmYALh+4r0+eyBpA88YCYrY4bcbyz8XbPxpTOmGhiZ2vxKhqIqU/ tM3g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=DwyxIm1caFWG66qOhqiWSEf4/thuMkY4uvJl1Wz1rYE=; b=0cBPQ/LZG14Kjug1MuoI6F4206mARL6qFUuFWKJ8q3RqWBOCAaxWpHvWFyMtTHw2oc vbXsQE5vHHaSLFMhwuT6923Ppnk3SjJSOWfFZ5AhpW9+ecfgIEfNhzt52zR17dqIA/GL r16YjSUidcQqVkC/3lrQhm3ER8xI5HHgHrpwVkhHlgFapHErXA92iJx7h4wKgBDFGom8 Mn7TLJEGQuXPVBY5ExKYNt+or0YC/rk1GRrmL8NvwKJCuTxakmXgcXcLwmrMun4sHTaD yyynOma1MLtuXTOZCWjX42stDMSQkNU7ZlKI3IBEEMdExeOOPPjUNNmRW7R5fTRYucyw h70Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=hLKGJy6u; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id s14si8489147pgs.98.2019.03.18.02.34.40; Mon, 18 Mar 2019 02:34:55 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=hLKGJy6u; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728800AbfCRJdA (ORCPT + 99 others); Mon, 18 Mar 2019 05:33:00 -0400 Received: from mail.kernel.org ([198.145.29.99]:41072 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728784AbfCRJc7 (ORCPT ); Mon, 18 Mar 2019 05:32:59 -0400 Received: from localhost (5356596B.cm-6-7b.dynamic.ziggo.nl [83.86.89.107]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id A8EBE2075C; Mon, 18 Mar 2019 09:32:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1552901578; bh=bN30s1/uq26W162x1j+ZniXtlCNUzt+dLeFrWjMRSsg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hLKGJy6u5LLzNDeiC/Sij0QouiQFz+cID2ONy2SSnJYo5zDYq2FI7JOOCwZmg0E2e +9Ka2gNXWtogDYihdMiqojPeFwAw0HEe33/etldSMZd1VC3rplzdcepb7cwhri60Sr caAZOcmoHklwGleErs9iccEP+DjSRO0uhrFetMKI= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Al Viro , "David S. Miller" , "Paul E. McKenney" Subject: [PATCH 4.19 35/52] missing barriers in some of unix_sock ->addr and ->path accesses Date: Mon, 18 Mar 2019 10:25:32 +0100 Message-Id: <20190318084017.657538834@linuxfoundation.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190318084013.532280682@linuxfoundation.org> References: <20190318084013.532280682@linuxfoundation.org> User-Agent: quilt/0.65 X-stable: review X-Patchwork-Hint: ignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 4.19-stable review patch. If anyone has any objections, please let me know. ------------------ From: Al Viro [ Upstream commit ae3b564179bfd06f32d051b9e5d72ce4b2a07c37 ] Several u->addr and u->path users are not holding any locks in common with unix_bind(). unix_state_lock() is useless for those purposes. u->addr is assign-once and *(u->addr) is fully set up by the time we set u->addr (all under unix_table_lock). u->path is also set in the same critical area, also before setting u->addr, and any unix_sock with ->path filled will have non-NULL ->addr. So setting ->addr with smp_store_release() is all we need for those "lockless" users - just have them fetch ->addr with smp_load_acquire() and don't even bother looking at ->path if they see NULL ->addr. Users of ->addr and ->path fall into several classes now: 1) ones that do smp_load_acquire(u->addr) and access *(u->addr) and u->path only if smp_load_acquire() has returned non-NULL. 2) places holding unix_table_lock. These are guaranteed that *(u->addr) is seen fully initialized. If unix_sock is in one of the "bound" chains, so's ->path. 3) unix_sock_destructor() using ->addr is safe. All places that set u->addr are guaranteed to have seen all stores *(u->addr) while holding a reference to u and unix_sock_destructor() is called when (atomic) refcount hits zero. 4) unix_release_sock() using ->path is safe. unix_bind() is serialized wrt unix_release() (normally - by struct file refcount), and for the instances that had ->path set by unix_bind() unix_release_sock() comes from unix_release(), so they are fine. Instances that had it set in unix_stream_connect() either end up attached to a socket (in unix_accept()), in which case the call chain to unix_release_sock() and serialization are the same as in the previous case, or they never get accept'ed and unix_release_sock() is called when the listener is shut down and its queue gets purged. In that case the listener's queue lock provides the barriers needed - unix_stream_connect() shoves our unix_sock into listener's queue under that lock right after having set ->path and eventual unix_release_sock() caller picks them from that queue under the same lock right before calling unix_release_sock(). 5) unix_find_other() use of ->path is pointless, but safe - it happens with successful lookup by (abstract) name, so ->path.dentry is guaranteed to be NULL there. earlier-variant-reviewed-by: "Paul E. McKenney" Signed-off-by: Al Viro Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/unix/af_unix.c | 57 +++++++++++++++++++++++++++++---------------------- net/unix/diag.c | 3 +- security/lsm_audit.c | 10 +++++--- 3 files changed, 41 insertions(+), 29 deletions(-) --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -888,7 +888,7 @@ retry: addr->hash ^= sk->sk_type; __unix_remove_socket(sk); - u->addr = addr; + smp_store_release(&u->addr, addr); __unix_insert_socket(&unix_socket_table[addr->hash], sk); spin_unlock(&unix_table_lock); err = 0; @@ -1058,7 +1058,7 @@ static int unix_bind(struct socket *sock err = 0; __unix_remove_socket(sk); - u->addr = addr; + smp_store_release(&u->addr, addr); __unix_insert_socket(list, sk); out_unlock: @@ -1329,15 +1329,29 @@ restart: RCU_INIT_POINTER(newsk->sk_wq, &newu->peer_wq); otheru = unix_sk(other); - /* copy address information from listening to new sock*/ - if (otheru->addr) { - refcount_inc(&otheru->addr->refcnt); - newu->addr = otheru->addr; - } + /* copy address information from listening to new sock + * + * The contents of *(otheru->addr) and otheru->path + * are seen fully set up here, since we have found + * otheru in hash under unix_table_lock. Insertion + * into the hash chain we'd found it in had been done + * in an earlier critical area protected by unix_table_lock, + * the same one where we'd set *(otheru->addr) contents, + * as well as otheru->path and otheru->addr itself. + * + * Using smp_store_release() here to set newu->addr + * is enough to make those stores, as well as stores + * to newu->path visible to anyone who gets newu->addr + * by smp_load_acquire(). IOW, the same warranties + * as for unix_sock instances bound in unix_bind() or + * in unix_autobind(). + */ if (otheru->path.dentry) { path_get(&otheru->path); newu->path = otheru->path; } + refcount_inc(&otheru->addr->refcnt); + smp_store_release(&newu->addr, otheru->addr); /* Set credentials */ copy_peercred(sk, other); @@ -1451,7 +1465,7 @@ out: static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int peer) { struct sock *sk = sock->sk; - struct unix_sock *u; + struct unix_address *addr; DECLARE_SOCKADDR(struct sockaddr_un *, sunaddr, uaddr); int err = 0; @@ -1466,19 +1480,15 @@ static int unix_getname(struct socket *s sock_hold(sk); } - u = unix_sk(sk); - unix_state_lock(sk); - if (!u->addr) { + addr = smp_load_acquire(&unix_sk(sk)->addr); + if (!addr) { sunaddr->sun_family = AF_UNIX; sunaddr->sun_path[0] = 0; err = sizeof(short); } else { - struct unix_address *addr = u->addr; - err = addr->len; memcpy(sunaddr, addr->name, addr->len); } - unix_state_unlock(sk); sock_put(sk); out: return err; @@ -2071,11 +2081,11 @@ static int unix_seqpacket_recvmsg(struct static void unix_copy_addr(struct msghdr *msg, struct sock *sk) { - struct unix_sock *u = unix_sk(sk); + struct unix_address *addr = smp_load_acquire(&unix_sk(sk)->addr); - if (u->addr) { - msg->msg_namelen = u->addr->len; - memcpy(msg->msg_name, u->addr->name, u->addr->len); + if (addr) { + msg->msg_namelen = addr->len; + memcpy(msg->msg_name, addr->name, addr->len); } } @@ -2579,15 +2589,14 @@ static int unix_open_file(struct sock *s if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) return -EPERM; - unix_state_lock(sk); + if (!smp_load_acquire(&unix_sk(sk)->addr)) + return -ENOENT; + path = unix_sk(sk)->path; - if (!path.dentry) { - unix_state_unlock(sk); + if (!path.dentry) return -ENOENT; - } path_get(&path); - unix_state_unlock(sk); fd = get_unused_fd_flags(O_CLOEXEC); if (fd < 0) @@ -2828,7 +2837,7 @@ static int unix_seq_show(struct seq_file (s->sk_state == TCP_ESTABLISHED ? SS_CONNECTING : SS_DISCONNECTING), sock_i_ino(s)); - if (u->addr) { + if (u->addr) { // under unix_table_lock here int i, len; seq_putc(seq, ' '); --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -10,7 +10,8 @@ static int sk_diag_dump_name(struct sock *sk, struct sk_buff *nlskb) { - struct unix_address *addr = unix_sk(sk)->addr; + /* might or might not have unix_table_lock */ + struct unix_address *addr = smp_load_acquire(&unix_sk(sk)->addr); if (!addr) return 0; --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -321,6 +321,7 @@ static void dump_common_audit_data(struc if (a->u.net->sk) { struct sock *sk = a->u.net->sk; struct unix_sock *u; + struct unix_address *addr; int len = 0; char *p = NULL; @@ -351,14 +352,15 @@ static void dump_common_audit_data(struc #endif case AF_UNIX: u = unix_sk(sk); + addr = smp_load_acquire(&u->addr); + if (!addr) + break; if (u->path.dentry) { audit_log_d_path(ab, " path=", &u->path); break; } - if (!u->addr) - break; - len = u->addr->len-sizeof(short); - p = &u->addr->name->sun_path[0]; + len = addr->len-sizeof(short); + p = &addr->name->sun_path[0]; audit_log_format(ab, " path="); if (*p) audit_log_untrustedstring(ab, p);