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 		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 
sys_getcpu(unsigned * cpu,unsigned * node,void * cache)144 static long sys_getcpu(unsigned * cpu, unsigned * node,
145 		       void* cache)
146 {
147 	return syscall(__NR_getcpu, cpu, node, cache);
148 }
149 
sys_clock_gettime(clockid_t id,struct timespec * ts)150 static inline int sys_clock_gettime(clockid_t id, struct timespec *ts)
151 {
152 	return syscall(__NR_clock_gettime, id, ts);
153 }
154 
sys_clock_gettime64(clockid_t id,struct __kernel_timespec * ts)155 static inline int sys_clock_gettime64(clockid_t id, struct __kernel_timespec *ts)
156 {
157 	return syscall(__NR_clock_gettime64, id, ts);
158 }
159 
sys_gettimeofday(struct timeval * tv,struct timezone * tz)160 static inline int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
161 {
162 	return syscall(__NR_gettimeofday, tv, tz);
163 }
164 
test_getcpu(void)165 static 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_sys, &node_sys, 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 
ts_leq(const struct timespec * a,const struct timespec * b)217 static 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 
ts64_leq(const struct __kernel_timespec * a,const struct __kernel_timespec * b)225 static 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 
tv_leq(const struct timeval * a,const struct timeval * b)234 static 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 
242 static 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 
test_one_clock_gettime(int clock,const char * name)257 static 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 
test_clock_gettime(void)303 static 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, clocknames[clock]);
312 
313 	/* Also test some invalid clock ids */
314 	test_one_clock_gettime(-1, "invalid");
315 	test_one_clock_gettime(INT_MIN, "invalid");
316 	test_one_clock_gettime(INT_MAX, "invalid");
317 }
318 
test_one_clock_gettime64(int clock,const char * name)319 static 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 
test_clock_gettime64(void)365 static 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, clocknames[clock]);
374 
375 	/* Also test some invalid clock ids */
376 	test_one_clock_gettime64(-1, "invalid");
377 	test_one_clock_gettime64(INT_MIN, "invalid");
378 	test_one_clock_gettime64(INT_MAX, "invalid");
379 }
380 
test_gettimeofday(void)381 static 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(&start, &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 
main(int argc,char ** argv)431 int 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