diff --git a/SOURCES/glibc-rh1888660.patch b/SOURCES/glibc-rh1888660.patch
new file mode 100644
index 0000000..ec80b81
--- /dev/null
+++ b/SOURCES/glibc-rh1888660.patch
@@ -0,0 +1,767 @@
+This patch is a RHEL-8.7 backport of the following upstream commit:
+
+commit 52a103e237329b9f88a28513fe7506ffc3bd8ced
+Author: Arjun Shankar <arjun@redhat.com>
+Date:   Tue May 24 17:57:36 2022 +0200
+
+    Fix deadlock when pthread_atfork handler calls pthread_atfork or dlclose
+    
+    In multi-threaded programs, registering via pthread_atfork,
+    de-registering implicitly via dlclose, or running pthread_atfork
+    handlers during fork was protected by an internal lock.  This meant
+    that a pthread_atfork handler attempting to register another handler or
+    dlclose a dynamically loaded library would lead to a deadlock.
+    
+    This commit fixes the deadlock in the following way:
+    
+    During the execution of handlers at fork time, the atfork lock is
+    released prior to the execution of each handler and taken again upon its
+    return.  Any handler registrations or de-registrations that occurred
+    during the execution of the handler are accounted for before proceeding
+    with further handler execution.
+    
+    If a handler that hasn't been executed yet gets de-registered by another
+    handler during fork, it will not be executed.   If a handler gets
+    registered by another handler during fork, it will not be executed
+    during that particular fork.
+    
+    The possibility that handlers may now be registered or deregistered
+    during handler execution means that identifying the next handler to be
+    run after a given handler may register/de-register others requires some
+    bookkeeping.  The fork_handler struct has an additional field, 'id',
+    which is assigned sequentially during registration.  Thus, handlers are
+    executed in ascending order of 'id' during 'prepare', and descending
+    order of 'id' during parent/child handler execution after the fork.
+    
+    Two tests are included:
+    
+    * tst-atfork3: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+      This test exercises calling dlclose from prepare, parent, and child
+      handlers.
+    
+    * tst-atfork4: This test exercises calling pthread_atfork and dlclose
+      from the prepare handler.
+    
+    [BZ #24595, BZ #27054]
+    
+    Co-authored-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+diff --git a/nptl/Makefile b/nptl/Makefile
+index 70a3be23ecfcd9c9..76c914e23e8873f2 100644
+--- a/nptl/Makefile
++++ b/nptl/Makefile
+@@ -382,8 +382,17 @@ tests += tst-cancelx2 tst-cancelx3 tst-cancelx4 tst-cancelx5 \
+ 	 tst-cancelx16 tst-cancelx17 tst-cancelx18 tst-cancelx20 tst-cancelx21 \
+ 	 tst-cleanupx0 tst-cleanupx1 tst-cleanupx2 tst-cleanupx3 tst-cleanupx4
+ ifeq ($(build-shared),yes)
+-tests += tst-atfork2 tst-tls4 tst-_res1 tst-fini1 tst-compat-forwarder \
+-	 tst-audit-threads
++tests += \
++  tst-atfork2 \
++  tst-tls4 \
++  tst-_res1 \
++  tst-fini1 \
++  tst-compat-forwarder \
++  tst-audit-threads \
++  tst-atfork3 \
++  tst-atfork4 \
++# tests
++
+ tests-internal += tst-tls3 tst-tls3-malloc tst-tls5 tst-stackguard1
+ tests-nolibpthread += tst-fini1
+ ifeq ($(have-z-execstack),yes)
+@@ -391,18 +400,39 @@ tests += tst-execstack
+ endif
+ endif
+ 
+-modules-names = tst-atfork2mod tst-tls3mod tst-tls4moda tst-tls4modb \
+-		tst-tls5mod tst-tls5moda tst-tls5modb tst-tls5modc \
+-		tst-tls5modd tst-tls5mode tst-tls5modf tst-stack4mod \
+-		tst-_res1mod1 tst-_res1mod2 tst-execstack-mod tst-fini1mod \
+-		tst-join7mod tst-compat-forwarder-mod tst-audit-threads-mod1 \
+-		tst-audit-threads-mod2
++modules-names = \
++  tst-atfork2mod \
++  tst-tls3mod \
++  tst-tls4moda \
++  tst-tls4modb \
++  tst-tls5mod \
++  tst-tls5moda \
++  tst-tls5modb \
++  tst-tls5modc \
++  tst-tls5modd \
++  tst-tls5mode \
++  tst-tls5modf \
++  tst-stack4mod \
++  tst-_res1mod1 \
++  tst-_res1mod2 \
++  tst-execstack-mod \
++  tst-fini1mod \
++  tst-join7mod \
++  tst-compat-forwarder-mod \
++  tst-audit-threads-mod1 \
++  tst-audit-threads-mod2 \
++  tst-atfork3mod \
++  tst-atfork4mod \
++# module-names
++
+ extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) \
+ 		   tst-cleanup4aux.o tst-cleanupx4aux.o
+ test-extras += tst-cleanup4aux tst-cleanupx4aux
+ test-modules = $(addprefix $(objpfx),$(addsuffix .so,$(modules-names)))
+ 
+ tst-atfork2mod.so-no-z-defs = yes
++tst-atfork3mod.so-no-z-defs = yes
++tst-atfork4mod.so-no-z-defs = yes
+ tst-tls3mod.so-no-z-defs = yes
+ tst-tls5mod.so-no-z-defs = yes
+ tst-tls5moda.so-no-z-defs = yes
+@@ -541,6 +571,14 @@ LDFLAGS-tst-atfork2 = -rdynamic
+ tst-atfork2-ENV = MALLOC_TRACE=$(objpfx)tst-atfork2.mtrace
+ $(objpfx)tst-atfork2mod.so: $(shared-thread-library)
+ 
++$(objpfx)tst-atfork3: $(libdl) $(shared-thread-library)
++LDFLAGS-tst-atfork3 = -rdynamic
++$(objpfx)tst-atfork3mod.so: $(shared-thread-library)
++
++$(objpfx)tst-atfork4: $(libdl) $(shared-thread-library)
++LDFLAGS-tst-atfork4 = -rdynamic
++$(objpfx)tst-atfork4mod.so: $(shared-thread-library)
++
+ tst-stack3-ENV = MALLOC_TRACE=$(objpfx)tst-stack3.mtrace
+ $(objpfx)tst-stack3-mem.out: $(objpfx)tst-stack3.out
+ 	$(common-objpfx)malloc/mtrace $(objpfx)tst-stack3.mtrace > $@; \
+@@ -640,6 +678,8 @@ $(objpfx)../libc.so: $(common-objpfx)libc.so ;
+ $(addprefix $(objpfx),$(tests-static) $(xtests-static)): $(objpfx)libpthread.a
+ 
+ $(objpfx)tst-atfork2.out: $(objpfx)tst-atfork2mod.so
++$(objpfx)tst-atfork3.out: $(objpfx)tst-atfork3mod.so
++$(objpfx)tst-atfork4.out: $(objpfx)tst-atfork4mod.so
+ else
+ $(addprefix $(objpfx),$(tests) $(test-srcs)): $(objpfx)libpthread.a
+ endif
+diff --git a/nptl/register-atfork.c b/nptl/register-atfork.c
+index 9edb7d4bbb49fbed..4c1e20ae8cab005f 100644
+--- a/nptl/register-atfork.c
++++ b/nptl/register-atfork.c
+@@ -21,6 +21,8 @@
+ #include <string.h>
+ #include <fork.h>
+ #include <atomic.h>
++#include <intprops.h>
++#include <stdio.h>
+ 
+ #define DYNARRAY_ELEMENT           struct fork_handler
+ #define DYNARRAY_STRUCT            fork_handler_list
+@@ -29,7 +31,7 @@
+ #include <malloc/dynarray-skeleton.c>
+ 
+ static struct fork_handler_list fork_handlers;
+-static bool fork_handler_init = false;
++static uint64_t fork_handler_counter;
+ 
+ static int atfork_lock = LLL_LOCK_INITIALIZER;
+ 
+@@ -39,11 +41,8 @@ __register_atfork (void (*prepare) (void), void (*parent) (void),
+ {
+   lll_lock (atfork_lock, LLL_PRIVATE);
+ 
+-  if (!fork_handler_init)
+-    {
+-      fork_handler_list_init (&fork_handlers);
+-      fork_handler_init = true;
+-    }
++  if (fork_handler_counter == 0)
++    fork_handler_list_init (&fork_handlers);
+ 
+   struct fork_handler *newp = fork_handler_list_emplace (&fork_handlers);
+   if (newp != NULL)
+@@ -52,6 +51,13 @@ __register_atfork (void (*prepare) (void), void (*parent) (void),
+       newp->parent_handler = parent;
+       newp->child_handler = child;
+       newp->dso_handle = dso_handle;
++
++      /* IDs assigned to handlers start at 1 and increment with handler
++         registration.  Un-registering a handlers discards the corresponding
++         ID.  It is not reused in future registrations.  */
++      if (INT_ADD_OVERFLOW (fork_handler_counter, 1))
++        __libc_fatal ("fork handler counter overflow");
++      newp->id = ++fork_handler_counter;
+     }
+ 
+   /* Release the lock.  */
+@@ -106,37 +112,111 @@ __unregister_atfork (void *dso_handle)
+   lll_unlock (atfork_lock, LLL_PRIVATE);
+ }
+ 
+-void
+-__run_fork_handlers (enum __run_fork_handler_type who, _Bool do_locking)
++uint64_t
++__run_prefork_handlers (_Bool do_locking)
+ {
+-  struct fork_handler *runp;
++  uint64_t lastrun;
+ 
+-  if (who == atfork_run_prepare)
++  if (do_locking)
++    lll_lock (atfork_lock, LLL_PRIVATE);
++
++  /* We run prepare handlers from last to first.  After fork, only
++     handlers up to the last handler found here (pre-fork) will be run.
++     Handlers registered during __run_prefork_handlers or
++     __run_postfork_handlers will be positioned after this last handler, and
++     since their prepare handlers won't be run now, their parent/child
++     handlers should also be ignored.  */
++  lastrun = fork_handler_counter;
++
++  size_t sl = fork_handler_list_size (&fork_handlers);
++  for (size_t i = sl; i > 0;)
+     {
+-      if (do_locking)
+-	lll_lock (atfork_lock, LLL_PRIVATE);
+-      size_t sl = fork_handler_list_size (&fork_handlers);
+-      for (size_t i = sl; i > 0; i--)
+-	{
+-	  runp = fork_handler_list_at (&fork_handlers, i - 1);
+-	  if (runp->prepare_handler != NULL)
+-	    runp->prepare_handler ();
+-	}
++      struct fork_handler *runp
++        = fork_handler_list_at (&fork_handlers, i - 1);
++
++      uint64_t id = runp->id;
++
++      if (runp->prepare_handler != NULL)
++        {
++          if (do_locking)
++            lll_unlock (atfork_lock, LLL_PRIVATE);
++
++          runp->prepare_handler ();
++
++          if (do_locking)
++            lll_lock (atfork_lock, LLL_PRIVATE);
++        }
++
++      /* We unlocked, ran the handler, and locked again.  In the
++         meanwhile, one or more deregistrations could have occurred leading
++         to the current (just run) handler being moved up the list or even
++         removed from the list itself.  Since handler IDs are guaranteed to
++         to be in increasing order, the next handler has to have:  */
++
++      /* A. An earlier position than the current one has.  */
++      i--;
++
++      /* B. A lower ID than the current one does.  The code below skips
++         any newly added handlers with higher IDs.  */
++      while (i > 0
++             && fork_handler_list_at (&fork_handlers, i - 1)->id >= id)
++        i--;
+     }
+-  else
++
++  return lastrun;
++}
++
++void
++__run_postfork_handlers (enum __run_fork_handler_type who, _Bool do_locking,
++                         uint64_t lastrun)
++{
++  size_t sl = fork_handler_list_size (&fork_handlers);
++  for (size_t i = 0; i < sl;)
+     {
+-      size_t sl = fork_handler_list_size (&fork_handlers);
+-      for (size_t i = 0; i < sl; i++)
+-	{
+-	  runp = fork_handler_list_at (&fork_handlers, i);
+-	  if (who == atfork_run_child && runp->child_handler)
+-	    runp->child_handler ();
+-	  else if (who == atfork_run_parent && runp->parent_handler)
+-	    runp->parent_handler ();
+-	}
++      struct fork_handler *runp = fork_handler_list_at (&fork_handlers, i);
++      uint64_t id = runp->id;
++
++      /* prepare handlers were not run for handlers with ID > LASTRUN.
++         Thus, parent/child handlers will also not be run.  */
++      if (id > lastrun)
++        break;
++
+       if (do_locking)
+-	lll_unlock (atfork_lock, LLL_PRIVATE);
++        lll_unlock (atfork_lock, LLL_PRIVATE);
++
++      if (who == atfork_run_child && runp->child_handler)
++        runp->child_handler ();
++      else if (who == atfork_run_parent && runp->parent_handler)
++        runp->parent_handler ();
++
++      if (do_locking)
++        lll_lock (atfork_lock, LLL_PRIVATE);
++
++      /* We unlocked, ran the handler, and locked again.  In the meanwhile,
++         one or more [de]registrations could have occurred.  Due to this,
++         the list size must be updated.  */
++      sl = fork_handler_list_size (&fork_handlers);
++
++      /* The just-run handler could also have moved up the list. */
++
++      if (sl > i && fork_handler_list_at (&fork_handlers, i)->id == id)
++        /* The position of the recently run handler hasn't changed.  The
++           next handler to be run is an easy increment away.  */
++        i++;
++      else
++        {
++          /* The next handler to be run is the first handler in the list
++             to have an ID higher than the current one.  */
++          for (i = 0; i < sl; i++)
++            {
++              if (fork_handler_list_at (&fork_handlers, i)->id > id)
++                break;
++            }
++        }
+     }
++
++  if (do_locking)
++    lll_unlock (atfork_lock, LLL_PRIVATE);
+ }
+ 
+ 
+diff --git a/nptl/tst-atfork3.c b/nptl/tst-atfork3.c
+new file mode 100644
+index 0000000000000000..bb2250e432ab79ad
+--- /dev/null
++++ b/nptl/tst-atfork3.c
+@@ -0,0 +1,118 @@
++/* Check if pthread_atfork handler can call dlclose (BZ#24595).
++   Copyright (C) 2022 Free Software Foundation, Inc.
++   This file is part of the GNU C Library.
++
++   The GNU C Library is free software; you can redistribute it and/or
++   modify it under the terms of the GNU Lesser General Public
++   License as published by the Free Software Foundation; either
++   version 2.1 of the License, or (at your option) any later version.
++
++   The GNU C Library is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++   Lesser General Public License for more details.
++
++   You should have received a copy of the GNU Lesser General Public
++   License along with the GNU C Library; if not, see
++   <http://www.gnu.org/licenses/>.  */
++
++#include <stdio.h>
++#include <pthread.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include <stdbool.h>
++
++#include <support/check.h>
++#include <support/xthread.h>
++#include <support/capture_subprocess.h>
++#include <support/xdlfcn.h>
++
++/* Check if pthread_atfork handlers do not deadlock when calling a function
++   that might alter the internal fork handle list, such as dlclose.
++
++   The test registers a callback set with pthread_atfork(), dlopen() a shared
++   library (nptl/tst-atfork3mod.c), calls an exported symbol from the library
++   (which in turn also registers atfork handlers), and calls fork to trigger
++   the callbacks.  */
++
++static void *handler;
++static bool run_dlclose_prepare;
++static bool run_dlclose_parent;
++static bool run_dlclose_child;
++
++static void
++prepare (void)
++{
++  if (run_dlclose_prepare)
++    xdlclose (handler);
++}
++
++static void
++parent (void)
++{
++  if (run_dlclose_parent)
++    xdlclose (handler);
++}
++
++static void
++child (void)
++{
++  if (run_dlclose_child)
++    xdlclose (handler);
++}
++
++static void
++proc_func (void *closure)
++{
++}
++
++static void
++do_test_generic (bool dlclose_prepare, bool dlclose_parent, bool dlclose_child)
++{
++  run_dlclose_prepare = dlclose_prepare;
++  run_dlclose_parent = dlclose_parent;
++  run_dlclose_child = dlclose_child;
++
++  handler = xdlopen ("tst-atfork3mod.so", RTLD_NOW);
++
++  int (*atfork3mod_func)(void);
++  atfork3mod_func = xdlsym (handler, "atfork3mod_func");
++
++  atfork3mod_func ();
++
++  struct support_capture_subprocess proc
++    = support_capture_subprocess (proc_func, NULL);
++  support_capture_subprocess_check (&proc, "tst-atfork3", 0, sc_allow_none);
++
++  handler = atfork3mod_func = NULL;
++
++  support_capture_subprocess_free (&proc);
++}
++
++static void *
++thread_func (void *closure)
++{
++  return NULL;
++}
++
++static int
++do_test (void)
++{
++  {
++    /* Make the process acts as multithread.  */
++    pthread_attr_t attr;
++    xpthread_attr_init (&attr);
++    xpthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
++    xpthread_create (&attr, thread_func, NULL);
++  }
++
++  TEST_COMPARE (pthread_atfork (prepare, parent, child), 0);
++
++  do_test_generic (true  /* prepare */, false /* parent */, false /* child */);
++  do_test_generic (false /* prepare */, true  /* parent */, false /* child */);
++  do_test_generic (false /* prepare */, false /* parent */, true  /* child */);
++
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/nptl/tst-atfork3mod.c b/nptl/tst-atfork3mod.c
+new file mode 100644
+index 0000000000000000..6d0658cb9efdecbc
+--- /dev/null
++++ b/nptl/tst-atfork3mod.c
+@@ -0,0 +1,44 @@
++/* Copyright (C) 2022 Free Software Foundation, Inc.
++   This file is part of the GNU C Library.
++
++   The GNU C Library is free software; you can redistribute it and/or
++   modify it under the terms of the GNU Lesser General Public
++   License as published by the Free Software Foundation; either
++   version 2.1 of the License, or (at your option) any later version.
++
++   The GNU C Library is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++   Lesser General Public License for more details.
++
++   You should have received a copy of the GNU Lesser General Public
++   License along with the GNU C Library; if not, see
++   <http://www.gnu.org/licenses/>.  */
++
++#include <unistd.h>
++#include <stdlib.h>
++#include <pthread.h>
++
++#include <support/check.h>
++
++static void
++mod_prepare (void)
++{
++}
++
++static void
++mod_parent (void)
++{
++}
++
++static void
++mod_child (void)
++{
++}
++
++int atfork3mod_func (void)
++{
++  TEST_COMPARE (pthread_atfork (mod_prepare, mod_parent, mod_child), 0);
++
++  return 0;
++}
+diff --git a/nptl/tst-atfork4.c b/nptl/tst-atfork4.c
+new file mode 100644
+index 0000000000000000..52dc87e73b846ab9
+--- /dev/null
++++ b/nptl/tst-atfork4.c
+@@ -0,0 +1,128 @@
++/* pthread_atfork supports handlers that call pthread_atfork or dlclose.
++   Copyright (C) 2022 Free Software Foundation, Inc.
++   This file is part of the GNU C Library.
++
++   The GNU C Library is free software; you can redistribute it and/or
++   modify it under the terms of the GNU Lesser General Public
++   License as published by the Free Software Foundation; either
++   version 2.1 of the License, or (at your option) any later version.
++
++   The GNU C Library is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++   Lesser General Public License for more details.
++
++   You should have received a copy of the GNU Lesser General Public
++   License along with the GNU C Library; if not, see
++   <https://www.gnu.org/licenses/>.  */
++
++#include <support/xdlfcn.h>
++#include <stdio.h>
++#include <support/xthread.h>
++#include <sys/types.h>
++#include <sys/wait.h>
++#include <support/xunistd.h>
++#include <support/check.h>
++#include <stdlib.h>
++
++static void *
++thread_func (void *x)
++{
++  return NULL;
++}
++
++static unsigned int second_atfork_handler_runcount = 0;
++
++static void
++second_atfork_handler (void)
++{
++  second_atfork_handler_runcount++;
++}
++
++static void *h = NULL;
++
++static unsigned int atfork_handler_runcount = 0;
++
++static void
++prepare (void)
++{
++  /* These atfork handlers are registered while atfork handlers are being
++     executed and thus will not be executed during the corresponding
++     fork.  */
++  TEST_VERIFY_EXIT (pthread_atfork (second_atfork_handler,
++                                    second_atfork_handler,
++                                    second_atfork_handler) == 0);
++
++  /* This will de-register the atfork handlers registered by the dlopen'd
++     library and so they will not be executed.  */
++  if (h != NULL)
++    {
++      xdlclose (h);
++      h = NULL;
++    }
++
++  atfork_handler_runcount++;
++}
++
++static void
++after (void)
++{
++  atfork_handler_runcount++;
++}
++
++static int
++do_test (void)
++{
++  /* Make sure __libc_single_threaded is 0.  */
++  pthread_attr_t attr;
++  xpthread_attr_init (&attr);
++  xpthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
++  xpthread_create (&attr, thread_func, NULL);
++
++  void (*reg_atfork_handlers) (void);
++
++  h = xdlopen ("tst-atfork4mod.so", RTLD_LAZY);
++
++  reg_atfork_handlers = xdlsym (h, "reg_atfork_handlers");
++
++  reg_atfork_handlers ();
++
++  /* We register our atfork handlers *after* loading the module so that our
++     prepare handler is called first at fork, where we then dlclose the
++     module before its prepare handler has a chance to be called.  */
++  TEST_VERIFY_EXIT (pthread_atfork (prepare, after, after) == 0);
++
++  pid_t pid = xfork ();
++
++  /* Both the parent and the child processes should observe this.  */
++  TEST_VERIFY_EXIT (atfork_handler_runcount == 2);
++  TEST_VERIFY_EXIT (second_atfork_handler_runcount == 0);
++
++  if (pid > 0)
++    {
++      int childstat;
++
++      xwaitpid (-1, &childstat, 0);
++      TEST_VERIFY_EXIT (WIFEXITED (childstat)
++                        && WEXITSTATUS (childstat) == 0);
++
++      /* This time, the second set of atfork handlers should also be called
++         since the handlers are already in place before fork is called.  */
++
++      pid = xfork ();
++
++      TEST_VERIFY_EXIT (atfork_handler_runcount == 4);
++      TEST_VERIFY_EXIT (second_atfork_handler_runcount == 2);
++
++      if (pid > 0)
++        {
++          xwaitpid (-1, &childstat, 0);
++          TEST_VERIFY_EXIT (WIFEXITED (childstat)
++                            && WEXITSTATUS (childstat) == 0);
++        }
++    }
++
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/nptl/tst-atfork4mod.c b/nptl/tst-atfork4mod.c
+new file mode 100644
+index 0000000000000000..e111efeb185916e0
+--- /dev/null
++++ b/nptl/tst-atfork4mod.c
+@@ -0,0 +1,48 @@
++/* pthread_atfork supports handlers that call pthread_atfork or dlclose.
++   Copyright (C) 2022 Free Software Foundation, Inc.
++   This file is part of the GNU C Library.
++
++   The GNU C Library is free software; you can redistribute it and/or
++   modify it under the terms of the GNU Lesser General Public
++   License as published by the Free Software Foundation; either
++   version 2.1 of the License, or (at your option) any later version.
++
++   The GNU C Library is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++   Lesser General Public License for more details.
++
++   You should have received a copy of the GNU Lesser General Public
++   License along with the GNU C Library; if not, see
++   <https://www.gnu.org/licenses/>.  */
++
++#include <pthread.h>
++#include <stdlib.h>
++
++/* This dynamically loaded library simply registers its atfork handlers when
++   asked to.  The atfork handlers should never be executed because the
++   library is unloaded before fork is called by the test program.  */
++
++static void
++prepare (void)
++{
++  abort ();
++}
++
++static void
++parent (void)
++{
++  abort ();
++}
++
++static void
++child (void)
++{
++  abort ();
++}
++
++void
++reg_atfork_handlers (void)
++{
++  pthread_atfork (prepare, parent, child);
++}
+diff --git a/sysdeps/nptl/fork.c b/sysdeps/nptl/fork.c
+index b4d20fa652f4ba3b..1324b813136764fc 100644
+--- a/sysdeps/nptl/fork.c
++++ b/sysdeps/nptl/fork.c
+@@ -54,8 +54,9 @@ __libc_fork (void)
+      signal handlers.  POSIX requires that fork is async-signal-safe,
+      but our current fork implementation is not.  */
+   bool multiple_threads = THREAD_GETMEM (THREAD_SELF, header.multiple_threads);
++  uint64_t lastrun;
+ 
+-  __run_fork_handlers (atfork_run_prepare, multiple_threads);
++  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
+@@ -129,7 +130,7 @@ __libc_fork (void)
+       __rtld_lock_initialize (GL(dl_load_tls_lock));
+ 
+       /* Run the handlers registered for the child.  */
+-      __run_fork_handlers (atfork_run_child, multiple_threads);
++      __run_postfork_handlers (atfork_run_child, multiple_threads, lastrun);
+     }
+   else
+     {
+@@ -144,7 +145,7 @@ __libc_fork (void)
+ 	}
+ 
+       /* Run the handlers registered for the parent.  */
+-      __run_fork_handlers (atfork_run_parent, multiple_threads);
++      __run_postfork_handlers (atfork_run_parent, multiple_threads, lastrun);
+     }
+ 
+   return pid;
+diff --git a/sysdeps/nptl/fork.h b/sysdeps/nptl/fork.h
+index bef2b7a8a6af8635..222c4f618970a455 100644
+--- a/sysdeps/nptl/fork.h
++++ b/sysdeps/nptl/fork.h
+@@ -31,6 +31,7 @@ struct fork_handler
+   void (*parent_handler) (void);
+   void (*child_handler) (void);
+   void *dso_handle;
++  uint64_t id;
+ };
+ 
+ /* Function to call to unregister fork handlers.  */
+@@ -44,19 +45,18 @@ enum __run_fork_handler_type
+   atfork_run_parent
+ };
+ 
+-/* Run the atfork handlers and lock/unlock the internal lock depending
+-   of the WHO argument:
++/* Run the atfork prepare handlers in the reverse order of registration and
++   return the ID of the last registered handler.  If DO_LOCKING is true, the
++   internal lock is held locked upon return.  */
++extern uint64_t __run_prefork_handlers (_Bool do_locking) attribute_hidden;
+ 
+-   - atfork_run_prepare: run all the PREPARE_HANDLER in reverse order of
+-			 insertion and locks the internal lock.
+-   - atfork_run_child: run all the CHILD_HANDLER and unlocks the internal
+-		       lock.
+-   - atfork_run_parent: run all the PARENT_HANDLER and unlocks the internal
+-			lock.
+-
+-   Perform locking only if DO_LOCKING.  */
+-extern void __run_fork_handlers (enum __run_fork_handler_type who,
+-				 _Bool do_locking) attribute_hidden;
++/* Given a handler type (parent or child), run all the atfork handlers in
++   the order of registration up to and including the handler with id equal
++   to LASTRUN.  If DO_LOCKING is true, the internal lock is unlocked prior
++   to return.  */
++extern void __run_postfork_handlers (enum __run_fork_handler_type who,
++                                     _Bool do_locking,
++                                     uint64_t lastrun) attribute_hidden;
+ 
+ /* C library side function to register new fork handlers.  */
+ extern int __register_atfork (void (*__prepare) (void),
diff --git a/SPECS/glibc.spec b/SPECS/glibc.spec
index 4dc4642..9636a1e 100644
--- a/SPECS/glibc.spec
+++ b/SPECS/glibc.spec
@@ -1,6 +1,6 @@
 %define glibcsrcdir glibc-2.28
 %define glibcversion 2.28
-%define glibcrelease 207%{?dist}
+%define glibcrelease 208%{?dist}
 # Pre-release tarballs are pulled in from git using a command that is
 # effectively:
 #
@@ -904,6 +904,7 @@ Patch709: glibc-rh2089247-4.patch
 Patch710: glibc-rh2089247-5.patch
 Patch711: glibc-rh2089247-6.patch
 Patch712: glibc-rh2091553.patch
+Patch713: glibc-rh1888660.patch
 
 # Intel Optimizations
 Patch10001: glibc-sw24097-1.patch
@@ -2849,7 +2850,10 @@ fi
 %files -f compat-libpthread-nonshared.filelist -n compat-libpthread-nonshared
 
 %changelog
-* Thu Jun 09 2022 Ali Erdinc Koroglu <aekoroglu@centosproject.org> - 2.28.207
+* Thu Jun 09 2022 Arjun Shankar <arjun@redhat.com> - 2.28-208
+- Fix deadlocks in pthread_atfork handlers (#1888660) 
+
+* Thu Jun 09 2022 Ali Erdinc Koroglu <aekoroglu@centosproject.org> - 2.28-207
 - Intel architecture optimizations
 
 * Tue Jun 07 2022 DJ Delorie <dj@redhat.com) - 2.28-206
@@ -2858,7 +2862,7 @@ fi
 * Mon May 23 2022 Florian Weimer <fweimer@redhat.com> - 2.28-205
 - Increase tempnam randomness (#2089247)
 
-* Fri May 20 2022 Ali Erdinc Koroglu <aekoroglu@centosproject.org> - 2.28.204
+* Fri May 20 2022 Ali Erdinc Koroglu <aekoroglu@centosproject.org> - 2.28-204
 - Intel architecture optimizations
 
 * Tue May 17 2022 Patsy Griffin <patsy@redhat.com> - 2.28-203