Commit 7277bf833d3d56723feee2c415d271ab933ca439

Patrick Steinhardt 2019-07-05T13:33:05

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.

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;
 }