Skip to content

Commit 1b6847f

Browse files
derrickstoleepeff
authored andcommitted
line-log: protect inner strbuf from free
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 394affd (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] #5185 This issue would have been caught by the new test, when Git is compiled with ASan to catch these double frees. Co-authored-by: Jeff King <[email protected]> Signed-off-by: Jeff King <[email protected]> Signed-off-by: Derrick Stolee <[email protected]>
1 parent 1bdf957 commit 1b6847f

File tree

2 files changed

+32
-6
lines changed

2 files changed

+32
-6
lines changed

line-log.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -897,13 +897,13 @@ static void print_line(const char *prefix, char first,
897897
fputs("\\ No newline at end of file\n", file);
898898
}
899899

900-
static char *output_prefix(struct diff_options *opt)
900+
static const char *output_prefix(struct diff_options *opt)
901901
{
902902
if (opt->output_prefix) {
903903
struct strbuf *sb = opt->output_prefix(opt, opt->output_prefix_data);
904904
return sb->buf;
905905
} else {
906-
return xstrdup("");
906+
return "";
907907
}
908908
}
909909

@@ -916,7 +916,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
916916
struct diff_ranges *diff = &range->diff;
917917

918918
struct diff_options *opt = &rev->diffopt;
919-
char *prefix = output_prefix(opt);
919+
const char *prefix = output_prefix(opt);
920920
const char *c_reset = diff_get_color(opt->use_color, DIFF_RESET);
921921
const char *c_frag = diff_get_color(opt->use_color, DIFF_FRAGINFO);
922922
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
10031003
out:
10041004
free(p_ends);
10051005
free(t_ends);
1006-
free(prefix);
10071006
}
10081007

10091008
/*
@@ -1012,10 +1011,9 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
10121011
*/
10131012
static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)
10141013
{
1015-
char *prefix = output_prefix(&rev->diffopt);
1014+
const char *prefix = output_prefix(&rev->diffopt);
10161015

10171016
fprintf(rev->diffopt.file, "%s\n", prefix);
1018-
free(prefix);
10191017

10201018
while (range) {
10211019
dump_diff_hacky_one(rev, range);

t/t4211-line-log.sh

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,4 +337,32 @@ test_expect_success 'zero-width regex .* matches any function name' '
337337
test_cmp expect actual
338338
'
339339

340+
test_expect_success 'show line-log with graph' '
341+
qz_to_tab_space >expect <<-EOF &&
342+
* $head_oid Modify func2() in file.c
343+
|Z
344+
| diff --git a/file.c b/file.c
345+
| --- a/file.c
346+
| +++ b/file.c
347+
| @@ -6,4 +6,4 @@
348+
| int func2()
349+
| {
350+
| - return F2;
351+
| + return F2 + 2;
352+
| }
353+
* $root_oid Add func1() and func2() in file.c
354+
ZZ
355+
diff --git a/file.c b/file.c
356+
--- /dev/null
357+
+++ b/file.c
358+
@@ -0,0 +6,4 @@
359+
+int func2()
360+
+{
361+
+ return F2;
362+
+}
363+
EOF
364+
git log --graph --oneline -L:func2:file.c >actual &&
365+
test_cmp expect actual
366+
'
367+
340368
test_done

0 commit comments

Comments
 (0)