mirror of
https://github.com/torvalds/linux.git
synced 2026-09-12 12:34:02 +02:00
af_unix: Update last skb marker in manage_oob().
Fahad Alharbi reported that blocking recv(MSG_PEEK) could hog CPU
due to OOB skb.
In the following cases, manage_oob() skips OOB skb(s) and returns
NULL for the last recv(MSG_PEEK):
socketpair(AF_UNIX, SOCK_STREAM, 0, sk);
1) skb -> OOB skb -> NULL
send(sk[0], "ab", 2, MSG_OOB);
recv(sk[1], buf, 0, MSG_PEEK);
2) skb -> consumed OOB skb -> NULL
send(sk[0], "ab", 2, MSG_OOB);
recv(sk[1], buf, 1, MSG_OOB);
recv(sk[1], buf, 0, MSG_PEEK);
3) consumed OOB skb -> OOB skb -> NULL
send(sk[0], "a", 1, MSG_OOB);
recv(sk[1], buf, 0, MSG_OOB);
send(sk[0], "b", 1, MSG_OOB);
recv(sk[1], buf, 1, MSG_PEEK);
Then, @copied is 0 in unix_stream_read_generic() (zero-length buffer,
or non-OOB skb is not yet consumed), and unix_stream_data_wait() is
called.
However, it returns immediately because @last is not updated in
unix_stream_read_generic(), and the thread busy-waits for a new skb.
Let's update @last in manage_oob().
For MSG_PEEK, @last is updated with the skipped OOB, and for the
non-peek case, @last matches the returned value (when !copied)
because OOB is unlinked.
Note that manage_oob() is inlined and no stack canary is added.
Fixes: 22dd70eb2c ("af_unix: Don't peek OOB data without MSG_OOB.")
Reported-by: Fahad Alharbi <fahad@codepure.com>
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Link: https://patch.msgid.link/20260902202202.892676-2-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
74f27fc864
commit
94fd4debd2
|
|
@ -2812,8 +2812,8 @@ static int unix_stream_recv_urg(struct unix_stream_read_state *state)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk,
|
||||
int flags, int copied)
|
||||
static struct sk_buff *manage_oob(struct sk_buff *skb, struct sk_buff **last,
|
||||
struct sock *sk, int flags, int copied)
|
||||
{
|
||||
struct sk_buff *read_skb = NULL, *unread_skb = NULL;
|
||||
struct unix_sock *u = unix_sk(sk);
|
||||
|
|
@ -2827,11 +2827,13 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk,
|
|||
if (copied && (!u->oob_skb || skb == u->oob_skb)) {
|
||||
skb = NULL;
|
||||
} else if (flags & MSG_PEEK) {
|
||||
*last = skb;
|
||||
skb = skb_peek_next(skb, &sk->sk_receive_queue);
|
||||
} else {
|
||||
read_skb = skb;
|
||||
skb = skb_peek_next(skb, &sk->sk_receive_queue);
|
||||
__skb_unlink(read_skb, &sk->sk_receive_queue);
|
||||
*last = skb;
|
||||
}
|
||||
|
||||
if (!skb)
|
||||
|
|
@ -2850,8 +2852,10 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk,
|
|||
__skb_unlink(skb, &sk->sk_receive_queue);
|
||||
unread_skb = skb;
|
||||
skb = skb_peek(&sk->sk_receive_queue);
|
||||
*last = skb;
|
||||
}
|
||||
} else if (!sock_flag(sk, SOCK_URGINLINE)) {
|
||||
*last = skb;
|
||||
skb = skb_peek_next(skb, &sk->sk_receive_queue);
|
||||
}
|
||||
|
||||
|
|
@ -2971,7 +2975,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
|
|||
again:
|
||||
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
||||
if (skb) {
|
||||
skb = manage_oob(skb, sk, flags, copied);
|
||||
skb = manage_oob(skb, &last, sk, flags, copied);
|
||||
if (!skb && copied) {
|
||||
unix_state_unlock(sk);
|
||||
break;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user