1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * ldt_gdt.c - Test cases for LDT and GDT access
4 * Copyright (c) 2011-2015 Andrew Lutomirski
5 */
6
7 #define _GNU_SOURCE
8
9 #include <stdio.h>
10 #include <sys/time.h>
11 #include <time.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/syscall.h>
15 #include <dlfcn.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <sched.h>
19 #include <stdbool.h>
20 #include <limits.h>
21
22 #include "vdso_config.h"
23
24 static const char **name;
25
26 #ifndef SYS_getcpu
27 # ifdef __x86_64__
28 # define SYS_getcpu 309
29 # else
30 # define SYS_getcpu 318
31 # endif
32 #endif
33
34 #ifndef __NR_clock_gettime64
35 #define __NR_clock_gettime64 403
36 #endif
37
38 #ifndef __kernel_timespec
39 struct __kernel_timespec {
40 long long tv_sec;
41 long long tv_nsec;
42 };
43 #endif
44
45 /* max length of lines in /proc/self/maps - anything longer is skipped here */
46 #define MAPS_LINE_LEN 128
47
48 int nerrs = 0;
49
50 typedef int (*vgettime_t)(clockid_t, struct timespec *);
51
52 vgettime_t vdso_clock_gettime;
53
54 typedef int (*vgettime64_t)(clockid_t, struct __kernel_timespec *);
55
56 vgettime64_t vdso_clock_gettime64;
57
58 typedef long (*vgtod_t)(struct timeval *tv, struct timezone *tz);
59
60 vgtod_t vdso_gettimeofday;
61
62 typedef long (*getcpu_t)(unsigned *, unsigned *, void *);
63
64 getcpu_t vgetcpu;
65 getcpu_t vdso_getcpu;
66
vsyscall_getcpu(void)67 static void *vsyscall_getcpu(void)
68 {
69 #ifdef __x86_64__
70 FILE *maps;
71 char line[MAPS_LINE_LEN];
72 bool found = false;
73
74 maps = fopen("/proc/self/maps", "r");
75 if (!maps) /* might still be present, but ignore it here, as we test vDSO not vsyscall */
76 return NULL;
77
78 while (fgets(line, MAPS_LINE_LEN, maps)) {
79 char r, x;
80 void *start, *end;
81 char name[MAPS_LINE_LEN];
82
83 /* sscanf() is safe here as strlen(name) >= strlen(line) */
84 if (sscanf(line, "%p-%p %c-%cp %*x %*x:%*x %*u %s",
85 &start, &end, &r, &x, name) != 5)
86 continue;
87
88 if (strcmp(name, "[vsyscall]"))
89 continue;
90
91 /* assume entries are OK, as we test vDSO here not vsyscall */
92 found = true;
93 break;
94 }
95
96 fclose(maps);
97
98 if (!found) {
99 printf("Warning: failed to find vsyscall getcpu\n");
100 return NULL;
101 }
102 return (void *) (0xffffffffff600800);
103 #else
104 return NULL;
105 #endif
106 }
107
108
fill_function_pointers()109 static void fill_function_pointers()
110 {
111 void *vdso = dlopen("linux-vdso.so.1",
112 RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
113 if (!vdso)
114 vdso = dlopen("linux-gate.so.1",
115 RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
116 if (!vdso) {
117 printf("[WARN]\tfailed to find vDSO\n");
118 return;
119 }
120
121 vdso_getcpu = (getcpu_t)dlsym(vdso, name[4]);
122 if (!vdso_getcpu)
123 printf("Warning: failed to find getcpu in vDSO\n");
124
125 vgetcpu = (getcpu_t) vsyscall_getcpu();
126
127 vdso_clock_gettime = (vgettime_t)dlsym(vdso, name[1]);
128 if (!vdso_clock_gettime)
129 printf("Warning: failed to find clock_gettime in vDSO\n");
130
131 #if defined(VDSO_32BIT)
132 vdso_clock_gettime64 = (vgettime64_t)dlsym(vdso, name[5]);
133 if (!vdso_clock_gettime64)
134 printf("Warning: failed to find clock_gettime64 in vDSO\n");
135 #endif
136
137 vdso_gettimeofday = (vgtod_t)dlsym(vdso, name[0]);
138 if (!vdso_gettimeofday)
139 printf("Warning: failed to find gettimeofday in vDSO\n");
140
141 }
142
sys_getcpu(unsigned * cpu,unsigned * node,void * cache)143 static long sys_getcpu(unsigned * cpu, unsigned * node,
144 void* cache)
145 {
146 return syscall(__NR_getcpu, cpu, node, cache);
147 }
148
sys_clock_gettime(clockid_t id,struct timespec * ts)149 static inline int sys_clock_gettime(clockid_t id, struct timespec *ts)
150 {
151 return syscall(__NR_clock_gettime, id, ts);
152 }
153
sys_clock_gettime64(clockid_t id,struct __kernel_timespec * ts)154 static inline int sys_clock_gettime64(clockid_t id, struct __kernel_timespec *ts)
155 {
156 return syscall(__NR_clock_gettime64, id, ts);
157 }
158
sys_gettimeofday(struct timeval * tv,struct timezone * tz)159 static inline int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
160 {
161 return syscall(__NR_gettimeofday, tv, tz);
162 }
163
test_getcpu(void)164 static void test_getcpu(void)
165 {
166 printf("[RUN]\tTesting getcpu...\n");
167
168 for (int cpu = 0; ; cpu++) {
169 cpu_set_t cpuset;
170 CPU_ZERO(&cpuset);
171 CPU_SET(cpu, &cpuset);
172 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
173 return;
174
175 unsigned cpu_sys, cpu_vdso, cpu_vsys,
176 node_sys, node_vdso, node_vsys;
177 long ret_sys, ret_vdso = 1, ret_vsys = 1;
178 unsigned node;
179
180 ret_sys = sys_getcpu(&cpu_sys, &node_sys, 0);
181 if (vdso_getcpu)
182 ret_vdso = vdso_getcpu(&cpu_vdso, &node_vdso, 0);
183 if (vgetcpu)
184 ret_vsys = vgetcpu(&cpu_vsys, &node_vsys, 0);
185
186 if (!ret_sys)
187 node = node_sys;
188 else if (!ret_vdso)
189 node = node_vdso;
190 else if (!ret_vsys)
191 node = node_vsys;
192
193 bool ok = true;
194 if (!ret_sys && (cpu_sys != cpu || node_sys != node))
195 ok = false;
196 if (!ret_vdso && (cpu_vdso != cpu || node_vdso != node))
197 ok = false;
198 if (!ret_vsys && (cpu_vsys != cpu || node_vsys != node))
199 ok = false;
200
201 printf("[%s]\tCPU %u:", ok ? "OK" : "FAIL", cpu);
202 if (!ret_sys)
203 printf(" syscall: cpu %u, node %u", cpu_sys, node_sys);
204 if (!ret_vdso)
205 printf(" vdso: cpu %u, node %u", cpu_vdso, node_vdso);
206 if (!ret_vsys)
207 printf(" vsyscall: cpu %u, node %u", cpu_vsys,
208 node_vsys);
209 printf("\n");
210
211 if (!ok)
212 nerrs++;
213 }
214 }
215
ts_leq(const struct timespec * a,const struct timespec * b)216 static bool ts_leq(const struct timespec *a, const struct timespec *b)
217 {
218 if (a->tv_sec != b->tv_sec)
219 return a->tv_sec < b->tv_sec;
220 else
221 return a->tv_nsec <= b->tv_nsec;
222 }
223
ts64_leq(const struct __kernel_timespec * a,const struct __kernel_timespec * b)224 static bool ts64_leq(const struct __kernel_timespec *a,
225 const struct __kernel_timespec *b)
226 {
227 if (a->tv_sec != b->tv_sec)
228 return a->tv_sec < b->tv_sec;
229 else
230 return a->tv_nsec <= b->tv_nsec;
231 }
232
tv_leq(const struct timeval * a,const struct timeval * b)233 static bool tv_leq(const struct timeval *a, const struct timeval *b)
234 {
235 if (a->tv_sec != b->tv_sec)
236 return a->tv_sec < b->tv_sec;
237 else
238 return a->tv_usec <= b->tv_usec;
239 }
240
241 static char const * const clocknames[] = {
242 [0] = "CLOCK_REALTIME",
243 [1] = "CLOCK_MONOTONIC",
244 [2] = "CLOCK_PROCESS_CPUTIME_ID",
245 [3] = "CLOCK_THREAD_CPUTIME_ID",
246 [4] = "CLOCK_MONOTONIC_RAW",
247 [5] = "CLOCK_REALTIME_COARSE",
248 [6] = "CLOCK_MONOTONIC_COARSE",
249 [7] = "CLOCK_BOOTTIME",
250 [8] = "CLOCK_REALTIME_ALARM",
251 [9] = "CLOCK_BOOTTIME_ALARM",
252 [10] = "CLOCK_SGI_CYCLE",
253 [11] = "CLOCK_TAI",
254 };
255
test_one_clock_gettime(int clock,const char * name)256 static void test_one_clock_gettime(int clock, const char *name)
257 {
258 struct timespec start, vdso, end;
259 int vdso_ret, end_ret;
260
261 printf("[RUN]\tTesting clock_gettime for clock %s (%d)...\n", name, clock);
262
263 if (sys_clock_gettime(clock, &start) < 0) {
264 if (errno == EINVAL) {
265 vdso_ret = vdso_clock_gettime(clock, &vdso);
266 if (vdso_ret == -EINVAL) {
267 printf("[OK]\tNo such clock.\n");
268 } else {
269 printf("[FAIL]\tNo such clock, but __vdso_clock_gettime returned %d\n", vdso_ret);
270 nerrs++;
271 }
272 } else {
273 printf("[WARN]\t clock_gettime(%d) syscall returned error %d\n", clock, errno);
274 }
275 return;
276 }
277
278 vdso_ret = vdso_clock_gettime(clock, &vdso);
279 end_ret = sys_clock_gettime(clock, &end);
280
281 if (vdso_ret != 0 || end_ret != 0) {
282 printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
283 vdso_ret, errno);
284 nerrs++;
285 return;
286 }
287
288 printf("\t%llu.%09ld %llu.%09ld %llu.%09ld\n",
289 (unsigned long long)start.tv_sec, start.tv_nsec,
290 (unsigned long long)vdso.tv_sec, vdso.tv_nsec,
291 (unsigned long long)end.tv_sec, end.tv_nsec);
292
293 if (!ts_leq(&start, &vdso) || !ts_leq(&vdso, &end)) {
294 printf("[FAIL]\tTimes are out of sequence\n");
295 nerrs++;
296 return;
297 }
298
299 printf("[OK]\tTest Passed.\n");
300 }
301
test_clock_gettime(void)302 static void test_clock_gettime(void)
303 {
304 if (!vdso_clock_gettime) {
305 printf("[SKIP]\tNo vDSO, so skipping clock_gettime() tests\n");
306 return;
307 }
308
309 for (int clock = 0; clock < sizeof(clocknames) / sizeof(clocknames[0]);
310 clock++) {
311 test_one_clock_gettime(clock, clocknames[clock]);
312 }
313
314 /* Also test some invalid clock ids */
315 test_one_clock_gettime(-1, "invalid");
316 test_one_clock_gettime(INT_MIN, "invalid");
317 test_one_clock_gettime(INT_MAX, "invalid");
318 }
319
test_one_clock_gettime64(int clock,const char * name)320 static void test_one_clock_gettime64(int clock, const char *name)
321 {
322 struct __kernel_timespec start, vdso, end;
323 int vdso_ret, end_ret;
324
325 printf("[RUN]\tTesting clock_gettime64 for clock %s (%d)...\n", name, clock);
326
327 if (sys_clock_gettime64(clock, &start) < 0) {
328 if (errno == EINVAL) {
329 vdso_ret = vdso_clock_gettime64(clock, &vdso);
330 if (vdso_ret == -EINVAL) {
331 printf("[OK]\tNo such clock.\n");
332 } else {
333 printf("[FAIL]\tNo such clock, but __vdso_clock_gettime64 returned %d\n", vdso_ret);
334 nerrs++;
335 }
336 } else {
337 printf("[WARN]\t clock_gettime64(%d) syscall returned error %d\n", clock, errno);
338 }
339 return;
340 }
341
342 vdso_ret = vdso_clock_gettime64(clock, &vdso);
343 end_ret = sys_clock_gettime64(clock, &end);
344
345 if (vdso_ret != 0 || end_ret != 0) {
346 printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
347 vdso_ret, errno);
348 nerrs++;
349 return;
350 }
351
352 printf("\t%llu.%09lld %llu.%09lld %llu.%09lld\n",
353 (unsigned long long)start.tv_sec, start.tv_nsec,
354 (unsigned long long)vdso.tv_sec, vdso.tv_nsec,
355 (unsigned long long)end.tv_sec, end.tv_nsec);
356
357 if (!ts64_leq(&start, &vdso) || !ts64_leq(&vdso, &end)) {
358 printf("[FAIL]\tTimes are out of sequence\n");
359 nerrs++;
360 return;
361 }
362
363 printf("[OK]\tTest Passed.\n");
364 }
365
test_clock_gettime64(void)366 static void test_clock_gettime64(void)
367 {
368 if (!vdso_clock_gettime64) {
369 printf("[SKIP]\tNo vDSO, so skipping clock_gettime64() tests\n");
370 return;
371 }
372
373 for (int clock = 0; clock < sizeof(clocknames) / sizeof(clocknames[0]);
374 clock++) {
375 test_one_clock_gettime64(clock, clocknames[clock]);
376 }
377
378 /* Also test some invalid clock ids */
379 test_one_clock_gettime64(-1, "invalid");
380 test_one_clock_gettime64(INT_MIN, "invalid");
381 test_one_clock_gettime64(INT_MAX, "invalid");
382 }
383
test_gettimeofday(void)384 static void test_gettimeofday(void)
385 {
386 struct timeval start, vdso, end;
387 struct timezone sys_tz, vdso_tz;
388 int vdso_ret, end_ret;
389
390 if (!vdso_gettimeofday)
391 return;
392
393 printf("[RUN]\tTesting gettimeofday...\n");
394
395 if (sys_gettimeofday(&start, &sys_tz) < 0) {
396 printf("[FAIL]\tsys_gettimeofday failed (%d)\n", errno);
397 nerrs++;
398 return;
399 }
400
401 vdso_ret = vdso_gettimeofday(&vdso, &vdso_tz);
402 end_ret = sys_gettimeofday(&end, NULL);
403
404 if (vdso_ret != 0 || end_ret != 0) {
405 printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
406 vdso_ret, errno);
407 nerrs++;
408 return;
409 }
410
411 printf("\t%llu.%06ld %llu.%06ld %llu.%06ld\n",
412 (unsigned long long)start.tv_sec, start.tv_usec,
413 (unsigned long long)vdso.tv_sec, vdso.tv_usec,
414 (unsigned long long)end.tv_sec, end.tv_usec);
415
416 if (!tv_leq(&start, &vdso) || !tv_leq(&vdso, &end)) {
417 printf("[FAIL]\tTimes are out of sequence\n");
418 nerrs++;
419 }
420
421 if (sys_tz.tz_minuteswest == vdso_tz.tz_minuteswest &&
422 sys_tz.tz_dsttime == vdso_tz.tz_dsttime) {
423 printf("[OK]\ttimezones match: minuteswest=%d, dsttime=%d\n",
424 sys_tz.tz_minuteswest, sys_tz.tz_dsttime);
425 } else {
426 printf("[FAIL]\ttimezones do not match\n");
427 nerrs++;
428 }
429
430 /* And make sure that passing NULL for tz doesn't crash. */
431 vdso_gettimeofday(&vdso, NULL);
432 }
433
main(int argc,char ** argv)434 int main(int argc, char **argv)
435 {
436 name = (const char **)&names[VDSO_NAMES];
437
438 fill_function_pointers();
439
440 test_clock_gettime();
441 test_clock_gettime64();
442 test_gettimeofday();
443
444 /*
445 * Test getcpu() last so that, if something goes wrong setting affinity,
446 * we still run the other tests.
447 */
448 test_getcpu();
449
450 return nerrs ? 1 : 0;
451 }
452