From 59c6c2860a573521a96a402f8232127ceb27b0f6 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 27 Oct 2016 12:31:17 +0200 Subject: [PATCH] global: synchronize initialization and shutdown with pthreads When trying to initialize and tear down global data structures from different threads at once with `git_libgit2_init` and `git_libgit2_shutdown`, we race around initializing data. While we use `pthread_once` to assert that we only initilize data a single time, we actually reset the `pthread_once_t` on the last call to `git_libgit2_shutdown`. As resetting this variable is not synchronized with other threads trying to access it, this is actually racy when one thread tries to do a complete shutdown of libgit2 while another thread tries to initialize it. Fix the issue by creating a mutex which synchronizes `init_once` and the library shutdown. --- src/global.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/global.c b/src/global.c index 45b1ab8f6..de722c7e8 100644 --- a/src/global.c +++ b/src/global.c @@ -247,6 +247,7 @@ BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved) #elif defined(GIT_THREADS) && defined(_POSIX_THREADS) static pthread_key_t _tls_key; +static pthread_mutex_t _init_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_once_t _once_init = PTHREAD_ONCE_INIT; int init_error = 0; @@ -268,12 +269,19 @@ static void init_once(void) int git_libgit2_init(void) { - int ret; + int ret, err; ret = git_atomic_inc(&git__n_inits); - pthread_once(&_once_init, init_once); - return init_error ? init_error : ret; + if ((err = pthread_mutex_lock(&_init_mutex)) != 0) + return err; + err = pthread_once(&_once_init, init_once); + err |= pthread_mutex_unlock(&_init_mutex); + + if (err || init_error) + return err | init_error; + + return ret; } int git_libgit2_shutdown(void) @@ -285,6 +293,9 @@ int git_libgit2_shutdown(void) if ((ret = git_atomic_dec(&git__n_inits)) != 0) return ret; + if ((ret = pthread_mutex_lock(&_init_mutex)) != 0) + return ret; + /* Shut down any subsystems that have global state */ shutdown_common(); @@ -298,6 +309,9 @@ int git_libgit2_shutdown(void) git_mutex_free(&git__mwindow_mutex); _once_init = new_once; + if ((ret = pthread_mutex_unlock(&_init_mutex)) != 0) + return ret; + return 0; }