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