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