Subject: Re: [PATCH v3 18/22] builtin/format-patch: fix various trivial memory leaks

From: Patrick Steinhardt <[email-masked]>
Date: Wed, 14 Aug 2024 06:56:25 +0200

On Tue, Aug 13, 2024 at 09:55:10AM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <[email-masked]> writes:
> 
> > There are various memory leaks hit by git-format-patch(1). Basically all
> > of them are trivial, except that un-setting `diffopt.no_free` requires
> > us to unset the `diffopt.file` because we manually close it already.
> >
> > Signed-off-by: Patrick Steinhardt <[email-masked]>
> > ---
> >  builtin/log.c           | 12 +++++++++---
> >  t/t4014-format-patch.sh |  1 +
> >  2 files changed, 10 insertions(+), 3 deletions(-)
> >
> > diff --git a/builtin/log.c b/builtin/log.c
> > index a73a767606..ff997a0d0e 100644
> > --- a/builtin/log.c
> > +++ b/builtin/log.c
> > @@ -1833,6 +1833,7 @@ static struct commit *get_base_commit(const struct format_config *cfg,
> >  			}
> >  
> >  			rev[i] = merge_base->item;
> > +			free_commit_list(merge_base);
> >  		}
> >  
> >  		if (rev_nr % 2)
> 
> This is correct, but isn't merge_base leaking when there are
> multiple and we are not dying on failure?  Perhaps something along
> this line?

Yes, good catch.

> 			struct commit_list *merge_base = NULL;
> 			if (repo_get_merge_bases(the_repository,
> 						 rev[2 * i],
> 						 rev[2 * i + 1], &merge_base) < 0 ||
> 			    !merge_base || merge_base->next) {
> 				if (die_on_failure) {
> 					die(_("failed to find exact merge base"));
> 				} else {
>                  +               	free_commit_list(merge_base);
> 					free(rev);
> 					return NULL;
> 				}
> 			}
> 
> > @@ -2548,12 +2550,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
> >  			else
> >  				print_signature(signature, rev.diffopt.file);
> >  		}
> > -		if (output_directory)
> > +		if (output_directory) {
> >  			fclose(rev.diffopt.file);
> > +			rev.diffopt.file = NULL;
> 
> Is this a leakfix, or just a general code hygiene improvement?

Not a leak fix, but required because of the leak fix. As we now unset
`rev.diffopt.no_free`, `release_revisions()` will call `diff_free()` and
try to close the file pointer. But as we already did, it would cause a
segfault as we now try to close it twice.

Patrick