summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <ps@pks.im>2019-10-24 12:20:27 +0200
committerPatrick Steinhardt <ps@pks.im>2019-11-05 12:34:19 +0100
commit56b203a5e0a5348700d85479b0070289d37c2cf0 (patch)
treeb8dc3925c4d3b4b4c1d652c8e5d371371044c2af
parent0927156a24b1c66fdb5b0c9ab4e5889ebe0fd2b1 (diff)
downloadlibgit2-56b203a5e0a5348700d85479b0070289d37c2cf0.tar.gz
config_file: keep reference to config entries when creating iterator
When creating a configuration file iterator, then we first refresh the backend and then afterwards duplicate all refreshed configuration entries into the iterator in order to avoid seeing any concurrent modifications of the entries while iterating. The duplication of entries is not guarded, though, as we do not increase the refcount of the entries that we duplicate right now. This opens us up for a race, as another thread may concurrently refresh the repository configuration and thus swap out the current set of entries. As we didn't increase the refcount, this may lead to the entries being free'd while we iterate over them in the first thread. Fix the issue by properly handling the lifecycle of the backend's entries via `config_file_entries_take` and `git_config_entries_free`, respectively.
-rw-r--r--src/config_file.c8
1 files changed, 5 insertions, 3 deletions
diff --git a/src/config_file.c b/src/config_file.c
index 4d1bcde7b..c9e36493e 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -250,17 +250,19 @@ static int config_file_iterator(
struct git_config_backend *backend)
{
config_file_backend *b = GIT_CONTAINER_OF(backend, config_file_backend, parent);
- git_config_entries *entries = NULL;
+ git_config_entries *dupped = NULL, *entries = NULL;
int error;
if ((error = config_file_refresh(backend)) < 0 ||
- (error = git_config_entries_dup(&entries, b->entries)) < 0 ||
- (error = git_config_entries_iterator_new(iter, entries)) < 0)
+ (error = config_file_entries_take(&entries, b)) < 0 ||
+ (error = git_config_entries_dup(&dupped, entries)) < 0 ||
+ (error = git_config_entries_iterator_new(iter, dupped)) < 0)
goto out;
out:
/* Let iterator delete duplicated entries when it's done */
git_config_entries_free(entries);
+ git_config_entries_free(dupped);
return error;
}