xref: /linux/tools/testing/selftests/vDSO/vdso_test_correctness.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
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 
27 static const char *version;
28 static const char **name;
29 
30 #ifndef __NR_clock_gettime64
31 #define __NR_clock_gettime64	403
32 #endif
33 
34 #ifndef __kernel_timespec
35 struct __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 
44 int nerrs = 0;
45 
46 typedef int (*vgettime_t)(clockid_t, struct timespec *);
47 
48 vgettime_t vdso_clock_gettime;
49 
50 typedef int (*vgettime64_t)(clockid_t, struct __kernel_timespec *);
51 
52 vgettime64_t vdso_clock_gettime64;
53 
54 typedef long (*vgtod_t)(struct timeval *tv, struct timezone *tz);
55 
56 vgtod_t vdso_gettimeofday;
57 
58 typedef time_t (*vtime_t)(__kernel_time_t *tloc);
59 
60 vtime_t vdso_time;
61 
62 typedef long (*getcpu_t)(unsigned *, unsigned *, void *);
63 
64 getcpu_t vgetcpu;
65 getcpu_t vdso_getcpu;
66 
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 
109 static 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 
146 static long sys_getcpu(unsigned * cpu, unsigned * node,
147 		       void* cache)
148 {
149 	return syscall(__NR_getcpu, cpu, node, cache);
150 }
151 
152 static inline int sys_clock_gettime(clockid_t id, struct timespec *ts)
153 {
154 	return syscall(__NR_clock_gettime, id, ts);
155 }
156 
157 static inline int sys_clock_gettime64(clockid_t id, struct __kernel_timespec *ts)
158 {
159 	return syscall(__NR_clock_gettime64, id, ts);
160 }
161 
162 static inline int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
163 {
164 	return syscall(__NR_gettimeofday, tv, tz);
165 }
166 
167 static 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 
177 static 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 
229 static 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 
237 static 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 
246 static 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 
254 static 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 
269 static 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 
315 static 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 
331 static 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 
377 static 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 
393 static 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 
443 static 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 
490 int 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 }
510