diff mbox series

[16/26] help: fix leaking return value from `help_unknown_cmd()`

Message ID ea8677ff965711ebca140f130c62c704e0ab0753.1730901926.git.ps@pks.im (mailing list archive)
State New
Headers show
Series [01/26] builtin/blame: fix leaking blame entries with `--incremental` | expand

Commit Message

Patrick Steinhardt Nov. 6, 2024, 3:11 p.m. UTC
While `help_unknown_cmd()` would usually die on an unknown command, it
instead returns an autocorrected command when "help.autocorrect" is set.
But while the function is declared to return a string constant, it
actually returns an allocated string in that case. Callers thus aren't
aware that they have to free the string, leading to a memory leak.

Fix the function return type to be non-constant and free the returned
value at its only callsite.

Note that we cannot simply take ownership of `main_cmds.names[0]->name`
and then eventually free it. This is because the `struct cmdname` is
using a flex array to allocate the name, so the name pointer points into
the middle of the structure and thus cannot be freed.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 git.c  |  4 +++-
 help.c | 11 +++++------
 help.h |  2 +-
 3 files changed, 9 insertions(+), 8 deletions(-)
diff mbox series

Patch

diff --git a/git.c b/git.c
index 159dd45b082..46b3c740c5d 100644
--- a/git.c
+++ b/git.c
@@ -961,7 +961,9 @@  int cmd_main(int argc, const char **argv)
 			exit(1);
 		}
 		if (!done_help) {
-			strvec_replace(&args, 0, help_unknown_cmd(cmd));
+			char *assumed = help_unknown_cmd(cmd);
+			strvec_replace(&args, 0, assumed);
+			free(assumed);
 			cmd = args.v[0];
 			done_help = 1;
 		} else {
diff --git a/help.c b/help.c
index 51adc530d7a..0290aba9f38 100644
--- a/help.c
+++ b/help.c
@@ -612,14 +612,14 @@  static const char bad_interpreter_advice[] =
 	N_("'%s' appears to be a git command, but we were not\n"
 	"able to execute it. Maybe git-%s is broken?");
 
-const char *help_unknown_cmd(const char *cmd)
+char *help_unknown_cmd(const char *cmd)
 {
 	struct help_unknown_cmd_config cfg = { 0 };
 	int i, n, best_similarity = 0;
 	struct cmdnames main_cmds = { 0 };
 	struct cmdnames other_cmds = { 0 };
 	struct cmdname_help *common_cmds;
-	const char *assumed = NULL;
+	char *assumed = NULL;
 
 	read_early_config(the_repository, git_unknown_cmd_config, &cfg);
 
@@ -696,9 +696,8 @@  const char *help_unknown_cmd(const char *cmd)
 			; /* still counting */
 	}
 	if (cfg.autocorrect && n == 1 && SIMILAR_ENOUGH(best_similarity)) {
-		assumed = main_cmds.names[0]->name;
-		main_cmds.names[0] = NULL;
-		cmdnames_release(&main_cmds);
+		assumed = xstrdup(main_cmds.names[0]->name);
+
 		fprintf_ln(stderr,
 			   _("WARNING: You called a Git command named '%s', "
 			     "which does not exist."),
@@ -716,7 +715,7 @@  const char *help_unknown_cmd(const char *cmd)
 			strbuf_release(&msg);
 			if (!(starts_with(answer, "y") ||
 			      starts_with(answer, "Y"))) {
-				assumed = NULL;
+				FREE_AND_NULL(assumed);
 				goto out;
 			}
 		} else {
diff --git a/help.h b/help.h
index e716ee27ea1..67207b3073c 100644
--- a/help.h
+++ b/help.h
@@ -32,7 +32,7 @@  void list_all_other_cmds(struct string_list *list);
 void list_cmds_by_category(struct string_list *list,
 			   const char *category);
 void list_cmds_by_config(struct string_list *list);
-const char *help_unknown_cmd(const char *cmd);
+char *help_unknown_cmd(const char *cmd);
 void load_command_list(const char *prefix,
 		       struct cmdnames *main_cmds,
 		       struct cmdnames *other_cmds);