From bf541423b785ae14ee2c7015d96dba6ac368358c Mon Sep 17 00:00:00 2001 From: Rao Shoaib Date: Tue, 16 Apr 2024 12:39:54 -0700 Subject: [PATCH] AF_UNIX: Fix handling of OOB Data This patch fixes corner cases in reading when there is a pending OOB and MSG_PEEK flag is set. Since there is no standard for handling MSG_OOB and MSG_PEEK, behavior of TCP is used as a reference. First byte in the read queue is OOB byte First byte in the read queue is OOB byte followed by normal data New OOB arrives before the previous one was consumed Signed-off-by: Rao Shoaib --- net/unix/af_unix.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index d032eb5fa6df..50cae03a1f98 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2217,8 +2217,13 @@ static int queue_oob(struct socket *sock, struct msghdr *msg, struct sock *other maybe_add_creds(skb, sock, other); skb_get(skb); - if (ousk->oob_skb) - consume_skb(ousk->oob_skb); + if (ousk->oob_skb) { + skb_unref(ousk->oob_skb); + if (!sock_flag(other, SOCK_URGINLINE)) { + skb_unlink(ousk->oob_skb, &other->sk_receive_queue); + consume_skb(ousk->oob_skb); + } + } WRITE_ONCE(ousk->oob_skb, skb); @@ -2658,17 +2663,20 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk, if (skb == u->oob_skb) { if (copied) { skb = NULL; - } else if (sock_flag(sk, SOCK_URGINLINE)) { - if (!(flags & MSG_PEEK)) { + } 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); + if (!WARN_ON_ONCE(skb_unref(skb))) + kfree_skb(skb); + skb = skb_peek(&sk->sk_receive_queue); } - } else if (!(flags & MSG_PEEK)) { - skb_unlink(skb, &sk->sk_receive_queue); - WRITE_ONCE(u->oob_skb, NULL); - if (!WARN_ON_ONCE(skb_unref(skb))) - kfree_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); } } } @@ -2741,18 +2749,18 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state, last = skb = skb_peek(&sk->sk_receive_queue); last_len = last ? last->len : 0; +again: #if IS_ENABLED(CONFIG_AF_UNIX_OOB) if (skb) { skb = manage_oob(skb, sk, flags, copied); if (!skb) { unix_state_unlock(sk); - if (copied) + if (copied || (flags & MSG_PEEK)) break; goto redo; } } #endif -again: if (skb == NULL) { if (copied >= target) goto unlock; -- 2.39.3