234 lines · c
1/* SPDX-License-Identifier: GPL-2.0 */2 3#define _GNU_SOURCE4#include <errno.h>5#include <linux/sched.h>6#include <linux/types.h>7#include <signal.h>8#include <stdint.h>9#include <stdio.h>10#include <stdlib.h>11#include <sched.h>12#include <string.h>13#include <sys/resource.h>14#include <sys/time.h>15#include <sys/types.h>16#include <sys/wait.h>17#include <unistd.h>18 19#include "pidfd.h"20#include "../kselftest_harness.h"21 22#define ptr_to_u64(ptr) ((__u64)((uintptr_t)(ptr)))23 24/* Attempt to de-conflict with the selftests tree. */25#ifndef SKIP26#define SKIP(s, ...) XFAIL(s, ##__VA_ARGS__)27#endif28 29static pid_t sys_clone3(struct clone_args *args)30{31 return syscall(__NR_clone3, args, sizeof(struct clone_args));32}33 34static int sys_waitid(int which, pid_t pid, siginfo_t *info, int options,35 struct rusage *ru)36{37 return syscall(__NR_waitid, which, pid, info, options, ru);38}39 40TEST(wait_simple)41{42 int pidfd = -1;43 pid_t parent_tid = -1;44 struct clone_args args = {45 .parent_tid = ptr_to_u64(&parent_tid),46 .pidfd = ptr_to_u64(&pidfd),47 .flags = CLONE_PIDFD | CLONE_PARENT_SETTID,48 .exit_signal = SIGCHLD,49 };50 pid_t pid;51 siginfo_t info = {52 .si_signo = 0,53 };54 55 pidfd = open("/proc/self", O_DIRECTORY | O_RDONLY | O_CLOEXEC);56 ASSERT_GE(pidfd, 0);57 58 pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);59 ASSERT_NE(pid, 0);60 EXPECT_EQ(close(pidfd), 0);61 pidfd = -1;62 63 pidfd = open("/dev/null", O_RDONLY | O_CLOEXEC);64 ASSERT_GE(pidfd, 0);65 66 pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);67 ASSERT_NE(pid, 0);68 EXPECT_EQ(close(pidfd), 0);69 pidfd = -1;70 71 pid = sys_clone3(&args);72 ASSERT_GE(pid, 0);73 74 if (pid == 0)75 exit(EXIT_SUCCESS);76 77 pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);78 ASSERT_GE(pid, 0);79 ASSERT_EQ(WIFEXITED(info.si_status), true);80 ASSERT_EQ(WEXITSTATUS(info.si_status), 0);81 EXPECT_EQ(close(pidfd), 0);82 83 ASSERT_EQ(info.si_signo, SIGCHLD);84 ASSERT_EQ(info.si_code, CLD_EXITED);85 ASSERT_EQ(info.si_pid, parent_tid);86}87 88TEST(wait_states)89{90 int pidfd = -1;91 pid_t parent_tid = -1;92 struct clone_args args = {93 .parent_tid = ptr_to_u64(&parent_tid),94 .pidfd = ptr_to_u64(&pidfd),95 .flags = CLONE_PIDFD | CLONE_PARENT_SETTID,96 .exit_signal = SIGCHLD,97 };98 int pfd[2];99 pid_t pid;100 siginfo_t info = {101 .si_signo = 0,102 };103 104 ASSERT_EQ(pipe(pfd), 0);105 pid = sys_clone3(&args);106 ASSERT_GE(pid, 0);107 108 if (pid == 0) {109 char buf[2];110 111 close(pfd[1]);112 kill(getpid(), SIGSTOP);113 ASSERT_EQ(read(pfd[0], buf, 1), 1);114 close(pfd[0]);115 kill(getpid(), SIGSTOP);116 exit(EXIT_SUCCESS);117 }118 119 close(pfd[0]);120 ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WSTOPPED, NULL), 0);121 ASSERT_EQ(info.si_signo, SIGCHLD);122 ASSERT_EQ(info.si_code, CLD_STOPPED);123 ASSERT_EQ(info.si_pid, parent_tid);124 125 ASSERT_EQ(sys_pidfd_send_signal(pidfd, SIGCONT, NULL, 0), 0);126 127 ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WCONTINUED, NULL), 0);128 ASSERT_EQ(write(pfd[1], "C", 1), 1);129 close(pfd[1]);130 ASSERT_EQ(info.si_signo, SIGCHLD);131 ASSERT_EQ(info.si_code, CLD_CONTINUED);132 ASSERT_EQ(info.si_pid, parent_tid);133 134 ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WUNTRACED, NULL), 0);135 ASSERT_EQ(info.si_signo, SIGCHLD);136 ASSERT_EQ(info.si_code, CLD_STOPPED);137 ASSERT_EQ(info.si_pid, parent_tid);138 139 ASSERT_EQ(sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0), 0);140 141 ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL), 0);142 ASSERT_EQ(info.si_signo, SIGCHLD);143 ASSERT_EQ(info.si_code, CLD_KILLED);144 ASSERT_EQ(info.si_pid, parent_tid);145 146 EXPECT_EQ(close(pidfd), 0);147}148 149TEST(wait_nonblock)150{151 int pidfd;152 unsigned int flags = 0;153 pid_t parent_tid = -1;154 struct clone_args args = {155 .parent_tid = ptr_to_u64(&parent_tid),156 .flags = CLONE_PARENT_SETTID,157 .exit_signal = SIGCHLD,158 };159 int ret;160 pid_t pid;161 siginfo_t info = {162 .si_signo = 0,163 };164 165 /*166 * Callers need to see ECHILD with non-blocking pidfds when no child167 * processes exists.168 */169 pidfd = sys_pidfd_open(getpid(), PIDFD_NONBLOCK);170 EXPECT_GE(pidfd, 0) {171 /* pidfd_open() doesn't support PIDFD_NONBLOCK. */172 ASSERT_EQ(errno, EINVAL);173 SKIP(return, "Skipping PIDFD_NONBLOCK test");174 }175 176 ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);177 ASSERT_LT(ret, 0);178 ASSERT_EQ(errno, ECHILD);179 EXPECT_EQ(close(pidfd), 0);180 181 pid = sys_clone3(&args);182 ASSERT_GE(pid, 0);183 184 if (pid == 0) {185 kill(getpid(), SIGSTOP);186 exit(EXIT_SUCCESS);187 }188 189 pidfd = sys_pidfd_open(pid, PIDFD_NONBLOCK);190 EXPECT_GE(pidfd, 0) {191 /* pidfd_open() doesn't support PIDFD_NONBLOCK. */192 ASSERT_EQ(errno, EINVAL);193 SKIP(return, "Skipping PIDFD_NONBLOCK test");194 }195 196 flags = fcntl(pidfd, F_GETFL, 0);197 ASSERT_GT(flags, 0);198 ASSERT_GT((flags & O_NONBLOCK), 0);199 200 /*201 * Callers need to see EAGAIN/EWOULDBLOCK with non-blocking pidfd when202 * child processes exist but none have exited.203 */204 ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);205 ASSERT_LT(ret, 0);206 ASSERT_EQ(errno, EAGAIN);207 208 /*209 * Callers need to continue seeing 0 with non-blocking pidfd and210 * WNOHANG raised explicitly when child processes exist but none have211 * exited.212 */213 ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED | WNOHANG, NULL);214 ASSERT_EQ(ret, 0);215 216 ASSERT_EQ(fcntl(pidfd, F_SETFL, (flags & ~O_NONBLOCK)), 0);217 218 ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WSTOPPED, NULL), 0);219 ASSERT_EQ(info.si_signo, SIGCHLD);220 ASSERT_EQ(info.si_code, CLD_STOPPED);221 ASSERT_EQ(info.si_pid, parent_tid);222 223 ASSERT_EQ(sys_pidfd_send_signal(pidfd, SIGCONT, NULL, 0), 0);224 225 ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL), 0);226 ASSERT_EQ(info.si_signo, SIGCHLD);227 ASSERT_EQ(info.si_code, CLD_EXITED);228 ASSERT_EQ(info.si_pid, parent_tid);229 230 EXPECT_EQ(close(pidfd), 0);231}232 233TEST_HARNESS_MAIN234