pidfd: add pidfd_wait tests

Add tests for pidfd_wait() and CLONE_WAIT_PID:
- test that waitid(P_PIDFD) fails on /proc/<pid>
- test that waitid(P_PIDFD) fails on /dev/null
- test that waitid(P_PIDFD) can wait on a pidfd
- test that waitid(P_PIDFD) can wait on a pidfd and return siginfo_t
- test that waitid(P_PIDFD) works with WEXITED
- test that waitid(P_PIDFD) works with WSTOPPED
- test that waitid(P_PIDFD) works with WUNTRACED
- test that waitid(P_PIDFD) works with WCONTINUED
- test that waitid(P_PIDFD) works with WNOWAIT
- test that waitid(P_PIDFD)works with WNOHANG

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Joel Fernandes (Google) <joel@joelfernandes.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: David Howells <dhowells@redhat.com>
Cc: Jann Horn <jannh@google.com>
Cc: Andy Lutomirsky <luto@kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Link: https://lore.kernel.org/r/20190727222229.6516-3-christian@brauner.io
This commit is contained in:
Christian Brauner 2019-07-28 00:22:30 +02:00
parent 3695eae5fe
commit e63f308570
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
5 changed files with 298 additions and 15 deletions

View File

@ -1,2 +1,3 @@
pidfd_open_test pidfd_open_test
pidfd_test pidfd_test
pidfd_wait

View File

@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
CFLAGS += -g -I../../../../usr/include/ -lpthread CFLAGS += -g -I../../../../usr/include/ -lpthread
TEST_GEN_PROGS := pidfd_test pidfd_open_test TEST_GEN_PROGS := pidfd_test pidfd_open_test pidfd_wait
include ../lib.mk include ../lib.mk

View File

@ -16,6 +16,26 @@
#include "../kselftest.h" #include "../kselftest.h"
#ifndef P_PIDFD
#define P_PIDFD 3
#endif
#ifndef CLONE_PIDFD
#define CLONE_PIDFD 0x00001000
#endif
#ifndef __NR_pidfd_open
#define __NR_pidfd_open -1
#endif
#ifndef __NR_pidfd_send_signal
#define __NR_pidfd_send_signal -1
#endif
#ifndef __NR_clone3
#define __NR_clone3 -1
#endif
/* /*
* The kernel reserves 300 pids via RESERVED_PIDS in kernel/pid.c * The kernel reserves 300 pids via RESERVED_PIDS in kernel/pid.c
* That means, when it wraps around any pid < 300 will be skipped. * That means, when it wraps around any pid < 300 will be skipped.
@ -53,5 +73,10 @@ again:
return WEXITSTATUS(status); return WEXITSTATUS(status);
} }
static inline int sys_pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
unsigned int flags)
{
return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
}
#endif /* __PIDFD_H */ #endif /* __PIDFD_H */

View File

@ -21,20 +21,12 @@
#include "pidfd.h" #include "pidfd.h"
#include "../kselftest.h" #include "../kselftest.h"
#ifndef __NR_pidfd_send_signal
#define __NR_pidfd_send_signal -1
#endif
#define str(s) _str(s) #define str(s) _str(s)
#define _str(s) #s #define _str(s) #s
#define CHILD_THREAD_MIN_WAIT 3 /* seconds */ #define CHILD_THREAD_MIN_WAIT 3 /* seconds */
#define MAX_EVENTS 5 #define MAX_EVENTS 5
#ifndef CLONE_PIDFD
#define CLONE_PIDFD 0x00001000
#endif
static pid_t pidfd_clone(int flags, int *pidfd, int (*fn)(void *)) static pid_t pidfd_clone(int flags, int *pidfd, int (*fn)(void *))
{ {
size_t stack_size = 1024; size_t stack_size = 1024;
@ -47,12 +39,6 @@ static pid_t pidfd_clone(int flags, int *pidfd, int (*fn)(void *))
#endif #endif
} }
static inline int sys_pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
unsigned int flags)
{
return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
}
static int signal_received; static int signal_received;
static void set_signal_received_on_sigusr1(int sig) static void set_signal_received_on_sigusr1(int sig)

View File

@ -0,0 +1,271 @@
/* SPDX-License-Identifier: GPL-2.0 */
#define _GNU_SOURCE
#include <errno.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "pidfd.h"
#include "../kselftest.h"
#define ptr_to_u64(ptr) ((__u64)((uintptr_t)(ptr)))
static pid_t sys_clone3(struct clone_args *args)
{
return syscall(__NR_clone3, args, sizeof(struct clone_args));
}
static int sys_waitid(int which, pid_t pid, siginfo_t *info, int options,
struct rusage *ru)
{
return syscall(__NR_waitid, which, pid, info, options, ru);
}
static int test_pidfd_wait_simple(void)
{
const char *test_name = "pidfd wait simple";
int pidfd = -1, status = 0;
pid_t parent_tid = -1;
struct clone_args args = {
.parent_tid = ptr_to_u64(&parent_tid),
.pidfd = ptr_to_u64(&pidfd),
.flags = CLONE_PIDFD | CLONE_PARENT_SETTID,
.exit_signal = SIGCHLD,
};
int ret;
pid_t pid;
siginfo_t info = {
.si_signo = 0,
};
pidfd = open("/proc/self", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
if (pidfd < 0)
ksft_exit_fail_msg("%s test: failed to open /proc/self %s\n",
test_name, strerror(errno));
pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
if (pid == 0)
ksft_exit_fail_msg(
"%s test: succeeded to wait on invalid pidfd %s\n",
test_name, strerror(errno));
close(pidfd);
pidfd = -1;
pidfd = open("/dev/null", O_RDONLY | O_CLOEXEC);
if (pidfd == 0)
ksft_exit_fail_msg("%s test: failed to open /dev/null %s\n",
test_name, strerror(errno));
pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
if (pid == 0)
ksft_exit_fail_msg(
"%s test: succeeded to wait on invalid pidfd %s\n",
test_name, strerror(errno));
close(pidfd);
pidfd = -1;
pid = sys_clone3(&args);
if (pid < 0)
ksft_exit_fail_msg("%s test: failed to create new process %s\n",
test_name, strerror(errno));
if (pid == 0)
exit(EXIT_SUCCESS);
pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
if (pid < 0)
ksft_exit_fail_msg(
"%s test: failed to wait on process with pid %d and pidfd %d: %s\n",
test_name, parent_tid, pidfd, strerror(errno));
if (!WIFEXITED(info.si_status) || WEXITSTATUS(info.si_status))
ksft_exit_fail_msg(
"%s test: unexpected status received after waiting on process with pid %d and pidfd %d: %s\n",
test_name, parent_tid, pidfd, strerror(errno));
close(pidfd);
if (info.si_signo != SIGCHLD)
ksft_exit_fail_msg(
"%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
test_name, info.si_signo, parent_tid, pidfd,
strerror(errno));
if (info.si_code != CLD_EXITED)
ksft_exit_fail_msg(
"%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
test_name, info.si_code, parent_tid, pidfd,
strerror(errno));
if (info.si_pid != parent_tid)
ksft_exit_fail_msg(
"%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
test_name, info.si_pid, parent_tid, pidfd,
strerror(errno));
ksft_test_result_pass("%s test: Passed\n", test_name);
return 0;
}
static int test_pidfd_wait_states(void)
{
const char *test_name = "pidfd wait states";
int pidfd = -1, status = 0;
pid_t parent_tid = -1;
struct clone_args args = {
.parent_tid = ptr_to_u64(&parent_tid),
.pidfd = ptr_to_u64(&pidfd),
.flags = CLONE_PIDFD | CLONE_PARENT_SETTID,
.exit_signal = SIGCHLD,
};
int ret;
pid_t pid;
siginfo_t info = {
.si_signo = 0,
};
pid = sys_clone3(&args);
if (pid < 0)
ksft_exit_fail_msg("%s test: failed to create new process %s\n",
test_name, strerror(errno));
if (pid == 0) {
kill(getpid(), SIGSTOP);
kill(getpid(), SIGSTOP);
exit(EXIT_SUCCESS);
}
ret = sys_waitid(P_PIDFD, pidfd, &info, WSTOPPED, NULL);
if (ret < 0)
ksft_exit_fail_msg(
"%s test: failed to wait on WSTOPPED process with pid %d and pidfd %d: %s\n",
test_name, parent_tid, pidfd, strerror(errno));
if (info.si_signo != SIGCHLD)
ksft_exit_fail_msg(
"%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
test_name, info.si_signo, parent_tid, pidfd,
strerror(errno));
if (info.si_code != CLD_STOPPED)
ksft_exit_fail_msg(
"%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
test_name, info.si_code, parent_tid, pidfd,
strerror(errno));
if (info.si_pid != parent_tid)
ksft_exit_fail_msg(
"%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
test_name, info.si_pid, parent_tid, pidfd,
strerror(errno));
ret = sys_pidfd_send_signal(pidfd, SIGCONT, NULL, 0);
if (ret < 0)
ksft_exit_fail_msg(
"%s test: failed to send signal to process with pid %d and pidfd %d: %s\n",
test_name, parent_tid, pidfd, strerror(errno));
ret = sys_waitid(P_PIDFD, pidfd, &info, WCONTINUED, NULL);
if (ret < 0)
ksft_exit_fail_msg(
"%s test: failed to wait WCONTINUED on process with pid %d and pidfd %d: %s\n",
test_name, parent_tid, pidfd, strerror(errno));
if (info.si_signo != SIGCHLD)
ksft_exit_fail_msg(
"%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
test_name, info.si_signo, parent_tid, pidfd,
strerror(errno));
if (info.si_code != CLD_CONTINUED)
ksft_exit_fail_msg(
"%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
test_name, info.si_code, parent_tid, pidfd,
strerror(errno));
if (info.si_pid != parent_tid)
ksft_exit_fail_msg(
"%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
test_name, info.si_pid, parent_tid, pidfd,
strerror(errno));
ret = sys_waitid(P_PIDFD, pidfd, &info, WUNTRACED, NULL);
if (ret < 0)
ksft_exit_fail_msg(
"%s test: failed to wait on WUNTRACED process with pid %d and pidfd %d: %s\n",
test_name, parent_tid, pidfd, strerror(errno));
if (info.si_signo != SIGCHLD)
ksft_exit_fail_msg(
"%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
test_name, info.si_signo, parent_tid, pidfd,
strerror(errno));
if (info.si_code != CLD_STOPPED)
ksft_exit_fail_msg(
"%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
test_name, info.si_code, parent_tid, pidfd,
strerror(errno));
if (info.si_pid != parent_tid)
ksft_exit_fail_msg(
"%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
test_name, info.si_pid, parent_tid, pidfd,
strerror(errno));
ret = sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0);
if (ret < 0)
ksft_exit_fail_msg(
"%s test: failed to send SIGKILL to process with pid %d and pidfd %d: %s\n",
test_name, parent_tid, pidfd, strerror(errno));
ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
if (ret < 0)
ksft_exit_fail_msg(
"%s test: failed to wait on WEXITED process with pid %d and pidfd %d: %s\n",
test_name, parent_tid, pidfd, strerror(errno));
if (info.si_signo != SIGCHLD)
ksft_exit_fail_msg(
"%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
test_name, info.si_signo, parent_tid, pidfd,
strerror(errno));
if (info.si_code != CLD_KILLED)
ksft_exit_fail_msg(
"%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
test_name, info.si_code, parent_tid, pidfd,
strerror(errno));
if (info.si_pid != parent_tid)
ksft_exit_fail_msg(
"%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
test_name, info.si_pid, parent_tid, pidfd,
strerror(errno));
close(pidfd);
ksft_test_result_pass("%s test: Passed\n", test_name);
return 0;
}
int main(int argc, char **argv)
{
ksft_print_header();
ksft_set_plan(2);
test_pidfd_wait_simple();
test_pidfd_wait_states();
return ksft_exit_pass();
}