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

[PATCH 01/11] config: make sequencer.c's git_config_string_dup() public

From
Jeff King <peff@peff.net>
Date
Apr 7, 2024, 00:58 UTC
Message-ID
<20240407005831.GA868358@coredump.intra.peff.net>
In-Reply-To
<20240407005656.GA436890@coredump.intra.peff.net>

Just about every caller of git_config_string() has a possible leak in it: if we parse a config variable twice, it will overwrite the pointer that was allocated the first time, leaving the memory unreferenced.

Unfortunately we can't just fix this directly in git_config_string(). Some callers do something like:

   const char *foo = "default_value";
   ...
   git_config_string(&foo, var, value);

And we must _not_ free that initial value, as it's a string literal. We can't help those cases easily, as there's no way to distinguish a heap-allocated variable from the default one. But let's start by at least providing a helper that avoids the leak. That will let us convert some cases right away, and give us one potential path forward for the more complex ones.

It turns out we already have such a helper, courtesy of 03a4e260e2 (sequencer: plug memory leaks for the option values, 2016-10-21). The problem is more acute in sequencer.c, which may load config multiple times. Hence the solution was limited to that file back then. But this really is a more general problem within our config callbacks.

Note that the new helper takes a "char *" rather than a const pointer. This is more appropriate, since we tend to use "const" as a signal for a lack of memory ownership (and this function is most certainly asserting ownership over the pointed-to memory).

Signed-off-by: Jeff King <peff@peff.net>
---
 config.c    |  9 +++++++++
 config.h    | 12 ++++++++++++
 sequencer.c | 10 ----------
 3 files changed, 21 insertions(+), 10 deletions(-)
diff --git a/config.c b/config.c
index eebce8c7e0..2194fb078a 100644
--- a/config.c
+++ b/config.c
@@ -1345,6 +1345,15 @@ int git_config_string(const char **dest, const char *var, const char *value)
 	return 0;
 }
 
+int git_config_string_dup(char **dest, const char *var, const char *value)
+{
+	if (!value)
+		return config_error_nonbool(var);
+	free(*dest);
+	*dest = xstrdup(value);
+	return 0;
+}
+
 int git_config_pathname(const char **dest, const char *var, const char *value)
 {
 	if (!value)
diff --git a/config.h b/config.h
index f4966e3749..cdffc14ccf 100644
--- a/config.h
+++ b/config.h
@@ -279,9 +279,21 @@ int git_config_bool(const char *, const char *);
 /**
  * Allocates and copies the value string into the `dest` parameter; if no
  * string is given, prints an error message and returns -1.
+ *
+ * Note that this function does _not_ free the memory referenced by the
+ * destination pointer. This makes it safe to use on a variable that initially
+ * points to a string literal, but it also means that it leaks if the config
+ * option is seen multiple times.
  */
 int git_config_string(const char **, const char *, const char *);
 
+/**
+ * Like git_config_string(), but frees any previously-allocated
+ * string at the destination pointer, avoiding a leak when a
+ * config variable is seen multiple times.
+ */
+int git_config_string_dup(char **, const char *, const char *);
+
 /**
  * Similar to `git_config_string`, but expands `~` or `~user` into the
  * user's home directory when found at the beginning of the path.
diff --git a/sequencer.c b/sequencer.c
index 2c19846385..3e5d82e0e5 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2920,16 +2920,6 @@ static int read_populate_todo(struct repository *r,
 	return 0;
 }
 
-static int git_config_string_dup(char **dest,
-				 const char *var, const char *value)
-{
-	if (!value)
-		return config_error_nonbool(var);
-	free(*dest);
-	*dest = xstrdup(value);
-	return 0;
-}
-
 static int populate_opts_cb(const char *key, const char *value,
 			    const struct config_context *ctx,
 			    void *data)
-- 
2.44.0.872.g288abe5b5b
Previous: Jeff KingNext: Jeff King
Message 4 of 23 in “config: do not leak excludes_file”
  1. config: do not leak excludes_fileJunio C Hamano, Apr 6, 2024
  2. [WIP] git_config_pathname() leakfixJunio C Hamano, Apr 6, 2024
  3. 0/12 git_config_string() considered harmfulJeff King, Apr 7, 2024
  4. 01/11 config: make sequencer.c's git_config_string_dup() publicJeff King, Apr 7, 2024
  5. 02/11 config: add git_config_pathname_dup()Jeff King, Apr 7, 2024
  6. 03/11 config: prefer git_config_string_dup() for temp variablesJeff King, Apr 7, 2024
  7. Eric SunshineApr 7, 2024
  8. Jeff KingApr 7, 2024
  9. 04/11 config: use git_config_string_dup() for open-coded equivalentsJeff King, Apr 7, 2024
  10. 05/11 config: use git_config_string_dup() to fix leaky open codingJeff King, Apr 7, 2024
  11. 06/11 config: use git_config_string() in easy casesJeff King, Apr 7, 2024
  12. Eric SunshineApr 7, 2024
  13. Jeff KingApr 7, 2024
  14. 07/11 config: use git_config_pathname_dup() in easy casesJeff King, Apr 7, 2024
  15. 08/11 http: use git_config_string_dup()Jeff King, Apr 7, 2024
  16. 09/11 merge: use git_config_string_dup() for pull strategiesJeff King, Apr 7, 2024
  17. 10/11 userdiff: use git_config_string_dup() when we canJeff King, Apr 7, 2024
  18. 11/11 blame: use "dup" string_list for ignore-revs filesJeff King, Apr 7, 2024
  19. Eric SunshineApr 7, 2024
  20. Jeff KingApr 7, 2024
  21. Rubén JustoApr 7, 2024
  22. Jeff KingApr 8, 2024
  23. Rubén JustoApr 11, 2024

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.