diff options
Diffstat (limited to 'net/unix/af_unix.c')
| -rw-r--r-- | net/unix/af_unix.c | 57 | 
1 files changed, 33 insertions, 24 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 74d1eed7cbd4..a95d479caeea 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -890,7 +890,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; @@ -1060,7 +1060,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)  	err = 0;  	__unix_remove_socket(sk); -	u->addr = addr; +	smp_store_release(&u->addr, addr);  	__unix_insert_socket(list, sk);  out_unlock: @@ -1331,15 +1331,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); @@ -1453,7 +1467,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; @@ -1468,19 +1482,15 @@ static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int peer)  		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; @@ -2073,11 +2083,11 @@ static int unix_seqpacket_recvmsg(struct socket *sock, struct msghdr *msg,  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);  	}  } @@ -2581,15 +2591,14 @@ static int unix_open_file(struct sock *sk)  	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) @@ -2830,7 +2839,7 @@ static int unix_seq_show(struct seq_file *seq, void *v)  			(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, ' ');  | 
