Ævar Arnfjörð Bjarmason <avarab@xxxxxxxxx> writes: > Fix a regression in c45dc9cf30 (diff: plug memory leak from regcomp() > on {log,diff} -I, 2021-02-11), as noted in [1] there was a logic error > where we'd free the regex too soon. > > Now we'll ensure that diff_free() can be called repeatedly > instead. We'd ultimately like to do away with the "no_free" confusion > surrounding it, and to attempt to free() things only once, as outlined > in [2]. But in the meantime this will fix the segfault. Hmph, repeated calls to diff_free_file() now closes the file upon the first call. I would have expected that such a resource would be released when all the references go away, i.e. upon the last call. The same thing for the ignore-regex array. Clearing the "options->close_file" bit, and using FREE_AND_NULL(), would hide a breakage that could be caused by this change, doesn't it, because any use-after-release will say "ah, no need to close the file" and "oh, there is no regex". The former is not so worrisome, but the latter may be---we may no longer have regex because the first call to diff_free_ignore_regex() has cleared it and the code that wants to use the regex, if exists, would happily say "oh, there is no regex", instead of exposing the use-after-release breakage to a segfault. > Thus we're here testing that -I<regex> is ignored in this case, and > likewise for --output=<file>, but since this is what we were doing > before c45dc9cf30 let's accept it for now. It is true that the result of applying this patch is equivalent to c45dc9cf (diff: plug memory leak from regcomp() on {log,diff} -I, 2021-02-11), but doesn't that merely point at the commit as the source of behaviour breakage? With ignore-regex leaking before that commit, wouldn't we have been using ignore-regex with combined diff machinery? Sorry, but I am failing to convince myself that this is not sweep the issue under the rug. > 1. https://lore.kernel.org/git/a6a14213-bc82-d6fb-43dd-5a423c40a4f8@xxxxxx/ > 2. https://lore.kernel.org/git/220520.86pmk81a9z.gmgdl@xxxxxxxxxxxxxxxxxxx/ > > Reported-by: René Scharfe <l.s.r@xxxxxx> > Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@xxxxxxxxx> > --- > > On Sat, May 14 2022, René Scharfe wrote: > >> Hi all, >> >> git diff segfaults when it's asked to produce a combined diff and ignore >> certain lines with --ignore-matching-lines/-I, e.g.: >> >> $ git diff -I DEF_VER v2.33.3 v2.33.3^@ >> zsh: segmentation fault ./git-diff -I DEF_VER v2.33.3 v2.33.3^@ > > diff.c | 9 ++++++--- > t/t4013-diff-various.sh | 15 +++++++++++++++ > 2 files changed, 21 insertions(+), 3 deletions(-) > > diff --git a/diff.c b/diff.c > index e71cf758861..183c9f21305 100644 > --- a/diff.c > +++ b/diff.c > @@ -6432,8 +6432,10 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o) > > static void diff_free_file(struct diff_options *options) > { > - if (options->close_file) > - fclose(options->file); > + if (!options->close_file) > + return; > + options->close_file = 0; > + fclose(options->file); > } > > static void diff_free_ignore_regex(struct diff_options *options) > @@ -6444,7 +6446,8 @@ static void diff_free_ignore_regex(struct diff_options *options) > regfree(options->ignore_regex[i]); > free(options->ignore_regex[i]); > } > - free(options->ignore_regex); > + options->ignore_regex_nr = 0; > + FREE_AND_NULL(options->ignore_regex); > } > > void diff_free(struct diff_options *options) > diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh > index 056e922164d..b556d185f53 100755 > --- a/t/t4013-diff-various.sh > +++ b/t/t4013-diff-various.sh > @@ -614,4 +614,19 @@ test_expect_success 'diff -I<regex>: detect malformed regex' ' > test_i18ngrep "invalid regex given to -I: " error > ' > > +test_expect_success 'diff -I<regex>: combined diff does not segfault' ' > + revs="HEAD~2 HEAD~ HEAD" && > + git diff $revs >expect && > + git diff -I . $revs >actual && > + test_cmp expect actual And indeed this casts such a broken behaviour in stone. > +' > + > +test_expect_success 'diff --output=<file>: combined diff does not segfault' ' > + revs="HEAD~2 HEAD~ HEAD" && > + git diff --output=expect.file $revs >expect.out && > + git diff $revs >actual && > + test_cmp expect.out actual && > + test_must_be_empty expect.file So is this one. > +' > + > test_done