In `handle_builtin()` we may end up creating an ad-hoc argv array in case we see that the command line contains the "--help" parameter. In this case we observe two memory leaks though: - We leak the `struct strvec` itself because we directly exit after calling `run_builtin()`, without bothering about any cleanups. - Even if we free'd that vector we'd end up leaking some of its strings because `run_builtin()` will modify the array. Plug both of these leaks. Signed-off-by: Patrick Steinhardt <ps@xxxxxx> --- git.c | 22 +++++++++++++++++++--- t/t0012-help.sh | 1 + 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/git.c b/git.c index 9a618a2740f..3c7fabfda26 100644 --- a/git.c +++ b/git.c @@ -711,6 +711,7 @@ static void strip_extension(const char **argv) static void handle_builtin(int argc, const char **argv) { struct strvec args = STRVEC_INIT; + const char **argv_copy = NULL; const char *cmd; struct cmd_struct *builtin; @@ -731,13 +732,28 @@ static void handle_builtin(int argc, const char **argv) } argc++; - argv = args.v; + + /* + * `run_builtin()` will modify the argv array, so we need to + * create a shallow copy such that we can free all of its + * strings. + */ + CALLOC_ARRAY(argv_copy, argc + 1); + COPY_ARRAY(argv_copy, args.v, argc); + + argv = argv_copy; } builtin = get_builtin(cmd); - if (builtin) - exit(run_builtin(builtin, argc, argv)); + if (builtin) { + int ret = run_builtin(builtin, argc, argv); + strvec_clear(&args); + free(argv_copy); + exit(ret); + } + strvec_clear(&args); + free(argv_copy); } static void execv_dashed_external(const char **argv) diff --git a/t/t0012-help.sh b/t/t0012-help.sh index 1d273d91c21..9eae0d83563 100755 --- a/t/t0012-help.sh +++ b/t/t0012-help.sh @@ -2,6 +2,7 @@ test_description='help' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh configure_help () { -- 2.46.0.551.gc5ee8f2d1c.dirty