Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F162319803
D58023.id181356.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
12 KB
Referenced Files
None
Subscribers
None
D58023.id181356.diff
View Options
diff --git a/tests/sys/kern/procdesc.c b/tests/sys/kern/procdesc.c
--- a/tests/sys/kern/procdesc.c
+++ b/tests/sys/kern/procdesc.c
@@ -27,16 +27,21 @@
*/
#include <sys/types.h>
+#include <sys/capsicum.h>
+#include <sys/resource.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/procdesc.h>
#include <sys/sysctl.h>
#include <sys/wait.h>
+#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <pthread.h>
+#include <signal.h>
#include <stdio.h>
+#include <string.h>
#include <unistd.h>
#include <atf-c.h>
@@ -205,11 +210,471 @@
ATF_REQUIRE_MSG(close(pd) == 0, "close: %s", strerror(errno));
}
+/* Tests for pdopenpid(2) */
+
+/*
+ * Basic: open a process descriptor for a child, verify pdgetpid returns the
+ * right pid.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_basic);
+ATF_TC_BODY(pdopenpid_basic, tc)
+{
+ pid_t child, queried;
+ int fd;
+
+ child = fork();
+ ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
+ if (child == 0) {
+ for (;;)
+ pause();
+ _exit(0);
+ }
+
+ fd = pdopenpid(child, 0);
+ ATF_REQUIRE_MSG(fd >= 0, "pdopenpid: %s", strerror(errno));
+
+ ATF_REQUIRE_MSG(pdgetpid(fd, &queried) == 0,
+ "pdgetpid: %s", strerror(errno));
+ ATF_REQUIRE_EQ(child, queried);
+
+ ATF_REQUIRE_MSG(pdkill(fd, SIGKILL) == 0,
+ "pdkill: %s", strerror(errno));
+ ATF_REQUIRE_EQ(child, waitpid(child, NULL, 0));
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * pdopenpid with PD_CLOEXEC should set the close-on-exec flag.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_cloexec);
+ATF_TC_BODY(pdopenpid_cloexec, tc)
+{
+ pid_t child;
+ int fd, flags;
+
+ child = fork();
+ ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
+ if (child == 0) {
+ for (;;)
+ pause();
+ _exit(0);
+ }
+
+ fd = pdopenpid(child, PD_CLOEXEC);
+ ATF_REQUIRE_MSG(fd >= 0, "pdopenpid: %s", strerror(errno));
+
+ flags = fcntl(fd, F_GETFD);
+ ATF_REQUIRE(flags >= 0);
+ ATF_REQUIRE(flags & FD_CLOEXEC);
+
+ ATF_REQUIRE_MSG(pdkill(fd, SIGKILL) == 0,
+ "pdkill: %s", strerror(errno));
+ ATF_REQUIRE_EQ(child, waitpid(child, NULL, 0));
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Invalid flags should return EINVAL.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_einval);
+ATF_TC_BODY(pdopenpid_einval, tc)
+{
+ ATF_REQUIRE_ERRNO(EINVAL, pdopenpid(getpid(), 0xdeadbeef) < 0);
+ ATF_REQUIRE_ERRNO(EINVAL, pdopenpid(getpid(), ~PD_ALLOWED_AT_FORK) < 0);
+}
+
+/*
+ * Validate handling of EMFILE.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_emfile);
+ATF_TC_BODY(pdopenpid_emfile, tc)
+{
+ pid_t child;
+ struct rlimit rl;
+ int fd;
+
+ child = fork();
+ ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
+ if (child == 0) {
+ pause();
+ _exit(0);
+ }
+
+ /*
+ * Determine the lowest unused fd, then set the fd limit to that
+ * value so that no new fds can be allocated.
+ */
+ fd = dup(STDIN_FILENO);
+ ATF_REQUIRE(fd >= 0);
+ ATF_REQUIRE(close(fd) == 0);
+
+ ATF_REQUIRE_EQ(getrlimit(RLIMIT_NOFILE, &rl), 0);
+ rl.rlim_cur = fd;
+ ATF_REQUIRE_EQ(setrlimit(RLIMIT_NOFILE, &rl), 0);
+
+ ATF_REQUIRE_ERRNO(EMFILE, pdopenpid(child, 0) < 0);
+
+ /*
+ * The child should not have been killed or reparented as a side
+ * effect of the failed syscall.
+ */
+ ATF_REQUIRE_MSG(kill(child, 0) == 0,
+ "child was killed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(waitpid(child, NULL, WNOHANG) == 0,
+ "child was reparented");
+
+ ATF_REQUIRE_MSG(kill(child, SIGKILL) == 0,
+ "kill: %s", strerror(errno));
+ ATF_REQUIRE_EQ(child, waitpid(child, NULL, 0));
+}
+
+/*
+ * Opening a nonexistent pid should return ESRCH.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_esrch);
+ATF_TC_BODY(pdopenpid_esrch, tc)
+{
+ ATF_REQUIRE_ERRNO(ESRCH, pdopenpid(123456789, 0) < 0);
+}
+
+/*
+ * pdopenpid should fail in capability mode.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_capmode);
+ATF_TC_BODY(pdopenpid_capmode, tc)
+{
+ pid_t child, parent;
+
+ parent = getpid();
+ child = fork();
+ ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
+ if (child == 0) {
+ while (getppid() == parent)
+ sleep(1);
+ _exit(0);
+ }
+
+ ATF_REQUIRE_MSG(cap_enter() == 0, "cap_enter: %s", strerror(errno));
+ ATF_REQUIRE_ERRNO(ECAPMODE, pdopenpid(child, 0) < 0);
+}
+
+/*
+ * Open a process descriptor for a child that already has one from pdfork.
+ * Both fds should refer to the same process.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_pdfork_then_open);
+ATF_TC_BODY(pdopenpid_pdfork_then_open, tc)
+{
+ pid_t child, pid1, pid2;
+ int fd1, fd2;
+
+ child = pdfork(&fd1, PD_DAEMON);
+ ATF_REQUIRE_MSG(child >= 0, "pdfork: %s", strerror(errno));
+ if (child == 0) {
+ for (;;)
+ pause();
+ _exit(0);
+ }
+
+ fd2 = pdopenpid(child, 0);
+ ATF_REQUIRE_MSG(fd2 >= 0, "pdopenpid: %s", strerror(errno));
+
+ ATF_REQUIRE(pdgetpid(fd1, &pid1) == 0);
+ ATF_REQUIRE(pdgetpid(fd2, &pid2) == 0);
+ ATF_REQUIRE_EQ(pid1, pid2);
+ ATF_REQUIRE_EQ(child, pid1);
+
+ /* Kill via the second fd, wait via the first. */
+ ATF_REQUIRE_MSG(pdkill(fd2, SIGKILL) == 0,
+ "pdkill: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pdwait(fd1, NULL, WEXITED, NULL, NULL) == 0,
+ "pdwait: %s", strerror(errno));
+
+ ATF_REQUIRE(close(fd1) == 0);
+ ATF_REQUIRE(close(fd2) == 0);
+}
+
+/*
+ * Open a process descriptor for a child created with fork(), which has no
+ * pre-existing procdesc. Then use pdkill() and pdwait() on it.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_fork_then_open);
+ATF_TC_BODY(pdopenpid_fork_then_open, tc)
+{
+ pid_t child;
+ int fd, status;
+
+ child = fork();
+ ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
+ if (child == 0) {
+ for (;;)
+ pause();
+ _exit(0);
+ }
+
+ fd = pdopenpid(child, 0);
+ ATF_REQUIRE_MSG(fd >= 0, "pdopenpid: %s", strerror(errno));
+
+ ATF_REQUIRE_MSG(pdkill(fd, SIGKILL) == 0,
+ "pdkill: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pdwait(fd, &status, WEXITED, NULL, NULL) == 0,
+ "pdwait: %s", strerror(errno));
+ ATF_REQUIRE(WIFSIGNALED(status));
+ ATF_REQUIRE_EQ(WTERMSIG(status), SIGKILL);
+
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Closing one fd should not kill the process when another fd still references
+ * the same procdesc.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_close_one_of_two);
+ATF_TC_BODY(pdopenpid_close_one_of_two, tc)
+{
+ pid_t child, queried;
+ int fd1, fd2, status;
+
+ child = pdfork(&fd1, PD_DAEMON);
+ ATF_REQUIRE_MSG(child >= 0, "pdfork: %s", strerror(errno));
+ if (child == 0) {
+ for (;;)
+ pause();
+ _exit(0);
+ }
+
+ fd2 = pdopenpid(child, 0);
+ ATF_REQUIRE_MSG(fd2 >= 0, "pdopenpid: %s", strerror(errno));
+
+ /* Close the first fd; the process should remain alive. */
+ ATF_REQUIRE(close(fd1) == 0);
+
+ /*
+ * Verify the process is still reachable via the second fd and still
+ * alive.
+ */
+ ATF_REQUIRE_EQ(0, pdgetpid(fd2, &queried));
+ ATF_REQUIRE_EQ(child, queried);
+ ATF_REQUIRE_MSG(pdkill(fd2, 0) == 0,
+ "pdkill(0) after closing first fd: %s", strerror(errno));
+
+ /* Now kill and reap via the second fd. */
+ ATF_REQUIRE_MSG(pdkill(fd2, SIGKILL) == 0,
+ "pdkill: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pdwait(fd2, &status, WEXITED, NULL, NULL) == 0,
+ "pdwait: %s", strerror(errno));
+ ATF_REQUIRE(WIFSIGNALED(status));
+ ATF_REQUIRE_EQ(WTERMSIG(status), SIGKILL);
+
+ ATF_REQUIRE(close(fd2) == 0);
+}
+
+/*
+ * Two calls to pdopenpid for the same process (no pdfork) should both work.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_open_twice);
+ATF_TC_BODY(pdopenpid_open_twice, tc)
+{
+ pid_t child, pid1, pid2;
+ int fd1, fd2;
+
+ child = fork();
+ ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
+ if (child == 0) {
+ for (;;)
+ pause();
+ _exit(0);
+ }
+
+ fd1 = pdopenpid(child, 0);
+ ATF_REQUIRE_MSG(fd1 >= 0, "pdopenpid(1): %s", strerror(errno));
+
+ fd2 = pdopenpid(child, 0);
+ ATF_REQUIRE_MSG(fd2 >= 0, "pdopenpid(2): %s", strerror(errno));
+
+ ATF_REQUIRE_EQ(0, pdgetpid(fd1, &pid1));
+ ATF_REQUIRE_EQ(0, pdgetpid(fd2, &pid2));
+ ATF_REQUIRE_EQ(pid1, pid2);
+
+ /* Close the first, process should survive. */
+ ATF_REQUIRE(close(fd1) == 0);
+ ATF_REQUIRE_MSG(pdkill(fd2, 0) == 0,
+ "pdkill(0) after closing first fd: %s", strerror(errno));
+
+ ATF_REQUIRE_MSG(pdkill(fd2, SIGKILL) == 0,
+ "pdkill: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pdwait(fd2, NULL, WEXITED, NULL, NULL) == 0,
+ "pdwait: %s", strerror(errno));
+ ATF_REQUIRE(close(fd2) == 0);
+}
+
+/*
+ * When a process with two procdesc fds exits, only one pdwait should succeed
+ * in collecting the exit status. The other should get ESRCH.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_pdwait_only_one);
+ATF_TC_BODY(pdopenpid_pdwait_only_one, tc)
+{
+ pid_t child;
+ int fd1, fd2, status;
+
+ child = pdfork(&fd1, PD_DAEMON);
+ ATF_REQUIRE_MSG(child >= 0, "pdfork: %s", strerror(errno));
+ if (child == 0)
+ _exit(42);
+
+ fd2 = pdopenpid(child, 0);
+ ATF_REQUIRE_MSG(fd2 >= 0, "pdopenpid: %s", strerror(errno));
+
+ /* Collect via the first fd. */
+ ATF_REQUIRE_MSG(pdwait(fd1, &status, WEXITED, NULL, NULL) == 0,
+ "pdwait(fd1): %s", strerror(errno));
+ ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 42);
+
+ /* The second fd should no longer be able to collect. */
+ ATF_REQUIRE_ERRNO(ESRCH, pdwait(fd2, &status, WEXITED, NULL, NULL) < 0);
+
+ ATF_REQUIRE(close(fd1) == 0);
+ ATF_REQUIRE(close(fd2) == 0);
+}
+
+/*
+ * Opening a process descriptor for ourselves should work.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_self);
+ATF_TC_BODY(pdopenpid_self, tc)
+{
+ pid_t child, queried;
+ int childfd, fd, status;
+
+ /*
+ * Closing self-referencing procdesc would steal the exit
+ * status from the parent. Keep one more procdesc for the
+ * child in the parent so that we can query the state.
+ *
+ * For the same reason we must use fork() and test in the
+ * child, otherwise kyua does not get the SIGCHILD nor the
+ * exit status.
+ */
+ child = pdfork(&childfd, 0);
+ if (child == 0) {
+ fd = pdopenpid(getpid(), PD_DAEMON);
+ if (fd < 0)
+ _exit(21);
+ if (pdgetpid(fd, &queried) == -1)
+ _exit(22);
+ if (getpid() != queried)
+ _exit(23);
+ if (close(fd) != 0)
+ _exit(24);
+ _exit(0);
+ }
+
+ ATF_REQUIRE(pdgetpid(childfd, &queried) == 0);
+ ATF_REQUIRE_EQ(queried, child);
+ ATF_REQUIRE(pdwait(childfd, &status, WEXITED, NULL, NULL) == 0);
+
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
+ ATF_REQUIRE(close(childfd) == 0);
+}
+
+/*
+ * pdopenpid for a process that is exiting (P_WEXIT set) should fail with EBUSY.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_exiting);
+ATF_TC_BODY(pdopenpid_exiting, tc)
+{
+ pid_t child;
+ int fd, pip[2], status;
+
+ ATF_REQUIRE_EQ(pipe(pip), 0);
+
+ child = fork();
+ ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
+ if (child == 0) {
+ char c;
+
+ close(pip[1]);
+ (void)read(pip[0], &c, 1);
+ _exit(0);
+ }
+ ATF_REQUIRE(close(pip[0]) == 0);
+
+ /* Tell the child to exit. */
+ ATF_REQUIRE(close(pip[1]) == 0);
+
+ /* Might still race. */
+ usleep(1000);
+ fd = pdopenpid(child, 0);
+ if (fd >= 0) {
+ ATF_REQUIRE(close(fd) == 0);
+ } else {
+ ATF_REQUIRE_MSG(errno == EBUSY,
+ "unexpected errno %d (%s)", errno, strerror(errno));
+ }
+
+ ATF_REQUIRE(waitpid(child, &status, 0) == child);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
+}
+
+/*
+ * Make sure that opening a process descriptor doesn't cause a wakeup in the
+ * target process.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_no_wakeup);
+ATF_TC_BODY(pdopenpid_no_wakeup, tc)
+{
+ pid_t child;
+ int fd, pip[2];
+
+ ATF_REQUIRE(pipe(pip) == 0);
+
+ child = fork();
+ ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
+ if (child == 0) {
+ char buf[1];
+
+ close(pip[1]);
+ (void)read(pip[0], buf, 1);
+ _exit(0);
+ }
+ close(pip[0]);
+
+ wait_for_naptime(child, "piperd");
+
+ fd = pdopenpid(child, 0);
+ ATF_REQUIRE_MSG(fd >= 0, "pdopenpid: %s", strerror(errno));
+
+ /* If pdopenpid caused a wakeup, read() returned and the child exited. */
+ ATF_REQUIRE_MSG(waitpid(child, NULL, WNOHANG) == 0,
+ "child exited unexpectedly — pdopenpid caused a wakeup");
+
+ close(pip[1]);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, pid_recycle);
ATF_TP_ADD_TC(tp, poll_close_race);
ATF_TP_ADD_TC(tp, poll_exit_wakeup);
+ ATF_TP_ADD_TC(tp, pdopenpid_basic);
+ ATF_TP_ADD_TC(tp, pdopenpid_cloexec);
+ ATF_TP_ADD_TC(tp, pdopenpid_einval);
+ ATF_TP_ADD_TC(tp, pdopenpid_emfile);
+ ATF_TP_ADD_TC(tp, pdopenpid_esrch);
+ ATF_TP_ADD_TC(tp, pdopenpid_capmode);
+ ATF_TP_ADD_TC(tp, pdopenpid_pdfork_then_open);
+ ATF_TP_ADD_TC(tp, pdopenpid_fork_then_open);
+ ATF_TP_ADD_TC(tp, pdopenpid_close_one_of_two);
+ ATF_TP_ADD_TC(tp, pdopenpid_open_twice);
+ ATF_TP_ADD_TC(tp, pdopenpid_pdwait_only_one);
+ ATF_TP_ADD_TC(tp, pdopenpid_self);
+ ATF_TP_ADD_TC(tp, pdopenpid_exiting);
+ ATF_TP_ADD_TC(tp, pdopenpid_no_wakeup);
+
return (atf_no_error());
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Jul 13, 12:47 AM (13 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34800354
Default Alt Text
D58023.id181356.diff (12 KB)
Attached To
Mode
D58023: tests/procdesc: Add some test cases for pdopenpid()
Attached
Detach File
Event Timeline
Log In to Comment