628 lines · 15.2 KB · C
RawDownload
/* SPDX-License-Identifier: GPL-2.0 */ #define _GNU_SOURCE#include <errno.h>#include <fcntl.h>#include <linux/types.h>#include <pthread.h>#include <sched.h>#include <signal.h>#include <stdio.h>#include <stdbool.h>#include <stdlib.h>#include <string.h>#include <syscall.h>#include <sys/epoll.h>#include <sys/mman.h>#include <sys/mount.h>#include <sys/wait.h>#include <time.h>#include <unistd.h> #include "pidfd.h"#include "kselftest.h" #define str(s) _str(s)#define _str(s) #s#define CHILD_THREAD_MIN_WAIT 3 /* seconds */ #define MAX_EVENTS 5 static bool have_pidfd_send_signal; static pid_t pidfd_clone(int flags, int *pidfd, int (*fn)(void *)){	size_t stack_size = 1024;	char *stack[1024] = { 0 }; #ifdef __ia64__	return __clone2(fn, stack, stack_size, flags | SIGCHLD, NULL, pidfd);#else	return clone(fn, stack + stack_size, flags | SIGCHLD, NULL, pidfd);#endif} static pthread_t signal_received; static void set_signal_received_on_sigusr1(int sig){	if (sig == SIGUSR1)		signal_received = pthread_self();} static int send_signal(int pidfd){	int ret = 0; 	if (sys_pidfd_send_signal(pidfd, SIGUSR1, NULL, 0) < 0) {		ret = -EINVAL;		goto exit;	} 	if (signal_received != pthread_self()) {		ret = -EINVAL;		goto exit;	} exit:	signal_received = 0;	return ret;} static void *send_signal_worker(void *arg){	int pidfd = (int)(intptr_t)arg;	int ret; 	/* We forward any errors for the caller to handle. */	ret = send_signal(pidfd);	return (void *)(intptr_t)ret;} /* * Straightforward test to see whether pidfd_send_signal() works is to send * a signal to ourself. */static int test_pidfd_send_signal_simple_success(void){	int pidfd;	const char *test_name = "pidfd_send_signal send SIGUSR1";	pthread_t thread;	void *thread_res;	int err; 	if (!have_pidfd_send_signal) {		ksft_test_result_skip(			"%s test: pidfd_send_signal() syscall not supported\n",			test_name);		return 0;	} 	signal(SIGUSR1, set_signal_received_on_sigusr1); 	/* Try sending a signal to ourselves via /proc/self. */	pidfd = open("/proc/self", O_DIRECTORY | O_CLOEXEC);	if (pidfd < 0)		ksft_exit_fail_msg(			"%s test: Failed to open process file descriptor\n",			test_name);	err = send_signal(pidfd);	if (err)		ksft_exit_fail_msg(			"%s test: Error %d on sending pidfd signal\n",			test_name, err);	close(pidfd); 	/* Now try the same thing only using PIDFD_SELF_THREAD_GROUP. */	err = send_signal(PIDFD_SELF_THREAD_GROUP);	if (err)		ksft_exit_fail_msg(			"%s test: Error %d on PIDFD_SELF_THREAD_GROUP signal\n",			test_name, err); 	/*	 * Now try the same thing in a thread and assert thread ID is equal to	 * worker thread ID.	 */	if (pthread_create(&thread, NULL, send_signal_worker,			   (void *)(intptr_t)PIDFD_SELF_THREAD))		ksft_exit_fail_msg("%s test: Failed to create thread\n",				   test_name);	if (pthread_join(thread, &thread_res))		ksft_exit_fail_msg("%s test: Failed to join thread\n",				   test_name);	err = (int)(intptr_t)thread_res;	if (err)		ksft_exit_fail_msg(			"%s test: Error %d on PIDFD_SELF_THREAD signal\n",			test_name, err); 	ksft_test_result_pass("%s test: Sent signal\n", test_name);	return 0;} static int test_pidfd_send_signal_exited_fail(void){	int pidfd, ret, saved_errno;	char buf[256];	pid_t pid;	const char *test_name = "pidfd_send_signal signal exited process"; 	if (!have_pidfd_send_signal) {		ksft_test_result_skip(			"%s test: pidfd_send_signal() syscall not supported\n",			test_name);		return 0;	} 	pid = fork();	if (pid < 0)		ksft_exit_fail_msg("%s test: Failed to create new process\n",				   test_name); 	if (pid == 0)		_exit(EXIT_SUCCESS); 	snprintf(buf, sizeof(buf), "/proc/%d", pid); 	pidfd = open(buf, O_DIRECTORY | O_CLOEXEC); 	ret = wait_for_pid(pid);	ksft_print_msg("waitpid WEXITSTATUS=%d\n", ret); 	if (pidfd < 0)		ksft_exit_fail_msg(			"%s test: Failed to open process file descriptor\n",			test_name); 	ret = sys_pidfd_send_signal(pidfd, 0, NULL, 0);	saved_errno = errno;	close(pidfd);	if (ret == 0)		ksft_exit_fail_msg(			"%s test: Managed to send signal to process even though it should have failed\n",			test_name); 	if (saved_errno != ESRCH)		ksft_exit_fail_msg(			"%s test: Expected to receive ESRCH as errno value but received %d instead\n",			test_name, saved_errno); 	ksft_test_result_pass("%s test: Failed to send signal as expected\n",			      test_name);	return 0;} /* * Maximum number of cycles we allow. This is equivalent to PID_MAX_DEFAULT. * If users set a higher limit or we have cycled PIDFD_MAX_DEFAULT number of * times then we skip the test to not go into an infinite loop or block for a * long time. */#define PIDFD_MAX_DEFAULT 0x8000 static int test_pidfd_send_signal_recycled_pid_fail(void){	int i, ret;	pid_t pid1;	const char *test_name = "pidfd_send_signal signal recycled pid"; 	if (!have_pidfd_send_signal) {		ksft_test_result_skip(			"%s test: pidfd_send_signal() syscall not supported\n",			test_name);		return 0;	} 	ret = unshare(CLONE_NEWPID);	if (ret < 0) {		if (errno == EPERM) {			ksft_test_result_skip("%s test: Unsharing pid namespace not permitted\n",					      test_name);			return 0;		}		ksft_exit_fail_msg("%s test: Failed to unshare pid namespace\n",				   test_name);	} 	ret = unshare(CLONE_NEWNS);	if (ret < 0) {		if (errno == EPERM) {			ksft_test_result_skip("%s test: Unsharing mount namespace not permitted\n",					      test_name);			return 0;		}		ksft_exit_fail_msg("%s test: Failed to unshare mount namespace\n",				   test_name);	} 	ret = mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0);	if (ret < 0)		ksft_exit_fail_msg("%s test: Failed to remount / private\n",				   test_name); 	/* pid 1 in new pid namespace */	pid1 = fork();	if (pid1 < 0)		ksft_exit_fail_msg("%s test: Failed to create new process\n",				   test_name); 	if (pid1 == 0) {		char buf[256];		pid_t pid2;		int pidfd = -1; 		(void)umount2("/proc", MNT_DETACH);		ret = mount("proc", "/proc", "proc", 0, NULL);		if (ret < 0)			_exit(PIDFD_ERROR); 		/* grab pid PID_RECYCLE */		for (i = 0; i <= PIDFD_MAX_DEFAULT; i++) {			pid2 = fork();			if (pid2 < 0)				_exit(PIDFD_ERROR); 			if (pid2 == 0)				_exit(PIDFD_PASS); 			if (pid2 == PID_RECYCLE) {				snprintf(buf, sizeof(buf), "/proc/%d", pid2);				ksft_print_msg("pid to recycle is %d\n", pid2);				pidfd = open(buf, O_DIRECTORY | O_CLOEXEC);			} 			if (wait_for_pid(pid2))				_exit(PIDFD_ERROR); 			if (pid2 >= PID_RECYCLE)				break;		} 		/*		 * We want to be as predictable as we can so if we haven't been		 * able to grab pid PID_RECYCLE skip the test.		 */		if (pid2 != PID_RECYCLE) {			/* skip test */			close(pidfd);			_exit(PIDFD_SKIP);		} 		if (pidfd < 0)			_exit(PIDFD_ERROR); 		for (i = 0; i <= PIDFD_MAX_DEFAULT; i++) {			char c;			int pipe_fds[2];			pid_t recycled_pid;			int child_ret = PIDFD_PASS; 			ret = pipe2(pipe_fds, O_CLOEXEC);			if (ret < 0)				_exit(PIDFD_ERROR); 			recycled_pid = fork();			if (recycled_pid < 0)				_exit(PIDFD_ERROR); 			if (recycled_pid == 0) {				close(pipe_fds[1]);				(void)read(pipe_fds[0], &c, 1);				close(pipe_fds[0]); 				_exit(PIDFD_PASS);			} 			/*			 * Stop the child so we can inspect whether we have			 * recycled pid PID_RECYCLE.			 */			close(pipe_fds[0]);			ret = kill(recycled_pid, SIGSTOP);			close(pipe_fds[1]);			if (ret) {				(void)wait_for_pid(recycled_pid);				_exit(PIDFD_ERROR);			} 			/*			 * We have recycled the pid. Try to signal it. This			 * needs to fail since this is a different process than			 * the one the pidfd refers to.			 */			if (recycled_pid == PID_RECYCLE) {				ret = sys_pidfd_send_signal(pidfd, SIGCONT,							    NULL, 0);				if (ret && errno == ESRCH)					child_ret = PIDFD_XFAIL;				else					child_ret = PIDFD_FAIL;			} 			/* let the process move on */			ret = kill(recycled_pid, SIGCONT);			if (ret)				(void)kill(recycled_pid, SIGKILL); 			if (wait_for_pid(recycled_pid))				_exit(PIDFD_ERROR); 			switch (child_ret) {			case PIDFD_FAIL:				/* fallthrough */			case PIDFD_XFAIL:				_exit(child_ret);			case PIDFD_PASS:				break;			default:				/* not reached */				_exit(PIDFD_ERROR);			} 			/*			 * If the user set a custom pid_max limit we could be			 * in the millions.			 * Skip the test in this case.			 */			if (recycled_pid > PIDFD_MAX_DEFAULT)				_exit(PIDFD_SKIP);		} 		/* failed to recycle pid */		_exit(PIDFD_SKIP);	} 	ret = wait_for_pid(pid1);	switch (ret) {	case PIDFD_FAIL:		ksft_exit_fail_msg(			"%s test: Managed to signal recycled pid %d\n",			test_name, PID_RECYCLE);	case PIDFD_PASS:		ksft_exit_fail_msg("%s test: Failed to recycle pid %d\n",				   test_name, PID_RECYCLE);	case PIDFD_SKIP:		ksft_test_result_skip("%s test: Skipping test\n", test_name);		ret = 0;		break;	case PIDFD_XFAIL:		ksft_test_result_pass(			"%s test: Failed to signal recycled pid as expected\n",			test_name);		ret = 0;		break;	default /* PIDFD_ERROR */:		ksft_exit_fail_msg("%s test: Error while running tests\n",				   test_name);	} 	return ret;} static int test_pidfd_send_signal_syscall_support(void){	int pidfd, ret;	const char *test_name = "pidfd_send_signal check for support"; 	pidfd = open("/proc/self", O_DIRECTORY | O_CLOEXEC);	if (pidfd < 0)		ksft_exit_fail_msg(			"%s test: Failed to open process file descriptor\n",			test_name); 	ret = sys_pidfd_send_signal(pidfd, 0, NULL, 0);	if (ret < 0) {		if (errno == ENOSYS) {			ksft_test_result_skip(				"%s test: pidfd_send_signal() syscall not supported\n",				test_name);			return 0;		}		ksft_exit_fail_msg("%s test: Failed to send signal\n",				   test_name);	} 	have_pidfd_send_signal = true;	close(pidfd);	ksft_test_result_pass(		"%s test: pidfd_send_signal() syscall is supported. Tests can be executed\n",		test_name);	return 0;} static void *test_pidfd_poll_exec_thread(void *priv){	ksft_print_msg("Child Thread: starting. pid %d tid %ld ; and sleeping\n",			getpid(), syscall(SYS_gettid));	ksft_print_msg("Child Thread: doing exec of sleep\n"); 	execl("/bin/sleep", "sleep", str(CHILD_THREAD_MIN_WAIT), (char *)NULL); 	ksft_print_msg("Child Thread: DONE. pid %d tid %ld\n",			getpid(), syscall(SYS_gettid));	return NULL;} static void poll_pidfd(const char *test_name, int pidfd){	int c;	int epoll_fd = epoll_create1(EPOLL_CLOEXEC);	struct epoll_event event, events[MAX_EVENTS]; 	if (epoll_fd == -1)		ksft_exit_fail_msg("%s test: Failed to create epoll file descriptor "				   "(errno %d)\n",				   test_name, errno); 	event.events = EPOLLIN;	event.data.fd = pidfd; 	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pidfd, &event)) {		ksft_exit_fail_msg("%s test: Failed to add epoll file descriptor "				   "(errno %d)\n",				   test_name, errno);	} 	c = epoll_wait(epoll_fd, events, MAX_EVENTS, 5000);	if (c != 1 || !(events[0].events & EPOLLIN))		ksft_exit_fail_msg("%s test: Unexpected epoll_wait result (c=%d, events=%x) "				   "(errno %d)\n",				   test_name, c, events[0].events, errno); 	close(epoll_fd);	return; } static int child_poll_exec_test(void *args){	pthread_t t1; 	ksft_print_msg("Child (pidfd): starting. pid %d tid %ld\n", getpid(),			syscall(SYS_gettid));	pthread_create(&t1, NULL, test_pidfd_poll_exec_thread, NULL);	/*	 * Exec in the non-leader thread will destroy the leader immediately.	 * If the wait in the parent returns too soon, the test fails.	 */	while (1)		sleep(1); 	return 0;} static void test_pidfd_poll_exec(int use_waitpid){	int pid, pidfd = 0;	int status, ret;	time_t prog_start = time(NULL);	const char *test_name = "pidfd_poll check for premature notification on child thread exec";

Lines 1–500 of 628.