From patchwork Fri Aug 18 14:06:41 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella Netto X-Patchwork-Id: 1822942 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.a=rsa-sha256 header.s=default header.b=Kpqo6Siw; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=libc-alpha-bounces+incoming=patchwork.ozlabs.org@sourceware.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4RS3dd35kfz22PN for ; Sat, 19 Aug 2023 00:09:09 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 8D9BD3870C0A for ; Fri, 18 Aug 2023 14:09:06 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 8D9BD3870C0A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1692367746; bh=OFTcAR36Lu+D8vbHgVuvD55iFjbhXyQvWE2iqweYauA=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=Kpqo6SiwOiUHTxxfMT532MlRcLAOsF38nc68Q/em7PD8SDfq5yepxMPxZT10aAsL5 etWiL0SvVCgUEwlxLWQIeqKLIWYmJXixAxMt5jNFx54Q4OraluJ8J5fqhFQ1J9HoeW JgMjVWlfeJNPY2xjtlKw+vEZlfM1S1nRV+xsoht8= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-oa1-x2d.google.com (mail-oa1-x2d.google.com [IPv6:2001:4860:4864:20::2d]) by sourceware.org (Postfix) with ESMTPS id 69518385021A for ; Fri, 18 Aug 2023 14:07:05 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 69518385021A Received: by mail-oa1-x2d.google.com with SMTP id 586e51a60fabf-1a28de15c8aso495842fac.2 for ; Fri, 18 Aug 2023 07:07:05 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1692367624; x=1692972424; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=OFTcAR36Lu+D8vbHgVuvD55iFjbhXyQvWE2iqweYauA=; b=TgNof74p1//4DMStpHVkWsElJ7g93h616YWz8ok59Hp96IzmMoh1z2SKkf6yTZrvhE 0wq7WEtHNOJpfybhkU/PY6zelaIhZGc8tYn8w3cQDL+Rl49ICJ4odq+TemY36q+Pjai7 FLN6RHsa0HlbyTPWXr/7Qgnww6MAKdU6ge0QeEYMXO4FNAQr7KyHtxFOIr4vm9EuKcdf XUURimPSSakfIx69j6T0pjR9mDgGLCcNrzzFwqueoY5aDK8JuSzsj1S+cPpwmh3ZeL3C aC5nUriF1siuRW2WNNIRqsJdEpH7xvfWrwYa3iPQ7Q3zifQ7WvftF4HHHjEo63RrDZvs tRiQ== X-Gm-Message-State: AOJu0YzSi0Uj69Q02Cih71+OD6Ft70c3Vk84BagAqLoFtgxfVt5xwIIg YzcVZUROJUmzyEhVSoeWrK+TJXRl9gc5R9yTA3mqAw== X-Google-Smtp-Source: AGHT+IGMEadCHJ8XzqdjJeeL+sb13bhwek+7OUe+/GFS5pzZkDCKH24bk3PjXs3xMgri6k+zkgW5bw== X-Received: by 2002:a05:6871:b28:b0:1b0:12d7:1ef6 with SMTP id fq40-20020a0568710b2800b001b012d71ef6mr2836394oab.25.1692367622702; Fri, 18 Aug 2023 07:07:02 -0700 (PDT) Received: from mandiga.. ([2804:1b3:a7c2:c275:7563:d0a5:7f5d:1313]) by smtp.gmail.com with ESMTPSA id r7-20020a056870734700b001c0e840ebcfsm1042841oal.35.2023.08.18.07.07.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Aug 2023 07:07:01 -0700 (PDT) To: libc-alpha@sourceware.org, Florian Weimer Subject: [PATCH v8 6/7] posix: Add fork_np (BZ 26371) Date: Fri, 18 Aug 2023 11:06:41 -0300 Message-Id: <20230818140642.1623571-7-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230818140642.1623571-1-adhemerval.zanella@linaro.org> References: <20230818140642.1623571-1-adhemerval.zanella@linaro.org> MIME-Version: 1.0 X-Spam-Status: No, score=-12.6 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Adhemerval Zanella via Libc-alpha From: Adhemerval Zanella Netto Reply-To: Adhemerval Zanella Errors-To: libc-alpha-bounces+incoming=patchwork.ozlabs.org@sourceware.org Sender: "Libc-alpha" Returning a pidfd allows a process to keep a race-free handle to a child process. However, to create a process file descriptor the caller needs to use pidfd_open which still might be subject to TOCTOU. The implementation assures that the kernel must support the complete pidfd interface, meaning that waitid (P_PIDFD) should be supported. It ensures that a non-racy workaround is required (such as reading procfs fdinfo pid to use along with old wait interfaces). If the kernel does not have the required support the interface returns -1 and set errno to ENOSYS. The interface is: typedef union { struct { __uint64_t fork_np_flags; int fork_np_pidfd; int fork_np_cgroup; int fork_np_exit_signal; #define fork_np_flags __data.fork_np_flags #define fork_np_pidfd __data.fork_np_pidfd #define fork_np_cgroup __data.fork_np_cgroup #define fork_np_exit_signal __data.fork_np_exit_signal } __data; char __size [FORK_NP_ARGS_SIZE_VER0]; } fork_np_args_t; #define FORK_NP_PIDFD (1ULL << 1) #define FORK_NP_CGROUP (1ULL << 2) #define FORK_NP_ASYNCSAFE (1ULL << 3) #define FORK_NP_EXIT_SIGNAL (1ULL << 4) pid_t fork_np (fork_np_args_t *args, size_t size) The SIZE must represent a supported fork_np_args_t type, otherwise, the function returns EINVAL. Also, each new member should add a new flag so fork_np can be extended. If ARGS has all members set to 0, no file descriptor is returned and fork_np acts as fork. If FORK_NP_PIDFD is set on the flags member, a new file descriptor is returned on the pidfd member and the kernel sets O_CLOEXEC as default. The fork_np follows the fork/_Fork convention on returning a positive or negative value to the parent (with a negative indicating an error) and zero to the child. If FORK_NP_CGROUP is set, the value on the cgroup member is used as the cgroupv2 to be placed in the new process (by using the CLONE_INTO_CGROUP clone flag). If FORK_NP_EXIT_SIGNAL is set, the new process will send the exit signal defined by exit_signal on termination or none if it is set to 0. When using this flag, the parent process must specify the __WALL or __WCLONE Linux-specific options when waiting for the child with wait or waitid. If FORK_NP_ASYNCSAFE is set, fork_np acts as _Fork, thus avoiding running pthread_atfork handlers. Checked on x86_64-linux-gnu on Linux 4.15 (no CLONE_PIDFD or waitid support), Linux 5.4 (full support), and Linux 6.2. --- NEWS | 7 + include/clone_internal.h | 17 ++ manual/process.texi | 82 +++++- posix/Makefile | 3 +- posix/fork-internal.c | 127 ++++++++++ posix/fork-internal.h | 36 +++ posix/fork.c | 107 +------- sysdeps/nptl/_Fork.c | 2 +- sysdeps/unix/sysv/linux/Makefile | 3 + sysdeps/unix/sysv/linux/Versions | 1 + sysdeps/unix/sysv/linux/aarch64/libc.abilist | 1 + sysdeps/unix/sysv/linux/alpha/libc.abilist | 1 + sysdeps/unix/sysv/linux/arc/libc.abilist | 1 + sysdeps/unix/sysv/linux/arch-fork.h | 16 +- sysdeps/unix/sysv/linux/arm/be/libc.abilist | 1 + sysdeps/unix/sysv/linux/arm/le/libc.abilist | 1 + sysdeps/unix/sysv/linux/bits/unistd_ext.h | 51 ++++ sysdeps/unix/sysv/linux/clone-internal.c | 58 ++++- sysdeps/unix/sysv/linux/csky/libc.abilist | 1 + sysdeps/unix/sysv/linux/fork_np.c | 97 +++++++ sysdeps/unix/sysv/linux/hppa/libc.abilist | 1 + sysdeps/unix/sysv/linux/i386/libc.abilist | 1 + sysdeps/unix/sysv/linux/ia64/libc.abilist | 1 + .../sysv/linux/loongarch/lp64/libc.abilist | 1 + .../sysv/linux/m68k/coldfire/libc.abilist | 1 + .../unix/sysv/linux/m68k/m680x0/libc.abilist | 1 + .../sysv/linux/microblaze/be/libc.abilist | 1 + .../sysv/linux/microblaze/le/libc.abilist | 1 + .../sysv/linux/mips/mips32/fpu/libc.abilist | 1 + .../sysv/linux/mips/mips32/nofpu/libc.abilist | 1 + .../sysv/linux/mips/mips64/n32/libc.abilist | 1 + .../sysv/linux/mips/mips64/n64/libc.abilist | 1 + sysdeps/unix/sysv/linux/nios2/libc.abilist | 1 + sysdeps/unix/sysv/linux/or1k/libc.abilist | 1 + .../linux/powerpc/powerpc32/fpu/libc.abilist | 1 + .../powerpc/powerpc32/nofpu/libc.abilist | 1 + .../linux/powerpc/powerpc64/be/libc.abilist | 1 + .../linux/powerpc/powerpc64/le/libc.abilist | 1 + .../unix/sysv/linux/riscv/rv32/libc.abilist | 1 + .../unix/sysv/linux/riscv/rv64/libc.abilist | 1 + .../unix/sysv/linux/s390/s390-32/libc.abilist | 1 + .../unix/sysv/linux/s390/s390-64/libc.abilist | 1 + sysdeps/unix/sysv/linux/sh/be/libc.abilist | 1 + sysdeps/unix/sysv/linux/sh/le/libc.abilist | 1 + .../sysv/linux/sparc/sparc32/libc.abilist | 1 + .../sysv/linux/sparc/sparc64/libc.abilist | 1 + sysdeps/unix/sysv/linux/tst-fork_np-cgroup.c | 170 +++++++++++++ sysdeps/unix/sysv/linux/tst-fork_np.c | 236 ++++++++++++++++++ .../unix/sysv/linux/x86_64/64/libc.abilist | 1 + .../unix/sysv/linux/x86_64/x32/libc.abilist | 1 + 50 files changed, 928 insertions(+), 119 deletions(-) create mode 100644 posix/fork-internal.c create mode 100644 posix/fork-internal.h create mode 100644 sysdeps/unix/sysv/linux/fork_np.c create mode 100644 sysdeps/unix/sysv/linux/tst-fork_np-cgroup.c create mode 100644 sysdeps/unix/sysv/linux/tst-fork_np.c diff --git a/NEWS b/NEWS index 97681e6796..00e9553e8f 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,13 @@ Major new features: The pidfd functionality avoids the issue of PID reuse with the traditional posix_spawn interface. +* On Linux, the fork_np has been added. It has a similar semantic as ai + fork or _Fork, where it clones the calling process; and allows to extend + of the fork functionality by allowing the return of a process file + descriptor (as pidfd_spawn), setting a cgroupv2 of the new process (as + posix_spawnattr_getcgroup_np), setting a different signal on process + termination, and making the function act as _Fork. + Deprecated and removed features, and other changes affecting compatibility: [Add deprecations, removals and changes affecting compatibility here] diff --git a/include/clone_internal.h b/include/clone_internal.h index 567160ebb5..340cc39a37 100644 --- a/include/clone_internal.h +++ b/include/clone_internal.h @@ -2,6 +2,8 @@ #define _CLONE_INTERNAL_H #include +#include +#include /* The clone3 syscall provides a superset of the functionality of the clone interface. The kernel might extend __CL_ARGS struct in the future, with @@ -35,6 +37,21 @@ extern int __clone_internal_fallback (struct clone_args *__cl_args, void *__arg) attribute_hidden; +/* Call the clone3/clone syscall with fork semantic (i.e. no stack setting + required). The EXTRA_FLAGS define any additional flag to be used besides + CLONE_CHILD_SETTID and CLONE_CHILD_CLEARTID, the PIDFD indicates where + the process file descriptor (set with CLONE_PIDFD) should be returned, + and the CGROUP specifies the cgroupsv2 (set with CLONE_INTO_CGROUP). + + Similar to __clone3_internal, it uses the stick check to avoid re-issue + the clone3 syscall if kernel does not support it. + + It does not provide CLONE_INTO_CGROUP/CGROUP fallback if clone3 is not + supported, in this case the function returns -1/ENOTSUP. */ +extern int __clone_fork (uint64_t __extra_flags, void *__pidfd, int __cgroup, + int __exit_signal) + attribute_hidden; + /* Return whether the kernel supports pid file descriptor, including clone with CLONE_PIDFD and waitid with P_PIDFD. */ extern bool __clone_pidfd_supported (void) attribute_hidden; diff --git a/manual/process.texi b/manual/process.texi index 68361c3f61..e6ac1f934f 100644 --- a/manual/process.texi +++ b/manual/process.texi @@ -137,12 +137,12 @@ creating a process and making it run another program. @cindex subprocess A new processes is created when one of the functions @code{posix_spawn}, @code{fork}, @code{_Fork}, @code{vfork}, or -@code{pidfd_spawn} is called. (The @code{system} and @code{popen} also -create new processes internally.) Due to the name of the @code{fork} -function, the act of creating a new process is sometimes called -@dfn{forking} a process. Each new process (the @dfn{child process} or -@dfn{subprocess}) is allocated a process ID, distinct from the process -ID of the parent process. @xref{Process Identification}. +@code{pidfd_spawn}, or @code{fork_np} is called. (The @code{system} +and @code{popen} also create new processes internally.) Due to the name +of the @code{fork} function, the act of creating a new process is +sometimes called @dfn{forking} a process. Each new process (the +@dfn{child process} or @dfn{subprocess}) is allocated a process ID, +distinct from the process ID of the parent process. @xref{Process Identification}. After forking a child process, both the parent and child processes continue to execute normally. If you want your program to wait for a @@ -153,10 +153,10 @@ limited information about why the child terminated---for example, its exit status code. A newly forked child process continues to execute the same program as -its parent process, at the point where the @code{fork} or @code{_Fork} -call returns. You can use the return value from @code{fork} or -@code{_Fork} to tell whether the program is running in the parent process -or the child. +its parent process, at the point where the @code{fork}, @code{_Fork}, +or @code{fork_np} call returns. You can use the return value from +@code{fork}, @code{_Fork}, or @code{fork_np} to tell whether the +program is running in the parent process or the child. @cindex process image Having several processes run the same program is only occasionally @@ -362,6 +362,68 @@ the proper precautions for using @code{vfork}, your program will still work even if the system uses @code{fork} instead. @end deftypefun +@deftp {Data Type} {fork_np_args_t} +@standards{GNU, unistd.h} +This structure is used to along @code{fork_np} to enable extra +functionality. + +@table @code +@item uint64_t fork_np_flags +If @code{FORK_NP_PIDFD} is set, the process file descriptor will be +returned on @code{pidfd}. +If @code{FORK_NP_CGROUP} is set, the value from @code{cgroup} will be used +to specify a different cgroupv2 to start the new process. +If @code{FORK_NP_ASYNCSAFE} is set, @code{fork_np} will not issue any +atfork handler (similar to @code{_Fork}). +If @code{PIDFDFORK_EXIT_SIGNAL} is set, the signal defined at @code{exit_signal} +will be send on process termination. + +@item int fork_np_pidfd +Return the process file descriptor if @code{FORK_NP_PIDFD} is set. + +@item int fork_np_cgroup +Set the cgroupv2 to be used on the new process. + +@item int fork_np_exit_signal; +Define which signal to send on process termination. +@end table + +This union is a GNU extension. +@end deftp + +@deftypefun pid_t fork_np (fork_np_args_t @var{args}, size_t @var{len}) +@standards{GNU, unistd.h} +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}} +The @code{fork_np} function is similar to @code{fork} on both semantic and +return code value, but allows extra functionality through the @code{args} +parameter. The @code{len} must be the size of @code{args}, otherwise the +function returns with a failure. + +If @code{FORK_NP_PIDFD} is set on @code{fork_np_flags}, and the process is +correctly created the @code{fork_np_pidfd} frkm @var{args} will contain a +file descriptor that can be used along other pidfd functions (like +@code{pidfd_send_signal} or with @code{waitid} along with@code{P_PIDFD}. + +If @code{FORK_NP_CGROUP} is set on @code{fork_np_flags}, the +@code{fork_np_cgroup} value from @var{args} will be used as the cgroups v2 +control group on process creation. There is no fallback implementation, +meaning If the kernel does not provide the required support an error is returned. + +If @code{FORK_NP_ASYNCSAFE} is set on @code{fork_np_flags}, @code{fork_np} +acts as @code{_Fork}, where it does not invoke any callbacks registered with +@code{pthread_atfork}, nor does it reset internal state or locks (such as the +@code{malloc} locks). + +If @code{FORK_NP_EXIT_SIGNAL} is set on @code{flags}, the signal number +@code{fork_np_exit_signal} from @code{args} will be sent on process +termination. The @code{0} value is also valid, meaning that no signal +will be sent. @strong{NB:} When using this flag, the parent process must +specify the @code{__WALL} or @code{__WCLONE} options when waiting for the +child with @code{wait} or @code{waitid}. + +This function is a GNU extension and specific to Linux. +@end deftypefun + @node Executing a File @section Executing a File @cindex executing a file diff --git a/posix/Makefile b/posix/Makefile index 905cf9fb54..949f5632eb 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -85,6 +85,7 @@ routines := \ fexecve \ fnmatch \ fork \ + fork-internal \ fpathconf \ gai_strerror \ get_child_max \ @@ -589,7 +590,7 @@ CFLAGS-execl.os = -fomit-frame-pointer CFLAGS-execvp.os = -fomit-frame-pointer CFLAGS-execlp.os = -fomit-frame-pointer CFLAGS-nanosleep.c += -fexceptions -fasynchronous-unwind-tables -CFLAGS-fork.c = $(libio-mtsafe) $(config-cflags-wno-ignored-attributes) +CFLAGS-fork-internal.c = $(libio-mtsafe) $(config-cflags-wno-ignored-attributes) tstgetopt-ARGS = -a -b -cfoobar --required foobar --optional=bazbug \ --none random --col --color --colour diff --git a/posix/fork-internal.c b/posix/fork-internal.c new file mode 100644 index 0000000000..a5e47cbe53 --- /dev/null +++ b/posix/fork-internal.c @@ -0,0 +1,127 @@ +/* Internal fork definitions. + Copyright (C) 2023 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 +#include +#include +#include + +static void +fresetlockfiles (void) +{ + _IO_ITER i; + + for (i = _IO_iter_begin(); i != _IO_iter_end(); i = _IO_iter_next(i)) + if ((_IO_iter_file (i)->_flags & _IO_USER_LOCK) == 0) + _IO_lock_init (*((_IO_lock_t *) _IO_iter_file(i)->_lock)); +} + +uint64_t +__fork_pre (bool multiple_threads, struct nss_database_data *nss_database_data) +{ + uint64_t lastrun = __run_prefork_handlers (multiple_threads); + + /* If we are not running multiple threads, we do not have to + preserve lock state. If fork runs from a signal handler, only + async-signal-safe functions can be used in the child. These data + structures are only used by unsafe functions, so their state does + not matter if fork was called from a signal handler. */ + if (multiple_threads) + { + call_function_static_weak (__nss_database_fork_prepare_parent, + nss_database_data); + + _IO_list_lock (); + + /* Acquire malloc locks. This needs to come last because fork + handlers may use malloc, and the libio list lock has an + indirect malloc dependency as well (via the getdelim + function). */ + call_function_static_weak (__malloc_fork_lock_parent); + } + + return lastrun; +} + +void +__fork_post (struct fork_post_state_t *state, + struct nss_database_data *nss_database_data) +{ + if (state->pid == 0) + { + fork_system_setup (); + + /* Reset the lock state in the multi-threaded case. */ + if (state->multiple_threads) + { + __libc_unwind_link_after_fork (); + + fork_system_setup_after_fork (); + + /* Release malloc locks. */ + call_function_static_weak (__malloc_fork_unlock_child); + + /* Reset the file list. These are recursive mutexes. */ + fresetlockfiles (); + + /* Reset locks in the I/O code. */ + _IO_list_resetlock (); + + call_function_static_weak (__nss_database_fork_subprocess, + nss_database_data); + } + + /* Reset the lock the dynamic loader uses to protect its data. */ + __rtld_lock_initialize (GL(dl_load_lock)); + + /* Reset the lock protecting dynamic TLS related data. */ + __rtld_lock_initialize (GL(dl_load_tls_lock)); + + reclaim_stacks (); + + /* Run the handlers registered for the child. */ + __run_postfork_handlers (atfork_run_child, state->multiple_threads, + state->lastrun); + } + else + { + /* If _Fork failed, preserve its errno value. */ + int save_errno = errno; + + /* Release acquired locks in the multi-threaded case. */ + if (state->multiple_threads) + { + /* Release malloc locks, parent process variant. */ + call_function_static_weak (__malloc_fork_unlock_parent); + + /* We execute this even if the 'fork' call failed. */ + _IO_list_unlock (); + } + + /* Run the handlers registered for the parent. */ + __run_postfork_handlers (atfork_run_parent, state->multiple_threads, + state->lastrun); + + if (state->pid < 0) + __set_errno (save_errno); + } +} diff --git a/posix/fork-internal.h b/posix/fork-internal.h new file mode 100644 index 0000000000..5017061e1e --- /dev/null +++ b/posix/fork-internal.h @@ -0,0 +1,36 @@ +/* Internal fork definitions. + Copyright (C) 2023 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 + . */ + +#ifndef _FORK_INTERNAL_H +#define _FORK_INTERNAL_H + +#include +#include + +struct fork_post_state_t +{ + bool multiple_threads; + pid_t pid; + uint64_t lastrun; +}; + +uint64_t __fork_pre (bool, struct nss_database_data *) attribute_hidden; +void __fork_post (struct fork_post_state_t *, struct nss_database_data *) + attribute_hidden; + +#endif diff --git a/posix/fork.c b/posix/fork.c index b4aaa9fa6d..1708473e72 100644 --- a/posix/fork.c +++ b/posix/fork.c @@ -16,25 +16,10 @@ License along with the GNU C Library; if not, see . */ -#include -#include -#include -#include -#include -#include -#include +#include #include #include - -static void -fresetlockfiles (void) -{ - _IO_ITER i; - - for (i = _IO_iter_begin(); i != _IO_iter_end(); i = _IO_iter_next(i)) - if ((_IO_iter_file (i)->_flags & _IO_USER_LOCK) == 0) - _IO_lock_init (*((_IO_lock_t *) _IO_iter_file(i)->_lock)); -} +#include pid_t __libc_fork (void) @@ -45,92 +30,18 @@ __libc_fork (void) requirement for fork (Austin Group tracker issue #62) this is best effort to make is async-signal-safe at least for single-thread case. */ - bool multiple_threads = !SINGLE_THREAD_P; - uint64_t lastrun; - - lastrun = __run_prefork_handlers (multiple_threads); - + struct fork_post_state_t state = { + .multiple_threads = !SINGLE_THREAD_P + }; struct nss_database_data nss_database_data; - /* If we are not running multiple threads, we do not have to - preserve lock state. If fork runs from a signal handler, only - async-signal-safe functions can be used in the child. These data - structures are only used by unsafe functions, so their state does - not matter if fork was called from a signal handler. */ - if (multiple_threads) - { - call_function_static_weak (__nss_database_fork_prepare_parent, - &nss_database_data); - - _IO_list_lock (); - - /* Acquire malloc locks. This needs to come last because fork - handlers may use malloc, and the libio list lock has an - indirect malloc dependency as well (via the getdelim - function). */ - call_function_static_weak (__malloc_fork_lock_parent); - } - - pid_t pid = _Fork (); - - if (pid == 0) - { - fork_system_setup (); - - /* Reset the lock state in the multi-threaded case. */ - if (multiple_threads) - { - __libc_unwind_link_after_fork (); - - fork_system_setup_after_fork (); - - /* Release malloc locks. */ - call_function_static_weak (__malloc_fork_unlock_child); - - /* Reset the file list. These are recursive mutexes. */ - fresetlockfiles (); - - /* Reset locks in the I/O code. */ - _IO_list_resetlock (); - - call_function_static_weak (__nss_database_fork_subprocess, - &nss_database_data); - } - - /* Reset the lock the dynamic loader uses to protect its data. */ - __rtld_lock_initialize (GL(dl_load_lock)); - - /* Reset the lock protecting dynamic TLS related data. */ - __rtld_lock_initialize (GL(dl_load_tls_lock)); - - reclaim_stacks (); - - /* Run the handlers registered for the child. */ - __run_postfork_handlers (atfork_run_child, multiple_threads, lastrun); - } - else - { - /* If _Fork failed, preserve its errno value. */ - int save_errno = errno; - - /* Release acquired locks in the multi-threaded case. */ - if (multiple_threads) - { - /* Release malloc locks, parent process variant. */ - call_function_static_weak (__malloc_fork_unlock_parent); - - /* We execute this even if the 'fork' call failed. */ - _IO_list_unlock (); - } + state.lastrun = __fork_pre (state.multiple_threads, &nss_database_data); - /* Run the handlers registered for the parent. */ - __run_postfork_handlers (atfork_run_parent, multiple_threads, lastrun); + state.pid = _Fork (); - if (pid < 0) - __set_errno (save_errno); - } + __fork_post (&state, &nss_database_data); - return pid; + return state.pid; } weak_alias (__libc_fork, __fork) libc_hidden_def (__fork) diff --git a/sysdeps/nptl/_Fork.c b/sysdeps/nptl/_Fork.c index f8322ae557..397f059fb0 100644 --- a/sysdeps/nptl/_Fork.c +++ b/sysdeps/nptl/_Fork.c @@ -22,7 +22,7 @@ pid_t _Fork (void) { - pid_t pid = arch_fork (&THREAD_SELF->tid); + pid_t pid = arch_fork (SIGCHLD, NULL, &THREAD_SELF->tid); if (pid == 0) { struct pthread *self = THREAD_SELF; diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index 3ecfa184d0..c9164e9d0a 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -493,6 +493,7 @@ sysdep_headers += \ sysdep_routines += \ getcpu \ oldglob \ + fork_np \ pidfd_spawn \ pidfd_spawnp \ sched_getcpu \ @@ -503,6 +504,8 @@ sysdep_routines += \ tests += \ tst-affinity \ tst-affinity-pid \ + tst-fork_np \ + tst-fork_np-cgroup \ tst-posix_spawn-setsid-pidfd \ tst-spawn-cgroup \ tst-spawn-chdir-pidfd \ diff --git a/sysdeps/unix/sysv/linux/Versions b/sysdeps/unix/sysv/linux/Versions index a8bae0c2a2..c677631f24 100644 --- a/sysdeps/unix/sysv/linux/Versions +++ b/sysdeps/unix/sysv/linux/Versions @@ -322,6 +322,7 @@ libc { %endif } GLIBC_2.39 { + fork_np; pidfd_spawn; pidfd_spawnp; posix_spawnattr_getcgroup_np; diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist index 6f23556067..dab02f0087 100644 --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist @@ -2673,6 +2673,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist index 02c43beb13..1db00408cf 100644 --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist @@ -2782,6 +2782,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist index dd8e5912d8..032aacc1ba 100644 --- a/sysdeps/unix/sysv/linux/arc/libc.abilist +++ b/sysdeps/unix/sysv/linux/arc/libc.abilist @@ -2434,6 +2434,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/arch-fork.h b/sysdeps/unix/sysv/linux/arch-fork.h index 0e0eccbf38..f978d4c4f4 100644 --- a/sysdeps/unix/sysv/linux/arch-fork.h +++ b/sysdeps/unix/sysv/linux/arch-fork.h @@ -32,24 +32,24 @@ override it with one of the supported calling convention (check generic kernel-features.h for the clone abi variants). */ static inline pid_t -arch_fork (void *ctid) +arch_fork (int flags, void *ptid, void *ctid) { - const int flags = CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD; long int ret; + flags |= CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID; #ifdef __ASSUME_CLONE_BACKWARDS # ifdef INLINE_CLONE_SYSCALL - ret = INLINE_CLONE_SYSCALL (flags, 0, NULL, 0, ctid); + ret = INLINE_CLONE_SYSCALL (flags, 0, ptid, 0, ctid); # else - ret = INLINE_SYSCALL_CALL (clone, flags, 0, NULL, 0, ctid); + ret = INLINE_SYSCALL_CALL (clone, flags, 0, ptid, 0, ctid); # endif #elif defined(__ASSUME_CLONE_BACKWARDS2) - ret = INLINE_SYSCALL_CALL (clone, 0, flags, NULL, ctid, 0); + ret = INLINE_SYSCALL_CALL (clone, 0, flags, ptid, ctid, 0); #elif defined(__ASSUME_CLONE_BACKWARDS3) - ret = INLINE_SYSCALL_CALL (clone, flags, 0, 0, NULL, ctid, 0); + ret = INLINE_SYSCALL_CALL (clone, flags, 0, 0, ptid, ctid, 0); #elif defined(__ASSUME_CLONE2) - ret = INLINE_SYSCALL_CALL (clone2, flags, 0, 0, NULL, ctid, 0); + ret = INLINE_SYSCALL_CALL (clone2, flags, 0, 0, ptid, ctid, 0); #elif defined(__ASSUME_CLONE_DEFAULT) - ret = INLINE_SYSCALL_CALL (clone, flags, 0, NULL, ctid, 0); + ret = INLINE_SYSCALL_CALL (clone, flags, 0, ptid, ctid, 0); #else # error "Undefined clone variant" #endif diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist index a751e5f5a9..9f3ef16280 100644 --- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist @@ -554,6 +554,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist index 0eda3459ed..c2c6c8af6b 100644 --- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist @@ -551,6 +551,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/bits/unistd_ext.h b/sysdeps/unix/sysv/linux/bits/unistd_ext.h index c523ef67c1..1872728c51 100644 --- a/sysdeps/unix/sysv/linux/bits/unistd_ext.h +++ b/sysdeps/unix/sysv/linux/bits/unistd_ext.h @@ -47,4 +47,55 @@ extern __pid_t gettid (void) __THROW; # define CLOSE_RANGE_CLOEXEC (1U << 2) #endif +#define FORK_NP_ARGS_SIZE_VER0 24 +typedef union +{ + struct + { + __uint64_t fork_np_flags; + int fork_np_pidfd; + int fork_np_cgroup; + int fork_np_exit_signal; +#define fork_np_flags __data.fork_np_flags +#define fork_np_pidfd __data.fork_np_pidfd +#define fork_np_cgroup __data.fork_np_cgroup +#define fork_np_exit_signal __data.fork_np_exit_signal + } __data; + char __size [FORK_NP_ARGS_SIZE_VER0]; +} fork_np_args_t; + +/* Return the process file descriptor. */ +#define FORK_NP_PIDFD (1ULL << 1) +/* Specify a different cgroup2 than the default one. */ +#define FORK_NP_CGROUP (1ULL << 2) +/* Do not issue the pthread_atfork on process creation. */ +#define FORK_NP_ASYNCSAFE (1ULL << 3) +/* Send a different signal to parent on child termination. */ +#define FORK_NP_EXIT_SIGNAL (1ULL << 4) + +/* Clone the calling process, creating an exact copy and return a file + descriptor that can be used along other pidfd functions. + + The ARGS changes how the process creation is done. + + If FORK_NP_PIDFD is set on FLAGS, a process file descriptor is returned on + PIDFD (which can be used along other pidfd function, like pidfd_signal). + + If FORK_NP_CGROUP is set on FLAGS, the CGROUP file descriptor must + reference a cgroup v2 control group which will be used on process + creation. + + If FORK_NP_ASYNCSAFE is set on FLAGS, fork_np does not invoke the + registered pthread_atfork callacks (similar to _Fork). + + If FORK_NP_EXIT_SIGNAL is set on FLAGS, send the EXIT_SIGNAL signal + on process termination. + + On success, the PID of the child process is returned in the parent, + and 0 is returned to child. On failure, -1 is returned in the + parent, no child process is created. */ +extern pid_t fork_np (fork_np_args_t *__args, __SIZE_TYPE__ __size) + __THROW; + + #endif /* __USE_GNU */ diff --git a/sysdeps/unix/sysv/linux/clone-internal.c b/sysdeps/unix/sysv/linux/clone-internal.c index 790739cfce..d121be48bc 100644 --- a/sysdeps/unix/sysv/linux/clone-internal.c +++ b/sysdeps/unix/sysv/linux/clone-internal.c @@ -16,6 +16,7 @@ License along with the GNU C Library. If not, see . */ +#include #include #include #include @@ -43,6 +44,11 @@ _Static_assert (offsetofend (struct clone_args, cgroup) == CLONE_ARGS_SIZE_VER2, _Static_assert (sizeof (struct clone_args) == CLONE_ARGS_SIZE_VER2, "sizeof (struct clone_args) != CLONE_ARGS_SIZE_VER2"); +#if !__ASSUME_CLONE3 && defined __NR_clone3 +/* Set to 0 if kernel does not support clone3 syscall. */ +static int clone3_supported = 1; +#endif + int __clone_internal_fallback (struct clone_args *cl_args, int (*func) (void *arg), void *arg) @@ -84,7 +90,6 @@ __clone3_internal (struct clone_args *cl_args, int (*func) (void *args), # if __ASSUME_CLONE3 return __clone3 (cl_args, sizeof (*cl_args), func, arg); # else - static int clone3_supported = 1; if (atomic_load_relaxed (&clone3_supported) == 1) { int ret = __clone3 (cl_args, sizeof (*cl_args), func, arg); @@ -118,3 +123,54 @@ __clone_internal (struct clone_args *cl_args, } libc_hidden_def (__clone_internal) + +int +__clone_fork (uint64_t extra_flags, void *pidfd, int cgroup, int exit_signal) +{ +#ifdef __NR_clone3 + struct clone_args clone_args = + { + .flags = extra_flags + | CLONE_CHILD_SETTID + | CLONE_CHILD_CLEARTID, + .exit_signal = exit_signal, + .cgroup = cgroup, + .child_tid = (uintptr_t) &THREAD_SELF->tid, + .pidfd = (uintptr_t) pidfd, + .parent_tid = (uintptr_t) pidfd + }; +#endif + +#if __ASSUME_CLONE3 + return INLINE_SYSCALL_CALL (clone3, &clone_args, sizeof (clone_args)); +#else + /* Some architecture still does not export clone3. */ + pid_t pid; +# ifdef __NR_clone3 + if (atomic_load_relaxed (&clone3_supported) == 1) + { + pid = INLINE_SYSCALL_CALL (clone3, &clone_args, sizeof (clone_args)); + if (pid != -1 || errno != ENOSYS) + return pid; + + atomic_store_relaxed (&clone3_supported, 0); + } +# endif + + if (!(extra_flags & CLONE_INTO_CGROUP)) + { + int flags = extra_flags | (exit_signal & 0xff); + pid = arch_fork (flags, pidfd, &THREAD_SELF->tid); + } + else + { + /* No fallback for POSIX_SPAWN_SETCGROUP if clone3 is not supported. */ + pid = -1; +# ifdef __NR_clone3 + if (errno == ENOSYS) +# endif + errno = ENOTSUP; + } + return pid; +#endif +} diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist index 4f4e99427b..4112163af2 100644 --- a/sysdeps/unix/sysv/linux/csky/libc.abilist +++ b/sysdeps/unix/sysv/linux/csky/libc.abilist @@ -2710,6 +2710,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/fork_np.c b/sysdeps/unix/sysv/linux/fork_np.c new file mode 100644 index 0000000000..ca9a83bb22 --- /dev/null +++ b/sysdeps/unix/sysv/linux/fork_np.c @@ -0,0 +1,97 @@ +/* fork_np - Duplicated calling process and return a process file + descriptor. + Copyright (C) 2023 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 + +static pid_t +fork_syscall (fork_np_args_t *args) +{ + bool use_pidfd = args->fork_np_flags & FORK_NP_PIDFD; + bool use_cgroup = args->fork_np_flags & FORK_NP_CGROUP; + + int *pidfd = use_pidfd ? &args->fork_np_pidfd : NULL; + int cgroup = use_cgroup ? args->fork_np_cgroup : 0; + + uint64_t extra_flags = (use_pidfd ? CLONE_PIDFD : 0) + | (use_cgroup ? CLONE_INTO_CGROUP : 0); + int exit_signal = (args->fork_np_flags & FORK_NP_EXIT_SIGNAL) + ? args->fork_np_exit_signal : SIGCHLD; + + pid_t pid = __clone_fork (extra_flags, pidfd, cgroup, exit_signal); + + if (pid == 0) + { + struct pthread *self = THREAD_SELF; + + /* Initialize the robust mutex, check _Fork implementation for a full + description why this is required. */ +#if __PTHREAD_MUTEX_HAVE_PREV + self->robust_prev = &self->robust_head; +#endif + self->robust_head.list = &self->robust_head; + INTERNAL_SYSCALL_CALL (set_robust_list, &self->robust_head, + sizeof (struct robust_list_head)); + } + return pid; +} + +#define SUPPORTED_FLAGS (FORK_NP_PIDFD \ + | FORK_NP_CGROUP \ + | FORK_NP_ASYNCSAFE \ + | FORK_NP_EXIT_SIGNAL) + +_Static_assert (sizeof (fork_np_args_t) == FORK_NP_ARGS_SIZE_VER0, + "sizeof (fork_np_args_t) != FORK_NP_ARGS_SIZE_VER0"); + +pid_t +fork_np (fork_np_args_t *args, size_t size) +{ + if (size != FORK_NP_ARGS_SIZE_VER0) + return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL); + + if (args->fork_np_flags & ~(SUPPORTED_FLAGS)) + return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL); + + if ((args->fork_np_flags & FORK_NP_CGROUP) && !__clone_pidfd_supported ()) + return INLINE_SYSCALL_ERROR_RETURN_VALUE (ENOSYS); + + pid_t pid; + if (!(args->fork_np_flags & FORK_NP_ASYNCSAFE)) + { + bool multiple_threads = !SINGLE_THREAD_P; + struct fork_post_state_t state = { + .multiple_threads = !SINGLE_THREAD_P + }; + struct nss_database_data nss_database_data; + + state.lastrun = __fork_pre (multiple_threads, &nss_database_data); + state.pid = fork_syscall (args); + /* It follow the usual fork semantic, where a positive or negative + value is returned to parent, and 0 for the child. */ + __fork_post (&state, &nss_database_data); + + pid = state.pid; + } + else + pid = fork_syscall (args); + + return pid; +} diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist index abc471dd0b..b01734661b 100644 --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist @@ -2659,6 +2659,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist index 9f03c8a9a2..14e58ef02d 100644 --- a/sysdeps/unix/sysv/linux/i386/libc.abilist +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist @@ -2843,6 +2843,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist index ce1d20b722..25936400b8 100644 --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist @@ -2608,6 +2608,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist index 8c3640b004..4299a45d2f 100644 --- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist +++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist @@ -2194,6 +2194,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist index a594916319..98d11f7e00 100644 --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist @@ -555,6 +555,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist index 7f61d4824d..311b17c166 100644 --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist @@ -2786,6 +2786,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist index 83ebb84ff3..9a645345e7 100644 --- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist @@ -2759,6 +2759,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist index 89a0ff83bf..bc6b3094fc 100644 --- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist @@ -2756,6 +2756,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist index e21c752057..14f2335c29 100644 --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist @@ -2751,6 +2751,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist index 42f470d397..f41a1adaca 100644 --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist @@ -2749,6 +2749,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist index 6907f5f98b..3500745aa0 100644 --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist @@ -2757,6 +2757,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist index 4b1f017a98..64cc996c51 100644 --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist @@ -2659,6 +2659,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist index 0d45902209..723956e4be 100644 --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist @@ -2798,6 +2798,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist index c59032ef14..97657be343 100644 --- a/sysdeps/unix/sysv/linux/or1k/libc.abilist +++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist @@ -2180,6 +2180,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist index e014314d3e..a3fa2f4f87 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist @@ -2825,6 +2825,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist index ac05154915..bddf0f2d01 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist @@ -2858,6 +2858,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist index e13ee6e72a..ee9db4eff2 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist @@ -2579,6 +2579,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist index 0e8c9ab3fe..0a0c4c4650 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist @@ -2893,6 +2893,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist index b0559a5a64..0c9a1648e1 100644 --- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist +++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist @@ -2436,6 +2436,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist index 5f79a84016..0acdd6fff4 100644 --- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist +++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist @@ -2636,6 +2636,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist index 498886ccb2..b94792e4c1 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist @@ -2823,6 +2823,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist index 51679c2990..7d3a6e3c90 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist @@ -2616,6 +2616,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist index af7b6f5bc9..1c26740359 100644 --- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist @@ -2666,6 +2666,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist index b766299f31..5b0bd8c6c8 100644 --- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist @@ -2663,6 +2663,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist index f5b9200a33..9e18f09c1e 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist @@ -2818,6 +2818,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist index f6012e6e17..3a94cf17ee 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist @@ -2631,6 +2631,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/tst-fork_np-cgroup.c b/sysdeps/unix/sysv/linux/tst-fork_np-cgroup.c new file mode 100644 index 0000000000..e024537fc8 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-fork_np-cgroup.c @@ -0,0 +1,170 @@ +/* fork_np test using cgroupsv2. + Copyright (C) 2023 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#define CGROUPFS "/sys/fs/cgroup/" +#ifndef CGROUP2_SUPER_MAGIC +# define CGROUP2_SUPER_MAGIC 0x63677270 +#endif + +#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b) + +static inline char * +startswith(const char *s, const char *prefix) +{ + size_t l = strlen (prefix); + if (strncmp (s, prefix, l) == 0) + return (char*) s + l; + return NULL; +} + +static char * +get_cgroup (void) +{ + FILE *f = xfopen ("/proc/self/cgroup", "re"); + + char *cgroup = NULL; + + char *line = NULL; + size_t linesiz = 0; + while (xgetline (&line, &linesiz, f) > 0) + { + char *entry = startswith (line, "0:"); + if (entry == NULL) + continue; + + entry = strchr (entry, ':'); + if (entry == NULL) + continue; + + cgroup = entry + 1; + size_t l = strlen (cgroup); + if (cgroup[l - 1] == '\n') + cgroup[l - 1] = '\0'; + + cgroup = xstrdup (entry + 1); + break; + } + + xfclose (f); + free (line); + + return cgroup; +} + +static int +do_test (void) +{ + struct statfs fs; + if (statfs (CGROUPFS, &fs) < 0) + { + if (errno == ENOENT) + FAIL_UNSUPPORTED ("not cgroupv2 mount found"); + FAIL_EXIT1 ("statfs (%s): %m\n", CGROUPFS); + } + + if (!F_TYPE_EQUAL (fs.f_type, CGROUP2_SUPER_MAGIC)) + FAIL_UNSUPPORTED ("%s is not a cgroupv2", CGROUPFS); + + char *cgroup = get_cgroup (); + TEST_VERIFY_EXIT (cgroup != NULL); + char *newcgroup = xasprintf ("%s/%s", cgroup, "test-fork_np-cgroup"); + char *cgpath = xasprintf ("%s%s/test-fork_np-cgroup", CGROUPFS, cgroup); + free (cgroup); + + if (mkdir (cgpath, 0755) == -1 && errno != EEXIST) + { + if (errno == EACCES || errno == EPERM || errno == EROFS) + FAIL_UNSUPPORTED ("can not create a new cgroupv2 group"); + FAIL_EXIT1 ("mkdir (%s): %m", cgpath); + } + add_temp_file (cgpath); + + int dfd = xopen (cgpath, O_DIRECTORY | O_RDONLY | O_CLOEXEC, 0666); + + /* Check if the cgroup used at creation is the same returned by the kernel + and not as the parent. */ + { + fork_np_args_t pidfd_args = { + .fork_np_flags = FORK_NP_CGROUP, + .fork_np_cgroup = dfd, + }; + pid_t pid = fork_np (&pidfd_args, sizeof pidfd_args); + if (pid == -1 && errno == ENOTSUP) + FAIL_UNSUPPORTED ("kernel does not support CLONE_PIDFD clone flag"); + TEST_VERIFY_EXIT (pid != -1); + if (pid == 0) + { + char *child_cgroup = get_cgroup (); + TEST_VERIFY_EXIT (child_cgroup != NULL); + TEST_COMPARE_STRING (newcgroup, child_cgroup); + _exit (EXIT_SUCCESS); + } + + siginfo_t sinfo; + TEST_COMPARE (waitid (P_PID, pid, &sinfo, WEXITED), 0); + TEST_COMPARE (sinfo.si_signo, SIGCHLD); + TEST_COMPARE (sinfo.si_code, CLD_EXITED); + TEST_COMPARE (sinfo.si_status, 0); + } + + /* Same as before, but also check along with process file descriptor. */ + { + fork_np_args_t pidfd_args = { + .fork_np_flags = FORK_NP_PIDFD | FORK_NP_CGROUP, + .fork_np_cgroup = dfd, + }; + pid_t pid = fork_np (&pidfd_args, sizeof pidfd_args); + TEST_VERIFY_EXIT (pid != -1); + if (pid == 0) + { + char *child_cgroup = get_cgroup (); + TEST_VERIFY_EXIT (child_cgroup != NULL); + TEST_COMPARE_STRING (newcgroup, child_cgroup); + _exit (EXIT_SUCCESS); + } + + siginfo_t sinfo; + TEST_COMPARE (waitid (P_PIDFD, pidfd_args.fork_np_pidfd, &sinfo, + WEXITED), 0); + TEST_COMPARE (sinfo.si_signo, SIGCHLD); + TEST_COMPARE (sinfo.si_code, CLD_EXITED); + TEST_COMPARE (sinfo.si_status, 0); + } + + free (cgpath); + free (newcgroup); + + return 0; +} + +#include diff --git a/sysdeps/unix/sysv/linux/tst-fork_np.c b/sysdeps/unix/sysv/linux/tst-fork_np.c new file mode 100644 index 0000000000..568d2245ee --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-fork_np.c @@ -0,0 +1,236 @@ +/* Basic tests for fork_np. + Copyright (C) 2023 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 +#include +#include +#include +#include +#include +#include + +#define SIG_PID_EXIT_CODE 20 + +static bool atfork_prepare_var; +static bool atfork_parent_var; +static bool atfork_child_var; + +static sig_atomic_t sigchld_called; + +static void +sigchld_handler (int sig) +{ + sigchld_called = 1; +} + +static void +atfork_prepare (void) +{ + atfork_prepare_var = true; +} + +static void +atfork_parent (void) +{ + atfork_parent_var = true; +} + +static void +atfork_child (void) +{ + atfork_child_var = true; +} + +static int +singlethread_test (bool async, bool wait_with_pid, bool nosigchld) +{ + const char testdata1[] = "abcdefghijklmnopqrtuvwxz"; + enum { testdatalen1 = array_length (testdata1) }; + const char testdata2[] = "01234567890"; + enum { testdatalen2 = array_length (testdata2) }; + + pid_t ppid = getpid (); + + int tempfd = create_temp_file ("tst-fork_np", NULL); + + /* Check if the opened file is shared between process by read and write + some data on parent and child processes. */ + xwrite (tempfd, testdata1, testdatalen1); + off_t off = xlseek (tempfd, 0, SEEK_CUR); + TEST_COMPARE (off, testdatalen1); + + fork_np_args_t fork_args = { + .fork_np_flags = FORK_NP_PIDFD + | (async ? FORK_NP_ASYNCSAFE : 0) + | (nosigchld ? FORK_NP_EXIT_SIGNAL : 0) + }; + pid_t pid = fork_np (&fork_args, sizeof fork_args); + TEST_VERIFY_EXIT (pid != -1); + + sigchld_called = 0; + + if (pid == 0) + { + if (async) + TEST_VERIFY (!atfork_child_var); + else + TEST_VERIFY (atfork_child_var); + + TEST_VERIFY_EXIT (getpid () != ppid); + TEST_COMPARE (getppid(), ppid); + + TEST_COMPARE (xlseek (tempfd, 0, SEEK_CUR), testdatalen1); + + xlseek (tempfd, 0, SEEK_SET); + char buf[testdatalen1]; + TEST_COMPARE (read (tempfd, buf, sizeof (buf)), testdatalen1); + TEST_COMPARE_BLOB (buf, testdatalen1, testdata1, testdatalen1); + + xlseek (tempfd, 0, SEEK_SET); + xwrite (tempfd, testdata2, testdatalen2); + + xclose (tempfd); + + _exit (EXIT_SUCCESS); + } + + { + siginfo_t sinfo; + int options = WEXITED | (nosigchld ? __WCLONE : 0); + if (wait_with_pid) + TEST_COMPARE (waitid (P_PID, pid, &sinfo, options), 0); + else + TEST_COMPARE (waitid (P_PIDFD, fork_args.fork_np_pidfd, &sinfo, + options), 0); + TEST_COMPARE (sinfo.si_signo, SIGCHLD); + TEST_COMPARE (sinfo.si_code, CLD_EXITED); + TEST_COMPARE (sinfo.si_status, 0); + + /* If nosigchld is specified no SIGCHLD should be sent by the kernel. */ + TEST_COMPARE (sigchld_called, nosigchld ? 0 : 1); + } + + TEST_COMPARE (xlseek (tempfd, 0, SEEK_CUR), testdatalen2); + + xlseek (tempfd, 0, SEEK_SET); + char buf[testdatalen2]; + TEST_COMPARE (read (tempfd, buf, sizeof (buf)), testdatalen2); + + TEST_COMPARE_BLOB (buf, testdatalen2, testdata2, testdatalen2); + + return 0; +} + +static int +do_test (void) +{ + /* Sanity check for pidfd support. */ + TEST_COMPARE (fork_np (NULL, -1), -1); + TEST_COMPARE (errno, EINVAL); + + { + fork_np_args_t fork_args = { + .fork_np_flags = FORK_NP_PIDFD, + }; + pid_t pid = fork_np (&fork_args, sizeof fork_args); + if (pid == -1 && errno == ENOSYS) + FAIL_UNSUPPORTED ("kernel does not support CLONE_PIDFD clone flag"); + TEST_VERIFY_EXIT (pid != -1); + if (pid == 0) + _exit (EXIT_SUCCESS); + + siginfo_t sinfo; + TEST_COMPARE (waitid (P_PID, pid, &sinfo, WEXITED), 0); + TEST_COMPARE (sinfo.si_signo, SIGCHLD); + TEST_COMPARE (sinfo.si_code, CLD_EXITED); + TEST_COMPARE (sinfo.si_status, 0); + } + + { + struct sigaction sa; + sa.sa_handler = sigchld_handler; + sa.sa_flags = 0; + sigemptyset (&sa.sa_mask); + xsigaction (SIGCHLD, &sa, NULL); + } + + pthread_atfork (atfork_prepare, atfork_parent, atfork_child); + + /* With default flags, fork_np acts as fork and run the pthread_atfork + handlers. */ + { + atfork_prepare_var = atfork_parent_var = atfork_child_var = false; + singlethread_test (false, false, false); + TEST_VERIFY (atfork_prepare_var); + TEST_VERIFY (atfork_parent_var); + TEST_VERIFY (!atfork_child_var); + } + + /* Same as before, but also wait using the PID instead of pidfd. */ + { + atfork_prepare_var = atfork_parent_var = atfork_child_var = false; + singlethread_test (false, true, false); + TEST_VERIFY (atfork_prepare_var); + TEST_VERIFY (atfork_parent_var); + TEST_VERIFY (!atfork_child_var); + } + + /* Using pidfd and disable SIGCHLD. */ + { + atfork_prepare_var = atfork_parent_var = atfork_child_var = false; + singlethread_test (false, false, true); + TEST_VERIFY (atfork_prepare_var); + TEST_VERIFY (atfork_parent_var); + TEST_VERIFY (!atfork_child_var); + } + + /* With FORK_NP_ASYNCSAFE, fork_np acts as _Fork. */ + { + atfork_prepare_var = atfork_parent_var = atfork_child_var = false; + pthread_atfork (atfork_prepare, atfork_parent, atfork_child); + singlethread_test (true, false, false); + TEST_VERIFY (!atfork_prepare_var); + TEST_VERIFY (!atfork_parent_var); + TEST_VERIFY (!atfork_child_var); + } + + { + atfork_prepare_var = atfork_parent_var = atfork_child_var = false; + pthread_atfork (atfork_prepare, atfork_parent, atfork_child); + singlethread_test (true, true, false); + TEST_VERIFY (!atfork_prepare_var); + TEST_VERIFY (!atfork_parent_var); + TEST_VERIFY (!atfork_child_var); + } + + { + atfork_prepare_var = atfork_parent_var = atfork_child_var = false; + singlethread_test (true, true, true); + TEST_VERIFY (!atfork_prepare_var); + TEST_VERIFY (!atfork_parent_var); + TEST_VERIFY (!atfork_child_var); + } + + return 0; +} + +#include diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist index e35bf54779..bf06381f82 100644 --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist @@ -2582,6 +2582,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist index e7d7eb61c0..032347e89c 100644 --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist @@ -2688,6 +2688,7 @@ GLIBC_2.38 strlcat F GLIBC_2.38 strlcpy F GLIBC_2.38 wcslcat F GLIBC_2.38 wcslcpy F +GLIBC_2.39 fork_np F GLIBC_2.39 pidfd_spawn F GLIBC_2.39 pidfd_spawnp F GLIBC_2.39 posix_spawnattr_getcgroup_np F