From e77b3da6bb60e9af5963c9e42442afe53af1780b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Sep 2022 01:14:08 +0200 Subject: [PATCH 01/17] submodule--helper: fix a leak in "clone_submodule" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a memory leak of the "clone_data_path" variable that we copy or derive from the "struct module_clone_data" in clone_submodule(). This code was refactored in preceding commits, but the leak has been with us since f8eaa0ba98b (submodule--helper, module_clone: always operate on absolute paths, 2016-03-31). For the "else" case we don't need to xstrdup() the "clone_data->path", and we don't need to free our own "clone_data_path". We can therefore assign the "clone_data->path" to our own "clone_data_path" right away, and only override it (and remember to free it!) if we need to xstrfmt() a replacement. In the case of the module_clone() caller it's from "argv", and doesn't need to be free'd, and in the case of the add_submodule() caller we get a pointer to "sm_path", which doesn't need to be directly free'd either. Fixing this leak makes several tests pass, so let's mark them as passing with TEST_PASSES_SANITIZE_LEAK=true. Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 10 +++++----- t/t1500-rev-parse.sh | 1 + t/t6008-rev-list-submodule.sh | 1 + t/t7414-submodule-mistakes.sh | 2 ++ t/t7506-status-submodule.sh | 1 + t/t7507-commit-verbose.sh | 2 ++ 6 files changed, 12 insertions(+), 5 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 3a2a92da51..d308b14904 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1587,13 +1587,12 @@ static int clone_submodule(const struct module_clone_data *clone_data, char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name); char *sm_alternate = NULL, *error_strategy = NULL; struct child_process cp = CHILD_PROCESS_INIT; - const char *clone_data_path; + const char *clone_data_path = clone_data->path; + char *to_free = NULL; if (!is_absolute_path(clone_data->path)) - clone_data_path = xstrfmt("%s/%s", get_git_work_tree(), - clone_data->path); - else - clone_data_path = xstrdup(clone_data->path); + clone_data_path = to_free = xstrfmt("%s/%s", get_git_work_tree(), + clone_data->path); if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0) die(_("refusing to create/use '%s' in another submodule's " @@ -1678,6 +1677,7 @@ static int clone_submodule(const struct module_clone_data *clone_data, free(sm_gitdir); free(p); + free(to_free); return 0; } diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh index 1c2df08333..0e13bcb4eb 100755 --- a/t/t1500-rev-parse.sh +++ b/t/t1500-rev-parse.sh @@ -4,6 +4,7 @@ test_description='test git rev-parse' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_one () { diff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh index 3153a0d891..12e67e187e 100755 --- a/t/t6008-rev-list-submodule.sh +++ b/t/t6008-rev-list-submodule.sh @@ -8,6 +8,7 @@ test_description='git rev-list involving submodules that this repo has' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh index f2e7df59cf..3269298197 100755 --- a/t/t7414-submodule-mistakes.sh +++ b/t/t7414-submodule-mistakes.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='handling of common mistakes people may make with submodules' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'create embedded repository' ' diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh index 3fcb44767f..f5426a8e58 100755 --- a/t/t7506-status-submodule.sh +++ b/t/t7506-status-submodule.sh @@ -2,6 +2,7 @@ test_description='git status for submodule' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_create_repo_with_commit () { diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh index ed2653d46f..92462a2237 100755 --- a/t/t7507-commit-verbose.sh +++ b/t/t7507-commit-verbose.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='verbose commit template' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh write_script "check-for-diff" <<\EOF && From d76260e60a5c0379ca21a26bbded6d6426ae878f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Sep 2022 01:14:09 +0200 Subject: [PATCH 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a leak in code added in 1012a5cbc3f (submodule--helper run-update-procedure: learn --remote, 2022-03-04), we need to free() the xstrdup()'d string. This gets e.g. t/t7419-submodule-set-branch.sh closer to passing under SANITIZE=leak. Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index d308b14904..d46ba102f6 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -2448,6 +2448,8 @@ static int update_submodule(struct update_data *update_data) return code; remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch); + free(remote_name); + if (!update_data->nofetch) { if (fetch_in_submodule(update_data->sm_path, update_data->depth, 0, NULL)) From 8fb201d4da0e9244f44dabdb1a5f5ea2809b2270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Sep 2022 01:14:10 +0200 Subject: [PATCH 03/17] submodule--helper: fix most "struct pathspec" memory leaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Call clear_pathspec() at the end of various functions that work with and allocate a "struct pathspec". In some cases the zero-initialization here isn't strictly needed, but as we're moving to a "goto cleanup" pattern let's make sure that it's safe to call clear_pathspec(), we don't want the data to be uninitialized. E.g. for module_foreach() we can see from looking at module_list_compute() that if it returns non-zero that the "pathspec" will always have been initialized. But relying on that both assumes knowledge about parse_pathspec(), and would set up a fragile pattern going forward. Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 74 +++++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index d46ba102f6..4bba1c7561 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -379,7 +379,7 @@ cleanup: static int module_foreach(int argc, const char **argv, const char *prefix) { struct foreach_cb info = FOREACH_CB_INIT; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; struct option module_foreach_options[] = { OPT__QUIET(&info.quiet, N_("suppress output of entering each submodule command")), @@ -391,12 +391,13 @@ static int module_foreach(int argc, const char **argv, const char *prefix) N_("git submodule foreach [--quiet] [--recursive] [--] "), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, module_foreach_options, git_submodule_helper_usage, 0); if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0) - return 1; + goto cleanup; info.argc = argc; info.argv = argv; @@ -404,7 +405,10 @@ static int module_foreach(int argc, const char **argv, const char *prefix) for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info); - return 0; + ret = 0; +cleanup: + clear_pathspec(&pathspec); + return ret; } static int starts_with_dot_slash(const char *const path) @@ -515,7 +519,7 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data static int module_init(int argc, const char **argv, const char *prefix) { struct init_cb info = INIT_CB_INIT; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; int quiet = 0; struct option module_init_options[] = { @@ -526,12 +530,13 @@ static int module_init(int argc, const char **argv, const char *prefix) N_("git submodule init [] []"), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, module_init_options, git_submodule_helper_usage, 0); if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; + goto cleanup; /* * If there are no path args and submodule.active is set then, @@ -546,7 +551,10 @@ static int module_init(int argc, const char **argv, const char *prefix) for_each_listed_submodule(&list, init_submodule_cb, &info); - return 0; + ret = 0; +cleanup: + clear_pathspec(&pathspec); + return ret; } struct status_cb { @@ -693,7 +701,7 @@ static void status_submodule_cb(const struct cache_entry *list_item, static int module_status(int argc, const char **argv, const char *prefix) { struct status_cb info = STATUS_CB_INIT; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; int quiet = 0; struct option module_status_options[] = { @@ -706,12 +714,13 @@ static int module_status(int argc, const char **argv, const char *prefix) N_("git submodule status [--quiet] [--cached] [--recursive] [...]"), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, module_status_options, git_submodule_helper_usage, 0); if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; + goto cleanup; info.prefix = prefix; if (quiet) @@ -719,7 +728,10 @@ static int module_status(int argc, const char **argv, const char *prefix) for_each_listed_submodule(&list, status_submodule_cb, &info); - return 0; + ret = 0; +cleanup: + clear_pathspec(&pathspec); + return ret; } struct module_cb { @@ -1258,7 +1270,7 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data static int module_sync(int argc, const char **argv, const char *prefix) { struct sync_cb info = SYNC_CB_INIT; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; int quiet = 0; int recursive = 0; @@ -1272,12 +1284,13 @@ static int module_sync(int argc, const char **argv, const char *prefix) N_("git submodule sync [--quiet] [--recursive] []"), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, module_sync_options, git_submodule_helper_usage, 0); if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; + goto cleanup; info.prefix = prefix; if (quiet) @@ -1287,7 +1300,10 @@ static int module_sync(int argc, const char **argv, const char *prefix) for_each_listed_submodule(&list, sync_submodule_cb, &info); - return 0; + ret = 0; +cleanup: + clear_pathspec(&pathspec); + return ret; } struct deinit_cb { @@ -1396,7 +1412,7 @@ static void deinit_submodule_cb(const struct cache_entry *list_item, static int module_deinit(int argc, const char **argv, const char *prefix) { struct deinit_cb info = DEINIT_CB_INIT; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; int quiet = 0; int force = 0; @@ -1411,6 +1427,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix) N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [...]]"), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, module_deinit_options, git_submodule_helper_usage, 0); @@ -1425,7 +1442,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix) die(_("Use '--all' if you really want to deinitialize all submodules")); if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; + goto cleanup; info.prefix = prefix; if (quiet) @@ -1435,7 +1452,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix) for_each_listed_submodule(&list, deinit_submodule_cb, &info); - return 0; + ret = 0; +cleanup: + clear_pathspec(&pathspec); + return ret; } struct module_clone_data { @@ -2540,7 +2560,7 @@ cleanup: static int module_update(int argc, const char **argv, const char *prefix) { - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct update_data opt = UPDATE_DATA_INIT; struct list_objects_filter_options filter_options = { 0 }; int ret; @@ -2617,8 +2637,8 @@ static int module_update(int argc, const char **argv, const char *prefix) opt.update_strategy.type = opt.update_default; if (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) { - list_objects_filter_release(&filter_options); - return 1; + ret = 1; + goto cleanup; } if (pathspec.nr) @@ -2629,8 +2649,10 @@ static int module_update(int argc, const char **argv, const char *prefix) struct init_cb info = INIT_CB_INIT; if (module_list_compute(argc, argv, opt.prefix, - &pathspec, &list) < 0) - return 1; + &pathspec, &list) < 0) { + ret = 1; + goto cleanup; + } /* * If there are no path args and submodule.active is set then, @@ -2647,7 +2669,9 @@ static int module_update(int argc, const char **argv, const char *prefix) } ret = update_submodules(&opt); +cleanup: list_objects_filter_release(&filter_options); + clear_pathspec(&pathspec); return ret; } @@ -2731,7 +2755,7 @@ static int push_check(int argc, const char **argv, const char *prefix) static int absorb_git_dirs(int argc, const char **argv, const char *prefix) { int i; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES; struct option embed_gitdir_options[] = { @@ -2746,17 +2770,21 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix) N_("git submodule absorbgitdirs [] [...]"), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, embed_gitdir_options, git_submodule_helper_usage, 0); if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; + goto cleanup; for (i = 0; i < list.nr; i++) absorb_git_dir_into_superproject(list.entries[i]->name, flags); - return 0; + ret = 0; +cleanup: + clear_pathspec(&pathspec); + return ret; } static int module_config(int argc, const char **argv, const char *prefix) From 4b9d12460d18d02bf6f12cc3c96340e53acf03e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Sep 2022 01:14:11 +0200 Subject: [PATCH 04/17] submodule--helper: "struct pathspec" memory leak in module_update() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The module_update() function calls module_list_compute() twice, which in turn will reset the "struct pathspec" passed to it. Let's instead track two of them, and clear them both. Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 4bba1c7561..ef56471a8c 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -2561,6 +2561,7 @@ cleanup: static int module_update(int argc, const char **argv, const char *prefix) { struct pathspec pathspec = { 0 }; + struct pathspec pathspec2 = { 0 }; struct update_data opt = UPDATE_DATA_INIT; struct list_objects_filter_options filter_options = { 0 }; int ret; @@ -2649,7 +2650,7 @@ static int module_update(int argc, const char **argv, const char *prefix) struct init_cb info = INIT_CB_INIT; if (module_list_compute(argc, argv, opt.prefix, - &pathspec, &list) < 0) { + &pathspec2, &list) < 0) { ret = 1; goto cleanup; } @@ -2672,6 +2673,7 @@ static int module_update(int argc, const char **argv, const char *prefix) cleanup: list_objects_filter_release(&filter_options); clear_pathspec(&pathspec); + clear_pathspec(&pathspec2); return ret; } From 0a4d31537d66bd2249adc8d923e2e6829204059e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Sep 2022 01:14:12 +0200 Subject: [PATCH 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a memory leak in c51f8f94e5b (submodule--helper: run update procedures from C, 2021-08-24) and 3c3558f0953 (submodule--helper: run update using child process struct, 2022-03-15) by not allocating memory in the first place. The "dir" member of "struct child_process" will not be modified by that API, and it's declared to be "const char *". So let's not needlessly duplicate these strings. Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index ef56471a8c..ebd00f57e5 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -2126,7 +2126,7 @@ static int is_tip_reachable(const char *path, const struct object_id *oid) char *hex = oid_to_hex(oid); cp.git_cmd = 1; - cp.dir = xstrdup(path); + cp.dir = path; cp.no_stderr = 1; strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL); @@ -2145,7 +2145,7 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, prepare_submodule_repo_env(&cp.env); cp.git_cmd = 1; - cp.dir = xstrdup(module_path); + cp.dir = module_path; strvec_push(&cp.args, "fetch"); if (quiet) @@ -2198,7 +2198,7 @@ static int run_update_command(const struct update_data *ud, int subforce) } strvec_push(&cp.args, oid); - cp.dir = xstrdup(ud->sm_path); + cp.dir = ud->sm_path; prepare_submodule_repo_env(&cp.env); if ((ret = run_command(&cp))) { switch (ud->update_strategy.type) { From 87a683482a3982751fdc9d4252035d0cafab605d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Sep 2022 01:14:13 +0200 Subject: [PATCH 06/17] submodule--helper: add and use *_release() functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add release functions for "struct module_list", "struct submodule_update_clone" and "struct update_data". Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 28 +++++++++++++++++++++++++++- t/t6134-pathspec-in-submodule.sh | 1 + 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index ebd00f57e5..65cd25a251 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -172,6 +172,11 @@ struct module_list { }; #define MODULE_LIST_INIT { 0 } +static void module_list_release(struct module_list *ml) +{ + free(ml->entries); +} + static int module_list_compute(int argc, const char **argv, const char *prefix, struct pathspec *pathspec, @@ -234,7 +239,7 @@ static void module_list_active(struct module_list *list) active_modules.entries[active_modules.nr++] = ce; } - free(list->entries); + module_list_release(list); *list = active_modules; } @@ -407,6 +412,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix) ret = 0; cleanup: + module_list_release(&list); clear_pathspec(&pathspec); return ret; } @@ -553,6 +559,7 @@ static int module_init(int argc, const char **argv, const char *prefix) ret = 0; cleanup: + module_list_release(&list); clear_pathspec(&pathspec); return ret; } @@ -730,6 +737,7 @@ static int module_status(int argc, const char **argv, const char *prefix) ret = 0; cleanup: + module_list_release(&list); clear_pathspec(&pathspec); return ret; } @@ -1302,6 +1310,7 @@ static int module_sync(int argc, const char **argv, const char *prefix) ret = 0; cleanup: + module_list_release(&list); clear_pathspec(&pathspec); return ret; } @@ -1454,6 +1463,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix) ret = 0; cleanup: + module_list_release(&list); clear_pathspec(&pathspec); return ret; } @@ -1831,6 +1841,12 @@ struct submodule_update_clone { }; #define SUBMODULE_UPDATE_CLONE_INIT { 0 } +static void submodule_update_clone_release(struct submodule_update_clone *suc) +{ + free(suc->update_clone); + free(suc->failed_clones); +} + struct update_data { const char *prefix; const char *displaypath; @@ -1869,6 +1885,11 @@ struct update_data { .max_jobs = 1, \ } +static void update_data_release(struct update_data *ud) +{ + module_list_release(&ud->list); +} + static void next_submodule_warn_missing(struct submodule_update_clone *suc, struct strbuf *out, const char *displaypath) { @@ -2554,6 +2575,7 @@ static int update_submodules(struct update_data *update_data) } cleanup: + submodule_update_clone_release(&suc); string_list_clear(&update_data->references, 0); return ret; } @@ -2651,6 +2673,7 @@ static int module_update(int argc, const char **argv, const char *prefix) if (module_list_compute(argc, argv, opt.prefix, &pathspec2, &list) < 0) { + module_list_release(&list); ret = 1; goto cleanup; } @@ -2667,10 +2690,12 @@ static int module_update(int argc, const char **argv, const char *prefix) info.flags |= OPT_QUIET; for_each_listed_submodule(&list, init_submodule_cb, &info); + module_list_release(&list); } ret = update_submodules(&opt); cleanup: + update_data_release(&opt); list_objects_filter_release(&filter_options); clear_pathspec(&pathspec); clear_pathspec(&pathspec2); @@ -2786,6 +2811,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix) ret = 0; cleanup: clear_pathspec(&pathspec); + module_list_release(&list); return ret; } diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh index 0f1cb49ced..3a241f259d 100755 --- a/t/t6134-pathspec-in-submodule.sh +++ b/t/t6134-pathspec-in-submodule.sh @@ -2,6 +2,7 @@ test_description='test case exclude pathspec' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup a submodule' ' From 61adac6c4b5839ffcc8b0f7081acac4a18240644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Sep 2022 01:14:14 +0200 Subject: [PATCH 07/17] submodule--helper: fix "errmsg_str" memory leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a memory leak introduced in e83e3333b57 (submodule: port submodule subcommand 'summary' from shell to C, 2020-08-13), we sometimes append to the "errmsg", and need to free the "struct strbuf". Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 65cd25a251..c95098e621 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -973,6 +973,7 @@ static void generate_submodule_summary(struct summary_cb *info, free(displaypath); free(src_abbrev); free(dst_abbrev); + strbuf_release(&errmsg); } static void prepare_submodule_summary(struct summary_cb *info, From 980416e469ad2ed0d38630cf4c25390ae9731416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Sep 2022 01:14:15 +0200 Subject: [PATCH 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix leaks in "struct module_cb_list" and the "struct module_cb" which it contains, these fix leaks in e83e3333b57 (submodule: port submodule subcommand 'summary' from shell to C, 2020-08-13). The "sm_path" should always have been a "char *", not a "const char *", we always create it with xstrdup(). We can't mark any tests passing passing with SANITIZE=leak using "TEST_PASSES_SANITIZE_LEAK=true" as a result of this change, but "t7401-submodule-summary.sh" gets closer to passing as a result of this change. Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 21 ++++++++++++++++++++- t/t7401-submodule-summary.sh | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index c95098e621..3453faabd3 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -748,16 +748,34 @@ struct module_cb { struct object_id oid_src; struct object_id oid_dst; char status; - const char *sm_path; + char *sm_path; }; #define MODULE_CB_INIT { 0 } +static void module_cb_release(struct module_cb *mcb) +{ + free(mcb->sm_path); +} + struct module_cb_list { struct module_cb **entries; int alloc, nr; }; #define MODULE_CB_LIST_INIT { 0 } +static void module_cb_list_release(struct module_cb_list *mcbl) +{ + int i; + + for (i = 0; i < mcbl->nr; i++) { + struct module_cb *mcb = mcbl->entries[i]; + + module_cb_release(mcb); + free(mcb); + } + free(mcbl->entries); +} + struct summary_cb { int argc; const char **argv; @@ -1101,6 +1119,7 @@ static int compute_summary_module_list(struct object_id *head_oid, cleanup: strvec_clear(&diff_args); release_revisions(&rev); + module_cb_list_release(&list); return ret; } diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh index 9c3cc4cf40..542b3331a7 100755 --- a/t/t7401-submodule-summary.sh +++ b/t/t7401-submodule-summary.sh @@ -17,6 +17,7 @@ This test script tries to verify the sanity of summary subcommand of git submodu # various reasons, one of them being that there are lots of commands taking place # outside of 'test_expect_success' block, which is no longer in good-style. +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh add_file () { From 17af0a8444523f4df98e21165a3e476f05749237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Sep 2022 01:14:16 +0200 Subject: [PATCH 09/17] submodule--helper: fix a leak with repo_clear() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Call repo_clear() in ensure_core_worktree() to free the "struct repository". Fixes a leak that's been here since 74d4731da1f (submodule--helper: replace connect-gitdir-workingtree by ensure-core-worktree, 2018-08-13). Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 3453faabd3..79ffcb5090 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -2397,6 +2397,7 @@ static int ensure_core_worktree(const char *path) strbuf_release(&sb); } + repo_clear(&subrepo); return 0; } From ae3ef94d9bc61931636647c4eab558e09aaeb50a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Sep 2022 01:14:17 +0200 Subject: [PATCH 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a memory leak in the get_default_remote_submodule() function added in a77c3fcb5ec (submodule--helper: get remote names from any repository, 2022-03-04), we need to repo_clear() the submodule we initialize. Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 79ffcb5090..8866af2250 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -65,12 +65,16 @@ static int repo_get_default_remote(struct repository *repo, char **default_remot static int get_default_remote_submodule(const char *module_path, char **default_remote) { struct repository subrepo; + int ret; if (repo_submodule_init(&subrepo, the_repository, module_path, null_oid()) < 0) return die_message(_("could not get a repository handle for submodule '%s'"), module_path); - return repo_get_default_remote(&subrepo, default_remote); + ret = repo_get_default_remote(&subrepo, default_remote); + repo_clear(&subrepo); + + return ret; } static char *get_default_remote(void) From 4c81ee9669999fa02f196cb6904134bcf4c1c010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Sep 2022 01:14:18 +0200 Subject: [PATCH 11/17] submodule--helper: fix "reference" leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix leaks in the "reference" variable declared in add_submodule() and module_clone(). In preceding commits this variable was refactored out of the "struct module_clone_data", but the leak has been with us since 31224cbdc72 (clone: recursive and reference option triggers submodule alternates, 2016-08-17) and 8c8195e9c3e (submodule--helper: introduce add-clone subcommand, 2021-07-10). Those commits added an xstrdup()'d member of the STRING_LIST_INIT_NODUP'd "struct string_list". We need to free() those, but not the ones we get from argv, let's make use of the "util" member, if it has a pointer it's the pointer we'll need to free, otherwise it'll be NULL (i.e. from argv). Note that the free() of the "util" member is needed in both module_clone() and add_submodule(). The module_clone() function itself doesn't populate the "util" pointer as add_submodule() does, but module_clone() is upstream of the add_possible_reference_from_superproject() caller we're modifying here, which does do that. This does preclude the use of the "util" pointer for any other reasons for now, but that's OK. If we ever need to use it for something else we could turn it into a small "struct" with an optional "to_free" member, and switch to using string_list_clear_func(). Alternatively we could have another "struct string_list to_free" which would keep a copy of the strings we've dup'd to free(). But for now this is perfectly adequate. Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 8866af2250..7e2ffd8869 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1563,7 +1563,9 @@ static int add_possible_reference_from_superproject( sm_alternate = compute_alternate_path(sb.buf, &err); if (sm_alternate) { - string_list_append(sas->reference, xstrdup(sb.buf)); + char *p = strbuf_detach(&sb, NULL); + + string_list_append(sas->reference, p)->util = p; free(sm_alternate); } else { switch (sas->error_mode) { @@ -1795,6 +1797,7 @@ static int module_clone(int argc, const char **argv, const char *prefix) clone_submodule(&clone_data, &reference); list_objects_filter_release(&filter_options); + string_list_clear(&reference, 1); return 0; } @@ -3042,6 +3045,7 @@ static int add_submodule(const struct add_data *add_data) char *submod_gitdir_path; struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT; struct string_list reference = STRING_LIST_INIT_NODUP; + int ret = -1; /* perhaps the path already exists and is already a git repo, else clone it */ if (is_directory(add_data->sm_path)) { @@ -3097,15 +3101,17 @@ static int add_submodule(const struct add_data *add_data) clone_data.url = add_data->realrepo; clone_data.quiet = add_data->quiet; clone_data.progress = add_data->progress; - if (add_data->reference_path) - string_list_append(&reference, - xstrdup(add_data->reference_path)); + if (add_data->reference_path) { + char *p = xstrdup(add_data->reference_path); + + string_list_append(&reference, p)->util = p; + } clone_data.dissociate = add_data->dissociate; if (add_data->depth >= 0) clone_data.depth = xstrfmt("%d", add_data->depth); if (clone_submodule(&clone_data, &reference)) - return -1; + goto cleanup; prepare_submodule_repo_env(&cp.env); cp.git_cmd = 1; @@ -3124,7 +3130,10 @@ static int add_submodule(const struct add_data *add_data) if (run_command(&cp)) die(_("unable to checkout submodule '%s'"), add_data->sm_path); } - return 0; + ret = 0; +cleanup: + string_list_clear(&reference, 1); + return ret; } static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value) From 4e83605d38e30ba56878c121443695cb7eef0f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Sep 2022 01:14:19 +0200 Subject: [PATCH 12/17] submodule--helper: fix obscure leak in module_add() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix an obscure leak in module_add(), if the "git add" command we were piping to failed we'd fail to strbuf_release(&sb). This fixes a leak introduced in a6226fd772b (submodule--helper: convert the bulk of cmd_add() to C, 2021-08-10). In fixing it move to a "goto cleanup" pattern, and since we need to introduce a "ret" variable to do that let's also get rid of the intermediate "exit_code" variable. The initialization to "-1" in a6226fd772b has always been redundant, we'd only use the "exit_code" value after assigning the return value of pipe_command() to it. Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 7e2ffd8869..6435508d28 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -3293,6 +3293,8 @@ static int module_add(int argc, const char **argv, const char *prefix) N_("git submodule add [] [--] []"), NULL }; + struct strbuf sb = STRBUF_INIT; + int ret = 1; argc = parse_options(argc, argv, prefix, options, usage, 0); @@ -3342,21 +3344,17 @@ static int module_add(int argc, const char **argv, const char *prefix) die_on_repo_without_commits(add_data.sm_path); if (!force) { - int exit_code = -1; - struct strbuf sb = STRBUF_INIT; struct child_process cp = CHILD_PROCESS_INIT; cp.git_cmd = 1; cp.no_stdout = 1; strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing", "--no-warn-embedded-repo", add_data.sm_path, NULL); - if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) { + if ((ret = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) { strbuf_complete_line(&sb); fputs(sb.buf, stderr); - free(add_data.sm_path); - return exit_code; + goto cleanup; } - strbuf_release(&sb); } if(!add_data.sm_name) @@ -3371,15 +3369,17 @@ static int module_add(int argc, const char **argv, const char *prefix) add_data.progress = !!progress; add_data.dissociate = !!dissociate; - if (add_submodule(&add_data)) { - free(add_data.sm_path); - return 1; - } + if (add_submodule(&add_data)) + goto cleanup; configure_added_submodule(&add_data); + + ret = 0; +cleanup: free(add_data.sm_path); free(to_free); + strbuf_release(&sb); - return 0; + return ret; } #define SUPPORT_SUPER_PREFIX (1<<0) From 623bd7d154daf93e910414bfc8b3080c7269f2a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Sep 2022 01:14:20 +0200 Subject: [PATCH 13/17] submodule--helper: fix a leak in module_add() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a leak in module_path(), since a6226fd772b (submodule--helper: convert the bulk of cmd_add() to C, 2021-08-10), we've been freeing add_data.sm_path, but in this case we clobbered it, and didn't free the value we clobbered. This makes test 28 of "t/t7400-submodule-basic.sh" ("submodule add in subdirectory") pass when we're compiled with SANITIZE=leak.. Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 6435508d28..5c9a59083f 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -3314,8 +3314,12 @@ static int module_add(int argc, const char **argv, const char *prefix) else add_data.sm_path = xstrdup(argv[1]); - if (prefix && *prefix && !is_absolute_path(add_data.sm_path)) - add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path); + if (prefix && *prefix && !is_absolute_path(add_data.sm_path)) { + char *sm_path = add_data.sm_path; + + add_data.sm_path = xstrfmt("%s%s", prefix, sm_path); + free(sm_path); + } if (starts_with_dot_dot_slash(add_data.repo) || starts_with_dot_slash(add_data.repo)) { From 25b6a95d03fe4fbb7acb8bbd0e1faad9a7f4ea9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Sep 2022 01:14:21 +0200 Subject: [PATCH 14/17] submodule--helper: fix a memory leak in print_status() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a leak in print_status(), the compute_rev_name() function implemented in this file will return a strbuf_detach()'d value, or NULL. This leak has existed since this code was added in a9f8a37584a (submodule: port submodule subcommand 'status' from shell to C, 2017-10-06), but in 0b5e2ea7cf3 (submodule--helper: don't print null in 'submodule status', 2018-04-18) we added a "const" intermediate variable for the return value, that "const" should be removed. Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 5c9a59083f..23c4f8d576 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -583,10 +583,11 @@ static void print_status(unsigned int flags, char state, const char *path, printf("%c%s %s", state, oid_to_hex(oid), displaypath); if (state == ' ' || state == '+') { - const char *name = compute_rev_name(path, oid_to_hex(oid)); + char *name = compute_rev_name(path, oid_to_hex(oid)); if (name) printf(" (%s)", name); + free(name); } printf("\n"); From d40c42e06b1bbeab66b6911b864f57407ad68f95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Sep 2022 01:14:22 +0200 Subject: [PATCH 15/17] submodule--helper: free some "displaypath" in "struct update_data" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the update_data_release() function free "displaypath" member when appropriate. The "displaypath" member is always ours, the "const" on the "char *" was wrong to begin with. This leaves a leak of "displaypath" in update_submodule(), which as we'll see in subsequent commits is harder to deal with than this trivial fix. Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 3 ++- t/t2403-worktree-move.sh | 1 + t/t7412-submodule-absorbgitdirs.sh | 1 + t/t7419-submodule-set-branch.sh | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 23c4f8d576..cc32eb0dc3 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1877,7 +1877,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc) struct update_data { const char *prefix; - const char *displaypath; + char *displaypath; enum submodule_update_type update_default; struct object_id suboid; struct string_list references; @@ -1915,6 +1915,7 @@ struct update_data { static void update_data_release(struct update_data *ud) { + free(ud->displaypath); module_list_release(&ud->list); } diff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh index a4e1a178e0..1168e9f998 100755 --- a/t/t2403-worktree-move.sh +++ b/t/t2403-worktree-move.sh @@ -2,6 +2,7 @@ test_description='test git worktree move, remove, lock and unlock' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh index 1cfa150768..2859695c6d 100755 --- a/t/t7412-submodule-absorbgitdirs.sh +++ b/t/t7412-submodule-absorbgitdirs.sh @@ -6,6 +6,7 @@ This test verifies that `git submodue absorbgitdirs` moves a submodules git directory into the superproject. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup a real submodule' ' diff --git a/t/t7419-submodule-set-branch.sh b/t/t7419-submodule-set-branch.sh index 3b925c302f..96e9842321 100755 --- a/t/t7419-submodule-set-branch.sh +++ b/t/t7419-submodule-set-branch.sh @@ -9,6 +9,7 @@ This test verifies that the set-branch subcommand of git-submodule is working as expected. ' +TEST_PASSES_SANITIZE_LEAK=true TEST_NO_CREATE_REPO=1 . ./test-lib.sh From 4c4d3e7c0ae3eda9331227c7cf317afa1d83a0e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Sep 2022 01:14:23 +0200 Subject: [PATCH 16/17] submodule--helper: free rest of "displaypath" in "struct update_data" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a leak in code added in c51f8f94e5b (submodule--helper: run update procedures from C, 2021-08-24), we clobber the "displaypath" member of the passed-in "struct update_data" both so that die() messages in this update_submodule() function itself can use it, and for the run_update_procedure() called within this function. Fix a leak in code added in 51f8f94e5b (submodule--helper: run update procedures from C, 2021-08-24). We'd always clobber the old "displaypath" member of the previously passed-in "struct update_data". A better fix for this would be to remove the "displaypath" member from the "struct update_data" entirely. Along with "oid", "suboid", "just_cloned" and "sm_path" it's managing members that mainly need to be passed between 1-3 stack frames of functions adjacent to this code. But doing so would be a much larger change (I have it locally, and fully untangling that in an incremental way is a 10 patch journey). So let's go for this much more isolated fix suggested by Glen. We FREE_AND_NULL() the "update_data->displaypath", the "AND_NULL()" part of that is needed due to the later "free(ud->displaypath)" in "update_data_release()" introduced in the preceding commit Moving ensure_core_worktree() out of update_submodule() may not be strictly required, but in doing so we are left with the exact same ordering as before, making this a smaller functional change. Helped-by: Glen Choo Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index cc32eb0dc3..f8470d848e 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -2484,13 +2484,6 @@ static int update_submodule(struct update_data *update_data) { int ret; - ret = ensure_core_worktree(update_data->sm_path); - if (ret) - return ret; - - update_data->displaypath = get_submodule_displaypath( - update_data->sm_path, update_data->prefix); - ret = determine_submodule_update_strategy(the_repository, update_data->just_cloned, update_data->sm_path, @@ -2596,7 +2589,15 @@ static int update_submodules(struct update_data *update_data) update_data->just_cloned = ucd.just_cloned; update_data->sm_path = ucd.sub->path; + code = ensure_core_worktree(update_data->sm_path); + if (code) + goto fail; + + update_data->displaypath = get_submodule_displaypath( + update_data->sm_path, update_data->prefix); code = update_submodule(update_data); + FREE_AND_NULL(update_data->displaypath); +fail: if (!code) continue; ret = code; From fe4c750fb13ca1c721e2de2bef80ce581c8b3f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Sep 2022 01:14:24 +0200 Subject: [PATCH 17/17] submodule--helper: fix a configure_added_submodule() leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix config API a memory leak added in a452128a36c (submodule--helper: introduce add-config subcommand, 2021-08-06) by using the *_tmp() variant of git_config_get_string(). In this case we're only checking whether the (repo|git)_config_get_string() call is telling us that the "submodule.active" key exists. As with the preceding commit we'll find many other such patterns in the codebase if we go fishing. E.g. "git gc" leaks in the code added in 61f7a383d3b (maintenance: use 'incremental' strategy by default, 2020-10-15). Similar code in "git gc" added in b08ff1fee00 (maintenance: add --schedule option and config, 2020-09-11) doesn't leak, but we could avoid the malloc() & free() in that case. A coccinelle rule to find those would find and fix some leaks, and cases where we're doing needless malloc() + free()'s but only care about the key existence, or are copying the (repo|git)_config_get_string() return value right away. But as with the preceding commit let's punt on all of that for now, and just narrowly fix this specific case in submodule--helper. Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Glen Choo Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 4 ++-- t/t7413-submodule-is-active.sh | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index f8470d848e..1de376d826 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -3157,7 +3157,7 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con static void configure_added_submodule(struct add_data *add_data) { char *key; - char *val = NULL; + const char *val; struct child_process add_submod = CHILD_PROCESS_INIT; struct child_process add_gitmodules = CHILD_PROCESS_INIT; @@ -3202,7 +3202,7 @@ static void configure_added_submodule(struct add_data *add_data) * is_submodule_active(), since that function needs to find * out the value of "submodule.active" again anyway. */ - if (!git_config_get_string("submodule.active", &val)) { + if (!git_config_get_string_tmp("submodule.active", &val)) { /* * If the submodule being added isn't already covered by the * current configured pathspec, set the submodule's active flag diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh index ede6f02dbd..4dc7d08942 100755 --- a/t/t7413-submodule-is-active.sh +++ b/t/t7413-submodule-is-active.sh @@ -9,6 +9,7 @@ This is a unit test of the submodule.c is_submodule_active() function, which is also indirectly tested elsewhere. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' '