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

source code of linux/tools/testing/selftests/vDSO/vdso_test_correctness.c