attrcache: fix multiple memory leaks when inserting macros The function `git_attr_cache__insert_macro` is responsible for adopting macros in the per-repo macro cache. When adding a macro that replaces an already existing macro (e.g. because of re-parsing gitattributes files), then we do not free the previous macro and thus cause a memory leak. Fix this leak by first checking if the cache already has a macro defined with the same name. If so, free it before replacing the cache entry with the new instance.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
diff --git a/src/attrcache.c b/src/attrcache.c
index b88bc09..52a8830 100644
--- a/src/attrcache.c
+++ b/src/attrcache.c
@@ -424,21 +424,27 @@ void git_attr_cache_flush(git_repository *repo)
int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
{
git_attr_cache *cache = git_repository_attr_cache(repo);
- git_strmap *macros = cache->macros;
- int error;
+ git_attr_rule *preexisting;
+ bool locked = false;
+ int error = 0;
/* TODO: generate warning log if (macro->assigns.length == 0) */
if (macro->assigns.length == 0)
- return 0;
+ goto out;
- if (attr_cache_lock(cache) < 0) {
- git_error_set(GIT_ERROR_OS, "unable to get attr cache lock");
- error = -1;
- } else {
- error = git_strmap_set(macros, macro->match.pattern, macro);
- git_mutex_unlock(&cache->lock);
- }
+ if ((error = attr_cache_lock(cache)) < 0)
+ goto out;
+ locked = true;
+
+ if ((preexisting = git_strmap_get(cache->macros, macro->match.pattern)) != NULL)
+ git_attr_rule__free(preexisting);
+ if ((error = git_strmap_set(cache->macros, macro->match.pattern, macro)) < 0)
+ goto out;
+
+out:
+ if (locked)
+ attr_cache_unlock(cache);
return error;
}