1 /* 2 * Check decoding of select/_newselect syscalls. 3 * 4 * Copyright (c) 2015-2018 Dmitry V. Levin <ldv@altlinux.org> 5 * Copyright (c) 2015-2017 The strace developers. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /* 32 * Based on test by Dr. David Alan Gilbert <dave@treblig.org> 33 */ 34 35 #include <errno.h> 36 #include <limits.h> 37 #include <stdint.h> 38 #include <stdio.h> 39 #include <string.h> 40 #include <unistd.h> 41 #include <sys/select.h> 42 43 static const char *errstr; 44 45 static long 46 xselect(const kernel_ulong_t nfds, 47 const kernel_ulong_t rs, 48 const kernel_ulong_t ws, 49 const kernel_ulong_t es, 50 const kernel_ulong_t tv) 51 #ifndef xselect 52 { 53 long rc = syscall(TEST_SYSCALL_NR, 54 F8ILL_KULONG_MASK | nfds, rs, ws, es, tv); 55 errstr = sprintrc(rc); 56 return rc; 57 } 58 #else 59 ; 60 #endif 61 62 #define XSELECT(expected_, ...) \ 63 do { \ 64 long rc = xselect(__VA_ARGS__); \ 65 if (rc != (expected_)) \ 66 perror_msg_and_fail(TEST_SYSCALL_STR \ 67 ": expected %d" \ 68 ", returned %ld", \ 69 (expected_), rc); \ 70 } while (0) \ 71 /* End of XSELECT definition. */ 72 73 int 74 main(void) 75 { 76 #ifdef PATH_TRACING_FD 77 skip_if_unavailable("/proc/self/fd/"); 78 #endif 79 80 for (int i = 3; i < FD_SETSIZE; ++i) { 81 #ifdef PATH_TRACING_FD 82 if (i == PATH_TRACING_FD) 83 continue; 84 #endif 85 (void) close(i); 86 } 87 88 int fds[2]; 89 if (pipe(fds)) 90 perror_msg_and_fail("pipe"); 91 92 static const int smallset_size = sizeof(kernel_ulong_t) * 8; 93 const int nfds = fds[1] + 1; 94 if (nfds > smallset_size) 95 error_msg_and_fail("nfds[%d] > smallset_size[%d]\n", 96 nfds, smallset_size); 97 98 struct timeval tv_in = { 0, 123 }; 99 struct timeval *const tv = tail_memdup(&tv_in, sizeof(tv_in)); 100 const uintptr_t a_tv = (uintptr_t) tv; 101 102 TAIL_ALLOC_OBJECT_VAR_PTR(kernel_ulong_t, l_rs); 103 fd_set *const rs = (void *) l_rs; 104 const uintptr_t a_rs = (uintptr_t) rs; 105 106 TAIL_ALLOC_OBJECT_VAR_PTR(kernel_ulong_t, l_ws); 107 fd_set *const ws = (void *) l_ws; 108 const uintptr_t a_ws = (uintptr_t) ws; 109 110 TAIL_ALLOC_OBJECT_VAR_PTR(kernel_ulong_t, l_es); 111 fd_set *const es = (void *) l_es; 112 const uintptr_t a_es = (uintptr_t) es; 113 114 long rc; 115 116 /* 117 * An equivalent of nanosleep. 118 */ 119 if (xselect(0, 0, 0, 0, a_tv)) { 120 if (errno == ENOSYS) 121 perror_msg_and_skip(TEST_SYSCALL_STR); 122 else 123 perror_msg_and_fail(TEST_SYSCALL_STR); 124 } 125 #ifndef PATH_TRACING_FD 126 printf("%s(0, NULL, NULL, NULL, {tv_sec=%lld, tv_usec=%llu})" 127 " = 0 (Timeout)\n", 128 TEST_SYSCALL_STR, (long long) tv_in.tv_sec, 129 zero_extend_signed_to_ull(tv_in.tv_usec)); 130 #endif 131 132 /* EFAULT on tv argument */ 133 XSELECT(-1, 0, 0, 0, 0, a_tv + 1); 134 #ifndef PATH_TRACING_FD 135 printf("%s(0, NULL, NULL, NULL, %#lx) = %s\n", 136 TEST_SYSCALL_STR, (unsigned long) a_tv + 1, errstr); 137 #endif 138 139 /* 140 * Start with a nice simple select with the same set. 141 */ 142 for (int i = nfds; i <= smallset_size; ++i) { 143 *l_rs = (1UL << fds[0]) | (1UL << fds[1]); 144 XSELECT(1, i, a_rs, a_rs, a_rs, 0); 145 #ifndef PATH_TRACING_FD 146 printf("%s(%d, [%d %d], [%d %d], [%d %d], NULL) = 1 ()\n", 147 TEST_SYSCALL_STR, i, fds[0], fds[1], 148 fds[0], fds[1], fds[0], fds[1]); 149 #else 150 *l_rs = (1UL << fds[0]) | (1UL << fds[1]) | 151 (1UL << PATH_TRACING_FD); 152 XSELECT(i > PATH_TRACING_FD ? 3 : 1, i, a_rs, a_rs, a_rs, 0); 153 if (i > PATH_TRACING_FD) { 154 printf("%s(%d, [%d %d %d], [%d %d %d], [%d %d %d]" 155 ", NULL) = 3 ()\n", 156 TEST_SYSCALL_STR, i, 157 fds[0], fds[1], PATH_TRACING_FD, 158 fds[0], fds[1], PATH_TRACING_FD, 159 fds[0], fds[1], PATH_TRACING_FD); 160 } 161 #endif 162 } 163 164 /* 165 * Odd timeout. 166 */ 167 *l_rs = (1UL << fds[0]) | (1UL << fds[1]); 168 tv_in.tv_sec = 0xdeadbeefU; 169 tv_in.tv_usec = 0xfacefeedU; 170 memcpy(tv, &tv_in, sizeof(tv_in)); 171 rc = xselect(nfds, a_rs, a_rs, a_rs, a_tv); 172 if (rc < 0) { 173 #ifndef PATH_TRACING_FD 174 printf("%s(%d, [%d %d], [%d %d], [%d %d]" 175 ", {tv_sec=%lld, tv_usec=%llu}) = %s\n", 176 TEST_SYSCALL_STR, nfds, fds[0], fds[1], 177 fds[0], fds[1], fds[0], fds[1], 178 (long long) tv_in.tv_sec, 179 zero_extend_signed_to_ull(tv_in.tv_usec), 180 errstr); 181 #endif /* !PATH_TRACING_FD */ 182 } else { 183 #ifndef PATH_TRACING_FD 184 printf("%s(%d, [%d %d], [%d %d], [%d %d]" 185 ", {tv_sec=%lld, tv_usec=%llu}) = %ld" 186 " (left {tv_sec=%lld, tv_usec=%llu})\n", 187 TEST_SYSCALL_STR, nfds, fds[0], fds[1], 188 fds[0], fds[1], fds[0], fds[1], 189 (long long) tv_in.tv_sec, 190 zero_extend_signed_to_ull(tv_in.tv_usec), 191 rc, (long long) tv->tv_sec, 192 zero_extend_signed_to_ull(tv->tv_usec)); 193 #endif /* !PATH_TRACING_FD */ 194 } 195 196 /* 197 * Very odd timeout. 198 */ 199 *l_rs = (1UL << fds[0]) | (1UL << fds[1]); 200 tv_in.tv_sec = (time_t) 0xcafef00ddeadbeefLL; 201 tv_in.tv_usec = (suseconds_t) 0xbadc0dedfacefeedLL; 202 memcpy(tv, &tv_in, sizeof(tv_in)); 203 rc = xselect(nfds, a_rs, a_rs, a_rs, a_tv); 204 if (rc < 0) { 205 #ifndef PATH_TRACING_FD 206 printf("%s(%d, [%d %d], [%d %d], [%d %d]" 207 ", {tv_sec=%lld, tv_usec=%llu}) = %s\n", 208 TEST_SYSCALL_STR, nfds, fds[0], fds[1], 209 fds[0], fds[1], fds[0], fds[1], 210 (long long) tv_in.tv_sec, 211 zero_extend_signed_to_ull(tv_in.tv_usec), 212 errstr); 213 #endif /* PATH_TRACING_FD */ 214 } else { 215 #ifndef PATH_TRACING_FD 216 printf("%s(%d, [%d %d], [%d %d], [%d %d]" 217 ", {tv_sec=%lld, tv_usec=%llu}) = %ld" 218 " (left {tv_sec=%lld, tv_usec=%llu})\n", 219 TEST_SYSCALL_STR, nfds, fds[0], fds[1], 220 fds[0], fds[1], fds[0], fds[1], 221 (long long) tv_in.tv_sec, 222 zero_extend_signed_to_ull(tv_in.tv_usec), 223 rc, (long long) tv->tv_sec, 224 zero_extend_signed_to_ull(tv->tv_usec)); 225 #endif /* PATH_TRACING_FD */ 226 } 227 228 /* 229 * Another simple one, with a timeout. 230 */ 231 for (int i = nfds; i <= smallset_size; ++i) { 232 *l_rs = (1UL << fds[0]) | (1UL << fds[1]); 233 *l_ws = (1UL << 1) | (1UL << 2) | 234 (1UL << fds[0]) | (1UL << fds[1]); 235 *l_es = 0; 236 tv_in.tv_sec = 0xc0de1; 237 tv_in.tv_usec = 0xc0de2; 238 memcpy(tv, &tv_in, sizeof(tv_in)); 239 XSELECT(3, i, a_rs, a_ws, a_es, a_tv); 240 #ifndef PATH_TRACING_FD 241 printf("%s(%d, [%d %d], [%d %d %d %d], []" 242 ", {tv_sec=%lld, tv_usec=%llu}) = 3 (out [1 2 %d]" 243 ", left {tv_sec=%lld, tv_usec=%llu})\n", 244 TEST_SYSCALL_STR, i, fds[0], fds[1], 245 1, 2, fds[0], fds[1], 246 (long long) tv_in.tv_sec, 247 zero_extend_signed_to_ull(tv_in.tv_usec), 248 fds[1], 249 (long long) tv->tv_sec, 250 zero_extend_signed_to_ull(tv->tv_usec)); 251 #else 252 *l_rs = (1UL << fds[0]) | (1UL << fds[1]) | 253 (1UL << PATH_TRACING_FD); 254 *l_ws = (1UL << 1) | (1UL << 2) | 255 (1UL << fds[0]) | (1UL << fds[1]); 256 tv_in.tv_sec = 0xc0de1; 257 tv_in.tv_usec = 0xc0de2; 258 memcpy(tv, &tv_in, sizeof(tv_in)); 259 XSELECT(3 + (i > PATH_TRACING_FD), i, a_rs, a_ws, a_es, a_tv); 260 if (i > PATH_TRACING_FD) { 261 printf("%s(%d, [%d %d %d], [%d %d %d %d], []" 262 ", {tv_sec=%lld, tv_usec=%llu})" 263 " = 4 (in [%d], out [1 2 %d]" 264 ", left {tv_sec=%lld, tv_usec=%llu})\n", 265 TEST_SYSCALL_STR, i, 266 fds[0], fds[1], PATH_TRACING_FD, 267 1, 2, fds[0], fds[1], 268 (long long) tv_in.tv_sec, 269 zero_extend_signed_to_ull(tv_in.tv_usec), 270 PATH_TRACING_FD, fds[1], 271 (long long) tv->tv_sec, 272 zero_extend_signed_to_ull(tv->tv_usec)); 273 } 274 275 *l_rs = (1UL << fds[0]) | (1UL << fds[1]); 276 *l_ws = (1UL << 1) | (1UL << 2) | 277 (1UL << fds[0]) | (1UL << fds[1]) | 278 (1UL << PATH_TRACING_FD); 279 tv_in.tv_sec = 0xc0de1; 280 tv_in.tv_usec = 0xc0de2; 281 memcpy(tv, &tv_in, sizeof(tv_in)); 282 XSELECT(3 + (i > PATH_TRACING_FD), i, a_rs, a_ws, a_es, a_tv); 283 if (i > PATH_TRACING_FD) { 284 printf("%s(%d, [%d %d], [%d %d %d %d %d], []" 285 ", {tv_sec=%lld, tv_usec=%llu})" 286 " = 4 (out [1 2 %d %d]" 287 ", left {tv_sec=%lld, tv_usec=%llu})\n", 288 TEST_SYSCALL_STR, i, 289 fds[0], fds[1], 290 1, 2, fds[0], fds[1], PATH_TRACING_FD, 291 (long long) tv_in.tv_sec, 292 zero_extend_signed_to_ull(tv_in.tv_usec), 293 fds[1], PATH_TRACING_FD, 294 (long long) tv->tv_sec, 295 zero_extend_signed_to_ull(tv->tv_usec)); 296 } 297 298 *l_rs = (1UL << fds[0]) | (1UL << fds[1]); 299 *l_ws = (1UL << 1) | (1UL << 2) | 300 (1UL << fds[0]) | (1UL << fds[1]); 301 *l_es = (1UL << PATH_TRACING_FD); 302 tv_in.tv_sec = 0xc0de1; 303 tv_in.tv_usec = 0xc0de2; 304 memcpy(tv, &tv_in, sizeof(tv_in)); 305 XSELECT(3, i, a_rs, a_ws, a_es, a_tv); 306 if (i > PATH_TRACING_FD) { 307 printf("%s(%d, [%d %d], [%d %d %d %d], [%d]" 308 ", {tv_sec=%lld, tv_usec=%llu}) = 3 (out [1 2 %d]" 309 ", left {tv_sec=%lld, tv_usec=%llu})\n", 310 TEST_SYSCALL_STR, i, 311 fds[0], fds[1], 312 1, 2, fds[0], fds[1], PATH_TRACING_FD, 313 (long long) tv_in.tv_sec, 314 zero_extend_signed_to_ull(tv_in.tv_usec), 315 fds[1], 316 (long long) tv->tv_sec, 317 zero_extend_signed_to_ull(tv->tv_usec)); 318 } 319 320 #endif /* PATH_TRACING_FD */ 321 } 322 323 /* 324 * Now the crash case that trinity found, negative nfds 325 * but with a pointer to a large chunk of valid memory. 326 */ 327 static fd_set set[0x1000000 / sizeof(fd_set)]; 328 FD_SET(fds[1], set); 329 XSELECT(-1, -1U, 0, (uintptr_t) set, 0, 0); 330 #ifndef PATH_TRACING_FD 331 printf("%s(-1, NULL, %p, NULL, NULL) = %s\n", 332 TEST_SYSCALL_STR, set, errstr); 333 #endif 334 335 /* 336 * Big sets, nfds exceeds FD_SETSIZE limit. 337 */ 338 const size_t big_size = sizeof(fd_set) + sizeof(long); 339 fd_set *const big_rs = tail_alloc(big_size); 340 const uintptr_t a_big_rs = (uintptr_t) big_rs; 341 342 fd_set *const big_ws = tail_alloc(big_size); 343 const uintptr_t a_big_ws = (uintptr_t) big_ws; 344 345 for (unsigned int i = FD_SETSIZE; i <= big_size * 8; ++i) { 346 memset(big_rs, 0, big_size); 347 memset(big_ws, 0, big_size); 348 FD_SET(fds[0], big_rs); 349 tv->tv_sec = 0; 350 tv->tv_usec = 10 + (i - FD_SETSIZE); 351 XSELECT(0, i, a_big_rs, a_big_ws, 0, a_tv); 352 #ifndef PATH_TRACING_FD 353 printf("%s(%d, [%d], [], NULL, {tv_sec=0, tv_usec=%d})" 354 " = 0 (Timeout)\n", 355 TEST_SYSCALL_STR, i, fds[0], 10 + (i - FD_SETSIZE)); 356 #else 357 FD_SET(fds[0], big_rs); 358 FD_SET(PATH_TRACING_FD, big_rs); 359 tv->tv_sec = 0; 360 tv->tv_usec = 10 + (i - FD_SETSIZE); 361 XSELECT(1, i, a_big_rs, a_big_ws, 0, a_tv); 362 printf("%s(%d, [%d %d], [], NULL, {tv_sec=0, tv_usec=%d})" 363 " = 1 (in [%d], left {tv_sec=0, tv_usec=%llu})\n", 364 TEST_SYSCALL_STR, i, fds[0], PATH_TRACING_FD, 365 10 + (i - FD_SETSIZE), PATH_TRACING_FD, 366 zero_extend_signed_to_ull(tv->tv_usec)); 367 #endif /* PATH_TRACING_FD */ 368 } 369 370 /* 371 * Huge sets, nfds equals to INT_MAX. 372 */ 373 FD_SET(fds[0], set); 374 FD_SET(fds[1], set); 375 tv->tv_sec = 0; 376 tv->tv_usec = 123; 377 XSELECT(0, INT_MAX, (uintptr_t) set, (uintptr_t) &set[1], 378 (uintptr_t) &set[2], a_tv); 379 #ifndef PATH_TRACING_FD 380 printf("%s(%d, [%d %d], [], [], {tv_sec=0, tv_usec=123})" 381 " = 0 (Timeout)\n", 382 TEST_SYSCALL_STR, INT_MAX, fds[0], fds[1]); 383 #else 384 FD_SET(fds[0], set); 385 FD_SET(fds[1], set); 386 FD_SET(PATH_TRACING_FD, set); 387 tv->tv_sec = 0; 388 tv->tv_usec = 123; 389 XSELECT(1, INT_MAX, (uintptr_t) set, (uintptr_t) &set[1], 390 (uintptr_t) &set[2], a_tv); 391 printf("%s(%d, [%d %d %d], [], [], {tv_sec=0, tv_usec=123})" 392 " = 1 (in [%d], left {tv_sec=0, tv_usec=%llu})\n", 393 TEST_SYSCALL_STR, INT_MAX, fds[0], fds[1], PATH_TRACING_FD, 394 PATH_TRACING_FD, zero_extend_signed_to_ull(tv->tv_usec)); 395 #endif /* PATH_TRACING_FD */ 396 397 /* 398 * Small sets, nfds exceeds FD_SETSIZE limit. 399 * The kernel seems to be fine with it but strace cannot follow. 400 */ 401 *l_rs = (1UL << fds[0]) | (1UL << fds[1]) 402 #ifdef PATH_TRACING_FD 403 | (1UL << PATH_TRACING_FD) 404 #endif 405 ; 406 *l_ws = (1UL << fds[0]); 407 *l_es = (1UL << fds[0]) | (1UL << fds[1]) 408 #ifdef PATH_TRACING_FD 409 | (1UL << PATH_TRACING_FD) 410 #endif 411 ; 412 tv->tv_sec = 0; 413 tv->tv_usec = 123; 414 rc = xselect(FD_SETSIZE + 1, a_rs, a_ws, a_es, a_tv); 415 if (rc < 0) { 416 #ifndef PATH_TRACING_FD 417 printf("%s(%d, %p, %p, %p, {tv_sec=0, tv_usec=123}) = %s\n", 418 TEST_SYSCALL_STR, FD_SETSIZE + 1, rs, ws, es, errstr); 419 #endif 420 } else { 421 #ifndef PATH_TRACING_FD 422 printf("%s(%d, %p, %p, %p, {tv_sec=0, tv_usec=123})" 423 " = 0 (Timeout)\n", 424 TEST_SYSCALL_STR, FD_SETSIZE + 1, rs, ws, es); 425 #endif 426 } 427 428 /* 429 * Small sets, one of allocated descriptors exceeds smallset_size. 430 */ 431 if (dup2(fds[1], smallset_size) != smallset_size) 432 perror_msg_and_fail("dup2"); 433 #ifdef PATH_TRACING_FD 434 FD_SET(PATH_TRACING_FD, rs); 435 FD_SET(PATH_TRACING_FD, ws); 436 FD_SET(PATH_TRACING_FD, es); 437 #endif 438 XSELECT(-1, smallset_size + 1, a_rs, a_ws, a_es, 0); 439 #ifndef PATH_TRACING_FD 440 printf("%s(%d, %p, %p, %p, NULL) = %s\n", 441 TEST_SYSCALL_STR, smallset_size + 1, rs, ws, es, errstr); 442 #endif 443 444 /* 445 * Small and big sets, 446 * one of allocated descriptors exceeds smallset_size. 447 */ 448 memset(big_rs, 0, big_size); 449 FD_SET(fds[0], big_rs); 450 FD_SET(smallset_size, big_rs); 451 memset(big_ws, 0, big_size); 452 FD_SET(fds[1], big_ws); 453 FD_SET(smallset_size, big_ws); 454 XSELECT(-1, smallset_size + 1, a_big_rs, a_big_ws, a_es, 0); 455 #ifndef PATH_TRACING_FD 456 printf("%s(%d, [%d %d], [%d %d], %p, NULL) = %s\n", 457 TEST_SYSCALL_STR, smallset_size + 1, 458 fds[0], smallset_size, 459 fds[1], smallset_size, 460 es, errstr); 461 #endif /* !PATH_TRACING_FD */ 462 XSELECT(-1, smallset_size + 1, a_es, a_big_ws, a_big_rs, 0); 463 #ifndef PATH_TRACING_FD 464 printf("%s(%d, %p, [%d %d], [%d %d], NULL) = %s\n", 465 TEST_SYSCALL_STR, smallset_size + 1, 466 es, 467 fds[1], smallset_size, 468 fds[0], smallset_size, 469 errstr); 470 #endif /* !PATH_TRACING_FD */ 471 472 puts("+++ exited with 0 +++"); 473 return 0; 474 } 475