peeksiginfo.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. // SPDX-License-Identifier: GPL-2.0
  2. #define _GNU_SOURCE
  3. #include <stdio.h>
  4. #include <signal.h>
  5. #include <unistd.h>
  6. #include <errno.h>
  7. #include <linux/types.h>
  8. #include <sys/wait.h>
  9. #include <sys/syscall.h>
  10. #include <sys/user.h>
  11. #include <sys/mman.h>
  12. #include "linux/ptrace.h"
  13. static int sys_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *uinfo)
  14. {
  15. return syscall(SYS_rt_sigqueueinfo, tgid, sig, uinfo);
  16. }
  17. static int sys_rt_tgsigqueueinfo(pid_t tgid, pid_t tid,
  18. int sig, siginfo_t *uinfo)
  19. {
  20. return syscall(SYS_rt_tgsigqueueinfo, tgid, tid, sig, uinfo);
  21. }
  22. static int sys_ptrace(int request, pid_t pid, void *addr, void *data)
  23. {
  24. return syscall(SYS_ptrace, request, pid, addr, data);
  25. }
  26. #define SIGNR 10
  27. #define TEST_SICODE_PRIV -1
  28. #define TEST_SICODE_SHARE -2
  29. #ifndef PAGE_SIZE
  30. #define PAGE_SIZE sysconf(_SC_PAGESIZE)
  31. #endif
  32. #define err(fmt, ...) \
  33. fprintf(stderr, \
  34. "Error (%s:%d): " fmt, \
  35. __FILE__, __LINE__, ##__VA_ARGS__)
  36. static int check_error_paths(pid_t child)
  37. {
  38. struct ptrace_peeksiginfo_args arg;
  39. int ret, exit_code = -1;
  40. void *addr_rw, *addr_ro;
  41. /*
  42. * Allocate two contiguous pages. The first one is for read-write,
  43. * another is for read-only.
  44. */
  45. addr_rw = mmap(NULL, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE,
  46. MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  47. if (addr_rw == MAP_FAILED) {
  48. err("mmap() failed: %m\n");
  49. return 1;
  50. }
  51. addr_ro = mmap(addr_rw + PAGE_SIZE, PAGE_SIZE, PROT_READ,
  52. MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
  53. if (addr_ro == MAP_FAILED) {
  54. err("mmap() failed: %m\n");
  55. goto out;
  56. }
  57. arg.nr = SIGNR;
  58. arg.off = 0;
  59. /* Unsupported flags */
  60. arg.flags = ~0;
  61. ret = sys_ptrace(PTRACE_PEEKSIGINFO, child, &arg, addr_rw);
  62. if (ret != -1 || errno != EINVAL) {
  63. err("sys_ptrace() returns %d (expected -1),"
  64. " errno %d (expected %d): %m\n",
  65. ret, errno, EINVAL);
  66. goto out;
  67. }
  68. arg.flags = 0;
  69. /* A part of the buffer is read-only */
  70. ret = sys_ptrace(PTRACE_PEEKSIGINFO, child, &arg,
  71. addr_ro - sizeof(siginfo_t) * 2);
  72. if (ret != 2) {
  73. err("sys_ptrace() returns %d (expected 2): %m\n", ret);
  74. goto out;
  75. }
  76. /* Read-only buffer */
  77. ret = sys_ptrace(PTRACE_PEEKSIGINFO, child, &arg, addr_ro);
  78. if (ret != -1 && errno != EFAULT) {
  79. err("sys_ptrace() returns %d (expected -1),"
  80. " errno %d (expected %d): %m\n",
  81. ret, errno, EFAULT);
  82. goto out;
  83. }
  84. exit_code = 0;
  85. out:
  86. munmap(addr_rw, 2 * PAGE_SIZE);
  87. return exit_code;
  88. }
  89. int check_direct_path(pid_t child, int shared, int nr)
  90. {
  91. struct ptrace_peeksiginfo_args arg = {.flags = 0, .nr = nr, .off = 0};
  92. int i, j, ret, exit_code = -1;
  93. siginfo_t siginfo[SIGNR];
  94. int si_code;
  95. if (shared == 1) {
  96. arg.flags = PTRACE_PEEKSIGINFO_SHARED;
  97. si_code = TEST_SICODE_SHARE;
  98. } else {
  99. arg.flags = 0;
  100. si_code = TEST_SICODE_PRIV;
  101. }
  102. for (i = 0; i < SIGNR; ) {
  103. arg.off = i;
  104. ret = sys_ptrace(PTRACE_PEEKSIGINFO, child, &arg, siginfo);
  105. if (ret == -1) {
  106. err("ptrace() failed: %m\n");
  107. goto out;
  108. }
  109. if (ret == 0)
  110. break;
  111. for (j = 0; j < ret; j++, i++) {
  112. if (siginfo[j].si_code == si_code &&
  113. siginfo[j].si_int == i)
  114. continue;
  115. err("%d: Wrong siginfo i=%d si_code=%d si_int=%d\n",
  116. shared, i, siginfo[j].si_code, siginfo[j].si_int);
  117. goto out;
  118. }
  119. }
  120. if (i != SIGNR) {
  121. err("Only %d signals were read\n", i);
  122. goto out;
  123. }
  124. exit_code = 0;
  125. out:
  126. return exit_code;
  127. }
  128. int main(int argc, char *argv[])
  129. {
  130. siginfo_t siginfo[SIGNR];
  131. int i, exit_code = 1;
  132. sigset_t blockmask;
  133. pid_t child;
  134. sigemptyset(&blockmask);
  135. sigaddset(&blockmask, SIGRTMIN);
  136. sigprocmask(SIG_BLOCK, &blockmask, NULL);
  137. child = fork();
  138. if (child == -1) {
  139. err("fork() failed: %m");
  140. return 1;
  141. } else if (child == 0) {
  142. pid_t ppid = getppid();
  143. while (1) {
  144. if (ppid != getppid())
  145. break;
  146. sleep(1);
  147. }
  148. return 1;
  149. }
  150. /* Send signals in process-wide and per-thread queues */
  151. for (i = 0; i < SIGNR; i++) {
  152. siginfo->si_code = TEST_SICODE_SHARE;
  153. siginfo->si_int = i;
  154. sys_rt_sigqueueinfo(child, SIGRTMIN, siginfo);
  155. siginfo->si_code = TEST_SICODE_PRIV;
  156. siginfo->si_int = i;
  157. sys_rt_tgsigqueueinfo(child, child, SIGRTMIN, siginfo);
  158. }
  159. if (sys_ptrace(PTRACE_ATTACH, child, NULL, NULL) == -1)
  160. return 1;
  161. waitpid(child, NULL, 0);
  162. /* Dump signals one by one*/
  163. if (check_direct_path(child, 0, 1))
  164. goto out;
  165. /* Dump all signals for one call */
  166. if (check_direct_path(child, 0, SIGNR))
  167. goto out;
  168. /*
  169. * Dump signal from the process-wide queue.
  170. * The number of signals is not multible to the buffer size
  171. */
  172. if (check_direct_path(child, 1, 3))
  173. goto out;
  174. if (check_error_paths(child))
  175. goto out;
  176. printf("PASS\n");
  177. exit_code = 0;
  178. out:
  179. if (sys_ptrace(PTRACE_KILL, child, NULL, NULL) == -1)
  180. return 1;
  181. waitpid(child, NULL, 0);
  182. return exit_code;
  183. }