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