arrfab / rpms / glibc

Forked from rpms/glibc 4 years ago
Clone

Blame SOURCES/glibc-rh906468-1.patch

147e83
Backport of these upstream commits:
147e83
147e83
commit 29d794863cd6e03115d3670707cc873a9965ba92
147e83
Author: Florian Weimer <fweimer@redhat.com>
147e83
Date:   Thu Apr 14 09:17:02 2016 +0200
147e83
147e83
    malloc: Run fork handler as late as possible [BZ #19431]
147e83
    
147e83
    Previously, a thread M invoking fork would acquire locks in this order:
147e83
    
147e83
      (M1) malloc arena locks (in the registered fork handler)
147e83
      (M2) libio list lock
147e83
    
147e83
    A thread F invoking flush (NULL) would acquire locks in this order:
147e83
    
147e83
      (F1) libio list lock
147e83
      (F2) individual _IO_FILE locks
147e83
    
147e83
    A thread G running getdelim would use this order:
147e83
    
147e83
      (G1) _IO_FILE lock
147e83
      (G2) malloc arena lock
147e83
    
147e83
    After executing (M1), (F1), (G1), none of the threads can make progress.
147e83
    
147e83
    This commit changes the fork lock order to:
147e83
    
147e83
      (M'1) libio list lock
147e83
      (M'2) malloc arena locks
147e83
    
147e83
    It explicitly encodes the lock order in the implementations of fork,
147e83
    and does not rely on the registration order, thus avoiding the deadlock.
147e83
147e83
commit 186fe877f3df0b84d57dfbf0386f6332c6aa69bc
147e83
Author: Florian Weimer <fweimer@redhat.com>
147e83
Date:   Thu Apr 14 12:53:03 2016 +0200
147e83
147e83
    malloc: Add missing internal_function attributes on function definitions
147e83
    
147e83
    Fixes build on i386 after commit 29d794863cd6e03115d3670707cc873a9965ba92.
147e83
147e83
Index: b/malloc/Makefile
147e83
===================================================================
147e83
--- a/malloc/Makefile
147e83
+++ b/malloc/Makefile
147e83
@@ -28,7 +28,7 @@ tests := mallocbug tst-malloc tst-valloc
147e83
 	 tst-mallocstate tst-mcheck tst-mallocfork tst-trim1 \
147e83
 	 tst-malloc-usable \
147e83
 	 tst-malloc-backtrace tst-malloc-thread-exit \
147e83
-	 tst-malloc-thread-fail
147e83
+	 tst-malloc-thread-fail tst-malloc-fork-deadlock
147e83
 test-srcs = tst-mtrace
147e83
 
147e83
 routines = malloc morecore mcheck mtrace obstack
147e83
@@ -49,6 +49,7 @@ $(objpfx)tst-malloc-thread-fail: $(commo
147e83
 			       $(common-objpfx)nptl/libpthread_nonshared.a
147e83
 $(objpfx)tst-malloc-thread-exit: $(common-objpfx)nptl/libpthread.so \
147e83
 			       $(common-objpfx)nptl/libpthread_nonshared.a
147e83
+$(objpfx)tst-malloc-fork-deadlock: $(shared-thread-library)
147e83
 
147e83
 # These should be removed by `make clean'.
147e83
 extra-objs = mcheck-init.o libmcheck.a
147e83
Index: b/malloc/arena.c
147e83
===================================================================
147e83
--- a/malloc/arena.c
147e83
+++ b/malloc/arena.c
147e83
@@ -162,10 +162,6 @@ static void           (*save_free_hook)
147e83
 					 const __malloc_ptr_t);
147e83
 static void*        save_arena;
147e83
 
147e83
-#ifdef ATFORK_MEM
147e83
-ATFORK_MEM;
147e83
-#endif
147e83
-
147e83
 /* Magic value for the thread-specific arena pointer when
147e83
    malloc_atfork() is in use.  */
147e83
 
147e83
@@ -228,14 +224,15 @@ free_atfork(void* mem, const void *calle
147e83
 /* Counter for number of times the list is locked by the same thread.  */
147e83
 static unsigned int atfork_recursive_cntr;
147e83
 
147e83
-/* The following two functions are registered via thread_atfork() to
147e83
-   make sure that the mutexes remain in a consistent state in the
147e83
-   fork()ed version of a thread.  Also adapt the malloc and free hooks
147e83
-   temporarily, because the `atfork' handler mechanism may use
147e83
-   malloc/free internally (e.g. in LinuxThreads). */
147e83
+/* The following three functions are called around fork from a
147e83
+   multi-threaded process.  We do not use the general fork handler
147e83
+   mechanism to make sure that our handlers are the last ones being
147e83
+   called, so that other fork handlers can use the malloc
147e83
+   subsystem.  */
147e83
 
147e83
-static void
147e83
-ptmalloc_lock_all (void)
147e83
+void
147e83
+internal_function
147e83
+__malloc_fork_lock_parent (void)
147e83
 {
147e83
   mstate ar_ptr;
147e83
 
147e83
@@ -243,7 +240,7 @@ ptmalloc_lock_all (void)
147e83
     return;
147e83
 
147e83
   /* We do not acquire free_list_lock here because we completely
147e83
-     reconstruct free_list in ptmalloc_unlock_all2.  */
147e83
+     reconstruct free_list in __malloc_fork_unlock_child.  */
147e83
 
147e83
   if (mutex_trylock(&list_lock))
147e83
     {
147e83
@@ -268,7 +265,7 @@ ptmalloc_lock_all (void)
147e83
   __free_hook = free_atfork;
147e83
   /* Only the current thread may perform malloc/free calls now.
147e83
      save_arena will be reattached to the current thread, in
147e83
-     ptmalloc_lock_all, so save_arena->attached_threads is not
147e83
+     __malloc_fork_lock_parent, so save_arena->attached_threads is not
147e83
      updated.  */
147e83
   tsd_getspecific(arena_key, save_arena);
147e83
   tsd_setspecific(arena_key, ATFORK_ARENA_PTR);
147e83
@@ -276,8 +273,9 @@ ptmalloc_lock_all (void)
147e83
   ++atfork_recursive_cntr;
147e83
 }
147e83
 
147e83
-static void
147e83
-ptmalloc_unlock_all (void)
147e83
+void
147e83
+internal_function
147e83
+__malloc_fork_unlock_parent (void)
147e83
 {
147e83
   mstate ar_ptr;
147e83
 
147e83
@@ -286,8 +284,8 @@ ptmalloc_unlock_all (void)
147e83
   if (--atfork_recursive_cntr != 0)
147e83
     return;
147e83
   /* Replace ATFORK_ARENA_PTR with save_arena.
147e83
-     save_arena->attached_threads was not changed in ptmalloc_lock_all
147e83
-     and is still correct.  */
147e83
+     save_arena->attached_threads was not changed in
147e83
+     __malloc_fork_lock_parent and is still correct.  */
147e83
   tsd_setspecific(arena_key, save_arena);
147e83
   __malloc_hook = save_malloc_hook;
147e83
   __free_hook = save_free_hook;
147e83
@@ -299,15 +297,9 @@ ptmalloc_unlock_all (void)
147e83
   (void)mutex_unlock(&list_lock);
147e83
 }
147e83
 
147e83
-# ifdef __linux__
147e83
-
147e83
-/* In NPTL, unlocking a mutex in the child process after a
147e83
-   fork() is currently unsafe, whereas re-initializing it is safe and
147e83
-   does not leak resources.  Therefore, a special atfork handler is
147e83
-   installed for the child. */
147e83
-
147e83
-static void
147e83
-ptmalloc_unlock_all2 (void)
147e83
+void
147e83
+internal_function
147e83
+__malloc_fork_unlock_child (void)
147e83
 {
147e83
   mstate ar_ptr;
147e83
 
147e83
@@ -338,12 +330,6 @@ ptmalloc_unlock_all2 (void)
147e83
   atfork_recursive_cntr = 0;
147e83
 }
147e83
 
147e83
-# else
147e83
-
147e83
-#  define ptmalloc_unlock_all2 ptmalloc_unlock_all
147e83
-
147e83
-# endif
147e83
-
147e83
 #endif  /* !NO_THREADS */
147e83
 
147e83
 /* Initialization routine. */
147e83
@@ -413,7 +399,6 @@ ptmalloc_init (void)
147e83
 
147e83
   tsd_key_create(&arena_key, NULL);
147e83
   tsd_setspecific(arena_key, (void *)&main_arena);
147e83
-  thread_atfork(ptmalloc_lock_all, ptmalloc_unlock_all, ptmalloc_unlock_all2);
147e83
   const char *s = NULL;
147e83
   if (__builtin_expect (_environ != NULL, 1))
147e83
     {
147e83
@@ -487,12 +472,6 @@ ptmalloc_init (void)
147e83
   __malloc_initialized = 1;
147e83
 }
147e83
 
147e83
-/* There are platforms (e.g. Hurd) with a link-time hook mechanism. */
147e83
-#ifdef thread_atfork_static
147e83
-thread_atfork_static(ptmalloc_lock_all, ptmalloc_unlock_all, \
147e83
-		     ptmalloc_unlock_all2)
147e83
-#endif
147e83
-
147e83
 
147e83
 
147e83
 /* Managing heaps and arenas (for concurrent threads) */
147e83
@@ -827,7 +806,8 @@ _int_new_arena(size_t size)
147e83
      limit is reached).  At this point, some arena has to be attached
147e83
      to two threads.  We could acquire the arena lock before list_lock
147e83
      to make it less likely that reused_arena picks this new arena,
147e83
-     but this could result in a deadlock with ptmalloc_lock_all.  */
147e83
+     but this could result in a deadlock with
147e83
+     __malloc_fork_lock_parent.  */
147e83
 
147e83
   (void) mutex_lock (&a->mutex);
147e83
 
147e83
Index: b/malloc/malloc-internal.h
147e83
===================================================================
147e83
--- /dev/null
147e83
+++ b/malloc/malloc-internal.h
147e83
@@ -0,0 +1,32 @@
147e83
+/* Internal declarations for malloc, for use within libc.
147e83
+   Copyright (C) 2016 Free Software Foundation, Inc.
147e83
+   This file is part of the GNU C Library.
147e83
+
147e83
+   The GNU C Library is free software; you can redistribute it and/or
147e83
+   modify it under the terms of the GNU Lesser General Public License as
147e83
+   published by the Free Software Foundation; either version 2.1 of the
147e83
+   License, or (at your option) any later version.
147e83
+
147e83
+   The GNU C Library is distributed in the hope that it will be useful,
147e83
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
147e83
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
147e83
+   Lesser General Public License for more details.
147e83
+
147e83
+   You should have received a copy of the GNU Lesser General Public
147e83
+   License along with the GNU C Library; see the file COPYING.LIB.  If
147e83
+   not, see <http://www.gnu.org/licenses/>.  */
147e83
+
147e83
+#ifndef _MALLOC_PRIVATE_H
147e83
+#define _MALLOC_PRIVATE_H
147e83
+
147e83
+/* Called in the parent process before a fork.  */
147e83
+void __malloc_fork_lock_parent (void) internal_function attribute_hidden;
147e83
+
147e83
+/* Called in the parent process after a fork.  */
147e83
+void __malloc_fork_unlock_parent (void) internal_function attribute_hidden;
147e83
+
147e83
+/* Called in the child process after a fork.  */
147e83
+void __malloc_fork_unlock_child (void) internal_function attribute_hidden;
147e83
+
147e83
+
147e83
+#endif /* _MALLOC_PRIVATE_H */
147e83
Index: b/malloc/malloc.c
147e83
===================================================================
147e83
--- a/malloc/malloc.c
147e83
+++ b/malloc/malloc.c
147e83
@@ -291,6 +291,7 @@ __malloc_assert (const char *assertion,
147e83
 }
147e83
 #endif
147e83
 
147e83
+#include <malloc/malloc-internal.h>
147e83
 
147e83
 /*
147e83
   INTERNAL_SIZE_T is the word-size used for internal bookkeeping
147e83
Index: b/malloc/tst-malloc-fork-deadlock.c
147e83
===================================================================
147e83
--- /dev/null
147e83
+++ b/malloc/tst-malloc-fork-deadlock.c
147e83
@@ -0,0 +1,220 @@
147e83
+/* Test concurrent fork, getline, and fflush (NULL).
147e83
+   Copyright (C) 2016 Free Software Foundation, Inc.
147e83
+   This file is part of the GNU C Library.
147e83
+
147e83
+   The GNU C Library is free software; you can redistribute it and/or
147e83
+   modify it under the terms of the GNU Lesser General Public License as
147e83
+   published by the Free Software Foundation; either version 2.1 of the
147e83
+   License, or (at your option) any later version.
147e83
+
147e83
+   The GNU C Library is distributed in the hope that it will be useful,
147e83
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
147e83
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
147e83
+   Lesser General Public License for more details.
147e83
+
147e83
+   You should have received a copy of the GNU Lesser General Public
147e83
+   License along with the GNU C Library; see the file COPYING.LIB.  If
147e83
+   not, see <http://www.gnu.org/licenses/>.  */
147e83
+
147e83
+#include <sys/wait.h>
147e83
+#include <unistd.h>
147e83
+#include <errno.h>
147e83
+#include <stdio.h>
147e83
+#include <pthread.h>
147e83
+#include <stdbool.h>
147e83
+#include <stdlib.h>
147e83
+#include <malloc.h>
147e83
+#include <time.h>
147e83
+#include <string.h>
147e83
+#include <signal.h>
147e83
+
147e83
+static int do_test (void);
147e83
+#define TEST_FUNCTION do_test ()
147e83
+#include "../test-skeleton.c"
147e83
+
147e83
+enum {
147e83
+  /* Number of threads which call fork.  */
147e83
+  fork_thread_count = 4,
147e83
+  /* Number of threads which call getline (and, indirectly,
147e83
+     malloc).  */
147e83
+  read_thread_count = 8,
147e83
+};
147e83
+
147e83
+static bool termination_requested;
147e83
+
147e83
+static void *
147e83
+fork_thread_function (void *closure)
147e83
+{
147e83
+  while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
147e83
+    {
147e83
+      pid_t pid = fork ();
147e83
+      if (pid < 0)
147e83
+        {
147e83
+          printf ("error: fork: %m\n");
147e83
+          abort ();
147e83
+        }
147e83
+      else if (pid == 0)
147e83
+        _exit (17);
147e83
+
147e83
+      int status;
147e83
+      if (waitpid (pid, &status, 0) < 0)
147e83
+        {
147e83
+          printf ("error: waitpid: %m\n");
147e83
+          abort ();
147e83
+        }
147e83
+      if (!WIFEXITED (status) || WEXITSTATUS (status) != 17)
147e83
+        {
147e83
+          printf ("error: waitpid returned invalid status: %d\n", status);
147e83
+          abort ();
147e83
+        }
147e83
+    }
147e83
+  return NULL;
147e83
+}
147e83
+
147e83
+static char *file_to_read;
147e83
+
147e83
+static void *
147e83
+read_thread_function (void *closure)
147e83
+{
147e83
+  FILE *f = fopen (file_to_read, "r");
147e83
+  if (f == NULL)
147e83
+    {
147e83
+      printf ("error: fopen (%s): %m\n", file_to_read);
147e83
+      abort ();
147e83
+    }
147e83
+
147e83
+  while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
147e83
+    {
147e83
+      rewind (f);
147e83
+      char *line = NULL;
147e83
+      size_t line_allocated = 0;
147e83
+      ssize_t ret = getline (&line, &line_allocated, f);
147e83
+      if (ret < 0)
147e83
+        {
147e83
+          printf ("error: getline: %m\n");
147e83
+          abort ();
147e83
+        }
147e83
+      free (line);
147e83
+    }
147e83
+  fclose (f);
147e83
+
147e83
+  return NULL;
147e83
+}
147e83
+
147e83
+static void *
147e83
+flushall_thread_function (void *closure)
147e83
+{
147e83
+  while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
147e83
+    if (fflush (NULL) != 0)
147e83
+      {
147e83
+        printf ("error: fflush (NULL): %m\n");
147e83
+        abort ();
147e83
+      }
147e83
+  return NULL;
147e83
+}
147e83
+
147e83
+static void
147e83
+create_threads (pthread_t *threads, size_t count, void *(*func) (void *))
147e83
+{
147e83
+  for (size_t i = 0; i < count; ++i)
147e83
+    {
147e83
+      int ret = pthread_create (threads + i, NULL, func, NULL);
147e83
+      if (ret != 0)
147e83
+        {
147e83
+          errno = ret;
147e83
+          printf ("error: pthread_create: %m\n");
147e83
+          abort ();
147e83
+        }
147e83
+    }
147e83
+}
147e83
+
147e83
+static void
147e83
+join_threads (pthread_t *threads, size_t count)
147e83
+{
147e83
+  for (size_t i = 0; i < count; ++i)
147e83
+    {
147e83
+      int ret = pthread_join (threads[i], NULL);
147e83
+      if (ret != 0)
147e83
+        {
147e83
+          errno = ret;
147e83
+          printf ("error: pthread_join: %m\n");
147e83
+          abort ();
147e83
+        }
147e83
+    }
147e83
+}
147e83
+
147e83
+/* Create a file which consists of a single long line, and assigns
147e83
+   file_to_read.  The hope is that this triggers an allocation in
147e83
+   getline which needs a lock.  */
147e83
+static void
147e83
+create_file_with_large_line (void)
147e83
+{
147e83
+  int fd = create_temp_file ("bug19431-large-line", &file_to_read);
147e83
+  if (fd < 0)
147e83
+    {
147e83
+      printf ("error: create_temp_file: %m\n");
147e83
+      abort ();
147e83
+    }
147e83
+  FILE *f = fdopen (fd, "w+");
147e83
+  if (f == NULL)
147e83
+    {
147e83
+      printf ("error: fdopen: %m\n");
147e83
+      abort ();
147e83
+    }
147e83
+  for (int i = 0; i < 50000; ++i)
147e83
+    fputc ('x', f);
147e83
+  fputc ('\n', f);
147e83
+  if (ferror (f))
147e83
+    {
147e83
+      printf ("error: fputc: %m\n");
147e83
+      abort ();
147e83
+    }
147e83
+  if (fclose (f) != 0)
147e83
+    {
147e83
+      printf ("error: fclose: %m\n");
147e83
+      abort ();
147e83
+    }
147e83
+}
147e83
+
147e83
+static int
147e83
+do_test (void)
147e83
+{
147e83
+  /* Make sure that we do not exceed the arena limit with the number
147e83
+     of threads we configured.  */
147e83
+  if (mallopt (M_ARENA_MAX, 400) == 0)
147e83
+    {
147e83
+      printf ("error: mallopt (M_ARENA_MAX) failed\n");
147e83
+      return 1;
147e83
+    }
147e83
+
147e83
+  /* Leave some room for shutting down all threads gracefully.  */
147e83
+  int timeout = 3;
147e83
+  if (timeout > TIMEOUT)
147e83
+    timeout = TIMEOUT - 1;
147e83
+
147e83
+  create_file_with_large_line ();
147e83
+
147e83
+  pthread_t fork_threads[fork_thread_count];
147e83
+  create_threads (fork_threads, fork_thread_count, fork_thread_function);
147e83
+  pthread_t read_threads[read_thread_count];
147e83
+  create_threads (read_threads, read_thread_count, read_thread_function);
147e83
+  pthread_t flushall_threads[1];
147e83
+  create_threads (flushall_threads, 1, flushall_thread_function);
147e83
+
147e83
+  struct timespec ts = {timeout, 0};
147e83
+  if (nanosleep (&ts, NULL))
147e83
+    {
147e83
+      printf ("error: error: nanosleep: %m\n");
147e83
+      abort ();
147e83
+    }
147e83
+
147e83
+  __atomic_store_n (&termination_requested, true, __ATOMIC_RELAXED);
147e83
+
147e83
+  join_threads (flushall_threads, 1);
147e83
+  join_threads (read_threads, read_thread_count);
147e83
+  join_threads (fork_threads, fork_thread_count);
147e83
+
147e83
+  free (file_to_read);
147e83
+
147e83
+  return 0;
147e83
+}
147e83
Index: b/manual/memory.texi
147e83
===================================================================
147e83
--- a/manual/memory.texi
147e83
+++ b/manual/memory.texi
147e83
@@ -1055,14 +1055,6 @@ systems that do not support @w{ISO C11}.
147e83
 @c     _dl_addr_inside_object ok
147e83
 @c    determine_info ok
147e83
 @c    __rtld_lock_unlock_recursive (dl_load_lock) @aculock
147e83
-@c   thread_atfork @asulock @aculock @acsfd @acsmem
147e83
-@c    __register_atfork @asulock @aculock @acsfd @acsmem
147e83
-@c     lll_lock (__fork_lock) @asulock @aculock
147e83
-@c     fork_handler_alloc @asulock @aculock @acsfd @acsmem
147e83
-@c      calloc dup @asulock @aculock @acsfd @acsmem
147e83
-@c     __linkin_atfork ok
147e83
-@c      catomic_compare_and_exchange_bool_acq ok
147e83
-@c     lll_unlock (__fork_lock) @aculock
147e83
 @c   *_environ @mtsenv
147e83
 @c   next_env_entry ok
147e83
 @c   strcspn dup ok
147e83
Index: b/nptl/sysdeps/unix/sysv/linux/fork.c
147e83
===================================================================
147e83
--- a/nptl/sysdeps/unix/sysv/linux/fork.c
147e83
+++ b/nptl/sysdeps/unix/sysv/linux/fork.c
147e83
@@ -29,7 +29,7 @@
147e83
 #include <bits/stdio-lock.h>
147e83
 #include <atomic.h>
147e83
 #include <pthreadP.h>
147e83
-
147e83
+#include <malloc/malloc-internal.h>
147e83
 
147e83
 unsigned long int *__fork_generation_pointer;
147e83
 
147e83
@@ -116,6 +116,11 @@ __libc_fork (void)
147e83
 
147e83
   _IO_list_lock ();
147e83
 
147e83
+  /* Acquire malloc locks.  This needs to come last because fork
147e83
+     handlers may use malloc, and the libio list lock has an indirect
147e83
+     malloc dependency as well (via the getdelim function).  */
147e83
+  __malloc_fork_lock_parent ();
147e83
+
147e83
 #ifndef NDEBUG
147e83
   pid_t ppid = THREAD_GETMEM (THREAD_SELF, tid);
147e83
 #endif
147e83
@@ -172,6 +177,9 @@ __libc_fork (void)
147e83
 # endif
147e83
 #endif
147e83
 
147e83
+      /* Release malloc locks.  */
147e83
+      __malloc_fork_unlock_child ();
147e83
+
147e83
       /* Reset the file list.  These are recursive mutexes.  */
147e83
       fresetlockfiles ();
147e83
 
147e83
@@ -213,6 +221,9 @@ __libc_fork (void)
147e83
       /* Restore the PID value.  */
147e83
       THREAD_SETMEM (THREAD_SELF, pid, parentpid);
147e83
 
147e83
+      /* Release malloc locks, parent process variant.  */
147e83
+      __malloc_fork_unlock_parent ();
147e83
+
147e83
       /* We execute this even if the 'fork' call failed.  */
147e83
       _IO_list_unlock ();
147e83