Skip to content

Commit 93c99f2

Browse files
q2venPaolo Abeni
authored andcommitted
af_unix: Don't stop recv(MSG_DONTWAIT) if consumed OOB skb is at the head.
Let's say a socket send()s "hello" with MSG_OOB and "world" without flags, >>> from socket import * >>> c1, c2 = socketpair(AF_UNIX) >>> c1.send(b'hello', MSG_OOB) 5 >>> c1.send(b'world') 5 and its peer recv()s "hell" and "o". >>> c2.recv(10) b'hell' >>> c2.recv(1, MSG_OOB) b'o' Now the consumed OOB skb stays at the head of recvq to return a correct value for ioctl(SIOCATMARK), which is broken now and fixed by a later patch. Then, if peer issues recv() with MSG_DONTWAIT, manage_oob() returns NULL, so recv() ends up with -EAGAIN. >>> c2.setblocking(False) # This causes -EAGAIN even with available data >>> c2.recv(5) Traceback (most recent call last): File "<stdin>", line 1, in <module> BlockingIOError: [Errno 11] Resource temporarily unavailable However, next recv() will return the following available data, "world". >>> c2.recv(5) b'world' When the consumed OOB skb is at the head of the queue, we need to fetch the next skb to fix the weird behaviour. Note that the issue does not happen without MSG_DONTWAIT because we can retry after manage_oob(). This patch also adds a test case that covers the issue. Without fix: # RUN msg_oob.no_peek.ex_oob_break ... # msg_oob.c:134:ex_oob_break:AF_UNIX :Resource temporarily unavailable # msg_oob.c:135:ex_oob_break:Expected:ld # msg_oob.c:137:ex_oob_break:Expected ret[0] (-1) == expected_len (2) # ex_oob_break: Test terminated by assertion # FAIL msg_oob.no_peek.ex_oob_break not ok 8 msg_oob.no_peek.ex_oob_break With fix: # RUN msg_oob.no_peek.ex_oob_break ... # OK msg_oob.no_peek.ex_oob_break ok 8 msg_oob.no_peek.ex_oob_break Fixes: 314001f ("af_unix: Add OOB support") Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
1 parent b94038d commit 93c99f2

2 files changed

Lines changed: 26 additions & 4 deletions

File tree

net/unix/af_unix.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2614,12 +2614,23 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk,
26142614
struct unix_sock *u = unix_sk(sk);
26152615

26162616
if (!unix_skb_len(skb)) {
2617-
if (!(flags & MSG_PEEK)) {
2618-
skb_unlink(skb, &sk->sk_receive_queue);
2619-
consume_skb(skb);
2617+
struct sk_buff *unlinked_skb = NULL;
2618+
2619+
spin_lock(&sk->sk_receive_queue.lock);
2620+
2621+
if (copied) {
2622+
skb = NULL;
2623+
} else if (flags & MSG_PEEK) {
2624+
skb = skb_peek_next(skb, &sk->sk_receive_queue);
2625+
} else {
2626+
unlinked_skb = skb;
2627+
skb = skb_peek_next(skb, &sk->sk_receive_queue);
2628+
__skb_unlink(unlinked_skb, &sk->sk_receive_queue);
26202629
}
26212630

2622-
skb = NULL;
2631+
spin_unlock(&sk->sk_receive_queue.lock);
2632+
2633+
consume_skb(unlinked_skb);
26232634
} else {
26242635
struct sk_buff *unlinked_skb = NULL;
26252636

tools/testing/selftests/net/af_unix/msg_oob.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,4 +238,15 @@ TEST_F(msg_oob, oob_break_drop)
238238
recvpair("", -EINVAL, 1, MSG_OOB);
239239
}
240240

241+
TEST_F(msg_oob, ex_oob_break)
242+
{
243+
sendpair("hello", 5, MSG_OOB);
244+
sendpair("wor", 3, MSG_OOB);
245+
sendpair("ld", 2, 0);
246+
247+
recvpair("hellowo", 7, 10, 0); /* Break at OOB but not at ex-OOB. */
248+
recvpair("r", 1, 1, MSG_OOB);
249+
recvpair("ld", 2, 2, 0);
250+
}
251+
241252
TEST_HARNESS_MAIN

0 commit comments

Comments
 (0)