1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
217afab1dSAndrey Vagin #define _GNU_SOURCE
317afab1dSAndrey Vagin #include <stdio.h>
417afab1dSAndrey Vagin #include <signal.h>
517afab1dSAndrey Vagin #include <unistd.h>
617afab1dSAndrey Vagin #include <errno.h>
717afab1dSAndrey Vagin #include <linux/types.h>
817afab1dSAndrey Vagin #include <sys/wait.h>
917afab1dSAndrey Vagin #include <sys/syscall.h>
1017afab1dSAndrey Vagin #include <sys/user.h>
1117afab1dSAndrey Vagin #include <sys/mman.h>
1217afab1dSAndrey Vagin
1317afab1dSAndrey Vagin #include "linux/ptrace.h"
1417afab1dSAndrey Vagin
sys_rt_sigqueueinfo(pid_t tgid,int sig,siginfo_t * uinfo)1517afab1dSAndrey Vagin static int sys_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *uinfo)
1617afab1dSAndrey Vagin {
1717afab1dSAndrey Vagin return syscall(SYS_rt_sigqueueinfo, tgid, sig, uinfo);
1817afab1dSAndrey Vagin }
1917afab1dSAndrey Vagin
sys_rt_tgsigqueueinfo(pid_t tgid,pid_t tid,int sig,siginfo_t * uinfo)2017afab1dSAndrey Vagin static int sys_rt_tgsigqueueinfo(pid_t tgid, pid_t tid,
2117afab1dSAndrey Vagin int sig, siginfo_t *uinfo)
2217afab1dSAndrey Vagin {
2317afab1dSAndrey Vagin return syscall(SYS_rt_tgsigqueueinfo, tgid, tid, sig, uinfo);
2417afab1dSAndrey Vagin }
2517afab1dSAndrey Vagin
sys_ptrace(int request,pid_t pid,void * addr,void * data)2617afab1dSAndrey Vagin static int sys_ptrace(int request, pid_t pid, void *addr, void *data)
2717afab1dSAndrey Vagin {
2817afab1dSAndrey Vagin return syscall(SYS_ptrace, request, pid, addr, data);
2917afab1dSAndrey Vagin }
3017afab1dSAndrey Vagin
3117afab1dSAndrey Vagin #define SIGNR 10
3217afab1dSAndrey Vagin #define TEST_SICODE_PRIV -1
3317afab1dSAndrey Vagin #define TEST_SICODE_SHARE -2
3417afab1dSAndrey Vagin
358b6aaf65SThierry Fauck #ifndef PAGE_SIZE
368b6aaf65SThierry Fauck #define PAGE_SIZE sysconf(_SC_PAGESIZE)
378b6aaf65SThierry Fauck #endif
388b6aaf65SThierry Fauck
3917afab1dSAndrey Vagin #define err(fmt, ...) \
4017afab1dSAndrey Vagin fprintf(stderr, \
4117afab1dSAndrey Vagin "Error (%s:%d): " fmt, \
4217afab1dSAndrey Vagin __FILE__, __LINE__, ##__VA_ARGS__)
4317afab1dSAndrey Vagin
check_error_paths(pid_t child)4417afab1dSAndrey Vagin static int check_error_paths(pid_t child)
4517afab1dSAndrey Vagin {
4617afab1dSAndrey Vagin struct ptrace_peeksiginfo_args arg;
4717afab1dSAndrey Vagin int ret, exit_code = -1;
4817afab1dSAndrey Vagin void *addr_rw, *addr_ro;
4917afab1dSAndrey Vagin
5017afab1dSAndrey Vagin /*
5117afab1dSAndrey Vagin * Allocate two contiguous pages. The first one is for read-write,
5217afab1dSAndrey Vagin * another is for read-only.
5317afab1dSAndrey Vagin */
5417afab1dSAndrey Vagin addr_rw = mmap(NULL, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE,
5517afab1dSAndrey Vagin MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
5617afab1dSAndrey Vagin if (addr_rw == MAP_FAILED) {
5717afab1dSAndrey Vagin err("mmap() failed: %m\n");
5817afab1dSAndrey Vagin return 1;
5917afab1dSAndrey Vagin }
6017afab1dSAndrey Vagin
6117afab1dSAndrey Vagin addr_ro = mmap(addr_rw + PAGE_SIZE, PAGE_SIZE, PROT_READ,
6217afab1dSAndrey Vagin MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
6317afab1dSAndrey Vagin if (addr_ro == MAP_FAILED) {
6417afab1dSAndrey Vagin err("mmap() failed: %m\n");
6517afab1dSAndrey Vagin goto out;
6617afab1dSAndrey Vagin }
6717afab1dSAndrey Vagin
6817afab1dSAndrey Vagin arg.nr = SIGNR;
6917afab1dSAndrey Vagin arg.off = 0;
7017afab1dSAndrey Vagin
7117afab1dSAndrey Vagin /* Unsupported flags */
7217afab1dSAndrey Vagin arg.flags = ~0;
7317afab1dSAndrey Vagin ret = sys_ptrace(PTRACE_PEEKSIGINFO, child, &arg, addr_rw);
7417afab1dSAndrey Vagin if (ret != -1 || errno != EINVAL) {
7517afab1dSAndrey Vagin err("sys_ptrace() returns %d (expected -1),"
7617afab1dSAndrey Vagin " errno %d (expected %d): %m\n",
7717afab1dSAndrey Vagin ret, errno, EINVAL);
7817afab1dSAndrey Vagin goto out;
7917afab1dSAndrey Vagin }
8017afab1dSAndrey Vagin arg.flags = 0;
8117afab1dSAndrey Vagin
8217afab1dSAndrey Vagin /* A part of the buffer is read-only */
8317afab1dSAndrey Vagin ret = sys_ptrace(PTRACE_PEEKSIGINFO, child, &arg,
8417afab1dSAndrey Vagin addr_ro - sizeof(siginfo_t) * 2);
8517afab1dSAndrey Vagin if (ret != 2) {
8617afab1dSAndrey Vagin err("sys_ptrace() returns %d (expected 2): %m\n", ret);
8717afab1dSAndrey Vagin goto out;
8817afab1dSAndrey Vagin }
8917afab1dSAndrey Vagin
9017afab1dSAndrey Vagin /* Read-only buffer */
9117afab1dSAndrey Vagin ret = sys_ptrace(PTRACE_PEEKSIGINFO, child, &arg, addr_ro);
9217afab1dSAndrey Vagin if (ret != -1 && errno != EFAULT) {
9317afab1dSAndrey Vagin err("sys_ptrace() returns %d (expected -1),"
9417afab1dSAndrey Vagin " errno %d (expected %d): %m\n",
9517afab1dSAndrey Vagin ret, errno, EFAULT);
9617afab1dSAndrey Vagin goto out;
9717afab1dSAndrey Vagin }
9817afab1dSAndrey Vagin
9917afab1dSAndrey Vagin exit_code = 0;
10017afab1dSAndrey Vagin out:
10117afab1dSAndrey Vagin munmap(addr_rw, 2 * PAGE_SIZE);
10217afab1dSAndrey Vagin return exit_code;
10317afab1dSAndrey Vagin }
10417afab1dSAndrey Vagin
check_direct_path(pid_t child,int shared,int nr)10517afab1dSAndrey Vagin int check_direct_path(pid_t child, int shared, int nr)
10617afab1dSAndrey Vagin {
10717afab1dSAndrey Vagin struct ptrace_peeksiginfo_args arg = {.flags = 0, .nr = nr, .off = 0};
10817afab1dSAndrey Vagin int i, j, ret, exit_code = -1;
10917afab1dSAndrey Vagin siginfo_t siginfo[SIGNR];
11017afab1dSAndrey Vagin int si_code;
11117afab1dSAndrey Vagin
11217afab1dSAndrey Vagin if (shared == 1) {
11317afab1dSAndrey Vagin arg.flags = PTRACE_PEEKSIGINFO_SHARED;
11417afab1dSAndrey Vagin si_code = TEST_SICODE_SHARE;
11517afab1dSAndrey Vagin } else {
11617afab1dSAndrey Vagin arg.flags = 0;
11717afab1dSAndrey Vagin si_code = TEST_SICODE_PRIV;
11817afab1dSAndrey Vagin }
11917afab1dSAndrey Vagin
12017afab1dSAndrey Vagin for (i = 0; i < SIGNR; ) {
12117afab1dSAndrey Vagin arg.off = i;
12217afab1dSAndrey Vagin ret = sys_ptrace(PTRACE_PEEKSIGINFO, child, &arg, siginfo);
12317afab1dSAndrey Vagin if (ret == -1) {
12417afab1dSAndrey Vagin err("ptrace() failed: %m\n");
12517afab1dSAndrey Vagin goto out;
12617afab1dSAndrey Vagin }
12717afab1dSAndrey Vagin
12817afab1dSAndrey Vagin if (ret == 0)
12917afab1dSAndrey Vagin break;
13017afab1dSAndrey Vagin
13117afab1dSAndrey Vagin for (j = 0; j < ret; j++, i++) {
13217afab1dSAndrey Vagin if (siginfo[j].si_code == si_code &&
13317afab1dSAndrey Vagin siginfo[j].si_int == i)
13417afab1dSAndrey Vagin continue;
13517afab1dSAndrey Vagin
13617afab1dSAndrey Vagin err("%d: Wrong siginfo i=%d si_code=%d si_int=%d\n",
13717afab1dSAndrey Vagin shared, i, siginfo[j].si_code, siginfo[j].si_int);
13817afab1dSAndrey Vagin goto out;
13917afab1dSAndrey Vagin }
14017afab1dSAndrey Vagin }
14117afab1dSAndrey Vagin
14217afab1dSAndrey Vagin if (i != SIGNR) {
14317afab1dSAndrey Vagin err("Only %d signals were read\n", i);
14417afab1dSAndrey Vagin goto out;
14517afab1dSAndrey Vagin }
14617afab1dSAndrey Vagin
14717afab1dSAndrey Vagin exit_code = 0;
14817afab1dSAndrey Vagin out:
14917afab1dSAndrey Vagin return exit_code;
15017afab1dSAndrey Vagin }
15117afab1dSAndrey Vagin
main(int argc,char * argv[])15217afab1dSAndrey Vagin int main(int argc, char *argv[])
15317afab1dSAndrey Vagin {
154*350d216dSIvan Orlov siginfo_t siginfo;
15517afab1dSAndrey Vagin int i, exit_code = 1;
15617afab1dSAndrey Vagin sigset_t blockmask;
15717afab1dSAndrey Vagin pid_t child;
15817afab1dSAndrey Vagin
15917afab1dSAndrey Vagin sigemptyset(&blockmask);
16017afab1dSAndrey Vagin sigaddset(&blockmask, SIGRTMIN);
16117afab1dSAndrey Vagin sigprocmask(SIG_BLOCK, &blockmask, NULL);
16217afab1dSAndrey Vagin
16317afab1dSAndrey Vagin child = fork();
16417afab1dSAndrey Vagin if (child == -1) {
16517afab1dSAndrey Vagin err("fork() failed: %m");
16617afab1dSAndrey Vagin return 1;
16717afab1dSAndrey Vagin } else if (child == 0) {
16817afab1dSAndrey Vagin pid_t ppid = getppid();
16917afab1dSAndrey Vagin while (1) {
17017afab1dSAndrey Vagin if (ppid != getppid())
17117afab1dSAndrey Vagin break;
17217afab1dSAndrey Vagin sleep(1);
17317afab1dSAndrey Vagin }
17417afab1dSAndrey Vagin return 1;
17517afab1dSAndrey Vagin }
17617afab1dSAndrey Vagin
17717afab1dSAndrey Vagin /* Send signals in process-wide and per-thread queues */
17817afab1dSAndrey Vagin for (i = 0; i < SIGNR; i++) {
179*350d216dSIvan Orlov siginfo.si_code = TEST_SICODE_SHARE;
180*350d216dSIvan Orlov siginfo.si_int = i;
181*350d216dSIvan Orlov sys_rt_sigqueueinfo(child, SIGRTMIN, &siginfo);
18217afab1dSAndrey Vagin
183*350d216dSIvan Orlov siginfo.si_code = TEST_SICODE_PRIV;
184*350d216dSIvan Orlov siginfo.si_int = i;
185*350d216dSIvan Orlov sys_rt_tgsigqueueinfo(child, child, SIGRTMIN, &siginfo);
18617afab1dSAndrey Vagin }
18717afab1dSAndrey Vagin
18817afab1dSAndrey Vagin if (sys_ptrace(PTRACE_ATTACH, child, NULL, NULL) == -1)
18917afab1dSAndrey Vagin return 1;
19017afab1dSAndrey Vagin
19117afab1dSAndrey Vagin waitpid(child, NULL, 0);
19217afab1dSAndrey Vagin
19317afab1dSAndrey Vagin /* Dump signals one by one*/
19417afab1dSAndrey Vagin if (check_direct_path(child, 0, 1))
19517afab1dSAndrey Vagin goto out;
19617afab1dSAndrey Vagin /* Dump all signals for one call */
19717afab1dSAndrey Vagin if (check_direct_path(child, 0, SIGNR))
19817afab1dSAndrey Vagin goto out;
19917afab1dSAndrey Vagin
20017afab1dSAndrey Vagin /*
20117afab1dSAndrey Vagin * Dump signal from the process-wide queue.
20217afab1dSAndrey Vagin * The number of signals is not multible to the buffer size
20317afab1dSAndrey Vagin */
20417afab1dSAndrey Vagin if (check_direct_path(child, 1, 3))
20517afab1dSAndrey Vagin goto out;
20617afab1dSAndrey Vagin
20717afab1dSAndrey Vagin if (check_error_paths(child))
20817afab1dSAndrey Vagin goto out;
20917afab1dSAndrey Vagin
21017afab1dSAndrey Vagin printf("PASS\n");
21117afab1dSAndrey Vagin exit_code = 0;
21217afab1dSAndrey Vagin out:
21317afab1dSAndrey Vagin if (sys_ptrace(PTRACE_KILL, child, NULL, NULL) == -1)
21417afab1dSAndrey Vagin return 1;
21517afab1dSAndrey Vagin
21617afab1dSAndrey Vagin waitpid(child, NULL, 0);
21717afab1dSAndrey Vagin
21817afab1dSAndrey Vagin return exit_code;
21917afab1dSAndrey Vagin }
220