Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'af_unix-fix-so_peek_off-bug-in-unix_stream_read_generic'

Kuniyuki Iwashima says:

====================
af_unix: Fix SO_PEEK_OFF bug in unix_stream_read_generic().

Miao Wang reported a bug of SO_PEEK_OFF on AF_UNIX SOCK_STREAM socket.

Patch 1 fixes the bug and Patch 2 adds a new selftest to cover the case.
====================

Link: https://patch.msgid.link/20251117174740.3684604-1-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+165 -2
+1 -2
net/unix/af_unix.c
··· 2954 2954 2955 2955 u = unix_sk(sk); 2956 2956 2957 + redo: 2957 2958 /* Lock the socket to prevent queue disordering 2958 2959 * while sleeps in memcpy_tomsg 2959 2960 */ ··· 2966 2965 struct sk_buff *skb, *last; 2967 2966 int chunk; 2968 2967 2969 - redo: 2970 2968 unix_state_lock(sk); 2971 2969 if (sock_flag(sk, SOCK_DEAD)) { 2972 2970 err = -ECONNRESET; ··· 3015 3015 goto out; 3016 3016 } 3017 3017 3018 - mutex_lock(&u->iolock); 3019 3018 goto redo; 3020 3019 unlock: 3021 3020 unix_state_unlock(sk);
+1
tools/testing/selftests/net/.gitignore
··· 45 45 socket 46 46 so_incoming_cpu 47 47 so_netns_cookie 48 + so_peek_off 48 49 so_txtime 49 50 so_rcv_listener 50 51 stress_reuseport_listen
+1
tools/testing/selftests/net/af_unix/Makefile
··· 6 6 scm_inq \ 7 7 scm_pidfd \ 8 8 scm_rights \ 9 + so_peek_off \ 9 10 unix_connect \ 10 11 # end of TEST_GEN_PROGS 11 12
+162
tools/testing/selftests/net/af_unix/so_peek_off.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright 2025 Google LLC */ 3 + 4 + #include <stdlib.h> 5 + #include <unistd.h> 6 + 7 + #include <sys/socket.h> 8 + 9 + #include "../../kselftest_harness.h" 10 + 11 + FIXTURE(so_peek_off) 12 + { 13 + int fd[2]; /* 0: sender, 1: receiver */ 14 + }; 15 + 16 + FIXTURE_VARIANT(so_peek_off) 17 + { 18 + int type; 19 + }; 20 + 21 + FIXTURE_VARIANT_ADD(so_peek_off, stream) 22 + { 23 + .type = SOCK_STREAM, 24 + }; 25 + 26 + FIXTURE_VARIANT_ADD(so_peek_off, dgram) 27 + { 28 + .type = SOCK_DGRAM, 29 + }; 30 + 31 + FIXTURE_VARIANT_ADD(so_peek_off, seqpacket) 32 + { 33 + .type = SOCK_SEQPACKET, 34 + }; 35 + 36 + FIXTURE_SETUP(so_peek_off) 37 + { 38 + struct timeval timeout = { 39 + .tv_sec = 0, 40 + .tv_usec = 3000, 41 + }; 42 + int ret; 43 + 44 + ret = socketpair(AF_UNIX, variant->type, 0, self->fd); 45 + ASSERT_EQ(0, ret); 46 + 47 + ret = setsockopt(self->fd[1], SOL_SOCKET, SO_RCVTIMEO_NEW, 48 + &timeout, sizeof(timeout)); 49 + ASSERT_EQ(0, ret); 50 + 51 + ret = setsockopt(self->fd[1], SOL_SOCKET, SO_PEEK_OFF, 52 + &(int){0}, sizeof(int)); 53 + ASSERT_EQ(0, ret); 54 + } 55 + 56 + FIXTURE_TEARDOWN(so_peek_off) 57 + { 58 + close_range(self->fd[0], self->fd[1], 0); 59 + } 60 + 61 + #define sendeq(fd, str, flags) \ 62 + do { \ 63 + int bytes, len = strlen(str); \ 64 + \ 65 + bytes = send(fd, str, len, flags); \ 66 + ASSERT_EQ(len, bytes); \ 67 + } while (0) 68 + 69 + #define recveq(fd, str, buflen, flags) \ 70 + do { \ 71 + char buf[(buflen) + 1] = {}; \ 72 + int bytes; \ 73 + \ 74 + bytes = recv(fd, buf, buflen, flags); \ 75 + ASSERT_NE(-1, bytes); \ 76 + ASSERT_STREQ(str, buf); \ 77 + } while (0) 78 + 79 + #define async \ 80 + for (pid_t pid = (pid = fork(), \ 81 + pid < 0 ? \ 82 + __TH_LOG("Failed to start async {}"), \ 83 + _metadata->exit_code = KSFT_FAIL, \ 84 + __bail(1, _metadata), \ 85 + 0xdead : \ 86 + pid); \ 87 + !pid; exit(0)) 88 + 89 + TEST_F(so_peek_off, single_chunk) 90 + { 91 + sendeq(self->fd[0], "aaaabbbb", 0); 92 + 93 + recveq(self->fd[1], "aaaa", 4, MSG_PEEK); 94 + recveq(self->fd[1], "bbbb", 100, MSG_PEEK); 95 + } 96 + 97 + TEST_F(so_peek_off, two_chunks) 98 + { 99 + sendeq(self->fd[0], "aaaa", 0); 100 + sendeq(self->fd[0], "bbbb", 0); 101 + 102 + recveq(self->fd[1], "aaaa", 4, MSG_PEEK); 103 + recveq(self->fd[1], "bbbb", 100, MSG_PEEK); 104 + } 105 + 106 + TEST_F(so_peek_off, two_chunks_blocking) 107 + { 108 + async { 109 + usleep(1000); 110 + sendeq(self->fd[0], "aaaa", 0); 111 + } 112 + 113 + recveq(self->fd[1], "aaaa", 4, MSG_PEEK); 114 + 115 + async { 116 + usleep(1000); 117 + sendeq(self->fd[0], "bbbb", 0); 118 + } 119 + 120 + /* goto again; -> goto redo; in unix_stream_read_generic(). */ 121 + recveq(self->fd[1], "bbbb", 100, MSG_PEEK); 122 + } 123 + 124 + TEST_F(so_peek_off, two_chunks_overlap) 125 + { 126 + sendeq(self->fd[0], "aaaa", 0); 127 + recveq(self->fd[1], "aa", 2, MSG_PEEK); 128 + 129 + sendeq(self->fd[0], "bbbb", 0); 130 + 131 + if (variant->type == SOCK_STREAM) { 132 + /* SOCK_STREAM tries to fill the buffer. */ 133 + recveq(self->fd[1], "aabb", 4, MSG_PEEK); 134 + recveq(self->fd[1], "bb", 100, MSG_PEEK); 135 + } else { 136 + /* SOCK_DGRAM and SOCK_SEQPACKET returns at the skb boundary. */ 137 + recveq(self->fd[1], "aa", 100, MSG_PEEK); 138 + recveq(self->fd[1], "bbbb", 100, MSG_PEEK); 139 + } 140 + } 141 + 142 + TEST_F(so_peek_off, two_chunks_overlap_blocking) 143 + { 144 + async { 145 + usleep(1000); 146 + sendeq(self->fd[0], "aaaa", 0); 147 + } 148 + 149 + recveq(self->fd[1], "aa", 2, MSG_PEEK); 150 + 151 + async { 152 + usleep(1000); 153 + sendeq(self->fd[0], "bbbb", 0); 154 + } 155 + 156 + /* Even SOCK_STREAM does not wait if at least one byte is read. */ 157 + recveq(self->fd[1], "aa", 100, MSG_PEEK); 158 + 159 + recveq(self->fd[1], "bbbb", 100, MSG_PEEK); 160 + } 161 + 162 + TEST_HARNESS_MAIN