220 lines · c
1// SPDX-License-Identifier: GPL-2.02#define _GNU_SOURCE3#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 1032#define TEST_SICODE_PRIV -133#define TEST_SICODE_SHARE -234 35#ifndef PAGE_SIZE36#define PAGE_SIZE sysconf(_SC_PAGESIZE)37#endif38 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;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 size203 */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}220