summaryrefslogtreecommitdiff
path: root/sysdeps
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2016-04-14 09:17:02 +0200
committerFlorian Weimer <fweimer@redhat.com>2016-04-14 09:17:02 +0200
commit29d794863cd6e03115d3670707cc873a9965ba92 (patch)
treef5d714f3857f3c2f2468c9f9977fcefc2be75cfb /sysdeps
parentb49ab5f4503f36dcbf43f821f817da66b2931fe6 (diff)
malloc: Run fork handler as late as possible [BZ #19431]
Previously, a thread M invoking fork would acquire locks in this order: (M1) malloc arena locks (in the registered fork handler) (M2) libio list lock A thread F invoking flush (NULL) would acquire locks in this order: (F1) libio list lock (F2) individual _IO_FILE locks A thread G running getdelim would use this order: (G1) _IO_FILE lock (G2) malloc arena lock After executing (M1), (F1), (G1), none of the threads can make progress. This commit changes the fork lock order to: (M'1) libio list lock (M'2) malloc arena locks It explicitly encodes the lock order in the implementations of fork, and does not rely on the registration order, thus avoiding the deadlock.
Diffstat (limited to 'sysdeps')
-rw-r--r--sysdeps/mach/hurd/fork.c13
-rw-r--r--sysdeps/nptl/fork.c13
2 files changed, 25 insertions, 1 deletions
diff --git a/sysdeps/mach/hurd/fork.c b/sysdeps/mach/hurd/fork.c
index ad09fd7c41..2e8b59e7c0 100644
--- a/sysdeps/mach/hurd/fork.c
+++ b/sysdeps/mach/hurd/fork.c
@@ -26,6 +26,7 @@
#include <assert.h>
#include "hurdmalloc.h" /* XXX */
#include <tls.h>
+#include <malloc/malloc-internal.h>
#undef __fork
@@ -107,6 +108,12 @@ __fork (void)
/* Run things that prepare for forking before we create the task. */
RUN_HOOK (_hurd_fork_prepare_hook, ());
+ /* 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). */
+ __malloc_fork_lock_parent ();
+
/* Lock things that want to be locked before we fork. */
{
void *const *p;
@@ -604,6 +611,9 @@ __fork (void)
nthreads * sizeof (*threads));
}
+ /* Release malloc locks. */
+ __malloc_fork_unlock_parent ();
+
/* Run things that want to run in the parent to restore it to
normality. Usually prepare hooks and parent hooks are
symmetrical: the prepare hook arrests state in some way for the
@@ -655,6 +665,9 @@ __fork (void)
/* Forking clears the trace flag. */
__sigemptyset (&_hurdsig_traced);
+ /* Release malloc locks. */
+ __malloc_fork_unlock_child ();
+
/* Run things that want to run in the child task to set up. */
RUN_HOOK (_hurd_fork_child_hook, ());
diff --git a/sysdeps/nptl/fork.c b/sysdeps/nptl/fork.c
index 27f8d52e30..1a68cbd647 100644
--- a/sysdeps/nptl/fork.c
+++ b/sysdeps/nptl/fork.c
@@ -31,7 +31,7 @@
#include <fork.h>
#include <arch-fork.h>
#include <futex-internal.h>
-
+#include <malloc/malloc-internal.h>
static void
fresetlockfiles (void)
@@ -111,6 +111,11 @@ __libc_fork (void)
_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). */
+ __malloc_fork_lock_parent ();
+
#ifndef NDEBUG
pid_t ppid = THREAD_GETMEM (THREAD_SELF, tid);
#endif
@@ -168,6 +173,9 @@ __libc_fork (void)
# endif
#endif
+ /* Release malloc locks. */
+ __malloc_fork_unlock_child ();
+
/* Reset the file list. These are recursive mutexes. */
fresetlockfiles ();
@@ -209,6 +217,9 @@ __libc_fork (void)
/* Restore the PID value. */
THREAD_SETMEM (THREAD_SELF, pid, parentpid);
+ /* Release malloc locks, parent process variant. */
+ __malloc_fork_unlock_parent ();
+
/* We execute this even if the 'fork' call failed. */
_IO_list_unlock ();