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