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