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