Commit 0933fdc5 by Patrick Steinhardt

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`.
parent 0fd0bfe4
......@@ -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)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment