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

[PATCH] line-log: protect inner strbuf from free

From
Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>
Date
Oct 2, 2024, 16:07 UTC
Message-ID
<pull.1806.git.1727885224966.gitgitgadget@gmail.com>
From: Derrick Stolee <stolee@gmail.com>

The output_prefix() method in line-log.c may call a function pointer via the diff_options struct. This function pointer returns a strbuf struct and then its buffer is passed back. However, that implies that the consumer is responsible to free the string. This is especially true because the default behavior is to duplicate the empty string.

The existing functions used in the output_prefix pointer include:
 1. idiff_prefix_cb() in diff-lib.c. This returns the data pointer, so
    the value exists across multiple calls.
 2. diff_output_prefix_callback() in graph.c. This uses a static strbuf
    struct, so it reuses buffers across calls. These should not be
    freed.
 3. output_prefix_cb() in range-diff.c. This is similar to the
    diff-lib.c case.

In each case, we should not be freeing this buffer. We can convert the output_prefix() function to return a const char pointer and stop freeing the result.

This choice is essentially the opposite of what was done in 394affd46d (line-log: always allocate the output prefix, 2024-06-07).

This was discovered via 'valgrind' while investigating a public report of a bug in 'git log --graph -L' [1].

[1] https://github.com/git-for-windows/git/issues/5185
Signed-off-by: Derrick Stolee <stolee@gmail.com>
---
    line-log: protect inner strbuf from free
    
    This fixes a regression introduced in 2.46.0.
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1806%2Fderrickstolee%2Fline-log-use-after-free-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1806/derrickstolee/line-log-use-after-free-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1806
 line-log.c | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/line-log.c b/line-log.c
index 67c80b39a0d..29cf66bdd10 100644
--- a/line-log.c
+++ b/line-log.c
@@ -897,13 +897,13 @@ static void print_line(const char *prefix, char first,
 		fputs("\\ No newline at end of file\n", file);
 }
 
-static char *output_prefix(struct diff_options *opt)
+static const char *output_prefix(struct diff_options *opt)
 {
 	if (opt->output_prefix) {
 		struct strbuf *sb = opt->output_prefix(opt, opt->output_prefix_data);
 		return sb->buf;
 	} else {
-		return xstrdup("");
+		return "";
 	}
 }
 
@@ -916,7 +916,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
 	struct diff_ranges *diff = &range->diff;
 
 	struct diff_options *opt = &rev->diffopt;
-	char *prefix = output_prefix(opt);
+	const char *prefix = output_prefix(opt);
 	const char *c_reset = diff_get_color(opt->use_color, DIFF_RESET);
 	const char *c_frag = diff_get_color(opt->use_color, DIFF_FRAGINFO);
 	const char *c_meta = diff_get_color(opt->use_color, DIFF_METAINFO);
@@ -1003,7 +1003,6 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
 out:
 	free(p_ends);
 	free(t_ends);
-	free(prefix);
 }
 
 /*
@@ -1012,10 +1011,9 @@ out:
  */
 static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)
 {
-	char *prefix = output_prefix(&rev->diffopt);
+	const char *prefix = output_prefix(&rev->diffopt);
 
 	fprintf(rev->diffopt.file, "%s\n", prefix);
-	free(prefix);
 
 	while (range) {
 		dump_diff_hacky_one(rev, range);

base-commit: e9356ba3ea2a6754281ff7697b3e5a1697b21e24
-- 
gitgitgadget
Next: Jeff King
Message 1 of 24 in “line-log: protect inner strbuf from free”
  1. line-log: protect inner strbuf from freeDerrick Stolee via GitGitGadget, Oct 2, 2024
  2. Jeff KingOct 2, 2024
  3. Jeff KingOct 3, 2024
  4. Derrick StoleeOct 3, 2024
  5. Jeff KingOct 3, 2024
  6. Derrick StoleeOct 3, 2024
  7. Jeff KingOct 3, 2024
  8. 0/3 line-log: protect inner strbuf from freeDerrick Stolee via GitGitGadget, Oct 3, 2024
  9. 1/3 line-log: protect inner strbuf from freeDerrick Stolee via GitGitGadget, Oct 3, 2024
  10. Patrick SteinhardtOct 4, 2024
  11. 2/3 line-log: remove output_prefix()Jeff King via GitGitGadget, Oct 3, 2024
  12. 3/3 diff: modify output_prefix function pointerJeff King via GitGitGadget, Oct 3, 2024
  13. Junio C HamanoOct 3, 2024
  14. 0/5 diff output_prefix cleanupsJeff King, Oct 3, 2024
  15. 1/5 line-log: use diff_line_prefix() instead of custom helperJeff King, Oct 3, 2024
  16. 2/5 diff: drop line_prefix_length fieldJeff King, Oct 3, 2024
  17. 3/5 diff: return const char from output_prefix callbackJeff King, Oct 3, 2024
  18. 4/5 diff: return line_prefix directly when possibleJeff King, Oct 3, 2024
  19. 5/5 diff: store graph prefix buf in git_graph structJeff King, Oct 3, 2024
  20. Junio C HamanoOct 3, 2024
  21. Patrick SteinhardtOct 4, 2024
  22. Derrick StoleeOct 4, 2024
  23. Junio C HamanoOct 4, 2024
  24. Derrick StoleeOct 4, 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.