Skip to content

Commit 7bf3a47

Browse files
q2venkuba-moo
authored andcommitted
af_unix: Read sk_peek_offset() again after sleeping in unix_stream_read_generic().
Miao Wang reported a bug of SO_PEEK_OFF on AF_UNIX SOCK_STREAM socket. The unexpected behaviour is triggered when the peek offset is larger than the recv queue and the thread is unblocked by new data. Let's assume a socket which has "aaaa" in the recv queue and the peek offset is 4. First, unix_stream_read_generic() reads the offset 4 and skips the skb(s) of "aaaa" with the code below: skip = max(sk_peek_offset(sk, flags), 0); /* @Skip is 4. */ do { ... while (skip >= unix_skb_len(skb)) { skip -= unix_skb_len(skb); ... skb = skb_peek_next(skb, &sk->sk_receive_queue); if (!skb) goto again; /* @Skip is 0. */ } The thread jumps to the 'again' label and goes to sleep since new data has not arrived yet. Later, new data "bbbb" unblocks the thread, and the thread jumps to the 'redo:' label to restart the entire process from the first skb in the recv queue. do { ... redo: ... last = skb = skb_peek(&sk->sk_receive_queue); ... again: if (skb == NULL) { ... timeo = unix_stream_data_wait(sk, timeo, last, last_len, freezable); ... goto redo; /* @Skip is 0 !! */ However, the peek offset is not reset in the path. If the buffer size is 8, recv() will return "aaaabbbb" without skipping any data, and the final offset will be 12 (the original offset 4 + peeked skbs' length 8). After sleeping in unix_stream_read_generic(), we have to fetch the peek offset again. Let's move the redo label before mutex_lock(&u->iolock). Fixes: 9f389e3 ("af_unix: return data from multiple SKBs on recv() with MSG_PEEK flag") Reported-by: Miao Wang <shankerwangmiao@gmail.com> Closes: https://lore.kernel.org/netdev/3B969F90-F51F-4B9D-AB1A-994D9A54D460@gmail.com/ Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com> Link: https://patch.msgid.link/20251117174740.3684604-2-kuniyu@google.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1 parent d47515a commit 7bf3a47

1 file changed

Lines changed: 1 addition & 2 deletions

File tree

net/unix/af_unix.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2954,6 +2954,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
29542954

29552955
u = unix_sk(sk);
29562956

2957+
redo:
29572958
/* Lock the socket to prevent queue disordering
29582959
* while sleeps in memcpy_tomsg
29592960
*/
@@ -2965,7 +2966,6 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
29652966
struct sk_buff *skb, *last;
29662967
int chunk;
29672968

2968-
redo:
29692969
unix_state_lock(sk);
29702970
if (sock_flag(sk, SOCK_DEAD)) {
29712971
err = -ECONNRESET;
@@ -3015,7 +3015,6 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
30153015
goto out;
30163016
}
30173017

3018-
mutex_lock(&u->iolock);
30193018
goto redo;
30203019
unlock:
30213020
unix_state_unlock(sk);

0 commit comments

Comments
 (0)