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