bp-modify.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <linux/compiler.h>
  3. #include <sys/types.h>
  4. #include <sys/wait.h>
  5. #include <sys/user.h>
  6. #include <syscall.h>
  7. #include <unistd.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <sys/ptrace.h>
  11. #include <asm/ptrace.h>
  12. #include <errno.h>
  13. #include "debug.h"
  14. #include "tests/tests.h"
  15. #include "arch-tests.h"
  16. static noinline int bp_1(void)
  17. {
  18. pr_debug("in %s\n", __func__);
  19. return 0;
  20. }
  21. static noinline int bp_2(void)
  22. {
  23. pr_debug("in %s\n", __func__);
  24. return 0;
  25. }
  26. static int spawn_child(void)
  27. {
  28. int child = fork();
  29. if (child == 0) {
  30. /*
  31. * The child sets itself for as tracee and
  32. * waits in signal for parent to trace it,
  33. * then it calls bp_1 and quits.
  34. */
  35. int err = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
  36. if (err) {
  37. pr_debug("failed to PTRACE_TRACEME\n");
  38. exit(1);
  39. }
  40. raise(SIGCONT);
  41. bp_1();
  42. exit(0);
  43. }
  44. return child;
  45. }
  46. /*
  47. * This tests creates HW breakpoint, tries to
  48. * change it and checks it was properly changed.
  49. */
  50. static int bp_modify1(void)
  51. {
  52. pid_t child;
  53. int status;
  54. unsigned long rip = 0, dr7 = 1;
  55. child = spawn_child();
  56. waitpid(child, &status, 0);
  57. if (WIFEXITED(status)) {
  58. pr_debug("tracee exited prematurely 1\n");
  59. return TEST_FAIL;
  60. }
  61. /*
  62. * The parent does following steps:
  63. * - creates a new breakpoint (id 0) for bp_2 function
  64. * - changes that breakponit to bp_1 function
  65. * - waits for the breakpoint to hit and checks
  66. * it has proper rip of bp_1 function
  67. * - detaches the child
  68. */
  69. if (ptrace(PTRACE_POKEUSER, child,
  70. offsetof(struct user, u_debugreg[0]), bp_2)) {
  71. pr_debug("failed to set breakpoint, 1st time: %s\n",
  72. strerror(errno));
  73. goto out;
  74. }
  75. if (ptrace(PTRACE_POKEUSER, child,
  76. offsetof(struct user, u_debugreg[0]), bp_1)) {
  77. pr_debug("failed to set breakpoint, 2nd time: %s\n",
  78. strerror(errno));
  79. goto out;
  80. }
  81. if (ptrace(PTRACE_POKEUSER, child,
  82. offsetof(struct user, u_debugreg[7]), dr7)) {
  83. pr_debug("failed to set dr7: %s\n", strerror(errno));
  84. goto out;
  85. }
  86. if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
  87. pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
  88. goto out;
  89. }
  90. waitpid(child, &status, 0);
  91. if (WIFEXITED(status)) {
  92. pr_debug("tracee exited prematurely 2\n");
  93. return TEST_FAIL;
  94. }
  95. rip = ptrace(PTRACE_PEEKUSER, child,
  96. offsetof(struct user_regs_struct, rip), NULL);
  97. if (rip == (unsigned long) -1) {
  98. pr_debug("failed to PTRACE_PEEKUSER: %s\n",
  99. strerror(errno));
  100. goto out;
  101. }
  102. pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
  103. out:
  104. if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
  105. pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
  106. return TEST_FAIL;
  107. }
  108. return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
  109. }
  110. /*
  111. * This tests creates HW breakpoint, tries to
  112. * change it to bogus value and checks the original
  113. * breakpoint is hit.
  114. */
  115. static int bp_modify2(void)
  116. {
  117. pid_t child;
  118. int status;
  119. unsigned long rip = 0, dr7 = 1;
  120. child = spawn_child();
  121. waitpid(child, &status, 0);
  122. if (WIFEXITED(status)) {
  123. pr_debug("tracee exited prematurely 1\n");
  124. return TEST_FAIL;
  125. }
  126. /*
  127. * The parent does following steps:
  128. * - creates a new breakpoint (id 0) for bp_1 function
  129. * - tries to change that breakpoint to (-1) address
  130. * - waits for the breakpoint to hit and checks
  131. * it has proper rip of bp_1 function
  132. * - detaches the child
  133. */
  134. if (ptrace(PTRACE_POKEUSER, child,
  135. offsetof(struct user, u_debugreg[0]), bp_1)) {
  136. pr_debug("failed to set breakpoint: %s\n",
  137. strerror(errno));
  138. goto out;
  139. }
  140. if (ptrace(PTRACE_POKEUSER, child,
  141. offsetof(struct user, u_debugreg[7]), dr7)) {
  142. pr_debug("failed to set dr7: %s\n", strerror(errno));
  143. goto out;
  144. }
  145. if (!ptrace(PTRACE_POKEUSER, child,
  146. offsetof(struct user, u_debugreg[0]), (unsigned long) (-1))) {
  147. pr_debug("failed, breakpoint set to bogus address\n");
  148. goto out;
  149. }
  150. if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
  151. pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
  152. goto out;
  153. }
  154. waitpid(child, &status, 0);
  155. if (WIFEXITED(status)) {
  156. pr_debug("tracee exited prematurely 2\n");
  157. return TEST_FAIL;
  158. }
  159. rip = ptrace(PTRACE_PEEKUSER, child,
  160. offsetof(struct user_regs_struct, rip), NULL);
  161. if (rip == (unsigned long) -1) {
  162. pr_debug("failed to PTRACE_PEEKUSER: %s\n",
  163. strerror(errno));
  164. goto out;
  165. }
  166. pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
  167. out:
  168. if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
  169. pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
  170. return TEST_FAIL;
  171. }
  172. return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
  173. }
  174. int test__bp_modify(struct test *test __maybe_unused,
  175. int subtest __maybe_unused)
  176. {
  177. TEST_ASSERT_VAL("modify test 1 failed\n", !bp_modify1());
  178. TEST_ASSERT_VAL("modify test 2 failed\n", !bp_modify2());
  179. return 0;
  180. }