if (sys_pidfd_send_signal(pidfd, SIGUSR1, NULL, 0) < 0) {
ret = -EINVAL; gotoexit;
}
if (signal_received != pthread_self()) {
ret = -EINVAL; gotoexit;
}
exit:
signal_received = 0; return ret;
}
staticvoid *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.
*/ staticint test_pidfd_send_signal_simple_success(void)
{ int pidfd; constchar *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;
}
staticint test_pidfd_send_signal_exited_fail(void)
{ int pidfd, ret, saved_errno; char buf[256];
pid_t pid; constchar *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
staticint test_pidfd_send_signal_recycled_pid_fail(void)
{ int i, ret;
pid_t pid1; constchar *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;
}
staticint test_pidfd_send_signal_syscall_support(void)
{ int pidfd, ret; constchar *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;
}
staticvoid *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");
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;
}
staticvoid test_pidfd_poll_exec(int use_waitpid)
{ int pid, pidfd = 0; int status, ret;
time_t prog_start = time(NULL); constchar *test_name = "pidfd_poll check for premature notification on child thread exec";
/* * glibc exit calls exit_group syscall, so explicitly call exit only * so that only the group leader exits, leaving the threads alone.
*/
*child_exit_secs = time(NULL);
syscall(SYS_exit, 0); /* Never reached, but appeases compiler thinking we should return. */ exit(0);
}
staticvoid test_pidfd_poll_leader_exit(int use_waitpid)
{ int pid, pidfd = 0; int status, ret = 0; constchar *test_name = "pidfd_poll check for premature notification on non-empty" "group leader exit";
ksft_print_msg("Parent: Waiting for Child (%d) to complete.\n", pid);
if (use_waitpid) {
ret = waitpid(pid, &status, 0); if (ret == -1)
ksft_print_msg("Parent: error\n");
} else { /* * This sleep tests for the case where if the child exits, and is in * EXIT_ZOMBIE, but the thread group leader is non-empty, then the poll * doesn't prematurely return even though there are active threads
*/
sleep(1);
poll_pidfd(test_name, pidfd);
}
if (ret == pid)
ksft_print_msg("Parent: Child process waited for.\n");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.