git: refactor builtin handling to use a `struct strvec`

Similar as with the preceding commit, `handle_builtin()` does not
properly track lifetimes of the `argv` array and its strings. As it may
end up modifying the array this can lead to memory leaks in case it
contains allocated strings.

Refactor the function to use a `struct strvec` instead.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
Patrick Steinhardt 2024-11-20 14:39:40 +01:00 committed by Junio C Hamano
parent ffc5c046fb
commit 1dd7c32daa
2 changed files with 31 additions and 35 deletions

62
git.c
View File

@ -696,63 +696,57 @@ void load_builtin_commands(const char *prefix, struct cmdnames *cmds)
} }


#ifdef STRIP_EXTENSION #ifdef STRIP_EXTENSION
static void strip_extension(const char **argv) static void strip_extension(struct strvec *args)
{ {
size_t len; size_t len;


if (strip_suffix(argv[0], STRIP_EXTENSION, &len)) if (strip_suffix(args->v[0], STRIP_EXTENSION, &len)) {
argv[0] = xmemdupz(argv[0], len); char *stripped = xmemdupz(args->v[0], len);
strvec_replace(args, 0, stripped);
free(stripped);
}
} }
#else #else
#define strip_extension(cmd) #define strip_extension(cmd)
#endif #endif


static void handle_builtin(int argc, const char **argv) static void handle_builtin(struct strvec *args)
{ {
struct strvec args = STRVEC_INIT;
const char **argv_copy = NULL;
const char *cmd; const char *cmd;
struct cmd_struct *builtin; struct cmd_struct *builtin;


strip_extension(argv); strip_extension(args);
cmd = argv[0]; cmd = args->v[0];


/* Turn "git cmd --help" into "git help --exclude-guides cmd" */ /* Turn "git cmd --help" into "git help --exclude-guides cmd" */
if (argc > 1 && !strcmp(argv[1], "--help")) { if (args->nr > 1 && !strcmp(args->v[1], "--help")) {
int i; const char *exclude_guides_arg[] = { "--exclude-guides" };


argv[1] = argv[0]; strvec_replace(args, 1, args->v[0]);
argv[0] = cmd = "help"; strvec_replace(args, 0, "help");

cmd = "help";
for (i = 0; i < argc; i++) { strvec_splice(args, 2, 0, exclude_guides_arg,
strvec_push(&args, argv[i]); ARRAY_SIZE(exclude_guides_arg));
if (!i)
strvec_push(&args, "--exclude-guides");
} }


argc++; builtin = get_builtin(cmd);
if (builtin) {
const char **argv_copy = NULL;
int ret;


/* /*
* `run_builtin()` will modify the argv array, so we need to * `run_builtin()` will modify the argv array, so we need to
* create a shallow copy such that we can free all of its * create a shallow copy such that we can free all of its
* strings. * strings.
*/ */
CALLOC_ARRAY(argv_copy, argc + 1); if (args->nr)
COPY_ARRAY(argv_copy, args.v, argc); DUP_ARRAY(argv_copy, args->v, args->nr + 1);


argv = argv_copy; ret = run_builtin(builtin, args->nr, argv_copy, the_repository);
} strvec_clear(args);

builtin = get_builtin(cmd);
if (builtin) {
int ret = run_builtin(builtin, argc, argv, the_repository);
strvec_clear(&args);
free(argv_copy); free(argv_copy);
exit(ret); exit(ret);
} }

strvec_clear(&args);
free(argv_copy);
} }


static void execv_dashed_external(const char **argv) static void execv_dashed_external(const char **argv)
@ -815,7 +809,7 @@ static int run_argv(struct strvec *args)
* process. * process.
*/ */
if (!done_alias) if (!done_alias)
handle_builtin(args->nr, args->v); handle_builtin(args);
else if (get_builtin(args->v[0])) { else if (get_builtin(args->v[0])) {
struct child_process cmd = CHILD_PROCESS_INIT; struct child_process cmd = CHILD_PROCESS_INIT;
int i; int i;
@ -916,8 +910,10 @@ int cmd_main(int argc, const char **argv)
* that one cannot handle it. * that one cannot handle it.
*/ */
if (skip_prefix(cmd, "git-", &cmd)) { if (skip_prefix(cmd, "git-", &cmd)) {
argv[0] = cmd; strvec_push(&args, cmd);
handle_builtin(argc, argv); strvec_pushv(&args, argv + 1);
handle_builtin(&args);
strvec_clear(&args);
die(_("cannot handle %s as a builtin"), cmd); die(_("cannot handle %s as a builtin"), cmd);
} }



View File

@ -2,7 +2,7 @@


test_description='test trace2 facility (perf target)' test_description='test trace2 facility (perf target)'


TEST_PASSES_SANITIZE_LEAK=false TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh


# Turn off any inherited trace2 settings for this test. # Turn off any inherited trace2 settings for this test.