diff options
Diffstat (limited to 'net/unix')
| -rw-r--r-- | net/unix/af_unix.c | 41 | ||||
| -rw-r--r-- | net/unix/unix_bpf.c | 3 | 
2 files changed, 43 insertions, 1 deletions
| diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index b0a4c6d08e0a..0be0dcb07f7b 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2721,10 +2721,49 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk,  static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor)  { +	struct unix_sock *u = unix_sk(sk); +	struct sk_buff *skb; +	int err; +  	if (unlikely(READ_ONCE(sk->sk_state) != TCP_ESTABLISHED))  		return -ENOTCONN; -	return unix_read_skb(sk, recv_actor); +	mutex_lock(&u->iolock); +	skb = skb_recv_datagram(sk, MSG_DONTWAIT, &err); +	mutex_unlock(&u->iolock); +	if (!skb) +		return err; + +#if IS_ENABLED(CONFIG_AF_UNIX_OOB) +	if (unlikely(skb == READ_ONCE(u->oob_skb))) { +		bool drop = false; + +		unix_state_lock(sk); + +		if (sock_flag(sk, SOCK_DEAD)) { +			unix_state_unlock(sk); +			kfree_skb(skb); +			return -ECONNRESET; +		} + +		spin_lock(&sk->sk_receive_queue.lock); +		if (likely(skb == u->oob_skb)) { +			WRITE_ONCE(u->oob_skb, NULL); +			drop = true; +		} +		spin_unlock(&sk->sk_receive_queue.lock); + +		unix_state_unlock(sk); + +		if (drop) { +			WARN_ON_ONCE(skb_unref(skb)); +			kfree_skb(skb); +			return -EAGAIN; +		} +	} +#endif + +	return recv_actor(sk, skb);  }  static int unix_stream_read_generic(struct unix_stream_read_state *state, diff --git a/net/unix/unix_bpf.c b/net/unix/unix_bpf.c index bd84785bf8d6..bca2d86ba97d 100644 --- a/net/unix/unix_bpf.c +++ b/net/unix/unix_bpf.c @@ -54,6 +54,9 @@ static int unix_bpf_recvmsg(struct sock *sk, struct msghdr *msg,  	struct sk_psock *psock;  	int copied; +	if (flags & MSG_OOB) +		return -EOPNOTSUPP; +  	if (!len)  		return 0; |