diff options
author | Florian Weimer <fweimer@redhat.com> | 2016-05-12 08:54:17 +0200 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2016-05-12 15:26:55 +0200 |
commit | 56290d6e762c1194547e73ff0b948cd79d3a1e03 (patch) | |
tree | c6025736ee13b24a908e7d12496d47b636592345 /sysdeps/nptl/fork.c | |
parent | cd065b68439076971640047cc6eaada27dcfd0f7 (diff) | |
download | glibc-56290d6e762c1194547e73ff0b948cd79d3a1e03.tar.gz |
Increase fork signal safety for single-threaded processes [BZ #19703]
This provides a band-aid and addresses the scenario where fork is
called from a signal handler while the process is in the malloc
subsystem (or has acquired the libio list lock). It does not
address the general issue of async-signal-safety of fork;
multi-threaded processes are not covered, and some glibc
subsystems have fork handlers which are not async-signal-safe.
Diffstat (limited to 'sysdeps/nptl/fork.c')
-rw-r--r-- | sysdeps/nptl/fork.c | 53 |
1 files changed, 38 insertions, 15 deletions
diff --git a/sysdeps/nptl/fork.c b/sysdeps/nptl/fork.c index 1a68cbd647..616d897a36 100644 --- a/sysdeps/nptl/fork.c +++ b/sysdeps/nptl/fork.c @@ -54,6 +54,12 @@ __libc_fork (void) struct used_handler *next; } *allp = NULL; + /* Determine if we are running multiple threads. We skip some fork + handlers in the single-thread case, to make fork safer to use in + signal handlers. POSIX requires that fork is async-signal-safe, + but our current fork implementation is not. */ + bool multiple_threads = THREAD_GETMEM (THREAD_SELF, header.multiple_threads); + /* Run all the registered preparation handlers. In reverse order. While doing this we build up a list of all the entries. */ struct fork_handler *runp; @@ -109,12 +115,21 @@ __libc_fork (void) break; } - _IO_list_lock (); + /* If we are not running multiple threads, we do not have to + preserve lock state. If fork runs from a signal handler, only + async-signal-safe functions can be used in the child. These data + structures are only used by unsafe functions, so their state does + not matter if fork was called from a signal handler. */ + if (multiple_threads) + { + _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 (); + /* 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); @@ -173,14 +188,18 @@ __libc_fork (void) # endif #endif - /* Release malloc locks. */ - __malloc_fork_unlock_child (); + /* Reset the lock state in the multi-threaded case. */ + if (multiple_threads) + { + /* Release malloc locks. */ + __malloc_fork_unlock_child (); - /* Reset the file list. These are recursive mutexes. */ - fresetlockfiles (); + /* Reset the file list. These are recursive mutexes. */ + fresetlockfiles (); - /* Reset locks in the I/O code. */ - _IO_list_resetlock (); + /* Reset locks in the I/O code. */ + _IO_list_resetlock (); + } /* Reset the lock the dynamic loader uses to protect its data. */ __rtld_lock_initialize (GL(dl_load_lock)); @@ -217,11 +236,15 @@ __libc_fork (void) /* Restore the PID value. */ THREAD_SETMEM (THREAD_SELF, pid, parentpid); - /* Release malloc locks, parent process variant. */ - __malloc_fork_unlock_parent (); + /* Release acquired locks in the multi-threaded case. */ + if (multiple_threads) + { + /* Release malloc locks, parent process variant. */ + __malloc_fork_unlock_parent (); - /* We execute this even if the 'fork' call failed. */ - _IO_list_unlock (); + /* We execute this even if the 'fork' call failed. */ + _IO_list_unlock (); + } /* Run the handlers registered for the parent. */ while (allp != NULL) |