The Pedigree Project 0.1
clock-adjust-contract-test/main.c
1#define _GNU_SOURCE
2#include <errno.h>
3#include <inttypes.h>
4#include <limits.h>
5#include <signal.h>
6#include <stddef.h>
7#include <stdint.h>
8#include <stdio.h>
9#include <string.h>
10#include <time.h>
11#include <unistd.h>
12
13#include <sys/syscall.h>
14#include <sys/timerfd.h>
15#include <sys/timex.h>
16#include <sys/wait.h>
17
18_Static_assert(sizeof(struct timex) == 208, "Linux amd64 timex ABI");
19_Static_assert(offsetof(struct timex, time) == 72, "Linux amd64 timex time ABI");
20_Static_assert(offsetof(struct timex, tai) == 160, "Linux amd64 timex TAI ABI");
21
22#define CHECK(expression) \
23 do { \
24 if (!(expression)) { \
25 fprintf(stderr, "CLOCK-ADJUST-CONTRACT: line=%d errno=%d %s\n", __LINE__, errno, \
26 #expression); \
27 failed = 1; \
28 goto out; \
29 } \
30 } while (0)
31
32static int64_t now(clockid_t clock) {
33 struct timespec value;
34 return clock_gettime(clock, &value) ? -1 : (int64_t)value.tv_sec * 1000000000 + value.tv_nsec;
35}
36
37static struct timespec timespec(int64_t value) {
38 return (struct timespec){value / 1000000000, value % 1000000000};
39}
40
41static int state(const struct timex* value) {
42 const long units = value->status & STA_NANO ? 1000000000 : 1000000;
43 return (value->status & STA_UNSYNC) && !(value->status & (STA_PLL | STA_PPSFREQ | STA_PPSTIME)) &&
44 value->time.tv_sec > 0 && value->time.tv_usec >= 0 && value->time.tv_usec < units &&
45 value->offset == 0 && value->freq == 0 && value->ppsfreq == 0 && value->maxerror > 0 &&
46 value->esterror > 0;
47}
48
49static int query(void) {
50 int failed = 0;
51 struct timex value;
52 memset(&value, 0xA5, sizeof(value));
53 value.modes = 0;
54 const int64_t before = now(CLOCK_REALTIME);
55 CHECK(adjtimex(&value) == TIME_ERROR && state(&value));
56 const int64_t timestamp = (int64_t)value.time.tv_sec * 1000000000 +
57 value.time.tv_usec * ((value.status & STA_NANO) ? 1 : 1000);
58 CHECK(timestamp >= before - 100000000 && timestamp <= now(CLOCK_REALTIME) + 100000000);
59 for (unsigned n = 0; n < sizeof(value.__padding) / sizeof(value.__padding[0]); ++n)
60 CHECK(value.__padding[n] == 0);
61 value = (struct timex){0};
62 CHECK(syscall(SYS_clock_adjtime, CLOCK_REALTIME, &value) == TIME_ERROR && state(&value));
63 value = (struct timex){.modes = ADJ_OFFSET_SS_READ};
64 CHECK(syscall(SYS_adjtimex, &value) == TIME_ERROR && state(&value) && value.offset == 0);
65 value = (struct timex){0};
66 CHECK(clock_adjtime(CLOCK_MONOTONIC, &value) == -1 && errno == EOPNOTSUPP);
67 CHECK(syscall(SYS_clock_adjtime, 123456, &value) == -1 && errno == EINVAL);
68 CHECK(syscall(SYS_adjtimex, NULL) == -1 && errno == EFAULT);
69 CHECK(syscall(SYS_clock_adjtime, CLOCK_REALTIME, NULL) == -1 && errno == EFAULT);
70out:
71 return failed;
72}
73
74static int step(int64_t seconds, int64_t fraction, unsigned modes, int route) {
75 struct timex value = {.modes = ADJ_SETOFFSET | modes,
76 .time = {.tv_sec = seconds, .tv_usec = fraction}};
77 return route ? syscall(SYS_clock_adjtime, CLOCK_REALTIME, &value) : syscall(SYS_adjtimex, &value);
78}
79
80static int steps(void) {
81 int failed = 0, canceled = -1, absolute = -1, monotonic = -1;
82 struct timex value = {.modes = ADJ_NANO};
83 struct itimerspec setting = {0}, remaining;
84 uint64_t count;
85 CHECK(geteuid() == 0);
86 CHECK(adjtimex(&value) == TIME_ERROR && (value.status & STA_NANO));
87 value = (struct timex){.modes = ADJ_OFFSET_SS_READ};
88 CHECK(adjtimex(&value) == TIME_ERROR && (value.status & STA_NANO));
89 value = (struct timex){0};
90 CHECK(adjtimex(&value) == TIME_ERROR && (value.status & STA_NANO));
91
92 CHECK((canceled = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK)) >= 0);
93 CHECK((absolute = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK)) >= 0);
94 CHECK((monotonic = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK)) >= 0);
95 setting.it_value = timespec(now(CLOCK_REALTIME) + 5000000000);
96 CHECK(timerfd_settime(canceled, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &setting, NULL) ==
97 0);
98 CHECK(timerfd_settime(absolute, TFD_TIMER_ABSTIME, &setting, NULL) == 0);
99 setting.it_value = timespec(now(CLOCK_MONOTONIC) + 5000000000);
100 CHECK(timerfd_settime(monotonic, TFD_TIMER_ABSTIME, &setting, NULL) == 0);
101 int64_t before = now(CLOCK_REALTIME) - now(CLOCK_MONOTONIC);
102 CHECK(step(10, 250000000, ADJ_NANO, 1) == TIME_ERROR);
103 int64_t after = now(CLOCK_REALTIME) - now(CLOCK_MONOTONIC);
104 CHECK(after - before > 10150000000 && after - before < 10350000000);
105 CHECK(read(canceled, &count, sizeof(count)) == -1 && errno == ECANCELED);
106 CHECK(read(absolute, &count, sizeof(count)) == sizeof(count) && count == 1);
107 CHECK(read(monotonic, &count, sizeof(count)) == -1 && errno == EAGAIN);
108 CHECK(timerfd_gettime(monotonic, &remaining) == 0 && remaining.it_value.tv_sec >= 3);
109
110 // SETOFFSET input remains microseconds unless this request includes ADJ_NANO.
111 before = after;
112 CHECK(step(-2, 750000, 0, 0) == TIME_ERROR);
113 after = now(CLOCK_REALTIME) - now(CLOCK_MONOTONIC);
114 CHECK(after - before > -1350000000 && after - before < -1150000000);
115 value = (struct timex){0};
116 CHECK(adjtimex(&value) == TIME_ERROR && (value.status & STA_NANO));
117
118 value = (struct timex){.modes = ADJ_MICRO};
119 CHECK(adjtimex(&value) == TIME_ERROR && !(value.status & STA_NANO));
120 CHECK(step(0, -1, ADJ_NANO, 0) == -1 && errno == EINVAL);
121 CHECK(step(0, 1000000, 0, 0) == -1 && errno == EINVAL);
122 CHECK(step(0, 1000000000, ADJ_NANO, 1) == -1 && errno == EINVAL);
123 CHECK(step(INT64_MAX, 0, ADJ_NANO, 0) == -1 && errno == EINVAL);
124 CHECK(step(INT64_MIN, 0, ADJ_NANO, 1) == -1 && errno == EINVAL);
125 CHECK(step(-now(CLOCK_REALTIME) / 1000000000 - 60, 0, ADJ_NANO, 0) == -1 && errno == EINVAL);
126 value = (struct timex){0};
127 CHECK(adjtimex(&value) == TIME_ERROR && !(value.status & STA_NANO));
128 const unsigned unsupported[] = {ADJ_FREQUENCY, ADJ_OFFSET, ADJ_OFFSET_SINGLESHOT,
129 ADJ_STATUS, ADJ_TICK, ADJ_TAI,
130 ADJ_MAXERROR, ADJ_ESTERROR, ADJ_TIMECONST};
131 for (unsigned n = 0; n < sizeof(unsupported) / sizeof(unsupported[0]); ++n) {
132 value = (struct timex){.modes = unsupported[n], .freq = 100, .status = STA_PLL};
133 CHECK(adjtimex(&value) == -1 && errno == EOPNOTSUPP);
134 }
135 value = (struct timex){.modes = 0x40000000};
136 CHECK(adjtimex(&value) == -1 && errno == EINVAL);
137out:
138 if (canceled >= 0)
139 close(canceled);
140 if (absolute >= 0)
141 close(absolute);
142 if (monotonic >= 0)
143 close(monotonic);
144 return failed;
145}
146
147static int privilege(void) {
148 int failed = 0;
149 CHECK(setuid(65534) == 0 && geteuid() == 65534);
150 CHECK(query() == 0);
151 CHECK(step(0, 0, 0, 0) == -1 && errno == EPERM);
152 const unsigned modes[] = {ADJ_NANO, ADJ_MICRO, ADJ_OFFSET_SINGLESHOT, ADJ_FREQUENCY};
153 for (unsigned n = 0; n < sizeof(modes) / sizeof(modes[0]); ++n) {
154 struct timex value = {.modes = modes[n]};
155 CHECK(adjtimex(&value) == -1 && errno == EPERM);
156 }
157out:
158 return failed;
159}
160
161static int reap(pid_t child) {
162 const int64_t deadline = now(CLOCK_MONOTONIC) + 30000000000;
163 while (now(CLOCK_MONOTONIC) < deadline) {
164 int status;
165 pid_t result = waitpid(child, &status, WNOHANG);
166 if (result == child)
167 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
168 if (result < 0 && errno != EINTR)
169 return -1;
170 struct timespec pause = {0, 5000000};
171 nanosleep(&pause, NULL);
172 }
173 kill(child, SIGKILL);
174 while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
175 }
176 return -1;
177}
178
179int main(void) {
180 if (geteuid()) {
181 puts("CLOCK-ADJUST-CONTRACT: FAIL requires root");
182 return 1;
183 }
184 const struct {
185 const char* name;
186 int (*test)(void);
187 } suites[] = {{"query", query}, {"steps", steps}, {"privilege", privilege}};
188 for (unsigned n = 0; n < sizeof(suites) / sizeof(suites[0]); ++n) {
189 struct timex original = {0};
190 if (adjtimex(&original) < 0)
191 return 1;
192 int64_t realtime = now(CLOCK_REALTIME), monotonic = now(CLOCK_MONOTONIC);
193 printf("CLOCK-ADJUST-CONTRACT: BEGIN %s\n", suites[n].name);
194 fflush(stdout);
195 pid_t child = fork();
196 if (child < 0)
197 return 1;
198 if (!child) {
199 alarm(25);
200 int result = suites[n].test();
201 fflush(stdout);
202 fflush(stderr);
203 _exit(result ? 1 : 0);
204 }
205 int result = reap(child);
206 if (!strcmp(suites[n].name, "steps")) {
207 struct timespec restored = timespec(realtime + now(CLOCK_MONOTONIC) - monotonic);
208 struct timex mode = {.modes = original.status & STA_NANO ? ADJ_NANO : ADJ_MICRO};
209 if (clock_settime(CLOCK_REALTIME, &restored) || adjtimex(&mode) < 0)
210 result = -1;
211 }
212 printf("CLOCK-ADJUST-CONTRACT: %s %s status=%d\n", result ? "FAIL" : "PASS", suites[n].name,
213 result);
214 fflush(stdout);
215 if (result)
216 return 1;
217 }
218 puts("CLOCK-ADJUST-CONTRACT: END PASS");
219 return 0;
220}