From c3f1918b6c9b0df5ee18ff1f51a2e857be7c8272 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Fri, 6 May 2016 20:39:34 +0200 Subject: [PATCH] Resolves: #1332917 Fix deadlock between between fflush, getdelim, and fork --- glibc-rh1332917-1.patch | 82 ++++++ glibc-rh1332917-2.patch | 554 ++++++++++++++++++++++++++++++++++++++++ glibc-rh1332917-3.patch | 218 ++++++++++++++++ glibc-rh1332917-4.patch | 36 +++ glibc-rh1332917-5.patch | 27 ++ glibc.spec | 11 + 6 files changed, 928 insertions(+) create mode 100644 glibc-rh1332917-1.patch create mode 100644 glibc-rh1332917-2.patch create mode 100644 glibc-rh1332917-3.patch create mode 100644 glibc-rh1332917-4.patch create mode 100644 glibc-rh1332917-5.patch diff --git a/glibc-rh1332917-1.patch b/glibc-rh1332917-1.patch new file mode 100644 index 0000000..738c755 --- /dev/null +++ b/glibc-rh1332917-1.patch @@ -0,0 +1,82 @@ +commit 59eda029a8a35e5f4e5cd7be0f84c6629e48ec6e +Author: Florian Weimer +Date: Fri Feb 19 17:07:45 2016 +0100 + + malloc: Remove NO_THREADS + + No functional change. It was not possible to build without + threading support before. + +Index: b/malloc/arena.c +=================================================================== +--- a/malloc/arena.c ++++ b/malloc/arena.c +@@ -135,8 +135,6 @@ int __malloc_initialized = -1; + + /**************************************************************************/ + +-#ifndef NO_THREADS +- + /* atfork support. */ + + static void *(*save_malloc_hook)(size_t __size, const void *); +@@ -341,7 +339,6 @@ ptmalloc_unlock_all2 (void) + + # define ptmalloc_unlock_all2 ptmalloc_unlock_all + # endif +-#endif /* !NO_THREADS */ + + /* Initialization routine. */ + #include +Index: b/malloc/malloc.c +=================================================================== +--- a/malloc/malloc.c ++++ b/malloc/malloc.c +@@ -1074,10 +1074,8 @@ static void* realloc_check(void* oldme + const void *caller); + static void* memalign_check(size_t alignment, size_t bytes, + const void *caller); +-#ifndef NO_THREADS + static void* malloc_atfork(size_t sz, const void *caller); + static void free_atfork(void* mem, const void *caller); +-#endif + + /* ------------------ MMAP support ------------------ */ + +Index: b/sysdeps/generic/malloc-machine.h +=================================================================== +--- a/sysdeps/generic/malloc-machine.h ++++ b/sysdeps/generic/malloc-machine.h +@@ -22,30 +22,9 @@ + + #include + +-#ifndef mutex_init /* No threads, provide dummy macros */ ++#ifndef mutex_init + +-# define NO_THREADS +- +-/* The mutex functions used to do absolutely nothing, i.e. lock, +- trylock and unlock would always just return 0. However, even +- without any concurrently active threads, a mutex can be used +- legitimately as an `in use' flag. To make the code that is +- protected by a mutex async-signal safe, these macros would have to +- be based on atomic test-and-set operations, for example. */ +-typedef int mutex_t; +- +-# define mutex_init(m) (*(m) = 0) +-# define mutex_lock(m) ({ *(m) = 1; 0; }) +-# define mutex_trylock(m) (*(m) ? 1 : ((*(m) = 1), 0)) +-# define mutex_unlock(m) (*(m) = 0) +-# define MUTEX_INITIALIZER (0) +- +-typedef void *tsd_key_t; +-# define tsd_key_create(key, destr) do {} while(0) +-# define tsd_setspecific(key, data) ((key) = (data)) +-# define tsd_getspecific(key, vptr) (vptr = (key)) +- +-# define thread_atfork(prepare, parent, child) do {} while(0) ++#error NO_THREADS no longer supported + + #endif /* !defined mutex_init */ + diff --git a/glibc-rh1332917-2.patch b/glibc-rh1332917-2.patch new file mode 100644 index 0000000..f5a72c5 --- /dev/null +++ b/glibc-rh1332917-2.patch @@ -0,0 +1,554 @@ +commit 29d794863cd6e03115d3670707cc873a9965ba92 +Author: Florian Weimer +Date: Thu Apr 14 09:17:02 2016 +0200 + + 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. + +Index: b/malloc/arena.c +=================================================================== +--- a/malloc/arena.c ++++ b/malloc/arena.c +@@ -141,10 +141,6 @@ static void *(*save_malloc_hook)(size_t + static void (*save_free_hook) (void *__ptr, const void *); + static void *save_arena; + +-# ifdef ATFORK_MEM +-ATFORK_MEM; +-# endif +- + /* Magic value for the thread-specific arena pointer when + malloc_atfork() is in use. */ + +@@ -214,14 +210,14 @@ free_atfork (void *mem, const void *call + /* Counter for number of times the list is locked by the same thread. */ + static unsigned int atfork_recursive_cntr; + +-/* The following two functions are registered via thread_atfork() to +- make sure that the mutexes remain in a consistent state in the +- fork()ed version of a thread. Also adapt the malloc and free hooks +- temporarily, because the `atfork' handler mechanism may use +- malloc/free internally (e.g. in LinuxThreads). */ ++/* The following three functions are called around fork from a ++ multi-threaded process. We do not use the general fork handler ++ mechanism to make sure that our handlers are the last ones being ++ called, so that other fork handlers can use the malloc ++ subsystem. */ + +-static void +-ptmalloc_lock_all (void) ++void ++__malloc_fork_lock_parent (void) + { + mstate ar_ptr; + +@@ -229,7 +225,7 @@ ptmalloc_lock_all (void) + return; + + /* We do not acquire free_list_lock here because we completely +- reconstruct free_list in ptmalloc_unlock_all2. */ ++ reconstruct free_list in __malloc_fork_unlock_child. */ + + if (mutex_trylock (&list_lock)) + { +@@ -256,7 +252,7 @@ ptmalloc_lock_all (void) + __free_hook = free_atfork; + /* Only the current thread may perform malloc/free calls now. + save_arena will be reattached to the current thread, in +- ptmalloc_lock_all, so save_arena->attached_threads is not ++ __malloc_fork_lock_parent, so save_arena->attached_threads is not + updated. */ + tsd_getspecific (arena_key, save_arena); + tsd_setspecific (arena_key, ATFORK_ARENA_PTR); +@@ -264,8 +260,8 @@ out: + ++atfork_recursive_cntr; + } + +-static void +-ptmalloc_unlock_all (void) ++void ++__malloc_fork_unlock_parent (void) + { + mstate ar_ptr; + +@@ -276,8 +272,8 @@ ptmalloc_unlock_all (void) + return; + + /* Replace ATFORK_ARENA_PTR with save_arena. +- save_arena->attached_threads was not changed in ptmalloc_lock_all +- and is still correct. */ ++ save_arena->attached_threads was not changed in ++ __malloc_fork_lock_parent and is still correct. */ + tsd_setspecific (arena_key, save_arena); + __malloc_hook = save_malloc_hook; + __free_hook = save_free_hook; +@@ -291,15 +287,8 @@ ptmalloc_unlock_all (void) + (void) mutex_unlock (&list_lock); + } + +-# ifdef __linux__ +- +-/* In NPTL, unlocking a mutex in the child process after a +- fork() is currently unsafe, whereas re-initializing it is safe and +- does not leak resources. Therefore, a special atfork handler is +- installed for the child. */ +- +-static void +-ptmalloc_unlock_all2 (void) ++void ++__malloc_fork_unlock_child (void) + { + mstate ar_ptr; + +@@ -335,11 +324,6 @@ ptmalloc_unlock_all2 (void) + atfork_recursive_cntr = 0; + } + +-# else +- +-# define ptmalloc_unlock_all2 ptmalloc_unlock_all +-# endif +- + /* Initialization routine. */ + #include + extern char **_environ; +@@ -409,7 +393,6 @@ ptmalloc_init (void) + + tsd_key_create (&arena_key, NULL); + tsd_setspecific (arena_key, (void *) &main_arena); +- thread_atfork (ptmalloc_lock_all, ptmalloc_unlock_all, ptmalloc_unlock_all2); + const char *s = NULL; + if (__glibc_likely (_environ != NULL)) + { +@@ -484,14 +467,6 @@ ptmalloc_init (void) + __malloc_initialized = 1; + } + +-/* There are platforms (e.g. Hurd) with a link-time hook mechanism. */ +-#ifdef thread_atfork_static +-thread_atfork_static (ptmalloc_lock_all, ptmalloc_unlock_all, \ +- ptmalloc_unlock_all2) +-#endif +- +- +- + /* Managing heaps and arenas (for concurrent threads) */ + + #if MALLOC_DEBUG > 1 +@@ -838,7 +813,8 @@ _int_new_arena (size_t size) + limit is reached). At this point, some arena has to be attached + to two threads. We could acquire the arena lock before list_lock + to make it less likely that reused_arena picks this new arena, +- but this could result in a deadlock with ptmalloc_lock_all. */ ++ but this could result in a deadlock with ++ __malloc_fork_lock_parent. */ + + (void) mutex_lock (&a->mutex); + +Index: b/malloc/malloc-internal.h +=================================================================== +--- /dev/null ++++ b/malloc/malloc-internal.h +@@ -0,0 +1,32 @@ ++/* Internal declarations for malloc, for use within libc. ++ Copyright (C) 2016 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; see the file COPYING.LIB. If ++ not, see . */ ++ ++#ifndef _MALLOC_PRIVATE_H ++#define _MALLOC_PRIVATE_H ++ ++/* Called in the parent process before a fork. */ ++void __malloc_fork_lock_parent (void) internal_function attribute_hidden; ++ ++/* Called in the parent process after a fork. */ ++void __malloc_fork_unlock_parent (void) internal_function attribute_hidden; ++ ++/* Called in the child process after a fork. */ ++void __malloc_fork_unlock_child (void) internal_function attribute_hidden; ++ ++ ++#endif /* _MALLOC_PRIVATE_H */ +Index: b/malloc/malloc.c +=================================================================== +--- a/malloc/malloc.c ++++ b/malloc/malloc.c +@@ -244,6 +244,7 @@ + /* For ALIGN_UP. */ + #include + ++#include + + /* + Debugging: +Index: b/malloc/tst-malloc-fork-deadlock.c +=================================================================== +--- /dev/null ++++ b/malloc/tst-malloc-fork-deadlock.c +@@ -0,0 +1,220 @@ ++/* Test concurrent fork, getline, and fflush (NULL). ++ Copyright (C) 2016 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; see the file COPYING.LIB. If ++ not, see . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int do_test (void); ++#define TEST_FUNCTION do_test () ++#include "../test-skeleton.c" ++ ++enum { ++ /* Number of threads which call fork. */ ++ fork_thread_count = 4, ++ /* Number of threads which call getline (and, indirectly, ++ malloc). */ ++ read_thread_count = 8, ++}; ++ ++static bool termination_requested; ++ ++static void * ++fork_thread_function (void *closure) ++{ ++ while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED)) ++ { ++ pid_t pid = fork (); ++ if (pid < 0) ++ { ++ printf ("error: fork: %m\n"); ++ abort (); ++ } ++ else if (pid == 0) ++ _exit (17); ++ ++ int status; ++ if (waitpid (pid, &status, 0) < 0) ++ { ++ printf ("error: waitpid: %m\n"); ++ abort (); ++ } ++ if (!WIFEXITED (status) || WEXITSTATUS (status) != 17) ++ { ++ printf ("error: waitpid returned invalid status: %d\n", status); ++ abort (); ++ } ++ } ++ return NULL; ++} ++ ++static char *file_to_read; ++ ++static void * ++read_thread_function (void *closure) ++{ ++ FILE *f = fopen (file_to_read, "r"); ++ if (f == NULL) ++ { ++ printf ("error: fopen (%s): %m\n", file_to_read); ++ abort (); ++ } ++ ++ while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED)) ++ { ++ rewind (f); ++ char *line = NULL; ++ size_t line_allocated = 0; ++ ssize_t ret = getline (&line, &line_allocated, f); ++ if (ret < 0) ++ { ++ printf ("error: getline: %m\n"); ++ abort (); ++ } ++ free (line); ++ } ++ fclose (f); ++ ++ return NULL; ++} ++ ++static void * ++flushall_thread_function (void *closure) ++{ ++ while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED)) ++ if (fflush (NULL) != 0) ++ { ++ printf ("error: fflush (NULL): %m\n"); ++ abort (); ++ } ++ return NULL; ++} ++ ++static void ++create_threads (pthread_t *threads, size_t count, void *(*func) (void *)) ++{ ++ for (size_t i = 0; i < count; ++i) ++ { ++ int ret = pthread_create (threads + i, NULL, func, NULL); ++ if (ret != 0) ++ { ++ errno = ret; ++ printf ("error: pthread_create: %m\n"); ++ abort (); ++ } ++ } ++} ++ ++static void ++join_threads (pthread_t *threads, size_t count) ++{ ++ for (size_t i = 0; i < count; ++i) ++ { ++ int ret = pthread_join (threads[i], NULL); ++ if (ret != 0) ++ { ++ errno = ret; ++ printf ("error: pthread_join: %m\n"); ++ abort (); ++ } ++ } ++} ++ ++/* Create a file which consists of a single long line, and assigns ++ file_to_read. The hope is that this triggers an allocation in ++ getline which needs a lock. */ ++static void ++create_file_with_large_line (void) ++{ ++ int fd = create_temp_file ("bug19431-large-line", &file_to_read); ++ if (fd < 0) ++ { ++ printf ("error: create_temp_file: %m\n"); ++ abort (); ++ } ++ FILE *f = fdopen (fd, "w+"); ++ if (f == NULL) ++ { ++ printf ("error: fdopen: %m\n"); ++ abort (); ++ } ++ for (int i = 0; i < 50000; ++i) ++ fputc ('x', f); ++ fputc ('\n', f); ++ if (ferror (f)) ++ { ++ printf ("error: fputc: %m\n"); ++ abort (); ++ } ++ if (fclose (f) != 0) ++ { ++ printf ("error: fclose: %m\n"); ++ abort (); ++ } ++} ++ ++static int ++do_test (void) ++{ ++ /* Make sure that we do not exceed the arena limit with the number ++ of threads we configured. */ ++ if (mallopt (M_ARENA_MAX, 400) == 0) ++ { ++ printf ("error: mallopt (M_ARENA_MAX) failed\n"); ++ return 1; ++ } ++ ++ /* Leave some room for shutting down all threads gracefully. */ ++ int timeout = 3; ++ if (timeout > TIMEOUT) ++ timeout = TIMEOUT - 1; ++ ++ create_file_with_large_line (); ++ ++ pthread_t fork_threads[fork_thread_count]; ++ create_threads (fork_threads, fork_thread_count, fork_thread_function); ++ pthread_t read_threads[read_thread_count]; ++ create_threads (read_threads, read_thread_count, read_thread_function); ++ pthread_t flushall_threads[1]; ++ create_threads (flushall_threads, 1, flushall_thread_function); ++ ++ struct timespec ts = {timeout, 0}; ++ if (nanosleep (&ts, NULL)) ++ { ++ printf ("error: error: nanosleep: %m\n"); ++ abort (); ++ } ++ ++ __atomic_store_n (&termination_requested, true, __ATOMIC_RELAXED); ++ ++ join_threads (flushall_threads, 1); ++ join_threads (read_threads, read_thread_count); ++ join_threads (fork_threads, fork_thread_count); ++ ++ free (file_to_read); ++ ++ return 0; ++} +Index: b/manual/memory.texi +=================================================================== +--- a/manual/memory.texi ++++ b/manual/memory.texi +@@ -1055,14 +1055,6 @@ systems that do not support @w{ISO C11}. + @c _dl_addr_inside_object ok + @c determine_info ok + @c __rtld_lock_unlock_recursive (dl_load_lock) @aculock +-@c thread_atfork @asulock @aculock @acsfd @acsmem +-@c __register_atfork @asulock @aculock @acsfd @acsmem +-@c lll_lock (__fork_lock) @asulock @aculock +-@c fork_handler_alloc @asulock @aculock @acsfd @acsmem +-@c calloc dup @asulock @aculock @acsfd @acsmem +-@c __linkin_atfork ok +-@c catomic_compare_and_exchange_bool_acq ok +-@c lll_unlock (__fork_lock) @aculock + @c *_environ @mtsenv + @c next_env_entry ok + @c strcspn dup ok +Index: b/sysdeps/mach/hurd/fork.c +=================================================================== +--- 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; +@@ -608,6 +615,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 +@@ -659,6 +669,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, ()); + +Index: b/sysdeps/nptl/fork.c +=================================================================== +--- a/sysdeps/nptl/fork.c ++++ b/sysdeps/nptl/fork.c +@@ -31,7 +31,7 @@ + #include + #include + #include +- ++#include + + 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 (); + diff --git a/glibc-rh1332917-3.patch b/glibc-rh1332917-3.patch new file mode 100644 index 0000000..28c4a60 --- /dev/null +++ b/glibc-rh1332917-3.patch @@ -0,0 +1,218 @@ +commit 8a727af925be63aa6ea0f5f90e16751fd541626b +Author: Florian Weimer +Date: Thu Apr 14 09:18:30 2016 +0200 + + malloc: Remove malloc hooks from fork handler + + The fork handler now runs so late that there is no risk anymore that + other fork handlers in the same thread use malloc, so it is no + longer necessary to install malloc hooks which made a subset + of malloc functionality available to the thread that called fork. + +Index: b/malloc/arena.c +=================================================================== +--- a/malloc/arena.c ++++ b/malloc/arena.c +@@ -137,79 +137,6 @@ int __malloc_initialized = -1; + + /* atfork support. */ + +-static void *(*save_malloc_hook)(size_t __size, const void *); +-static void (*save_free_hook) (void *__ptr, const void *); +-static void *save_arena; +- +-/* Magic value for the thread-specific arena pointer when +- malloc_atfork() is in use. */ +- +-# define ATFORK_ARENA_PTR ((void *) -1) +- +-/* The following hooks are used while the `atfork' handling mechanism +- is active. */ +- +-static void * +-malloc_atfork (size_t sz, const void *caller) +-{ +- void *vptr = NULL; +- void *victim; +- +- tsd_getspecific (arena_key, vptr); +- if (vptr == ATFORK_ARENA_PTR) +- { +- /* We are the only thread that may allocate at all. */ +- if (save_malloc_hook != malloc_check) +- { +- return _int_malloc (&main_arena, sz); +- } +- else +- { +- if (top_check () < 0) +- return 0; +- +- victim = _int_malloc (&main_arena, sz + 1); +- return mem2mem_check (victim, sz); +- } +- } +- else +- { +- /* Suspend the thread until the `atfork' handlers have completed. +- By that time, the hooks will have been reset as well, so that +- mALLOc() can be used again. */ +- (void) mutex_lock (&list_lock); +- (void) mutex_unlock (&list_lock); +- return __libc_malloc (sz); +- } +-} +- +-static void +-free_atfork (void *mem, const void *caller) +-{ +- void *vptr = NULL; +- mstate ar_ptr; +- mchunkptr p; /* chunk corresponding to mem */ +- +- if (mem == 0) /* free(0) has no effect */ +- return; +- +- p = mem2chunk (mem); /* do not bother to replicate free_check here */ +- +- if (chunk_is_mmapped (p)) /* release mmapped memory. */ +- { +- munmap_chunk (p); +- return; +- } +- +- ar_ptr = arena_for_chunk (p); +- tsd_getspecific (arena_key, vptr); +- _int_free (ar_ptr, p, vptr == ATFORK_ARENA_PTR); +-} +- +- +-/* Counter for number of times the list is locked by the same thread. */ +-static unsigned int atfork_recursive_cntr; +- + /* The following three functions are called around fork from a + multi-threaded process. We do not use the general fork handler + mechanism to make sure that our handlers are the last ones being +@@ -219,65 +146,29 @@ static unsigned int atfork_recursive_cnt + void + __malloc_fork_lock_parent (void) + { +- mstate ar_ptr; +- + if (__malloc_initialized < 1) + return; + + /* We do not acquire free_list_lock here because we completely + reconstruct free_list in __malloc_fork_unlock_child. */ + +- if (mutex_trylock (&list_lock)) +- { +- void *my_arena; +- tsd_getspecific (arena_key, my_arena); +- if (my_arena == ATFORK_ARENA_PTR) +- /* This is the same thread which already locks the global list. +- Just bump the counter. */ +- goto out; +- +- /* This thread has to wait its turn. */ +- (void) mutex_lock (&list_lock); +- } +- for (ar_ptr = &main_arena;; ) ++ (void) mutex_lock (&list_lock); ++ for (mstate ar_ptr = &main_arena;; ) + { + (void) mutex_lock (&ar_ptr->mutex); + ar_ptr = ar_ptr->next; + if (ar_ptr == &main_arena) + break; + } +- save_malloc_hook = __malloc_hook; +- save_free_hook = __free_hook; +- __malloc_hook = malloc_atfork; +- __free_hook = free_atfork; +- /* Only the current thread may perform malloc/free calls now. +- save_arena will be reattached to the current thread, in +- __malloc_fork_lock_parent, so save_arena->attached_threads is not +- updated. */ +- tsd_getspecific (arena_key, save_arena); +- tsd_setspecific (arena_key, ATFORK_ARENA_PTR); +-out: +- ++atfork_recursive_cntr; + } + + void + __malloc_fork_unlock_parent (void) + { +- mstate ar_ptr; +- + if (__malloc_initialized < 1) + return; + +- if (--atfork_recursive_cntr != 0) +- return; +- +- /* Replace ATFORK_ARENA_PTR with save_arena. +- save_arena->attached_threads was not changed in +- __malloc_fork_lock_parent and is still correct. */ +- tsd_setspecific (arena_key, save_arena); +- __malloc_hook = save_malloc_hook; +- __free_hook = save_free_hook; +- for (ar_ptr = &main_arena;; ) ++ for (mstate ar_ptr = &main_arena;; ) + { + (void) mutex_unlock (&ar_ptr->mutex); + ar_ptr = ar_ptr->next; +@@ -290,25 +181,22 @@ __malloc_fork_unlock_parent (void) + void + __malloc_fork_unlock_child (void) + { +- mstate ar_ptr; +- + if (__malloc_initialized < 1) + return; + +- tsd_setspecific (arena_key, save_arena); +- __malloc_hook = save_malloc_hook; +- __free_hook = save_free_hook; ++ void *vptr = NULL; ++ mstate current_arena = tsd_getspecific (arena_key, vptr); + +- /* Push all arenas to the free list, except save_arena, which is ++ /* Push all arenas to the free list, except current_arena, which is + attached to the current thread. */ + mutex_init (&free_list_lock); +- if (save_arena != NULL) +- ((mstate) save_arena)->attached_threads = 1; ++ if (current_arena != NULL) ++ current_arena->attached_threads = 1; + free_list = NULL; +- for (ar_ptr = &main_arena;; ) ++ for (mstate ar_ptr = &main_arena;; ) + { + mutex_init (&ar_ptr->mutex); +- if (ar_ptr != save_arena) ++ if (ar_ptr != current_arena) + { + /* This arena is no longer attached to any thread. */ + ar_ptr->attached_threads = 0; +@@ -321,7 +209,6 @@ __malloc_fork_unlock_child (void) + } + + mutex_init (&list_lock); +- atfork_recursive_cntr = 0; + } + + /* Initialization routine. */ +Index: b/malloc/malloc.c +=================================================================== +--- a/malloc/malloc.c ++++ b/malloc/malloc.c +@@ -1075,8 +1075,6 @@ static void* realloc_check(void* oldme + const void *caller); + static void* memalign_check(size_t alignment, size_t bytes, + const void *caller); +-static void* malloc_atfork(size_t sz, const void *caller); +-static void free_atfork(void* mem, const void *caller); + + /* ------------------ MMAP support ------------------ */ + diff --git a/glibc-rh1332917-4.patch b/glibc-rh1332917-4.patch new file mode 100644 index 0000000..29c4bea --- /dev/null +++ b/glibc-rh1332917-4.patch @@ -0,0 +1,36 @@ +commit 186fe877f3df0b84d57dfbf0386f6332c6aa69bc +Author: Florian Weimer +Date: Thu Apr 14 12:53:03 2016 +0200 + + malloc: Add missing internal_function attributes on function definitions + + Fixes build on i386 after commit 29d794863cd6e03115d3670707cc873a9965ba92. + +Index: b/malloc/arena.c +=================================================================== +--- a/malloc/arena.c ++++ b/malloc/arena.c +@@ -144,6 +144,7 @@ int __malloc_initialized = -1; + subsystem. */ + + void ++internal_function + __malloc_fork_lock_parent (void) + { + if (__malloc_initialized < 1) +@@ -163,6 +164,7 @@ __malloc_fork_lock_parent (void) + } + + void ++internal_function + __malloc_fork_unlock_parent (void) + { + if (__malloc_initialized < 1) +@@ -179,6 +181,7 @@ __malloc_fork_unlock_parent (void) + } + + void ++internal_function + __malloc_fork_unlock_child (void) + { + if (__malloc_initialized < 1) diff --git a/glibc-rh1332917-5.patch b/glibc-rh1332917-5.patch new file mode 100644 index 0000000..31ac0bf --- /dev/null +++ b/glibc-rh1332917-5.patch @@ -0,0 +1,27 @@ +commit 66355680f840f336eb83e7f46a873556a1bdedcc +Author: Florian Weimer +Date: Wed May 4 15:25:39 2016 +0200 + + malloc: Adjust header file guard in malloc-internal.h + +diff --git a/malloc/malloc-internal.h b/malloc/malloc-internal.h +index b830d3f..98afd14 100644 +--- a/malloc/malloc-internal.h ++++ b/malloc/malloc-internal.h +@@ -16,8 +16,8 @@ + License along with the GNU C Library; see the file COPYING.LIB. If + not, see . */ + +-#ifndef _MALLOC_PRIVATE_H +-#define _MALLOC_PRIVATE_H ++#ifndef _MALLOC_INTERNAL_H ++#define _MALLOC_INTERNAL_H + + /* Called in the parent process before a fork. */ + void __malloc_fork_lock_parent (void) internal_function attribute_hidden; +@@ -29,4 +29,4 @@ void __malloc_fork_unlock_parent (void) internal_function attribute_hidden; + void __malloc_fork_unlock_child (void) internal_function attribute_hidden; + + +-#endif /* _MALLOC_PRIVATE_H */ ++#endif /* _MALLOC_INTERNAL_H */ diff --git a/glibc.spec b/glibc.spec index ffa924b..2453e17 100644 --- a/glibc.spec +++ b/glibc.spec @@ -247,6 +247,11 @@ Patch1024: glibc-rh1315648-1.patch Patch1025: glibc-rh1315648-2.patch Patch1026: glibc-rh1333945.patch Patch1027: glibc-rh1333945-2.patch +Patch1028: glibc-rh1332917-1.patch +Patch1029: glibc-rh1332917-2.patch +Patch1030: glibc-rh1332917-3.patch +Patch1031: glibc-rh1332917-4.patch +Patch1032: glibc-rh1332917-5.patch ############################################################################## # @@ -693,6 +698,11 @@ microbenchmark tests on the system. %patch1025 -p1 %patch1026 -p1 %patch1027 -p1 +%patch1028 -p1 +%patch1029 -p1 +%patch1030 -p1 +%patch1031 -p1 +%patch1032 -p1 %patch0059 -p1 ############################################################################## @@ -1917,6 +1927,7 @@ rm -f *.filelist* - Fix getnameinfo memory leak and incorrect truncation (#1333901) - CVE-2016-1234: buffer overflow in glob with GLOB_ALTDIRFUNC (#1315648) - Set dlerror after dlsym (RTLD_NEXT) failure (#1333945) +- Fix deadlock between between fflush, getdelim, and fork (#1332917) * Fri May 6 2016 Florian Weimer - 2.22-13 - Avoid build failure in TZ tests (#1333940)