DF-5842 / timeout_rig.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | /* * DF-5842 rig: select(2)/pselect(2)/ppoll(2) accept invalid (negative / * unnormalized) timeout values and feed them unvalidated into * kern_kevent()->tstohz_high()/precise_sleep(). * * sys/kern/sys_generic.c: * sys_select() : copyin timeval, TIMEVAL_TO_TIMESPEC, no range check * sys_pselect() : copyin timespec, no range check * sys_ppoll() : copyin timespec, no range check * (contrast: the nd==0/nfds==0 path goes to nanosleep1() -> * clock_nanosleep1() which DOES reject tv_sec<0 / tv_nsec<0 / * tv_nsec>=1e9 -> EINVAL; kern_time.c:467) * * Demonstrated misbehavior: * T1: pselect with tv_sec = -1 -> immediate rv=0 (POSIX: EINVAL) * T2: ppoll with tv_nsec = 2^62 -> immediate rv=0 (POSIX: EINVAL; * intended sleep ~146 years collapses; int overflow in the PRECISE * ustimeout math at kern_event.c:975 or tick wrap in tstohz_high) * T3: select with tv_usec = -5 -> immediate rv=0 * T4: control ppoll {2,0} on idle pipe -> blocks ~2s (proves rig works) * T5: control ppoll {0,500000000} -> blocks ~0.5s * T6: select(0 fds, {tv_sec=-1}) -> dotimeout_only -> nanosleep1 * -> EINVAL (proves the nd==0 path validates, nd>0 does not) */ #include <sys/types.h> #include <sys/select.h> #include <sys/time.h> #include <poll.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <signal.h> static void dummy(int sig __unused) { } static double now(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (ts.tv_sec + ts.tv_nsec / 1e9); } int main(void) { struct pollfd pfd[1]; struct timespec ts; struct timeval tv; fd_set rfds; int p[2], rc; double t0, dt; if (pipe(p) < 0) { perror("pipe"); return (1); } signal(SIGALRM, dummy); alarm(30); /* bound any test that fails to collapse */ FD_ZERO(&rfds); /* T1: pselect tv_sec=-1 -> expect immediate 0 instead of EINVAL */ FD_SET(p[0], &rfds); ts.tv_sec = -1; ts.tv_nsec = 0; t0 = now(); rc = pselect(p[0] + 1, &rfds, NULL, NULL, &ts, NULL); dt = now() - t0; printf("T1 pselect{ -1s } rc=%d errno=%d (%s) elapsed=%.6f\n", rc, errno, errno ? strerror(errno) : "-", dt); /* T2: ppoll tv_nsec = 2^62 -> expect immediate 0 (timer collapse) */ pfd[0].fd = p[0]; pfd[0].events = POLLIN; ts.tv_sec = 0; ts.tv_nsec = 4611686018427387904LL; /* 2^62 */ t0 = now(); rc = ppoll(pfd, 1, &ts, NULL); dt = now() - t0; printf("T2 ppoll{ 0s, 2^62ns } rc=%d errno=%d (%s) elapsed=%.6f " "revents=%04x\n", rc, errno, errno ? strerror(errno) : "-", dt, pfd[0].revents); /* T2b: ppoll tv_sec = -1 -> expect immediate 0 */ ts.tv_sec = -1; ts.tv_nsec = 0; t0 = now(); rc = ppoll(pfd, 1, &ts, NULL); dt = now() - t0; printf("T2b ppoll{ -1s } rc=%d errno=%d elapsed=%.6f\n", rc, errno, dt); /* T3: select tv_usec = -5 -> expect immediate 0 */ FD_SET(p[0], &rfds); tv.tv_sec = 0; tv.tv_usec = -5; t0 = now(); rc = select(p[0] + 1, &rfds, NULL, NULL, &tv); dt = now() - t0; printf("T3 select{ 0s, -5us } rc=%d errno=%d (%s) elapsed=%.6f\n", rc, errno, errno ? strerror(errno) : "-", dt); /* T4: control ppoll {2,0} -> ~2s */ ts.tv_sec = 2; ts.tv_nsec = 0; t0 = now(); rc = ppoll(pfd, 1, &ts, NULL); dt = now() - t0; printf("T4 control ppoll{ 2s } rc=%d elapsed=%.6f\n", rc, dt); /* T5: control ppoll {0,500ms} -> ~0.5s */ ts.tv_sec = 0; ts.tv_nsec = 500000000; t0 = now(); rc = ppoll(pfd, 1, &ts, NULL); dt = now() - t0; printf("T5 control ppoll{ .5s } rc=%d elapsed=%.6f\n", rc, dt); /* T6: select(nd=0, invalid ts) -> nanosleep1 path -> EINVAL expected */ ts.tv_sec = -1; ts.tv_nsec = 0; t0 = now(); rc = select(0, NULL, NULL, NULL, &tv); /* tv set below */ tv.tv_sec = -1; tv.tv_usec = 0; rc = select(0, NULL, NULL, NULL, &tv); dt = now() - t0; printf("T6 select(0){ -1s } rc=%d errno=%d (%s) elapsed=%.6f\n", rc, errno, errno ? strerror(errno) : "-", dt); /* * T7: unnormalized {0, 1500000us} = 1.5 s intended; POSIX requires * EINVAL (tv_usec >= 1e6). Compare against control T5 (0.5 s). */ FD_SET(p[0], &rfds); tv.tv_sec = 0; tv.tv_usec = 1500000; /* unnormalized 1.5 s */ t0 = now(); rc = select(p[0] + 1, &rfds, NULL, NULL, &tv); dt = now() - t0; printf("T7 select{ 0s, 1.5e6us } rc=%d errno=%d (%s) elapsed=%.6f\n", rc, errno, errno ? strerror(errno) : "-", dt); fflush(stdout); return (0); } |