diff mbox series

[v2,16/27] help: fix leaking return value from `help_unknown_cmd()`

Message ID 20241111-b4-pks-leak-fixes-pt10-v2-16-6154bf91f0b0@pks.im (mailing list archive)
State New
Headers show
Series Memory leak fixes (pt.10, final) | expand

Commit Message

Patrick Steinhardt Nov. 11, 2024, 10:38 a.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 | 7 +++----
 help.h | 2 +-
 3 files changed, 7 insertions(+), 6 deletions(-)
diff mbox series

Patch

diff --git a/git.c b/git.c
index 159dd45b08204c4a89d1dc4ab6990978e2454eb6..46b3c740c5d665388917c6eee3052cc3ef8368f2 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 8b56cd6e25ba5f2be2cbf2a7a9ed48136e12a0c7..8a830ba35c6fd1377213e2ed40846f3d46dba363 100644
--- a/help.c
+++ b/help.c
@@ -612,7 +612,7 @@  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;
@@ -695,9 +695,8 @@  const char *help_unknown_cmd(const char *cmd)
 			; /* still counting */
 	}
 	if (cfg.autocorrect && n == 1 && SIMILAR_ENOUGH(best_similarity)) {
-		const char *assumed = main_cmds.names[0]->name;
-		main_cmds.names[0] = NULL;
-		cmdnames_release(&main_cmds);
+		char *assumed = xstrdup(main_cmds.names[0]->name);
+
 		fprintf_ln(stderr,
 			   _("WARNING: You called a Git command named '%s', "
 			     "which does not exist."),
diff --git a/help.h b/help.h
index e716ee27ea174c4dfc3b941619bf361972894212..67207b3073ce48fa25f67f9a4922abc4e2ce7926 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);