linux/tools/testing/selftests/ptrace/peeksiginfo.c
<<
>>
Prefs
   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}
 220