From: Adhemerval Zanella Date: Fri, 3 Oct 2025 19:38:53 +0000 (-0300) Subject: Update PIDFD_* constants for Linux 6.17 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c0c9524a11c56889ec5b1de2e0b78112f2ebc0b7;p=thirdparty%2Fglibc.git Update PIDFD_* constants for Linux 6.17 The pidfd interface was extended with: * PIDFD_GET_INFO and pidfd_info (along with related extra flags) to allow get information about the process without the need to parse /proc (commit cdda1f26e74ba, Linux 6.13). * PIDFD_SELF_{THREAD,THREAD_GROUP,SELF,SELF_PROCESS} to allow pidfd_send_signal refer to the own process or thread lead groups without the need of allocating a file descriptor (commit f08d0c3a71114, Linux 6.15). * PIDFD_INFO_COREDUMP that extends PIDFD_GET_INFO to obtain coredump information. Linux uAPI header defines both PIDFD_SELF_THREAD and PIDFD_SELF_THREAD_GROUP on linux/fcntl.h (since they reserve part of the AT_* values), however for glibc I do not see any good reason to add pidfd definitions on fcntl-linux.h. The tst-pidfd.c is extended with some PIDFD_SELF_* tests and a new ‘tst-pidfd_getinfo.c’ test is added to check PIDFD_GET_INFO. The PIDFD_INFO_COREDUMP tests would require very large and complex tests that are already covered by kernel tests. Checked on aarch64-linux-gnu and x86_64-linux-gnu on kernels 6.8 and 6.17. Reviewed-by: Carlos O'Donell --- diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index d1ac6409b1..193798e2ae 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -211,6 +211,7 @@ tests += \ tst-ofdlocks \ tst-personality \ tst-pidfd \ + tst-pidfd_getinfo \ tst-pidfd_getpid \ tst-pkey \ tst-ppoll \ diff --git a/sysdeps/unix/sysv/linux/sys/pidfd.h b/sysdeps/unix/sysv/linux/sys/pidfd.h index e6fc024f96..1e6da6e553 100644 --- a/sysdeps/unix/sysv/linux/sys/pidfd.h +++ b/sysdeps/unix/sysv/linux/sys/pidfd.h @@ -42,6 +42,64 @@ #define PIDFD_GET_USER_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 9) #define PIDFD_GET_UTS_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 10) +/* Sentinels to avoid allocating a file descriptor to refer to own process. */ +#define PIDFD_SELF_THREAD -10000 +#define PIDFD_SELF_THREAD_GROUP -10001 +#define PIDFD_SELF PIDFD_SELF_THREAD +#define PIDFD_SELF_PROCESS PIDFD_SELF_THREAD_GROUP + + +/* Flags for pidfd_info. */ + +/* Always returned, even if not requested */ +#define PIDFD_INFO_PID (1UL << 0) +/* Always returned, even if not requested */ +#define PIDFD_INFO_CREDS (1UL << 1) +/* Always returned if available, even if not requested */ +#define PIDFD_INFO_CGROUPID (1UL << 2) +/* Only returned if requested. */ +#define PIDFD_INFO_EXIT (1UL << 3) +/* Only returned if requested. */ +#define PIDFD_INFO_COREDUMP (1UL << 4) + + +/* Value for coredump_mask in pidfd_info. Only valid if PIDFD_INFO_COREDUMP + is set in mask. */ + +/* Did crash and... */ +#define PIDFD_COREDUMPED (1U << 0) +/* coredumping generation was skipped. */ +#define PIDFD_COREDUMP_SKIP (1U << 1) +/* coredump was done as the user. */ +#define PIDFD_COREDUMP_USER (1U << 2) +/* coredump was done as root. */ +#define PIDFD_COREDUMP_ROOT (1U << 3) + +struct pidfd_info +{ + __uint64_t mask; + __uint64_t cgroupid; + __uint32_t pid; + __uint32_t tgid; + __uint32_t ppid; + __uint32_t ruid; + __uint32_t rgid; + __uint32_t euid; + __uint32_t egid; + __uint32_t suid; + __uint32_t sgid; + __uint32_t fsuid; + __uint32_t fsgid; + __int32_t exit_code; + __uint32_t coredump_mask; + __uint32_t __spare1; +}; + +/* sizeof first published struct */ +#define PIDFD_INFO_SIZE_VER0 64 + +#define PIDFD_GET_INFO _IOWR(PIDFS_IOCTL_MAGIC, 11, struct pidfd_info) + /* Returns a file descriptor that refers to the process PID. The close-on-exec is set on the file descriptor. */ extern int pidfd_open (__pid_t __pid, unsigned int __flags) __THROW; diff --git a/sysdeps/unix/sysv/linux/tst-pidfd-consts.py b/sysdeps/unix/sysv/linux/tst-pidfd-consts.py index 18010fe0e6..6dc678380c 100644 --- a/sysdeps/unix/sysv/linux/tst-pidfd-consts.py +++ b/sysdeps/unix/sysv/linux/tst-pidfd-consts.py @@ -39,7 +39,7 @@ def main(): sys.exit (77) linux_version_headers = glibcsyscalls.linux_kernel_version(args.cc) - linux_version_glibc = (6, 12) + linux_version_glibc = (6, 17) sys.exit(glibcextract.compare_macro_consts( '#include \n', '#include \n' diff --git a/sysdeps/unix/sysv/linux/tst-pidfd.c b/sysdeps/unix/sysv/linux/tst-pidfd.c index f7e0c5d9fa..4e4591f6c8 100644 --- a/sysdeps/unix/sysv/linux/tst-pidfd.c +++ b/sysdeps/unix/sysv/linux/tst-pidfd.c @@ -16,6 +16,7 @@ License along with the GNU C Library; if not, see . */ +#include #include #include #include @@ -41,15 +42,23 @@ static pid_t ppid; static uid_t puid; static void -sighandler (int sig) +sighandler_subprocess (int sig) { } +static sig_atomic_t pidfd_self_flag; + +static void +sighandler_parent (int sig) +{ + pidfd_self_flag = 1; +} + static void subprocess (void) { - xsignal (SIGUSR1, sighandler); - xsignal (SIGUSR2, sighandler); + xsignal (SIGUSR1, sighandler_subprocess); + xsignal (SIGUSR2, sighandler_subprocess); /* Check first pidfd_send_signal with default NULL siginfo_t argument. */ { @@ -102,6 +111,7 @@ do_test (void) FAIL_UNSUPPORTED ("kernel does not support pidfd_getfd, skipping test"); } + ppid = getpid (); puid = getuid (); @@ -115,6 +125,34 @@ do_test (void) TEST_COMPARE (errno, EBADF); } + xsignal (SIGUSR1, sighandler_parent); + + { + sigset_t mask, oldmask; + sigemptyset (&mask); + sigaddset (&mask, SIGUSR1); + TEST_COMPARE (sigprocmask (SIG_BLOCK, &mask, &oldmask), 0); + + /* PIDFD_SELF_{THREAD,THREAD_GROUP} were added on Linux 6.15. On older + kernels pidfd_send_signal should return -1/EBADF. */ + const int pidfd_selfs[] = { PIDFD_SELF, PIDFD_SELF_PROCESS }; + for (int i = 0; i < array_length (pidfd_selfs); i++) + { + pidfd_self_flag = 0; + int r = pidfd_send_signal (pidfd_selfs[i], SIGUSR1, NULL, 0); + if (r == -1) + TEST_COMPARE (errno, EBADF); + else + { + while (pidfd_self_flag == 0) + sigsuspend (&oldmask); + TEST_COMPARE (pidfd_self_flag, 1); + } + } + + TEST_COMPARE (sigprocmask (SIG_SETMASK, &oldmask, NULL), 0); + } + /* Check if pidfd_getpid returns ESRCH for exited subprocess. */ { pid_t pidfork = xfork (); diff --git a/sysdeps/unix/sysv/linux/tst-pidfd_getinfo.c b/sysdeps/unix/sysv/linux/tst-pidfd_getinfo.c new file mode 100644 index 0000000000..948a7cdb6b --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-pidfd_getinfo.c @@ -0,0 +1,71 @@ +/* Basic tests for Linux PID_GET_INFO interfaces. + Copyright (C) 2022-2025 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 + . */ + +#include +#include +#include +#include +#include + +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"); + } + + int pidfd = pidfd_open (getpid(), 0); + TEST_VERIFY (pidfd >= 0); + + int pid = pidfd_getpid (pidfd); + TEST_VERIFY (pid >= 0); + + struct pidfd_info info = { + .mask = PIDFD_INFO_CGROUPID, + }; + if (ioctl (pidfd, PIDFD_GET_INFO, &info) != 0) + { + if (errno == ENOTTY) + FAIL_UNSUPPORTED ("kernel does not support PIDFD_GET_INFO"); + else + FAIL_EXIT1 ("ioctl (PIDFD_GET_INFO) failed: %m"); + } + + TEST_COMPARE (info.pid, pid); + TEST_COMPARE (info.ppid, getppid ()); + TEST_COMPARE (info.ruid, getuid ()); + TEST_COMPARE (info.rgid, getgid ()); + TEST_COMPARE (info.euid, geteuid ()); + TEST_COMPARE (info.egid, getegid ()); + TEST_COMPARE (info.suid, geteuid ()); + TEST_COMPARE (info.sgid, getegid ()); + if (info.mask & PIDFD_INFO_CGROUPID) + TEST_VERIFY (info.cgroupid != 0); + + xclose (pidfd); + + return 0; +} + +#include