[v2] posix: Add _Fork [BZ #4737]

Message ID 20210211152641.2330920-1-adhemerval.zanella@linaro.org
State Superseded
Delegated to: Florian Weimer
Headers
Series [v2] posix: Add _Fork [BZ #4737] |

Commit Message

Adhemerval Zanella Feb. 11, 2021, 3:26 p.m. UTC
  Changes from previous version:

 * Added NEWS and manual entry.

--

Austin Group issue 62 [1] droped the async-signal-safe requirement
for fork and provided a async-signal-safe _Fork replacement that
does not run the atfork handlers.  It will be included in the future
POSIX standard.

It allow to close a long standing issue to make fork AS-safe (BZ#4737).
As indicated on the bug, besides the internal lock for the atfork
handlers itself; there is no guarantee that the handlers itself will
not introduce more AS-safe issues.

The idea is synchronize fork with the required internal locks to allow
children in multithread processes to use mostly of standard function
(even though POSIX states only AS-safe function should be used).  On
signal handles, _Fork should be used intead and only AS-safe functions
should be used.

For testing, the new tst-_Fork only check basic usage.  I also added
a new tst-mallocfork3 which uses the same strategy to check for
deadlock of tst-mallocfork2 but using threads instead of subprocesses
(and it does deadlock if it replaces _Fork with fork).

[1] https://austingroupbugs.net/view.php?id=62
---
 NEWS                                          |   8 +
 malloc/Makefile                               |   3 +
 malloc/tst-mallocfork3.c                      | 213 +++++++++++++++++
 manual/process.texi                           |  39 ++--
 posix/Makefile                                |   3 +-
 posix/Versions                                |   3 +
 posix/fork.c                                  |   6 +-
 posix/tst-_Fork.c                             | 218 ++++++++++++++++++
 posix/unistd.h                                |   7 +
 sysdeps/mach/hurd/i386/libc.abilist           |   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/arm/be/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/csky/libc.abilist     |   1 +
 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/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 +
 .../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 +
 .../unix/sysv/linux/x86_64/64/libc.abilist    |   1 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |   1 +
 42 files changed, 515 insertions(+), 18 deletions(-)
 create mode 100644 malloc/tst-mallocfork3.c
 create mode 100644 posix/tst-_Fork.c
  

Comments

Andreas Schwab Feb. 11, 2021, 4:21 p.m. UTC | #1
On Feb 11 2021, Adhemerval Zanella via Libc-alpha wrote:

> diff --git a/NEWS b/NEWS
> index 85e84fe536..b196dd2474 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -14,6 +14,14 @@ Major new features:
>    constant on Linux.  MINSIGSTKSZ is redefined to sysconf(_SC_MINSIGSTKSZ)
>    and SIGSTKSZ is redefined to sysconf (_SC_SIGSTKSZ).
>  
> +* The function _Fork has been added as a async-signal-safe fork
> +  replacement since Austin Group issue 62 droped the async-signal-safe
                                             dropped

> +  requirement for fork (and it will be included in the future POSIX

"a future" or "the next"?

> +  standard).  The new _Fork symbol does not issue any atfork handler neither
                               function does not run

> +  reset any internal state or lock (such as the malloc one) and only
     resets
> +  setup a minimal state required to call async-signal-safe functions (such
     sets up

> +  as raise or execve).  This function is currently a GNU extension.

Andreas.
  
Adhemerval Zanella Feb. 11, 2021, 5:10 p.m. UTC | #2
On 11/02/2021 13:21, Andreas Schwab wrote:
> On Feb 11 2021, Adhemerval Zanella via Libc-alpha wrote:
> 
>> diff --git a/NEWS b/NEWS
>> index 85e84fe536..b196dd2474 100644
>> --- a/NEWS
>> +++ b/NEWS
>> @@ -14,6 +14,14 @@ Major new features:
>>    constant on Linux.  MINSIGSTKSZ is redefined to sysconf(_SC_MINSIGSTKSZ)
>>    and SIGSTKSZ is redefined to sysconf (_SC_SIGSTKSZ).
>>  
>> +* The function _Fork has been added as a async-signal-safe fork
>> +  replacement since Austin Group issue 62 droped the async-signal-safe
>                                              dropped
> 
>> +  requirement for fork (and it will be included in the future POSIX
> 
> "a future" or "the next"?

Afaik in the next revision, I will fix it.

> 
>> +  standard).  The new _Fork symbol does not issue any atfork handler neither
>                                function does not run

I changed to 'The new _Fork symbol does not run any atfork function ...'.

> 
>> +  reset any internal state or lock (such as the malloc one) and only
>      resets

Ack.

>> +  setup a minimal state required to call async-signal-safe functions (such
>      sets up

Ack.

> 
>> +  as raise or execve).  This function is currently a GNU extension.
> 
> Andreas.
>
  
Andreas Schwab Feb. 11, 2021, 5:33 p.m. UTC | #3
On Feb 11 2021, Adhemerval Zanella via Libc-alpha wrote:

> I changed to 'The new _Fork symbol does not run any atfork function ...'.

A symbol doesn't run anything.  It's the function that the symbol
designates that does it.

Andreas.
  
Adhemerval Zanella Feb. 11, 2021, 5:36 p.m. UTC | #4
On 11/02/2021 14:33, Andreas Schwab wrote:
> On Feb 11 2021, Adhemerval Zanella via Libc-alpha wrote:
> 
>> I changed to 'The new _Fork symbol does not run any atfork function ...'.
> 
> A symbol doesn't run anything.  It's the function that the symbol
> designates that does it.
> 

Ack, I replace symbol with function.

> Andreas.
>
  

Patch

diff --git a/NEWS b/NEWS
index 85e84fe536..b196dd2474 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,14 @@  Major new features:
   constant on Linux.  MINSIGSTKSZ is redefined to sysconf(_SC_MINSIGSTKSZ)
   and SIGSTKSZ is redefined to sysconf (_SC_SIGSTKSZ).
 
+* The function _Fork has been added as a async-signal-safe fork
+  replacement since Austin Group issue 62 droped the async-signal-safe
+  requirement for fork (and it will be included in the future POSIX
+  standard).  The new _Fork symbol does not issue any atfork handler neither
+  reset any internal state or lock (such as the malloc one) and only
+  setup a minimal state required to call async-signal-safe functions (such
+  as raise or execve).  This function is currently a GNU extension.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
   [Add deprecations, removals and changes affecting compatibility here]
diff --git a/malloc/Makefile b/malloc/Makefile
index 583bbefb0d..42852f47a2 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -31,6 +31,7 @@  tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
 	 tst-malloc-backtrace tst-malloc-thread-exit \
 	 tst-malloc-thread-fail tst-malloc-fork-deadlock \
 	 tst-mallocfork2 \
+	 tst-mallocfork3 \
 	 tst-interpose-nothread \
 	 tst-interpose-thread \
 	 tst-alloc_buffer \
@@ -110,6 +111,8 @@  libmemusage-inhibit-o = $(filter-out .os,$(object-suffixes))
 $(objpfx)tst-malloc-backtrace: $(shared-thread-library)
 $(objpfx)tst-malloc-thread-exit: $(shared-thread-library)
 $(objpfx)tst-malloc-thread-fail: $(shared-thread-library)
+$(objpfx)tst-mallocfork3: $(shared-thread-library)
+$(objpfx)tst-mallocfork3-mcheck: $(shared-thread-library)
 $(objpfx)tst-malloc-fork-deadlock: $(shared-thread-library)
 $(objpfx)tst-malloc-stats-cancellation: $(shared-thread-library)
 $(objpfx)tst-malloc-backtrace-mcheck: $(shared-thread-library)
diff --git a/malloc/tst-mallocfork3.c b/malloc/tst-mallocfork3.c
new file mode 100644
index 0000000000..4ac99eea43
--- /dev/null
+++ b/malloc/tst-mallocfork3.c
@@ -0,0 +1,213 @@ 
+/* Test case for async-signal-safe _Fork (with respect to malloc).
+   Copyright (C) 2021 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; see the file COPYING.LIB.  If
+   not, see <https://www.gnu.org/licenses/>.  */
+
+/* This test is similar to tst-mallocfork2.c, but specifically stress
+   the async-signal-safeness of _Fork on multithread environment.  */
+
+#include <array_length.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xsignal.h>
+#include <support/xthread.h>
+#include <support/xunistd.h>
+#include <sys/wait.h>
+
+/* How many malloc objects to keep arond.  */
+enum { malloc_objects = 1009 };
+
+/* The maximum size of an object.  */
+enum { malloc_maximum_size = 70000 };
+
+/* How many iterations the test performs before exiting.  */
+enum { iterations = 10000 };
+
+/* Barrier for synchronization with the threads sending SIGUSR1
+   signals, to make it more likely that the signals arrive during a
+   fork/free/malloc call.  */
+static pthread_barrier_t barrier;
+
+/* Set to 1 if SIGUSR1 is received.  Used to detect a signal during
+   fork/free/malloc.  */
+static volatile sig_atomic_t sigusr1_received;
+
+/* Periodically set to 1, to indicate that the thread is making
+   progress.  Checked by liveness_signal_handler.  */
+static volatile sig_atomic_t progress_indicator = 1;
+
+/* Set to 1 if an error occurs in the signal handler.  */
+static volatile sig_atomic_t error_indicator = 0;
+
+static void
+sigusr1_handler (int signo)
+{
+  sigusr1_received = 1;
+
+  /* Perform a fork with a trivial subprocess.  */
+  pid_t pid = _Fork ();
+  if (pid == -1)
+    {
+      write_message ("error: fork\n");
+      error_indicator = 1;
+      return;
+    }
+  if (pid == 0)
+    _exit (0);
+  int status;
+  int ret = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0));
+  if (ret < 0)
+    {
+      write_message ("error: waitpid\n");
+      error_indicator = 1;
+      return;
+    }
+  if (status != 0)
+    {
+      write_message ("error: unexpected exit status from subprocess\n");
+      error_indicator = 1;
+      return;
+    }
+}
+
+static void
+liveness_signal_handler (int signo)
+{
+  if (progress_indicator)
+    progress_indicator = 0;
+  else
+    write_message ("warning: thread seems to be stuck\n");
+}
+
+struct signal_send_args
+{
+  pthread_t target;
+  int signo;
+  bool sleep;
+};
+#define SIGNAL_SEND_GET_ARG(arg, field) \
+  (((struct signal_send_args *)(arg))->field)
+
+/* Send SIGNO to the parent thread.  If SLEEP, wait a second between
+   signals, otherwise use barriers to delay sending signals.  */
+static void *
+signal_sender (void *args)
+{
+  int signo = SIGNAL_SEND_GET_ARG (args, signo);
+  bool sleep = SIGNAL_SEND_GET_ARG (args, sleep);
+
+  pthread_t target = SIGNAL_SEND_GET_ARG (args, target);
+  while (true)
+    {
+      if (!sleep)
+        xpthread_barrier_wait (&barrier);
+      xpthread_kill (target, signo);
+      if (sleep)
+        usleep (1 * 1000 * 1000);
+      else
+        xpthread_barrier_wait (&barrier);
+    }
+  return NULL;
+}
+
+static pthread_t sigusr1_sender[5];
+static pthread_t sigusr2_sender;
+
+static int
+do_test (void)
+{
+  xsignal (SIGUSR1, sigusr1_handler);
+  xsignal (SIGUSR2, liveness_signal_handler);
+
+  pthread_t self = pthread_self ();
+
+  struct signal_send_args sigusr2_args = { self, SIGUSR2, true };
+  sigusr2_sender = xpthread_create (NULL, signal_sender, &sigusr2_args);
+
+  /* Send SIGUSR1 signals from several threads.  Hopefully, one
+     signal will hit one of the ciritical functions.  Use a barrier to
+     avoid sending signals while not running fork/free/malloc.  */
+  struct signal_send_args sigusr1_args = { self, SIGUSR1, false };
+  xpthread_barrier_init (&barrier, NULL,
+                         array_length (sigusr1_sender) + 1);
+  for (size_t i = 0; i < array_length (sigusr1_sender); ++i)
+    sigusr1_sender[i] = xpthread_create (NULL, signal_sender, &sigusr1_args);
+
+  void *objects[malloc_objects] = {};
+  unsigned int fork_signals = 0;
+  unsigned int free_signals = 0;
+  unsigned int malloc_signals = 0;
+  unsigned int seed = 1;
+  for (int i = 0; i < iterations; ++i)
+    {
+      progress_indicator = 1;
+      int slot = rand_r (&seed) % malloc_objects;
+      size_t size = rand_r (&seed) % malloc_maximum_size;
+
+      /* Occasionally do a fork first, to catch deadlocks there as
+         well (see bug 24161).  */
+      bool do_fork = (rand_r (&seed) % 7) == 0;
+
+      xpthread_barrier_wait (&barrier);
+      if (do_fork)
+        {
+          sigusr1_received = 0;
+          pid_t pid = _Fork ();
+          TEST_VERIFY_EXIT (pid != -1);
+          if (sigusr1_received)
+            ++fork_signals;
+          if (pid == 0)
+            _exit (0);
+          int status;
+          int ret = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0));
+          if (ret < 0)
+            FAIL_EXIT1 ("waitpid: %m");
+          TEST_COMPARE (status, 0);
+        }
+      sigusr1_received = 0;
+      free (objects[slot]);
+      if (sigusr1_received)
+        ++free_signals;
+      sigusr1_received = 0;
+      objects[slot] = malloc (size);
+      if (sigusr1_received)
+        ++malloc_signals;
+      xpthread_barrier_wait (&barrier);
+
+      if (objects[slot] == NULL || error_indicator != 0)
+        {
+          printf ("error: malloc: %m\n");
+          return 1;
+        }
+    }
+
+  /* Clean up allocations.  */
+  for (int slot = 0; slot < malloc_objects; ++slot)
+    free (objects[slot]);
+
+  printf ("info: signals received during fork: %u\n", fork_signals);
+  printf ("info: signals received during free: %u\n", free_signals);
+  printf ("info: signals received during malloc: %u\n", malloc_signals);
+
+  return 0;
+}
+
+#define TIMEOUT 100
+#include <support/test-driver.c>
diff --git a/manual/process.texi b/manual/process.texi
index 54e65f76c6..e17149ce60 100644
--- a/manual/process.texi
+++ b/manual/process.texi
@@ -138,8 +138,8 @@  creating a process and making it run another program.
 @cindex parent process
 @cindex subprocess
 A new processes is created when one of the functions
-@code{posix_spawn}, @code{fork}, or @code{vfork} is called.  (The
-@code{system} and @code{popen} also create new processes internally.)
+@code{posix_spawn}, @code{fork}, @code{_Fork} or @code{vfork} 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
@@ -155,9 +155,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} call returns.
-You can use the return value from @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} 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.
 
 @cindex process image
 Having several processes run the same program is only occasionally
@@ -249,16 +250,13 @@  It is declared in the header file @file{unistd.h}.
 @deftypefun pid_t fork (void)
 @standards{POSIX.1, unistd.h}
 @safety{@prelim{}@mtsafe{}@asunsafe{@ascuplugin{}}@acunsafe{@aculock{}}}
-@c The nptl/.../linux implementation safely collects fork_handlers into
-@c an alloca()ed linked list and increments ref counters; it uses atomic
-@c ops and retries, avoiding locking altogether.  It then takes the
-@c IO_list lock, resets the thread-local pid, and runs fork.  The parent
-@c restores the thread-local pid, releases the lock, and runs parent
-@c handlers, decrementing the ref count and signaling futex wait if
-@c requested by unregister_atfork.  The child bumps the fork generation,
-@c sets the thread-local pid, resets cpu clocks, initializes the robust
-@c mutex list, the stream locks, the IO_list lock, the dynamic loader
-@c lock, runs the child handlers, reseting ref counters to 1, and
+@c The posix/fork.c implementation iterates over the fork_handlers 
+@c using a lock.  It then takes the IO_list lock, resets the thread-local
+@c pid, and runs fork.  The parent releases the lock, and runs parent
+@c handlers, and unlocks the internal lock.  The child bumps the fork
+@c generation, sets the thread-local pid, resets cpu clocks, initializes
+@c the robust mutex list, the stream locks, the IO_list lock, the dynamic
+@c loader lock, runs the child handlers, reseting ref counters to 1, and
 @c initializes the fork lock.  These are all safe, unless atfork
 @c handlers themselves are unsafe.
 The @code{fork} function creates a new process.
@@ -322,6 +320,17 @@  process is cleared.  (The child process inherits its mask of blocked
 signals and signal actions from the parent process.)
 @end itemize
 
+@deftypefun pid_t _Fork (void)
+@standards{GNU, unistd.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+The @code{_Fork} function is similar to @code{fork} but does not issue
+any atfork callback registered with @code{pthread_atfork} neither reset
+any internal state or locks (such as the malloc one) and only setup a
+minimal state required to call async-signal-safe functions (such as raise
+or execve).
+
+It is an async-signal-safe replacement of @code{fork}.
+@end deftypefun
 
 @deftypefun pid_t vfork (void)
 @standards{BSD, unistd.h}
diff --git a/posix/Makefile b/posix/Makefile
index e0a7802012..25cd5bc456 100644
--- a/posix/Makefile
+++ b/posix/Makefile
@@ -114,7 +114,7 @@  test-srcs	:= globtest
 tests           += wordexp-test tst-exec tst-spawn tst-spawn2 tst-spawn3
 endif
 ifeq (yesyes,$(build-shared)$(have-thread-library))
-tests		+= tst-getopt-cancel
+tests		+= tst-getopt-cancel tst-_Fork
 endif
 tests-static	= tst-exec-static tst-spawn-static
 tests		+= $(tests-static)
@@ -283,6 +283,7 @@  $(objpfx)ptestcases.h: PTESTS PTESTS2C.sed
 $(objpfx)runptests.o: $(objpfx)ptestcases.h
 
 $(objpfx)tst-getopt-cancel: $(shared-thread-library)
+$(objpfx)tst-_Fork: $(shared-thread-library)
 
 test-xfail-annexc = yes
 $(objpfx)annexc.out: $(objpfx)annexc
diff --git a/posix/Versions b/posix/Versions
index cfd3819966..0ee433299e 100644
--- a/posix/Versions
+++ b/posix/Versions
@@ -147,6 +147,9 @@  libc {
   }
   GLIBC_2.30 {
   }
+  GLIBC_2.34 {
+    _Fork;
+  }
   GLIBC_PRIVATE {
     __libc_fork; __libc_pread; __libc_pwrite;
     __nanosleep_nocancel; __pause_nocancel;
diff --git a/posix/fork.c b/posix/fork.c
index 85c819c00f..1c4481f9c2 100644
--- a/posix/fork.c
+++ b/posix/fork.c
@@ -40,8 +40,10 @@  __libc_fork (void)
 {
   /* Determine if we are running multiple threads.  We skip some fork
      handlers in the single-thread case, to make fork safer to use in
-     signal handlers.  POSIX requires that fork is async-signal-safe,
-     but our current fork implementation is not.  */
+     signal handlers.  Although POSIX has dropped async-signal-safe
+     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 = __libc_single_threaded == 0;
 
   __run_fork_handlers (atfork_run_prepare, multiple_threads);
diff --git a/posix/tst-_Fork.c b/posix/tst-_Fork.c
new file mode 100644
index 0000000000..29a7ab6aaf
--- /dev/null
+++ b/posix/tst-_Fork.c
@@ -0,0 +1,218 @@ 
+/* Basic tests for _Fork.
+   Copyright (C) 2021 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 <array_length.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <support/check.h>
+#include <support/xsignal.h>
+#include <support/temp_file.h>
+#include <support/xunistd.h>
+#include <support/xthread.h>
+
+/* For single-thread, _Fork behaves like fork.  */
+static int
+singlethread_test (void)
+{
+  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", 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);
+
+  pid_t pid = _Fork ();
+  TEST_VERIFY_EXIT (pid != -1);
+  if (pid == 0)
+    {
+      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 (memcmp (buf, testdata1, testdatalen1), 0);
+
+      xlseek (tempfd, 0, SEEK_SET);
+      xwrite (tempfd, testdata2, testdatalen2);
+
+      close (tempfd);
+
+      _exit (EXIT_SUCCESS);
+    }
+
+  int status;
+  xwaitpid (pid, &status, 0);
+  TEST_VERIFY (WIFEXITED (status));
+  TEST_COMPARE (WEXITSTATUS (status), EXIT_SUCCESS);
+
+  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 (memcmp (buf, testdata2, testdatalen2), 0);
+
+  return 0;
+}
+
+
+static volatile sig_atomic_t sigusr1_handler_ran;
+static pid_t sig_pid = -1;
+#define SIG_PID_EXIT_CODE 20
+
+static void
+sigusr1_handler (int signum)
+{
+  TEST_COMPARE (signum, SIGUSR1);
+
+  pid_t ppid = getpid ();
+
+  sig_pid = _Fork ();
+  TEST_VERIFY_EXIT (sig_pid != -1);
+  if (sig_pid == 0)
+    {
+      TEST_VERIFY_EXIT (getpid () != ppid);
+      TEST_COMPARE (getppid(), ppid);
+
+      _exit (SIG_PID_EXIT_CODE);
+    }
+
+  sigusr1_handler_ran = 1;
+}
+
+/* _Fork is async-signal-safe, so check if it can successfully issue
+    a new process in a signal handler.  */
+static int
+singlethread_signal_test (void)
+{
+  xsignal (SIGUSR1, &sigusr1_handler);
+  /* Assume synchronous signal handling.  */
+  xraise (SIGUSR1);
+  TEST_COMPARE (sigusr1_handler_ran, 1);
+
+  int status;
+  xwaitpid (sig_pid, &status, 0);
+  TEST_VERIFY (WIFEXITED (status));
+  TEST_COMPARE (WEXITSTATUS (status), SIG_PID_EXIT_CODE);
+
+  xsignal (SIGUSR1, SIG_DFL);
+
+  return 0;
+}
+
+
+static bool atfork_prepare_var;
+static bool atfork_parent_var;
+static bool atfork_child_var;
+
+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;
+}
+
+/* Different than fork, _Fork does not execute any pthread_atfork
+   handlers.  */
+static int
+singlethread_atfork_test (void)
+{
+  pthread_atfork (atfork_prepare, atfork_parent, atfork_child);
+  singlethread_test ();
+  TEST_COMPARE (atfork_prepare_var, false);
+  TEST_COMPARE (atfork_parent_var, false);
+  TEST_COMPARE (atfork_child_var, false);
+
+  return 0;
+}
+
+
+static void *
+mt_atfork_test (void *args)
+{
+  singlethread_atfork_test ();
+
+  return NULL;
+}
+
+static int
+multithread_atfork_test (void)
+{
+  pthread_t thr = xpthread_create (NULL, mt_atfork_test, NULL);
+  xpthread_join (thr);
+
+  return 0;
+}
+
+
+static void *
+mt_signal_test (void *args)
+{
+  singlethread_signal_test ();
+
+  return NULL;
+}
+
+static int
+multithread_signal_test (void)
+{
+  pthread_t thr = xpthread_create (NULL, mt_signal_test, NULL);
+  xpthread_join (thr);
+
+  return 0;
+}
+
+
+static int
+do_test (void)
+{
+  singlethread_atfork_test ();
+  singlethread_signal_test ();
+
+  multithread_atfork_test ();
+  multithread_signal_test ();
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/posix/unistd.h b/posix/unistd.h
index 3f22763379..670d471ac2 100644
--- a/posix/unistd.h
+++ b/posix/unistd.h
@@ -776,6 +776,13 @@  extern __pid_t fork (void) __THROWNL;
 extern __pid_t vfork (void) __THROW;
 #endif /* Use misc or XPG < 7. */
 
+#ifdef __USE_GNU
+/* This is similar to fork, however it does not run the atfork handlers
+   neither reinitialize any internal locks in multithread case.
+   Different than fork, _Fork is async-signal-safe.  */
+extern __pid_t _Fork (void) __THROWNL;
+#endif
+
 
 /* Return the pathname of the terminal FD is open on, or NULL on errors.
    The returned storage is good only until the next call to this function.  */
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index 509e9b7cb0..55834ca18b 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2203,6 +2203,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 4cc1c6a591..dc812707a2 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2171,3 +2171,4 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 26ad9845e4..3b7e00c87b 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2253,6 +2253,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index bb9dfd4daf..7a06fd731c 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -1931,3 +1931,4 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index 3b0a47e967..946568ac6a 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -155,6 +155,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index 9ab3924888..4d2d167974 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -152,6 +152,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 14a84dac8f..d3092f5702 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2115,3 +2115,4 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 5c8502f3d3..78a6fe365a 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2074,6 +2074,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index ddc5837059..f21e646797 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2241,6 +2241,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index e3b345b803..811da8c935 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2106,6 +2106,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 25f2d1c08f..1223fc32d7 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -156,6 +156,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0x98
 GLIBC_2.4 _IO_2_1_stdin_ D 0x98
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index c4891479d3..035d8d17fd 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2186,6 +2186,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index 143b0163b4..55d7144063 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2166,3 +2166,4 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index 13d374a031..f4223a84cf 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2163,3 +2163,4 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index b2295f1937..1ccb65fe80 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2157,6 +2157,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 4c786070d0..27b7489778 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2155,6 +2155,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index aa9c6a4dca..05b7afe7c8 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2163,6 +2163,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 5939588ad5..aa3845e160 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2157,6 +2157,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 92556c4237..efe35b8751 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2204,3 +2204,4 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 26c93dff05..eda0349d39 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2213,6 +2213,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index f04b167788..c51540783d 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2246,6 +2246,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index c2ca00709e..a508ef7e7a 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2076,6 +2076,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index 0ea50dc851..9366d54213 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2366,3 +2366,4 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index 0263e284d4..2a427fb493 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -1933,3 +1933,4 @@  GLIBC_2.33 wprintf F
 GLIBC_2.33 write F
 GLIBC_2.33 writev F
 GLIBC_2.33 wscanf F
+GLIBC_2.34 _Fork F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 1626c5351f..26dcd9ec47 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2133,3 +2133,4 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index a66426eb4d..753b7a99fa 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2211,6 +2211,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index ab351873ae..454f8d72ba 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2112,6 +2112,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index 22ceaa3d87..fe4d7b31ab 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2081,6 +2081,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index d36f228192..7ef2df0f07 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2078,6 +2078,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 59b4313280..380c51907a 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2202,6 +2202,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 266dcdfa08..3c3ad9188d 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2129,6 +2129,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 2744bba4af..aff31183eb 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2088,6 +2088,7 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index ce2f4fb72b..fd6e98861c 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2185,3 +2185,4 @@  GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
+GLIBC_2.34 _Fork F