diff options
| author | Patrick Steinhardt <ps@pks.im> | 2018-05-04 13:40:54 +0100 |
|---|---|---|
| committer | Patrick Steinhardt <ps@pks.im> | 2018-05-04 13:40:54 +0100 |
| commit | 0933fdc5f5db346ff0fe9e1614bf2a54e96acba9 (patch) | |
| tree | 3a18ce322e1f0ff7232002772971215794c00cad /src/global.c | |
| parent | 0fd0bfe435989b4947babfcd61b0bf573ff41e28 (diff) | |
| download | libgit2-0933fdc5f5db346ff0fe9e1614bf2a54e96acba9.tar.gz | |
global: adjust init count under lock
Our global initialization functions `git_libgit2_init()` and
`git_libgit2_shutdown()` both adjust a global init counter to determine
whether we are the first respectively last user of libgit2. On
Unix-systems do not do so under lock, though, which opens the
possibility of a race between these two functions:
Thread 1 Thread 2
git__n_inits = 0;
git_libgit2_init();
git_atomic_inc(&git__n_inits);
/* git__n_inits == 1 */
git_libgit2_shutdown();
if (git_atomic_dec(&git__n_inits) != 0)
/* git__n_inits == 0, no early exit here */
pthread_mutex_lock(&_init_mutex);
shutdown_common();
pthread_mutex_unlock(&_init_mutex);
pthread_mutex_lock(&_init_mutex);
init_once();
pthread_mutex_unlock(&_init_mutex);
So we can end up in a situation where we try to shutdown shared data
structures before they have been initialized.
Fix the race by always locking `_init_mutex` before incrementing or
decrementing `git__n_inits`.
Diffstat (limited to 'src/global.c')
| -rw-r--r-- | src/global.c | 21 |
1 files changed, 11 insertions, 10 deletions
diff --git a/src/global.c b/src/global.c index 89183080b..29c1118bd 100644 --- a/src/global.c +++ b/src/global.c @@ -272,10 +272,10 @@ int git_libgit2_init(void) { int ret, err; - ret = git_atomic_inc(&git__n_inits); - if ((err = pthread_mutex_lock(&_init_mutex)) != 0) return err; + + ret = git_atomic_inc(&git__n_inits); err = pthread_once(&_once_init, init_once); err |= pthread_mutex_unlock(&_init_mutex); @@ -289,13 +289,13 @@ int git_libgit2_shutdown(void) { void *ptr = NULL; pthread_once_t new_once = PTHREAD_ONCE_INIT; - int ret; + int error, ret; - if ((ret = git_atomic_dec(&git__n_inits)) != 0) - return ret; + if ((error = pthread_mutex_lock(&_init_mutex)) != 0) + return error; - if ((ret = pthread_mutex_lock(&_init_mutex)) != 0) - return ret; + if ((ret = git_atomic_dec(&git__n_inits)) != 0) + goto out; /* Shut down any subsystems that have global state */ shutdown_common(); @@ -310,10 +310,11 @@ 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; +out: + if ((error = pthread_mutex_unlock(&_init_mutex)) != 0) + return error; - return 0; + return ret; } git_global_st *git__global_state(void) |
