unwind_vdso.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * unwind_vdso.c - tests unwind info for AT_SYSINFO in the vDSO
  4. * Copyright (c) 2014-2015 Andrew Lutomirski
  5. *
  6. * This tests __kernel_vsyscall's unwind info.
  7. */
  8. #define _GNU_SOURCE
  9. #include <features.h>
  10. #include <stdio.h>
  11. #include "helpers.h"
  12. #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16
  13. int main()
  14. {
  15. /* We need getauxval(). */
  16. printf("[SKIP]\tGLIBC before 2.16 cannot compile this test\n");
  17. return 0;
  18. }
  19. #else
  20. #include <sys/time.h>
  21. #include <stdlib.h>
  22. #include <syscall.h>
  23. #include <unistd.h>
  24. #include <string.h>
  25. #include <inttypes.h>
  26. #include <sys/mman.h>
  27. #include <signal.h>
  28. #include <sys/ucontext.h>
  29. #include <err.h>
  30. #include <stddef.h>
  31. #include <stdbool.h>
  32. #include <sys/ptrace.h>
  33. #include <sys/user.h>
  34. #include <link.h>
  35. #include <sys/auxv.h>
  36. #include <dlfcn.h>
  37. #include <unwind.h>
  38. static volatile sig_atomic_t nerrs;
  39. static unsigned long sysinfo;
  40. static bool got_sysinfo = false;
  41. static unsigned long return_address;
  42. struct unwind_state {
  43. unsigned long ip; /* trap source */
  44. int depth; /* -1 until we hit the trap source */
  45. };
  46. _Unwind_Reason_Code trace_fn(struct _Unwind_Context * ctx, void *opaque)
  47. {
  48. struct unwind_state *state = opaque;
  49. unsigned long ip = _Unwind_GetIP(ctx);
  50. if (state->depth == -1) {
  51. if (ip == state->ip)
  52. state->depth = 0;
  53. else
  54. return _URC_NO_REASON; /* Not there yet */
  55. }
  56. printf("\t 0x%lx\n", ip);
  57. if (ip == return_address) {
  58. /* Here we are. */
  59. unsigned long eax = _Unwind_GetGR(ctx, 0);
  60. unsigned long ecx = _Unwind_GetGR(ctx, 1);
  61. unsigned long edx = _Unwind_GetGR(ctx, 2);
  62. unsigned long ebx = _Unwind_GetGR(ctx, 3);
  63. unsigned long ebp = _Unwind_GetGR(ctx, 5);
  64. unsigned long esi = _Unwind_GetGR(ctx, 6);
  65. unsigned long edi = _Unwind_GetGR(ctx, 7);
  66. bool ok = (eax == SYS_getpid || eax == getpid()) &&
  67. ebx == 1 && ecx == 2 && edx == 3 &&
  68. esi == 4 && edi == 5 && ebp == 6;
  69. if (!ok)
  70. nerrs++;
  71. printf("[%s]\t NR = %ld, args = %ld, %ld, %ld, %ld, %ld, %ld\n",
  72. (ok ? "OK" : "FAIL"),
  73. eax, ebx, ecx, edx, esi, edi, ebp);
  74. return _URC_NORMAL_STOP;
  75. } else {
  76. state->depth++;
  77. return _URC_NO_REASON;
  78. }
  79. }
  80. static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
  81. {
  82. ucontext_t *ctx = (ucontext_t *)ctx_void;
  83. struct unwind_state state;
  84. unsigned long ip = ctx->uc_mcontext.gregs[REG_EIP];
  85. if (!got_sysinfo && ip == sysinfo) {
  86. got_sysinfo = true;
  87. /* Find the return address. */
  88. return_address = *(unsigned long *)(unsigned long)ctx->uc_mcontext.gregs[REG_ESP];
  89. printf("\tIn vsyscall at 0x%lx, returning to 0x%lx\n",
  90. ip, return_address);
  91. }
  92. if (!got_sysinfo)
  93. return; /* Not there yet */
  94. if (ip == return_address) {
  95. ctx->uc_mcontext.gregs[REG_EFL] &= ~X86_EFLAGS_TF;
  96. printf("\tVsyscall is done\n");
  97. return;
  98. }
  99. printf("\tSIGTRAP at 0x%lx\n", ip);
  100. state.ip = ip;
  101. state.depth = -1;
  102. _Unwind_Backtrace(trace_fn, &state);
  103. }
  104. int main()
  105. {
  106. sysinfo = getauxval(AT_SYSINFO);
  107. printf("\tAT_SYSINFO is 0x%lx\n", sysinfo);
  108. Dl_info info;
  109. if (!dladdr((void *)sysinfo, &info)) {
  110. printf("[WARN]\tdladdr failed on AT_SYSINFO\n");
  111. } else {
  112. printf("[OK]\tAT_SYSINFO maps to %s, loaded at 0x%p\n",
  113. info.dli_fname, info.dli_fbase);
  114. }
  115. sethandler(SIGTRAP, sigtrap, 0);
  116. syscall(SYS_getpid); /* Force symbol binding without TF set. */
  117. printf("[RUN]\tSet TF and check a fast syscall\n");
  118. set_eflags(get_eflags() | X86_EFLAGS_TF);
  119. syscall(SYS_getpid, 1, 2, 3, 4, 5, 6);
  120. if (!got_sysinfo) {
  121. set_eflags(get_eflags() & ~X86_EFLAGS_TF);
  122. /*
  123. * The most likely cause of this is that you're on Debian or
  124. * a Debian-based distro, you're missing libc6-i686, and you're
  125. * affected by libc/19006 (https://sourceware.org/PR19006).
  126. */
  127. printf("[WARN]\tsyscall(2) didn't enter AT_SYSINFO\n");
  128. }
  129. if (get_eflags() & X86_EFLAGS_TF) {
  130. printf("[FAIL]\tTF is still set\n");
  131. nerrs++;
  132. }
  133. if (nerrs) {
  134. printf("[FAIL]\tThere were errors\n");
  135. return 1;
  136. } else {
  137. printf("[OK]\tAll is well\n");
  138. return 0;
  139. }
  140. }
  141. #endif /* New enough libc */