The Pedigree Project 0.1
timerfd.c
1#define _GNU_SOURCE
2#include <fcntl.h>
3#include <pthread.h>
4#include <signal.h>
5#include <stdlib.h>
6#include <string.h>
7#include <unistd.h>
8
9#include "contract.h"
10#include <sys/epoll.h>
11#include <sys/ioctl.h>
12#include <sys/mman.h>
13#include <sys/socket.h>
14#include <sys/stat.h>
15#include <sys/syscall.h>
16#include <sys/timerfd.h>
17#include <sys/uio.h>
18
19static int arm(int fd, int milliseconds, int interval) {
20 struct itimerspec setting = {.it_value = ed_timespec((int64_t)milliseconds * 1000000),
21 .it_interval = ed_timespec((int64_t)interval * 1000000)};
22 return timerfd_settime(fd, 0, &setting, NULL);
23}
24static int64_t remaining(const struct itimerspec* setting) {
25 return (int64_t)setting->it_value.tv_sec * 1000000000 + setting->it_value.tv_nsec;
26}
27
28static int counter_and_settings(void) {
29 int failed = 0, fd = -1, pipefd[2] = {-1, -1};
30 long page = sysconf(_SC_PAGESIZE);
31 void* bad = mmap(NULL, page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
32 uint64_t counts[2] = {0};
33 struct stat info;
34 char path[64], link[64];
35 struct itimerspec setting, replacement = {.it_value = {0, 1000000}}, old;
36 CHECK(bad != MAP_FAILED);
37 CHECK((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC)) >= 0);
38 CHECK((fcntl(fd, F_GETFL) & O_NONBLOCK) && (fcntl(fd, F_GETFD) & FD_CLOEXEC));
39 CHECK(fstat(fd, &info) == 0 && info.st_ino != 0 && info.st_nlink == 1 &&
40 (info.st_mode & 0777) == 0600 && info.st_size == 0);
41 CHECK(lseek(fd, 0, SEEK_SET) == 0 && lseek(fd, 123, SEEK_SET) == 0);
42 CHECK(lseek(fd, 0, 123) == -1 && errno == EINVAL);
43 snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
44 CHECK(readlink(path, link, sizeof(link)) == (ssize_t)strlen("anon_inode:[timerfd]") &&
45 !memcmp(link, "anon_inode:[timerfd]", strlen("anon_inode:[timerfd]")));
46 CHECK(write(fd, counts, 8) == -1 && errno == EINVAL);
47 CHECK(timerfd_create(-1, 0) == -1 && errno == EINVAL);
48 CHECK(timerfd_create(CLOCK_MONOTONIC, 0x40000000) == -1 && errno == EINVAL);
49 CHECK(timerfd_create(CLOCK_PROCESS_CPUTIME_ID, 0) == -1 && errno == EINVAL);
50 CHECK(timerfd_gettime(fd, &setting) == 0 && remaining(&setting) == 0);
51 CHECK(read(fd, counts, 0) == -1 && errno == EINVAL);
52 CHECK(read(fd, counts, 7) == -1 && errno == EINVAL);
53 CHECK(read(fd, counts, sizeof(counts)) == -1 && errno == EAGAIN);
54 CHECK(timerfd_gettime(fd, bad) == -1 && errno == EFAULT);
55 CHECK(syscall(SYS_timerfd_settime, fd, 0, NULL, NULL) == -1 && errno == EFAULT);
56 CHECK(timerfd_settime(fd, 0, bad, NULL) == -1 && errno == EFAULT);
57 CHECK(timerfd_settime(fd, 4, &replacement, NULL) == -1 && errno == EINVAL);
58 replacement.it_value.tv_nsec = 1000000000;
59 CHECK(timerfd_settime(fd, 0, &replacement, NULL) == -1 && errno == EINVAL);
60 replacement.it_value.tv_nsec = 1000000;
61 replacement.it_interval.tv_sec = -1;
62 CHECK(timerfd_settime(fd, 0, &replacement, NULL) == -1 && errno == EINVAL);
63 replacement.it_interval.tv_sec = 0;
64 CHECK(pipe(pipefd) == 0);
65 CHECK(timerfd_gettime(pipefd[0], &setting) == -1 && errno == EINVAL);
66 CHECK(timerfd_gettime(-1, &setting) == -1 && errno == EBADF);
67 CHECK(arm(fd, 3000, 77) == 0);
68 CHECK(timerfd_settime(fd, 0, &replacement, bad) == -1 && errno == EFAULT);
69 CHECK(timerfd_gettime(fd, &setting) == 0 && remaining(&setting) > 1000000000 &&
70 setting.it_interval.tv_nsec == 77000000);
71 replacement.it_value = ed_timespec(30000000);
72 CHECK(timerfd_settime(fd, 0, &replacement, &old) == 0 && remaining(&old) > 1000000000 &&
73 old.it_interval.tv_nsec == 77000000);
74 CHECK(ed_readable(fd, 2000) == 1);
75 CHECK(read(fd, bad, 8) == -1 && errno == EFAULT);
76 CHECK(ed_readable(fd, 0) == 1);
77 struct iovec faulty_vectors[2] = {{counts, 3}, {bad, 5}};
78 CHECK(readv(fd, faulty_vectors, 2) == -1 && errno == EFAULT);
79 CHECK(ed_readable(fd, 0) == 1);
80 counts[1] = UINT64_C(0xa5a5a5a5a5a5a5a5);
81 CHECK(read(fd, counts, sizeof(counts)) == 8 && counts[0] == 1 &&
82 counts[1] == UINT64_C(0xa5a5a5a5a5a5a5a5));
83 CHECK(timerfd_gettime(fd, &setting) == 0 && remaining(&setting) == 0);
84 CHECK(read(fd, counts, 8) == -1 && errno == EAGAIN);
85 CHECK(arm(fd, 20, 20) == 0);
86 ed_pause(140);
87 struct iovec vectors[2] = {{counts, 3}, {(unsigned char*)counts + 3, 5}};
88 CHECK(readv(fd, vectors, 2) == 8 && counts[0] >= 3);
89 CHECK(timerfd_gettime(fd, &setting) == 0 && remaining(&setting) > 0 &&
90 setting.it_interval.tv_nsec == 20000000);
91 CHECK(arm(fd, 0, 77) == 0);
92 CHECK(timerfd_gettime(fd, &setting) == 0 && remaining(&setting) == 0 &&
93 setting.it_interval.tv_sec == 0 && setting.it_interval.tv_nsec == 77000000);
94 CHECK(read(fd, counts, 8) == -1 && errno == EAGAIN);
95 CHECK(arm(fd, 10, 0) == 0 && ed_readable(fd, 2000) == 1);
96 CHECK(arm(fd, 3000, 0) == 0);
97 CHECK(read(fd, counts, 8) == -1 && errno == EAGAIN);
98 replacement.it_value = ed_timespec(ed_now(CLOCK_MONOTONIC) + 30000000);
99 CHECK(timerfd_settime(fd, TFD_TIMER_ABSTIME, &replacement, NULL) == 0);
100 CHECK(ed_readable(fd, 2000) == 1 && read(fd, counts, 8) == 8 && counts[0] == 1);
101out:
102 if (fd >= 0)
103 close(fd);
104 for (int n = 0; n < 2; ++n)
105 if (pipefd[n] >= 0)
106 close(pipefd[n]);
107 if (bad != MAP_FAILED)
108 munmap(bad, page);
109 return failed;
110}
111
112static int readiness(void) {
113 int failed = 0, fd = -1, epoll = -1;
114 uint64_t count;
115 struct epoll_event interest = {.events = EPOLLIN | EPOLLOUT, .data.u64 = 0x5678}, ready;
116 CHECK((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK)) >= 0);
117 CHECK((epoll = epoll_create1(EPOLL_CLOEXEC)) >= 0);
118 CHECK(epoll_ctl(epoll, EPOLL_CTL_ADD, fd, &interest) == 0);
119 CHECK(ed_readable(fd, 0) == 0 && epoll_wait(epoll, &ready, 1, 0) == 0);
120 CHECK(arm(fd, 30, 0) == 0);
121 CHECK(epoll_wait(epoll, &ready, 1, 2000) == 1 && ready.data.u64 == 0x5678 &&
122 ready.events == EPOLLIN);
123 CHECK(ed_readable(fd, 0) == 1 && epoll_wait(epoll, &ready, 1, 0) == 1);
124 CHECK(read(fd, &count, 8) == 8 && count == 1);
125 CHECK(ed_readable(fd, 0) == 0 && epoll_wait(epoll, &ready, 1, 0) == 0);
126 interest.events = EPOLLIN | EPOLLET;
127 CHECK(epoll_ctl(epoll, EPOLL_CTL_MOD, fd, &interest) == 0);
128 CHECK(arm(fd, 20, 20) == 0);
129 CHECK(epoll_wait(epoll, &ready, 1, 2000) == 1);
130 ed_pause(80);
131 CHECK(ed_readable(fd, 0) == 1 && epoll_wait(epoll, &ready, 1, 0) == 0);
132 CHECK(read(fd, &count, 8) == 8 && count >= 3);
133 CHECK(epoll_wait(epoll, &ready, 1, 2000) == 1);
134 CHECK(read(fd, &count, 8) == 8 && count >= 1);
135 CHECK(arm(fd, 0, 0) == 0);
136 CHECK(ed_readable(fd, 0) == 0 && epoll_wait(epoll, &ready, 1, 0) == 0);
137out:
138 if (epoll >= 0)
139 close(epoll);
140 if (fd >= 0)
141 close(fd);
142 return failed;
143}
144
145static int shared_lifetime(void) {
146 int failed = 0, fd = -1, alias = -1, sockets[2] = {-1, -1};
147 pid_t child = -1;
148 uint64_t count;
149 struct itimerspec setting;
150 struct stat info, shared_info;
151 char byte;
152 CHECK((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC)) >= 0);
153 CHECK((alias = dup(fd)) >= 0 && !(fcntl(alias, F_GETFD) & FD_CLOEXEC));
154 CHECK(fstat(fd, &info) == 0 && fstat(alias, &shared_info) == 0 &&
155 info.st_ino == shared_info.st_ino);
156 int nonblock = 0;
157 CHECK(ioctl(alias, FIONBIO, &nonblock) == 0 && !(fcntl(fd, F_GETFL) & O_NONBLOCK));
158 CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == 0 && (fcntl(alias, F_GETFL) & O_NONBLOCK));
159 CHECK((child = fork()) >= 0);
160 if (!child) {
161 close(fd);
162 if (arm(alias, 30, 0))
163 _exit(10);
164 close(alias);
165 _exit(0);
166 }
167 int status = ed_reap(child, 2000);
168 child = -1;
169 CHECK(status == 0 && ed_readable(fd, 2000) == 1);
170 CHECK(read(alias, &count, 8) == 8 && count == 1);
171 CHECK(read(fd, &count, 8) == -1 && errno == EAGAIN);
172 CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == 0);
173 CHECK((child = fork()) >= 0);
174 if (!child) {
175 alarm(5);
176 close(sockets[0]);
177 close(fd);
178 close(alias);
179 int received = ed_receive_fd(sockets[1]);
180 if (received < 0 || !(fcntl(received, F_GETFD) & FD_CLOEXEC) || arm(received, 30, 70) ||
181 send(sockets[1], "r", 1, 0) != 1 || recv(sockets[1], &byte, 1, 0) != 1 ||
182 ed_readable(received, 2000) != 1 || read(received, &count, 8) != 8 || count < 1)
183 _exit(11);
184 close(received);
185 close(sockets[1]);
186 _exit(0);
187 }
188 CHECK(ed_send_fd(sockets[0], fd) == 0 && recv(sockets[0], &byte, 1, 0) == 1 && byte == 'r');
189 CHECK(timerfd_gettime(alias, &setting) == 0 && setting.it_interval.tv_nsec == 70000000);
190 CHECK(close(fd) == 0);
191 fd = -1;
192 CHECK(close(alias) == 0);
193 alias = -1;
194 CHECK(send(sockets[0], "g", 1, 0) == 1);
195 status = ed_reap(child, 3000);
196 child = -1;
197 CHECK(status == 0);
198out:
199 if (child > 0) {
200 kill(child, SIGKILL);
201 ed_reap(child, 1000);
202 }
203 if (alias >= 0)
204 close(alias);
205 if (fd >= 0)
206 close(fd);
207 for (int n = 0; n < 2; ++n)
208 if (sockets[n] >= 0)
209 close(sockets[n]);
210 return failed;
211}
212
213int event_descriptor_timerfd_exec(int argc, char** argv) {
214 if (argc != 4)
215 return 2;
216 int keep = atoi(argv[2]), drop = atoi(argv[3]);
217 uint64_t count;
218 struct itimerspec setting;
219 if (fcntl(drop, F_GETFD) != -1 || errno != EBADF || fcntl(keep, F_GETFD) < 0 ||
220 timerfd_gettime(keep, &setting) || ed_readable(keep, 2000) != 1 ||
221 read(keep, &count, sizeof(count)) != 8 || count != 1)
222 return 1;
223 close(keep);
224 return 0;
225}
226static int closing_and_exec(void) {
227 int failed = 0, fd = -1, alias = -1, active = 0;
228 pid_t child = -1;
229 pthread_t thread;
230 struct ed_reader reader = {.size = 8};
231 CHECK((fd = timerfd_create(CLOCK_MONOTONIC, 0)) >= 0 && (alias = dup(fd)) >= 0);
232 reader.fd = fd;
233 CHECK(pthread_create(&thread, NULL, ed_read_thread, &reader) == 0);
234 active = 1;
235 CHECK(ed_wait(&reader.started, 1000) == 0);
236 ed_pause(30);
237 CHECK(!__atomic_load_n(&reader.done, __ATOMIC_ACQUIRE));
238 CHECK(close(fd) == 0);
239 fd = -1;
240 ed_pause(30);
241 CHECK(!__atomic_load_n(&reader.done, __ATOMIC_ACQUIRE));
242 CHECK(close(alias) == 0);
243 alias = -1;
244 CHECK(ed_wait(&reader.done, 2000) == 0 && pthread_join(thread, NULL) == 0);
245 active = 0;
246 CHECK(reader.result == -1 && reader.error == EBADF);
247 CHECK((child = fork()) >= 0);
248 if (!child) {
249 int keep = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
250 int drop = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
251 char kept[24], dropped[24];
252 if (keep < 0 || drop < 0 || arm(keep, 20, 0) || ed_readable(keep, 2000) != 1)
253 _exit(10);
254 snprintf(kept, sizeof(kept), "%d", keep);
255 snprintf(dropped, sizeof(dropped), "%d", drop);
256 execl(EVENT_DESCRIPTOR_APP, EVENT_DESCRIPTOR_APP, "timerfd-exec", kept, dropped, (char*)NULL);
257 _exit(11);
258 }
259 int status = ed_reap(child, 4000);
260 child = -1;
261 CHECK(status == 0);
262out:
263 if (fd >= 0)
264 close(fd);
265 if (alias >= 0)
266 close(alias);
267 if (active) {
268 pthread_cancel(thread);
269 pthread_join(thread, NULL);
270 }
271 if (child > 0) {
272 kill(child, SIGKILL);
273 ed_reap(child, 1000);
274 }
275 return failed;
276}
277
278int event_descriptor_test_timerfd(void) {
279 return counter_and_settings() || readiness() || shared_lifetime() || closing_and_exec();
280}
Definition waits.c:9