diff options
Diffstat (limited to 'net/unix/af_unix.c')
| -rw-r--r-- | net/unix/af_unix.c | 34 | 
1 files changed, 25 insertions, 9 deletions
| diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 2e2e9997a68e..52b155123985 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -660,6 +660,11 @@ static void unix_sock_destructor(struct sock *sk)  #endif  } +static unsigned int unix_skb_len(const struct sk_buff *skb) +{ +	return skb->len - UNIXCB(skb).consumed; +} +  static void unix_release_sock(struct sock *sk, int embrion)  {  	struct unix_sock *u = unix_sk(sk); @@ -694,10 +699,16 @@ static void unix_release_sock(struct sock *sk, int embrion)  	if (skpair != NULL) {  		if (sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET) { +			struct sk_buff *skb = skb_peek(&sk->sk_receive_queue); + +#if IS_ENABLED(CONFIG_AF_UNIX_OOB) +			if (skb && !unix_skb_len(skb)) +				skb = skb_peek_next(skb, &sk->sk_receive_queue); +#endif  			unix_state_lock(skpair);  			/* No more writes */  			WRITE_ONCE(skpair->sk_shutdown, SHUTDOWN_MASK); -			if (!skb_queue_empty_lockless(&sk->sk_receive_queue) || embrion) +			if (skb || embrion)  				WRITE_ONCE(skpair->sk_err, ECONNRESET);  			unix_state_unlock(skpair);  			skpair->sk_state_change(skpair); @@ -1971,7 +1982,8 @@ static void unix_maybe_add_creds(struct sk_buff *skb, const struct sock *sk,  	if (UNIXCB(skb).pid)  		return; -	if (unix_may_passcred(sk) || unix_may_passcred(other)) { +	if (unix_may_passcred(sk) || unix_may_passcred(other) || +	    !other->sk_socket) {  		UNIXCB(skb).pid = get_pid(task_tgid(current));  		current_uid_gid(&UNIXCB(skb).uid, &UNIXCB(skb).gid);  	} @@ -2660,11 +2672,6 @@ static long unix_stream_data_wait(struct sock *sk, long timeo,  	return timeo;  } -static unsigned int unix_skb_len(const struct sk_buff *skb) -{ -	return skb->len - UNIXCB(skb).consumed; -} -  struct unix_stream_read_state {  	int (*recv_actor)(struct sk_buff *, int, int,  			  struct unix_stream_read_state *); @@ -2679,11 +2686,11 @@ struct unix_stream_read_state {  #if IS_ENABLED(CONFIG_AF_UNIX_OOB)  static int unix_stream_recv_urg(struct unix_stream_read_state *state)  { +	struct sk_buff *oob_skb, *read_skb = NULL;  	struct socket *sock = state->socket;  	struct sock *sk = sock->sk;  	struct unix_sock *u = unix_sk(sk);  	int chunk = 1; -	struct sk_buff *oob_skb;  	mutex_lock(&u->iolock);  	unix_state_lock(sk); @@ -2698,9 +2705,16 @@ static int unix_stream_recv_urg(struct unix_stream_read_state *state)  	oob_skb = u->oob_skb; -	if (!(state->flags & MSG_PEEK)) +	if (!(state->flags & MSG_PEEK)) {  		WRITE_ONCE(u->oob_skb, NULL); +		if (oob_skb->prev != (struct sk_buff *)&sk->sk_receive_queue && +		    !unix_skb_len(oob_skb->prev)) { +			read_skb = oob_skb->prev; +			__skb_unlink(read_skb, &sk->sk_receive_queue); +		} +	} +  	spin_unlock(&sk->sk_receive_queue.lock);  	unix_state_unlock(sk); @@ -2711,6 +2725,8 @@ static int unix_stream_recv_urg(struct unix_stream_read_state *state)  	mutex_unlock(&u->iolock); +	consume_skb(read_skb); +  	if (chunk < 0)  		return -EFAULT; | 
