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 = 5, 40 .tv_usec = 0, 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 peekoffeq(fd, expected) \ 80 do { \ 81 socklen_t optlen = sizeof(int); \ 82 int off = -1; \ 83 int ret; \ 84 \ 85 ret = getsockopt(fd, SOL_SOCKET, SO_PEEK_OFF, \ 86 &off, &optlen); \ 87 ASSERT_EQ(0, ret); \ 88 ASSERT_EQ((socklen_t)sizeof(off), optlen); \ 89 ASSERT_EQ(expected, off); \ 90 } while (0) 91 92 #define async \ 93 for (pid_t pid = (pid = fork(), \ 94 pid < 0 ? \ 95 __TH_LOG("Failed to start async {}"), \ 96 _metadata->exit_code = KSFT_FAIL, \ 97 __bail(1, _metadata), \ 98 0xdead : \ 99 pid); \ 100 !pid; exit(0)) 101 102 TEST_F(so_peek_off, single_chunk) 103 { 104 sendeq(self->fd[0], "aaaabbbb", 0); 105 106 recveq(self->fd[1], "aaaa", 4, MSG_PEEK); 107 peekoffeq(self->fd[1], 4); 108 recveq(self->fd[1], "bbbb", 100, MSG_PEEK); 109 peekoffeq(self->fd[1], 8); 110 111 recveq(self->fd[1], "aaaabbbb", 8, 0); 112 peekoffeq(self->fd[1], 0); 113 } 114 115 TEST_F(so_peek_off, two_chunks) 116 { 117 sendeq(self->fd[0], "aaaa", 0); 118 sendeq(self->fd[0], "bbbb", 0); 119 120 recveq(self->fd[1], "aaaa", 4, MSG_PEEK); 121 peekoffeq(self->fd[1], 4); 122 recveq(self->fd[1], "bbbb", 100, MSG_PEEK); 123 peekoffeq(self->fd[1], 8); 124 125 recveq(self->fd[1], "aaaa", 4, 0); 126 recveq(self->fd[1], "bbbb", 4, 0); 127 peekoffeq(self->fd[1], 0); 128 } 129 130 TEST_F(so_peek_off, two_chunks_blocking) 131 { 132 async { 133 usleep(1000); 134 sendeq(self->fd[0], "aaaa", 0); 135 } 136 137 recveq(self->fd[1], "aaaa", 4, MSG_PEEK); 138 peekoffeq(self->fd[1], 4); 139 140 async { 141 usleep(1000); 142 sendeq(self->fd[0], "bbbb", 0); 143 } 144 145 /* goto again; -> goto redo; in unix_stream_read_generic(). */ 146 recveq(self->fd[1], "bbbb", 100, MSG_PEEK); 147 peekoffeq(self->fd[1], 8); 148 149 recveq(self->fd[1], "aaaa", 4, 0); 150 recveq(self->fd[1], "bbbb", 4, 0); 151 peekoffeq(self->fd[1], 0); 152 } 153 154 TEST_F(so_peek_off, two_chunks_overlap) 155 { 156 sendeq(self->fd[0], "aaaa", 0); 157 recveq(self->fd[1], "aa", 2, MSG_PEEK); 158 peekoffeq(self->fd[1], 2); 159 160 sendeq(self->fd[0], "bbbb", 0); 161 162 if (variant->type == SOCK_STREAM) { 163 /* SOCK_STREAM tries to fill the buffer. */ 164 recveq(self->fd[1], "aabb", 4, MSG_PEEK); 165 peekoffeq(self->fd[1], 6); 166 recveq(self->fd[1], "bb", 100, MSG_PEEK); 167 peekoffeq(self->fd[1], 8); 168 } else { 169 /* SOCK_DGRAM and SOCK_SEQPACKET returns at the skb boundary. */ 170 recveq(self->fd[1], "aa", 100, MSG_PEEK); 171 peekoffeq(self->fd[1], 4); 172 recveq(self->fd[1], "bbbb", 100, MSG_PEEK); 173 peekoffeq(self->fd[1], 8); 174 } 175 176 recveq(self->fd[1], "aaaa", 4, 0); 177 recveq(self->fd[1], "bbbb", 4, 0); 178 peekoffeq(self->fd[1], 0); 179 } 180 181 TEST_F(so_peek_off, two_chunks_overlap_blocking) 182 { 183 async { 184 usleep(1000); 185 sendeq(self->fd[0], "aaaa", 0); 186 } 187 188 recveq(self->fd[1], "aa", 2, MSG_PEEK); 189 peekoffeq(self->fd[1], 2); 190 191 async { 192 usleep(1000); 193 sendeq(self->fd[0], "bbbb", 0); 194 } 195 196 /* Even SOCK_STREAM does not wait if at least one byte is read. */ 197 recveq(self->fd[1], "aa", 100, MSG_PEEK); 198 peekoffeq(self->fd[1], 4); 199 200 recveq(self->fd[1], "bbbb", 100, MSG_PEEK); 201 peekoffeq(self->fd[1], 8); 202 203 recveq(self->fd[1], "aaaa", 4, 0); 204 recveq(self->fd[1], "bbbb", 4, 0); 205 peekoffeq(self->fd[1], 0); 206 } 207 208 TEST_HARNESS_MAIN 209