git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 2/3] dir: make clear_directory() free all relevant memory

From
Elijah Newren via GitGitGadget <gitgitgadget@gmail.com>
Date
Aug 16, 2020, 06:59 UTC
Message-ID
<068e097e22fa42b79e70b0346cc7460f1a3cbcff.1597561152.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.831.git.git.1597561152.gitgitgadget@gmail.com>
From: Elijah Newren <newren@gmail.com>

The calling convention for the dir API is supposed to end with a call to clear_directory() to free up no longer needed memory. However, clear_directory() didn't free dir->entries or dir->ignored. I believe this was oversight, but a number of callers noticed memory leaks and started free'ing these, but often somewhat haphazardly (sometimes freeing the entries in the arrays, and sometimes only free'ing the arrays themselves). This suggests the callers weren't trying to make sure any possible memory used might be free'd, but just the memory they noticed their usecase definitely had allocated. This also caused the extra memory deallocations to be duplicated in many places.

Fix this mess by moving all the duplicated free'ing logic into clear_directory().

Signed-off-by: Elijah Newren <newren@gmail.com>
---
 builtin/clean.c |  6 +-----
 builtin/stash.c |  3 ---
 dir.c           | 12 ++++++++----
 dir.h           |  2 +-
 wt-status.c     |  4 ----
 5 files changed, 10 insertions(+), 17 deletions(-)
diff --git a/builtin/clean.c b/builtin/clean.c
index 5a9c29a558..4ffe00dd7f 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -1021,11 +1021,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 		string_list_append(&del_list, rel);
 	}
 
-	for (i = 0; i < dir.nr; i++)
-		free(dir.entries[i]);
-
-	for (i = 0; i < dir.ignored_nr; i++)
-		free(dir.ignored[i]);
+	clear_directory(&dir);
 
 	if (interactive && del_list.nr > 0)
 		interactive_main_loop();
diff --git a/builtin/stash.c b/builtin/stash.c
index 10d87630cd..da48533d49 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -875,11 +875,8 @@ static int get_untracked_files(const struct pathspec *ps, int include_untracked,
 		strbuf_addstr(untracked_files, ent->name);
 		/* NUL-terminate: will be fed to update-index -z */
 		strbuf_addch(untracked_files, '\0');
-		free(ent);
 	}
 
-	free(dir.entries);
-	free(dir.ignored);
 	clear_directory(&dir);
 	return found;
 }
diff --git a/dir.c b/dir.c
index 08df469bf7..b136c037d9 100644
--- a/dir.c
+++ b/dir.c
@@ -3012,10 +3012,7 @@ int remove_path(const char *name)
 	return 0;
 }
 
-/*
- * Frees memory within dir which was allocated for exclude lists and
- * the exclude_stack.  Does not free dir itself.
- */
+/* Frees memory within dir which was allocated.  Does not free dir itself. */
 void clear_directory(struct dir_struct *dir)
 {
 	int i, j;
@@ -3034,6 +3031,13 @@ void clear_directory(struct dir_struct *dir)
 		free(group->pl);
 	}
 
+	for (i = 0; i < dir->ignored_nr; i++)
+		free(dir->ignored[i]);
+	for (i = 0; i < dir->nr; i++)
+		free(dir->entries[i]);
+	free(dir->ignored);
+	free(dir->entries);
+
 	stk = dir->exclude_stack;
 	while (stk) {
 		struct exclude_stack *prev = stk->prev;
diff --git a/dir.h b/dir.h
index 5855c065a6..7d76d0644f 100644
--- a/dir.h
+++ b/dir.h
@@ -36,7 +36,7 @@
  *
  * - Use `dir.entries[]`.
  *
- * - Call `clear_directory()` when none of the contained elements are no longer in use.
+ * - Call `clear_directory()` when the contained elements are no longer in use.
  *
  */
 
diff --git a/wt-status.c b/wt-status.c
index d75399085d..c00ea3e06a 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -724,18 +724,14 @@ static void wt_status_collect_untracked(struct wt_status *s)
 		struct dir_entry *ent = dir.entries[i];
 		if (index_name_is_other(istate, ent->name, ent->len))
 			string_list_insert(&s->untracked, ent->name);
-		free(ent);
 	}
 
 	for (i = 0; i < dir.ignored_nr; i++) {
 		struct dir_entry *ent = dir.ignored[i];
 		if (index_name_is_other(istate, ent->name, ent->len))
 			string_list_insert(&s->ignored, ent->name);
-		free(ent);
 	}
 
-	free(dir.entries);
-	free(dir.ignored);
 	clear_directory(&dir);
 
 	if (advice_status_u_option)
-- 
gitgitgadget
Previous: Jeff KingNext: Jeff King
Message 9 of 15 in “Clean up some memory leaks in and around dir.c”
  1. 0/3 Clean up some memory leaks in and around dir.cElijah Newren via GitGitGadget, Aug 16, 2020
  2. 1/3 dir: fix leak of parent_hashmap and recursive_hashmapElijah Newren via GitGitGadget, Aug 16, 2020
  3. Jeff KingAug 16, 2020
  4. Elijah NewrenAug 17, 2020
  5. 3/3 dir: fix problematic API to avoid memory leaksElijah Newren via GitGitGadget, Aug 16, 2020
  6. Jeff KingAug 16, 2020
  7. Elijah NewrenAug 17, 2020
  8. Jeff KingAug 17, 2020
  9. 2/3 dir: make clear_directory() free all relevant memoryElijah Newren via GitGitGadget, Aug 16, 2020
  10. Jeff KingAug 16, 2020
  11. Elijah NewrenAug 17, 2020
  12. 0/2 Clean up some memory leaks in and around dir.cElijah Newren via GitGitGadget, Aug 18, 2020
  13. 1/2 dir: make clear_directory() free all relevant memoryElijah Newren via GitGitGadget, Aug 18, 2020
  14. 2/2 dir: fix problematic API to avoid memory leaksElijah Newren via GitGitGadget, Aug 18, 2020
  15. Jeff KingAug 19, 2020

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.