From 29d794863cd6e03115d3670707cc873a9965ba92 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Thu, 14 Apr 2016 09:17:02 +0200 Subject: 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. --- sysdeps/mach/hurd/fork.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'sysdeps/mach/hurd') 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 #include "hurdmalloc.h" /* XXX */ #include +#include #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, ()); -- cgit 1.4.1