diff options
Diffstat (limited to 'net/unix')
| -rw-r--r-- | net/unix/af_unix.c | 94 | 
1 files changed, 66 insertions, 28 deletions
| diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 92345c9bb60c..89f9e85ae970 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -608,20 +608,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) @@ -806,7 +828,7 @@ static void unix_unhash(struct sock *sk)  }  struct proto unix_dgram_proto = { -	.name			= "UNIX-DGRAM", +	.name			= "UNIX",  	.owner			= THIS_MODULE,  	.obj_size		= sizeof(struct unix_sock),  	.close			= unix_close, @@ -828,20 +850,25 @@ struct proto unix_stream_proto = {  static struct sock *unix_create1(struct net *net, struct socket *sock, int kern, int type)  { -	struct sock *sk = NULL;  	struct unix_sock *u; +	struct sock *sk; +	int err;  	atomic_long_inc(&unix_nr_socks); -	if (atomic_long_read(&unix_nr_socks) > 2 * get_max_files()) -		goto out; +	if (atomic_long_read(&unix_nr_socks) > 2 * get_max_files()) { +		err = -ENFILE; +		goto err; +	}  	if (type == SOCK_STREAM)  		sk = sk_alloc(net, PF_UNIX, GFP_KERNEL, &unix_stream_proto, kern);  	else /*dgram and  seqpacket */  		sk = sk_alloc(net, PF_UNIX, GFP_KERNEL, &unix_dgram_proto, kern); -	if (!sk) -		goto out; +	if (!sk) { +		err = -ENOMEM; +		goto err; +	}  	sock_init_data(sock, sk); @@ -861,20 +888,23 @@ static struct sock *unix_create1(struct net *net, struct socket *sock, int kern,  	init_waitqueue_func_entry(&u->peer_wake, unix_dgram_peer_wake_relay);  	memset(&u->scm_stat, 0, sizeof(struct scm_stat));  	unix_insert_socket(unix_sockets_unbound(sk), sk); -out: -	if (sk == NULL) -		atomic_long_dec(&unix_nr_socks); -	else { -		local_bh_disable(); -		sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); -		local_bh_enable(); -	} + +	local_bh_disable(); +	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); +	local_bh_enable(); +  	return sk; + +err: +	atomic_long_dec(&unix_nr_socks); +	return ERR_PTR(err);  }  static int unix_create(struct net *net, struct socket *sock, int protocol,  		       int kern)  { +	struct sock *sk; +  	if (protocol && protocol != PF_UNIX)  		return -EPROTONOSUPPORT; @@ -901,7 +931,11 @@ static int unix_create(struct net *net, struct socket *sock, int protocol,  		return -ESOCKTNOSUPPORT;  	} -	return unix_create1(net, sock, kern, sock->type) ? 0 : -ENOMEM; +	sk = unix_create1(net, sock, kern, sock->type); +	if (IS_ERR(sk)) +		return PTR_ERR(sk); + +	return 0;  }  static int unix_release(struct socket *sock) @@ -1314,12 +1348,15 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,  	   we will have to recheck all again in any case.  	 */ -	err = -ENOMEM; -  	/* create new sock for complete connection */  	newsk = unix_create1(sock_net(sk), NULL, 0, sock->type); -	if (newsk == NULL) +	if (IS_ERR(newsk)) { +		err = PTR_ERR(newsk); +		newsk = NULL;  		goto out; +	} + +	err = -ENOMEM;  	/* Allocate skb for sending to listening sock */  	skb = sock_wmalloc(newsk, 1, 0, GFP_KERNEL); @@ -2845,6 +2882,9 @@ static int unix_shutdown(struct socket *sock, int mode)  	unix_state_lock(sk);  	sk->sk_shutdown |= mode; +	if ((sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET) && +	    mode == SHUTDOWN_MASK) +		sk->sk_state = TCP_CLOSE;  	other = unix_peer(sk);  	if (other)  		sock_hold(other); @@ -2867,12 +2907,10 @@ static int unix_shutdown(struct socket *sock, int mode)  		other->sk_shutdown |= peer_mode;  		unix_state_unlock(other);  		other->sk_state_change(other); -		if (peer_mode == SHUTDOWN_MASK) { +		if (peer_mode == SHUTDOWN_MASK)  			sk_wake_async(other, SOCK_WAKE_WAITD, POLL_HUP); -			other->sk_state = TCP_CLOSE; -		} else if (peer_mode & RCV_SHUTDOWN) { +		else if (peer_mode & RCV_SHUTDOWN)  			sk_wake_async(other, SOCK_WAKE_WAITD, POLL_IN); -		}  	}  	if (other)  		sock_put(other); | 
