diff options
Diffstat (limited to 'net/unix/af_unix.c')
| -rw-r--r-- | net/unix/af_unix.c | 80 | 
1 files changed, 34 insertions, 46 deletions
| diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 0be0dcb07f7b..001ccc55ef0f 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -693,10 +693,7 @@ static void unix_release_sock(struct sock *sk, int embrion)  	unix_state_unlock(sk);  #if IS_ENABLED(CONFIG_AF_UNIX_OOB) -	if (u->oob_skb) { -		kfree_skb(u->oob_skb); -		u->oob_skb = NULL; -	} +	u->oob_skb = NULL;  #endif  	wake_up_interruptible_all(&u->peer_wait); @@ -2226,13 +2223,9 @@ static int queue_oob(struct socket *sock, struct msghdr *msg, struct sock *other  	}  	maybe_add_creds(skb, sock, other); -	skb_get(skb); -  	scm_stat_add(other, skb);  	spin_lock(&other->sk_receive_queue.lock); -	if (ousk->oob_skb) -		consume_skb(ousk->oob_skb);  	WRITE_ONCE(ousk->oob_skb, skb);  	__skb_queue_tail(&other->sk_receive_queue, skb);  	spin_unlock(&other->sk_receive_queue.lock); @@ -2640,8 +2633,6 @@ static int unix_stream_recv_urg(struct unix_stream_read_state *state)  	if (!(state->flags & MSG_PEEK))  		WRITE_ONCE(u->oob_skb, NULL); -	else -		skb_get(oob_skb);  	spin_unlock(&sk->sk_receive_queue.lock);  	unix_state_unlock(sk); @@ -2651,8 +2642,6 @@ static int unix_stream_recv_urg(struct unix_stream_read_state *state)  	if (!(state->flags & MSG_PEEK))  		UNIXCB(oob_skb).consumed += 1; -	consume_skb(oob_skb); -  	mutex_unlock(&u->iolock);  	if (chunk < 0) @@ -2665,56 +2654,52 @@ static int unix_stream_recv_urg(struct unix_stream_read_state *state)  static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk,  				  int flags, int copied)  { +	struct sk_buff *read_skb = NULL, *unread_skb = NULL;  	struct unix_sock *u = unix_sk(sk); -	if (!unix_skb_len(skb)) { -		struct sk_buff *unlinked_skb = NULL; +	if (likely(unix_skb_len(skb) && skb != READ_ONCE(u->oob_skb))) +		return skb; -		spin_lock(&sk->sk_receive_queue.lock); +	spin_lock(&sk->sk_receive_queue.lock); +	if (!unix_skb_len(skb)) {  		if (copied && (!u->oob_skb || skb == u->oob_skb)) {  			skb = NULL;  		} else if (flags & MSG_PEEK) {  			skb = skb_peek_next(skb, &sk->sk_receive_queue);  		} else { -			unlinked_skb = skb; +			read_skb = skb;  			skb = skb_peek_next(skb, &sk->sk_receive_queue); -			__skb_unlink(unlinked_skb, &sk->sk_receive_queue); +			__skb_unlink(read_skb, &sk->sk_receive_queue);  		} -		spin_unlock(&sk->sk_receive_queue.lock); +		if (!skb) +			goto unlock; +	} -		consume_skb(unlinked_skb); -	} else { -		struct sk_buff *unlinked_skb = NULL; +	if (skb != u->oob_skb) +		goto unlock; -		spin_lock(&sk->sk_receive_queue.lock); +	if (copied) { +		skb = NULL; +	} else if (!(flags & MSG_PEEK)) { +		WRITE_ONCE(u->oob_skb, NULL); -		if (skb == u->oob_skb) { -			if (copied) { -				skb = NULL; -			} else if (!(flags & MSG_PEEK)) { -				if (sock_flag(sk, SOCK_URGINLINE)) { -					WRITE_ONCE(u->oob_skb, NULL); -					consume_skb(skb); -				} else { -					__skb_unlink(skb, &sk->sk_receive_queue); -					WRITE_ONCE(u->oob_skb, NULL); -					unlinked_skb = skb; -					skb = skb_peek(&sk->sk_receive_queue); -				} -			} else if (!sock_flag(sk, SOCK_URGINLINE)) { -				skb = skb_peek_next(skb, &sk->sk_receive_queue); -			} +		if (!sock_flag(sk, SOCK_URGINLINE)) { +			__skb_unlink(skb, &sk->sk_receive_queue); +			unread_skb = skb; +			skb = skb_peek(&sk->sk_receive_queue);  		} +	} else if (!sock_flag(sk, SOCK_URGINLINE)) { +		skb = skb_peek_next(skb, &sk->sk_receive_queue); +	} -		spin_unlock(&sk->sk_receive_queue.lock); +unlock: +	spin_unlock(&sk->sk_receive_queue.lock); + +	consume_skb(read_skb); +	kfree_skb(unread_skb); -		if (unlinked_skb) { -			WARN_ON_ONCE(skb_unref(unlinked_skb)); -			kfree_skb(unlinked_skb); -		} -	}  	return skb;  }  #endif @@ -2756,7 +2741,6 @@ static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor)  		unix_state_unlock(sk);  		if (drop) { -			WARN_ON_ONCE(skb_unref(skb));  			kfree_skb(skb);  			return -EAGAIN;  		} @@ -3192,9 +3176,13 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)  			skb = skb_peek(&sk->sk_receive_queue);  			if (skb) {  				struct sk_buff *oob_skb = READ_ONCE(u->oob_skb); +				struct sk_buff *next_skb; + +				next_skb = skb_peek_next(skb, &sk->sk_receive_queue);  				if (skb == oob_skb || -				    (!oob_skb && !unix_skb_len(skb))) +				    (!unix_skb_len(skb) && +				     (!oob_skb || next_skb == oob_skb)))  					answ = 1;  			} | 
