brintos

brintos / linux-shallow public Read only

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