sigtrap_loop.c 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (C) 2025 Intel Corporation
  4. */
  5. #define _GNU_SOURCE
  6. #include <err.h>
  7. #include <signal.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <sys/ucontext.h>
  12. #ifdef __x86_64__
  13. # define REG_IP REG_RIP
  14. #else
  15. # define REG_IP REG_EIP
  16. #endif
  17. static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), int flags)
  18. {
  19. struct sigaction sa;
  20. memset(&sa, 0, sizeof(sa));
  21. sa.sa_sigaction = handler;
  22. sa.sa_flags = SA_SIGINFO | flags;
  23. sigemptyset(&sa.sa_mask);
  24. if (sigaction(sig, &sa, 0))
  25. err(1, "sigaction");
  26. return;
  27. }
  28. static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
  29. {
  30. ucontext_t *ctx = (ucontext_t *)ctx_void;
  31. static unsigned int loop_count_on_same_ip;
  32. static unsigned long last_trap_ip;
  33. if (last_trap_ip == ctx->uc_mcontext.gregs[REG_IP]) {
  34. printf("\tTrapped at %016lx\n", last_trap_ip);
  35. /*
  36. * If the same IP is hit more than 10 times in a row, it is
  37. * _considered_ an infinite loop.
  38. */
  39. if (++loop_count_on_same_ip > 10) {
  40. printf("[FAIL]\tDetected SIGTRAP infinite loop\n");
  41. exit(1);
  42. }
  43. return;
  44. }
  45. loop_count_on_same_ip = 0;
  46. last_trap_ip = ctx->uc_mcontext.gregs[REG_IP];
  47. printf("\tTrapped at %016lx\n", last_trap_ip);
  48. }
  49. int main(int argc, char *argv[])
  50. {
  51. sethandler(SIGTRAP, sigtrap, 0);
  52. /*
  53. * Set the Trap Flag (TF) to single-step the test code, therefore to
  54. * trigger a SIGTRAP signal after each instruction until the TF is
  55. * cleared.
  56. *
  57. * Because the arithmetic flags are not significant here, the TF is
  58. * set by pushing 0x302 onto the stack and then popping it into the
  59. * flags register.
  60. *
  61. * Four instructions in the following asm code are executed with the
  62. * TF set, thus the SIGTRAP handler is expected to run four times.
  63. */
  64. printf("[RUN]\tSIGTRAP infinite loop detection\n");
  65. asm volatile(
  66. #ifdef __x86_64__
  67. /*
  68. * Avoid clobbering the redzone
  69. *
  70. * Equivalent to "sub $128, %rsp", however -128 can be encoded
  71. * in a single byte immediate while 128 uses 4 bytes.
  72. */
  73. "add $-128, %rsp\n\t"
  74. #endif
  75. "push $0x302\n\t"
  76. "popf\n\t"
  77. "nop\n\t"
  78. "nop\n\t"
  79. "push $0x202\n\t"
  80. "popf\n\t"
  81. #ifdef __x86_64__
  82. "sub $-128, %rsp\n\t"
  83. #endif
  84. );
  85. printf("[OK]\tNo SIGTRAP infinite loop detected\n");
  86. return 0;
  87. }