diff mbox series

[18/22] builtin/format-patch: fix various trivial memory leaks

Message ID bf818a8a79385af739345e22c74550acd5f31a0b.1722933643.git.ps@pks.im (mailing list archive)
State Superseded
Headers show
Series Memory leak fixes (pt.4) | expand

Commit Message

Patrick Steinhardt Aug. 6, 2024, 9 a.m. UTC
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 <ps@pks.im>
---
 builtin/log.c           | 12 +++++++++---
 t/t4014-format-patch.sh |  1 +
 2 files changed, 10 insertions(+), 3 deletions(-)

Comments

James Liu Aug. 7, 2024, 8:51 a.m. UTC | #1
> diff --git a/builtin/log.c b/builtin/log.c
> index a73a767606..ff997a0d0e 100644
> --- a/builtin/log.c
> +++ b/builtin/log.c
> @@ -2023,6 +2024,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
>  	const char *rfc = NULL;
>  	int creation_factor = -1;
>  	const char *signature = git_version_string;
> +	char *signature_to_free = NULL;
>  	char *signature_file_arg = NULL;
>  	struct keep_callback_data keep_callback_data = {
>  		.cfg = &cfg,
> @@ -2443,7 +2445,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
>  
>  		if (strbuf_read_file(&buf, signature_file, 128) < 0)
>  			die_errno(_("unable to read signature file '%s'"), signature_file);
> -		signature = strbuf_detach(&buf, NULL);
> +		signature = signature_to_free = strbuf_detach(&buf, NULL);

Do I understand this correctly, that the multiple assignment here allows
us to maintain a reference to the pointer returned by `strbuf_detach()`
in `signature_to_free`, and we do this because `signature` can take on a
different value below?

>  	} else if (cfg.signature) {
>  		signature = cfg.signature;
>  	}
Patrick Steinhardt Aug. 8, 2024, 5:05 a.m. UTC | #2
On Wed, Aug 07, 2024 at 06:51:52PM +1000, James Liu wrote:
> > diff --git a/builtin/log.c b/builtin/log.c
> > index a73a767606..ff997a0d0e 100644
> > --- a/builtin/log.c
> > +++ b/builtin/log.c
> > @@ -2023,6 +2024,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
> >  	const char *rfc = NULL;
> >  	int creation_factor = -1;
> >  	const char *signature = git_version_string;
> > +	char *signature_to_free = NULL;
> >  	char *signature_file_arg = NULL;
> >  	struct keep_callback_data keep_callback_data = {
> >  		.cfg = &cfg,
> > @@ -2443,7 +2445,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
> >  
> >  		if (strbuf_read_file(&buf, signature_file, 128) < 0)
> >  			die_errno(_("unable to read signature file '%s'"), signature_file);
> > -		signature = strbuf_detach(&buf, NULL);
> > +		signature = signature_to_free = strbuf_detach(&buf, NULL);
> 
> Do I understand this correctly, that the multiple assignment here allows
> us to maintain a reference to the pointer returned by `strbuf_detach()`
> in `signature_to_free`, and we do this because `signature` can take on a
> different value below?

Not only below, but also its default value is `git_version_string`,
which is a string constant. So we use the multiple assignment such that
we can avoid freeing `signature`, which may contain string constants,
and unconditionally free `signature_to_free` because that variable
always holds an allocated string or a `NULL` pointer.

Patrick

> >  	} else if (cfg.signature) {
> >  		signature = cfg.signature;
> >  	}
> 
>
diff mbox series

Patch

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)
@@ -2023,6 +2024,7 @@  int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	const char *rfc = NULL;
 	int creation_factor = -1;
 	const char *signature = git_version_string;
+	char *signature_to_free = NULL;
 	char *signature_file_arg = NULL;
 	struct keep_callback_data keep_callback_data = {
 		.cfg = &cfg,
@@ -2443,7 +2445,7 @@  int cmd_format_patch(int argc, const char **argv, const char *prefix)
 
 		if (strbuf_read_file(&buf, signature_file, 128) < 0)
 			die_errno(_("unable to read signature file '%s'"), signature_file);
-		signature = strbuf_detach(&buf, NULL);
+		signature = signature_to_free = strbuf_detach(&buf, NULL);
 	} else if (cfg.signature) {
 		signature = cfg.signature;
 	}
@@ -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;
+		}
 	}
 	stop_progress(&progress);
 	free(list);
-	free(branch_name);
 	if (ignore_if_in_upstream)
 		free_patch_ids(&ids);
 
@@ -2565,11 +2568,14 @@  int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	strbuf_release(&rdiff_title);
 	free(description_file);
 	free(signature_file_arg);
+	free(signature_to_free);
+	free(branch_name);
 	free(to_free);
 	free(rev.message_id);
 	if (rev.ref_message_ids)
 		string_list_clear(rev.ref_message_ids, 0);
 	free(rev.ref_message_ids);
+	rev.diffopt.no_free = 0;
 	release_revisions(&rev);
 	format_config_release(&cfg);
 	return 0;
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 884f83fb8a..1c46e963e4 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -8,6 +8,7 @@  test_description='various format-patch tests'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh