brintos

brintos / linux-shallow public Read only

0
0
Text · 4.6 KiB · a6884f6 Raw
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