Message ID | 20220207174431.360355-6-adhemerval.zanella@linaro.org |
---|---|
State | New |
Headers | show |
Series | linux: Add new syscalls | expand |
On 2/7/22 12:44, Adhemerval Zanella via Libc-alpha wrote: > To check for the pidfd functions pidfd_open, pidfd_getfd, pid_send_signal, > and waitid with P_PIDFD. OK with suggested text changes in the test. Reviewed-by: Carlos O'Donell <carlos@redhat.com> Tested-by: Carlos O'Donell <carlos@redhat.com> > Checked on x86_64-linux-gnu and i686-linux-gnu. > --- > sysdeps/unix/sysv/linux/Makefile | 1 + > sysdeps/unix/sysv/linux/tst-pidfd.c | 170 ++++++++++++++++++++++++++++ > 2 files changed, 171 insertions(+) > create mode 100644 sysdeps/unix/sysv/linux/tst-pidfd.c > > diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile > index 1c08f0918f..6fefcd8fe7 100644 > --- a/sysdeps/unix/sysv/linux/Makefile > +++ b/sysdeps/unix/sysv/linux/Makefile > @@ -127,6 +127,7 @@ tests += tst-clone tst-clone2 tst-clone3 tst-fanotify tst-personality \ > tst-prctl \ > tst-scm_rights \ > tst-epoll \ > + tst-pidfd \ OK. New linux-specific test. > # tests > > # Test for the symbol version of fcntl that was replaced in glibc 2.28. > diff --git a/sysdeps/unix/sysv/linux/tst-pidfd.c b/sysdeps/unix/sysv/linux/tst-pidfd.c > new file mode 100644 > index 0000000000..267b071af4 > --- /dev/null > +++ b/sysdeps/unix/sysv/linux/tst-pidfd.c > @@ -0,0 +1,170 @@ > +/* 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; OK. Globals for parent. > + > +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); > + } OK. > + > + /* 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); > + } OK. > + > + /* 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); OK. > + > + /* Wait for final pidfd_send_signal. */ > + pause (); > + > + _exit (0); > +} > + > +static int > +do_test (void) > +{ > + { > + /* The pidfd_getfd was the last one added on Linux. */ Suggest: /* 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_open, skipping test"); Lets be accurate about what we tested and what we say we tested: s/pidfd_open/pidfd_getfd/g > + } > + > + 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); > + 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); OK. > + } > + > + TEST_COMPARE (pidfd_send_signal (pidfd, SIGKILL, NULL, 0), 0); OK. Sends signal. > + { > + 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> OK.
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index 1c08f0918f..6fefcd8fe7 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -127,6 +127,7 @@ tests += tst-clone tst-clone2 tst-clone3 tst-fanotify tst-personality \ tst-prctl \ tst-scm_rights \ tst-epoll \ + tst-pidfd \ # tests # Test for the symbol version of fcntl that was replaced in glibc 2.28. diff --git a/sysdeps/unix/sysv/linux/tst-pidfd.c b/sysdeps/unix/sysv/linux/tst-pidfd.c new file mode 100644 index 0000000000..267b071af4 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-pidfd.c @@ -0,0 +1,170 @@ +/* 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 was the last one added on Linux. */ + int r = pidfd_getfd (0, 0, 1); + TEST_VERIFY_EXIT (r == -1); + if (errno == ENOSYS) + FAIL_UNSUPPORTED ("kernel does not support pidfd_open, 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); + 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>