Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftest: add a test case for PTRACE_PEEKSIGINFO

* Dump signals from process-wide and per-thread queues with
different sizes of buffers.
* Check error paths for buffers with restricted permissions. A part of
buffer or a whole buffer is for read-only.
* Try to get nonexistent signal.

Signed-off-by: Andrew Vagin <avagin@openvz.org>
Cc: Roland McGrath <roland@redhat.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
Cc: David Howells <dhowells@redhat.com>
Cc: Dave Jones <davej@redhat.com>
Cc: "Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com>
Cc: Pavel Emelyanov <xemul@parallels.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Pedro Alves <palves@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Andrey Vagin and committed by
Linus Torvalds
17afab1d 84c751bd

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