test_vdso.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  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. #define _GNU_SOURCE
  7. #include <stdio.h>
  8. #include <sys/time.h>
  9. #include <time.h>
  10. #include <stdlib.h>
  11. #include <unistd.h>
  12. #include <sys/syscall.h>
  13. #include <dlfcn.h>
  14. #include <string.h>
  15. #include <errno.h>
  16. #include <sched.h>
  17. #include <stdbool.h>
  18. #include <limits.h>
  19. #ifndef SYS_getcpu
  20. # ifdef __x86_64__
  21. # define SYS_getcpu 309
  22. # else
  23. # define SYS_getcpu 318
  24. # endif
  25. #endif
  26. /* max length of lines in /proc/self/maps - anything longer is skipped here */
  27. #define MAPS_LINE_LEN 128
  28. int nerrs = 0;
  29. typedef int (*vgettime_t)(clockid_t, struct timespec *);
  30. vgettime_t vdso_clock_gettime;
  31. typedef long (*vgtod_t)(struct timeval *tv, struct timezone *tz);
  32. vgtod_t vdso_gettimeofday;
  33. typedef long (*getcpu_t)(unsigned *, unsigned *, void *);
  34. getcpu_t vgetcpu;
  35. getcpu_t vdso_getcpu;
  36. static void *vsyscall_getcpu(void)
  37. {
  38. #ifdef __x86_64__
  39. FILE *maps;
  40. char line[MAPS_LINE_LEN];
  41. bool found = false;
  42. maps = fopen("/proc/self/maps", "r");
  43. if (!maps) /* might still be present, but ignore it here, as we test vDSO not vsyscall */
  44. return NULL;
  45. while (fgets(line, MAPS_LINE_LEN, maps)) {
  46. char r, x;
  47. void *start, *end;
  48. char name[MAPS_LINE_LEN];
  49. /* sscanf() is safe here as strlen(name) >= strlen(line) */
  50. if (sscanf(line, "%p-%p %c-%cp %*x %*x:%*x %*u %s",
  51. &start, &end, &r, &x, name) != 5)
  52. continue;
  53. if (strcmp(name, "[vsyscall]"))
  54. continue;
  55. /* assume entries are OK, as we test vDSO here not vsyscall */
  56. found = true;
  57. break;
  58. }
  59. fclose(maps);
  60. if (!found) {
  61. printf("Warning: failed to find vsyscall getcpu\n");
  62. return NULL;
  63. }
  64. return (void *) (0xffffffffff600800);
  65. #else
  66. return NULL;
  67. #endif
  68. }
  69. static void fill_function_pointers()
  70. {
  71. void *vdso = dlopen("linux-vdso.so.1",
  72. RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
  73. if (!vdso)
  74. vdso = dlopen("linux-gate.so.1",
  75. RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
  76. if (!vdso) {
  77. printf("[WARN]\tfailed to find vDSO\n");
  78. return;
  79. }
  80. vdso_getcpu = (getcpu_t)dlsym(vdso, "__vdso_getcpu");
  81. if (!vdso_getcpu)
  82. printf("Warning: failed to find getcpu in vDSO\n");
  83. vgetcpu = (getcpu_t) vsyscall_getcpu();
  84. vdso_clock_gettime = (vgettime_t)dlsym(vdso, "__vdso_clock_gettime");
  85. if (!vdso_clock_gettime)
  86. printf("Warning: failed to find clock_gettime in vDSO\n");
  87. vdso_gettimeofday = (vgtod_t)dlsym(vdso, "__vdso_gettimeofday");
  88. if (!vdso_gettimeofday)
  89. printf("Warning: failed to find gettimeofday in vDSO\n");
  90. }
  91. static long sys_getcpu(unsigned * cpu, unsigned * node,
  92. void* cache)
  93. {
  94. return syscall(__NR_getcpu, cpu, node, cache);
  95. }
  96. static inline int sys_clock_gettime(clockid_t id, struct timespec *ts)
  97. {
  98. return syscall(__NR_clock_gettime, id, ts);
  99. }
  100. static inline int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
  101. {
  102. return syscall(__NR_gettimeofday, tv, tz);
  103. }
  104. static void test_getcpu(void)
  105. {
  106. printf("[RUN]\tTesting getcpu...\n");
  107. for (int cpu = 0; ; cpu++) {
  108. cpu_set_t cpuset;
  109. CPU_ZERO(&cpuset);
  110. CPU_SET(cpu, &cpuset);
  111. if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
  112. return;
  113. unsigned cpu_sys, cpu_vdso, cpu_vsys,
  114. node_sys, node_vdso, node_vsys;
  115. long ret_sys, ret_vdso = 1, ret_vsys = 1;
  116. unsigned node;
  117. ret_sys = sys_getcpu(&cpu_sys, &node_sys, 0);
  118. if (vdso_getcpu)
  119. ret_vdso = vdso_getcpu(&cpu_vdso, &node_vdso, 0);
  120. if (vgetcpu)
  121. ret_vsys = vgetcpu(&cpu_vsys, &node_vsys, 0);
  122. if (!ret_sys)
  123. node = node_sys;
  124. else if (!ret_vdso)
  125. node = node_vdso;
  126. else if (!ret_vsys)
  127. node = node_vsys;
  128. bool ok = true;
  129. if (!ret_sys && (cpu_sys != cpu || node_sys != node))
  130. ok = false;
  131. if (!ret_vdso && (cpu_vdso != cpu || node_vdso != node))
  132. ok = false;
  133. if (!ret_vsys && (cpu_vsys != cpu || node_vsys != node))
  134. ok = false;
  135. printf("[%s]\tCPU %u:", ok ? "OK" : "FAIL", cpu);
  136. if (!ret_sys)
  137. printf(" syscall: cpu %u, node %u", cpu_sys, node_sys);
  138. if (!ret_vdso)
  139. printf(" vdso: cpu %u, node %u", cpu_vdso, node_vdso);
  140. if (!ret_vsys)
  141. printf(" vsyscall: cpu %u, node %u", cpu_vsys,
  142. node_vsys);
  143. printf("\n");
  144. if (!ok)
  145. nerrs++;
  146. }
  147. }
  148. static bool ts_leq(const struct timespec *a, const struct timespec *b)
  149. {
  150. if (a->tv_sec != b->tv_sec)
  151. return a->tv_sec < b->tv_sec;
  152. else
  153. return a->tv_nsec <= b->tv_nsec;
  154. }
  155. static bool tv_leq(const struct timeval *a, const struct timeval *b)
  156. {
  157. if (a->tv_sec != b->tv_sec)
  158. return a->tv_sec < b->tv_sec;
  159. else
  160. return a->tv_usec <= b->tv_usec;
  161. }
  162. static char const * const clocknames[] = {
  163. [0] = "CLOCK_REALTIME",
  164. [1] = "CLOCK_MONOTONIC",
  165. [2] = "CLOCK_PROCESS_CPUTIME_ID",
  166. [3] = "CLOCK_THREAD_CPUTIME_ID",
  167. [4] = "CLOCK_MONOTONIC_RAW",
  168. [5] = "CLOCK_REALTIME_COARSE",
  169. [6] = "CLOCK_MONOTONIC_COARSE",
  170. [7] = "CLOCK_BOOTTIME",
  171. [8] = "CLOCK_REALTIME_ALARM",
  172. [9] = "CLOCK_BOOTTIME_ALARM",
  173. [10] = "CLOCK_SGI_CYCLE",
  174. [11] = "CLOCK_TAI",
  175. };
  176. static void test_one_clock_gettime(int clock, const char *name)
  177. {
  178. struct timespec start, vdso, end;
  179. int vdso_ret, end_ret;
  180. printf("[RUN]\tTesting clock_gettime for clock %s (%d)...\n", name, clock);
  181. if (sys_clock_gettime(clock, &start) < 0) {
  182. if (errno == EINVAL) {
  183. vdso_ret = vdso_clock_gettime(clock, &vdso);
  184. if (vdso_ret == -EINVAL) {
  185. printf("[OK]\tNo such clock.\n");
  186. } else {
  187. printf("[FAIL]\tNo such clock, but __vdso_clock_gettime returned %d\n", vdso_ret);
  188. nerrs++;
  189. }
  190. } else {
  191. printf("[WARN]\t clock_gettime(%d) syscall returned error %d\n", clock, errno);
  192. }
  193. return;
  194. }
  195. vdso_ret = vdso_clock_gettime(clock, &vdso);
  196. end_ret = sys_clock_gettime(clock, &end);
  197. if (vdso_ret != 0 || end_ret != 0) {
  198. printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
  199. vdso_ret, errno);
  200. nerrs++;
  201. return;
  202. }
  203. printf("\t%llu.%09ld %llu.%09ld %llu.%09ld\n",
  204. (unsigned long long)start.tv_sec, start.tv_nsec,
  205. (unsigned long long)vdso.tv_sec, vdso.tv_nsec,
  206. (unsigned long long)end.tv_sec, end.tv_nsec);
  207. if (!ts_leq(&start, &vdso) || !ts_leq(&vdso, &end)) {
  208. printf("[FAIL]\tTimes are out of sequence\n");
  209. nerrs++;
  210. }
  211. }
  212. static void test_clock_gettime(void)
  213. {
  214. for (int clock = 0; clock < sizeof(clocknames) / sizeof(clocknames[0]);
  215. clock++) {
  216. test_one_clock_gettime(clock, clocknames[clock]);
  217. }
  218. /* Also test some invalid clock ids */
  219. test_one_clock_gettime(-1, "invalid");
  220. test_one_clock_gettime(INT_MIN, "invalid");
  221. test_one_clock_gettime(INT_MAX, "invalid");
  222. }
  223. static void test_gettimeofday(void)
  224. {
  225. struct timeval start, vdso, end;
  226. struct timezone sys_tz, vdso_tz;
  227. int vdso_ret, end_ret;
  228. if (!vdso_gettimeofday)
  229. return;
  230. printf("[RUN]\tTesting gettimeofday...\n");
  231. if (sys_gettimeofday(&start, &sys_tz) < 0) {
  232. printf("[FAIL]\tsys_gettimeofday failed (%d)\n", errno);
  233. nerrs++;
  234. return;
  235. }
  236. vdso_ret = vdso_gettimeofday(&vdso, &vdso_tz);
  237. end_ret = sys_gettimeofday(&end, NULL);
  238. if (vdso_ret != 0 || end_ret != 0) {
  239. printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
  240. vdso_ret, errno);
  241. nerrs++;
  242. return;
  243. }
  244. printf("\t%llu.%06ld %llu.%06ld %llu.%06ld\n",
  245. (unsigned long long)start.tv_sec, start.tv_usec,
  246. (unsigned long long)vdso.tv_sec, vdso.tv_usec,
  247. (unsigned long long)end.tv_sec, end.tv_usec);
  248. if (!tv_leq(&start, &vdso) || !tv_leq(&vdso, &end)) {
  249. printf("[FAIL]\tTimes are out of sequence\n");
  250. nerrs++;
  251. }
  252. if (sys_tz.tz_minuteswest == vdso_tz.tz_minuteswest &&
  253. sys_tz.tz_dsttime == vdso_tz.tz_dsttime) {
  254. printf("[OK]\ttimezones match: minuteswest=%d, dsttime=%d\n",
  255. sys_tz.tz_minuteswest, sys_tz.tz_dsttime);
  256. } else {
  257. printf("[FAIL]\ttimezones do not match\n");
  258. nerrs++;
  259. }
  260. /* And make sure that passing NULL for tz doesn't crash. */
  261. vdso_gettimeofday(&vdso, NULL);
  262. }
  263. int main(int argc, char **argv)
  264. {
  265. fill_function_pointers();
  266. test_clock_gettime();
  267. test_gettimeofday();
  268. /*
  269. * Test getcpu() last so that, if something goes wrong setting affinity,
  270. * we still run the other tests.
  271. */
  272. test_getcpu();
  273. return nerrs ? 1 : 0;
  274. }