cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

pidfd_test.c (14261B)


      1/* SPDX-License-Identifier: GPL-2.0 */
      2
      3#define _GNU_SOURCE
      4#include <errno.h>
      5#include <fcntl.h>
      6#include <linux/types.h>
      7#include <pthread.h>
      8#include <sched.h>
      9#include <signal.h>
     10#include <stdio.h>
     11#include <stdbool.h>
     12#include <stdlib.h>
     13#include <string.h>
     14#include <syscall.h>
     15#include <sys/epoll.h>
     16#include <sys/mman.h>
     17#include <sys/mount.h>
     18#include <sys/wait.h>
     19#include <time.h>
     20#include <unistd.h>
     21
     22#include "pidfd.h"
     23#include "../kselftest.h"
     24
     25#define str(s) _str(s)
     26#define _str(s) #s
     27#define CHILD_THREAD_MIN_WAIT 3 /* seconds */
     28
     29#define MAX_EVENTS 5
     30
     31static bool have_pidfd_send_signal;
     32
     33static pid_t pidfd_clone(int flags, int *pidfd, int (*fn)(void *))
     34{
     35	size_t stack_size = 1024;
     36	char *stack[1024] = { 0 };
     37
     38#ifdef __ia64__
     39	return __clone2(fn, stack, stack_size, flags | SIGCHLD, NULL, pidfd);
     40#else
     41	return clone(fn, stack + stack_size, flags | SIGCHLD, NULL, pidfd);
     42#endif
     43}
     44
     45static int signal_received;
     46
     47static void set_signal_received_on_sigusr1(int sig)
     48{
     49	if (sig == SIGUSR1)
     50		signal_received = 1;
     51}
     52
     53/*
     54 * Straightforward test to see whether pidfd_send_signal() works is to send
     55 * a signal to ourself.
     56 */
     57static int test_pidfd_send_signal_simple_success(void)
     58{
     59	int pidfd, ret;
     60	const char *test_name = "pidfd_send_signal send SIGUSR1";
     61
     62	if (!have_pidfd_send_signal) {
     63		ksft_test_result_skip(
     64			"%s test: pidfd_send_signal() syscall not supported\n",
     65			test_name);
     66		return 0;
     67	}
     68
     69	pidfd = open("/proc/self", O_DIRECTORY | O_CLOEXEC);
     70	if (pidfd < 0)
     71		ksft_exit_fail_msg(
     72			"%s test: Failed to open process file descriptor\n",
     73			test_name);
     74
     75	signal(SIGUSR1, set_signal_received_on_sigusr1);
     76
     77	ret = sys_pidfd_send_signal(pidfd, SIGUSR1, NULL, 0);
     78	close(pidfd);
     79	if (ret < 0)
     80		ksft_exit_fail_msg("%s test: Failed to send signal\n",
     81				   test_name);
     82
     83	if (signal_received != 1)
     84		ksft_exit_fail_msg("%s test: Failed to receive signal\n",
     85				   test_name);
     86
     87	signal_received = 0;
     88	ksft_test_result_pass("%s test: Sent signal\n", test_name);
     89	return 0;
     90}
     91
     92static int test_pidfd_send_signal_exited_fail(void)
     93{
     94	int pidfd, ret, saved_errno;
     95	char buf[256];
     96	pid_t pid;
     97	const char *test_name = "pidfd_send_signal signal exited process";
     98
     99	if (!have_pidfd_send_signal) {
    100		ksft_test_result_skip(
    101			"%s test: pidfd_send_signal() syscall not supported\n",
    102			test_name);
    103		return 0;
    104	}
    105
    106	pid = fork();
    107	if (pid < 0)
    108		ksft_exit_fail_msg("%s test: Failed to create new process\n",
    109				   test_name);
    110
    111	if (pid == 0)
    112		_exit(EXIT_SUCCESS);
    113
    114	snprintf(buf, sizeof(buf), "/proc/%d", pid);
    115
    116	pidfd = open(buf, O_DIRECTORY | O_CLOEXEC);
    117
    118	(void)wait_for_pid(pid);
    119
    120	if (pidfd < 0)
    121		ksft_exit_fail_msg(
    122			"%s test: Failed to open process file descriptor\n",
    123			test_name);
    124
    125	ret = sys_pidfd_send_signal(pidfd, 0, NULL, 0);
    126	saved_errno = errno;
    127	close(pidfd);
    128	if (ret == 0)
    129		ksft_exit_fail_msg(
    130			"%s test: Managed to send signal to process even though it should have failed\n",
    131			test_name);
    132
    133	if (saved_errno != ESRCH)
    134		ksft_exit_fail_msg(
    135			"%s test: Expected to receive ESRCH as errno value but received %d instead\n",
    136			test_name, saved_errno);
    137
    138	ksft_test_result_pass("%s test: Failed to send signal as expected\n",
    139			      test_name);
    140	return 0;
    141}
    142
    143/*
    144 * Maximum number of cycles we allow. This is equivalent to PID_MAX_DEFAULT.
    145 * If users set a higher limit or we have cycled PIDFD_MAX_DEFAULT number of
    146 * times then we skip the test to not go into an infinite loop or block for a
    147 * long time.
    148 */
    149#define PIDFD_MAX_DEFAULT 0x8000
    150
    151static int test_pidfd_send_signal_recycled_pid_fail(void)
    152{
    153	int i, ret;
    154	pid_t pid1;
    155	const char *test_name = "pidfd_send_signal signal recycled pid";
    156
    157	if (!have_pidfd_send_signal) {
    158		ksft_test_result_skip(
    159			"%s test: pidfd_send_signal() syscall not supported\n",
    160			test_name);
    161		return 0;
    162	}
    163
    164	ret = unshare(CLONE_NEWPID);
    165	if (ret < 0) {
    166		if (errno == EPERM) {
    167			ksft_test_result_skip("%s test: Unsharing pid namespace not permitted\n",
    168					      test_name);
    169			return 0;
    170		}
    171		ksft_exit_fail_msg("%s test: Failed to unshare pid namespace\n",
    172				   test_name);
    173	}
    174
    175	ret = unshare(CLONE_NEWNS);
    176	if (ret < 0) {
    177		if (errno == EPERM) {
    178			ksft_test_result_skip("%s test: Unsharing mount namespace not permitted\n",
    179					      test_name);
    180			return 0;
    181		}
    182		ksft_exit_fail_msg("%s test: Failed to unshare mount namespace\n",
    183				   test_name);
    184	}
    185
    186	ret = mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0);
    187	if (ret < 0)
    188		ksft_exit_fail_msg("%s test: Failed to remount / private\n",
    189				   test_name);
    190
    191	/* pid 1 in new pid namespace */
    192	pid1 = fork();
    193	if (pid1 < 0)
    194		ksft_exit_fail_msg("%s test: Failed to create new process\n",
    195				   test_name);
    196
    197	if (pid1 == 0) {
    198		char buf[256];
    199		pid_t pid2;
    200		int pidfd = -1;
    201
    202		(void)umount2("/proc", MNT_DETACH);
    203		ret = mount("proc", "/proc", "proc", 0, NULL);
    204		if (ret < 0)
    205			_exit(PIDFD_ERROR);
    206
    207		/* grab pid PID_RECYCLE */
    208		for (i = 0; i <= PIDFD_MAX_DEFAULT; i++) {
    209			pid2 = fork();
    210			if (pid2 < 0)
    211				_exit(PIDFD_ERROR);
    212
    213			if (pid2 == 0)
    214				_exit(PIDFD_PASS);
    215
    216			if (pid2 == PID_RECYCLE) {
    217				snprintf(buf, sizeof(buf), "/proc/%d", pid2);
    218				ksft_print_msg("pid to recycle is %d\n", pid2);
    219				pidfd = open(buf, O_DIRECTORY | O_CLOEXEC);
    220			}
    221
    222			if (wait_for_pid(pid2))
    223				_exit(PIDFD_ERROR);
    224
    225			if (pid2 >= PID_RECYCLE)
    226				break;
    227		}
    228
    229		/*
    230		 * We want to be as predictable as we can so if we haven't been
    231		 * able to grab pid PID_RECYCLE skip the test.
    232		 */
    233		if (pid2 != PID_RECYCLE) {
    234			/* skip test */
    235			close(pidfd);
    236			_exit(PIDFD_SKIP);
    237		}
    238
    239		if (pidfd < 0)
    240			_exit(PIDFD_ERROR);
    241
    242		for (i = 0; i <= PIDFD_MAX_DEFAULT; i++) {
    243			char c;
    244			int pipe_fds[2];
    245			pid_t recycled_pid;
    246			int child_ret = PIDFD_PASS;
    247
    248			ret = pipe2(pipe_fds, O_CLOEXEC);
    249			if (ret < 0)
    250				_exit(PIDFD_ERROR);
    251
    252			recycled_pid = fork();
    253			if (recycled_pid < 0)
    254				_exit(PIDFD_ERROR);
    255
    256			if (recycled_pid == 0) {
    257				close(pipe_fds[1]);
    258				(void)read(pipe_fds[0], &c, 1);
    259				close(pipe_fds[0]);
    260
    261				_exit(PIDFD_PASS);
    262			}
    263
    264			/*
    265			 * Stop the child so we can inspect whether we have
    266			 * recycled pid PID_RECYCLE.
    267			 */
    268			close(pipe_fds[0]);
    269			ret = kill(recycled_pid, SIGSTOP);
    270			close(pipe_fds[1]);
    271			if (ret) {
    272				(void)wait_for_pid(recycled_pid);
    273				_exit(PIDFD_ERROR);
    274			}
    275
    276			/*
    277			 * We have recycled the pid. Try to signal it. This
    278			 * needs to fail since this is a different process than
    279			 * the one the pidfd refers to.
    280			 */
    281			if (recycled_pid == PID_RECYCLE) {
    282				ret = sys_pidfd_send_signal(pidfd, SIGCONT,
    283							    NULL, 0);
    284				if (ret && errno == ESRCH)
    285					child_ret = PIDFD_XFAIL;
    286				else
    287					child_ret = PIDFD_FAIL;
    288			}
    289
    290			/* let the process move on */
    291			ret = kill(recycled_pid, SIGCONT);
    292			if (ret)
    293				(void)kill(recycled_pid, SIGKILL);
    294
    295			if (wait_for_pid(recycled_pid))
    296				_exit(PIDFD_ERROR);
    297
    298			switch (child_ret) {
    299			case PIDFD_FAIL:
    300				/* fallthrough */
    301			case PIDFD_XFAIL:
    302				_exit(child_ret);
    303			case PIDFD_PASS:
    304				break;
    305			default:
    306				/* not reached */
    307				_exit(PIDFD_ERROR);
    308			}
    309
    310			/*
    311			 * If the user set a custom pid_max limit we could be
    312			 * in the millions.
    313			 * Skip the test in this case.
    314			 */
    315			if (recycled_pid > PIDFD_MAX_DEFAULT)
    316				_exit(PIDFD_SKIP);
    317		}
    318
    319		/* failed to recycle pid */
    320		_exit(PIDFD_SKIP);
    321	}
    322
    323	ret = wait_for_pid(pid1);
    324	switch (ret) {
    325	case PIDFD_FAIL:
    326		ksft_exit_fail_msg(
    327			"%s test: Managed to signal recycled pid %d\n",
    328			test_name, PID_RECYCLE);
    329	case PIDFD_PASS:
    330		ksft_exit_fail_msg("%s test: Failed to recycle pid %d\n",
    331				   test_name, PID_RECYCLE);
    332	case PIDFD_SKIP:
    333		ksft_test_result_skip("%s test: Skipping test\n", test_name);
    334		ret = 0;
    335		break;
    336	case PIDFD_XFAIL:
    337		ksft_test_result_pass(
    338			"%s test: Failed to signal recycled pid as expected\n",
    339			test_name);
    340		ret = 0;
    341		break;
    342	default /* PIDFD_ERROR */:
    343		ksft_exit_fail_msg("%s test: Error while running tests\n",
    344				   test_name);
    345	}
    346
    347	return ret;
    348}
    349
    350static int test_pidfd_send_signal_syscall_support(void)
    351{
    352	int pidfd, ret;
    353	const char *test_name = "pidfd_send_signal check for support";
    354
    355	pidfd = open("/proc/self", O_DIRECTORY | O_CLOEXEC);
    356	if (pidfd < 0)
    357		ksft_exit_fail_msg(
    358			"%s test: Failed to open process file descriptor\n",
    359			test_name);
    360
    361	ret = sys_pidfd_send_signal(pidfd, 0, NULL, 0);
    362	if (ret < 0) {
    363		if (errno == ENOSYS) {
    364			ksft_test_result_skip(
    365				"%s test: pidfd_send_signal() syscall not supported\n",
    366				test_name);
    367			return 0;
    368		}
    369		ksft_exit_fail_msg("%s test: Failed to send signal\n",
    370				   test_name);
    371	}
    372
    373	have_pidfd_send_signal = true;
    374	close(pidfd);
    375	ksft_test_result_pass(
    376		"%s test: pidfd_send_signal() syscall is supported. Tests can be executed\n",
    377		test_name);
    378	return 0;
    379}
    380
    381static void *test_pidfd_poll_exec_thread(void *priv)
    382{
    383	ksft_print_msg("Child Thread: starting. pid %d tid %d ; and sleeping\n",
    384			getpid(), syscall(SYS_gettid));
    385	ksft_print_msg("Child Thread: doing exec of sleep\n");
    386
    387	execl("/bin/sleep", "sleep", str(CHILD_THREAD_MIN_WAIT), (char *)NULL);
    388
    389	ksft_print_msg("Child Thread: DONE. pid %d tid %d\n",
    390			getpid(), syscall(SYS_gettid));
    391	return NULL;
    392}
    393
    394static void poll_pidfd(const char *test_name, int pidfd)
    395{
    396	int c;
    397	int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    398	struct epoll_event event, events[MAX_EVENTS];
    399
    400	if (epoll_fd == -1)
    401		ksft_exit_fail_msg("%s test: Failed to create epoll file descriptor "
    402				   "(errno %d)\n",
    403				   test_name, errno);
    404
    405	event.events = EPOLLIN;
    406	event.data.fd = pidfd;
    407
    408	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pidfd, &event)) {
    409		ksft_exit_fail_msg("%s test: Failed to add epoll file descriptor "
    410				   "(errno %d)\n",
    411				   test_name, errno);
    412	}
    413
    414	c = epoll_wait(epoll_fd, events, MAX_EVENTS, 5000);
    415	if (c != 1 || !(events[0].events & EPOLLIN))
    416		ksft_exit_fail_msg("%s test: Unexpected epoll_wait result (c=%d, events=%x) ",
    417				   "(errno %d)\n",
    418				   test_name, c, events[0].events, errno);
    419
    420	close(epoll_fd);
    421	return;
    422
    423}
    424
    425static int child_poll_exec_test(void *args)
    426{
    427	pthread_t t1;
    428
    429	ksft_print_msg("Child (pidfd): starting. pid %d tid %d\n", getpid(),
    430			syscall(SYS_gettid));
    431	pthread_create(&t1, NULL, test_pidfd_poll_exec_thread, NULL);
    432	/*
    433	 * Exec in the non-leader thread will destroy the leader immediately.
    434	 * If the wait in the parent returns too soon, the test fails.
    435	 */
    436	while (1)
    437		sleep(1);
    438}
    439
    440static void test_pidfd_poll_exec(int use_waitpid)
    441{
    442	int pid, pidfd = 0;
    443	int status, ret;
    444	time_t prog_start = time(NULL);
    445	const char *test_name = "pidfd_poll check for premature notification on child thread exec";
    446
    447	ksft_print_msg("Parent: pid: %d\n", getpid());
    448	pid = pidfd_clone(CLONE_PIDFD, &pidfd, child_poll_exec_test);
    449	if (pid < 0)
    450		ksft_exit_fail_msg("%s test: pidfd_clone failed (ret %d, errno %d)\n",
    451				   test_name, pid, errno);
    452
    453	ksft_print_msg("Parent: Waiting for Child (%d) to complete.\n", pid);
    454
    455	if (use_waitpid) {
    456		ret = waitpid(pid, &status, 0);
    457		if (ret == -1)
    458			ksft_print_msg("Parent: error\n");
    459
    460		if (ret == pid)
    461			ksft_print_msg("Parent: Child process waited for.\n");
    462	} else {
    463		poll_pidfd(test_name, pidfd);
    464	}
    465
    466	time_t prog_time = time(NULL) - prog_start;
    467
    468	ksft_print_msg("Time waited for child: %lu\n", prog_time);
    469
    470	close(pidfd);
    471
    472	if (prog_time < CHILD_THREAD_MIN_WAIT || prog_time > CHILD_THREAD_MIN_WAIT + 2)
    473		ksft_exit_fail_msg("%s test: Failed\n", test_name);
    474	else
    475		ksft_test_result_pass("%s test: Passed\n", test_name);
    476}
    477
    478static void *test_pidfd_poll_leader_exit_thread(void *priv)
    479{
    480	ksft_print_msg("Child Thread: starting. pid %d tid %d ; and sleeping\n",
    481			getpid(), syscall(SYS_gettid));
    482	sleep(CHILD_THREAD_MIN_WAIT);
    483	ksft_print_msg("Child Thread: DONE. pid %d tid %d\n", getpid(), syscall(SYS_gettid));
    484	return NULL;
    485}
    486
    487static time_t *child_exit_secs;
    488static int child_poll_leader_exit_test(void *args)
    489{
    490	pthread_t t1, t2;
    491
    492	ksft_print_msg("Child: starting. pid %d tid %d\n", getpid(), syscall(SYS_gettid));
    493	pthread_create(&t1, NULL, test_pidfd_poll_leader_exit_thread, NULL);
    494	pthread_create(&t2, NULL, test_pidfd_poll_leader_exit_thread, NULL);
    495
    496	/*
    497	 * glibc exit calls exit_group syscall, so explicity call exit only
    498	 * so that only the group leader exits, leaving the threads alone.
    499	 */
    500	*child_exit_secs = time(NULL);
    501	syscall(SYS_exit, 0);
    502	/* Never reached, but appeases compiler thinking we should return. */
    503	exit(0);
    504}
    505
    506static void test_pidfd_poll_leader_exit(int use_waitpid)
    507{
    508	int pid, pidfd = 0;
    509	int status, ret = 0;
    510	const char *test_name = "pidfd_poll check for premature notification on non-empty"
    511				"group leader exit";
    512
    513	child_exit_secs = mmap(NULL, sizeof *child_exit_secs, PROT_READ | PROT_WRITE,
    514			MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    515
    516	if (child_exit_secs == MAP_FAILED)
    517		ksft_exit_fail_msg("%s test: mmap failed (errno %d)\n",
    518				   test_name, errno);
    519
    520	ksft_print_msg("Parent: pid: %d\n", getpid());
    521	pid = pidfd_clone(CLONE_PIDFD, &pidfd, child_poll_leader_exit_test);
    522	if (pid < 0)
    523		ksft_exit_fail_msg("%s test: pidfd_clone failed (ret %d, errno %d)\n",
    524				   test_name, pid, errno);
    525
    526	ksft_print_msg("Parent: Waiting for Child (%d) to complete.\n", pid);
    527
    528	if (use_waitpid) {
    529		ret = waitpid(pid, &status, 0);
    530		if (ret == -1)
    531			ksft_print_msg("Parent: error\n");
    532	} else {
    533		/*
    534		 * This sleep tests for the case where if the child exits, and is in
    535		 * EXIT_ZOMBIE, but the thread group leader is non-empty, then the poll
    536		 * doesn't prematurely return even though there are active threads
    537		 */
    538		sleep(1);
    539		poll_pidfd(test_name, pidfd);
    540	}
    541
    542	if (ret == pid)
    543		ksft_print_msg("Parent: Child process waited for.\n");
    544
    545	time_t since_child_exit = time(NULL) - *child_exit_secs;
    546
    547	ksft_print_msg("Time since child exit: %lu\n", since_child_exit);
    548
    549	close(pidfd);
    550
    551	if (since_child_exit < CHILD_THREAD_MIN_WAIT ||
    552			since_child_exit > CHILD_THREAD_MIN_WAIT + 2)
    553		ksft_exit_fail_msg("%s test: Failed\n", test_name);
    554	else
    555		ksft_test_result_pass("%s test: Passed\n", test_name);
    556}
    557
    558int main(int argc, char **argv)
    559{
    560	ksft_print_header();
    561	ksft_set_plan(8);
    562
    563	test_pidfd_poll_exec(0);
    564	test_pidfd_poll_exec(1);
    565	test_pidfd_poll_leader_exit(0);
    566	test_pidfd_poll_leader_exit(1);
    567	test_pidfd_send_signal_syscall_support();
    568	test_pidfd_send_signal_simple_success();
    569	test_pidfd_send_signal_exited_fail();
    570	test_pidfd_send_signal_recycled_pid_fail();
    571
    572	return ksft_exit_pass();
    573}