Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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/auxv.h>
15#include <sys/syscall.h>
16#include <string.h>
17#include <errno.h>
18#include <sched.h>
19#include <stdbool.h>
20#include <limits.h>
21
22#include "parse_vdso.h"
23#include "vdso_config.h"
24#include "vdso_call.h"
25#include "kselftest.h"
26
27static const char *version;
28static const char **name;
29
30#ifndef __NR_clock_gettime64
31#define __NR_clock_gettime64 403
32#endif
33
34#ifndef __kernel_timespec
35struct __kernel_timespec {
36 long long tv_sec;
37 long long tv_nsec;
38};
39#endif
40
41/* max length of lines in /proc/self/maps - anything longer is skipped here */
42#define MAPS_LINE_LEN 128
43
44int nerrs = 0;
45
46typedef int (*vgettime_t)(clockid_t, struct timespec *);
47
48vgettime_t vdso_clock_gettime;
49
50typedef int (*vgettime64_t)(clockid_t, struct __kernel_timespec *);
51
52vgettime64_t vdso_clock_gettime64;
53
54typedef long (*vgtod_t)(struct timeval *tv, struct timezone *tz);
55
56vgtod_t vdso_gettimeofday;
57
58typedef time_t (*vtime_t)(__kernel_time_t *tloc);
59
60vtime_t vdso_time;
61
62typedef long (*getcpu_t)(unsigned *, unsigned *, void *);
63
64getcpu_t vgetcpu;
65getcpu_t vdso_getcpu;
66
67static 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
109static void fill_function_pointers(void)
110{
111 unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
112
113 if (!sysinfo_ehdr) {
114 printf("[WARN]\tfailed to find vDSO\n");
115 return;
116 }
117
118 vdso_init_from_sysinfo_ehdr(sysinfo_ehdr);
119
120 vdso_getcpu = (getcpu_t)vdso_sym(version, name[4]);
121 if (!vdso_getcpu)
122 printf("Warning: failed to find getcpu in vDSO\n");
123
124 vgetcpu = (getcpu_t) vsyscall_getcpu();
125
126 vdso_clock_gettime = (vgettime_t)vdso_sym(version, name[1]);
127 if (!vdso_clock_gettime)
128 printf("Warning: failed to find clock_gettime in vDSO\n");
129
130#if defined(VDSO_32BIT)
131 vdso_clock_gettime64 = (vgettime64_t)vdso_sym(version, name[5]);
132 if (!vdso_clock_gettime64)
133 printf("Warning: failed to find clock_gettime64 in vDSO\n");
134#endif
135
136 vdso_gettimeofday = (vgtod_t)vdso_sym(version, name[0]);
137 if (!vdso_gettimeofday)
138 printf("Warning: failed to find gettimeofday in vDSO\n");
139
140 vdso_time = (vtime_t)vdso_sym(version, name[2]);
141 if (!vdso_time)
142 printf("Warning: failed to find time in vDSO\n");
143
144}
145
146static long sys_getcpu(unsigned * cpu, unsigned * node,
147 void* cache)
148{
149 return syscall(__NR_getcpu, cpu, node, cache);
150}
151
152static inline int sys_clock_gettime(clockid_t id, struct timespec *ts)
153{
154 return syscall(__NR_clock_gettime, id, ts);
155}
156
157static inline int sys_clock_gettime64(clockid_t id, struct __kernel_timespec *ts)
158{
159 return syscall(__NR_clock_gettime64, id, ts);
160}
161
162static inline int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
163{
164 return syscall(__NR_gettimeofday, tv, tz);
165}
166
167static inline __kernel_old_time_t sys_time(__kernel_old_time_t *tloc)
168{
169#ifdef __NR_time
170 return syscall(__NR_time, tloc);
171#else
172 errno = ENOSYS;
173 return -1;
174#endif
175}
176
177static void test_getcpu(void)
178{
179 printf("[RUN]\tTesting getcpu...\n");
180
181 for (int cpu = 0; ; cpu++) {
182 cpu_set_t cpuset;
183 CPU_ZERO(&cpuset);
184 CPU_SET(cpu, &cpuset);
185 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
186 return;
187
188 unsigned cpu_sys, cpu_vdso, cpu_vsys,
189 node_sys, node_vdso, node_vsys;
190 long ret_sys, ret_vdso = 1, ret_vsys = 1;
191 unsigned node;
192
193 ret_sys = sys_getcpu(&cpu_sys, &node_sys, 0);
194 if (vdso_getcpu)
195 ret_vdso = VDSO_CALL(vdso_getcpu, 3, &cpu_vdso, &node_vdso, 0);
196 if (vgetcpu)
197 ret_vsys = vgetcpu(&cpu_vsys, &node_vsys, 0);
198
199 if (!ret_sys)
200 node = node_sys;
201 else if (!ret_vdso)
202 node = node_vdso;
203 else if (!ret_vsys)
204 node = node_vsys;
205
206 bool ok = true;
207 if (!ret_sys && (cpu_sys != cpu || node_sys != node))
208 ok = false;
209 if (!ret_vdso && (cpu_vdso != cpu || node_vdso != node))
210 ok = false;
211 if (!ret_vsys && (cpu_vsys != cpu || node_vsys != node))
212 ok = false;
213
214 printf("[%s]\tCPU %u:", ok ? "OK" : "FAIL", cpu);
215 if (!ret_sys)
216 printf(" syscall: cpu %u, node %u", cpu_sys, node_sys);
217 if (!ret_vdso)
218 printf(" vdso: cpu %u, node %u", cpu_vdso, node_vdso);
219 if (!ret_vsys)
220 printf(" vsyscall: cpu %u, node %u", cpu_vsys,
221 node_vsys);
222 printf("\n");
223
224 if (!ok)
225 nerrs++;
226 }
227}
228
229static bool ts_leq(const struct timespec *a, const struct timespec *b)
230{
231 if (a->tv_sec != b->tv_sec)
232 return a->tv_sec < b->tv_sec;
233 else
234 return a->tv_nsec <= b->tv_nsec;
235}
236
237static bool ts64_leq(const struct __kernel_timespec *a,
238 const struct __kernel_timespec *b)
239{
240 if (a->tv_sec != b->tv_sec)
241 return a->tv_sec < b->tv_sec;
242 else
243 return a->tv_nsec <= b->tv_nsec;
244}
245
246static bool tv_leq(const struct timeval *a, const struct timeval *b)
247{
248 if (a->tv_sec != b->tv_sec)
249 return a->tv_sec < b->tv_sec;
250 else
251 return a->tv_usec <= b->tv_usec;
252}
253
254static char const * const clocknames[] = {
255 [0] = "CLOCK_REALTIME",
256 [1] = "CLOCK_MONOTONIC",
257 [2] = "CLOCK_PROCESS_CPUTIME_ID",
258 [3] = "CLOCK_THREAD_CPUTIME_ID",
259 [4] = "CLOCK_MONOTONIC_RAW",
260 [5] = "CLOCK_REALTIME_COARSE",
261 [6] = "CLOCK_MONOTONIC_COARSE",
262 [7] = "CLOCK_BOOTTIME",
263 [8] = "CLOCK_REALTIME_ALARM",
264 [9] = "CLOCK_BOOTTIME_ALARM",
265 [10] = "CLOCK_SGI_CYCLE",
266 [11] = "CLOCK_TAI",
267};
268
269static void test_one_clock_gettime(int clock, const char *name)
270{
271 struct timespec start, vdso, end;
272 int vdso_ret, end_ret;
273
274 printf("[RUN]\tTesting clock_gettime for clock %s (%d)...\n", name, clock);
275
276 if (sys_clock_gettime(clock, &start) < 0) {
277 if (errno == EINVAL) {
278 vdso_ret = VDSO_CALL(vdso_clock_gettime, 2, clock, &vdso);
279 if (vdso_ret == -EINVAL) {
280 printf("[OK]\tNo such clock.\n");
281 } else {
282 printf("[FAIL]\tNo such clock, but __vdso_clock_gettime returned %d\n", vdso_ret);
283 nerrs++;
284 }
285 } else {
286 printf("[WARN]\t clock_gettime(%d) syscall returned error %d\n", clock, errno);
287 }
288 return;
289 }
290
291 vdso_ret = VDSO_CALL(vdso_clock_gettime, 2, clock, &vdso);
292 end_ret = sys_clock_gettime(clock, &end);
293
294 if (vdso_ret != 0 || end_ret != 0) {
295 printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
296 vdso_ret, errno);
297 nerrs++;
298 return;
299 }
300
301 printf("\t%llu.%09ld %llu.%09ld %llu.%09ld\n",
302 (unsigned long long)start.tv_sec, start.tv_nsec,
303 (unsigned long long)vdso.tv_sec, vdso.tv_nsec,
304 (unsigned long long)end.tv_sec, end.tv_nsec);
305
306 if (!ts_leq(&start, &vdso) || !ts_leq(&vdso, &end)) {
307 printf("[FAIL]\tTimes are out of sequence\n");
308 nerrs++;
309 return;
310 }
311
312 printf("[OK]\tTest Passed.\n");
313}
314
315static void test_clock_gettime(void)
316{
317 if (!vdso_clock_gettime) {
318 printf("[SKIP]\tNo vDSO, so skipping clock_gettime() tests\n");
319 return;
320 }
321
322 for (int clock = 0; clock < ARRAY_SIZE(clocknames); clock++)
323 test_one_clock_gettime(clock, clocknames[clock]);
324
325 /* Also test some invalid clock ids */
326 test_one_clock_gettime(-1, "invalid");
327 test_one_clock_gettime(INT_MIN, "invalid");
328 test_one_clock_gettime(INT_MAX, "invalid");
329}
330
331static void test_one_clock_gettime64(int clock, const char *name)
332{
333 struct __kernel_timespec start, vdso, end;
334 int vdso_ret, end_ret;
335
336 printf("[RUN]\tTesting clock_gettime64 for clock %s (%d)...\n", name, clock);
337
338 if (sys_clock_gettime64(clock, &start) < 0) {
339 if (errno == EINVAL) {
340 vdso_ret = VDSO_CALL(vdso_clock_gettime64, 2, clock, &vdso);
341 if (vdso_ret == -EINVAL) {
342 printf("[OK]\tNo such clock.\n");
343 } else {
344 printf("[FAIL]\tNo such clock, but __vdso_clock_gettime64 returned %d\n", vdso_ret);
345 nerrs++;
346 }
347 } else {
348 printf("[WARN]\t clock_gettime64(%d) syscall returned error %d\n", clock, errno);
349 }
350 return;
351 }
352
353 vdso_ret = VDSO_CALL(vdso_clock_gettime64, 2, clock, &vdso);
354 end_ret = sys_clock_gettime64(clock, &end);
355
356 if (vdso_ret != 0 || end_ret != 0) {
357 printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
358 vdso_ret, errno);
359 nerrs++;
360 return;
361 }
362
363 printf("\t%llu.%09lld %llu.%09lld %llu.%09lld\n",
364 (unsigned long long)start.tv_sec, start.tv_nsec,
365 (unsigned long long)vdso.tv_sec, vdso.tv_nsec,
366 (unsigned long long)end.tv_sec, end.tv_nsec);
367
368 if (!ts64_leq(&start, &vdso) || !ts64_leq(&vdso, &end)) {
369 printf("[FAIL]\tTimes are out of sequence\n");
370 nerrs++;
371 return;
372 }
373
374 printf("[OK]\tTest Passed.\n");
375}
376
377static void test_clock_gettime64(void)
378{
379 if (!vdso_clock_gettime64) {
380 printf("[SKIP]\tNo vDSO, so skipping clock_gettime64() tests\n");
381 return;
382 }
383
384 for (int clock = 0; clock < ARRAY_SIZE(clocknames); clock++)
385 test_one_clock_gettime64(clock, clocknames[clock]);
386
387 /* Also test some invalid clock ids */
388 test_one_clock_gettime64(-1, "invalid");
389 test_one_clock_gettime64(INT_MIN, "invalid");
390 test_one_clock_gettime64(INT_MAX, "invalid");
391}
392
393static void test_gettimeofday(void)
394{
395 struct timeval start, vdso, end;
396 struct timezone sys_tz, vdso_tz;
397 int vdso_ret, end_ret;
398
399 if (!vdso_gettimeofday)
400 return;
401
402 printf("[RUN]\tTesting gettimeofday...\n");
403
404 if (sys_gettimeofday(&start, &sys_tz) < 0) {
405 printf("[FAIL]\tsys_gettimeofday failed (%d)\n", errno);
406 nerrs++;
407 return;
408 }
409
410 vdso_ret = VDSO_CALL(vdso_gettimeofday, 2, &vdso, &vdso_tz);
411 end_ret = sys_gettimeofday(&end, NULL);
412
413 if (vdso_ret != 0 || end_ret != 0) {
414 printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
415 vdso_ret, errno);
416 nerrs++;
417 return;
418 }
419
420 printf("\t%llu.%06lld %llu.%06lld %llu.%06lld\n",
421 (unsigned long long)start.tv_sec, (long long)start.tv_usec,
422 (unsigned long long)vdso.tv_sec, (long long)vdso.tv_usec,
423 (unsigned long long)end.tv_sec, (long long)end.tv_usec);
424
425 if (!tv_leq(&start, &vdso) || !tv_leq(&vdso, &end)) {
426 printf("[FAIL]\tTimes are out of sequence\n");
427 nerrs++;
428 }
429
430 if (sys_tz.tz_minuteswest == vdso_tz.tz_minuteswest &&
431 sys_tz.tz_dsttime == vdso_tz.tz_dsttime) {
432 printf("[OK]\ttimezones match: minuteswest=%d, dsttime=%d\n",
433 sys_tz.tz_minuteswest, sys_tz.tz_dsttime);
434 } else {
435 printf("[FAIL]\ttimezones do not match\n");
436 nerrs++;
437 }
438
439 /* And make sure that passing NULL for tz doesn't crash. */
440 VDSO_CALL(vdso_gettimeofday, 2, &vdso, NULL);
441}
442
443static void test_time(void)
444{
445 __kernel_old_time_t start, end, vdso_ret, vdso_param;
446
447 if (!vdso_time)
448 return;
449
450 printf("[RUN]\tTesting time...\n");
451
452 if (sys_time(&start) < 0) {
453 if (errno == -ENOSYS) {
454 printf("[SKIP]\tNo time() support\n");
455 } else {
456 printf("[FAIL]\tsys_time failed (%d)\n", errno);
457 nerrs++;
458 }
459 return;
460 }
461
462 vdso_ret = VDSO_CALL(vdso_time, 1, &vdso_param);
463 end = sys_time(NULL);
464
465 if (vdso_ret < 0 || end < 0) {
466 printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
467 (int)vdso_ret, errno);
468 nerrs++;
469 return;
470 }
471
472 printf("\t%lld %lld %lld\n",
473 (long long)start,
474 (long long)vdso_ret,
475 (long long)end);
476
477 if (vdso_ret != vdso_param) {
478 printf("[FAIL]\tinconsistent return values: %lld %lld\n",
479 (long long)vdso_ret, (long long)vdso_param);
480 nerrs++;
481 return;
482 }
483
484 if (!(start <= vdso_ret) || !(vdso_ret <= end)) {
485 printf("[FAIL]\tTimes are out of sequence\n");
486 nerrs++;
487 }
488}
489
490int main(int argc, char **argv)
491{
492 version = versions[VDSO_VERSION];
493 name = (const char **)&names[VDSO_NAMES];
494
495 fill_function_pointers();
496
497 test_clock_gettime();
498 test_clock_gettime64();
499 test_gettimeofday();
500 test_time();
501
502 /*
503 * Test getcpu() last so that, if something goes wrong setting affinity,
504 * we still run the other tests.
505 */
506 test_getcpu();
507
508 return nerrs ? 1 : 0;
509}