From: nobu@... Date: 2015-02-22T05:42:51+00:00 Subject: [ruby-core:68225] [Ruby trunk - Bug #10443] Forking with contended mutex held can lead to deadlock in child process upon unlock Issue #10443 has been updated by Nobuyoshi Nakada. Description updated Seems linux specific issue. I could reproduce it in a few iterations on Ubuntu 14 x86_64, but iterated successfully 1000 times on OSX. So I think it would be better to separate the code from `rb_thread_atfork()` as a function. ~~~diff diff --git a/thread.c b/thread.c index 6fdacec..0fa2cbb 100644 --- a/thread.c +++ b/thread.c @@ -3862,15 +3862,12 @@ terminate_atfork_i(rb_thread_t *th, const rb_thread_t *current_th) } } -void -rb_thread_atfork(void) +#ifdef __linux__ +static void +thread_destroy_keeping_mutexes(rb_thread_t *th) { - rb_thread_t *th = GET_THREAD(); size_t n = 0; rb_mutex_t *mutex; - rb_thread_atfork_internal(terminate_atfork_i); - - th->join_list = NULL; /* we preserve mutex state across fork, but ensure we do not deadlock */ mutex = th->keeping_mutexes; @@ -3890,6 +3887,20 @@ rb_thread_atfork(void) if (n) { rb_warn("%"PRIuSIZE" Mutex resource(s) leaked on fork", n); } +} +#else +#define thread_destroy_keeping_mutexes(th) /* do nothing */ +#endif + +void +rb_thread_atfork(void) +{ + rb_thread_t *th = GET_THREAD(); + + rb_thread_atfork_internal(terminate_atfork_i); + + th->join_list = NULL; + thread_destroy_keeping_mutexes(th); /* We don't want reproduce CVE-2003-0900. */ rb_reset_random_seed(); ~~~ ---------------------------------------- Bug #10443: Forking with contended mutex held can lead to deadlock in child process upon unlock https://bugs.ruby-lang.org/issues/10443#change-51586 * Author: Ben Weintraub * Status: Open * Priority: Normal * Assignee: Motohiro KOSAKI * ruby -v: ruby 2.1.3p242 (2014-09-19 revision 47630) [x86_64-darwin13.0] * Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN ---------------------------------------- If a Ruby thread calls `Process.fork` while holding a Mutex (for example, within a `Mutex#synchronize` block) that is also concurrently being contended for by a background thread, the thread in the child process will occasionally be unable to unlock the mutex it was holding at the time of the fork, and will hang under `rb_mutex_unlock_th` when attempting to acquire `mutex->lock`. I've been able to reproduce this on Ruby 2.1.1 - 2.1.3 and 2.2.0-preview1 (haven't tried elsewhere yet). The attached test case demonstrates the issue, although it can take up to 20 minutes to hit a reproducing case. The test case will print one '.' each time it forks. Once it stops printing dots, it has hit this bug (the parent process is stuck in a call to `Process.wait`, and the child is stuck in `rb_mutex_unlock_th`). The test case consists of a global lock that is contended for by 10 background threads, in addition to the main thread, which acquires it, forks, and then releases it. ---Files-------------------------------- rb-mutex-unlock-fork-test.rb (372 Bytes) rb-mutex-unlock-fork-test.rb (339 Bytes) 0001-thread.c-reinitialize-keeping-mutexes-on-fork.patch (3.06 KB) -- https://bugs.ruby-lang.org/