1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
/* Basic tests for Linux pidfd interfaces.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <errno.h>
#include <fcntl.h>
#include <support/capture_subprocess.h>
#include <support/check.h>
#include <support/process_state.h>
#include <support/support.h>
#include <support/xsignal.h>
#include <support/xunistd.h>
#include <support/xsocket.h>
#include <sys/pidfd.h>
#include <sys/wait.h>
#define REMOTE_PATH "/dev/null"
/* The pair of sockets used for coordination. The subprocess uses
sockets[1]. */
static int sockets[2];
static pid_t ppid;
static uid_t puid;
static void
sighandler (int sig)
{
}
static void
subprocess (void)
{
xsignal (SIGUSR1, sighandler);
xsignal (SIGUSR2, sighandler);
/* Check first pidfd_send_signal with default NULL siginfo_t argument. */
{
sigset_t set;
sigemptyset (&set);
sigaddset (&set, SIGUSR1);
siginfo_t info;
TEST_COMPARE (sigtimedwait (&set, &info, NULL), SIGUSR1);
TEST_COMPARE (info.si_signo, SIGUSR1);
TEST_COMPARE (info.si_errno, 0);
TEST_COMPARE (info.si_code, SI_USER);
TEST_COMPARE (info.si_pid, ppid);
TEST_COMPARE (info.si_uid, puid);
}
/* Check second pidfd_send_signal with crafted siginfo_t argument. */
{
sigset_t set;
sigemptyset (&set);
sigaddset (&set, SIGUSR2);
siginfo_t info;
TEST_COMPARE (sigtimedwait (&set, &info, NULL), SIGUSR2);
TEST_COMPARE (info.si_signo, SIGUSR2);
TEST_COMPARE (info.si_errno, EAGAIN);
TEST_COMPARE (info.si_code, -10);
TEST_COMPARE (info.si_pid, ppid);
TEST_COMPARE (info.si_uid, puid);
}
/* Send a local file descriptor value to check pidfd_getfd. */
int remote_fd = xopen (REMOTE_PATH, O_WRONLY | O_CLOEXEC, 0);
xsendto (sockets[1], &remote_fd, sizeof (remote_fd), 0, NULL, 0);
/* Wait for final pidfd_send_signal. */
pause ();
_exit (0);
}
static int
do_test (void)
{
{
/* The pidfd_getfd syscall was the last in the set of pidfd related
syscalls added to the kernel. Use pidfd_getfd to decide if this
kernel has pidfd support that we can test. */
int r = pidfd_getfd (0, 0, 1);
TEST_VERIFY_EXIT (r == -1);
if (errno == ENOSYS)
FAIL_UNSUPPORTED ("kernel does not support pidfd_getfd, skipping test");
}
ppid = getpid ();
puid = getuid ();
TEST_COMPARE (socketpair (AF_UNIX, SOCK_STREAM, 0, sockets), 0);
pid_t pid = xfork ();
if (pid == 0)
{
xclose (sockets[0]);
subprocess ();
}
xclose (sockets[1]);
TEST_COMPARE (pidfd_open (-1, 0), -1);
TEST_COMPARE (errno, EINVAL);
int pidfd = pidfd_open (pid, 0);
TEST_VERIFY (pidfd != -1);
/* Wait for first sigtimedwait. */
support_process_state_wait (pid, support_process_state_sleeping);
TEST_COMPARE (pidfd_send_signal (pidfd, SIGUSR1, NULL, 0), 0);
/* Wait for second sigtimedwait. */
support_process_state_wait (pid, support_process_state_sleeping);
{
siginfo_t info =
{
.si_signo = SIGUSR2,
.si_errno = EAGAIN,
.si_code = -10,
.si_pid = ppid,
.si_uid = puid
};
TEST_COMPARE (pidfd_send_signal (pidfd, SIGUSR2, &info, 0), 0);
}
/* Get remote file descriptor to check for pidfd_getfd. */
{
int remote_fd;
xrecvfrom (sockets[0], &remote_fd, sizeof (remote_fd), 0, NULL, 0);
int fd = pidfd_getfd (pidfd, remote_fd, 0);
/* pidfd_getfd may fail with EPERM if the process does not have
PTRACE_MODE_ATTACH_REALCREDS permissions. This means the call
may be denied if the process doesn't have CAP_SYS_PTRACE or
if a LSM security_ptrace_access_check denies access. */
if (fd == -1 && errno == EPERM)
{
TEST_COMPARE (pidfd_send_signal (pidfd, SIGKILL, NULL, 0), 0);
FAIL_UNSUPPORTED ("don't have permission to use pidfd_getfd on pidfd, "
"skipping test");
}
TEST_VERIFY (fd > 0);
char *path = xasprintf ("/proc/%d/fd/%d", pid, remote_fd);
char *resolved = xreadlink (path);
TEST_COMPARE_STRING (resolved, REMOTE_PATH);
int remote_fd_mode = fcntl (fd, F_GETFL);
TEST_VERIFY (remote_fd_mode != -1);
TEST_VERIFY (remote_fd_mode & O_WRONLY);
int remote_fd_flags = fcntl (fd, F_GETFD);
TEST_VERIFY (remote_fd_flags != -1);
TEST_VERIFY (remote_fd_flags & FD_CLOEXEC);
}
TEST_COMPARE (pidfd_send_signal (pidfd, SIGKILL, NULL, 0), 0);
{
siginfo_t info;
int r = waitid (P_PIDFD, pidfd, &info, WEXITED);
TEST_COMPARE (r, 0);
TEST_COMPARE (info.si_status, SIGKILL);
TEST_COMPARE (info.si_code, CLD_KILLED);
}
return 0;
}
#include <support/test-driver.c>
|