{"thread":{"id":"58151","subject":"[PATCH 00/11] submodule--helper: fix memory leaks","startedAt":"2022-07-13T13:16:29Z","lastAt":"2022-09-01T21:23:50Z","messageCount":186,"participants":["Ævar Arnfjörð Bjarmason","Glen Choo","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":11},"messages":[{"id":"458961","messageId":"cover-00.11-00000000000-20220713T131601Z-avarab@gmail.com","threadId":"58151","inReplyTo":null,"subject":"[PATCH 00/11] submodule--helper: fix memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-13T13:16:05Z","receivedAt":"2022-07-13T13:16:29Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This series fixes most of the memory leaks in\nbuiltin/submodule--helper.c, it needs to go on top of\ngc/submodule-use-super-prefix (which in turn goes on top of my\nab/submodule-cleanup.\n\nI'm planning to submit the patches to \"git rm git-submodule.sh\" after\nthis series, but fixing these leaks gives us a much better foundation\nfor doing so.\n\nI.e. we'll eventually want to have this code invoke itself without\nneeding run_command(), and being able to have clear lifecycle\nmanagement of the data it uses makes it possible to eventually call\nthe relevant code as a utility function instead.\n\nÆvar Arnfjörð Bjarmason (11):\n  submodule.c: free() memory from xgetcwd()\n  submodule--helper: replace memset() with { 0 }-initialization\n  submodule--helper: fix \"module_clone_data\" memory leaks\n  submodule--helper: fix \"struct pathspec\" memory leaks\n  submodule--helper: free() leaking {run,capture}_command() argument\n  submodule--helper: add and use *_release() functions\n  submodule--helper: fix \"errmsg_str\" memory leak\n  submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks\n  submodule--helper: free \"char *\" in \"struct update_data\"\n  submodule--helper: fix a leak with repo_clear()\n  submodule--helper:fix \"reference\" leak is \"module_clone_data\"\n\nÆvar Arnfjörð Bjarmason (11):\n  submodule.c: free() memory from xgetcwd()\n  submodule--helper: replace memset() with { 0 }-initialization\n  submodule--helper: fix \"module_clone_data\" memory leaks\n  submodule--helper: fix \"struct pathspec\" memory leaks\n  submodule--helper: free() leaking {run,capture}_command() argument\n  submodule--helper: add and use *_release() functions\n  submodule--helper: fix \"errmsg_str\" memory leak\n  submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks\n  submodule--helper: free \"char *\" in \"struct update_data\"\n  submodule--helper: fix a leak with repo_clear()\n  submodule--helper: fix \"reference\" leak is \"module_clone_data\"\n\n builtin/submodule--helper.c        | 260 ++++++++++++++++++++++-------\n submodule.c                        |   3 +-\n t/t2403-worktree-move.sh           |   1 +\n t/t6008-rev-list-submodule.sh      |   1 +\n t/t6134-pathspec-in-submodule.sh   |   1 +\n t/t7412-submodule-absorbgitdirs.sh |   1 +\n t/t7414-submodule-mistakes.sh      |   2 +\n t/t7506-status-submodule.sh        |   1 +\n t/t7507-commit-verbose.sh          |   2 +\n 9 files changed, 207 insertions(+), 65 deletions(-)\n\n-- \n2.37.0.932.g7b7031e73bc\n\n"},{"id":"458962","messageId":"patch-02.11-4049362e9b4-20220713T131601Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-00.11-00000000000-20220713T131601Z-avarab@gmail.com","subject":"[PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-13T13:16:07Z","receivedAt":"2022-07-13T13:16:32Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use the less verbose { 0 }-initialization syntax rather than memset()\nin builtin/submodule--helper.c, this doesn't make a difference in\nterms of behavior, but as we're about to modify adjacent code makes\nthis more consistent, and lets us avoid worrying about when the\nmemset() happens v.s. a \"goto cleanup\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 ++----\n 1 file changed, 2 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex fac52ade5e1..73717be957c 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1744,7 +1744,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n {\n \tint dissociate = 0, quiet = 0, progress = 0, require_init = 0;\n \tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n-\tstruct list_objects_filter_options filter_options;\n+\tstruct list_objects_filter_options filter_options = { 0 };\n \n \tstruct option module_clone_options[] = {\n \t\tOPT_STRING(0, \"prefix\", &clone_data.prefix,\n@@ -1786,7 +1786,6 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \t\tNULL\n \t};\n \n-\tmemset(&filter_options, 0, sizeof(filter_options));\n \targc = parse_options(argc, argv, prefix, module_clone_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n@@ -2563,7 +2562,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n {\n \tstruct pathspec pathspec;\n \tstruct update_data opt = UPDATE_DATA_INIT;\n-\tstruct list_objects_filter_options filter_options;\n+\tstruct list_objects_filter_options filter_options = { 0 };\n \tint ret;\n \n \tstruct option module_update_options[] = {\n@@ -2623,7 +2622,6 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \tupdate_clone_config_from_gitmodules(&opt.max_jobs);\n \tgit_config(git_update_clone_config, &opt.max_jobs);\n \n-\tmemset(&filter_options, 0, sizeof(filter_options));\n \targc = parse_options(argc, argv, prefix, module_update_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-- \n2.37.0.932.g7b7031e73bc\n\n"},{"id":"458963","messageId":"patch-01.11-f1c847ad613-20220713T131601Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-00.11-00000000000-20220713T131601Z-avarab@gmail.com","subject":"[PATCH 01/11] submodule.c: free() memory from xgetcwd()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-13T13:16:06Z","receivedAt":"2022-07-13T13:16:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak in code added in bf0231c6614 (rev-parse: add\n--show-superproject-working-tree, 2017-03-08), we should never have\nmade the result of xgetcwd() a \"const char *\", as we return a\nstrbuf_detach()'d value. Let's fix that and free() it when we're done\nwith it.\n\nWe can't mark any tests passing passing with SANITIZE=leak using\n\"TEST_PASSES_SANITIZE_LEAK=true\" as a result of this change, but\ne.g. \"t/t1500-rev-parse.sh\" now gets closer to passing.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n submodule.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/submodule.c b/submodule.c\nindex 2af16c647d5..3fa5db3ecdf 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -2388,7 +2388,7 @@ int get_superproject_working_tree(struct strbuf *buf)\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf one_up = STRBUF_INIT;\n-\tconst char *cwd = xgetcwd();\n+\tchar *cwd = xgetcwd();\n \tint ret = 0;\n \tconst char *subpath;\n \tint code;\n@@ -2451,6 +2451,7 @@ int get_superproject_working_tree(struct strbuf *buf)\n \t\tret = 1;\n \t\tfree(super_wt);\n \t}\n+\tfree(cwd);\n \tstrbuf_release(&sb);\n \n \tcode = finish_command(&cp);\n-- \n2.37.0.932.g7b7031e73bc\n\n"},{"id":"458964","messageId":"patch-03.11-e5ec6945409-20220713T131601Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-00.11-00000000000-20220713T131601Z-avarab@gmail.com","subject":"[PATCH 03/11] submodule--helper: fix \"module_clone_data\" memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-13T13:16:08Z","receivedAt":"2022-07-13T13:16:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix memory leaks related to the \"struct module_clone_data\" by creating\na module_clone_data_release() function to go with the\nMODULE_CLONE_DATA_INIT added in a98b02c1128 (submodule--helper:\nrefactor module_clone(), 2021-07-10).\n\nThe \"path\" member can come from \"argv\" (i.e. not malloc'd), or it can\nbe something we determine at runtime. In the latter case let's save\naway a pointer to free() to avoid leaking memory.\n\nFixing this leak makes several tests pass, so let's mark them as\npassing with TEST_PASSES_SANITIZE_LEAK=true.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c   | 24 +++++++++++++++++++-----\n t/t6008-rev-list-submodule.sh |  1 +\n t/t7414-submodule-mistakes.sh |  2 ++\n t/t7506-status-submodule.sh   |  1 +\n t/t7507-commit-verbose.sh     |  2 ++\n 5 files changed, 25 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 73717be957c..23ab9c7e349 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1511,6 +1511,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n struct module_clone_data {\n \tconst char *prefix;\n \tconst char *path;\n+\tchar *path_free;\n \tconst char *name;\n \tconst char *url;\n \tconst char *depth;\n@@ -1527,6 +1528,11 @@ struct module_clone_data {\n \t.single_branch = -1, \\\n }\n \n+static void module_clone_data_release(struct module_clone_data *cd)\n+{\n+\tfree(cd->path_free);\n+}\n+\n struct submodule_alternate_setup {\n \tconst char *submodule_name;\n \tenum SUBMODULE_ALTERNATE_ERROR_MODE {\n@@ -1651,9 +1657,9 @@ static int clone_submodule(struct module_clone_data *clone_data)\n \n \tif (!is_absolute_path(clone_data->path)) {\n \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n-\t\tclone_data->path = strbuf_detach(&sb, NULL);\n+\t\tclone_data->path = clone_data->path_free = strbuf_detach(&sb, NULL);\n \t} else {\n-\t\tclone_data->path = xstrdup(clone_data->path);\n+\t\tclone_data->path = clone_data->path_free = xstrdup(clone_data->path);\n \t}\n \n \tif (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)\n@@ -1801,6 +1807,8 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \n \tclone_submodule(&clone_data);\n \tlist_objects_filter_release(&filter_options);\n+\n+\tmodule_clone_data_release(&clone_data);\n \treturn 0;\n }\n \n@@ -3016,6 +3024,7 @@ static int add_submodule(const struct add_data *add_data)\n {\n \tchar *submod_gitdir_path;\n \tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n+\tint ret;\n \n \t/* perhaps the path already exists and is already a git repo, else clone it */\n \tif (is_directory(add_data->sm_path)) {\n@@ -3077,8 +3086,10 @@ static int add_submodule(const struct add_data *add_data)\n \t\tif (add_data->depth >= 0)\n \t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n \n-\t\tif (clone_submodule(&clone_data))\n-\t\t\treturn -1;\n+\t\tif (clone_submodule(&clone_data)) {\n+\t\t\tret = -1;\n+\t\t\tgoto cleanup;\n+\t\t}\n \n \t\tprepare_submodule_repo_env(&cp.env);\n \t\tcp.git_cmd = 1;\n@@ -3097,7 +3108,10 @@ static int add_submodule(const struct add_data *add_data)\n \t\tif (run_command(&cp))\n \t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n \t}\n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tmodule_clone_data_release(&clone_data);\n+\treturn ret;\n }\n \n static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)\ndiff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh\nindex 3153a0d8910..12e67e187ef 100755\n--- a/t/t6008-rev-list-submodule.sh\n+++ b/t/t6008-rev-list-submodule.sh\n@@ -8,6 +8,7 @@ test_description='git rev-list involving submodules that this repo has'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh\nindex f2e7df59cf2..3269298197c 100755\n--- a/t/t7414-submodule-mistakes.sh\n+++ b/t/t7414-submodule-mistakes.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='handling of common mistakes people may make with submodules'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'create embedded repository' '\ndiff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh\nindex 3fcb44767f5..f5426a8e589 100755\n--- a/t/t7506-status-submodule.sh\n+++ b/t/t7506-status-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='git status for submodule'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_create_repo_with_commit () {\ndiff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh\nindex ed2653d46fe..92462a22374 100755\n--- a/t/t7507-commit-verbose.sh\n+++ b/t/t7507-commit-verbose.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='verbose commit template'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n write_script \"check-for-diff\" <<\\EOF &&\n-- \n2.37.0.932.g7b7031e73bc\n\n"},{"id":"458965","messageId":"patch-04.11-cdefd283c23-20220713T131601Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-00.11-00000000000-20220713T131601Z-avarab@gmail.com","subject":"[PATCH 04/11] submodule--helper: fix \"struct pathspec\" memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-13T13:16:09Z","receivedAt":"2022-07-13T13:16:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call clear_pathspec() at the end of various functions that work with\nand allocate a \"struct pathspec\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 115 +++++++++++++++++++++++++-----------\n 1 file changed, 81 insertions(+), 34 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 23ab9c7e349..a8e439e59b8 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -269,7 +269,7 @@ static char *get_up_path(const char *path)\n static int module_list(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \n \tstruct option module_list_options[] = {\n@@ -278,6 +278,7 @@ static int module_list(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"alternative anchor for relative paths\")),\n \t\tOPT_END()\n \t};\n+\tint ret;\n \n \tconst char *const git_submodule_helper_usage[] = {\n \t\tN_(\"git submodule--helper list [--prefix=<path>] [<path>...]\"),\n@@ -287,8 +288,10 @@ static int module_list(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, module_list_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n+\t\tret = 1;\n+\t\tgoto cleanup;\n+\t}\n \n \tfor (i = 0; i < list.nr; i++) {\n \t\tconst struct cache_entry *ce = list.entries[i];\n@@ -302,7 +305,10 @@ static int module_list(int argc, const char **argv, const char *prefix)\n \n \t\tfprintf(stdout, \"%s\\n\", ce->name);\n \t}\n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static void for_each_listed_submodule(const struct module_list *list,\n@@ -427,7 +433,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,\n static int module_foreach(int argc, const char **argv, const char *prefix)\n {\n \tstruct foreach_cb info = FOREACH_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \n \tstruct option module_foreach_options[] = {\n@@ -441,12 +447,15 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule foreach [--quiet] [--recursive] [--] <command>\"),\n \t\tNULL\n \t};\n+\tint ret;\n \n \targc = parse_options(argc, argv, prefix, module_foreach_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-\tif (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\tif (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0) {\n+\t\tret = 1;\n+\t\tgoto cleanup;\n+\t}\n \n \tinfo.argc = argc;\n \tinfo.argv = argv;\n@@ -454,7 +463,10 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static int starts_with_dot_slash(const char *const path)\n@@ -562,7 +574,7 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data\n static int module_init(int argc, const char **argv, const char *prefix)\n {\n \tstruct init_cb info = INIT_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \n@@ -575,12 +587,15 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule init [<options>] [<path>]\"),\n \t\tNULL\n \t};\n+\tint ret;\n \n \targc = parse_options(argc, argv, prefix, module_init_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n+\t\tret = 1;\n+\t\tgoto cleanup;\n+\t}\n \n \t/*\n \t * If there are no path args and submodule.active is set then,\n@@ -595,7 +610,10 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, init_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct status_cb {\n@@ -740,7 +758,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,\n static int module_status(int argc, const char **argv, const char *prefix)\n {\n \tstruct status_cb info = STATUS_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \n@@ -755,12 +773,15 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule status [--quiet] [--cached] [--recursive] [<path>...]\"),\n \t\tNULL\n \t};\n+\tint ret;\n \n \targc = parse_options(argc, argv, prefix, module_status_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n+\t\tret = 1;\n+\t\tgoto cleanup;\n+\t}\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -768,7 +789,10 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, status_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static int module_name(int argc, const char **argv, const char *prefix)\n@@ -1105,7 +1129,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \tstruct strvec diff_args = STRVEC_INIT;\n \tstruct rev_info rev;\n \tstruct module_cb_list list = MODULE_CB_LIST_INIT;\n-\tint ret = 0;\n+\tint ret;\n \n \tstrvec_push(&diff_args, get_diff_cmd(diff_cmd));\n \tif (info->cached)\n@@ -1145,6 +1169,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \telse\n \t\trun_diff_files(&rev, 0);\n \tprepare_submodule_summary(info, &list);\n+\tret = 0;\n cleanup:\n \tstrvec_clear(&diff_args);\n \trelease_revisions(&rev);\n@@ -1326,10 +1351,11 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data\n static int module_sync(int argc, const char **argv, const char *prefix)\n {\n \tstruct sync_cb info = SYNC_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tint recursive = 0;\n+\tint ret;\n \n \tstruct option module_sync_options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"suppress output of synchronizing submodule url\")),\n@@ -1346,8 +1372,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, module_sync_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n+\t\tret = 1;\n+\t\tgoto cleanup;\n+\t}\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -1357,7 +1385,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, sync_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct deinit_cb {\n@@ -1464,7 +1495,7 @@ static void deinit_submodule_cb(const struct cache_entry *list_item,\n static int module_deinit(int argc, const char **argv, const char *prefix)\n {\n \tstruct deinit_cb info = DEINIT_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tint force = 0;\n@@ -1481,6 +1512,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]\"),\n \t\tNULL\n \t};\n+\tint ret;\n \n \targc = parse_options(argc, argv, prefix, module_deinit_options,\n \t\t\t     git_submodule_helper_usage, 0);\n@@ -1494,8 +1526,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \tif (!argc && !all)\n \t\tdie(_(\"Use '--all' if you really want to deinitialize all submodules\"));\n \n-\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n+\t\tret = 1;\n+\t\tgoto cleanup;\n+\t}\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -1505,7 +1539,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, deinit_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct module_clone_data {\n@@ -2568,7 +2605,7 @@ static int update_submodules(struct update_data *update_data)\n \n static int module_update(int argc, const char **argv, const char *prefix)\n {\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct update_data opt = UPDATE_DATA_INIT;\n \tstruct list_objects_filter_options filter_options = { 0 };\n \tint ret;\n@@ -2647,8 +2684,8 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\topt.update_strategy.type = opt.update_default;\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {\n-\t\tlist_objects_filter_release(&filter_options);\n-\t\treturn 1;\n+\t\tret = 1;\n+\t\tgoto cleanup;\n \t}\n \n \tif (pathspec.nr)\n@@ -2659,8 +2696,10 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\tstruct init_cb info = INIT_CB_INIT;\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n-\t\t\t\t\t&pathspec, &list) < 0)\n-\t\t\treturn 1;\n+\t\t\t\t\t&pathspec, &list) < 0) {\n+\t\t\tret = 1;\n+\t\t\tgoto cleanup;\n+\t\t}\n \n \t\t/*\n \t\t * If there are no path args and submodule.active is set then,\n@@ -2677,7 +2716,9 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t}\n \n \tret = update_submodules(&opt);\n+cleanup:\n \tlist_objects_filter_release(&filter_options);\n+\tclear_pathspec(&pathspec);\n \treturn ret;\n }\n \n@@ -2761,9 +2802,10 @@ static int push_check(int argc, const char **argv, const char *prefix)\n static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tunsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;\n+\tint ret;\n \n \tstruct option embed_gitdir_options[] = {\n \t\tOPT_STRING(0, \"prefix\", &prefix,\n@@ -2782,13 +2824,18 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, embed_gitdir_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n+\t\tret = 1;\n+\t\tgoto cleanup;\n+\t}\n \n \tfor (i = 0; i < list.nr; i++)\n \t\tabsorb_git_dir_into_superproject(list.entries[i]->name, flags);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static int is_active(int argc, const char **argv, const char *prefix)\n-- \n2.37.0.932.g7b7031e73bc\n\n"},{"id":"458966","messageId":"patch-05.11-76eab92c8b6-20220713T131601Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-00.11-00000000000-20220713T131601Z-avarab@gmail.com","subject":"[PATCH 05/11] submodule--helper: free() leaking {run,capture}_command() argument","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-13T13:16:10Z","receivedAt":"2022-07-13T13:16:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Free the \"dir\" member of \"struct child_process\" that various functions\nin submodule-helper.c allocate allocates with xstrdup().\n\nSince the \"dir\" argument is \"const char *\" let's keep a\n\"char *to_free\" variable around for this rather than casting when we\ncall free().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 41 +++++++++++++++++++++++++++----------\n 1 file changed, 30 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex a8e439e59b8..2099c5774b2 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2198,27 +2198,36 @@ static int is_tip_reachable(const char *path, struct object_id *oid)\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tstruct strbuf rev = STRBUF_INIT;\n \tchar *hex = oid_to_hex(oid);\n+\tchar *to_free;\n+\tint ret;\n \n \tcp.git_cmd = 1;\n-\tcp.dir = xstrdup(path);\n+\tcp.dir = to_free = xstrdup(path);\n \tcp.no_stderr = 1;\n \tstrvec_pushl(&cp.args, \"rev-list\", \"-n\", \"1\", hex, \"--not\", \"--all\", NULL);\n \n \tprepare_submodule_repo_env(&cp.env);\n \n-\tif (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len)\n-\t\treturn 0;\n+\tif (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len) {\n+\t\tret = 0;\n+\t\tgoto cleanup;\n+\t}\n \n-\treturn 1;\n+\tret = 1;\n+cleanup:\n+\tfree(to_free);\n+\treturn ret;\n }\n \n static int fetch_in_submodule(const char *module_path, int depth, int quiet, struct object_id *oid)\n {\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tchar *to_free;\n+\tint ret;\n \n \tprepare_submodule_repo_env(&cp.env);\n \tcp.git_cmd = 1;\n-\tcp.dir = xstrdup(module_path);\n+\tcp.dir = to_free = xstrdup(module_path);\n \n \tstrvec_push(&cp.args, \"fetch\");\n \tif (quiet)\n@@ -2232,7 +2241,9 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str\n \t\tfree(remote);\n \t}\n \n-\treturn run_command(&cp);\n+\tret = run_command(&cp);\n+\tfree(to_free);\n+\treturn ret;\n }\n \n static int run_update_command(struct update_data *ud, int subforce)\n@@ -2240,6 +2251,8 @@ static int run_update_command(struct update_data *ud, int subforce)\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tchar *oid = oid_to_hex(&ud->oid);\n \tint must_die_on_failure = 0;\n+\tchar *to_free;\n+\tint ret;\n \n \tswitch (ud->update_strategy.type) {\n \tcase SM_UPDATE_CHECKOUT:\n@@ -2273,7 +2286,7 @@ static int run_update_command(struct update_data *ud, int subforce)\n \t}\n \tstrvec_push(&cp.args, oid);\n \n-\tcp.dir = xstrdup(ud->sm_path);\n+\tcp.dir = to_free = xstrdup(ud->sm_path);\n \tprepare_submodule_repo_env(&cp.env);\n \tif (run_command(&cp)) {\n \t\tswitch (ud->update_strategy.type) {\n@@ -2301,11 +2314,14 @@ static int run_update_command(struct update_data *ud, int subforce)\n \t\t\texit(128);\n \n \t\t/* the command failed, but update must continue */\n-\t\treturn 1;\n+\t\tret = 1;\n+\t\tgoto cleanup;\n \t}\n \n-\tif (ud->quiet)\n-\t\treturn 0;\n+\tif (ud->quiet) {\n+\t\tret = 0;\n+\t\tgoto cleanup;\n+\t}\n \n \tswitch (ud->update_strategy.type) {\n \tcase SM_UPDATE_CHECKOUT:\n@@ -2329,7 +2345,10 @@ static int run_update_command(struct update_data *ud, int subforce)\n \t\t    submodule_strategy_to_string(&ud->update_strategy));\n \t}\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tfree(to_free);\n+\treturn ret;\n }\n \n static int run_update_procedure(struct update_data *ud)\n-- \n2.37.0.932.g7b7031e73bc\n\n"},{"id":"458967","messageId":"patch-07.11-f8c20bbf266-20220713T131601Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-00.11-00000000000-20220713T131601Z-avarab@gmail.com","subject":"[PATCH 07/11] submodule--helper: fix \"errmsg_str\" memory leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-13T13:16:12Z","receivedAt":"2022-07-13T13:16:44Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak introduced in e83e3333b57 (submodule: port submodule\nsubcommand 'summary' from shell to C, 2020-08-13), to do that stop\njuggling around the \"errmsg\" and \"struct strbuf\", let's instead move\nthe \"struct strbuf errmsg\" to the top-level.\n\nNow we don't need to strbuf_detach() it anymore, but we do need to\nensure that we pass NULL to print_submodule_summary() when we have no\nerror message.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 7 ++++---\n 1 file changed, 4 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex a964dbeec38..a05578a7382 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -932,7 +932,8 @@ static void generate_submodule_summary(struct summary_cb *info,\n {\n \tchar *displaypath, *src_abbrev = NULL, *dst_abbrev;\n \tint missing_src = 0, missing_dst = 0;\n-\tchar *errmsg = NULL;\n+\tchar *errmsg;\n+\tstruct strbuf errmsg_str = STRBUF_INIT;\n \tint total_commits = -1;\n \n \tif (!info->cached && oideq(&p->oid_dst, null_oid())) {\n@@ -1032,7 +1033,6 @@ static void generate_submodule_summary(struct summary_cb *info,\n \t\t * submodule, i.e., deleted or changed to blob\n \t\t */\n \t\tif (S_ISGITLINK(p->mod_dst)) {\n-\t\t\tstruct strbuf errmsg_str = STRBUF_INIT;\n \t\t\tif (missing_src && missing_dst) {\n \t\t\t\tstrbuf_addf(&errmsg_str, \"  Warn: %s doesn't contain commits %s and %s\\n\",\n \t\t\t\t\t    displaypath, oid_to_hex(&p->oid_src),\n@@ -1043,10 +1043,10 @@ static void generate_submodule_summary(struct summary_cb *info,\n \t\t\t\t\t    oid_to_hex(&p->oid_src) :\n \t\t\t\t\t    oid_to_hex(&p->oid_dst));\n \t\t\t}\n-\t\t\terrmsg = strbuf_detach(&errmsg_str, NULL);\n \t\t}\n \t}\n \n+\terrmsg = errmsg_str.len ? errmsg_str.buf : NULL;\n \tprint_submodule_summary(info, errmsg, total_commits,\n \t\t\t\tdisplaypath, src_abbrev,\n \t\t\t\tdst_abbrev, p);\n@@ -1054,6 +1054,7 @@ static void generate_submodule_summary(struct summary_cb *info,\n \tfree(displaypath);\n \tfree(src_abbrev);\n \tfree(dst_abbrev);\n+\tstrbuf_release(&errmsg_str);\n }\n \n static void prepare_submodule_summary(struct summary_cb *info,\n-- \n2.37.0.932.g7b7031e73bc\n\n"},{"id":"458968","messageId":"patch-08.11-c8bb7bb7bb3-20220713T131601Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-00.11-00000000000-20220713T131601Z-avarab@gmail.com","subject":"[PATCH 08/11] submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-13T13:16:13Z","receivedAt":"2022-07-13T13:16:46Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix leaks in \"struct module_cb_list\" and the \"struct module_cb\" which\nit contains, these fixes leaks in e83e3333b57 (submodule: port\nsubmodule subcommand 'summary' from shell to C, 2020-08-13).\n\nThe \"sm_path\" should always have been a \"char *\", not a \"const\nchar *\", we always create it with xstrdup().\n\nWe can't mark any tests passing passing with SANITIZE=leak using\n\"TEST_PASSES_SANITIZE_LEAK=true\" as a result of this change, but\n\"t7401-submodule-summary.sh\" gets closer to passing as a result of\nthis change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 20 +++++++++++++++++++-\n 1 file changed, 19 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex a05578a7382..2b44f391f15 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -828,9 +828,13 @@ struct module_cb {\n \tstruct object_id oid_src;\n \tstruct object_id oid_dst;\n \tchar status;\n-\tconst char *sm_path;\n+\tchar *sm_path;\n };\n #define MODULE_CB_INIT { 0 }\n+static void module_cb_release(struct module_cb *mcb)\n+{\n+\tfree(mcb->sm_path);\n+}\n \n struct module_cb_list {\n \tstruct module_cb **entries;\n@@ -838,6 +842,19 @@ struct module_cb_list {\n };\n #define MODULE_CB_LIST_INIT { 0 }\n \n+static void module_cb_list_release(struct module_cb_list *mcbl)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < mcbl->nr; i++) {\n+\t\tstruct module_cb *mcb = mcbl->entries[i];\n+\n+\t\tmodule_cb_release(mcb);\n+\t\tfree(mcb);\n+\t}\n+\tfree(mcbl->entries);\n+}\n+\n struct summary_cb {\n \tint argc;\n \tconst char **argv;\n@@ -1183,6 +1200,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n cleanup:\n \tstrvec_clear(&diff_args);\n \trelease_revisions(&rev);\n+\tmodule_cb_list_release(&list);\n \treturn ret;\n }\n \n-- \n2.37.0.932.g7b7031e73bc\n\n"},{"id":"458969","messageId":"patch-06.11-b9984e982db-20220713T131601Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-00.11-00000000000-20220713T131601Z-avarab@gmail.com","subject":"[PATCH 06/11] submodule--helper: add and use *_release() functions","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-13T13:16:11Z","receivedAt":"2022-07-13T13:16:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add release functions for \"struct module_list\", \"struct\nsubmodule_update_clone\" and \"struct update_data\". For now we're\nleaving some of the \"struct update_data\" members, we'll deal with\nthose in a subsequent commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c      | 30 +++++++++++++++++++++++++++++-\n t/t6134-pathspec-in-submodule.sh |  1 +\n 2 files changed, 30 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 2099c5774b2..a964dbeec38 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -182,6 +182,11 @@ struct module_list {\n };\n #define MODULE_LIST_INIT { 0 }\n \n+static void module_list_release(struct module_list *ml)\n+{\n+\tfree(ml->entries);\n+}\n+\n static int module_list_compute(int argc, const char **argv,\n \t\t\t       const char *prefix,\n \t\t\t       struct pathspec *pathspec,\n@@ -243,7 +248,7 @@ static void module_list_active(struct module_list *list)\n \t\tactive_modules.entries[active_modules.nr++] = ce;\n \t}\n \n-\tfree(list->entries);\n+\tmodule_list_release(list);\n \t*list = active_modules;\n }\n \n@@ -307,6 +312,7 @@ static int module_list(int argc, const char **argv, const char *prefix)\n \t}\n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -465,6 +471,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -612,6 +619,7 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -791,6 +799,7 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1387,6 +1396,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1541,6 +1551,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1910,6 +1921,13 @@ struct submodule_update_clone {\n };\n #define SUBMODULE_UPDATE_CLONE_INIT { 0 }\n \n+static void submodule_update_clone_release(struct submodule_update_clone *suc)\n+{\n+\t/* Not \"update_data\", owned by update_data_release() */\n+\tfree(suc->update_clone);\n+\tfree(suc->failed_clones);\n+}\n+\n struct update_data {\n \tconst char *prefix;\n \tconst char *displaypath;\n@@ -1948,6 +1966,11 @@ struct update_data {\n \t.max_jobs = 1, \\\n }\n \n+static void update_data_release(struct update_data *ud)\n+{\n+\tmodule_list_release(&ud->list);\n+}\n+\n static void next_submodule_warn_missing(struct submodule_update_clone *suc,\n \t\tstruct strbuf *out, const char *displaypath)\n {\n@@ -2618,6 +2641,7 @@ static int update_submodules(struct update_data *update_data)\n \t}\n \n cleanup:\n+\tsubmodule_update_clone_release(&suc);\n \tstring_list_clear(&update_data->references, 0);\n \treturn res;\n }\n@@ -2716,6 +2740,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n \t\t\t\t\t&pathspec, &list) < 0) {\n+\t\t\tmodule_list_release(&list);\n \t\t\tret = 1;\n \t\t\tgoto cleanup;\n \t\t}\n@@ -2732,10 +2757,12 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\t\tinfo.flags |= OPT_QUIET;\n \n \t\tfor_each_listed_submodule(&list, init_submodule_cb, &info);\n+\t\tmodule_list_release(&list);\n \t}\n \n \tret = update_submodules(&opt);\n cleanup:\n+\tupdate_data_release(&opt);\n \tlist_objects_filter_release(&filter_options);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n@@ -2854,6 +2881,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n \tret = 0;\n cleanup:\n \tclear_pathspec(&pathspec);\n+\tmodule_list_release(&list);\n \treturn ret;\n }\n \ndiff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh\nindex 0f1cb49cedc..3a241f259de 100755\n--- a/t/t6134-pathspec-in-submodule.sh\n+++ b/t/t6134-pathspec-in-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='test case exclude pathspec'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a submodule' '\n-- \n2.37.0.932.g7b7031e73bc\n\n"},{"id":"458970","messageId":"patch-10.11-e6c633256f3-20220713T131601Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-00.11-00000000000-20220713T131601Z-avarab@gmail.com","subject":"[PATCH 10/11] submodule--helper: fix a leak with repo_clear()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-13T13:16:15Z","receivedAt":"2022-07-13T13:16:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call repo_clear() in ensure_core_worktree() to free the \"struct\nrepository\". Fixes a leak that's been here since\n74d4731da1f (submodule--helper: replace connect-gitdir-workingtree by\nensure-core-worktree, 2018-08-13).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c        | 1 +\n t/t2403-worktree-move.sh           | 1 +\n t/t7412-submodule-absorbgitdirs.sh | 1 +\n 3 files changed, 3 insertions(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 0bac39880d2..d9fab5d77da 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2486,6 +2486,7 @@ static void ensure_core_worktree(const char *path)\n \t\tfree(abs_path);\n \t\tstrbuf_release(&sb);\n \t}\n+\trepo_clear(&subrepo);\n }\n \n static const char *submodule_update_type_to_label(enum submodule_update_type type)\ndiff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh\nindex a4e1a178e0a..1168e9f9982 100755\n--- a/t/t2403-worktree-move.sh\n+++ b/t/t2403-worktree-move.sh\n@@ -2,6 +2,7 @@\n \n test_description='test git worktree move, remove, lock and unlock'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh\nindex 1cfa150768d..2859695c6d2 100755\n--- a/t/t7412-submodule-absorbgitdirs.sh\n+++ b/t/t7412-submodule-absorbgitdirs.sh\n@@ -6,6 +6,7 @@ This test verifies that `git submodue absorbgitdirs` moves a submodules git\n directory into the superproject.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a real submodule' '\n-- \n2.37.0.932.g7b7031e73bc\n\n"},{"id":"458971","messageId":"patch-09.11-7b36f71879e-20220713T131601Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-00.11-00000000000-20220713T131601Z-avarab@gmail.com","subject":"[PATCH 09/11] submodule--helper: free \"char *\" in \"struct update_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-13T13:16:14Z","receivedAt":"2022-07-13T13:16:54Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Make the update_data_release() function free the \"recursive_prefix\"\nand \"displaypath\" members when appropriate. For the former it could\ncome from either \"argv\" or from our own allocation, so we need to keep\ntrack of a \"to_free\" sibling seperately.\n\nFor \"displaypath\" it's always ours, so the \"const char *\" type was\nwrong to begin with, it should be a \"char *\" instead.\n\nFor update_submodule() we'll free() these as we go along, it's called\nin a loop by update_submodules(), and we'll need to free the \"last\"\none.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 2b44f391f15..0bac39880d2 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1949,7 +1949,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)\n \n struct update_data {\n \tconst char *prefix;\n-\tconst char *displaypath;\n+\tchar *displaypath;\n \tenum submodule_update_type update_default;\n \tstruct object_id suboid;\n \tstruct string_list references;\n@@ -1987,6 +1987,7 @@ struct update_data {\n \n static void update_data_release(struct update_data *ud)\n {\n+\tfree(ud->displaypath);\n \tmodule_list_release(&ud->list);\n }\n \n-- \n2.37.0.932.g7b7031e73bc\n\n"},{"id":"458972","messageId":"patch-11.11-475b1196a93-20220713T131601Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-00.11-00000000000-20220713T131601Z-avarab@gmail.com","subject":"[PATCH 11/11] submodule--helper: fix \"reference\" leak is \"module_clone_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-13T13:16:16Z","receivedAt":"2022-07-13T13:17:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix leaks in the \"reference\" member of \"struct module_clone_data\" that\nhave been with us since 31224cbdc72 (clone: recursive and reference\noption triggers submodule alternates, 2016-08-17) and\n8c8195e9c3e (submodule--helper: introduce add-clone subcommand,\n2021-07-10).\n\nThose commits added an xstrdup()'d member of the\nSTRING_LIST_INIT_NODUP'd \"struct string_list\". We need to free()\nthose, but not the ones we get from argv, let's make use of the \"util\"\nmember, if it has a pointer it's the pointer we'll need to free.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 13 +++++++++----\n 1 file changed, 9 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d9fab5d77da..966f543fbd9 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1598,6 +1598,7 @@ struct module_clone_data {\n static void module_clone_data_release(struct module_clone_data *cd)\n {\n \tfree(cd->path_free);\n+\tstring_list_clear(&cd->reference, 1);\n }\n \n struct submodule_alternate_setup {\n@@ -1652,7 +1653,9 @@ static int add_possible_reference_from_superproject(\n \n \t\tsm_alternate = compute_alternate_path(sb.buf, &err);\n \t\tif (sm_alternate) {\n-\t\t\tstring_list_append(sas->reference, xstrdup(sb.buf));\n+\t\t\tchar *p = strbuf_detach(&sb, NULL);\n+\n+\t\t\tstring_list_append(sas->reference, p)->util = p;\n \t\t\tfree(sm_alternate);\n \t\t} else {\n \t\t\tswitch (sas->error_mode) {\n@@ -3194,9 +3197,11 @@ static int add_submodule(const struct add_data *add_data)\n \t\tclone_data.url = add_data->realrepo;\n \t\tclone_data.quiet = add_data->quiet;\n \t\tclone_data.progress = add_data->progress;\n-\t\tif (add_data->reference_path)\n-\t\t\tstring_list_append(&clone_data.reference,\n-\t\t\t\t\t   xstrdup(add_data->reference_path));\n+\t\tif (add_data->reference_path) {\n+\t\t\tchar *p = xstrdup(add_data->reference_path);\n+\n+\t\t\tstring_list_append(&clone_data.reference, p)->util = p;\n+\t\t}\n \t\tclone_data.dissociate = add_data->dissociate;\n \t\tif (add_data->depth >= 0)\n \t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n-- \n2.37.0.932.g7b7031e73bc\n\n"},{"id":"458995","messageId":"kl6lmtdc6hhp.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-03.11-e5ec6945409-20220713T131601Z-avarab@gmail.com","subject":"Re: [PATCH 03/11] submodule--helper: fix \"module_clone_data\" memory leaks","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-13T17:37:54Z","receivedAt":"2022-07-13T17:38:01Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> The \"path\" member can come from \"argv\" (i.e. not malloc'd), or it can\n> be something we determine at runtime. In the latter case let's save\n> away a pointer to free() to avoid leaking memory.\n\n[...]\n\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index 73717be957c..23ab9c7e349 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -1511,6 +1511,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n>  struct module_clone_data {\n>  \tconst char *prefix;\n>  \tconst char *path;\n> +\tchar *path_free;\n>  \tconst char *name;\n>  \tconst char *url;\n>  \tconst char *depth;\n> @@ -1527,6 +1528,11 @@ struct module_clone_data {\n>  \t.single_branch = -1, \\\n>  }\n>  \n> +static void module_clone_data_release(struct module_clone_data *cd)\n> +{\n> +\tfree(cd->path_free);\n> +}\n> +\n>  struct submodule_alternate_setup {\n>  \tconst char *submodule_name;\n>  \tenum SUBMODULE_ALTERNATE_ERROR_MODE {\n> @@ -1651,9 +1657,9 @@ static int clone_submodule(struct module_clone_data *clone_data)\n>  \n>  \tif (!is_absolute_path(clone_data->path)) {\n>  \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n> -\t\tclone_data->path = strbuf_detach(&sb, NULL);\n> +\t\tclone_data->path = clone_data->path_free = strbuf_detach(&sb, NULL);\n>  \t} else {\n> -\t\tclone_data->path = xstrdup(clone_data->path);\n> +\t\tclone_data->path = clone_data->path_free = xstrdup(clone_data->path);\n>  \t}\n\nHm, having .path_free doesn't seem necessary. If I'm reading the code\ncorrectly,\n\n- in module_clone() we set clone_data.path from argv\n- then we unconditionally call clone_submodule(), which does all of the\n  hard work\n- in clone_submodule(), we always hit this conditional, which means that\n  past this point, clone_data.path is always free()-able.\n\nwhich suggests that we don't need clone_data.path_free at all. I suspect\nthat just this\n\n   static void module_clone_data_release(struct module_clone_data *cd)\n   {\n   \tfree(cd->path);\n   }\n\nis enough to plug the leak (though admittedly, I haven't properly tested\nthe leak because it's been a PITA to set up locally).\n\nThat's a pretty hacky suggestion though, since we're still using the\nsame member to hold free()-able and non-free()-able memory. Instead,\nmaybe we could move this \"clone_data.path = freeable\" logic into\nmodule_clone(), like:\n\n  diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n  index 73717be957..d67d4b9647 100644\n  --- a/builtin/submodule--helper.c\n  +++ b/builtin/submodule--helper.c\n  @@ -1649,13 +1649,6 @@ static int clone_submodule(struct module_clone_data *clone_data)\n    sm_gitdir = absolute_pathdup(sb.buf);\n    strbuf_reset(&sb);\n\n  -\tif (!is_absolute_path(clone_data->path)) {\n  -\t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n  -\t\tclone_data->path = strbuf_detach(&sb, NULL);\n  -\t} else {\n  -\t\tclone_data->path = xstrdup(clone_data->path);\n  -\t}\n  -\n    if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)\n      die(_(\"refusing to create/use '%s' in another submodule's \"\n            \"git dir\"), sm_gitdir);\n  @@ -1745,12 +1738,13 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n    int dissociate = 0, quiet = 0, progress = 0, require_init = 0;\n    struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n    struct list_objects_filter_options filter_options = { 0 };\n  +\tconst char *clone_path;\n\n    struct option module_clone_options[] = {\n      OPT_STRING(0, \"prefix\", &clone_data.prefix,\n          N_(\"path\"),\n          N_(\"alternative anchor for relative paths\")),\n  -\t\tOPT_STRING(0, \"path\", &clone_data.path,\n  +\t\tOPT_STRING(0, \"path\", &clone_path,\n          N_(\"path\"),\n          N_(\"where the new submodule will be cloned to\")),\n      OPT_STRING(0, \"name\", &clone_data.name,\n  @@ -1795,6 +1789,15 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n    clone_data.require_init = !!require_init;\n    clone_data.filter_options = &filter_options;\n\n  +\tif (!is_absolute_path(clone_path)) {\n  +\t\tstruct strbuf sb = STRBUF_INIT;\n  +\t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_path);\n  +\t\tclone_data.path = strbuf_detach(&sb, NULL);\n  +\t\tstrbuf_release(&sb);\n  +\t} else {\n  +\t\tclone_data.path = xstrdup(clone_path);\n  +\t}\n  +\n    if (argc || !clone_data.url || !clone_data.path || !*(clone_data.path))\n      usage_with_options(git_submodule_helper_usage,\n            module_clone_options);\n"},{"id":"458998","messageId":"kl6lilo06g82.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"kl6lmtdc6hhp.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH 03/11] submodule--helper: fix \"module_clone_data\" memory leaks","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-13T18:05:17Z","receivedAt":"2022-07-13T18:05:23Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Glen Choo <chooglen@google.com> writes:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n>> index 73717be957c..23ab9c7e349 100644\n>> --- a/builtin/submodule--helper.c\n>> +++ b/builtin/submodule--helper.c\n>> @@ -1511,6 +1511,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n>>  struct module_clone_data {\n>>  \tconst char *prefix;\n>>  \tconst char *path;\n>> +\tchar *path_free;\n>>  \tconst char *name;\n>>  \tconst char *url;\n>>  \tconst char *depth;\n>> @@ -1527,6 +1528,11 @@ struct module_clone_data {\n>>  \t.single_branch = -1, \\\n>>  }\n>>  \n>> +static void module_clone_data_release(struct module_clone_data *cd)\n>> +{\n>> +\tfree(cd->path_free);\n>> +}\n>> +\n>>  struct submodule_alternate_setup {\n>>  \tconst char *submodule_name;\n>>  \tenum SUBMODULE_ALTERNATE_ERROR_MODE {\n>> @@ -1651,9 +1657,9 @@ static int clone_submodule(struct module_clone_data *clone_data)\n>>  \n>>  \tif (!is_absolute_path(clone_data->path)) {\n>>  \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n>> -\t\tclone_data->path = strbuf_detach(&sb, NULL);\n>> +\t\tclone_data->path = clone_data->path_free = strbuf_detach(&sb, NULL);\n>>  \t} else {\n>> -\t\tclone_data->path = xstrdup(clone_data->path);\n>> +\t\tclone_data->path = clone_data->path_free = xstrdup(clone_data->path);\n>>  \t}\n>\n> Hm, having .path_free doesn't seem necessary. If I'm reading the code\n> correctly,\n>\n> - in module_clone() we set clone_data.path from argv\n> - then we unconditionally call clone_submodule(), which does all of the\n>   hard work\n> - in clone_submodule(), we always hit this conditional, which means that\n>   past this point, clone_data.path is always free()-able.\n>\n> which suggests that we don't need clone_data.path_free at all. I suspect\n> that just this\n>\n>    static void module_clone_data_release(struct module_clone_data *cd)\n>    {\n>    \tfree(cd->path);\n>    }\n>\n> is enough to plug the leak (though admittedly, I haven't properly tested\n> the leak because it's been a PITA to set up locally).\n>\n> That's a pretty hacky suggestion though, since we're still using the\n> same member to hold free()-able and non-free()-able memory....\n\nAh, here's a wacky idea (whose feasibility I haven't thought about at\nall) that would make things a lot cleaner. If we had something like\nOPT_STRING_DUP, that xstrdup()-s the value from argv when we parse it,\nthen we could drop the \"const\" from clone_data.path and just free() it.\n"},{"id":"459011","messageId":"220713.86o7xs3g76.gmgdl@evledraar.gmail.com","threadId":"58151","inReplyTo":"kl6lilo06g82.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH 03/11] submodule--helper: fix \"module_clone_data\" memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-13T20:30:53Z","receivedAt":"2022-07-13T20:34:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Jul 13 2022, Glen Choo wrote:\n\n> Glen Choo <chooglen@google.com> writes:\n>\n>> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>>\n>>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n>>> index 73717be957c..23ab9c7e349 100644\n>>> --- a/builtin/submodule--helper.c\n>>> +++ b/builtin/submodule--helper.c\n>>> @@ -1511,6 +1511,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n>>>  struct module_clone_data {\n>>>  \tconst char *prefix;\n>>>  \tconst char *path;\n>>> +\tchar *path_free;\n>>>  \tconst char *name;\n>>>  \tconst char *url;\n>>>  \tconst char *depth;\n>>> @@ -1527,6 +1528,11 @@ struct module_clone_data {\n>>>  \t.single_branch = -1, \\\n>>>  }\n>>>  \n>>> +static void module_clone_data_release(struct module_clone_data *cd)\n>>> +{\n>>> +\tfree(cd->path_free);\n>>> +}\n>>> +\n>>>  struct submodule_alternate_setup {\n>>>  \tconst char *submodule_name;\n>>>  \tenum SUBMODULE_ALTERNATE_ERROR_MODE {\n>>> @@ -1651,9 +1657,9 @@ static int clone_submodule(struct module_clone_data *clone_data)\n>>>  \n>>>  \tif (!is_absolute_path(clone_data->path)) {\n>>>  \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n>>> -\t\tclone_data->path = strbuf_detach(&sb, NULL);\n>>> +\t\tclone_data->path = clone_data->path_free = strbuf_detach(&sb, NULL);\n>>>  \t} else {\n>>> -\t\tclone_data->path = xstrdup(clone_data->path);\n>>> +\t\tclone_data->path = clone_data->path_free = xstrdup(clone_data->path);\n>>>  \t}\n>>\n>> Hm, having .path_free doesn't seem necessary. If I'm reading the code\n>> correctly,\n>>\n>> - in module_clone() we set clone_data.path from argv\n>> - then we unconditionally call clone_submodule(), which does all of the\n>>   hard work\n>> - in clone_submodule(), we always hit this conditional, which means that\n>>   past this point, clone_data.path is always free()-able.\n>>\n>> which suggests that we don't need clone_data.path_free at all. I suspect\n>> that just this\n>>\n>>    static void module_clone_data_release(struct module_clone_data *cd)\n>>    {\n>>    \tfree(cd->path);\n>>    }\n>>\n>> is enough to plug the leak (though admittedly, I haven't properly tested\n>> the leak because it's been a PITA to set up locally).\n>>\n>> That's a pretty hacky suggestion though, since we're still using the\n>> same member to hold free()-able and non-free()-able memory....\n>\n> Ah, here's a wacky idea (whose feasibility I haven't thought about at\n> all) that would make things a lot cleaner. If we had something like\n> OPT_STRING_DUP, that xstrdup()-s the value from argv when we parse it,\n> then we could drop the \"const\" from clone_data.path and just free() it.\n\nI suppose so, it might make some things simpler, of course at the cost\nof needlessly duplicating things.\n\nBut we also have various common patterns such as string_lists where some\nelements are dup'd, some aren't, and need to deal with that. I think\njust having common idioms for tracking the dupe is usually better,\ne.g. in the case of a string list stick the pointer to free in \"util\".\n\nI think in this case the patch as-is is better than your suggestions,\nbecause it's a less fragile pattern, we explicitly mark when we dup\nsomething that's sometimes a dup, and sometimes not.\n\nWhereas if we do it with the xstrdup() at the start it requires more\nmoving things around, and if we have a new user who parses the same\nargument we'll bug out on that free().\n\nWhat do you think?\n"},{"id":"459015","messageId":"kl6lfsj4684n.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-02.11-4049362e9b4-20220713T131601Z-avarab@gmail.com","subject":"Re: [PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-13T21:00:08Z","receivedAt":"2022-07-13T21:00:29Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Use the less verbose { 0 }-initialization syntax rather than memset()\n> in builtin/submodule--helper.c, this doesn't make a difference in\n> terms of behavior, but as we're about to modify adjacent code makes\n> this more consistent, and lets us avoid worrying about when the\n> memset() happens v.s. a \"goto cleanup\".\n\nOk. I wonder if we could reduce this kind of churn in the future by\nadding this to CodingGuidelines, e.g. \"always use { 0 } for stack\nvariables\". Tangentially, do we require { NULL } when the first element\nis a pointer? (I'm not sure because this isn't in CodingGuidelines\neither AFAICT.)\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 6 ++----\n>  1 file changed, 2 insertions(+), 4 deletions(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index fac52ade5e1..73717be957c 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -1744,7 +1744,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n>  {\n>  \tint dissociate = 0, quiet = 0, progress = 0, require_init = 0;\n>  \tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n> -\tstruct list_objects_filter_options filter_options;\n> +\tstruct list_objects_filter_options filter_options = { 0 };\n>  \n>  \tstruct option module_clone_options[] = {\n>  \t\tOPT_STRING(0, \"prefix\", &clone_data.prefix,\n> @@ -1786,7 +1786,6 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n>  \t\tNULL\n>  \t};\n>  \n> -\tmemset(&filter_options, 0, sizeof(filter_options));\n>  \targc = parse_options(argc, argv, prefix, module_clone_options,\n>  \t\t\t     git_submodule_helper_usage, 0);\n>  \n> @@ -2563,7 +2562,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n>  {\n>  \tstruct pathspec pathspec;\n>  \tstruct update_data opt = UPDATE_DATA_INIT;\n> -\tstruct list_objects_filter_options filter_options;\n> +\tstruct list_objects_filter_options filter_options = { 0 };\n>  \tint ret;\n>  \n>  \tstruct option module_update_options[] = {\n> @@ -2623,7 +2622,6 @@ static int module_update(int argc, const char **argv, const char *prefix)\n>  \tupdate_clone_config_from_gitmodules(&opt.max_jobs);\n>  \tgit_config(git_update_clone_config, &opt.max_jobs);\n>  \n> -\tmemset(&filter_options, 0, sizeof(filter_options));\n>  \targc = parse_options(argc, argv, prefix, module_update_options,\n>  \t\t\t     git_submodule_helper_usage, 0);\n>  \n> -- \n> 2.37.0.932.g7b7031e73bc\n"},{"id":"459017","messageId":"xmqqy1wwaey8.fsf@gitster.g","threadId":"58151","inReplyTo":"kl6lfsj4684n.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-13T21:19:11Z","receivedAt":"2022-07-13T21:20:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Glen Choo <chooglen@google.com> writes:\n\n> Ok. I wonder if we could reduce this kind of churn in the future by\n> adding this to CodingGuidelines, e.g. \"always use { 0 } for stack\n> variables\". Tangentially, do we require { NULL } when the first element\n> is a pointer? (I'm not sure because this isn't in CodingGuidelines\n> either AFAICT.)\n\nA valiable can legitimately be left uninitialized, or initialized to\na known value that is not zero using designated initializers.  So\nsaying something like\n\n    When zero-initializing an auto variable that is a struct or\n    union in its definition, use \"{ 0 }\", whether the first member\n    in the struct is of a number, a pointer, or a compound type.\n\nmay be OK.  I do not think we would want to say \"always use X\", as\nthe world is not that simple..\n\nWe do favor designated initializers over traditional initialization\nin the order of members these days, so something like\n\n    When declaring a struct/union variable or an array with initial\n    value to some members or elements, consider using designated\n    initializers, instead of listing the values in the order of\n    members in the definition of the struct.\n\nwould also be good.\n\nThanks.\n"},{"id":"459025","messageId":"kl6lbkts63fb.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"xmqqy1wwaey8.fsf@gitster.g","subject":"Re: [PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-13T22:41:44Z","receivedAt":"2022-07-13T22:41:50Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Glen Choo <chooglen@google.com> writes:\n>\n>> Ok. I wonder if we could reduce this kind of churn in the future by\n>> adding this to CodingGuidelines, e.g. \"always use { 0 } for stack\n>> variables\". Tangentially, do we require { NULL } when the first element\n>> is a pointer? (I'm not sure because this isn't in CodingGuidelines\n>> either AFAICT.)\n>\n> A valiable can legitimately be left uninitialized, or initialized to\n> a known value that is not zero using designated initializers.  So\n> saying something like\n>\n>     When zero-initializing an auto variable that is a struct or\n>     union in its definition, use \"{ 0 }\", whether the first member\n>     in the struct is of a number, a pointer, or a compound type.\n>\n> may be OK.  I do not think we would want to say \"always use X\", as\n> the world is not that simple..\n\nThanks. Also, I made a typo here, I meant to be more specific with\nregards to memset(), like:\n\n  When zero-initializing an auto variable that is a struct or\n  union in its definition, use \"{ 0 }\", whether the first member\n  in the struct is of a number, a pointer, or a compound type. _Do not\n  use memset()._\n\nUnless there really is a legitimate reason to use memset(&my_auto_var, 0\nsizeof(my_autovar)) that I've missed?\n\n> We do favor designated initializers over traditional initialization\n> in the order of members these days, so something like\n>\n>     When declaring a struct/union variable or an array with initial\n>     value to some members or elements, consider using designated\n>     initializers, instead of listing the values in the order of\n>     members in the definition of the struct.\n>\n> would also be good.\n\nThanks. If I have time I'll send that proposal to CodingGuidelines, or\nsomeone else can send it (I don't mind either way).\n"},{"id":"459026","messageId":"kl6l8row62dg.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"220713.86o7xs3g76.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH 03/11] submodule--helper: fix \"module_clone_data\" memory leaks","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-13T23:04:27Z","receivedAt":"2022-07-13T23:04:33Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> On Wed, Jul 13 2022, Glen Choo wrote:\n>\n>> Glen Choo <chooglen@google.com> writes:\n>>\n>>> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>>>\n>>>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n>>>> index 73717be957c..23ab9c7e349 100644\n>>>> --- a/builtin/submodule--helper.c\n>>>> +++ b/builtin/submodule--helper.c\n>>>> @@ -1511,6 +1511,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n>>>>  struct module_clone_data {\n>>>>  \tconst char *prefix;\n>>>>  \tconst char *path;\n>>>> +\tchar *path_free;\n>>>>  \tconst char *name;\n>>>>  \tconst char *url;\n>>>>  \tconst char *depth;\n>>>> @@ -1527,6 +1528,11 @@ struct module_clone_data {\n>>>>  \t.single_branch = -1, \\\n>>>>  }\n>>>>  \n>>>> +static void module_clone_data_release(struct module_clone_data *cd)\n>>>> +{\n>>>> +\tfree(cd->path_free);\n>>>> +}\n>>>> +\n>>>>  struct submodule_alternate_setup {\n>>>>  \tconst char *submodule_name;\n>>>>  \tenum SUBMODULE_ALTERNATE_ERROR_MODE {\n>>>> @@ -1651,9 +1657,9 @@ static int clone_submodule(struct module_clone_data *clone_data)\n>>>>  \n>>>>  \tif (!is_absolute_path(clone_data->path)) {\n>>>>  \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n>>>> -\t\tclone_data->path = strbuf_detach(&sb, NULL);\n>>>> +\t\tclone_data->path = clone_data->path_free = strbuf_detach(&sb, NULL);\n>>>>  \t} else {\n>>>> -\t\tclone_data->path = xstrdup(clone_data->path);\n>>>> +\t\tclone_data->path = clone_data->path_free = xstrdup(clone_data->path);\n>>>>  \t}\n>>>\n>>> Hm, having .path_free doesn't seem necessary. If I'm reading the code\n>>> correctly,\n>>>\n>>> - in module_clone() we set clone_data.path from argv\n>>> - then we unconditionally call clone_submodule(), which does all of the\n>>>   hard work\n>>> - in clone_submodule(), we always hit this conditional, which means that\n>>>   past this point, clone_data.path is always free()-able.\n>>>\n>>> which suggests that we don't need clone_data.path_free at all. I suspect\n>>> that just this\n>>>\n>>>    static void module_clone_data_release(struct module_clone_data *cd)\n>>>    {\n>>>    \tfree(cd->path);\n>>>    }\n>>>\n>>> is enough to plug the leak (though admittedly, I haven't properly tested\n>>> the leak because it's been a PITA to set up locally).\n>>>\n>>> That's a pretty hacky suggestion though, since we're still using the\n>>> same member to hold free()-able and non-free()-able memory....\n>>\n>> Ah, here's a wacky idea (whose feasibility I haven't thought about at\n>> all) that would make things a lot cleaner. If we had something like\n>> OPT_STRING_DUP, that xstrdup()-s the value from argv when we parse it,\n>> then we could drop the \"const\" from clone_data.path and just free() it.\n>\n> I suppose so, it might make some things simpler, of course at the cost\n> of needlessly duplicating things.\n>\n> But we also have various common patterns such as string_lists where some\n> elements are dup'd, some aren't, and need to deal with that. I think\n> just having common idioms for tracking the dupe is usually better,\n> e.g. in the case of a string list stick the pointer to free in \"util\".\n\nHm, sounds fair. \"Sometimes dup and sometimes not\" sounds like an\ninevitability. I'm not experienced enough to know better, and folks\nwhose opinion I sought seem to agree with you.\n\n> I think in this case the patch as-is is better than your suggestions,\n> because it's a less fragile pattern, we explicitly mark when we dup\n> something that's sometimes a dup, and sometimes not.\n>\n> Whereas if we do it with the xstrdup() at the start it requires more\n> moving things around, and if we have a new user who parses the same\n> argument we'll bug out on that free().\n>\n> What do you think?\n\nFrankly I'm ok with moving things around; I think the code could use\na little cleaning up :) But yeah, I think my suggestion isn't so great -\nit's a bit weird to keep around an auto variable that exists only to be\ndup-ed to the thing we care about. We can forget about that.\n\nI do think that it's worth avoiding the \"sometimes dup, sometimes not\"\npattern if we can, though (of course these are just my non-C instincts\ntalking), and we can do that here if we just choose not to assign back\nto .path. Something like:\n\n  struct module_clone_data {\n    const char *prefix;\n  -\tconst char *path;\n  +\tchar *path;\n  +\tconst char *path_argv;\n\n  ...\n\n   \tif (!is_absolute_path(clone_data->path)) {\n  -\t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n  +\t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path_argv);\n  +\t\tclone_data->path = strbuf_detach(&sb, NULL);\n   \t} else {\n  -\t\tclone_data->path = xstrdup(clone_data->path);\n  +\t\tclone_data->path = xstrdup(clone_data->path_argv);\n   \t}\n\nwould be clearer to me since the const pointer never points to something\nthat the struct actually owns.\n\nBut if the \"= .to_free = \" idiom is well-understood and accepted to the\npoint that we don't need to actively avoid \"sometimes dup, sometimes\nnot\", then we should drop my suggestion and just go with your patch :)\n"},{"id":"459028","messageId":"220714.86k08g361i.gmgdl@evledraar.gmail.com","threadId":"58151","inReplyTo":"kl6l8row62dg.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH 03/11] submodule--helper: fix \"module_clone_data\" memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-14T00:06:56Z","receivedAt":"2022-07-14T00:13:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Jul 13 2022, Glen Choo wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>> [...]\n>> What do you think?\n>\n> Frankly I'm ok with moving things around; I think the code could use\n> a little cleaning up :) \n\nYeah, but this whole part of it is something we'll be throwing away\nentirely anyway, so I wanted to leave it at just fixing the memory leaks\nas narrowly as possible for now.\n\nI.e. we invoke \"clone\" from submodule--helper itself, which we don't\nneed to do if we just invoke it as a function, in which case this whole\nargv v.s. dynamically generated difference goes away.\n\nWell, we'll have a constant string in some cases, but we'll likely\neither strdup them all with a strvec, or more likely pass the arguments\nwith custom \"options\" struct or something.\n\n> But yeah, I think my suggestion isn't so great -\n> it's a bit weird to keep around an auto variable that exists only to be\n> dup-ed to the thing we care about. We can forget about that.\n>\n> I do think that it's worth avoiding the \"sometimes dup, sometimes not\"\n> pattern if we can, though (of course these are just my non-C instincts\n> talking), and we can do that here if we just choose not to assign back\n> to .path. Something like:\n>\n>   struct module_clone_data {\n>     const char *prefix;\n>   -\tconst char *path;\n>   +\tchar *path;\n>   +\tconst char *path_argv;\n>\n>   ...\n>\n>    \tif (!is_absolute_path(clone_data->path)) {\n>   -\t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n>   +\t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path_argv);\n>   +\t\tclone_data->path = strbuf_detach(&sb, NULL);\n>    \t} else {\n>   -\t\tclone_data->path = xstrdup(clone_data->path);\n>   +\t\tclone_data->path = xstrdup(clone_data->path_argv);\n>    \t}\n>\n> would be clearer to me since the const pointer never points to something\n> that the struct actually owns.\n\nI think that actually makes a lot of sense, I'll probably just change it\nto that. I'll mull over this again when I get to re-rolling this\n(depending on future comments), thanks!\n\n> But if the \"= .to_free = \" idiom is well-understood and accepted to the\n> point that we don't need to actively avoid \"sometimes dup, sometimes\n> not\", then we should drop my suggestion and just go with your patch :)\n\nFWIW \"git grep ' = to_free = ' finds a fair bit of them, but luckily we\nusually don't need to play that particular game...\n"},{"id":"459032","messageId":"kl6l5yk05psl.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-05.11-76eab92c8b6-20220713T131601Z-avarab@gmail.com","subject":"Re: [PATCH 05/11] submodule--helper: free() leaking {run,capture}_command() argument","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-14T03:36:10Z","receivedAt":"2022-07-14T03:36:23Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Free the \"dir\" member of \"struct child_process\" that various functions\n> in submodule-helper.c allocate allocates with xstrdup().\n>\n> Since the \"dir\" argument is \"const char *\" let's keep a\n> \"char *to_free\" variable around for this rather than casting when we\n> call free().\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 41 +++++++++++++++++++++++++++----------\n>  1 file changed, 30 insertions(+), 11 deletions(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index a8e439e59b8..2099c5774b2 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -2198,27 +2198,36 @@ static int is_tip_reachable(const char *path, struct object_id *oid)\n>  \tstruct child_process cp = CHILD_PROCESS_INIT;\n>  \tstruct strbuf rev = STRBUF_INIT;\n>  \tchar *hex = oid_to_hex(oid);\n> +\tchar *to_free;\n> +\tint ret;\n>  \n>  \tcp.git_cmd = 1;\n> -\tcp.dir = xstrdup(path);\n> +\tcp.dir = to_free = xstrdup(path);\n>  \tcp.no_stderr = 1;\n>  \tstrvec_pushl(&cp.args, \"rev-list\", \"-n\", \"1\", hex, \"--not\", \"--all\", NULL);\n>  \n>  \tprepare_submodule_repo_env(&cp.env);\n>  \n> -\tif (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len)\n> -\t\treturn 0;\n> +\tif (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len) {\n> +\t\tret = 0;\n> +\t\tgoto cleanup;\n> +\t}\n>  \n> -\treturn 1;\n> +\tret = 1;\n> +cleanup:\n> +\tfree(to_free);\n> +\treturn ret;\n>  }\n>  \n>  static int fetch_in_submodule(const char *module_path, int depth, int quiet, struct object_id *oid)\n>  {\n>  \tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\tchar *to_free;\n> +\tint ret;\n>  \n>  \tprepare_submodule_repo_env(&cp.env);\n>  \tcp.git_cmd = 1;\n> -\tcp.dir = xstrdup(module_path);\n> +\tcp.dir = to_free = xstrdup(module_path);\n>  \n>  \tstrvec_push(&cp.args, \"fetch\");\n>  \tif (quiet)\n> @@ -2232,7 +2241,9 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str\n>  \t\tfree(remote);\n>  \t}\n>  \n> -\treturn run_command(&cp);\n> +\tret = run_command(&cp);\n> +\tfree(to_free);\n> +\treturn ret;\n>  }\n>  \n>  static int run_update_command(struct update_data *ud, int subforce)\n> @@ -2240,6 +2251,8 @@ static int run_update_command(struct update_data *ud, int subforce)\n>  \tstruct child_process cp = CHILD_PROCESS_INIT;\n>  \tchar *oid = oid_to_hex(&ud->oid);\n>  \tint must_die_on_failure = 0;\n> +\tchar *to_free;\n> +\tint ret;\n>  \n>  \tswitch (ud->update_strategy.type) {\n>  \tcase SM_UPDATE_CHECKOUT:\n> @@ -2273,7 +2286,7 @@ static int run_update_command(struct update_data *ud, int subforce)\n>  \t}\n>  \tstrvec_push(&cp.args, oid);\n>  \n> -\tcp.dir = xstrdup(ud->sm_path);\n> +\tcp.dir = to_free = xstrdup(ud->sm_path);\n>  \tprepare_submodule_repo_env(&cp.env);\n>  \tif (run_command(&cp)) {\n>  \t\tswitch (ud->update_strategy.type) {\n> @@ -2301,11 +2314,14 @@ static int run_update_command(struct update_data *ud, int subforce)\n>  \t\t\texit(128);\n>  \n>  \t\t/* the command failed, but update must continue */\n> -\t\treturn 1;\n> +\t\tret = 1;\n> +\t\tgoto cleanup;\n>  \t}\n>  \n> -\tif (ud->quiet)\n> -\t\treturn 0;\n> +\tif (ud->quiet) {\n> +\t\tret = 0;\n> +\t\tgoto cleanup;\n> +\t}\n>  \n>  \tswitch (ud->update_strategy.type) {\n>  \tcase SM_UPDATE_CHECKOUT:\n> @@ -2329,7 +2345,10 @@ static int run_update_command(struct update_data *ud, int subforce)\n>  \t\t    submodule_strategy_to_string(&ud->update_strategy));\n>  \t}\n>  \n> -\treturn 0;\n> +\tret = 0;\n> +cleanup:\n> +\tfree(to_free);\n> +\treturn ret;\n>  }\n>  \n>  static int run_update_procedure(struct update_data *ud)\n\nI assume I'm missing something, but couldn't we achieve the same result\nby just removing the xstrdup() calls? i.e. we only leak .dir (which we\ndon't clear because it's const), so couldn't we just assign to it\nwithout xstrdup() and not have to worry about free()-ing it?\n\nI didn't see a correctness reason for us to xstrdup(), and indeed, t7406\npasses with the change I just described (which I believe covers all of\nthe sites here). In fact, we already have one site that does exactly\nthis in the recursive part of update_submodule():\n\n\tif (update_data->recursive) {\n\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n\t\tstruct update_data next = *update_data;\n\t\tint res;\n\n\t\tnext.prefix = NULL;\n\t\toidcpy(&next.oid, null_oid());\n\t\toidcpy(&next.suboid, null_oid());\n\n\t\tcp.dir = update_data->sm_path;\n\nTangentially related question (primarily for my own learning): in all of\nthese hunks, the string being xstrdup()-ed is update_data.sm_path, which\nis only ever set in update_submodules():\n\n\t\tupdate_data->sm_path = ucd.sub->path;\n\nwhere ucd.sub->path is a \"const char *\". So we never have to worry about\nfree()-ing update_data.sm_path, right? (Your patch doesn't attempt to\nfree() it, which sounds correct.)\n"},{"id":"459037","messageId":"220714.86v8s00y1z.gmgdl@evledraar.gmail.com","threadId":"58151","inReplyTo":"kl6lbkts63fb.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-14T10:25:42Z","receivedAt":"2022-07-14T10:49:20Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Jul 13 2022, Glen Choo wrote:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> Glen Choo <chooglen@google.com> writes:\n>>\n>>> Ok. I wonder if we could reduce this kind of churn in the future by\n>>> adding this to CodingGuidelines, e.g. \"always use { 0 } for stack\n>>> variables\". Tangentially, do we require { NULL } when the first element\n>>> is a pointer? (I'm not sure because this isn't in CodingGuidelines\n>>> either AFAICT.)\n>>\n>> A valiable can legitimately be left uninitialized, or initialized to\n>> a known value that is not zero using designated initializers.  So\n>> saying something like\n>>\n>>     When zero-initializing an auto variable that is a struct or\n>>     union in its definition, use \"{ 0 }\", whether the first member\n>>     in the struct is of a number, a pointer, or a compound type.\n>>\n>> may be OK.  I do not think we would want to say \"always use X\", as\n>> the world is not that simple..\n>\n> Thanks. Also, I made a typo here, I meant to be more specific with\n> regards to memset(), like:\n>\n>   When zero-initializing an auto variable that is a struct or\n>   union in its definition, use \"{ 0 }\", whether the first member\n>   in the struct is of a number, a pointer, or a compound type. _Do not\n>   use memset()._\n\nIt's curious that the { 0 } v.s. { NULL } form jumps out at people, but\nseemingly not that you don't memset(&x, NULL, ...). I.e. that we're\nalready dealing with a form where C's \"0 is NULL in pointer context\"\nrules kick in :)\n\nSo I wonder if we should say anything about the first member at all. On\nthe other hand this is quite an odd bit of C syntax, and reveals a bit\nof on edge case or wart that I'm not happy with.\n\nIt's why I believe GCC has a syntax extension so you can just do:\n\n\tstruct foo x = {};\n\nI.e. 6.7.8.10 and 6.7.8.21 disuss the semantics of this, basically that\nif there are fewer initializers than elements or members that the\nstructure is initialized as though it were \"static\".\n\nBut for the first member we rely on 0 and NULL being the same in pointer\ncontext (even though NULL my not be (void*)NULL!). So it's a tad nasty,\nit's basically doing the same as:\n\n    const char *foo = 0; /* not NULL */\n\nAs an aside I wonder if we should say \"C says this is undefined, but\nc'mon, let's just assume '#define NULL (void*)0'\". I think in practice\nit's like the recently standardized 2's compliment, in that almost\nnobody can even remember a platform where it isn't true, and none are in\ncurrent use (but maybe I'm wrong...).\n\nAnyway, I was curious about this so I tried the following locally:\n\t\n\t@@\n\ttype T;\n\tidentifier I;\n\t@@\n\t- T I;\n\t+ T I = { 0 };\n\t... when strict\n\t    when != \\( I \\| &I \\)\n\t(\n\t- memset(&I, 0, sizeof(I));\n\t|\n\t- memset(&I, 0, sizeof(T));\n\t)\n\t\n\nWhich aside from whitespace issues (that I've asked the cocci ML about)\nyields a sane result.\n\nWhat *doesn't* yield a sane result is getting rid of the \"when strict\"\nthere, i.e. we must not do this in xdiff/xhistogram.c's\nhistogram_diff(), as we \"goto\" to the memset() to re-zero-out the\nvariable.\n\nBut removing the \"when strict\" yields a change to 45 more files, the\ninitial one with \"when strict\" is 76 files. With the exception of that\nhistogram_diff() case (and I manually skimmed the code for the rest) it\npasses all tests at least... :)\n\n> Unless there really is a legitimate reason to use memset(&my_auto_var, 0\n> sizeof(my_autovar)) that I've missed?\n\nBasically no, except when you need to do the init N times, as noted\nabove.\n\n>> We do favor designated initializers over traditional initialization\n>> in the order of members these days, so something like\n>>\n>>     When declaring a struct/union variable or an array with initial\n>>     value to some members or elements, consider using designated\n>>     initializers, instead of listing the values in the order of\n>>     members in the definition of the struct.\n>>\n>> would also be good.\n>\n> Thanks. If I have time I'll send that proposal to CodingGuidelines, or\n> someone else can send it (I don't mind either way).\n\nSounds good!\n"},{"id":"459044","messageId":"220714.86r12n21rs.gmgdl@evledraar.gmail.com","threadId":"58151","inReplyTo":"kl6l5yk05psl.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH 05/11] submodule--helper: free() leaking {run,capture}_command() argument","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-14T14:43:01Z","receivedAt":"2022-07-14T14:43:28Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Jul 13 2022, Glen Choo wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> Free the \"dir\" member of \"struct child_process\" that various functions\n>> in submodule-helper.c allocate allocates with xstrdup().\n>>\n>> Since the \"dir\" argument is \"const char *\" let's keep a\n>> \"char *to_free\" variable around for this rather than casting when we\n>> call free().\n>>\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>>  builtin/submodule--helper.c | 41 +++++++++++++++++++++++++++----------\n>>  1 file changed, 30 insertions(+), 11 deletions(-)\n>>\n>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n>> index a8e439e59b8..2099c5774b2 100644\n>> --- a/builtin/submodule--helper.c\n>> +++ b/builtin/submodule--helper.c\n>> @@ -2198,27 +2198,36 @@ static int is_tip_reachable(const char *path, struct object_id *oid)\n>>  \tstruct child_process cp = CHILD_PROCESS_INIT;\n>>  \tstruct strbuf rev = STRBUF_INIT;\n>>  \tchar *hex = oid_to_hex(oid);\n>> +\tchar *to_free;\n>> +\tint ret;\n>>  \n>>  \tcp.git_cmd = 1;\n>> -\tcp.dir = xstrdup(path);\n>> +\tcp.dir = to_free = xstrdup(path);\n>>  \tcp.no_stderr = 1;\n>>  \tstrvec_pushl(&cp.args, \"rev-list\", \"-n\", \"1\", hex, \"--not\", \"--all\", NULL);\n>>  \n>>  \tprepare_submodule_repo_env(&cp.env);\n>>  \n>> -\tif (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len)\n>> -\t\treturn 0;\n>> +\tif (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len) {\n>> +\t\tret = 0;\n>> +\t\tgoto cleanup;\n>> +\t}\n>>  \n>> -\treturn 1;\n>> +\tret = 1;\n>> +cleanup:\n>> +\tfree(to_free);\n>> +\treturn ret;\n>>  }\n>>  \n>>  static int fetch_in_submodule(const char *module_path, int depth, int quiet, struct object_id *oid)\n>>  {\n>>  \tstruct child_process cp = CHILD_PROCESS_INIT;\n>> +\tchar *to_free;\n>> +\tint ret;\n>>  \n>>  \tprepare_submodule_repo_env(&cp.env);\n>>  \tcp.git_cmd = 1;\n>> -\tcp.dir = xstrdup(module_path);\n>> +\tcp.dir = to_free = xstrdup(module_path);\n>>  \n>>  \tstrvec_push(&cp.args, \"fetch\");\n>>  \tif (quiet)\n>> @@ -2232,7 +2241,9 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str\n>>  \t\tfree(remote);\n>>  \t}\n>>  \n>> -\treturn run_command(&cp);\n>> +\tret = run_command(&cp);\n>> +\tfree(to_free);\n>> +\treturn ret;\n>>  }\n>>  \n>>  static int run_update_command(struct update_data *ud, int subforce)\n>> @@ -2240,6 +2251,8 @@ static int run_update_command(struct update_data *ud, int subforce)\n>>  \tstruct child_process cp = CHILD_PROCESS_INIT;\n>>  \tchar *oid = oid_to_hex(&ud->oid);\n>>  \tint must_die_on_failure = 0;\n>> +\tchar *to_free;\n>> +\tint ret;\n>>  \n>>  \tswitch (ud->update_strategy.type) {\n>>  \tcase SM_UPDATE_CHECKOUT:\n>> @@ -2273,7 +2286,7 @@ static int run_update_command(struct update_data *ud, int subforce)\n>>  \t}\n>>  \tstrvec_push(&cp.args, oid);\n>>  \n>> -\tcp.dir = xstrdup(ud->sm_path);\n>> +\tcp.dir = to_free = xstrdup(ud->sm_path);\n>>  \tprepare_submodule_repo_env(&cp.env);\n>>  \tif (run_command(&cp)) {\n>>  \t\tswitch (ud->update_strategy.type) {\n>> @@ -2301,11 +2314,14 @@ static int run_update_command(struct update_data *ud, int subforce)\n>>  \t\t\texit(128);\n>>  \n>>  \t\t/* the command failed, but update must continue */\n>> -\t\treturn 1;\n>> +\t\tret = 1;\n>> +\t\tgoto cleanup;\n>>  \t}\n>>  \n>> -\tif (ud->quiet)\n>> -\t\treturn 0;\n>> +\tif (ud->quiet) {\n>> +\t\tret = 0;\n>> +\t\tgoto cleanup;\n>> +\t}\n>>  \n>>  \tswitch (ud->update_strategy.type) {\n>>  \tcase SM_UPDATE_CHECKOUT:\n>> @@ -2329,7 +2345,10 @@ static int run_update_command(struct update_data *ud, int subforce)\n>>  \t\t    submodule_strategy_to_string(&ud->update_strategy));\n>>  \t}\n>>  \n>> -\treturn 0;\n>> +\tret = 0;\n>> +cleanup:\n>> +\tfree(to_free);\n>> +\treturn ret;\n>>  }\n>>  \n>>  static int run_update_procedure(struct update_data *ud)\n>\n> I assume I'm missing something, but couldn't we achieve the same result\n> by just removing the xstrdup() calls? i.e. we only leak .dir (which we\n> don't clear because it's const), so couldn't we just assign to it\n> without xstrdup() and not have to worry about free()-ing it?\n>\n> I didn't see a correctness reason for us to xstrdup(), and indeed, t7406\n> passes with the change I just described (which I believe covers all of\n> the sites here). In fact, we already have one site that does exactly\n> this in the recursive part of update_submodule():\n>\n> \tif (update_data->recursive) {\n> \t\tstruct child_process cp = CHILD_PROCESS_INIT;\n> \t\tstruct update_data next = *update_data;\n> \t\tint res;\n>\n> \t\tnext.prefix = NULL;\n> \t\toidcpy(&next.oid, null_oid());\n> \t\toidcpy(&next.suboid, null_oid());\n>\n> \t\tcp.dir = update_data->sm_path;\n>\n> Tangentially related question (primarily for my own learning): in all of\n> these hunks, the string being xstrdup()-ed is update_data.sm_path, which\n> is only ever set in update_submodules():\n>\n> \t\tupdate_data->sm_path = ucd.sub->path;\n>\n> where ucd.sub->path is a \"const char *\". So we never have to worry about\n> free()-ing update_data.sm_path, right? (Your patch doesn't attempt to\n> free() it, which sounds correct.)\n\nYes, do'h! That's a much better fix, and only 3 lines of getting rid of\nthe xstrdup(). I'll re-roll with that.\n"},{"id":"459053","messageId":"xmqqbktr7gtv.fsf@gitster.g","threadId":"58151","inReplyTo":"220714.86v8s00y1z.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-14T17:19:08Z","receivedAt":"2022-07-14T17:19:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> It's curious that the { 0 } v.s. { NULL } form jumps out at people, but\n> seemingly not that you don't memset(&x, NULL, ...). I.e. that we're\n> already dealing with a form where C's \"0 is NULL in pointer context\"\n> rules kick in :)\n\nStrictly speaking there are different mechanisms at play here.\nLiteral \"0\" spelled in source code and assigned to a pointer\nvariable assigns a NULL to the variable even on architectures where\nthe representation of NULL is *not* \"all 0-bit filling the width of\nthe variable\" and C language is what guarantees \"NULL pointer is\nspelled as integer 0 in the source code\".\n\nAlso, the language, not runtime and pointer representation, is how\nsecond and subsequent members of a struct that are pointers are\ninitialized to NULL (not necessarily to \"all 0-bit filling the width\nof the member\").\n\nmemset(&ptr, '\\0', sizeof(ptr)), where ptr is a pointer variable or\na struct/union with a pointer member, on the other hand, is unaware\nof how a NULL pointer is represented on the platform.  All it can\n(and should) do is to fill the thing \"ptr\" with all 0-bit.  On an\nexotic architecture, where NULL is not \"all 0-bit filling the width\nof the variable\", I do not think it would work.\n\nSo from that point of view, using memset() as a replacement for zero\ninitialization is simply wrong, but in practice people do not work\non such a platform that the distinction matters anymore, hopefully?\n\n> So I wonder if we should say anything about the first member at all.\n\nThe mention of the first member of the struct is historically\nimportant only because some checkers like sparse used to complain\nabout\n\n    struct { TYPE *member0; ... } var = { 0 };\n\nthe same way as\n\n    char *string;\n    string = 0;\n\nbefore they got fixed so that they know about \"{ 0 }\" convention to\nbe silent about the former, while still warning about \"even though C\nlanguage says literal 0 is a valid way to spell NULL, you shouldn't\ndo that\" to the latter.\n\nThese days, we can safely write \"{ 0 }\" without having to worry\nabout the type of the first member.\n\n"},{"id":"459059","messageId":"kl6lzghb4l8w.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-06.11-b9984e982db-20220713T131601Z-avarab@gmail.com","subject":"Re: [PATCH 06/11] submodule--helper: add and use *_release() functions","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-14T18:11:59Z","receivedAt":"2022-07-14T18:12:12Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Add release functions for \"struct module_list\", \"struct\n> submodule_update_clone\" and \"struct update_data\". For now we're\n> leaving some of the \"struct update_data\" members, we'll deal with\n> those in a subsequent commit.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index 2099c5774b2..a964dbeec38 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -1910,6 +1921,13 @@ struct submodule_update_clone {\n>  };\n>  #define SUBMODULE_UPDATE_CLONE_INIT { 0 }\n>  \n> +static void submodule_update_clone_release(struct submodule_update_clone *suc)\n> +{\n> +\t/* Not \"update_data\", owned by update_data_release() */\n> +\tfree(suc->update_clone);\n> +\tfree(suc->failed_clones);\n> +}\n> +\n\nTo make this ownership clearer, should we also make\nsubmodule_update_clone.update_data const? i.e.\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex a964dbeec3..79fd901e5a 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1906,7 +1906,7 @@ struct submodule_update_clone {\n \tint current;\n \n \t/* configuration parameters which are passed on to the children */\n-\tstruct update_data *update_data;\n+\tconst struct update_data *update_data;\n \n \t/* to be consumed by update_submodule() */\n \tstruct update_clone_data *update_clone;\n@@ -2003,7 +2003,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,\n \tconst char *update_string;\n \tenum submodule_update_type update_type;\n \tchar *key;\n-\tstruct update_data *ud = suc->update_data;\n+\tconst struct update_data *ud = suc->update_data;\n \tchar *displaypath = get_submodule_displaypath(ce->name, ud->prefix);\n \tstruct strbuf sb = STRBUF_INIT;\n \tint needs_cloning = 0;\n"},{"id":"459060","messageId":"kl6lwncf4kvd.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-07.11-f8c20bbf266-20220713T131601Z-avarab@gmail.com","subject":"Re: [PATCH 07/11] submodule--helper: fix \"errmsg_str\" memory leak","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-14T18:20:06Z","receivedAt":"2022-07-14T18:20:11Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Fix a memory leak introduced in e83e3333b57 (submodule: port submodule\n> subcommand 'summary' from shell to C, 2020-08-13), to do that stop\n> juggling around the \"errmsg\" and \"struct strbuf\", let's instead move\n> the \"struct strbuf errmsg\" to the top-level.\n>\n> Now we don't need to strbuf_detach() it anymore, but we do need to\n> ensure that we pass NULL to print_submodule_summary() when we have no\n> error message.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 7 ++++---\n>  1 file changed, 4 insertions(+), 3 deletions(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index a964dbeec38..a05578a7382 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -932,7 +932,8 @@ static void generate_submodule_summary(struct summary_cb *info,\n>  {\n>  \tchar *displaypath, *src_abbrev = NULL, *dst_abbrev;\n>  \tint missing_src = 0, missing_dst = 0;\n> -\tchar *errmsg = NULL;\n> +\tchar *errmsg;\n> +\tstruct strbuf errmsg_str = STRBUF_INIT;\n>  \tint total_commits = -1;\n>  \n>  \tif (!info->cached && oideq(&p->oid_dst, null_oid())) {\n> @@ -1032,7 +1033,6 @@ static void generate_submodule_summary(struct summary_cb *info,\n>  \t\t * submodule, i.e., deleted or changed to blob\n>  \t\t */\n>  \t\tif (S_ISGITLINK(p->mod_dst)) {\n> -\t\t\tstruct strbuf errmsg_str = STRBUF_INIT;\n>  \t\t\tif (missing_src && missing_dst) {\n>  \t\t\t\tstrbuf_addf(&errmsg_str, \"  Warn: %s doesn't contain commits %s and %s\\n\",\n>  \t\t\t\t\t    displaypath, oid_to_hex(&p->oid_src),\n> @@ -1043,10 +1043,10 @@ static void generate_submodule_summary(struct summary_cb *info,\n>  \t\t\t\t\t    oid_to_hex(&p->oid_src) :\n>  \t\t\t\t\t    oid_to_hex(&p->oid_dst));\n>  \t\t\t}\n> -\t\t\terrmsg = strbuf_detach(&errmsg_str, NULL);\n>  \t\t}\n>  \t}\n>  \n> +\terrmsg = errmsg_str.len ? errmsg_str.buf : NULL;\n>  \tprint_submodule_summary(info, errmsg, total_commits,\n>  \t\t\t\tdisplaypath, src_abbrev,\n>  \t\t\t\tdst_abbrev, p);\n> @@ -1054,6 +1054,7 @@ static void generate_submodule_summary(struct summary_cb *info,\n>  \tfree(displaypath);\n>  \tfree(src_abbrev);\n>  \tfree(dst_abbrev);\n> +\tstrbuf_release(&errmsg_str);\n>  }\n>  \n>  static void prepare_submodule_summary(struct summary_cb *info,\n> -- \n> 2.37.0.932.g7b7031e73bc\n\nWhat do you think of getting rid of \"char *errmsg\" altogether? We can\nreplace it with errmsg_str.buf and do the length check in\nprint_submodule_summary():\n\n  diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n  index a964dbeec3..f9f0de7e83 100644\n  --- a/builtin/submodule--helper.c\n  +++ b/builtin/submodule--helper.c\n  @@ -896,7 +896,7 @@ static void print_submodule_summary(struct summary_cb *info, char *errmsg,\n    else\n      printf(\" (%d):\\n\", total_commits);\n\n  -\tif (errmsg) {\n  +\tif (errmsg && *errmsg) {\n      printf(_(\"%s\"), errmsg);\n    } else if (total_commits > 0) {\n      struct child_process cp_log = CHILD_PROCESS_INIT;\n  @@ -932,7 +932,7 @@ static void generate_submodule_summary(struct summary_cb *info,\n  {\n    char *displaypath, *src_abbrev = NULL, *dst_abbrev;\n    int missing_src = 0, missing_dst = 0;\n  -\tchar *errmsg = NULL;\n  +\tstruct strbuf errmsg_str = STRBUF_INIT;\n    int total_commits = -1;\n\n    if (!info->cached && oideq(&p->oid_dst, null_oid())) {\n  @@ -1032,7 +1032,6 @@ static void generate_submodule_summary(struct summary_cb *info,\n      * submodule, i.e., deleted or changed to blob\n      */\n      if (S_ISGITLINK(p->mod_dst)) {\n  -\t\t\tstruct strbuf errmsg_str = STRBUF_INIT;\n        if (missing_src && missing_dst) {\n          strbuf_addf(&errmsg_str, \"  Warn: %s doesn't contain commits %s and %s\\n\",\n                displaypath, oid_to_hex(&p->oid_src),\n  @@ -1043,17 +1042,17 @@ static void generate_submodule_summary(struct summary_cb *info,\n                oid_to_hex(&p->oid_src) :\n                oid_to_hex(&p->oid_dst));\n        }\n  -\t\t\terrmsg = strbuf_detach(&errmsg_str, NULL);\n      }\n    }\n\n  -\tprint_submodule_summary(info, errmsg, total_commits,\n  +\tprint_submodule_summary(info, errmsg_str.buf, total_commits,\n          displaypath, src_abbrev,\n          dst_abbrev, p);\n\n    free(displaypath);\n    free(src_abbrev);\n    free(dst_abbrev);\n  +\tstrbuf_release(&errmsg_str);\n  }\n\n  static void prepare_submodule_summary(struct summary_cb *info,\n\n"},{"id":"459063","messageId":"kl6ltu7j4k98.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-09.11-7b36f71879e-20220713T131601Z-avarab@gmail.com","subject":"Re: [PATCH 09/11] submodule--helper: free \"char *\" in \"struct update_data\"","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-14T18:33:23Z","receivedAt":"2022-07-14T18:33:29Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Make the update_data_release() function free the \"recursive_prefix\"\n> and \"displaypath\" members when appropriate. For the former it could\n> come from either \"argv\" or from our own allocation, so we need to keep\n> track of a \"to_free\" sibling seperately.\n\nObsolete message probably? \"recursive_prefix\" no longer exists as of\ngc/submodule-use-super-prefix ;)\n\n> For \"displaypath\" it's always ours, so the \"const char *\" type was\n> wrong to begin with, it should be a \"char *\" instead.\n\nOk.\n\n> For update_submodule() we'll free() these as we go along, it's called\n> in a loop by update_submodules(), and we'll need to free the \"last\"\n> one.\n\nHm, I don't follow this part. Does \"as we go along\" mean \"as we go along\nfreeing things in update_submodules()\", or \"we'll do this later on\"?\n\nI'm assuming it's the latter since this patch only frees the \"last\" one\nand doesn't free inside of update_submodule(), but maybe it's not so\nhard to do? I think it's just:\n\n  diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n  index 0bac39880d..34b54e97d1 100644\n  --- a/builtin/submodule--helper.c\n  +++ b/builtin/submodule--helper.c\n  @@ -2560,6 +2560,7 @@ static int update_submodule(struct update_data *update_data)\n  {\n    ensure_core_worktree(update_data->sm_path);\n\n  +\tfree(update_data->displaypath);\n    update_data->displaypath = get_submodule_displaypath(\n      update_data->sm_path, update_data->prefix);\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 3 ++-\n>  1 file changed, 2 insertions(+), 1 deletion(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index 2b44f391f15..0bac39880d2 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -1949,7 +1949,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)\n>  \n>  struct update_data {\n>  \tconst char *prefix;\n> -\tconst char *displaypath;\n> +\tchar *displaypath;\n>  \tenum submodule_update_type update_default;\n>  \tstruct object_id suboid;\n>  \tstruct string_list references;\n> @@ -1987,6 +1987,7 @@ struct update_data {\n>  \n>  static void update_data_release(struct update_data *ud)\n>  {\n> +\tfree(ud->displaypath);\n>  \tmodule_list_release(&ud->list);\n>  }\n>  \n> -- \n> 2.37.0.932.g7b7031e73bc\n"},{"id":"459064","messageId":"kl6lr12n4jtk.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-11.11-475b1196a93-20220713T131601Z-avarab@gmail.com","subject":"Re: [PATCH 11/11] submodule--helper: fix \"reference\" leak is \"module_clone_data\"","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-14T18:42:47Z","receivedAt":"2022-07-14T18:42:53Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Fix leaks in the \"reference\" member of \"struct module_clone_data\" that\n> have been with us since 31224cbdc72 (clone: recursive and reference\n> option triggers submodule alternates, 2016-08-17) and\n> 8c8195e9c3e (submodule--helper: introduce add-clone subcommand,\n> 2021-07-10).\n>\n> Those commits added an xstrdup()'d member of the\n> STRING_LIST_INIT_NODUP'd \"struct string_list\". We need to free()\n> those, but not the ones we get from argv, let's make use of the \"util\"\n> member, if it has a pointer it's the pointer we'll need to free.\n\nYes, this is a clear-cut case of \"sometimes free()-able, sometimes not\"\n(the others upthread were much less clear), so the approach sounds good.\n\nWell, we could make this always free()-able with some wacky\nOPT_STRING_LIST_DUP thing, but I don't think that idea is going anywhere\n;)\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 13 +++++++++----\n>  1 file changed, 9 insertions(+), 4 deletions(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index d9fab5d77da..966f543fbd9 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -1598,6 +1598,7 @@ struct module_clone_data {\n>  static void module_clone_data_release(struct module_clone_data *cd)\n>  {\n>  \tfree(cd->path_free);\n> +\tstring_list_clear(&cd->reference, 1);\n>  }\n>  \n>  struct submodule_alternate_setup {\n> @@ -1652,7 +1653,9 @@ static int add_possible_reference_from_superproject(\n>  \n>  \t\tsm_alternate = compute_alternate_path(sb.buf, &err);\n>  \t\tif (sm_alternate) {\n> -\t\t\tstring_list_append(sas->reference, xstrdup(sb.buf));\n> +\t\t\tchar *p = strbuf_detach(&sb, NULL);\n> +\n> +\t\t\tstring_list_append(sas->reference, p)->util = p;\n>  \t\t\tfree(sm_alternate);\n>  \t\t} else {\n>  \t\t\tswitch (sas->error_mode) {\n> @@ -3194,9 +3197,11 @@ static int add_submodule(const struct add_data *add_data)\n>  \t\tclone_data.url = add_data->realrepo;\n>  \t\tclone_data.quiet = add_data->quiet;\n>  \t\tclone_data.progress = add_data->progress;\n> -\t\tif (add_data->reference_path)\n> -\t\t\tstring_list_append(&clone_data.reference,\n> -\t\t\t\t\t   xstrdup(add_data->reference_path));\n> +\t\tif (add_data->reference_path) {\n> +\t\t\tchar *p = xstrdup(add_data->reference_path);\n> +\n> +\t\t\tstring_list_append(&clone_data.reference, p)->util = p;\n> +\t\t}\n>  \t\tclone_data.dissociate = add_data->dissociate;\n>  \t\tif (add_data->depth >= 0)\n>  \t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n> -- \n> 2.37.0.932.g7b7031e73bc\n\nOk.\n"},{"id":"459106","messageId":"kl6lr12ntek5.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"220714.86v8s00y1z.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-15T00:17:14Z","receivedAt":"2022-07-15T00:17:19Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> On Wed, Jul 13 2022, Glen Choo wrote:\n> Anyway, I was curious about this so I tried the following locally:\n> \t\n> \t@@\n> \ttype T;\n> \tidentifier I;\n> \t@@\n> \t- T I;\n> \t+ T I = { 0 };\n> \t... when strict\n> \t    when != \\( I \\| &I \\)\n> \t(\n> \t- memset(&I, 0, sizeof(I));\n> \t|\n> \t- memset(&I, 0, sizeof(T));\n> \t)\n> \t\n>\n> Which aside from whitespace issues (that I've asked the cocci ML about)\n> yields a sane result...\n>> ....... If I have time I'll send that proposal to CodingGuidelines, or\n>> someone else can send it (I don't mind either way).\n\nAdding an extra cocci check sounds like a great addition alongside the\nCodingGuidelines change (automatically checking the rule is way better\nthan doing it manually of course). The fact that { 0 } is wrapped around\ntwo lines is annoying, e.g.\n\n  -\tstruct update_callback_data data;\n  +\tstruct update_callback_data data = {\n  +\t\t0\n  +\t};\n    struct rev_info rev;\n\n  -\tmemset(&data, 0, sizeof(data));\n    data.flags = flags;\n\nbut since it goes away with \"make style\", I'm tempted to say that this\ncheck is worth having. Is it too confusing to have coccinelle recommend\nsomething that we expect users to fix afterwards?\n"},{"id":"459284","messageId":"220718.86v8ruwgc6.gmgdl@evledraar.gmail.com","threadId":"58151","inReplyTo":"kl6ltu7j4k98.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH 09/11] submodule--helper: free \"char *\" in \"struct update_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-18T16:10:16Z","receivedAt":"2022-07-18T16:11:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Jul 14 2022, Glen Choo wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> Make the update_data_release() function free the \"recursive_prefix\"\n>> and \"displaypath\" members when appropriate. For the former it could\n>> come from either \"argv\" or from our own allocation, so we need to keep\n>> track of a \"to_free\" sibling seperately.\n>\n> Obsolete message probably? \"recursive_prefix\" no longer exists as of\n> gc/submodule-use-super-prefix ;)\n\nThanks, will fix!\n\n>> For \"displaypath\" it's always ours, so the \"const char *\" type was\n>> wrong to begin with, it should be a \"char *\" instead.\n>\n> Ok.\n>\n>> For update_submodule() we'll free() these as we go along, it's called\n>> in a loop by update_submodules(), and we'll need to free the \"last\"\n>> one.\n>\n> Hm, I don't follow this part. Does \"as we go along\" mean \"as we go along\n> freeing things in update_submodules()\", or \"we'll do this later on\"?\n\nYou know what? After looking at this again I have no idea what I was\ntalking about here, and I think this makes no sense... :)\n\n> I'm assuming it's the latter since this patch only frees the \"last\" one\n> and doesn't free inside of update_submodule(), but maybe it's not so\n> hard to do? I think it's just:\n>\n>   diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n>   index 0bac39880d..34b54e97d1 100644\n>   --- a/builtin/submodule--helper.c\n>   +++ b/builtin/submodule--helper.c\n>   @@ -2560,6 +2560,7 @@ static int update_submodule(struct update_data *update_data)\n>   {\n>     ensure_core_worktree(update_data->sm_path);\n>\n>   +\tfree(update_data->displaypath);\n>     update_data->displaypath = get_submodule_displaypath(\n>       update_data->sm_path, update_data->prefix);\n\nThanks, I'll fix this case in a re-roll, fixing it generally turned out\nto be a lot trickier though, but in the process I fixed a lot of the\ncontrol flow issues. Am currently running extensive tests on it.\n\nThanks a lot for this & other reviews, it's been really useful, as\nalways.\n"},{"id":"459434","messageId":"patch-v2-01.24-fcdf4a2e2d9-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 01/24] submodule--helper: replace memset() with { 0 }-initialization","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:46:52Z","receivedAt":"2022-07-19T20:47:30Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use the less verbose { 0 }-initialization syntax rather than memset()\nin builtin/submodule--helper.c, this doesn't make a difference in\nterms of behavior, but as we're about to modify adjacent code makes\nthis more consistent, and lets us avoid worrying about when the\nmemset() happens v.s. a \"goto cleanup\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 ++----\n 1 file changed, 2 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex fac52ade5e1..73717be957c 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1744,7 +1744,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n {\n \tint dissociate = 0, quiet = 0, progress = 0, require_init = 0;\n \tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n-\tstruct list_objects_filter_options filter_options;\n+\tstruct list_objects_filter_options filter_options = { 0 };\n \n \tstruct option module_clone_options[] = {\n \t\tOPT_STRING(0, \"prefix\", &clone_data.prefix,\n@@ -1786,7 +1786,6 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \t\tNULL\n \t};\n \n-\tmemset(&filter_options, 0, sizeof(filter_options));\n \targc = parse_options(argc, argv, prefix, module_clone_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n@@ -2563,7 +2562,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n {\n \tstruct pathspec pathspec;\n \tstruct update_data opt = UPDATE_DATA_INIT;\n-\tstruct list_objects_filter_options filter_options;\n+\tstruct list_objects_filter_options filter_options = { 0 };\n \tint ret;\n \n \tstruct option module_update_options[] = {\n@@ -2623,7 +2622,6 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \tupdate_clone_config_from_gitmodules(&opt.max_jobs);\n \tgit_config(git_update_clone_config, &opt.max_jobs);\n \n-\tmemset(&filter_options, 0, sizeof(filter_options));\n \targc = parse_options(argc, argv, prefix, module_update_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459436","messageId":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-00.11-00000000000-20220713T131601Z-avarab@gmail.com","subject":"[PATCH v2 00/24] submodule--helper: fix memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:46:51Z","receivedAt":"2022-07-19T20:47:31Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This series fixes all of the memory leaks in\nbuiltin/submodule--helper.c. The v1 went on top of\ngc/submodule-use-super-prefix (which in turn was on top ofmy\nab/submodule-cleanup). Both of those have landed on \"master\", so this\nis directly on master.\n\nThanks a lot to everyone who looked at this & especially to Glen Choo,\nI think this re-roll addresses all of his comments.\n\nThis is now 24 patches, up from 11, but there's not much of an\nincrease in complexity here. The growth in size is because I chased\ndown the rest of the leaks from \"submodule--helper\", and to show the\nprogression for certain fixes. E.g. 19-22/24 are all working towards\n22/24, but doing so stepwise, to make it easier to reason about each\nindividual step.\n\nThere are leaks that are left that \"submodule--helper\" runs into, but\nit's not in the submodule--helper.c code. E.g. there's one in\nsubmodule & branch.c, and another to do with the object code, etc.. I\nthought it was best to leave those for now.\n\nGlen Choo (2):\n  submodule--helper: add \"const\" to copy of \"update_data\"\n  submodule--helper: refactor \"errmsg_str\" to be a \"struct strbuf\"\n\nÆvar Arnfjörð Bjarmason (22):\n  submodule--helper: replace memset() with { 0 }-initialization\n  submodule--helper: fix a leak in \"clone_submodule\"\n  submodule--helper: fix trivial get_default_remote_submodule() leak\n  submodule--helper: fix most \"struct pathspec\" memory leaks\n  submodule--helper: \"struct pathspec\" memory leak in module_update()\n  submodule--helper: don't leak {run,capture}_command() cp.dir argument\n  submodule--helper: add and use *_release() functions\n  submodule--helper: fix \"errmsg_str\" memory leak\n  submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks\n  submodule--helper: fix a leak with repo_clear()\n  submodule--helper: fix a memory leak in get_default_remote_submodule()\n  submodule--helper: fix \"reference\" leak is \"module_clone_data\"\n  submodule--helper: fix obscure leak in module_add()\n  submodule--helper: fix a leak in module_add()\n  submodule--helper: fix a memory leak in print_status()\n  submodule--helper: free some \"displaypath\" in \"struct update_data\"\n  submodule--helper: rename \"int res\" to \"int ret\"\n  submodule--helper: add skeleton \"goto cleanup\" to update_submodule()\n  submodule--helper: don't exit() on failure, return\n  submodule--helper: free rest of \"displaypath\" in \"struct update_data\"\n  submodule--helper: fix bad config API usage\n  submodule--helper: fix a configure_added_submodule() leak\n\n builtin/submodule--helper.c        | 347 +++++++++++++++++++++--------\n t/t1500-rev-parse.sh               |   1 +\n t/t2403-worktree-move.sh           |   1 +\n t/t6008-rev-list-submodule.sh      |   1 +\n t/t6134-pathspec-in-submodule.sh   |   1 +\n t/t7412-submodule-absorbgitdirs.sh |   1 +\n t/t7413-submodule-is-active.sh     |   1 +\n t/t7414-submodule-mistakes.sh      |   2 +\n t/t7506-status-submodule.sh        |   1 +\n t/t7507-commit-verbose.sh          |   2 +\n 10 files changed, 259 insertions(+), 99 deletions(-)\n\nRange-diff against v1:\n 2:  4049362e9b4 =  1:  fcdf4a2e2d9 submodule--helper: replace memset() with { 0 }-initialization\n 3:  e5ec6945409 !  2:  130a396b837 submodule--helper: fix \"module_clone_data\" memory leaks\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    submodule--helper: fix \"module_clone_data\" memory leaks\n    +    submodule--helper: fix a leak in \"clone_submodule\"\n     \n    -    Fix memory leaks related to the \"struct module_clone_data\" by creating\n    -    a module_clone_data_release() function to go with the\n    -    MODULE_CLONE_DATA_INIT added in a98b02c1128 (submodule--helper:\n    -    refactor module_clone(), 2021-07-10).\n    +    Fix a memory leak of the \"path\" member of the \"struct\n    +    module_clone_data\" in clone_submodule(). This fixes leaks in code\n    +    added in f8eaa0ba98b (submodule--helper, module_clone: always operate\n    +    on absolute paths, 2016-03-31).\n     \n    -    The \"path\" member can come from \"argv\" (i.e. not malloc'd), or it can\n    -    be something we determine at runtime. In the latter case let's save\n    -    away a pointer to free() to avoid leaking memory.\n    +    For the \"else\" case we don't need to xstrdup() the \"clone_data->path\",\n    +    if we're not creating a new one we'll leave it to our caller to keep\n    +    track of it.\n    +\n    +    In the case of the module_clone() caller it's from \"argv\", and doesn't\n    +    need to be free'd, and in the case of the add_submodule() caller we\n    +    get a pointer to \"sm_path\", which doesn't need to be directly free'd\n    +    either.\n     \n         Fixing this leak makes several tests pass, so let's mark them as\n         passing with TEST_PASSES_SANITIZE_LEAK=true.\n    @@ Commit message\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/submodule--helper.c ##\n    -@@ builtin/submodule--helper.c: static int module_deinit(int argc, const char **argv, const char *prefix)\n    - struct module_clone_data {\n    - \tconst char *prefix;\n    - \tconst char *path;\n    -+\tchar *path_free;\n    - \tconst char *name;\n    - \tconst char *url;\n    - \tconst char *depth;\n    -@@ builtin/submodule--helper.c: struct module_clone_data {\n    - \t.single_branch = -1, \\\n    - }\n    +@@ builtin/submodule--helper.c: static int clone_submodule(struct module_clone_data *clone_data)\n    + \tchar *sm_alternate = NULL, *error_strategy = NULL;\n    + \tstruct strbuf sb = STRBUF_INIT;\n    + \tstruct child_process cp = CHILD_PROCESS_INIT;\n    ++\tchar *to_free = NULL;\n      \n    -+static void module_clone_data_release(struct module_clone_data *cd)\n    -+{\n    -+\tfree(cd->path_free);\n    -+}\n    -+\n    - struct submodule_alternate_setup {\n    - \tconst char *submodule_name;\n    - \tenum SUBMODULE_ALTERNATE_ERROR_MODE {\n    + \tsubmodule_name_to_gitdir(&sb, the_repository, clone_data->name);\n    + \tsm_gitdir = absolute_pathdup(sb.buf);\n     @@ builtin/submodule--helper.c: static int clone_submodule(struct module_clone_data *clone_data)\n      \n      \tif (!is_absolute_path(clone_data->path)) {\n      \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n     -\t\tclone_data->path = strbuf_detach(&sb, NULL);\n    -+\t\tclone_data->path = clone_data->path_free = strbuf_detach(&sb, NULL);\n    ++\t\tclone_data->path = to_free = strbuf_detach(&sb, NULL);\n      \t} else {\n     -\t\tclone_data->path = xstrdup(clone_data->path);\n    -+\t\tclone_data->path = clone_data->path_free = xstrdup(clone_data->path);\n    ++\t\tclone_data->path = clone_data->path;\n      \t}\n      \n      \tif (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)\n    -@@ builtin/submodule--helper.c: static int module_clone(int argc, const char **argv, const char *prefix)\n    - \n    - \tclone_submodule(&clone_data);\n    - \tlist_objects_filter_release(&filter_options);\n    -+\n    -+\tmodule_clone_data_release(&clone_data);\n    +@@ builtin/submodule--helper.c: static int clone_submodule(struct module_clone_data *clone_data)\n    + \tstrbuf_release(&sb);\n    + \tfree(sm_gitdir);\n    + \tfree(p);\n    ++\tfree(to_free);\n      \treturn 0;\n      }\n      \n    -@@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add_data)\n    - {\n    - \tchar *submod_gitdir_path;\n    - \tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n    -+\tint ret;\n    - \n    - \t/* perhaps the path already exists and is already a git repo, else clone it */\n    - \tif (is_directory(add_data->sm_path)) {\n    -@@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add_data)\n    - \t\tif (add_data->depth >= 0)\n    - \t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n    - \n    --\t\tif (clone_submodule(&clone_data))\n    --\t\t\treturn -1;\n    -+\t\tif (clone_submodule(&clone_data)) {\n    -+\t\t\tret = -1;\n    -+\t\t\tgoto cleanup;\n    -+\t\t}\n    +\n    + ## t/t1500-rev-parse.sh ##\n    +@@ t/t1500-rev-parse.sh: test_description='test git rev-parse'\n    + GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n    + export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n      \n    - \t\tprepare_submodule_repo_env(&cp.env);\n    - \t\tcp.git_cmd = 1;\n    -@@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add_data)\n    - \t\tif (run_command(&cp))\n    - \t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n    - \t}\n    --\treturn 0;\n    -+\tret = 0;\n    -+cleanup:\n    -+\tmodule_clone_data_release(&clone_data);\n    -+\treturn ret;\n    - }\n    ++TEST_PASSES_SANITIZE_LEAK=true\n    + . ./test-lib.sh\n      \n    - static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)\n    + test_one () {\n     \n      ## t/t6008-rev-list-submodule.sh ##\n     @@ t/t6008-rev-list-submodule.sh: test_description='git rev-list involving submodules that this repo has'\n -:  ----------- >  3:  25e17bbb058 submodule--helper: fix trivial get_default_remote_submodule() leak\n 4:  cdefd283c23 !  4:  9fb60485c3e submodule--helper: fix \"struct pathspec\" memory leaks\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    submodule--helper: fix \"struct pathspec\" memory leaks\n    +    submodule--helper: fix most \"struct pathspec\" memory leaks\n     \n         Call clear_pathspec() at the end of various functions that work with\n         and allocate a \"struct pathspec\".\n -:  ----------- >  5:  a4672aa9c94 submodule--helper: \"struct pathspec\" memory leak in module_update()\n 5:  76eab92c8b6 !  6:  7925db18b33 submodule--helper: free() leaking {run,capture}_command() argument\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    submodule--helper: free() leaking {run,capture}_command() argument\n    +    submodule--helper: don't leak {run,capture}_command() cp.dir argument\n     \n    -    Free the \"dir\" member of \"struct child_process\" that various functions\n    -    in submodule-helper.c allocate allocates with xstrdup().\n    +    Fix a memory leak in c51f8f94e5b (submodule--helper: run update\n    +    procedures from C, 2021-08-24) and 3c3558f0953 (submodule--helper: run\n    +    update using child process struct, 2022-03-15) by not allocating\n    +    memory in the first place.\n     \n    -    Since the \"dir\" argument is \"const char *\" let's keep a\n    -    \"char *to_free\" variable around for this rather than casting when we\n    -    call free().\n    +    The \"dir\" member of \"struct child_process\" will not be modified by\n    +    that API, and it's declared to be \"const char *\". So let's not\n    +    needlessly duplicate these strings.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/submodule--helper.c ##\n     @@ builtin/submodule--helper.c: static int is_tip_reachable(const char *path, struct object_id *oid)\n    - \tstruct child_process cp = CHILD_PROCESS_INIT;\n    - \tstruct strbuf rev = STRBUF_INIT;\n      \tchar *hex = oid_to_hex(oid);\n    -+\tchar *to_free;\n    -+\tint ret;\n      \n      \tcp.git_cmd = 1;\n     -\tcp.dir = xstrdup(path);\n    -+\tcp.dir = to_free = xstrdup(path);\n    ++\tcp.dir = path;\n      \tcp.no_stderr = 1;\n      \tstrvec_pushl(&cp.args, \"rev-list\", \"-n\", \"1\", hex, \"--not\", \"--all\", NULL);\n      \n    - \tprepare_submodule_repo_env(&cp.env);\n    - \n    --\tif (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len)\n    --\t\treturn 0;\n    -+\tif (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len) {\n    -+\t\tret = 0;\n    -+\t\tgoto cleanup;\n    -+\t}\n    - \n    --\treturn 1;\n    -+\tret = 1;\n    -+cleanup:\n    -+\tfree(to_free);\n    -+\treturn ret;\n    - }\n    - \n    - static int fetch_in_submodule(const char *module_path, int depth, int quiet, struct object_id *oid)\n    - {\n    - \tstruct child_process cp = CHILD_PROCESS_INIT;\n    -+\tchar *to_free;\n    -+\tint ret;\n    +@@ builtin/submodule--helper.c: static int fetch_in_submodule(const char *module_path, int depth, int quiet, str\n      \n      \tprepare_submodule_repo_env(&cp.env);\n      \tcp.git_cmd = 1;\n     -\tcp.dir = xstrdup(module_path);\n    -+\tcp.dir = to_free = xstrdup(module_path);\n    ++\tcp.dir = module_path;\n      \n      \tstrvec_push(&cp.args, \"fetch\");\n      \tif (quiet)\n    -@@ builtin/submodule--helper.c: static int fetch_in_submodule(const char *module_path, int depth, int quiet, str\n    - \t\tfree(remote);\n    - \t}\n    - \n    --\treturn run_command(&cp);\n    -+\tret = run_command(&cp);\n    -+\tfree(to_free);\n    -+\treturn ret;\n    - }\n    - \n    - static int run_update_command(struct update_data *ud, int subforce)\n    -@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)\n    - \tstruct child_process cp = CHILD_PROCESS_INIT;\n    - \tchar *oid = oid_to_hex(&ud->oid);\n    - \tint must_die_on_failure = 0;\n    -+\tchar *to_free;\n    -+\tint ret;\n    - \n    - \tswitch (ud->update_strategy.type) {\n    - \tcase SM_UPDATE_CHECKOUT:\n     @@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)\n      \t}\n      \tstrvec_push(&cp.args, oid);\n      \n     -\tcp.dir = xstrdup(ud->sm_path);\n    -+\tcp.dir = to_free = xstrdup(ud->sm_path);\n    ++\tcp.dir = ud->sm_path;\n      \tprepare_submodule_repo_env(&cp.env);\n      \tif (run_command(&cp)) {\n      \t\tswitch (ud->update_strategy.type) {\n    -@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)\n    - \t\t\texit(128);\n    - \n    - \t\t/* the command failed, but update must continue */\n    --\t\treturn 1;\n    -+\t\tret = 1;\n    -+\t\tgoto cleanup;\n    - \t}\n    - \n    --\tif (ud->quiet)\n    --\t\treturn 0;\n    -+\tif (ud->quiet) {\n    -+\t\tret = 0;\n    -+\t\tgoto cleanup;\n    -+\t}\n    - \n    - \tswitch (ud->update_strategy.type) {\n    - \tcase SM_UPDATE_CHECKOUT:\n    -@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)\n    - \t\t    submodule_strategy_to_string(&ud->update_strategy));\n    - \t}\n    - \n    --\treturn 0;\n    -+\tret = 0;\n    -+cleanup:\n    -+\tfree(to_free);\n    -+\treturn ret;\n    - }\n    - \n    - static int run_update_procedure(struct update_data *ud)\n -:  ----------- >  7:  30883f3aa01 submodule--helper: add \"const\" to copy of \"update_data\"\n 6:  b9984e982db !  8:  b7582391c91 submodule--helper: add and use *_release() functions\n    @@ Commit message\n         submodule--helper: add and use *_release() functions\n     \n         Add release functions for \"struct module_list\", \"struct\n    -    submodule_update_clone\" and \"struct update_data\". For now we're\n    -    leaving some of the \"struct update_data\" members, we'll deal with\n    -    those in a subsequent commit.\n    +    submodule_update_clone\" and \"struct update_data\".\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ builtin/submodule--helper.c: struct submodule_update_clone {\n      \n     +static void submodule_update_clone_release(struct submodule_update_clone *suc)\n     +{\n    -+\t/* Not \"update_data\", owned by update_data_release() */\n     +\tfree(suc->update_clone);\n     +\tfree(suc->failed_clones);\n     +}\n    @@ builtin/submodule--helper.c: static int update_submodules(struct update_data *up\n     @@ builtin/submodule--helper.c: static int module_update(int argc, const char **argv, const char *prefix)\n      \n      \t\tif (module_list_compute(argc, argv, opt.prefix,\n    - \t\t\t\t\t&pathspec, &list) < 0) {\n    + \t\t\t\t\t&pathspec2, &list) < 0) {\n     +\t\t\tmodule_list_release(&list);\n      \t\t\tret = 1;\n      \t\t\tgoto cleanup;\n    @@ builtin/submodule--helper.c: static int module_update(int argc, const char **arg\n     +\tupdate_data_release(&opt);\n      \tlist_objects_filter_release(&filter_options);\n      \tclear_pathspec(&pathspec);\n    - \treturn ret;\n    + \tclear_pathspec(&pathspec2);\n     @@ builtin/submodule--helper.c: static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n      \tret = 0;\n      cleanup:\n 7:  f8c20bbf266 !  9:  92737916083 submodule--helper: fix \"errmsg_str\" memory leak\n    @@\n      ## Metadata ##\n    -Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n    +Author: Glen Choo <chooglen@google.com>\n     \n      ## Commit message ##\n    -    submodule--helper: fix \"errmsg_str\" memory leak\n    +    submodule--helper: refactor \"errmsg_str\" to be a \"struct strbuf\"\n     \n    -    Fix a memory leak introduced in e83e3333b57 (submodule: port submodule\n    -    subcommand 'summary' from shell to C, 2020-08-13), to do that stop\n    -    juggling around the \"errmsg\" and \"struct strbuf\", let's instead move\n    -    the \"struct strbuf errmsg\" to the top-level.\n    +    Refactor code added in e83e3333b57 (submodule: port submodule\n    +    subcommand 'summary' from shell to C, 2020-08-13) so that \"errmsg\" and\n    +    \"errmsg_str\" are folded into one. The distinction between the empty\n    +    string and NULL is something that's tested for by\n    +    e.g. \"t/t7401-submodule-summary.sh\".\n     \n    -    Now we don't need to strbuf_detach() it anymore, but we do need to\n    -    ensure that we pass NULL to print_submodule_summary() when we have no\n    -    error message.\n    +    This is in preparation for fixing a memory leak the \"struct strbuf\" in\n    +    the pre-image.\n     \n    +    Let's also pass a \"const char *\" to print_submodule_summary(), as it\n    +    should not be modifying the \"errmsg\".\n    +\n    +    Signed-off-by: Glen Choo <chooglen@google.com>\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/submodule--helper.c ##\n    +@@ builtin/submodule--helper.c: static char *verify_submodule_committish(const char *sm_path,\n    + \treturn strbuf_detach(&result, NULL);\n    + }\n    + \n    +-static void print_submodule_summary(struct summary_cb *info, char *errmsg,\n    ++static void print_submodule_summary(struct summary_cb *info, const char *errmsg,\n    + \t\t\t\t    int total_commits, const char *displaypath,\n    + \t\t\t\t    const char *src_abbrev, const char *dst_abbrev,\n    + \t\t\t\t    struct module_cb *p)\n     @@ builtin/submodule--helper.c: static void generate_submodule_summary(struct summary_cb *info,\n      {\n      \tchar *displaypath, *src_abbrev = NULL, *dst_abbrev;\n      \tint missing_src = 0, missing_dst = 0;\n     -\tchar *errmsg = NULL;\n    -+\tchar *errmsg;\n    -+\tstruct strbuf errmsg_str = STRBUF_INIT;\n    ++\tstruct strbuf errmsg = STRBUF_INIT;\n      \tint total_commits = -1;\n      \n      \tif (!info->cached && oideq(&p->oid_dst, null_oid())) {\n    @@ builtin/submodule--helper.c: static void generate_submodule_summary(struct summa\n      \t\tif (S_ISGITLINK(p->mod_dst)) {\n     -\t\t\tstruct strbuf errmsg_str = STRBUF_INIT;\n      \t\t\tif (missing_src && missing_dst) {\n    - \t\t\t\tstrbuf_addf(&errmsg_str, \"  Warn: %s doesn't contain commits %s and %s\\n\",\n    +-\t\t\t\tstrbuf_addf(&errmsg_str, \"  Warn: %s doesn't contain commits %s and %s\\n\",\n    ++\t\t\t\tstrbuf_addf(&errmsg, \"  Warn: %s doesn't contain commits %s and %s\\n\",\n      \t\t\t\t\t    displaypath, oid_to_hex(&p->oid_src),\n    -@@ builtin/submodule--helper.c: static void generate_submodule_summary(struct summary_cb *info,\n    + \t\t\t\t\t    oid_to_hex(&p->oid_dst));\n    + \t\t\t} else {\n    +-\t\t\t\tstrbuf_addf(&errmsg_str, \"  Warn: %s doesn't contain commit %s\\n\",\n    ++\t\t\t\tstrbuf_addf(&errmsg, \"  Warn: %s doesn't contain commit %s\\n\",\n    + \t\t\t\t\t    displaypath, missing_src ?\n      \t\t\t\t\t    oid_to_hex(&p->oid_src) :\n      \t\t\t\t\t    oid_to_hex(&p->oid_dst));\n      \t\t\t}\n    @@ builtin/submodule--helper.c: static void generate_submodule_summary(struct summa\n      \t\t}\n      \t}\n      \n    -+\terrmsg = errmsg_str.len ? errmsg_str.buf : NULL;\n    - \tprint_submodule_summary(info, errmsg, total_commits,\n    - \t\t\t\tdisplaypath, src_abbrev,\n    +-\tprint_submodule_summary(info, errmsg, total_commits,\n    +-\t\t\t\tdisplaypath, src_abbrev,\n    ++\tprint_submodule_summary(info, errmsg.len ? errmsg.buf : NULL,\n    ++\t\t\t\ttotal_commits, displaypath, src_abbrev,\n      \t\t\t\tdst_abbrev, p);\n    -@@ builtin/submodule--helper.c: static void generate_submodule_summary(struct summary_cb *info,\n    - \tfree(displaypath);\n    - \tfree(src_abbrev);\n    - \tfree(dst_abbrev);\n    -+\tstrbuf_release(&errmsg_str);\n    - }\n      \n    - static void prepare_submodule_summary(struct summary_cb *info,\n    + \tfree(displaypath);\n -:  ----------- > 10:  c81a4f69179 submodule--helper: fix \"errmsg_str\" memory leak\n 8:  c8bb7bb7bb3 = 11:  c25b55c9528 submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks\n10:  e6c633256f3 ! 12:  303447bd4f5 submodule--helper: fix a leak with repo_clear()\n    @@ builtin/submodule--helper.c: static void ensure_core_worktree(const char *path)\n      }\n      \n      static const char *submodule_update_type_to_label(enum submodule_update_type type)\n    -\n    - ## t/t2403-worktree-move.sh ##\n    -@@\n    - \n    - test_description='test git worktree move, remove, lock and unlock'\n    - \n    -+TEST_PASSES_SANITIZE_LEAK=true\n    - . ./test-lib.sh\n    - \n    - test_expect_success 'setup' '\n    -\n    - ## t/t7412-submodule-absorbgitdirs.sh ##\n    -@@ t/t7412-submodule-absorbgitdirs.sh: This test verifies that `git submodue absorbgitdirs` moves a submodules git\n    - directory into the superproject.\n    - '\n    - \n    -+TEST_PASSES_SANITIZE_LEAK=true\n    - . ./test-lib.sh\n    - \n    - test_expect_success 'setup a real submodule' '\n -:  ----------- > 13:  f8ededcdf77 submodule--helper: fix a memory leak in get_default_remote_submodule()\n11:  475b1196a93 ! 14:  c7610088968 submodule--helper: fix \"reference\" leak is \"module_clone_data\"\n    @@ Commit message\n         those, but not the ones we get from argv, let's make use of the \"util\"\n         member, if it has a pointer it's the pointer we'll need to free.\n     \n    +    To fix this create a a module_clone_data_release() function to go with\n    +    the MODULE_CLONE_DATA_INIT added in a98b02c1128 (submodule--helper:\n    +    refactor module_clone(), 2021-07-10). We only need to add it to\n    +    add_submodule() to fix the leak, but let's add it to module_clone() as\n    +    well for consistency.\n    +\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/submodule--helper.c ##\n     @@ builtin/submodule--helper.c: struct module_clone_data {\n    - static void module_clone_data_release(struct module_clone_data *cd)\n    - {\n    - \tfree(cd->path_free);\n    -+\tstring_list_clear(&cd->reference, 1);\n    + \t.single_branch = -1, \\\n      }\n      \n    ++static void module_clone_data_release(struct module_clone_data *cd)\n    ++{\n    ++\tstring_list_clear(&cd->reference, 1);\n    ++}\n    ++\n      struct submodule_alternate_setup {\n    + \tconst char *submodule_name;\n    + \tenum SUBMODULE_ALTERNATE_ERROR_MODE {\n     @@ builtin/submodule--helper.c: static int add_possible_reference_from_superproject(\n      \n      \t\tsm_alternate = compute_alternate_path(sb.buf, &err);\n    @@ builtin/submodule--helper.c: static int add_possible_reference_from_superproject\n      \t\t\tfree(sm_alternate);\n      \t\t} else {\n      \t\t\tswitch (sas->error_mode) {\n    +@@ builtin/submodule--helper.c: static int module_clone(int argc, const char **argv, const char *prefix)\n    + \n    + \tclone_submodule(&clone_data);\n    + \tlist_objects_filter_release(&filter_options);\n    ++\tmodule_clone_data_release(&clone_data);\n    + \treturn 0;\n    + }\n    + \n    +@@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add_data)\n    + {\n    + \tchar *submod_gitdir_path;\n    + \tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n    ++\tint ret;\n    + \n    + \t/* perhaps the path already exists and is already a git repo, else clone it */\n    + \tif (is_directory(add_data->sm_path)) {\n     @@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add_data)\n      \t\tclone_data.url = add_data->realrepo;\n      \t\tclone_data.quiet = add_data->quiet;\n    @@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add\n      \t\tclone_data.dissociate = add_data->dissociate;\n      \t\tif (add_data->depth >= 0)\n      \t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n    + \n    +-\t\tif (clone_submodule(&clone_data))\n    +-\t\t\treturn -1;\n    ++\t\tif (clone_submodule(&clone_data)) {\n    ++\t\t\tret = -1;\n    ++\t\t\tgoto cleanup;\n    ++\t\t}\n    + \n    + \t\tprepare_submodule_repo_env(&cp.env);\n    + \t\tcp.git_cmd = 1;\n    +@@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add_data)\n    + \t\tif (run_command(&cp))\n    + \t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n    + \t}\n    +-\treturn 0;\n    ++\tret = 0;\n    ++cleanup:\n    ++\tmodule_clone_data_release(&clone_data);\n    ++\treturn ret;\n    + }\n    + \n    + static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)\n -:  ----------- > 15:  abd8e2eef3a submodule--helper: fix obscure leak in module_add()\n -:  ----------- > 16:  1f01203d154 submodule--helper: fix a leak in module_add()\n -:  ----------- > 17:  12b8a945486 submodule--helper: fix a memory leak in print_status()\n 9:  7b36f71879e ! 18:  fac2c4491f3 submodule--helper: free \"char *\" in \"struct update_data\"\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    submodule--helper: free \"char *\" in \"struct update_data\"\n    +    submodule--helper: free some \"displaypath\" in \"struct update_data\"\n     \n    -    Make the update_data_release() function free the \"recursive_prefix\"\n    -    and \"displaypath\" members when appropriate. For the former it could\n    -    come from either \"argv\" or from our own allocation, so we need to keep\n    -    track of a \"to_free\" sibling seperately.\n    +    Make the update_data_release() function free \"displaypath\" member when\n    +    appropriate. The \"displaypath\" member is always ours, the \"const\" on\n    +    the \"char *\" was wrong to begin with.\n     \n    -    For \"displaypath\" it's always ours, so the \"const char *\" type was\n    -    wrong to begin with, it should be a \"char *\" instead.\n    -\n    -    For update_submodule() we'll free() these as we go along, it's called\n    -    in a loop by update_submodules(), and we'll need to free the \"last\"\n    -    one.\n    +    This leaves a leak of \"displaypath\" in update_submodule(), which as\n    +    we'll see in subsequent commits is harder to deal with than this\n    +    trivial fix.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ builtin/submodule--helper.c: struct update_data {\n      \tmodule_list_release(&ud->list);\n      }\n      \n    +\n    + ## t/t2403-worktree-move.sh ##\n    +@@\n    + \n    + test_description='test git worktree move, remove, lock and unlock'\n    + \n    ++TEST_PASSES_SANITIZE_LEAK=true\n    + . ./test-lib.sh\n    + \n    + test_expect_success 'setup' '\n    +\n    + ## t/t7412-submodule-absorbgitdirs.sh ##\n    +@@ t/t7412-submodule-absorbgitdirs.sh: This test verifies that `git submodue absorbgitdirs` moves a submodules git\n    + directory into the superproject.\n    + '\n    + \n    ++TEST_PASSES_SANITIZE_LEAK=true\n    + . ./test-lib.sh\n    + \n    + test_expect_success 'setup a real submodule' '\n -:  ----------- > 19:  cf0c8851954 submodule--helper: rename \"int res\" to \"int ret\"\n -:  ----------- > 20:  7882e33cdca submodule--helper: add skeleton \"goto cleanup\" to update_submodule()\n 1:  f1c847ad613 ! 21:  31395a2b4f8 submodule.c: free() memory from xgetcwd()\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    submodule.c: free() memory from xgetcwd()\n    +    submodule--helper: don't exit() on failure, return\n     \n    -    Fix a memory leak in code added in bf0231c6614 (rev-parse: add\n    -    --show-superproject-working-tree, 2017-03-08), we should never have\n    -    made the result of xgetcwd() a \"const char *\", as we return a\n    -    strbuf_detach()'d value. Let's fix that and free() it when we're done\n    -    with it.\n    +    Change code downstream of module_update() to short-circuit and return\n    +    to the top-level on failure, rather than calling exit().\n     \n    -    We can't mark any tests passing passing with SANITIZE=leak using\n    -    \"TEST_PASSES_SANITIZE_LEAK=true\" as a result of this change, but\n    -    e.g. \"t/t1500-rev-parse.sh\" now gets closer to passing.\n    +    To do so we need to diligently check whether we \"must_die_on_failure\",\n    +    which is a pattern started in c51f8f94e5b (submodule--helper: run\n    +    update procedures from C, 2021-08-24), but which hadn't been completed\n    +    to the point where we could avoid calling exit() here.\n    +\n    +    This introduces no functional changes, but makes it easier to both\n    +    call these routines as a library in the future, and to avoid leaking\n    +    memory.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    - ## submodule.c ##\n    -@@ submodule.c: int get_superproject_working_tree(struct strbuf *buf)\n    + ## builtin/submodule--helper.c ##\n    +@@ builtin/submodule--helper.c: static int fetch_in_submodule(const char *module_path, int depth, int quiet, str\n    + \treturn run_command(&cp);\n    + }\n    + \n    +-static int run_update_command(struct update_data *ud, int subforce)\n    ++static int run_update_command(struct update_data *ud, int subforce,\n    ++\t\t\t      int *must_die_on_failurep)\n    + {\n      \tstruct child_process cp = CHILD_PROCESS_INIT;\n    - \tstruct strbuf sb = STRBUF_INIT;\n    - \tstruct strbuf one_up = STRBUF_INIT;\n    --\tconst char *cwd = xgetcwd();\n    -+\tchar *cwd = xgetcwd();\n    - \tint ret = 0;\n    - \tconst char *subpath;\n    - \tint code;\n    -@@ submodule.c: int get_superproject_working_tree(struct strbuf *buf)\n    - \t\tret = 1;\n    - \t\tfree(super_wt);\n    + \tchar *oid = oid_to_hex(&ud->oid);\n    +@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)\n    + \t\t\tBUG(\"unexpected update strategy type: %s\",\n    + \t\t\t    submodule_strategy_to_string(&ud->update_strategy));\n    + \t\t}\n    +-\t\tif (must_die_on_failure)\n    +-\t\t\texit(128);\n    ++\t\tif (must_die_on_failure) {\n    ++\t\t\t*must_die_on_failurep = 1;\n    ++\t\t\treturn 128;\n    ++\t\t}\n    + \n    + \t\t/* the command failed, but update must continue */\n    + \t\treturn 1;\n    +@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)\n    + \treturn 0;\n    + }\n    + \n    +-static int run_update_procedure(struct update_data *ud)\n    ++static int run_update_procedure(struct update_data *ud,\n    ++\t\t\t\tint *must_die_on_failure)\n    + {\n    + \tint subforce = is_null_oid(&ud->suboid) || ud->force;\n    + \n    +@@ builtin/submodule--helper.c: static int run_update_procedure(struct update_data *ud)\n    + \t\t\t    ud->displaypath, oid_to_hex(&ud->oid));\n    + \t}\n    + \n    +-\treturn run_update_command(ud, subforce);\n    ++\treturn run_update_command(ud, subforce, must_die_on_failure);\n    + }\n    + \n    + static const char *remote_submodule_branch(const char *path)\n    +@@ builtin/submodule--helper.c: static void update_data_to_args(struct update_data *update_data, struct strvec *\n    + \t\t\t\t    \"--no-single-branch\");\n    + }\n    + \n    +-static int update_submodule(struct update_data *update_data)\n    ++static int update_submodule(struct update_data *update_data,\n    ++\t\t\t    int *must_die_on_failure)\n    + {\n    + \tint ret;\n    + \n    +@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)\n    + \t}\n    + \n    + \tif (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) {\n    +-\t\tret = run_update_procedure(update_data);\n    +-\t\tif (ret) {\n    ++\t\tret = run_update_procedure(update_data, must_die_on_failure);\n    ++\t\tif (ret && *must_die_on_failure) {\n    ++\t\t\tgoto cleanup;\n    ++\t\t} else if (ret) {\n    + \t\t\tret = 1;\n    + \t\t\tgoto cleanup;\n    + \t\t}\n    +@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)\n    + \t\tdie_message(_(\"Failed to recurse into submodule path '%s'\"),\n    + \t\t\t    update_data->displaypath);\n    + \t\tif (ret == 128) {\n    +-\t\t\texit(ret);\n    ++\t\t\t*must_die_on_failure = 1;\n    ++\t\t\tgoto cleanup;\n    + \t\t} else if (ret) {\n    + \t\t\tret = 1;\n    + \t\t\tgoto cleanup;\n    +@@ builtin/submodule--helper.c: static int update_submodules(struct update_data *update_data)\n    + \n    + \tfor (i = 0; i < suc.update_clone_nr; i++) {\n    + \t\tstruct update_clone_data ucd = suc.update_clone[i];\n    ++\t\tint code;\n    ++\t\tint must_die_on_failure = 0;\n    + \n    + \t\toidcpy(&update_data->oid, &ucd.oid);\n    + \t\tupdate_data->just_cloned = ucd.just_cloned;\n    + \t\tupdate_data->sm_path = ucd.sub->path;\n    + \n    +-\t\tif (update_submodule(update_data))\n    +-\t\t\tres = 1;\n    ++\t\tcode = update_submodule(update_data, &must_die_on_failure);\n    ++\t\tif (code)\n    ++\t\t\tres = code;\n    ++\t\tif (must_die_on_failure)\n    ++\t\t\tgoto cleanup;\n      \t}\n    -+\tfree(cwd);\n    - \tstrbuf_release(&sb);\n      \n    - \tcode = finish_command(&cp);\n    + cleanup:\n -:  ----------- > 22:  a2168cf1378 submodule--helper: free rest of \"displaypath\" in \"struct update_data\"\n -:  ----------- > 23:  d77c6665ca9 submodule--helper: fix bad config API usage\n -:  ----------- > 24:  531db4ddae6 submodule--helper: fix a configure_added_submodule() leak\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459435","messageId":"patch-v2-02.24-130a396b837-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 02/24] submodule--helper: fix a leak in \"clone_submodule\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:46:53Z","receivedAt":"2022-07-19T20:47:32Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak of the \"path\" member of the \"struct\nmodule_clone_data\" in clone_submodule(). This fixes leaks in code\nadded in f8eaa0ba98b (submodule--helper, module_clone: always operate\non absolute paths, 2016-03-31).\n\nFor the \"else\" case we don't need to xstrdup() the \"clone_data->path\",\nif we're not creating a new one we'll leave it to our caller to keep\ntrack of it.\n\nIn the case of the module_clone() caller it's from \"argv\", and doesn't\nneed to be free'd, and in the case of the add_submodule() caller we\nget a pointer to \"sm_path\", which doesn't need to be directly free'd\neither.\n\nFixing this leak makes several tests pass, so let's mark them as\npassing with TEST_PASSES_SANITIZE_LEAK=true.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c   | 6 ++++--\n t/t1500-rev-parse.sh          | 1 +\n t/t6008-rev-list-submodule.sh | 1 +\n t/t7414-submodule-mistakes.sh | 2 ++\n t/t7506-status-submodule.sh   | 1 +\n t/t7507-commit-verbose.sh     | 2 ++\n 6 files changed, 11 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 73717be957c..4155d2450e0 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1644,6 +1644,7 @@ static int clone_submodule(struct module_clone_data *clone_data)\n \tchar *sm_alternate = NULL, *error_strategy = NULL;\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tchar *to_free = NULL;\n \n \tsubmodule_name_to_gitdir(&sb, the_repository, clone_data->name);\n \tsm_gitdir = absolute_pathdup(sb.buf);\n@@ -1651,9 +1652,9 @@ static int clone_submodule(struct module_clone_data *clone_data)\n \n \tif (!is_absolute_path(clone_data->path)) {\n \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n-\t\tclone_data->path = strbuf_detach(&sb, NULL);\n+\t\tclone_data->path = to_free = strbuf_detach(&sb, NULL);\n \t} else {\n-\t\tclone_data->path = xstrdup(clone_data->path);\n+\t\tclone_data->path = clone_data->path;\n \t}\n \n \tif (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)\n@@ -1737,6 +1738,7 @@ static int clone_submodule(struct module_clone_data *clone_data)\n \tstrbuf_release(&sb);\n \tfree(sm_gitdir);\n \tfree(p);\n+\tfree(to_free);\n \treturn 0;\n }\n \ndiff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh\nindex 1c2df08333b..0e13bcb4ebb 100755\n--- a/t/t1500-rev-parse.sh\n+++ b/t/t1500-rev-parse.sh\n@@ -4,6 +4,7 @@ test_description='test git rev-parse'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_one () {\ndiff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh\nindex 3153a0d8910..12e67e187ef 100755\n--- a/t/t6008-rev-list-submodule.sh\n+++ b/t/t6008-rev-list-submodule.sh\n@@ -8,6 +8,7 @@ test_description='git rev-list involving submodules that this repo has'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh\nindex f2e7df59cf2..3269298197c 100755\n--- a/t/t7414-submodule-mistakes.sh\n+++ b/t/t7414-submodule-mistakes.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='handling of common mistakes people may make with submodules'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'create embedded repository' '\ndiff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh\nindex 3fcb44767f5..f5426a8e589 100755\n--- a/t/t7506-status-submodule.sh\n+++ b/t/t7506-status-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='git status for submodule'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_create_repo_with_commit () {\ndiff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh\nindex ed2653d46fe..92462a22374 100755\n--- a/t/t7507-commit-verbose.sh\n+++ b/t/t7507-commit-verbose.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='verbose commit template'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n write_script \"check-for-diff\" <<\\EOF &&\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459437","messageId":"patch-v2-03.24-25e17bbb058-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 03/24] submodule--helper: fix trivial get_default_remote_submodule() leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:46:54Z","receivedAt":"2022-07-19T20:47:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in code added in 1012a5cbc3f (submodule--helper\nrun-update-procedure: learn --remote, 2022-03-04), we need to free()\nthe xstrdup()'d string. This gets e.g. t/t7419-submodule-set-branch.sh\ncloser to passing under SANITIZE=leak.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 4155d2450e0..b36919b66c5 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2473,6 +2473,8 @@ static int update_submodule(struct update_data *update_data)\n \t\tconst char *branch = remote_submodule_branch(update_data->sm_path);\n \t\tchar *remote_ref = xstrfmt(\"refs/remotes/%s/%s\", remote_name, branch);\n \n+\t\tfree(remote_name);\n+\n \t\tif (!update_data->nofetch) {\n \t\t\tif (fetch_in_submodule(update_data->sm_path, update_data->depth,\n \t\t\t\t\t      0, NULL))\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459438","messageId":"patch-v2-04.24-9fb60485c3e-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 04/24] submodule--helper: fix most \"struct pathspec\" memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:46:55Z","receivedAt":"2022-07-19T20:47:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call clear_pathspec() at the end of various functions that work with\nand allocate a \"struct pathspec\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 115 +++++++++++++++++++++++++-----------\n 1 file changed, 81 insertions(+), 34 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex b36919b66c5..28c5fdb8954 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -269,7 +269,7 @@ static char *get_up_path(const char *path)\n static int module_list(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \n \tstruct option module_list_options[] = {\n@@ -278,6 +278,7 @@ static int module_list(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"alternative anchor for relative paths\")),\n \t\tOPT_END()\n \t};\n+\tint ret;\n \n \tconst char *const git_submodule_helper_usage[] = {\n \t\tN_(\"git submodule--helper list [--prefix=<path>] [<path>...]\"),\n@@ -287,8 +288,10 @@ static int module_list(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, module_list_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n+\t\tret = 1;\n+\t\tgoto cleanup;\n+\t}\n \n \tfor (i = 0; i < list.nr; i++) {\n \t\tconst struct cache_entry *ce = list.entries[i];\n@@ -302,7 +305,10 @@ static int module_list(int argc, const char **argv, const char *prefix)\n \n \t\tfprintf(stdout, \"%s\\n\", ce->name);\n \t}\n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static void for_each_listed_submodule(const struct module_list *list,\n@@ -427,7 +433,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,\n static int module_foreach(int argc, const char **argv, const char *prefix)\n {\n \tstruct foreach_cb info = FOREACH_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \n \tstruct option module_foreach_options[] = {\n@@ -441,12 +447,15 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule foreach [--quiet] [--recursive] [--] <command>\"),\n \t\tNULL\n \t};\n+\tint ret;\n \n \targc = parse_options(argc, argv, prefix, module_foreach_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-\tif (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\tif (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0) {\n+\t\tret = 1;\n+\t\tgoto cleanup;\n+\t}\n \n \tinfo.argc = argc;\n \tinfo.argv = argv;\n@@ -454,7 +463,10 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static int starts_with_dot_slash(const char *const path)\n@@ -562,7 +574,7 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data\n static int module_init(int argc, const char **argv, const char *prefix)\n {\n \tstruct init_cb info = INIT_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \n@@ -575,12 +587,15 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule init [<options>] [<path>]\"),\n \t\tNULL\n \t};\n+\tint ret;\n \n \targc = parse_options(argc, argv, prefix, module_init_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n+\t\tret = 1;\n+\t\tgoto cleanup;\n+\t}\n \n \t/*\n \t * If there are no path args and submodule.active is set then,\n@@ -595,7 +610,10 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, init_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct status_cb {\n@@ -740,7 +758,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,\n static int module_status(int argc, const char **argv, const char *prefix)\n {\n \tstruct status_cb info = STATUS_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \n@@ -755,12 +773,15 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule status [--quiet] [--cached] [--recursive] [<path>...]\"),\n \t\tNULL\n \t};\n+\tint ret;\n \n \targc = parse_options(argc, argv, prefix, module_status_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n+\t\tret = 1;\n+\t\tgoto cleanup;\n+\t}\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -768,7 +789,10 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, status_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static int module_name(int argc, const char **argv, const char *prefix)\n@@ -1105,7 +1129,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \tstruct strvec diff_args = STRVEC_INIT;\n \tstruct rev_info rev;\n \tstruct module_cb_list list = MODULE_CB_LIST_INIT;\n-\tint ret = 0;\n+\tint ret;\n \n \tstrvec_push(&diff_args, get_diff_cmd(diff_cmd));\n \tif (info->cached)\n@@ -1145,6 +1169,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \telse\n \t\trun_diff_files(&rev, 0);\n \tprepare_submodule_summary(info, &list);\n+\tret = 0;\n cleanup:\n \tstrvec_clear(&diff_args);\n \trelease_revisions(&rev);\n@@ -1326,10 +1351,11 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data\n static int module_sync(int argc, const char **argv, const char *prefix)\n {\n \tstruct sync_cb info = SYNC_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tint recursive = 0;\n+\tint ret;\n \n \tstruct option module_sync_options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"suppress output of synchronizing submodule url\")),\n@@ -1346,8 +1372,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, module_sync_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n+\t\tret = 1;\n+\t\tgoto cleanup;\n+\t}\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -1357,7 +1385,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, sync_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct deinit_cb {\n@@ -1464,7 +1495,7 @@ static void deinit_submodule_cb(const struct cache_entry *list_item,\n static int module_deinit(int argc, const char **argv, const char *prefix)\n {\n \tstruct deinit_cb info = DEINIT_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tint force = 0;\n@@ -1481,6 +1512,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]\"),\n \t\tNULL\n \t};\n+\tint ret;\n \n \targc = parse_options(argc, argv, prefix, module_deinit_options,\n \t\t\t     git_submodule_helper_usage, 0);\n@@ -1494,8 +1526,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \tif (!argc && !all)\n \t\tdie(_(\"Use '--all' if you really want to deinitialize all submodules\"));\n \n-\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n+\t\tret = 1;\n+\t\tgoto cleanup;\n+\t}\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -1505,7 +1539,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, deinit_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct module_clone_data {\n@@ -2564,7 +2601,7 @@ static int update_submodules(struct update_data *update_data)\n \n static int module_update(int argc, const char **argv, const char *prefix)\n {\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct update_data opt = UPDATE_DATA_INIT;\n \tstruct list_objects_filter_options filter_options = { 0 };\n \tint ret;\n@@ -2643,8 +2680,8 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\topt.update_strategy.type = opt.update_default;\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {\n-\t\tlist_objects_filter_release(&filter_options);\n-\t\treturn 1;\n+\t\tret = 1;\n+\t\tgoto cleanup;\n \t}\n \n \tif (pathspec.nr)\n@@ -2655,8 +2692,10 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\tstruct init_cb info = INIT_CB_INIT;\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n-\t\t\t\t\t&pathspec, &list) < 0)\n-\t\t\treturn 1;\n+\t\t\t\t\t&pathspec, &list) < 0) {\n+\t\t\tret = 1;\n+\t\t\tgoto cleanup;\n+\t\t}\n \n \t\t/*\n \t\t * If there are no path args and submodule.active is set then,\n@@ -2673,7 +2712,9 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t}\n \n \tret = update_submodules(&opt);\n+cleanup:\n \tlist_objects_filter_release(&filter_options);\n+\tclear_pathspec(&pathspec);\n \treturn ret;\n }\n \n@@ -2757,9 +2798,10 @@ static int push_check(int argc, const char **argv, const char *prefix)\n static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tunsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;\n+\tint ret;\n \n \tstruct option embed_gitdir_options[] = {\n \t\tOPT_STRING(0, \"prefix\", &prefix,\n@@ -2778,13 +2820,18 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, embed_gitdir_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n+\t\tret = 1;\n+\t\tgoto cleanup;\n+\t}\n \n \tfor (i = 0; i < list.nr; i++)\n \t\tabsorb_git_dir_into_superproject(list.entries[i]->name, flags);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static int is_active(int argc, const char **argv, const char *prefix)\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459439","messageId":"patch-v2-05.24-a4672aa9c94-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 05/24] submodule--helper: \"struct pathspec\" memory leak in module_update()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:46:56Z","receivedAt":"2022-07-19T20:47:47Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The module_update() function calls module_list_compute() twice, which\nin turn will reset the \"struct pathspec\" passed to it. Let's instead\ntrack two of them, and clear them both.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 28c5fdb8954..7466e781e97 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2602,6 +2602,7 @@ static int update_submodules(struct update_data *update_data)\n static int module_update(int argc, const char **argv, const char *prefix)\n {\n \tstruct pathspec pathspec = { 0 };\n+\tstruct pathspec pathspec2 = { 0 };\n \tstruct update_data opt = UPDATE_DATA_INIT;\n \tstruct list_objects_filter_options filter_options = { 0 };\n \tint ret;\n@@ -2692,7 +2693,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\tstruct init_cb info = INIT_CB_INIT;\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n-\t\t\t\t\t&pathspec, &list) < 0) {\n+\t\t\t\t\t&pathspec2, &list) < 0) {\n \t\t\tret = 1;\n \t\t\tgoto cleanup;\n \t\t}\n@@ -2715,6 +2716,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n cleanup:\n \tlist_objects_filter_release(&filter_options);\n \tclear_pathspec(&pathspec);\n+\tclear_pathspec(&pathspec2);\n \treturn ret;\n }\n \n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459440","messageId":"patch-v2-06.24-7925db18b33-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 06/24] submodule--helper: don't leak {run,capture}_command() cp.dir argument","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:46:57Z","receivedAt":"2022-07-19T20:47:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak in c51f8f94e5b (submodule--helper: run update\nprocedures from C, 2021-08-24) and 3c3558f0953 (submodule--helper: run\nupdate using child process struct, 2022-03-15) by not allocating\nmemory in the first place.\n\nThe \"dir\" member of \"struct child_process\" will not be modified by\nthat API, and it's declared to be \"const char *\". So let's not\nneedlessly duplicate these strings.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 7466e781e97..3d32fdb3960 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2194,7 +2194,7 @@ static int is_tip_reachable(const char *path, struct object_id *oid)\n \tchar *hex = oid_to_hex(oid);\n \n \tcp.git_cmd = 1;\n-\tcp.dir = xstrdup(path);\n+\tcp.dir = path;\n \tcp.no_stderr = 1;\n \tstrvec_pushl(&cp.args, \"rev-list\", \"-n\", \"1\", hex, \"--not\", \"--all\", NULL);\n \n@@ -2212,7 +2212,7 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str\n \n \tprepare_submodule_repo_env(&cp.env);\n \tcp.git_cmd = 1;\n-\tcp.dir = xstrdup(module_path);\n+\tcp.dir = module_path;\n \n \tstrvec_push(&cp.args, \"fetch\");\n \tif (quiet)\n@@ -2267,7 +2267,7 @@ static int run_update_command(struct update_data *ud, int subforce)\n \t}\n \tstrvec_push(&cp.args, oid);\n \n-\tcp.dir = xstrdup(ud->sm_path);\n+\tcp.dir = ud->sm_path;\n \tprepare_submodule_repo_env(&cp.env);\n \tif (run_command(&cp)) {\n \t\tswitch (ud->update_strategy.type) {\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459441","messageId":"patch-v2-09.24-92737916083-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 09/24] submodule--helper: refactor \"errmsg_str\" to be a \"struct strbuf\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:47:00Z","receivedAt":"2022-07-19T20:47:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"From: Glen Choo <chooglen@google.com>\n\nRefactor code added in e83e3333b57 (submodule: port submodule\nsubcommand 'summary' from shell to C, 2020-08-13) so that \"errmsg\" and\n\"errmsg_str\" are folded into one. The distinction between the empty\nstring and NULL is something that's tested for by\ne.g. \"t/t7401-submodule-summary.sh\".\n\nThis is in preparation for fixing a memory leak the \"struct strbuf\" in\nthe pre-image.\n\nLet's also pass a \"const char *\" to print_submodule_summary(), as it\nshould not be modifying the \"errmsg\".\n\nSigned-off-by: Glen Choo <chooglen@google.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 14 ++++++--------\n 1 file changed, 6 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 5d26e188560..c5127c1d50e 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -874,7 +874,7 @@ static char *verify_submodule_committish(const char *sm_path,\n \treturn strbuf_detach(&result, NULL);\n }\n \n-static void print_submodule_summary(struct summary_cb *info, char *errmsg,\n+static void print_submodule_summary(struct summary_cb *info, const char *errmsg,\n \t\t\t\t    int total_commits, const char *displaypath,\n \t\t\t\t    const char *src_abbrev, const char *dst_abbrev,\n \t\t\t\t    struct module_cb *p)\n@@ -932,7 +932,7 @@ static void generate_submodule_summary(struct summary_cb *info,\n {\n \tchar *displaypath, *src_abbrev = NULL, *dst_abbrev;\n \tint missing_src = 0, missing_dst = 0;\n-\tchar *errmsg = NULL;\n+\tstruct strbuf errmsg = STRBUF_INIT;\n \tint total_commits = -1;\n \n \tif (!info->cached && oideq(&p->oid_dst, null_oid())) {\n@@ -1032,23 +1032,21 @@ static void generate_submodule_summary(struct summary_cb *info,\n \t\t * submodule, i.e., deleted or changed to blob\n \t\t */\n \t\tif (S_ISGITLINK(p->mod_dst)) {\n-\t\t\tstruct strbuf errmsg_str = STRBUF_INIT;\n \t\t\tif (missing_src && missing_dst) {\n-\t\t\t\tstrbuf_addf(&errmsg_str, \"  Warn: %s doesn't contain commits %s and %s\\n\",\n+\t\t\t\tstrbuf_addf(&errmsg, \"  Warn: %s doesn't contain commits %s and %s\\n\",\n \t\t\t\t\t    displaypath, oid_to_hex(&p->oid_src),\n \t\t\t\t\t    oid_to_hex(&p->oid_dst));\n \t\t\t} else {\n-\t\t\t\tstrbuf_addf(&errmsg_str, \"  Warn: %s doesn't contain commit %s\\n\",\n+\t\t\t\tstrbuf_addf(&errmsg, \"  Warn: %s doesn't contain commit %s\\n\",\n \t\t\t\t\t    displaypath, missing_src ?\n \t\t\t\t\t    oid_to_hex(&p->oid_src) :\n \t\t\t\t\t    oid_to_hex(&p->oid_dst));\n \t\t\t}\n-\t\t\terrmsg = strbuf_detach(&errmsg_str, NULL);\n \t\t}\n \t}\n \n-\tprint_submodule_summary(info, errmsg, total_commits,\n-\t\t\t\tdisplaypath, src_abbrev,\n+\tprint_submodule_summary(info, errmsg.len ? errmsg.buf : NULL,\n+\t\t\t\ttotal_commits, displaypath, src_abbrev,\n \t\t\t\tdst_abbrev, p);\n \n \tfree(displaypath);\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459442","messageId":"patch-v2-07.24-30883f3aa01-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 07/24] submodule--helper: add \"const\" to copy of \"update_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:46:58Z","receivedAt":"2022-07-19T20:47:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"From: Glen Choo <chooglen@google.com>\n\nAdd a \"const\" to the copy of \"struct update_data\" that's tracked by\nthe \"struct submodule_update_clone\", as it neither owns nor modifies\nit.\n\nSigned-off-by: Glen Choo <chooglen@google.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 3d32fdb3960..7e9af9999bb 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1889,7 +1889,7 @@ struct submodule_update_clone {\n \tint current;\n \n \t/* configuration parameters which are passed on to the children */\n-\tstruct update_data *update_data;\n+\tconst struct update_data *update_data;\n \n \t/* to be consumed by update_submodule() */\n \tstruct update_clone_data *update_clone;\n@@ -1974,7 +1974,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,\n \tconst char *update_string;\n \tenum submodule_update_type update_type;\n \tchar *key;\n-\tstruct update_data *ud = suc->update_data;\n+\tconst struct update_data *ud = suc->update_data;\n \tchar *displaypath = get_submodule_displaypath(ce->name, ud->prefix);\n \tstruct strbuf sb = STRBUF_INIT;\n \tint needs_cloning = 0;\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459443","messageId":"patch-v2-11.24-c25b55c9528-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 11/24] submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:47:02Z","receivedAt":"2022-07-19T20:47:54Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix leaks in \"struct module_cb_list\" and the \"struct module_cb\" which\nit contains, these fixes leaks in e83e3333b57 (submodule: port\nsubmodule subcommand 'summary' from shell to C, 2020-08-13).\n\nThe \"sm_path\" should always have been a \"char *\", not a \"const\nchar *\", we always create it with xstrdup().\n\nWe can't mark any tests passing passing with SANITIZE=leak using\n\"TEST_PASSES_SANITIZE_LEAK=true\" as a result of this change, but\n\"t7401-submodule-summary.sh\" gets closer to passing as a result of\nthis change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 20 +++++++++++++++++++-\n 1 file changed, 19 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex e51640d020c..a95231b1698 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -828,9 +828,13 @@ struct module_cb {\n \tstruct object_id oid_src;\n \tstruct object_id oid_dst;\n \tchar status;\n-\tconst char *sm_path;\n+\tchar *sm_path;\n };\n #define MODULE_CB_INIT { 0 }\n+static void module_cb_release(struct module_cb *mcb)\n+{\n+\tfree(mcb->sm_path);\n+}\n \n struct module_cb_list {\n \tstruct module_cb **entries;\n@@ -838,6 +842,19 @@ struct module_cb_list {\n };\n #define MODULE_CB_LIST_INIT { 0 }\n \n+static void module_cb_list_release(struct module_cb_list *mcbl)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < mcbl->nr; i++) {\n+\t\tstruct module_cb *mcb = mcbl->entries[i];\n+\n+\t\tmodule_cb_release(mcb);\n+\t\tfree(mcb);\n+\t}\n+\tfree(mcbl->entries);\n+}\n+\n struct summary_cb {\n \tint argc;\n \tconst char **argv;\n@@ -1181,6 +1198,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n cleanup:\n \tstrvec_clear(&diff_args);\n \trelease_revisions(&rev);\n+\tmodule_cb_list_release(&list);\n \treturn ret;\n }\n \n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459444","messageId":"patch-v2-08.24-b7582391c91-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 08/24] submodule--helper: add and use *_release() functions","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:46:59Z","receivedAt":"2022-07-19T20:47:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add release functions for \"struct module_list\", \"struct\nsubmodule_update_clone\" and \"struct update_data\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c      | 29 ++++++++++++++++++++++++++++-\n t/t6134-pathspec-in-submodule.sh |  1 +\n 2 files changed, 29 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 7e9af9999bb..5d26e188560 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -182,6 +182,11 @@ struct module_list {\n };\n #define MODULE_LIST_INIT { 0 }\n \n+static void module_list_release(struct module_list *ml)\n+{\n+\tfree(ml->entries);\n+}\n+\n static int module_list_compute(int argc, const char **argv,\n \t\t\t       const char *prefix,\n \t\t\t       struct pathspec *pathspec,\n@@ -243,7 +248,7 @@ static void module_list_active(struct module_list *list)\n \t\tactive_modules.entries[active_modules.nr++] = ce;\n \t}\n \n-\tfree(list->entries);\n+\tmodule_list_release(list);\n \t*list = active_modules;\n }\n \n@@ -307,6 +312,7 @@ static int module_list(int argc, const char **argv, const char *prefix)\n \t}\n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -465,6 +471,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -612,6 +619,7 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -791,6 +799,7 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1387,6 +1396,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1541,6 +1551,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1904,6 +1915,12 @@ struct submodule_update_clone {\n };\n #define SUBMODULE_UPDATE_CLONE_INIT { 0 }\n \n+static void submodule_update_clone_release(struct submodule_update_clone *suc)\n+{\n+\tfree(suc->update_clone);\n+\tfree(suc->failed_clones);\n+}\n+\n struct update_data {\n \tconst char *prefix;\n \tconst char *displaypath;\n@@ -1942,6 +1959,11 @@ struct update_data {\n \t.max_jobs = 1, \\\n }\n \n+static void update_data_release(struct update_data *ud)\n+{\n+\tmodule_list_release(&ud->list);\n+}\n+\n static void next_submodule_warn_missing(struct submodule_update_clone *suc,\n \t\tstruct strbuf *out, const char *displaypath)\n {\n@@ -2595,6 +2617,7 @@ static int update_submodules(struct update_data *update_data)\n \t}\n \n cleanup:\n+\tsubmodule_update_clone_release(&suc);\n \tstring_list_clear(&update_data->references, 0);\n \treturn res;\n }\n@@ -2694,6 +2717,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n \t\t\t\t\t&pathspec2, &list) < 0) {\n+\t\t\tmodule_list_release(&list);\n \t\t\tret = 1;\n \t\t\tgoto cleanup;\n \t\t}\n@@ -2710,10 +2734,12 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\t\tinfo.flags |= OPT_QUIET;\n \n \t\tfor_each_listed_submodule(&list, init_submodule_cb, &info);\n+\t\tmodule_list_release(&list);\n \t}\n \n \tret = update_submodules(&opt);\n cleanup:\n+\tupdate_data_release(&opt);\n \tlist_objects_filter_release(&filter_options);\n \tclear_pathspec(&pathspec);\n \tclear_pathspec(&pathspec2);\n@@ -2833,6 +2859,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n \tret = 0;\n cleanup:\n \tclear_pathspec(&pathspec);\n+\tmodule_list_release(&list);\n \treturn ret;\n }\n \ndiff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh\nindex 0f1cb49cedc..3a241f259de 100755\n--- a/t/t6134-pathspec-in-submodule.sh\n+++ b/t/t6134-pathspec-in-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='test case exclude pathspec'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a submodule' '\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459445","messageId":"patch-v2-10.24-c81a4f69179-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 10/24] submodule--helper: fix \"errmsg_str\" memory leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:47:01Z","receivedAt":"2022-07-19T20:47:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak introduced in e83e3333b57 (submodule: port submodule\nsubcommand 'summary' from shell to C, 2020-08-13), we sometimes append\nto the \"errmsg\", and need to free the \"struct strbuf\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex c5127c1d50e..e51640d020c 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1052,6 +1052,7 @@ static void generate_submodule_summary(struct summary_cb *info,\n \tfree(displaypath);\n \tfree(src_abbrev);\n \tfree(dst_abbrev);\n+\tstrbuf_release(&errmsg);\n }\n \n static void prepare_submodule_summary(struct summary_cb *info,\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459446","messageId":"patch-v2-13.24-f8ededcdf77-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 13/24] submodule--helper: fix a memory leak in get_default_remote_submodule()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:47:04Z","receivedAt":"2022-07-19T20:47:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak in the get_default_remote_submodule() function added\nin a77c3fcb5ec (submodule--helper: get remote names from any\nrepository, 2022-03-04), we need to repo_clear() the submodule we\ninitialize.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 339ba94e658..cef8f14efb5 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -62,9 +62,13 @@ static char *repo_get_default_remote(struct repository *repo)\n static char *get_default_remote_submodule(const char *module_path)\n {\n \tstruct repository subrepo;\n+\tchar *ret;\n \n \trepo_submodule_init(&subrepo, the_repository, module_path, null_oid());\n-\treturn repo_get_default_remote(&subrepo);\n+\tret = repo_get_default_remote(&subrepo);\n+\trepo_clear(&subrepo);\n+\n+\treturn ret;\n }\n \n static char *get_default_remote(void)\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459447","messageId":"patch-v2-12.24-303447bd4f5-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 12/24] submodule--helper: fix a leak with repo_clear()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:47:03Z","receivedAt":"2022-07-19T20:48:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call repo_clear() in ensure_core_worktree() to free the \"struct\nrepository\". Fixes a leak that's been here since\n74d4731da1f (submodule--helper: replace connect-gitdir-workingtree by\nensure-core-worktree, 2018-08-13).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex a95231b1698..339ba94e658 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2457,6 +2457,7 @@ static void ensure_core_worktree(const char *path)\n \t\tfree(abs_path);\n \t\tstrbuf_release(&sb);\n \t}\n+\trepo_clear(&subrepo);\n }\n \n static const char *submodule_update_type_to_label(enum submodule_update_type type)\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459448","messageId":"patch-v2-14.24-c7610088968-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 14/24] submodule--helper: fix \"reference\" leak is \"module_clone_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:47:05Z","receivedAt":"2022-07-19T20:48:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix leaks in the \"reference\" member of \"struct module_clone_data\" that\nhave been with us since 31224cbdc72 (clone: recursive and reference\noption triggers submodule alternates, 2016-08-17) and\n8c8195e9c3e (submodule--helper: introduce add-clone subcommand,\n2021-07-10).\n\nThose commits added an xstrdup()'d member of the\nSTRING_LIST_INIT_NODUP'd \"struct string_list\". We need to free()\nthose, but not the ones we get from argv, let's make use of the \"util\"\nmember, if it has a pointer it's the pointer we'll need to free.\n\nTo fix this create a a module_clone_data_release() function to go with\nthe MODULE_CLONE_DATA_INIT added in a98b02c1128 (submodule--helper:\nrefactor module_clone(), 2021-07-10). We only need to add it to\nadd_submodule() to fix the leak, but let's add it to module_clone() as\nwell for consistency.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 30 +++++++++++++++++++++++-------\n 1 file changed, 23 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex cef8f14efb5..eb2f09d1178 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1596,6 +1596,11 @@ struct module_clone_data {\n \t.single_branch = -1, \\\n }\n \n+static void module_clone_data_release(struct module_clone_data *cd)\n+{\n+\tstring_list_clear(&cd->reference, 1);\n+}\n+\n struct submodule_alternate_setup {\n \tconst char *submodule_name;\n \tenum SUBMODULE_ALTERNATE_ERROR_MODE {\n@@ -1648,7 +1653,9 @@ static int add_possible_reference_from_superproject(\n \n \t\tsm_alternate = compute_alternate_path(sb.buf, &err);\n \t\tif (sm_alternate) {\n-\t\t\tstring_list_append(sas->reference, xstrdup(sb.buf));\n+\t\t\tchar *p = strbuf_detach(&sb, NULL);\n+\n+\t\t\tstring_list_append(sas->reference, p)->util = p;\n \t\t\tfree(sm_alternate);\n \t\t} else {\n \t\t\tswitch (sas->error_mode) {\n@@ -1872,6 +1879,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \n \tclone_submodule(&clone_data);\n \tlist_objects_filter_release(&filter_options);\n+\tmodule_clone_data_release(&clone_data);\n \treturn 0;\n }\n \n@@ -3118,6 +3126,7 @@ static int add_submodule(const struct add_data *add_data)\n {\n \tchar *submod_gitdir_path;\n \tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n+\tint ret;\n \n \t/* perhaps the path already exists and is already a git repo, else clone it */\n \tif (is_directory(add_data->sm_path)) {\n@@ -3172,15 +3181,19 @@ static int add_submodule(const struct add_data *add_data)\n \t\tclone_data.url = add_data->realrepo;\n \t\tclone_data.quiet = add_data->quiet;\n \t\tclone_data.progress = add_data->progress;\n-\t\tif (add_data->reference_path)\n-\t\t\tstring_list_append(&clone_data.reference,\n-\t\t\t\t\t   xstrdup(add_data->reference_path));\n+\t\tif (add_data->reference_path) {\n+\t\t\tchar *p = xstrdup(add_data->reference_path);\n+\n+\t\t\tstring_list_append(&clone_data.reference, p)->util = p;\n+\t\t}\n \t\tclone_data.dissociate = add_data->dissociate;\n \t\tif (add_data->depth >= 0)\n \t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n \n-\t\tif (clone_submodule(&clone_data))\n-\t\t\treturn -1;\n+\t\tif (clone_submodule(&clone_data)) {\n+\t\t\tret = -1;\n+\t\t\tgoto cleanup;\n+\t\t}\n \n \t\tprepare_submodule_repo_env(&cp.env);\n \t\tcp.git_cmd = 1;\n@@ -3199,7 +3212,10 @@ static int add_submodule(const struct add_data *add_data)\n \t\tif (run_command(&cp))\n \t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n \t}\n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tmodule_clone_data_release(&clone_data);\n+\treturn ret;\n }\n \n static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459449","messageId":"patch-v2-15.24-abd8e2eef3a-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 15/24] submodule--helper: fix obscure leak in module_add()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:47:06Z","receivedAt":"2022-07-19T20:48:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix an obscure leak in module_add()< if the \"git add\" command we were\npiping to failed we'd fail to strbuf_release(&sb). This fixes a leak\nintroduced in a6226fd772b (submodule--helper: convert the bulk of\ncmd_add() to C, 2021-08-10).\n\nIn fixing it move to a \"goto cleanup\" pattern, and since we need to\nintroduce a \"ret\" variable to do that let's also get rid of the\nintermediate \"exit_code\" variable. The initialization to \"-1\" in\na6226fd772b has always been redundant, we'd only use the \"exit_code\"\nvalue after assigning the return value of pipe_command() to it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 20 +++++++++++---------\n 1 file changed, 11 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex eb2f09d1178..46685bf0610 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3355,6 +3355,8 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \tint force = 0, quiet = 0, progress = 0, dissociate = 0;\n \tstruct add_data add_data = ADD_DATA_INIT;\n \tchar *to_free = NULL;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint ret;\n \n \tstruct option options[] = {\n \t\tOPT_STRING('b', \"branch\", &add_data.branch, N_(\"branch\"),\n@@ -3426,20 +3428,16 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \tdie_on_repo_without_commits(add_data.sm_path);\n \n \tif (!force) {\n-\t\tint exit_code = -1;\n-\t\tstruct strbuf sb = STRBUF_INIT;\n \t\tstruct child_process cp = CHILD_PROCESS_INIT;\n \t\tcp.git_cmd = 1;\n \t\tcp.no_stdout = 1;\n \t\tstrvec_pushl(&cp.args, \"add\", \"--dry-run\", \"--ignore-missing\",\n \t\t\t     \"--no-warn-embedded-repo\", add_data.sm_path, NULL);\n-\t\tif ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {\n+\t\tif ((ret = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {\n \t\t\tstrbuf_complete_line(&sb);\n \t\t\tfputs(sb.buf, stderr);\n-\t\t\tfree(add_data.sm_path);\n-\t\t\treturn exit_code;\n+\t\t\tgoto cleanup;\n \t\t}\n-\t\tstrbuf_release(&sb);\n \t}\n \n \tif(!add_data.sm_name)\n@@ -3455,14 +3453,18 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \tadd_data.dissociate = !!dissociate;\n \n \tif (add_submodule(&add_data)) {\n-\t\tfree(add_data.sm_path);\n-\t\treturn 1;\n+\t\tret = 1;\n+\t\tgoto cleanup;\n \t}\n \tconfigure_added_submodule(&add_data);\n+\n+\tret = 0;\n+cleanup:\n \tfree(add_data.sm_path);\n \tfree(to_free);\n+\tstrbuf_release(&sb);\n \n-\treturn 0;\n+\treturn ret;\n }\n \n #define SUPPORT_SUPER_PREFIX (1<<0)\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459450","messageId":"patch-v2-17.24-12b8a945486-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 17/24] submodule--helper: fix a memory leak in print_status()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:47:08Z","receivedAt":"2022-07-19T20:48:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in print_status(), the compute_rev_name() function\nimplemented in this file will return a strbuf_detach()'d value, or\nNULL.\n\nThis leak has existed since this code was added in\na9f8a37584a (submodule: port submodule subcommand 'status' from shell\nto C, 2017-10-06), but in 0b5e2ea7cf3 (submodule--helper: don't print\nnull in 'submodule status', 2018-04-18) we added a \"const\"\nintermediate variable for the return value, that \"const\" should be\nremoved.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 642595cc3f4..75e842af5ca 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -643,10 +643,11 @@ static void print_status(unsigned int flags, char state, const char *path,\n \tprintf(\"%c%s %s\", state, oid_to_hex(oid), displaypath);\n \n \tif (state == ' ' || state == '+') {\n-\t\tconst char *name = compute_rev_name(path, oid_to_hex(oid));\n+\t\tchar *name = compute_rev_name(path, oid_to_hex(oid));\n \n \t\tif (name)\n \t\t\tprintf(\" (%s)\", name);\n+\t\tfree(name);\n \t}\n \n \tprintf(\"\\n\");\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459451","messageId":"patch-v2-16.24-1f01203d154-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 16/24] submodule--helper: fix a leak in module_add()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:47:07Z","receivedAt":"2022-07-19T20:48:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in module_path(), since a6226fd772b (submodule--helper:\nconvert the bulk of cmd_add() to C, 2021-08-10), we've been freeing\nadd_data.sm_path, but in this case we clobbered it, and didn't free\nthe value we clobbered.\n\nThis makes test 28 of \"t/t7400-submodule-basic.sh\" (\"submodule add in\nsubdirectory\") pass when we're compiled with SANITIZE=leak..\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 7 +++++--\n 1 file changed, 5 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 46685bf0610..642595cc3f4 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3398,8 +3398,11 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \telse\n \t\tadd_data.sm_path = xstrdup(argv[1]);\n \n-\tif (prefix && *prefix && !is_absolute_path(add_data.sm_path))\n-\t\tadd_data.sm_path = xstrfmt(\"%s%s\", prefix, add_data.sm_path);\n+\tif (prefix && *prefix && !is_absolute_path(add_data.sm_path)) {\n+\t\tchar *sm_path = add_data.sm_path;\n+\t\tadd_data.sm_path = xstrfmt(\"%s%s\", prefix, sm_path);\n+\t\tfree(sm_path);\n+\t}\n \n \tif (starts_with_dot_dot_slash(add_data.repo) ||\n \t    starts_with_dot_slash(add_data.repo)) {\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459452","messageId":"patch-v2-18.24-fac2c4491f3-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 18/24] submodule--helper: free some \"displaypath\" in \"struct update_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:47:09Z","receivedAt":"2022-07-19T20:48:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Make the update_data_release() function free \"displaypath\" member when\nappropriate. The \"displaypath\" member is always ours, the \"const\" on\nthe \"char *\" was wrong to begin with.\n\nThis leaves a leak of \"displaypath\" in update_submodule(), which as\nwe'll see in subsequent commits is harder to deal with than this\ntrivial fix.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c        | 3 ++-\n t/t2403-worktree-move.sh           | 1 +\n t/t7412-submodule-absorbgitdirs.sh | 1 +\n 3 files changed, 4 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 75e842af5ca..dc27e28e98c 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1953,7 +1953,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)\n \n struct update_data {\n \tconst char *prefix;\n-\tconst char *displaypath;\n+\tchar *displaypath;\n \tenum submodule_update_type update_default;\n \tstruct object_id suboid;\n \tstruct string_list references;\n@@ -1991,6 +1991,7 @@ struct update_data {\n \n static void update_data_release(struct update_data *ud)\n {\n+\tfree(ud->displaypath);\n \tmodule_list_release(&ud->list);\n }\n \ndiff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh\nindex a4e1a178e0a..1168e9f9982 100755\n--- a/t/t2403-worktree-move.sh\n+++ b/t/t2403-worktree-move.sh\n@@ -2,6 +2,7 @@\n \n test_description='test git worktree move, remove, lock and unlock'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh\nindex 1cfa150768d..2859695c6d2 100755\n--- a/t/t7412-submodule-absorbgitdirs.sh\n+++ b/t/t7412-submodule-absorbgitdirs.sh\n@@ -6,6 +6,7 @@ This test verifies that `git submodue absorbgitdirs` moves a submodules git\n directory into the superproject.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a real submodule' '\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459453","messageId":"patch-v2-19.24-cf0c8851954-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 19/24] submodule--helper: rename \"int res\" to \"int ret\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:47:10Z","receivedAt":"2022-07-19T20:48:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the \"res\" variable added in b3c5f5cb048 (submodule: move core\ncmd_update() logic to C, 2022-03-15) to \"ret\", which is the convention\nin the rest of this file. Subsequent commits will change this code to\na \"goto cleanup\" pattern, let's have the post image look consistent\nwith the rest.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 12 ++++++------\n 1 file changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex dc27e28e98c..900f89f549d 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2587,7 +2587,7 @@ static int update_submodule(struct update_data *update_data)\n \tif (update_data->recursive) {\n \t\tstruct child_process cp = CHILD_PROCESS_INIT;\n \t\tstruct update_data next = *update_data;\n-\t\tint res;\n+\t\tint ret;\n \n \t\tnext.prefix = NULL;\n \t\toidcpy(&next.oid, null_oid());\n@@ -2599,14 +2599,14 @@ static int update_submodule(struct update_data *update_data)\n \t\tupdate_data_to_args(&next, &cp.args);\n \n \t\t/* die() if child process die()'d */\n-\t\tres = run_command(&cp);\n-\t\tif (!res)\n+\t\tret = run_command(&cp);\n+\t\tif (!ret)\n \t\t\treturn 0;\n \t\tdie_message(_(\"Failed to recurse into submodule path '%s'\"),\n \t\t\t    update_data->displaypath);\n-\t\tif (res == 128)\n-\t\t\texit(res);\n-\t\telse if (res)\n+\t\tif (ret == 128)\n+\t\t\texit(ret);\n+\t\telse if (ret)\n \t\t\treturn 1;\n \t}\n \n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459454","messageId":"patch-v2-20.24-7882e33cdca-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 20/24] submodule--helper: add skeleton \"goto cleanup\" to update_submodule()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:47:11Z","receivedAt":"2022-07-19T20:48:12Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a skeleton \"goto cleanup\" pattern to update_submodule(), rather\nthan having branches in it \"return\". This is in preparation for doing\nsomething useful with the \"cleanup\" label, but for now we're using it\nas the equivalent of a \"done\" label.\n\nThe \"exit()\" branch is not handled yet, and neither is the exit() that\nrun_update_procedure() might invoke. That'll be handled in a\nsubsequent commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 27 ++++++++++++++++++---------\n 1 file changed, 18 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 900f89f549d..3b0f46ad3f6 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2544,6 +2544,8 @@ static void update_data_to_args(struct update_data *update_data, struct strvec *\n \n static int update_submodule(struct update_data *update_data)\n {\n+\tint ret;\n+\n \tensure_core_worktree(update_data->sm_path);\n \n \tupdate_data->displaypath = get_submodule_displaypath(\n@@ -2580,14 +2582,17 @@ static int update_submodule(struct update_data *update_data)\n \t\tfree(remote_ref);\n \t}\n \n-\tif (!oideq(&update_data->oid, &update_data->suboid) || update_data->force)\n-\t\tif (run_update_procedure(update_data))\n-\t\t\treturn 1;\n+\tif (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) {\n+\t\tret = run_update_procedure(update_data);\n+\t\tif (ret) {\n+\t\t\tret = 1;\n+\t\t\tgoto cleanup;\n+\t\t}\n+\t}\n \n \tif (update_data->recursive) {\n \t\tstruct child_process cp = CHILD_PROCESS_INIT;\n \t\tstruct update_data next = *update_data;\n-\t\tint ret;\n \n \t\tnext.prefix = NULL;\n \t\toidcpy(&next.oid, null_oid());\n@@ -2601,16 +2606,20 @@ static int update_submodule(struct update_data *update_data)\n \t\t/* die() if child process die()'d */\n \t\tret = run_command(&cp);\n \t\tif (!ret)\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\tdie_message(_(\"Failed to recurse into submodule path '%s'\"),\n \t\t\t    update_data->displaypath);\n-\t\tif (ret == 128)\n+\t\tif (ret == 128) {\n \t\t\texit(ret);\n-\t\telse if (ret)\n-\t\t\treturn 1;\n+\t\t} else if (ret) {\n+\t\t\tret = 1;\n+\t\t\tgoto cleanup;\n+\t\t}\n \t}\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\treturn ret;\n }\n \n static int update_submodules(struct update_data *update_data)\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459455","messageId":"patch-v2-21.24-31395a2b4f8-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 21/24] submodule--helper: don't exit() on failure, return","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:47:12Z","receivedAt":"2022-07-19T20:48:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change code downstream of module_update() to short-circuit and return\nto the top-level on failure, rather than calling exit().\n\nTo do so we need to diligently check whether we \"must_die_on_failure\",\nwhich is a pattern started in c51f8f94e5b (submodule--helper: run\nupdate procedures from C, 2021-08-24), but which hadn't been completed\nto the point where we could avoid calling exit() here.\n\nThis introduces no functional changes, but makes it easier to both\ncall these routines as a library in the future, and to avoid leaking\nmemory.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 35 ++++++++++++++++++++++++-----------\n 1 file changed, 24 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 3b0f46ad3f6..707c5027961 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2282,7 +2282,8 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str\n \treturn run_command(&cp);\n }\n \n-static int run_update_command(struct update_data *ud, int subforce)\n+static int run_update_command(struct update_data *ud, int subforce,\n+\t\t\t      int *must_die_on_failurep)\n {\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tchar *oid = oid_to_hex(&ud->oid);\n@@ -2344,8 +2345,10 @@ static int run_update_command(struct update_data *ud, int subforce)\n \t\t\tBUG(\"unexpected update strategy type: %s\",\n \t\t\t    submodule_strategy_to_string(&ud->update_strategy));\n \t\t}\n-\t\tif (must_die_on_failure)\n-\t\t\texit(128);\n+\t\tif (must_die_on_failure) {\n+\t\t\t*must_die_on_failurep = 1;\n+\t\t\treturn 128;\n+\t\t}\n \n \t\t/* the command failed, but update must continue */\n \t\treturn 1;\n@@ -2379,7 +2382,8 @@ static int run_update_command(struct update_data *ud, int subforce)\n \treturn 0;\n }\n \n-static int run_update_procedure(struct update_data *ud)\n+static int run_update_procedure(struct update_data *ud,\n+\t\t\t\tint *must_die_on_failure)\n {\n \tint subforce = is_null_oid(&ud->suboid) || ud->force;\n \n@@ -2406,7 +2410,7 @@ static int run_update_procedure(struct update_data *ud)\n \t\t\t    ud->displaypath, oid_to_hex(&ud->oid));\n \t}\n \n-\treturn run_update_command(ud, subforce);\n+\treturn run_update_command(ud, subforce, must_die_on_failure);\n }\n \n static const char *remote_submodule_branch(const char *path)\n@@ -2542,7 +2546,8 @@ static void update_data_to_args(struct update_data *update_data, struct strvec *\n \t\t\t\t    \"--no-single-branch\");\n }\n \n-static int update_submodule(struct update_data *update_data)\n+static int update_submodule(struct update_data *update_data,\n+\t\t\t    int *must_die_on_failure)\n {\n \tint ret;\n \n@@ -2583,8 +2588,10 @@ static int update_submodule(struct update_data *update_data)\n \t}\n \n \tif (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) {\n-\t\tret = run_update_procedure(update_data);\n-\t\tif (ret) {\n+\t\tret = run_update_procedure(update_data, must_die_on_failure);\n+\t\tif (ret && *must_die_on_failure) {\n+\t\t\tgoto cleanup;\n+\t\t} else if (ret) {\n \t\t\tret = 1;\n \t\t\tgoto cleanup;\n \t\t}\n@@ -2610,7 +2617,8 @@ static int update_submodule(struct update_data *update_data)\n \t\tdie_message(_(\"Failed to recurse into submodule path '%s'\"),\n \t\t\t    update_data->displaypath);\n \t\tif (ret == 128) {\n-\t\t\texit(ret);\n+\t\t\t*must_die_on_failure = 1;\n+\t\t\tgoto cleanup;\n \t\t} else if (ret) {\n \t\t\tret = 1;\n \t\t\tgoto cleanup;\n@@ -2648,13 +2656,18 @@ static int update_submodules(struct update_data *update_data)\n \n \tfor (i = 0; i < suc.update_clone_nr; i++) {\n \t\tstruct update_clone_data ucd = suc.update_clone[i];\n+\t\tint code;\n+\t\tint must_die_on_failure = 0;\n \n \t\toidcpy(&update_data->oid, &ucd.oid);\n \t\tupdate_data->just_cloned = ucd.just_cloned;\n \t\tupdate_data->sm_path = ucd.sub->path;\n \n-\t\tif (update_submodule(update_data))\n-\t\t\tres = 1;\n+\t\tcode = update_submodule(update_data, &must_die_on_failure);\n+\t\tif (code)\n+\t\t\tres = code;\n+\t\tif (must_die_on_failure)\n+\t\t\tgoto cleanup;\n \t}\n \n cleanup:\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459456","messageId":"patch-v2-22.24-a2168cf1378-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 22/24] submodule--helper: free rest of \"displaypath\" in \"struct update_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:47:13Z","receivedAt":"2022-07-19T20:48:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in code added in c51f8f94e5b (submodule--helper: run update\nprocedures from C, 2021-08-24), we clobber the \"displaypath\" member of\nthe passed-in \"struct update_data\" both so that die() messages in this\nupdate_submodule() function itself can use it, and for the\nrun_update_procedure() called within this function.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 707c5027961..37d414258cc 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2550,10 +2550,11 @@ static int update_submodule(struct update_data *update_data,\n \t\t\t    int *must_die_on_failure)\n {\n \tint ret;\n+\tchar *to_free, *restore = update_data->displaypath;\n \n \tensure_core_worktree(update_data->sm_path);\n \n-\tupdate_data->displaypath = get_submodule_displaypath(\n+\tupdate_data->displaypath = to_free = get_submodule_displaypath(\n \t\tupdate_data->sm_path, update_data->prefix);\n \n \tdetermine_submodule_update_strategy(the_repository, update_data->just_cloned,\n@@ -2627,6 +2628,9 @@ static int update_submodule(struct update_data *update_data,\n \n \tret = 0;\n cleanup:\n+\tfree(to_free);\n+\tupdate_data->displaypath = restore;\n+\n \treturn ret;\n }\n \n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459457","messageId":"patch-v2-23.24-d77c6665ca9-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 23/24] submodule--helper: fix bad config API usage","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:47:14Z","receivedAt":"2022-07-19T20:48:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix bad config API usage added in a452128a36c (submodule--helper:\nintroduce add-config subcommand, 2021-08-06). After\ngit_config_get_string() returns successfully we know the \"char **dest\"\nwill be non-NULL.\n\nA coccinelle patch that transforms this turns up a couple of other\nsuch issues, one in fetch-pack.c, and another in upload-pack.c:\n\n\t@@\n\tidentifier F =~ \"^(repo|git)_config_get_string(_tmp)?$\";\n\tidentifier V;\n\t@@\n\t  !F(..., &V)\n\t- && (V)\n\nBut let's focus narrowly on submodule--helper for now, we can fix\nthose some other time.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 37d414258cc..a944f6fa442 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3309,7 +3309,7 @@ static void configure_added_submodule(struct add_data *add_data)\n \t * is_submodule_active(), since that function needs to find\n \t * out the value of \"submodule.active\" again anyway.\n \t */\n-\tif (!git_config_get_string(\"submodule.active\", &val) && val) {\n+\tif (!git_config_get_string(\"submodule.active\", &val)) {\n \t\t/*\n \t\t * If the submodule being added isn't already covered by the\n \t\t * current configured pathspec, set the submodule's active flag\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459458","messageId":"patch-v2-24.24-531db4ddae6-20220719T204458Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v2 24/24] submodule--helper: fix a configure_added_submodule() leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-19T20:47:15Z","receivedAt":"2022-07-19T20:48:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix config API a memory leak added in a452128a36c (submodule--helper:\nintroduce add-config subcommand, 2021-08-06) by using the *_tmp()\nvariant of git_config_get_string().\n\nIn this case we're only checking whether\nthe (repo|git)_config_get_string() call is telling us that the\n\"submodule.active\" key exists.\n\nAs with the preceding commit we'll find many other such patterns in\nthe codebase if we go fishing. E.g. \"git gc\" leaks in the code added\nin 61f7a383d3b (maintenance: use 'incremental' strategy by default,\n2020-10-15). Similar code in \"git gc\" added in\nb08ff1fee00 (maintenance: add --schedule option and config,\n2020-09-11) doesn't leak, but we could avoid the malloc() & free() in\nthat case.\n\nA coccinelle rule to find those would find and fix some leaks, and\ncases where we're doing needless malloc() + free()'s but only care\nabout the key existence, or are copying\nthe (repo|git)_config_get_string() return value right away.\n\nBut as with the preceding commit let's punt on all of that for now,\nand just narrowly fix this specific case in submodule--helper.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c    | 4 ++--\n t/t7413-submodule-is-active.sh | 1 +\n 2 files changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex a944f6fa442..09b8a1c2947 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3264,7 +3264,7 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con\n static void configure_added_submodule(struct add_data *add_data)\n {\n \tchar *key;\n-\tchar *val = NULL;\n+\tconst char *val;\n \tstruct child_process add_submod = CHILD_PROCESS_INIT;\n \tstruct child_process add_gitmodules = CHILD_PROCESS_INIT;\n \n@@ -3309,7 +3309,7 @@ static void configure_added_submodule(struct add_data *add_data)\n \t * is_submodule_active(), since that function needs to find\n \t * out the value of \"submodule.active\" again anyway.\n \t */\n-\tif (!git_config_get_string(\"submodule.active\", &val)) {\n+\tif (!git_config_get_string_tmp(\"submodule.active\", &val)) {\n \t\t/*\n \t\t * If the submodule being added isn't already covered by the\n \t\t * current configured pathspec, set the submodule's active flag\ndiff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh\nindex c8e7e983317..c4c1f86f3d2 100755\n--- a/t/t7413-submodule-is-active.sh\n+++ b/t/t7413-submodule-is-active.sh\n@@ -6,6 +6,7 @@ This test verifies that `git submodue--helper is-active` correctly identifies\n submodules which are \"active\" and interesting to the user.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n-- \n2.37.1.1062.g385eac7fccf\n\n"},{"id":"459472","messageId":"xmqqwnc894su.fsf@gitster.g","threadId":"58151","inReplyTo":"patch-v2-01.24-fcdf4a2e2d9-20220719T204458Z-avarab@gmail.com","subject":"Re: [PATCH v2 01/24] submodule--helper: replace memset() with { 0 }-initialization","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-19T21:21:53Z","receivedAt":"2022-07-19T21:21:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Use the less verbose { 0 }-initialization syntax rather than memset()\n> in builtin/submodule--helper.c, this doesn't make a difference in\n> terms of behavior, but as we're about to modify adjacent code makes\n> this more consistent, and lets us avoid worrying about when the\n> memset() happens v.s. a \"goto cleanup\".\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 6 ++----\n>  1 file changed, 2 insertions(+), 4 deletions(-)\n\nThese four hunks form two pairs of two hunks, one declaring (and in\nthe new code, initializing) a variable, the other clearing (and in\nthe new code, there is no need to) the variable.\n\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index fac52ade5e1..73717be957c 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -1744,7 +1744,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n>  {\n>  \tint dissociate = 0, quiet = 0, progress = 0, require_init = 0;\n>  \tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n> -\tstruct list_objects_filter_options filter_options;\n> +\tstruct list_objects_filter_options filter_options = { 0 };\n>  \n>  \tstruct option module_clone_options[] = {\n>  \t\tOPT_STRING(0, \"prefix\", &clone_data.prefix,\n> @@ -1786,7 +1786,6 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n>  \t\tNULL\n>  \t};\n>  \n> -\tmemset(&filter_options, 0, sizeof(filter_options));\n>  \targc = parse_options(argc, argv, prefix, module_clone_options,\n>  \t\t\t     git_submodule_helper_usage, 0);\n> @@ -2563,7 +2562,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n>  {\n>  \tstruct pathspec pathspec;\n>  \tstruct update_data opt = UPDATE_DATA_INIT;\n> -\tstruct list_objects_filter_options filter_options;\n> +\tstruct list_objects_filter_options filter_options = { 0 };\n>  \tint ret;\n>  \n>  \tstruct option module_update_options[] = {\n> @@ -2623,7 +2622,6 @@ static int module_update(int argc, const char **argv, const char *prefix)\n>  \tupdate_clone_config_from_gitmodules(&opt.max_jobs);\n>  \tgit_config(git_update_clone_config, &opt.max_jobs);\n>  \n> -\tmemset(&filter_options, 0, sizeof(filter_options));\n>  \targc = parse_options(argc, argv, prefix, module_update_options,\n>  \t\t\t     git_submodule_helper_usage, 0);\n\nThe fact that this patch is harder to read without wider context\ntells us that these two changes are good idea.  Absolutely nothing\nhappens in the first function between the declaration of the\nvariable and memset() but it is hard to see in a patch because they\nare so far apart.  In the second function, some things happen but\nthey are not using the variable before they get initialized, but\nagain, it is hard to see in a patch without wider context.\n\nThe post-image of this patch allows readers easily to see that\nnobody uses these variables without initialization, which is a great\nimprovement.\n\nThanks, will queue.\n"},{"id":"459473","messageId":"xmqqpmi094cs.fsf@gitster.g","threadId":"58151","inReplyTo":"patch-v2-02.24-130a396b837-20220719T204458Z-avarab@gmail.com","subject":"Re: [PATCH v2 02/24] submodule--helper: fix a leak in \"clone_submodule\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-19T21:31:31Z","receivedAt":"2022-07-19T21:31:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index 73717be957c..4155d2450e0 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -1644,6 +1644,7 @@ static int clone_submodule(struct module_clone_data *clone_data)\n>  \tchar *sm_alternate = NULL, *error_strategy = NULL;\n>  \tstruct strbuf sb = STRBUF_INIT;\n>  \tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\tchar *to_free = NULL;\n>  \n>  \tsubmodule_name_to_gitdir(&sb, the_repository, clone_data->name);\n>  \tsm_gitdir = absolute_pathdup(sb.buf);\n> @@ -1651,9 +1652,9 @@ static int clone_submodule(struct module_clone_data *clone_data)\n>  \n>  \tif (!is_absolute_path(clone_data->path)) {\n>  \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n> -\t\tclone_data->path = strbuf_detach(&sb, NULL);\n> +\t\tclone_data->path = to_free = strbuf_detach(&sb, NULL);\n>  \t} else {\n> -\t\tclone_data->path = xstrdup(clone_data->path);\n> +\t\tclone_data->path = clone_data->path;\n>  \t}\n\nWTH?  Shouldn't the entire else-clause just go?\n\n>  \n>  \tif (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)\n> @@ -1737,6 +1738,7 @@ static int clone_submodule(struct module_clone_data *clone_data)\n>  \tstrbuf_release(&sb);\n>  \tfree(sm_gitdir);\n>  \tfree(p);\n> +\tfree(to_free);\n>  \treturn 0;\n>  }\n\nThe caller passes clone_data to us, we may have stuffed an allocated\npiece of memory in there when we had to make it absolute, and we\nfree it but let the clone_data structure smuggle the now-stale\npointer out of the function, so that the caller may be able to abuse\nit?\n\nThat leaves a bad taste in my mouth.  Doesn't it in yours?\n\nIf the caller is *NOT* allowed to rely on the value in\nclone_data->path after we return, perhaps\n\n+\tfree(to_free);\n+\tclone_data->path = NULL;\n\nBut stepping back a bit, would it make more sense to introduce a\nlocal variable \"path\" and leave clone_data->path alone, after we\ndecide to either compute an absolute path out of it, or we decide\nto use the path as is, i.e.\n\n\tif (!is_absolute_path(...)) {\n\t\t...\n\t\tto_free = path = strbuf_detach(&sb, NULL);\n\t} ... {\n\t\tpath = clone_data->path;\n\t\tto_free = NULL;\n\t}\n\nand after that, never use clone_data->path but work solely on the\nlocal \"path\"?  A quick scan tells me that no line in the rest of the\nfunction passes the whole clone_data to other helpers, so it may just\nbe a matter of s/clone_data->path/path/g perhaps?\n"},{"id":"459515","messageId":"xmqq1quf4ucw.fsf@gitster.g","threadId":"58151","inReplyTo":"patch-v2-04.24-9fb60485c3e-20220719T204458Z-avarab@gmail.com","subject":"Re: [PATCH v2 04/24] submodule--helper: fix most \"struct pathspec\" memory leaks","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-20T16:33:19Z","receivedAt":"2022-07-20T16:33:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> @@ -269,7 +269,7 @@ static char *get_up_path(const char *path)\n>  static int module_list(int argc, const char **argv, const char *prefix)\n>  {\n>  \tint i;\n> -\tstruct pathspec pathspec;\n> +\tstruct pathspec pathspec = { 0 };\n>  \tstruct module_list list = MODULE_LIST_INIT;\n>  \n>  \tstruct option module_list_options[] = {\n> @@ -278,6 +278,7 @@ static int module_list(int argc, const char **argv, const char *prefix)\n>  \t\t\t   N_(\"alternative anchor for relative paths\")),\n>  \t\tOPT_END()\n>  \t};\n> +\tint ret;\n\nAdding such a simple variable near the top, perhaps next to \"int i\",\nwould probably make it easier to read, especially because you employ\na good pattern below to let the compiler detect unintended use of\nthe variable uninitialized, i.e. \"ret = 1; goto cleanup;\" is placed\nat each error-exit codepath, and \"ret = 0;\" is added immediately\nbefore the \"cleanup\" label.  If somebody jumps to the cleanup label\nwithout setting \"ret\", the compiler will catch it.\n\nBut you can do better by eliminating the need to check.  Initialize\n\"ret\" to non-zero (i.e. \"assume failure\"), and have each error-exit\ncodepath just \"goto cleanup\".  Keep the \"ret = 0;\" this patch uses\nimmediately before the \"cleanup\" label.\n\n> @@ -287,8 +288,10 @@ static int module_list(int argc, const char **argv, const char *prefix)\n>  \targc = parse_options(argc, argv, prefix, module_list_options,\n>  \t\t\t     git_submodule_helper_usage, 0);\n>  \n> -\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n> -\t\treturn 1;\n> +\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n> +\t\tret = 1;\n> +\t\tgoto cleanup;\n> +\t}\n\nWhich will simplify this hunk.\n\nThe same exact pattern repeats throughout this patch, and the above\ncommits apply to all of the hunks, I think.\n\nThanks.\n"},{"id":"459517","messageId":"xmqqwnc73fba.fsf@gitster.g","threadId":"58151","inReplyTo":"patch-v2-05.24-a4672aa9c94-20220719T204458Z-avarab@gmail.com","subject":"Re: [PATCH v2 05/24] submodule--helper: \"struct pathspec\" memory leak in module_update()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-20T16:43:37Z","receivedAt":"2022-07-20T16:43:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> The module_update() function calls module_list_compute() twice, which\n> in turn will reset the \"struct pathspec\" passed to it. Let's instead\n> track two of them, and clear them both.\n\nLooks correct.  I wish there were an easy way to limit the scope of\nthis new pathspec2 to the block guarded by \"if (opt.init)\", but with\nthe \"jump to error-exit\" idiom, there unfortunately isn't.\n\nThanks.\n\n\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 4 +++-\n>  1 file changed, 3 insertions(+), 1 deletion(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index 28c5fdb8954..7466e781e97 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -2602,6 +2602,7 @@ static int update_submodules(struct update_data *update_data)\n>  static int module_update(int argc, const char **argv, const char *prefix)\n>  {\n>  \tstruct pathspec pathspec = { 0 };\n> +\tstruct pathspec pathspec2 = { 0 };\n>  \tstruct update_data opt = UPDATE_DATA_INIT;\n>  \tstruct list_objects_filter_options filter_options = { 0 };\n>  \tint ret;\n> @@ -2692,7 +2693,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n>  \t\tstruct init_cb info = INIT_CB_INIT;\n>  \n>  \t\tif (module_list_compute(argc, argv, opt.prefix,\n> -\t\t\t\t\t&pathspec, &list) < 0) {\n> +\t\t\t\t\t&pathspec2, &list) < 0) {\n>  \t\t\tret = 1;\n>  \t\t\tgoto cleanup;\n>  \t\t}\n> @@ -2715,6 +2716,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n>  cleanup:\n>  \tlist_objects_filter_release(&filter_options);\n>  \tclear_pathspec(&pathspec);\n> +\tclear_pathspec(&pathspec2);\n>  \treturn ret;\n>  }\n"},{"id":"459519","messageId":"xmqqr12f3e9e.fsf@gitster.g","threadId":"58151","inReplyTo":"patch-v2-08.24-b7582391c91-20220719T204458Z-avarab@gmail.com","subject":"Re: [PATCH v2 08/24] submodule--helper: add and use *_release() functions","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-20T17:06:21Z","receivedAt":"2022-07-20T17:06:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Add release functions for \"struct module_list\", \"struct\n> submodule_update_clone\" and \"struct update_data\".\n\nOK, this does a lot of things, but in short, nobody bothered to free\nmodule_list (i.e. list of cache entries borrowed from the index to\nrepresent the paths we are interested in that are gitlinks),\nupdate_data (which has module_list among other things that do not\nneed to be freed), and submodule_update_clone (which has\nupdate_data, update_clone_data and failed_clones list) in the\noriginal (in other words, these release helpers had to be invented\nby this patch, not factored out from some codepaths that free them).\n\nI think the earlier part of the patch that deals with module_list is\ncorrect.  I also think the last one that clears update_data in\nmodule_update() is correct.\n\nI am not sure about the helper that releases suc, especially how\nsuc->update_data is left alone by update_submodules().  Presumably\nthe caller is responsible for releasing the resources held by\nupdate_data member alone, but the interaction makes me feel dirty.\nLuckily there is only one caller of suc_release(), so we can design\nhowever dirty interface for convenience, but still it feels wrong to\ndesign a release() helper that pretends to be usable by general\npublic, yet they have to be aware of the fact that some members in\nthe struct are still their responsibility to release.  I dunno.\n\n"},{"id":"459554","messageId":"xmqqr12f1kg2.fsf@gitster.g","threadId":"58151","inReplyTo":"patch-v2-11.24-c25b55c9528-20220719T204458Z-avarab@gmail.com","subject":"Re: [PATCH v2 11/24] submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-20T22:35:41Z","receivedAt":"2022-07-20T22:35:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> -\tconst char *sm_path;\n> +\tchar *sm_path;\n>  };\n>  #define MODULE_CB_INIT { 0 }\n> +static void module_cb_release(struct module_cb *mcb)\n> +{\n> +\tfree(mcb->sm_path);\n> +}\n\nIt would be nice to have a blank line before the new helper\nfunction, just like you did for the next hunk.\n\n>  struct module_cb_list {\n>  \tstruct module_cb **entries;\n> @@ -838,6 +842,19 @@ struct module_cb_list {\n>  };\n>  #define MODULE_CB_LIST_INIT { 0 }\n>  \n> +static void module_cb_list_release(struct module_cb_list *mcbl)\n> +{\n> +\tint i;\n> +\n> +\tfor (i = 0; i < mcbl->nr; i++) {\n> +\t\tstruct module_cb *mcb = mcbl->entries[i];\n> +\n> +\t\tmodule_cb_release(mcb);\n> +\t\tfree(mcb);\n> +\t}\n> +\tfree(mcbl->entries);\n> +}\n\nOK, this was populated in the diff callback function and each\nelement of the list->entries[] array is a module_cb data, which has\nits own release() helper above.\n\nLooking good.\n\n\n>  struct summary_cb {\n>  \tint argc;\n>  \tconst char **argv;\n> @@ -1181,6 +1198,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n>  cleanup:\n>  \tstrvec_clear(&diff_args);\n>  \trelease_revisions(&rev);\n> +\tmodule_cb_list_release(&list);\n>  \treturn ret;\n>  }\n"},{"id":"459556","messageId":"xmqqczdz1e7t.fsf@gitster.g","threadId":"58151","inReplyTo":"patch-v2-14.24-c7610088968-20220719T204458Z-avarab@gmail.com","subject":"Re: [PATCH v2 14/24] submodule--helper: fix \"reference\" leak is \"module_clone_data\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-21T00:50:14Z","receivedAt":"2022-07-21T00:50:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Those commits added an xstrdup()'d member of the\n> STRING_LIST_INIT_NODUP'd \"struct string_list\". We need to free()\n> those, but not the ones we get from argv, let's make use of the \"util\"\n> member, if it has a pointer it's the pointer we'll need to free.\n\nI find this description misleading; do we sometimes put the same 'p'\nas the key to the util member, and sometimes leave the util member\nNULL?\n\nI have seen some hacky codepaths that add keys with strdup_strings\nset to false, and then call string_list_clear after setting it set\nto true, to force the keys to be released (cf. bisect_clean_state()\nuses the hack to clear refs_fo_removal).  Compared to that, using\nthe util member is probably less hacky, but it would not extend to\ncases where the string_list needs to associate real payload to the\nkey strings.\n"},{"id":"459670","messageId":"220721.86edyetl3f.gmgdl@evledraar.gmail.com","threadId":"58151","inReplyTo":"xmqqr12f3e9e.fsf@gitster.g","subject":"Re: [PATCH v2 08/24] submodule--helper: add and use *_release() functions","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T17:44:27Z","receivedAt":"2022-07-21T17:46:44Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Jul 20 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> Add release functions for \"struct module_list\", \"struct\n>> submodule_update_clone\" and \"struct update_data\".\n>\n> OK, this does a lot of things, but in short, nobody bothered to free\n> module_list (i.e. list of cache entries borrowed from the index to\n> represent the paths we are interested in that are gitlinks),\n> update_data (which has module_list among other things that do not\n> need to be freed), and submodule_update_clone (which has\n> update_data, update_clone_data and failed_clones list) in the\n> original (in other words, these release helpers had to be invented\n> by this patch, not factored out from some codepaths that free them).\n>\n> I think the earlier part of the patch that deals with module_list is\n> correct.  I also think the last one that clears update_data in\n> module_update() is correct.\n\n...\n\n> I am not sure about the helper that releases suc, especially how\n> suc->update_data is left alone by update_submodules().  Presumably\n> the caller is responsible for releasing the resources held by\n> update_data member alone, but the interaction makes me feel dirty.\n> Luckily there is only one caller of suc_release(), so we can design\n> however dirty interface for convenience, but still it feels wrong to\n> design a release() helper that pretends to be usable by general\n> public, yet they have to be aware of the fact that some members in\n> the struct are still their responsibility to release.  I dunno.\n\nBut isn't this fine because that \"update_data\" member has a \"const\" on\nit:\n\n\t/* configuration parameters which are passed on to the children */\n\tconst struct update_data *update_data;\n\nI fix other similar ownership issues you pointed out for a re-roll I'm\npreparing, but I don't see why this one is a problem as long as that\n\"const\" is there, it's a slot we use for passing things in externally &\nto ferry it along.\n"},{"id":"459672","messageId":"kl6ledyetkxp.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-v2-04.24-9fb60485c3e-20220719T204458Z-avarab@gmail.com","subject":"Re: [PATCH v2 04/24] submodule--helper: fix most \"struct pathspec\" memory leaks","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-21T17:49:54Z","receivedAt":"2022-07-21T17:49:59Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Call clear_pathspec() at the end of various functions that work with\n> and allocate a \"struct pathspec\".\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 115 +++++++++++++++++++++++++-----------\n>  1 file changed, 81 insertions(+), 34 deletions(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index b36919b66c5..28c5fdb8954 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -1105,7 +1129,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n>  \tstruct strvec diff_args = STRVEC_INIT;\n>  \tstruct rev_info rev;\n>  \tstruct module_cb_list list = MODULE_CB_LIST_INIT;\n> -\tint ret = 0;\n> +\tint ret;\n>  \n>  \tstrvec_push(&diff_args, get_diff_cmd(diff_cmd));\n>  \tif (info->cached)\n> @@ -1145,6 +1169,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n>  \telse\n>  \t\trun_diff_files(&rev, 0);\n>  \tprepare_submodule_summary(info, &list);\n> +\tret = 0;\n>  cleanup:\n>  \tstrvec_clear(&diff_args);\n>  \trelease_revisions(&rev);\n\nThis function doesn't use a pathspec at all. Was it changed for\nconsistency reasons or perhaps an accidental inclusion?\n\nAll the other hunks look correct.\n"},{"id":"459685","messageId":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com","subject":"[PATCH v3 00/26] submodule--helper: fix memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:12:56Z","receivedAt":"2022-07-21T19:13:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This series fixes all of the memory leaks in submodule--helper.c that\nthe test suite spots (which aren't really in submodule.c, branch.c,\nobject code etc.).\n\nFor the v2, see:\nhttps://lore.kernel.org/git/cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com/\n\nJunio: I only replied to one feedback E-Mail of yours, but I think\nthis re-roll addresses all the rest, as well as the \"pathspec\" comment\nGlen sent in earlier today.\n\nGlen Choo (2):\n  submodule--helper: add \"const\" to copy of \"update_data\"\n  submodule--helper: refactor \"errmsg_str\" to be a \"struct strbuf\"\n\nÆvar Arnfjörð Bjarmason (24):\n  submodule--helper: replace memset() with { 0 }-initialization\n  submodule--helper: stop conflating \"sb\" in clone_submodule()\n  submodule--helper: pass a \"const struct module_clone_data\" to\n    clone_submodule()\n  submodule--helper: fix a leak in \"clone_submodule\"\n  submodule--helper: fix trivial get_default_remote_submodule() leak\n  submodule--helper: fix most \"struct pathspec\" memory leaks\n  submodule--helper: \"struct pathspec\" memory leak in module_update()\n  submodule--helper: don't leak {run,capture}_command() cp.dir argument\n  submodule--helper: add and use *_release() functions\n  submodule--helper: fix \"errmsg_str\" memory leak\n  submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks\n  submodule--helper: fix a leak with repo_clear()\n  submodule--helper: fix a memory leak in get_default_remote_submodule()\n  submodule--helper: fix \"reference\" leak is \"module_clone_data\"\n  submodule--helper: fix obscure leak in module_add()\n  submodule--helper: fix a leak in module_add()\n  submodule--helper: fix a memory leak in print_status()\n  submodule--helper: free some \"displaypath\" in \"struct update_data\"\n  submodule--helper: rename \"int res\" to \"int ret\"\n  submodule--helper: add skeleton \"goto cleanup\" to update_submodule()\n  submodule--helper: don't exit() on failure, return\n  submodule--helper: free rest of \"displaypath\" in \"struct update_data\"\n  submodule--helper: fix bad config API usage\n  submodule--helper: fix a configure_added_submodule() leak\n\n builtin/submodule--helper.c        | 374 ++++++++++++++++++++---------\n t/t1500-rev-parse.sh               |   1 +\n t/t2403-worktree-move.sh           |   1 +\n t/t6008-rev-list-submodule.sh      |   1 +\n t/t6134-pathspec-in-submodule.sh   |   1 +\n t/t7412-submodule-absorbgitdirs.sh |   1 +\n t/t7413-submodule-is-active.sh     |   1 +\n t/t7414-submodule-mistakes.sh      |   2 +\n t/t7506-status-submodule.sh        |   1 +\n t/t7507-commit-verbose.sh          |   2 +\n 10 files changed, 270 insertions(+), 115 deletions(-)\n\nRange-diff against v2:\n 1:  fcdf4a2e2d9 =  1:  3c7ae3e0222 submodule--helper: replace memset() with { 0 }-initialization\n -:  ----------- >  2:  32e4ae7ead5 submodule--helper: stop conflating \"sb\" in clone_submodule()\n -:  ----------- >  3:  eee6ca28c6f submodule--helper: pass a \"const struct module_clone_data\" to clone_submodule()\n 2:  130a396b837 !  4:  26f0caf0386 submodule--helper: fix a leak in \"clone_submodule\"\n    @@ Metadata\n      ## Commit message ##\n         submodule--helper: fix a leak in \"clone_submodule\"\n     \n    -    Fix a memory leak of the \"path\" member of the \"struct\n    -    module_clone_data\" in clone_submodule(). This fixes leaks in code\n    -    added in f8eaa0ba98b (submodule--helper, module_clone: always operate\n    +    Fix a memory leak of the \"clone_data_path\" variable that we copy or\n    +    derive from the \"struct module_clone_data\" in clone_submodule(). This\n    +    code was refactored in preceding commits, but the leak has been with\n    +    us since f8eaa0ba98b (submodule--helper, module_clone: always operate\n         on absolute paths, 2016-03-31).\n     \n         For the \"else\" case we don't need to xstrdup() the \"clone_data->path\",\n    -    if we're not creating a new one we'll leave it to our caller to keep\n    -    track of it.\n    +    and we don't need to free our own \"clone_data_path\".\n     \n         In the case of the module_clone() caller it's from \"argv\", and doesn't\n         need to be free'd, and in the case of the add_submodule() caller we\n    @@ Commit message\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/submodule--helper.c ##\n    -@@ builtin/submodule--helper.c: static int clone_submodule(struct module_clone_data *clone_data)\n    +@@ builtin/submodule--helper.c: static int clone_submodule(const struct module_clone_data *clone_data,\n      \tchar *sm_alternate = NULL, *error_strategy = NULL;\n    - \tstruct strbuf sb = STRBUF_INIT;\n      \tstruct child_process cp = CHILD_PROCESS_INIT;\n    + \tconst char *clone_data_path;\n     +\tchar *to_free = NULL;\n      \n    - \tsubmodule_name_to_gitdir(&sb, the_repository, clone_data->name);\n    - \tsm_gitdir = absolute_pathdup(sb.buf);\n    -@@ builtin/submodule--helper.c: static int clone_submodule(struct module_clone_data *clone_data)\n    - \n      \tif (!is_absolute_path(clone_data->path)) {\n    + \t\tstruct strbuf sb = STRBUF_INIT;\n    + \n      \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n    --\t\tclone_data->path = strbuf_detach(&sb, NULL);\n    -+\t\tclone_data->path = to_free = strbuf_detach(&sb, NULL);\n    +-\t\tclone_data_path = strbuf_detach(&sb, NULL);\n    ++\t\tclone_data_path = to_free = strbuf_detach(&sb, NULL);\n      \t} else {\n    --\t\tclone_data->path = xstrdup(clone_data->path);\n    -+\t\tclone_data->path = clone_data->path;\n    +-\t\tclone_data_path = xstrdup(clone_data_path);\n    ++\t\tclone_data_path = clone_data->path;\n      \t}\n      \n      \tif (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)\n    -@@ builtin/submodule--helper.c: static int clone_submodule(struct module_clone_data *clone_data)\n    - \tstrbuf_release(&sb);\n    +@@ builtin/submodule--helper.c: static int clone_submodule(const struct module_clone_data *clone_data,\n    + \n      \tfree(sm_gitdir);\n      \tfree(p);\n     +\tfree(to_free);\n 3:  25e17bbb058 =  5:  75775bf4f6c submodule--helper: fix trivial get_default_remote_submodule() leak\n 4:  9fb60485c3e !  6:  7672ef1305f submodule--helper: fix most \"struct pathspec\" memory leaks\n    @@ builtin/submodule--helper.c: static int module_list(int argc, const char **argv,\n      \t\t\t   N_(\"alternative anchor for relative paths\")),\n      \t\tOPT_END()\n      \t};\n    -+\tint ret;\n    ++\tint ret = 1;\n      \n      \tconst char *const git_submodule_helper_usage[] = {\n      \t\tN_(\"git submodule--helper list [--prefix=<path>] [<path>...]\"),\n     @@ builtin/submodule--helper.c: static int module_list(int argc, const char **argv, const char *prefix)\n    - \targc = parse_options(argc, argv, prefix, module_list_options,\n      \t\t\t     git_submodule_helper_usage, 0);\n      \n    --\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n    + \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n     -\t\treturn 1;\n    -+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n    -+\t\tret = 1;\n     +\t\tgoto cleanup;\n    -+\t}\n      \n      \tfor (i = 0; i < list.nr; i++) {\n      \t\tconst struct cache_entry *ce = list.entries[i];\n    @@ builtin/submodule--helper.c: static int module_foreach(int argc, const char **ar\n      \t\tN_(\"git submodule foreach [--quiet] [--recursive] [--] <command>\"),\n      \t\tNULL\n      \t};\n    -+\tint ret;\n    ++\tint ret = 1;\n      \n      \targc = parse_options(argc, argv, prefix, module_foreach_options,\n      \t\t\t     git_submodule_helper_usage, 0);\n      \n    --\tif (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)\n    + \tif (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)\n     -\t\treturn 1;\n    -+\tif (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0) {\n    -+\t\tret = 1;\n     +\t\tgoto cleanup;\n    -+\t}\n      \n      \tinfo.argc = argc;\n      \tinfo.argv = argv;\n    @@ builtin/submodule--helper.c: static int module_init(int argc, const char **argv,\n      \t\tN_(\"git submodule init [<options>] [<path>]\"),\n      \t\tNULL\n      \t};\n    -+\tint ret;\n    ++\tint ret = 1;\n      \n      \targc = parse_options(argc, argv, prefix, module_init_options,\n      \t\t\t     git_submodule_helper_usage, 0);\n      \n    --\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n    + \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n     -\t\treturn 1;\n    -+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n    -+\t\tret = 1;\n     +\t\tgoto cleanup;\n    -+\t}\n      \n      \t/*\n      \t * If there are no path args and submodule.active is set then,\n    @@ builtin/submodule--helper.c: static int module_status(int argc, const char **arg\n      \t\tN_(\"git submodule status [--quiet] [--cached] [--recursive] [<path>...]\"),\n      \t\tNULL\n      \t};\n    -+\tint ret;\n    ++\tint ret = 1;\n      \n      \targc = parse_options(argc, argv, prefix, module_status_options,\n      \t\t\t     git_submodule_helper_usage, 0);\n      \n    --\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n    + \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n     -\t\treturn 1;\n    -+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n    -+\t\tret = 1;\n     +\t\tgoto cleanup;\n    -+\t}\n      \n      \tinfo.prefix = prefix;\n      \tif (quiet)\n    @@ builtin/submodule--helper.c: static int module_status(int argc, const char **arg\n      }\n      \n      static int module_name(int argc, const char **argv, const char *prefix)\n    -@@ builtin/submodule--helper.c: static int compute_summary_module_list(struct object_id *head_oid,\n    - \tstruct strvec diff_args = STRVEC_INIT;\n    - \tstruct rev_info rev;\n    - \tstruct module_cb_list list = MODULE_CB_LIST_INIT;\n    --\tint ret = 0;\n    -+\tint ret;\n    - \n    - \tstrvec_push(&diff_args, get_diff_cmd(diff_cmd));\n    - \tif (info->cached)\n    -@@ builtin/submodule--helper.c: static int compute_summary_module_list(struct object_id *head_oid,\n    - \telse\n    - \t\trun_diff_files(&rev, 0);\n    - \tprepare_submodule_summary(info, &list);\n    -+\tret = 0;\n    - cleanup:\n    - \tstrvec_clear(&diff_args);\n    - \trelease_revisions(&rev);\n     @@ builtin/submodule--helper.c: static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data\n      static int module_sync(int argc, const char **argv, const char *prefix)\n      {\n    @@ builtin/submodule--helper.c: static void sync_submodule_cb(const struct cache_en\n      \tstruct module_list list = MODULE_LIST_INIT;\n      \tint quiet = 0;\n      \tint recursive = 0;\n    -+\tint ret;\n    ++\tint ret = 1;\n      \n      \tstruct option module_sync_options[] = {\n      \t\tOPT__QUIET(&quiet, N_(\"suppress output of synchronizing submodule url\")),\n     @@ builtin/submodule--helper.c: static int module_sync(int argc, const char **argv, const char *prefix)\n    - \targc = parse_options(argc, argv, prefix, module_sync_options,\n      \t\t\t     git_submodule_helper_usage, 0);\n      \n    --\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n    + \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n     -\t\treturn 1;\n    -+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n    -+\t\tret = 1;\n     +\t\tgoto cleanup;\n    -+\t}\n      \n      \tinfo.prefix = prefix;\n      \tif (quiet)\n    @@ builtin/submodule--helper.c: static int module_deinit(int argc, const char **arg\n      \t\tN_(\"git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]\"),\n      \t\tNULL\n      \t};\n    -+\tint ret;\n    ++\tint ret = 1;\n      \n      \targc = parse_options(argc, argv, prefix, module_deinit_options,\n      \t\t\t     git_submodule_helper_usage, 0);\n     @@ builtin/submodule--helper.c: static int module_deinit(int argc, const char **argv, const char *prefix)\n    - \tif (!argc && !all)\n      \t\tdie(_(\"Use '--all' if you really want to deinitialize all submodules\"));\n      \n    --\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n    + \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n     -\t\treturn 1;\n    -+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n    -+\t\tret = 1;\n     +\t\tgoto cleanup;\n    -+\t}\n      \n      \tinfo.prefix = prefix;\n      \tif (quiet)\n    @@ builtin/submodule--helper.c: static int push_check(int argc, const char **argv,\n     +\tstruct pathspec pathspec = { 0 };\n      \tstruct module_list list = MODULE_LIST_INIT;\n      \tunsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;\n    -+\tint ret;\n    ++\tint ret = 1;\n      \n      \tstruct option embed_gitdir_options[] = {\n      \t\tOPT_STRING(0, \"prefix\", &prefix,\n     @@ builtin/submodule--helper.c: static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n    - \targc = parse_options(argc, argv, prefix, embed_gitdir_options,\n      \t\t\t     git_submodule_helper_usage, 0);\n      \n    --\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n    + \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n     -\t\treturn 1;\n    -+\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n    -+\t\tret = 1;\n     +\t\tgoto cleanup;\n    -+\t}\n      \n      \tfor (i = 0; i < list.nr; i++)\n      \t\tabsorb_git_dir_into_superproject(list.entries[i]->name, flags);\n 5:  a4672aa9c94 =  7:  325aa1521e2 submodule--helper: \"struct pathspec\" memory leak in module_update()\n 6:  7925db18b33 =  8:  424b24961b5 submodule--helper: don't leak {run,capture}_command() cp.dir argument\n 7:  30883f3aa01 =  9:  abf5c4754a4 submodule--helper: add \"const\" to copy of \"update_data\"\n 8:  b7582391c91 = 10:  6ab5aabae35 submodule--helper: add and use *_release() functions\n 9:  92737916083 = 11:  feec1f20bf9 submodule--helper: refactor \"errmsg_str\" to be a \"struct strbuf\"\n10:  c81a4f69179 = 12:  d368db73de7 submodule--helper: fix \"errmsg_str\" memory leak\n11:  c25b55c9528 ! 13:  5be941b3d1b submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks\n    @@ builtin/submodule--helper.c: struct module_cb {\n     +\tchar *sm_path;\n      };\n      #define MODULE_CB_INIT { 0 }\n    + \n     +static void module_cb_release(struct module_cb *mcb)\n     +{\n     +\tfree(mcb->sm_path);\n     +}\n    - \n    ++\n      struct module_cb_list {\n      \tstruct module_cb **entries;\n    -@@ builtin/submodule--helper.c: struct module_cb_list {\n    + \tint alloc, nr;\n      };\n      #define MODULE_CB_LIST_INIT { 0 }\n      \n12:  303447bd4f5 = 14:  b8560e8c111 submodule--helper: fix a leak with repo_clear()\n13:  f8ededcdf77 = 15:  abfd61f9f05 submodule--helper: fix a memory leak in get_default_remote_submodule()\n14:  c7610088968 ! 16:  b8df96a9cf0 submodule--helper: fix \"reference\" leak is \"module_clone_data\"\n    @@ Commit message\n         Those commits added an xstrdup()'d member of the\n         STRING_LIST_INIT_NODUP'd \"struct string_list\". We need to free()\n         those, but not the ones we get from argv, let's make use of the \"util\"\n    -    member, if it has a pointer it's the pointer we'll need to free.\n    +    member, if it has a pointer it's the pointer we'll need to free,\n    +    otherwise it'll be NULL (i.e. from argv).\n     \n         To fix this create a a module_clone_data_release() function to go with\n         the MODULE_CLONE_DATA_INIT added in a98b02c1128 (submodule--helper:\n    @@ builtin/submodule--helper.c: static int add_possible_reference_from_superproject\n      \t\t\tswitch (sas->error_mode) {\n     @@ builtin/submodule--helper.c: static int module_clone(int argc, const char **argv, const char *prefix)\n      \n    - \tclone_submodule(&clone_data);\n    + \tclone_submodule(&clone_data, &clone_data.reference);\n      \tlist_objects_filter_release(&filter_options);\n     +\tmodule_clone_data_release(&clone_data);\n      \treturn 0;\n    @@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add\n      {\n      \tchar *submod_gitdir_path;\n      \tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n    -+\tint ret;\n    ++\tint ret = -1;\n      \n      \t/* perhaps the path already exists and is already a git repo, else clone it */\n      \tif (is_directory(add_data->sm_path)) {\n    @@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add\n      \t\tif (add_data->depth >= 0)\n      \t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n      \n    --\t\tif (clone_submodule(&clone_data))\n    + \t\tif (clone_submodule(&clone_data, &clone_data.reference))\n     -\t\t\treturn -1;\n    -+\t\tif (clone_submodule(&clone_data)) {\n    -+\t\t\tret = -1;\n     +\t\t\tgoto cleanup;\n    -+\t\t}\n      \n      \t\tprepare_submodule_repo_env(&cp.env);\n      \t\tcp.git_cmd = 1;\n15:  abd8e2eef3a ! 17:  7811bdbf149 submodule--helper: fix obscure leak in module_add()\n    @@ builtin/submodule--helper.c: static int module_add(int argc, const char **argv,\n      \tstruct add_data add_data = ADD_DATA_INIT;\n      \tchar *to_free = NULL;\n     +\tstruct strbuf sb = STRBUF_INIT;\n    -+\tint ret;\n    ++\tint ret = 1;\n      \n      \tstruct option options[] = {\n      \t\tOPT_STRING('b', \"branch\", &add_data.branch, N_(\"branch\"),\n    @@ builtin/submodule--helper.c: static int module_add(int argc, const char **argv,\n      \n      \tif(!add_data.sm_name)\n     @@ builtin/submodule--helper.c: static int module_add(int argc, const char **argv, const char *prefix)\n    + \tadd_data.progress = !!progress;\n      \tadd_data.dissociate = !!dissociate;\n      \n    - \tif (add_submodule(&add_data)) {\n    +-\tif (add_submodule(&add_data)) {\n     -\t\tfree(add_data.sm_path);\n     -\t\treturn 1;\n    -+\t\tret = 1;\n    +-\t}\n    ++\tif (add_submodule(&add_data))\n     +\t\tgoto cleanup;\n    - \t}\n      \tconfigure_added_submodule(&add_data);\n     +\n     +\tret = 0;\n16:  1f01203d154 = 18:  01566d63926 submodule--helper: fix a leak in module_add()\n17:  12b8a945486 = 19:  7ef89abed86 submodule--helper: fix a memory leak in print_status()\n18:  fac2c4491f3 = 20:  b8d47fc7d70 submodule--helper: free some \"displaypath\" in \"struct update_data\"\n19:  cf0c8851954 = 21:  4fb17f0dff1 submodule--helper: rename \"int res\" to \"int ret\"\n20:  7882e33cdca ! 22:  af83925046b submodule--helper: add skeleton \"goto cleanup\" to update_submodule()\n    @@ builtin/submodule--helper.c: static void update_data_to_args(struct update_data\n      \n      static int update_submodule(struct update_data *update_data)\n      {\n    -+\tint ret;\n    ++\tint ret = 1;\n     +\n      \tensure_core_worktree(update_data->sm_path);\n      \n    @@ builtin/submodule--helper.c: static int update_submodule(struct update_data *upd\n      \t}\n      \n     -\tif (!oideq(&update_data->oid, &update_data->suboid) || update_data->force)\n    --\t\tif (run_update_procedure(update_data))\n    --\t\t\treturn 1;\n     +\tif (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) {\n    -+\t\tret = run_update_procedure(update_data);\n    -+\t\tif (ret) {\n    -+\t\t\tret = 1;\n    + \t\tif (run_update_procedure(update_data))\n    +-\t\t\treturn 1;\n     +\t\t\tgoto cleanup;\n    -+\t\t}\n     +\t}\n      \n      \tif (update_data->recursive) {\n21:  31395a2b4f8 ! 23:  4c60784d281 submodule--helper: don't exit() on failure, return\n    @@ builtin/submodule--helper.c: static void update_data_to_args(struct update_data\n     +static int update_submodule(struct update_data *update_data,\n     +\t\t\t    int *must_die_on_failure)\n      {\n    - \tint ret;\n    + \tint ret = 1;\n      \n     @@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)\n      \t}\n      \n      \tif (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) {\n    --\t\tret = run_update_procedure(update_data);\n    --\t\tif (ret) {\n    +-\t\tif (run_update_procedure(update_data))\n     +\t\tret = run_update_procedure(update_data, must_die_on_failure);\n     +\t\tif (ret && *must_die_on_failure) {\n     +\t\t\tgoto cleanup;\n     +\t\t} else if (ret) {\n    - \t\t\tret = 1;\n    ++\t\t\tret = 1;\n      \t\t\tgoto cleanup;\n    - \t\t}\n    ++\t\t}\n    + \t}\n    + \n    + \tif (update_data->recursive) {\n     @@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)\n      \t\tdie_message(_(\"Failed to recurse into submodule path '%s'\"),\n      \t\t\t    update_data->displaypath);\n22:  a2168cf1378 ! 24:  7551af195ad submodule--helper: free rest of \"displaypath\" in \"struct update_data\"\n    @@ builtin/submodule--helper.c\n     @@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data,\n      \t\t\t    int *must_die_on_failure)\n      {\n    - \tint ret;\n    + \tint ret = 1;\n     +\tchar *to_free, *restore = update_data->displaypath;\n      \n      \tensure_core_worktree(update_data->sm_path);\n23:  d77c6665ca9 = 25:  f650716cd7e submodule--helper: fix bad config API usage\n24:  531db4ddae6 = 26:  581ce0872c0 submodule--helper: fix a configure_added_submodule() leak\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459686","messageId":"patch-v3-01.26-3c7ae3e0222-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 01/26] submodule--helper: replace memset() with { 0 }-initialization","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:12:57Z","receivedAt":"2022-07-21T19:13:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use the less verbose { 0 }-initialization syntax rather than memset()\nin builtin/submodule--helper.c, this doesn't make a difference in\nterms of behavior, but as we're about to modify adjacent code makes\nthis more consistent, and lets us avoid worrying about when the\nmemset() happens v.s. a \"goto cleanup\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 ++----\n 1 file changed, 2 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex fac52ade5e1..73717be957c 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1744,7 +1744,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n {\n \tint dissociate = 0, quiet = 0, progress = 0, require_init = 0;\n \tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n-\tstruct list_objects_filter_options filter_options;\n+\tstruct list_objects_filter_options filter_options = { 0 };\n \n \tstruct option module_clone_options[] = {\n \t\tOPT_STRING(0, \"prefix\", &clone_data.prefix,\n@@ -1786,7 +1786,6 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \t\tNULL\n \t};\n \n-\tmemset(&filter_options, 0, sizeof(filter_options));\n \targc = parse_options(argc, argv, prefix, module_clone_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n@@ -2563,7 +2562,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n {\n \tstruct pathspec pathspec;\n \tstruct update_data opt = UPDATE_DATA_INIT;\n-\tstruct list_objects_filter_options filter_options;\n+\tstruct list_objects_filter_options filter_options = { 0 };\n \tint ret;\n \n \tstruct option module_update_options[] = {\n@@ -2623,7 +2622,6 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \tupdate_clone_config_from_gitmodules(&opt.max_jobs);\n \tgit_config(git_update_clone_config, &opt.max_jobs);\n \n-\tmemset(&filter_options, 0, sizeof(filter_options));\n \targc = parse_options(argc, argv, prefix, module_update_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459687","messageId":"patch-v3-02.26-32e4ae7ead5-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 02/26] submodule--helper: stop conflating \"sb\" in clone_submodule()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:12:58Z","receivedAt":"2022-07-21T19:13:46Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor the three uses of a \"struct strbuf sb\" such that each of them\nexists in its own scope. This makes the control flow clearer.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 27 +++++++++++++++++++--------\n 1 file changed, 19 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 73717be957c..29641690c8c 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1638,18 +1638,28 @@ static void prepare_possible_alternates(const char *sm_name,\n \tfree(error_strategy);\n }\n \n-static int clone_submodule(struct module_clone_data *clone_data)\n+static char *clone_submodule_sm_gitdir(const char *name)\n {\n-\tchar *p, *sm_gitdir;\n-\tchar *sm_alternate = NULL, *error_strategy = NULL;\n \tstruct strbuf sb = STRBUF_INIT;\n-\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tchar *sm_gitdir;\n \n-\tsubmodule_name_to_gitdir(&sb, the_repository, clone_data->name);\n+\tsubmodule_name_to_gitdir(&sb, the_repository, name);\n \tsm_gitdir = absolute_pathdup(sb.buf);\n-\tstrbuf_reset(&sb);\n+\tstrbuf_release(&sb);\n+\n+\treturn sm_gitdir;\n+}\n+\n+static int clone_submodule(struct module_clone_data *clone_data)\n+{\n+\tchar *p;\n+\tchar *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);\n+\tchar *sm_alternate = NULL, *error_strategy = NULL;\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n \n \tif (!is_absolute_path(clone_data->path)) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n \t\tclone_data->path = strbuf_detach(&sb, NULL);\n \t} else {\n@@ -1705,6 +1715,8 @@ static int clone_submodule(struct module_clone_data *clone_data)\n \t\t\tdie(_(\"clone of '%s' into submodule path '%s' failed\"),\n \t\t\t    clone_data->url, clone_data->path);\n \t} else {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n \t\tif (clone_data->require_init && !access(clone_data->path, X_OK) &&\n \t\t    !is_empty_dir(clone_data->path))\n \t\t\tdie(_(\"directory not empty: '%s'\"), clone_data->path);\n@@ -1712,7 +1724,7 @@ static int clone_submodule(struct module_clone_data *clone_data)\n \t\t\tdie(_(\"could not create directory '%s'\"), clone_data->path);\n \t\tstrbuf_addf(&sb, \"%s/index\", sm_gitdir);\n \t\tunlink_or_warn(sb.buf);\n-\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_release(&sb);\n \t}\n \n \tconnect_work_tree_and_git_dir(clone_data->path, sm_gitdir, 0);\n@@ -1734,7 +1746,6 @@ static int clone_submodule(struct module_clone_data *clone_data)\n \tfree(sm_alternate);\n \tfree(error_strategy);\n \n-\tstrbuf_release(&sb);\n \tfree(sm_gitdir);\n \tfree(p);\n \treturn 0;\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459688","messageId":"patch-v3-03.26-eee6ca28c6f-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 03/26] submodule--helper: pass a \"const struct module_clone_data\" to clone_submodule()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:12:59Z","receivedAt":"2022-07-21T19:13:47Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add \"const\" to the \"struct module_clone_data\" that we pass to\nclone_submodule(), which makes the ownership clear, and stops us from\nclobbering the \"clone_data->path\".\n\nWe still need to add to the \"reference\" member, which is a \"struct\nstring_list\". We could do this by having clone_submodule() create its\nown, and copy the contents over, but let's instead pass it as a\nseparate parameter. The main point of doing this is to make it clear\nthat e.g. \"clone_data->path\" always comes from the \"argv\", there's no\nambiguity about whether we can eventually free() the \"struct\nstring_list\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 38 +++++++++++++++++++------------------\n 1 file changed, 20 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 29641690c8c..7d5ee6a6261 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1650,20 +1650,22 @@ static char *clone_submodule_sm_gitdir(const char *name)\n \treturn sm_gitdir;\n }\n \n-static int clone_submodule(struct module_clone_data *clone_data)\n+static int clone_submodule(const struct module_clone_data *clone_data,\n+\t\t\t   struct string_list *reference)\n {\n \tchar *p;\n \tchar *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);\n \tchar *sm_alternate = NULL, *error_strategy = NULL;\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tconst char *clone_data_path;\n \n \tif (!is_absolute_path(clone_data->path)) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \n \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n-\t\tclone_data->path = strbuf_detach(&sb, NULL);\n+\t\tclone_data_path = strbuf_detach(&sb, NULL);\n \t} else {\n-\t\tclone_data->path = xstrdup(clone_data->path);\n+\t\tclone_data_path = xstrdup(clone_data_path);\n \t}\n \n \tif (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)\n@@ -1674,7 +1676,7 @@ static int clone_submodule(struct module_clone_data *clone_data)\n \t\tif (safe_create_leading_directories_const(sm_gitdir) < 0)\n \t\t\tdie(_(\"could not create directory '%s'\"), sm_gitdir);\n \n-\t\tprepare_possible_alternates(clone_data->name, &clone_data->reference);\n+\t\tprepare_possible_alternates(clone_data->name, reference);\n \n \t\tstrvec_push(&cp.args, \"clone\");\n \t\tstrvec_push(&cp.args, \"--no-checkout\");\n@@ -1684,9 +1686,9 @@ static int clone_submodule(struct module_clone_data *clone_data)\n \t\t\tstrvec_push(&cp.args, \"--progress\");\n \t\tif (clone_data->depth && *(clone_data->depth))\n \t\t\tstrvec_pushl(&cp.args, \"--depth\", clone_data->depth, NULL);\n-\t\tif (clone_data->reference.nr) {\n+\t\tif (reference->nr) {\n \t\t\tstruct string_list_item *item;\n-\t\t\tfor_each_string_list_item(item, &clone_data->reference)\n+\t\t\tfor_each_string_list_item(item, reference)\n \t\t\t\tstrvec_pushl(&cp.args, \"--reference\",\n \t\t\t\t\t     item->string, NULL);\n \t\t}\n@@ -1705,7 +1707,7 @@ static int clone_submodule(struct module_clone_data *clone_data)\n \n \t\tstrvec_push(&cp.args, \"--\");\n \t\tstrvec_push(&cp.args, clone_data->url);\n-\t\tstrvec_push(&cp.args, clone_data->path);\n+\t\tstrvec_push(&cp.args, clone_data_path);\n \n \t\tcp.git_cmd = 1;\n \t\tprepare_submodule_repo_env(&cp.env);\n@@ -1713,25 +1715,25 @@ static int clone_submodule(struct module_clone_data *clone_data)\n \n \t\tif(run_command(&cp))\n \t\t\tdie(_(\"clone of '%s' into submodule path '%s' failed\"),\n-\t\t\t    clone_data->url, clone_data->path);\n+\t\t\t    clone_data->url, clone_data_path);\n \t} else {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \n-\t\tif (clone_data->require_init && !access(clone_data->path, X_OK) &&\n-\t\t    !is_empty_dir(clone_data->path))\n-\t\t\tdie(_(\"directory not empty: '%s'\"), clone_data->path);\n-\t\tif (safe_create_leading_directories_const(clone_data->path) < 0)\n-\t\t\tdie(_(\"could not create directory '%s'\"), clone_data->path);\n+\t\tif (clone_data->require_init && !access(clone_data_path, X_OK) &&\n+\t\t    !is_empty_dir(clone_data_path))\n+\t\t\tdie(_(\"directory not empty: '%s'\"), clone_data_path);\n+\t\tif (safe_create_leading_directories_const(clone_data_path) < 0)\n+\t\t\tdie(_(\"could not create directory '%s'\"), clone_data_path);\n \t\tstrbuf_addf(&sb, \"%s/index\", sm_gitdir);\n \t\tunlink_or_warn(sb.buf);\n \t\tstrbuf_release(&sb);\n \t}\n \n-\tconnect_work_tree_and_git_dir(clone_data->path, sm_gitdir, 0);\n+\tconnect_work_tree_and_git_dir(clone_data_path, sm_gitdir, 0);\n \n-\tp = git_pathdup_submodule(clone_data->path, \"config\");\n+\tp = git_pathdup_submodule(clone_data_path, \"config\");\n \tif (!p)\n-\t\tdie(_(\"could not get submodule directory for '%s'\"), clone_data->path);\n+\t\tdie(_(\"could not get submodule directory for '%s'\"), clone_data_path);\n \n \t/* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */\n \tgit_config_get_string(\"submodule.alternateLocation\", &sm_alternate);\n@@ -1810,7 +1812,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options(git_submodule_helper_usage,\n \t\t\t\t   module_clone_options);\n \n-\tclone_submodule(&clone_data);\n+\tclone_submodule(&clone_data, &clone_data.reference);\n \tlist_objects_filter_release(&filter_options);\n \treturn 0;\n }\n@@ -3088,7 +3090,7 @@ static int add_submodule(const struct add_data *add_data)\n \t\tif (add_data->depth >= 0)\n \t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n \n-\t\tif (clone_submodule(&clone_data))\n+\t\tif (clone_submodule(&clone_data, &clone_data.reference))\n \t\t\treturn -1;\n \n \t\tprepare_submodule_repo_env(&cp.env);\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459689","messageId":"patch-v3-04.26-26f0caf0386-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 04/26] submodule--helper: fix a leak in \"clone_submodule\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:00Z","receivedAt":"2022-07-21T19:13:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak of the \"clone_data_path\" variable that we copy or\nderive from the \"struct module_clone_data\" in clone_submodule(). This\ncode was refactored in preceding commits, but the leak has been with\nus since f8eaa0ba98b (submodule--helper, module_clone: always operate\non absolute paths, 2016-03-31).\n\nFor the \"else\" case we don't need to xstrdup() the \"clone_data->path\",\nand we don't need to free our own \"clone_data_path\".\n\nIn the case of the module_clone() caller it's from \"argv\", and doesn't\nneed to be free'd, and in the case of the add_submodule() caller we\nget a pointer to \"sm_path\", which doesn't need to be directly free'd\neither.\n\nFixing this leak makes several tests pass, so let's mark them as\npassing with TEST_PASSES_SANITIZE_LEAK=true.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c   | 6 ++++--\n t/t1500-rev-parse.sh          | 1 +\n t/t6008-rev-list-submodule.sh | 1 +\n t/t7414-submodule-mistakes.sh | 2 ++\n t/t7506-status-submodule.sh   | 1 +\n t/t7507-commit-verbose.sh     | 2 ++\n 6 files changed, 11 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 7d5ee6a6261..1ddce8e19c1 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1658,14 +1658,15 @@ static int clone_submodule(const struct module_clone_data *clone_data,\n \tchar *sm_alternate = NULL, *error_strategy = NULL;\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tconst char *clone_data_path;\n+\tchar *to_free = NULL;\n \n \tif (!is_absolute_path(clone_data->path)) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \n \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n-\t\tclone_data_path = strbuf_detach(&sb, NULL);\n+\t\tclone_data_path = to_free = strbuf_detach(&sb, NULL);\n \t} else {\n-\t\tclone_data_path = xstrdup(clone_data_path);\n+\t\tclone_data_path = clone_data->path;\n \t}\n \n \tif (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)\n@@ -1750,6 +1751,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,\n \n \tfree(sm_gitdir);\n \tfree(p);\n+\tfree(to_free);\n \treturn 0;\n }\n \ndiff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh\nindex 1c2df08333b..0e13bcb4ebb 100755\n--- a/t/t1500-rev-parse.sh\n+++ b/t/t1500-rev-parse.sh\n@@ -4,6 +4,7 @@ test_description='test git rev-parse'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_one () {\ndiff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh\nindex 3153a0d8910..12e67e187ef 100755\n--- a/t/t6008-rev-list-submodule.sh\n+++ b/t/t6008-rev-list-submodule.sh\n@@ -8,6 +8,7 @@ test_description='git rev-list involving submodules that this repo has'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh\nindex f2e7df59cf2..3269298197c 100755\n--- a/t/t7414-submodule-mistakes.sh\n+++ b/t/t7414-submodule-mistakes.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='handling of common mistakes people may make with submodules'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'create embedded repository' '\ndiff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh\nindex 3fcb44767f5..f5426a8e589 100755\n--- a/t/t7506-status-submodule.sh\n+++ b/t/t7506-status-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='git status for submodule'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_create_repo_with_commit () {\ndiff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh\nindex ed2653d46fe..92462a22374 100755\n--- a/t/t7507-commit-verbose.sh\n+++ b/t/t7507-commit-verbose.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='verbose commit template'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n write_script \"check-for-diff\" <<\\EOF &&\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459690","messageId":"patch-v3-05.26-75775bf4f6c-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 05/26] submodule--helper: fix trivial get_default_remote_submodule() leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:01Z","receivedAt":"2022-07-21T19:13:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in code added in 1012a5cbc3f (submodule--helper\nrun-update-procedure: learn --remote, 2022-03-04), we need to free()\nthe xstrdup()'d string. This gets e.g. t/t7419-submodule-set-branch.sh\ncloser to passing under SANITIZE=leak.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 1ddce8e19c1..73ac1fcbb9c 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2486,6 +2486,8 @@ static int update_submodule(struct update_data *update_data)\n \t\tconst char *branch = remote_submodule_branch(update_data->sm_path);\n \t\tchar *remote_ref = xstrfmt(\"refs/remotes/%s/%s\", remote_name, branch);\n \n+\t\tfree(remote_name);\n+\n \t\tif (!update_data->nofetch) {\n \t\t\tif (fetch_in_submodule(update_data->sm_path, update_data->depth,\n \t\t\t\t\t      0, NULL))\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459691","messageId":"patch-v3-06.26-7672ef1305f-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 06/26] submodule--helper: fix most \"struct pathspec\" memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:02Z","receivedAt":"2022-07-21T19:13:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call clear_pathspec() at the end of various functions that work with\nand allocate a \"struct pathspec\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 84 +++++++++++++++++++++++++------------\n 1 file changed, 58 insertions(+), 26 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 73ac1fcbb9c..a60dc6af178 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -269,7 +269,7 @@ static char *get_up_path(const char *path)\n static int module_list(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \n \tstruct option module_list_options[] = {\n@@ -278,6 +278,7 @@ static int module_list(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"alternative anchor for relative paths\")),\n \t\tOPT_END()\n \t};\n+\tint ret = 1;\n \n \tconst char *const git_submodule_helper_usage[] = {\n \t\tN_(\"git submodule--helper list [--prefix=<path>] [<path>...]\"),\n@@ -288,7 +289,7 @@ static int module_list(int argc, const char **argv, const char *prefix)\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tfor (i = 0; i < list.nr; i++) {\n \t\tconst struct cache_entry *ce = list.entries[i];\n@@ -302,7 +303,10 @@ static int module_list(int argc, const char **argv, const char *prefix)\n \n \t\tfprintf(stdout, \"%s\\n\", ce->name);\n \t}\n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static void for_each_listed_submodule(const struct module_list *list,\n@@ -427,7 +431,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,\n static int module_foreach(int argc, const char **argv, const char *prefix)\n {\n \tstruct foreach_cb info = FOREACH_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \n \tstruct option module_foreach_options[] = {\n@@ -441,12 +445,13 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule foreach [--quiet] [--recursive] [--] <command>\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_foreach_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.argc = argc;\n \tinfo.argv = argv;\n@@ -454,7 +459,10 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static int starts_with_dot_slash(const char *const path)\n@@ -562,7 +570,7 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data\n static int module_init(int argc, const char **argv, const char *prefix)\n {\n \tstruct init_cb info = INIT_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \n@@ -575,12 +583,13 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule init [<options>] [<path>]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_init_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \t/*\n \t * If there are no path args and submodule.active is set then,\n@@ -595,7 +604,10 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, init_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct status_cb {\n@@ -740,7 +752,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,\n static int module_status(int argc, const char **argv, const char *prefix)\n {\n \tstruct status_cb info = STATUS_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \n@@ -755,12 +767,13 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule status [--quiet] [--cached] [--recursive] [<path>...]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_status_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -768,7 +781,10 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, status_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static int module_name(int argc, const char **argv, const char *prefix)\n@@ -1326,10 +1342,11 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data\n static int module_sync(int argc, const char **argv, const char *prefix)\n {\n \tstruct sync_cb info = SYNC_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tint recursive = 0;\n+\tint ret = 1;\n \n \tstruct option module_sync_options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"suppress output of synchronizing submodule url\")),\n@@ -1347,7 +1364,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -1357,7 +1374,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, sync_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct deinit_cb {\n@@ -1464,7 +1484,7 @@ static void deinit_submodule_cb(const struct cache_entry *list_item,\n static int module_deinit(int argc, const char **argv, const char *prefix)\n {\n \tstruct deinit_cb info = DEINIT_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tint force = 0;\n@@ -1481,6 +1501,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_deinit_options,\n \t\t\t     git_submodule_helper_usage, 0);\n@@ -1495,7 +1516,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"Use '--all' if you really want to deinitialize all submodules\"));\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -1505,7 +1526,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, deinit_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct module_clone_data {\n@@ -2577,7 +2601,7 @@ static int update_submodules(struct update_data *update_data)\n \n static int module_update(int argc, const char **argv, const char *prefix)\n {\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct update_data opt = UPDATE_DATA_INIT;\n \tstruct list_objects_filter_options filter_options = { 0 };\n \tint ret;\n@@ -2656,8 +2680,8 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\topt.update_strategy.type = opt.update_default;\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {\n-\t\tlist_objects_filter_release(&filter_options);\n-\t\treturn 1;\n+\t\tret = 1;\n+\t\tgoto cleanup;\n \t}\n \n \tif (pathspec.nr)\n@@ -2668,8 +2692,10 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\tstruct init_cb info = INIT_CB_INIT;\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n-\t\t\t\t\t&pathspec, &list) < 0)\n-\t\t\treturn 1;\n+\t\t\t\t\t&pathspec, &list) < 0) {\n+\t\t\tret = 1;\n+\t\t\tgoto cleanup;\n+\t\t}\n \n \t\t/*\n \t\t * If there are no path args and submodule.active is set then,\n@@ -2686,7 +2712,9 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t}\n \n \tret = update_submodules(&opt);\n+cleanup:\n \tlist_objects_filter_release(&filter_options);\n+\tclear_pathspec(&pathspec);\n \treturn ret;\n }\n \n@@ -2770,9 +2798,10 @@ static int push_check(int argc, const char **argv, const char *prefix)\n static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tunsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;\n+\tint ret = 1;\n \n \tstruct option embed_gitdir_options[] = {\n \t\tOPT_STRING(0, \"prefix\", &prefix,\n@@ -2792,12 +2821,15 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tfor (i = 0; i < list.nr; i++)\n \t\tabsorb_git_dir_into_superproject(list.entries[i]->name, flags);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static int is_active(int argc, const char **argv, const char *prefix)\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459692","messageId":"patch-v3-07.26-325aa1521e2-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 07/26] submodule--helper: \"struct pathspec\" memory leak in module_update()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:03Z","receivedAt":"2022-07-21T19:14:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The module_update() function calls module_list_compute() twice, which\nin turn will reset the \"struct pathspec\" passed to it. Let's instead\ntrack two of them, and clear them both.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex a60dc6af178..f317236f016 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2602,6 +2602,7 @@ static int update_submodules(struct update_data *update_data)\n static int module_update(int argc, const char **argv, const char *prefix)\n {\n \tstruct pathspec pathspec = { 0 };\n+\tstruct pathspec pathspec2 = { 0 };\n \tstruct update_data opt = UPDATE_DATA_INIT;\n \tstruct list_objects_filter_options filter_options = { 0 };\n \tint ret;\n@@ -2692,7 +2693,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\tstruct init_cb info = INIT_CB_INIT;\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n-\t\t\t\t\t&pathspec, &list) < 0) {\n+\t\t\t\t\t&pathspec2, &list) < 0) {\n \t\t\tret = 1;\n \t\t\tgoto cleanup;\n \t\t}\n@@ -2715,6 +2716,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n cleanup:\n \tlist_objects_filter_release(&filter_options);\n \tclear_pathspec(&pathspec);\n+\tclear_pathspec(&pathspec2);\n \treturn ret;\n }\n \n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459693","messageId":"patch-v3-08.26-424b24961b5-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 08/26] submodule--helper: don't leak {run,capture}_command() cp.dir argument","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:04Z","receivedAt":"2022-07-21T19:14:07Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak in c51f8f94e5b (submodule--helper: run update\nprocedures from C, 2021-08-24) and 3c3558f0953 (submodule--helper: run\nupdate using child process struct, 2022-03-15) by not allocating\nmemory in the first place.\n\nThe \"dir\" member of \"struct child_process\" will not be modified by\nthat API, and it's declared to be \"const char *\". So let's not\nneedlessly duplicate these strings.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex f317236f016..fb3102f024f 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2194,7 +2194,7 @@ static int is_tip_reachable(const char *path, struct object_id *oid)\n \tchar *hex = oid_to_hex(oid);\n \n \tcp.git_cmd = 1;\n-\tcp.dir = xstrdup(path);\n+\tcp.dir = path;\n \tcp.no_stderr = 1;\n \tstrvec_pushl(&cp.args, \"rev-list\", \"-n\", \"1\", hex, \"--not\", \"--all\", NULL);\n \n@@ -2212,7 +2212,7 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str\n \n \tprepare_submodule_repo_env(&cp.env);\n \tcp.git_cmd = 1;\n-\tcp.dir = xstrdup(module_path);\n+\tcp.dir = module_path;\n \n \tstrvec_push(&cp.args, \"fetch\");\n \tif (quiet)\n@@ -2267,7 +2267,7 @@ static int run_update_command(struct update_data *ud, int subforce)\n \t}\n \tstrvec_push(&cp.args, oid);\n \n-\tcp.dir = xstrdup(ud->sm_path);\n+\tcp.dir = ud->sm_path;\n \tprepare_submodule_repo_env(&cp.env);\n \tif (run_command(&cp)) {\n \t\tswitch (ud->update_strategy.type) {\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459694","messageId":"patch-v3-09.26-abf5c4754a4-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 09/26] submodule--helper: add \"const\" to copy of \"update_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:05Z","receivedAt":"2022-07-21T19:14:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"From: Glen Choo <chooglen@google.com>\n\nAdd a \"const\" to the copy of \"struct update_data\" that's tracked by\nthe \"struct submodule_update_clone\", as it neither owns nor modifies\nit.\n\nSigned-off-by: Glen Choo <chooglen@google.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex fb3102f024f..3223c93f91a 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1889,7 +1889,7 @@ struct submodule_update_clone {\n \tint current;\n \n \t/* configuration parameters which are passed on to the children */\n-\tstruct update_data *update_data;\n+\tconst struct update_data *update_data;\n \n \t/* to be consumed by update_submodule() */\n \tstruct update_clone_data *update_clone;\n@@ -1974,7 +1974,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,\n \tconst char *update_string;\n \tenum submodule_update_type update_type;\n \tchar *key;\n-\tstruct update_data *ud = suc->update_data;\n+\tconst struct update_data *ud = suc->update_data;\n \tchar *displaypath = get_submodule_displaypath(ce->name, ud->prefix);\n \tstruct strbuf sb = STRBUF_INIT;\n \tint needs_cloning = 0;\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459696","messageId":"patch-v3-10.26-6ab5aabae35-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 10/26] submodule--helper: add and use *_release() functions","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:06Z","receivedAt":"2022-07-21T19:14:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add release functions for \"struct module_list\", \"struct\nsubmodule_update_clone\" and \"struct update_data\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c      | 29 ++++++++++++++++++++++++++++-\n t/t6134-pathspec-in-submodule.sh |  1 +\n 2 files changed, 29 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 3223c93f91a..21b3abb7b40 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -182,6 +182,11 @@ struct module_list {\n };\n #define MODULE_LIST_INIT { 0 }\n \n+static void module_list_release(struct module_list *ml)\n+{\n+\tfree(ml->entries);\n+}\n+\n static int module_list_compute(int argc, const char **argv,\n \t\t\t       const char *prefix,\n \t\t\t       struct pathspec *pathspec,\n@@ -243,7 +248,7 @@ static void module_list_active(struct module_list *list)\n \t\tactive_modules.entries[active_modules.nr++] = ce;\n \t}\n \n-\tfree(list->entries);\n+\tmodule_list_release(list);\n \t*list = active_modules;\n }\n \n@@ -305,6 +310,7 @@ static int module_list(int argc, const char **argv, const char *prefix)\n \t}\n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -461,6 +467,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -606,6 +613,7 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -783,6 +791,7 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1376,6 +1385,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1528,6 +1538,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1904,6 +1915,12 @@ struct submodule_update_clone {\n };\n #define SUBMODULE_UPDATE_CLONE_INIT { 0 }\n \n+static void submodule_update_clone_release(struct submodule_update_clone *suc)\n+{\n+\tfree(suc->update_clone);\n+\tfree(suc->failed_clones);\n+}\n+\n struct update_data {\n \tconst char *prefix;\n \tconst char *displaypath;\n@@ -1942,6 +1959,11 @@ struct update_data {\n \t.max_jobs = 1, \\\n }\n \n+static void update_data_release(struct update_data *ud)\n+{\n+\tmodule_list_release(&ud->list);\n+}\n+\n static void next_submodule_warn_missing(struct submodule_update_clone *suc,\n \t\tstruct strbuf *out, const char *displaypath)\n {\n@@ -2595,6 +2617,7 @@ static int update_submodules(struct update_data *update_data)\n \t}\n \n cleanup:\n+\tsubmodule_update_clone_release(&suc);\n \tstring_list_clear(&update_data->references, 0);\n \treturn res;\n }\n@@ -2694,6 +2717,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n \t\t\t\t\t&pathspec2, &list) < 0) {\n+\t\t\tmodule_list_release(&list);\n \t\t\tret = 1;\n \t\t\tgoto cleanup;\n \t\t}\n@@ -2710,10 +2734,12 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\t\tinfo.flags |= OPT_QUIET;\n \n \t\tfor_each_listed_submodule(&list, init_submodule_cb, &info);\n+\t\tmodule_list_release(&list);\n \t}\n \n \tret = update_submodules(&opt);\n cleanup:\n+\tupdate_data_release(&opt);\n \tlist_objects_filter_release(&filter_options);\n \tclear_pathspec(&pathspec);\n \tclear_pathspec(&pathspec2);\n@@ -2831,6 +2857,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n \tret = 0;\n cleanup:\n \tclear_pathspec(&pathspec);\n+\tmodule_list_release(&list);\n \treturn ret;\n }\n \ndiff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh\nindex 0f1cb49cedc..3a241f259de 100755\n--- a/t/t6134-pathspec-in-submodule.sh\n+++ b/t/t6134-pathspec-in-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='test case exclude pathspec'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a submodule' '\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459697","messageId":"patch-v3-11.26-feec1f20bf9-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 11/26] submodule--helper: refactor \"errmsg_str\" to be a \"struct strbuf\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:07Z","receivedAt":"2022-07-21T19:14:20Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"From: Glen Choo <chooglen@google.com>\n\nRefactor code added in e83e3333b57 (submodule: port submodule\nsubcommand 'summary' from shell to C, 2020-08-13) so that \"errmsg\" and\n\"errmsg_str\" are folded into one. The distinction between the empty\nstring and NULL is something that's tested for by\ne.g. \"t/t7401-submodule-summary.sh\".\n\nThis is in preparation for fixing a memory leak the \"struct strbuf\" in\nthe pre-image.\n\nLet's also pass a \"const char *\" to print_submodule_summary(), as it\nshould not be modifying the \"errmsg\".\n\nSigned-off-by: Glen Choo <chooglen@google.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 14 ++++++--------\n 1 file changed, 6 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 21b3abb7b40..f794d2b588b 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -866,7 +866,7 @@ static char *verify_submodule_committish(const char *sm_path,\n \treturn strbuf_detach(&result, NULL);\n }\n \n-static void print_submodule_summary(struct summary_cb *info, char *errmsg,\n+static void print_submodule_summary(struct summary_cb *info, const char *errmsg,\n \t\t\t\t    int total_commits, const char *displaypath,\n \t\t\t\t    const char *src_abbrev, const char *dst_abbrev,\n \t\t\t\t    struct module_cb *p)\n@@ -924,7 +924,7 @@ static void generate_submodule_summary(struct summary_cb *info,\n {\n \tchar *displaypath, *src_abbrev = NULL, *dst_abbrev;\n \tint missing_src = 0, missing_dst = 0;\n-\tchar *errmsg = NULL;\n+\tstruct strbuf errmsg = STRBUF_INIT;\n \tint total_commits = -1;\n \n \tif (!info->cached && oideq(&p->oid_dst, null_oid())) {\n@@ -1024,23 +1024,21 @@ static void generate_submodule_summary(struct summary_cb *info,\n \t\t * submodule, i.e., deleted or changed to blob\n \t\t */\n \t\tif (S_ISGITLINK(p->mod_dst)) {\n-\t\t\tstruct strbuf errmsg_str = STRBUF_INIT;\n \t\t\tif (missing_src && missing_dst) {\n-\t\t\t\tstrbuf_addf(&errmsg_str, \"  Warn: %s doesn't contain commits %s and %s\\n\",\n+\t\t\t\tstrbuf_addf(&errmsg, \"  Warn: %s doesn't contain commits %s and %s\\n\",\n \t\t\t\t\t    displaypath, oid_to_hex(&p->oid_src),\n \t\t\t\t\t    oid_to_hex(&p->oid_dst));\n \t\t\t} else {\n-\t\t\t\tstrbuf_addf(&errmsg_str, \"  Warn: %s doesn't contain commit %s\\n\",\n+\t\t\t\tstrbuf_addf(&errmsg, \"  Warn: %s doesn't contain commit %s\\n\",\n \t\t\t\t\t    displaypath, missing_src ?\n \t\t\t\t\t    oid_to_hex(&p->oid_src) :\n \t\t\t\t\t    oid_to_hex(&p->oid_dst));\n \t\t\t}\n-\t\t\terrmsg = strbuf_detach(&errmsg_str, NULL);\n \t\t}\n \t}\n \n-\tprint_submodule_summary(info, errmsg, total_commits,\n-\t\t\t\tdisplaypath, src_abbrev,\n+\tprint_submodule_summary(info, errmsg.len ? errmsg.buf : NULL,\n+\t\t\t\ttotal_commits, displaypath, src_abbrev,\n \t\t\t\tdst_abbrev, p);\n \n \tfree(displaypath);\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459698","messageId":"patch-v3-12.26-d368db73de7-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 12/26] submodule--helper: fix \"errmsg_str\" memory leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:08Z","receivedAt":"2022-07-21T19:14:21Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak introduced in e83e3333b57 (submodule: port submodule\nsubcommand 'summary' from shell to C, 2020-08-13), we sometimes append\nto the \"errmsg\", and need to free the \"struct strbuf\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex f794d2b588b..ad11ff20022 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1044,6 +1044,7 @@ static void generate_submodule_summary(struct summary_cb *info,\n \tfree(displaypath);\n \tfree(src_abbrev);\n \tfree(dst_abbrev);\n+\tstrbuf_release(&errmsg);\n }\n \n static void prepare_submodule_summary(struct summary_cb *info,\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459699","messageId":"patch-v3-13.26-5be941b3d1b-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 13/26] submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:09Z","receivedAt":"2022-07-21T19:14:29Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix leaks in \"struct module_cb_list\" and the \"struct module_cb\" which\nit contains, these fixes leaks in e83e3333b57 (submodule: port\nsubmodule subcommand 'summary' from shell to C, 2020-08-13).\n\nThe \"sm_path\" should always have been a \"char *\", not a \"const\nchar *\", we always create it with xstrdup().\n\nWe can't mark any tests passing passing with SANITIZE=leak using\n\"TEST_PASSES_SANITIZE_LEAK=true\" as a result of this change, but\n\"t7401-submodule-summary.sh\" gets closer to passing as a result of\nthis change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 21 ++++++++++++++++++++-\n 1 file changed, 20 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex ad11ff20022..a5492c6d89a 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -820,16 +820,34 @@ struct module_cb {\n \tstruct object_id oid_src;\n \tstruct object_id oid_dst;\n \tchar status;\n-\tconst char *sm_path;\n+\tchar *sm_path;\n };\n #define MODULE_CB_INIT { 0 }\n \n+static void module_cb_release(struct module_cb *mcb)\n+{\n+\tfree(mcb->sm_path);\n+}\n+\n struct module_cb_list {\n \tstruct module_cb **entries;\n \tint alloc, nr;\n };\n #define MODULE_CB_LIST_INIT { 0 }\n \n+static void module_cb_list_release(struct module_cb_list *mcbl)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < mcbl->nr; i++) {\n+\t\tstruct module_cb *mcb = mcbl->entries[i];\n+\n+\t\tmodule_cb_release(mcb);\n+\t\tfree(mcb);\n+\t}\n+\tfree(mcbl->entries);\n+}\n+\n struct summary_cb {\n \tint argc;\n \tconst char **argv;\n@@ -1172,6 +1190,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n cleanup:\n \tstrvec_clear(&diff_args);\n \trelease_revisions(&rev);\n+\tmodule_cb_list_release(&list);\n \treturn ret;\n }\n \n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459700","messageId":"patch-v3-14.26-b8560e8c111-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 14/26] submodule--helper: fix a leak with repo_clear()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:10Z","receivedAt":"2022-07-21T19:14:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call repo_clear() in ensure_core_worktree() to free the \"struct\nrepository\". Fixes a leak that's been here since\n74d4731da1f (submodule--helper: replace connect-gitdir-workingtree by\nensure-core-worktree, 2018-08-13).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex a5492c6d89a..a3de17754fd 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2458,6 +2458,7 @@ static void ensure_core_worktree(const char *path)\n \t\tfree(abs_path);\n \t\tstrbuf_release(&sb);\n \t}\n+\trepo_clear(&subrepo);\n }\n \n static const char *submodule_update_type_to_label(enum submodule_update_type type)\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459701","messageId":"patch-v3-15.26-abfd61f9f05-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 15/26] submodule--helper: fix a memory leak in get_default_remote_submodule()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:11Z","receivedAt":"2022-07-21T19:14:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak in the get_default_remote_submodule() function added\nin a77c3fcb5ec (submodule--helper: get remote names from any\nrepository, 2022-03-04), we need to repo_clear() the submodule we\ninitialize.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex a3de17754fd..fe490c8d44d 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -62,9 +62,13 @@ static char *repo_get_default_remote(struct repository *repo)\n static char *get_default_remote_submodule(const char *module_path)\n {\n \tstruct repository subrepo;\n+\tchar *ret;\n \n \trepo_submodule_init(&subrepo, the_repository, module_path, null_oid());\n-\treturn repo_get_default_remote(&subrepo);\n+\tret = repo_get_default_remote(&subrepo);\n+\trepo_clear(&subrepo);\n+\n+\treturn ret;\n }\n \n static char *get_default_remote(void)\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459702","messageId":"patch-v3-16.26-b8df96a9cf0-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 16/26] submodule--helper: fix \"reference\" leak is \"module_clone_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:12Z","receivedAt":"2022-07-21T19:14:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix leaks in the \"reference\" member of \"struct module_clone_data\" that\nhave been with us since 31224cbdc72 (clone: recursive and reference\noption triggers submodule alternates, 2016-08-17) and\n8c8195e9c3e (submodule--helper: introduce add-clone subcommand,\n2021-07-10).\n\nThose commits added an xstrdup()'d member of the\nSTRING_LIST_INIT_NODUP'd \"struct string_list\". We need to free()\nthose, but not the ones we get from argv, let's make use of the \"util\"\nmember, if it has a pointer it's the pointer we'll need to free,\notherwise it'll be NULL (i.e. from argv).\n\nTo fix this create a a module_clone_data_release() function to go with\nthe MODULE_CLONE_DATA_INIT added in a98b02c1128 (submodule--helper:\nrefactor module_clone(), 2021-07-10). We only need to add it to\nadd_submodule() to fix the leak, but let's add it to module_clone() as\nwell for consistency.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 26 ++++++++++++++++++++------\n 1 file changed, 20 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex fe490c8d44d..80c1c1d4918 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1584,6 +1584,11 @@ struct module_clone_data {\n \t.single_branch = -1, \\\n }\n \n+static void module_clone_data_release(struct module_clone_data *cd)\n+{\n+\tstring_list_clear(&cd->reference, 1);\n+}\n+\n struct submodule_alternate_setup {\n \tconst char *submodule_name;\n \tenum SUBMODULE_ALTERNATE_ERROR_MODE {\n@@ -1636,7 +1641,9 @@ static int add_possible_reference_from_superproject(\n \n \t\tsm_alternate = compute_alternate_path(sb.buf, &err);\n \t\tif (sm_alternate) {\n-\t\t\tstring_list_append(sas->reference, xstrdup(sb.buf));\n+\t\t\tchar *p = strbuf_detach(&sb, NULL);\n+\n+\t\t\tstring_list_append(sas->reference, p)->util = p;\n \t\t\tfree(sm_alternate);\n \t\t} else {\n \t\t\tswitch (sas->error_mode) {\n@@ -1873,6 +1880,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \n \tclone_submodule(&clone_data, &clone_data.reference);\n \tlist_objects_filter_release(&filter_options);\n+\tmodule_clone_data_release(&clone_data);\n \treturn 0;\n }\n \n@@ -3117,6 +3125,7 @@ static int add_submodule(const struct add_data *add_data)\n {\n \tchar *submod_gitdir_path;\n \tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n+\tint ret = -1;\n \n \t/* perhaps the path already exists and is already a git repo, else clone it */\n \tif (is_directory(add_data->sm_path)) {\n@@ -3171,15 +3180,17 @@ static int add_submodule(const struct add_data *add_data)\n \t\tclone_data.url = add_data->realrepo;\n \t\tclone_data.quiet = add_data->quiet;\n \t\tclone_data.progress = add_data->progress;\n-\t\tif (add_data->reference_path)\n-\t\t\tstring_list_append(&clone_data.reference,\n-\t\t\t\t\t   xstrdup(add_data->reference_path));\n+\t\tif (add_data->reference_path) {\n+\t\t\tchar *p = xstrdup(add_data->reference_path);\n+\n+\t\t\tstring_list_append(&clone_data.reference, p)->util = p;\n+\t\t}\n \t\tclone_data.dissociate = add_data->dissociate;\n \t\tif (add_data->depth >= 0)\n \t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n \n \t\tif (clone_submodule(&clone_data, &clone_data.reference))\n-\t\t\treturn -1;\n+\t\t\tgoto cleanup;\n \n \t\tprepare_submodule_repo_env(&cp.env);\n \t\tcp.git_cmd = 1;\n@@ -3198,7 +3209,10 @@ static int add_submodule(const struct add_data *add_data)\n \t\tif (run_command(&cp))\n \t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n \t}\n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tmodule_clone_data_release(&clone_data);\n+\treturn ret;\n }\n \n static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459703","messageId":"patch-v3-17.26-7811bdbf149-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 17/26] submodule--helper: fix obscure leak in module_add()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:13Z","receivedAt":"2022-07-21T19:14:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix an obscure leak in module_add()< if the \"git add\" command we were\npiping to failed we'd fail to strbuf_release(&sb). This fixes a leak\nintroduced in a6226fd772b (submodule--helper: convert the bulk of\ncmd_add() to C, 2021-08-10).\n\nIn fixing it move to a \"goto cleanup\" pattern, and since we need to\nintroduce a \"ret\" variable to do that let's also get rid of the\nintermediate \"exit_code\" variable. The initialization to \"-1\" in\na6226fd772b has always been redundant, we'd only use the \"exit_code\"\nvalue after assigning the return value of pipe_command() to it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 22 +++++++++++-----------\n 1 file changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 80c1c1d4918..04b5141c0f9 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3352,6 +3352,8 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \tint force = 0, quiet = 0, progress = 0, dissociate = 0;\n \tstruct add_data add_data = ADD_DATA_INIT;\n \tchar *to_free = NULL;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint ret = 1;\n \n \tstruct option options[] = {\n \t\tOPT_STRING('b', \"branch\", &add_data.branch, N_(\"branch\"),\n@@ -3423,20 +3425,16 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \tdie_on_repo_without_commits(add_data.sm_path);\n \n \tif (!force) {\n-\t\tint exit_code = -1;\n-\t\tstruct strbuf sb = STRBUF_INIT;\n \t\tstruct child_process cp = CHILD_PROCESS_INIT;\n \t\tcp.git_cmd = 1;\n \t\tcp.no_stdout = 1;\n \t\tstrvec_pushl(&cp.args, \"add\", \"--dry-run\", \"--ignore-missing\",\n \t\t\t     \"--no-warn-embedded-repo\", add_data.sm_path, NULL);\n-\t\tif ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {\n+\t\tif ((ret = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {\n \t\t\tstrbuf_complete_line(&sb);\n \t\t\tfputs(sb.buf, stderr);\n-\t\t\tfree(add_data.sm_path);\n-\t\t\treturn exit_code;\n+\t\t\tgoto cleanup;\n \t\t}\n-\t\tstrbuf_release(&sb);\n \t}\n \n \tif(!add_data.sm_name)\n@@ -3451,15 +3449,17 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \tadd_data.progress = !!progress;\n \tadd_data.dissociate = !!dissociate;\n \n-\tif (add_submodule(&add_data)) {\n-\t\tfree(add_data.sm_path);\n-\t\treturn 1;\n-\t}\n+\tif (add_submodule(&add_data))\n+\t\tgoto cleanup;\n \tconfigure_added_submodule(&add_data);\n+\n+\tret = 0;\n+cleanup:\n \tfree(add_data.sm_path);\n \tfree(to_free);\n+\tstrbuf_release(&sb);\n \n-\treturn 0;\n+\treturn ret;\n }\n \n #define SUPPORT_SUPER_PREFIX (1<<0)\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459704","messageId":"patch-v3-18.26-01566d63926-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 18/26] submodule--helper: fix a leak in module_add()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:14Z","receivedAt":"2022-07-21T19:14:54Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in module_path(), since a6226fd772b (submodule--helper:\nconvert the bulk of cmd_add() to C, 2021-08-10), we've been freeing\nadd_data.sm_path, but in this case we clobbered it, and didn't free\nthe value we clobbered.\n\nThis makes test 28 of \"t/t7400-submodule-basic.sh\" (\"submodule add in\nsubdirectory\") pass when we're compiled with SANITIZE=leak..\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 7 +++++--\n 1 file changed, 5 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 04b5141c0f9..0fb720d3a16 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3395,8 +3395,11 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \telse\n \t\tadd_data.sm_path = xstrdup(argv[1]);\n \n-\tif (prefix && *prefix && !is_absolute_path(add_data.sm_path))\n-\t\tadd_data.sm_path = xstrfmt(\"%s%s\", prefix, add_data.sm_path);\n+\tif (prefix && *prefix && !is_absolute_path(add_data.sm_path)) {\n+\t\tchar *sm_path = add_data.sm_path;\n+\t\tadd_data.sm_path = xstrfmt(\"%s%s\", prefix, sm_path);\n+\t\tfree(sm_path);\n+\t}\n \n \tif (starts_with_dot_dot_slash(add_data.repo) ||\n \t    starts_with_dot_slash(add_data.repo)) {\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459705","messageId":"patch-v3-19.26-7ef89abed86-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 19/26] submodule--helper: fix a memory leak in print_status()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:15Z","receivedAt":"2022-07-21T19:14:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in print_status(), the compute_rev_name() function\nimplemented in this file will return a strbuf_detach()'d value, or\nNULL.\n\nThis leak has existed since this code was added in\na9f8a37584a (submodule: port submodule subcommand 'status' from shell\nto C, 2017-10-06), but in 0b5e2ea7cf3 (submodule--helper: don't print\nnull in 'submodule status', 2018-04-18) we added a \"const\"\nintermediate variable for the return value, that \"const\" should be\nremoved.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 0fb720d3a16..4decbdcdc5c 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -637,10 +637,11 @@ static void print_status(unsigned int flags, char state, const char *path,\n \tprintf(\"%c%s %s\", state, oid_to_hex(oid), displaypath);\n \n \tif (state == ' ' || state == '+') {\n-\t\tconst char *name = compute_rev_name(path, oid_to_hex(oid));\n+\t\tchar *name = compute_rev_name(path, oid_to_hex(oid));\n \n \t\tif (name)\n \t\t\tprintf(\" (%s)\", name);\n+\t\tfree(name);\n \t}\n \n \tprintf(\"\\n\");\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459706","messageId":"patch-v3-20.26-b8d47fc7d70-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 20/26] submodule--helper: free some \"displaypath\" in \"struct update_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:16Z","receivedAt":"2022-07-21T19:15:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Make the update_data_release() function free \"displaypath\" member when\nappropriate. The \"displaypath\" member is always ours, the \"const\" on\nthe \"char *\" was wrong to begin with.\n\nThis leaves a leak of \"displaypath\" in update_submodule(), which as\nwe'll see in subsequent commits is harder to deal with than this\ntrivial fix.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c        | 3 ++-\n t/t2403-worktree-move.sh           | 1 +\n t/t7412-submodule-absorbgitdirs.sh | 1 +\n 3 files changed, 4 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 4decbdcdc5c..378f85e37fa 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1954,7 +1954,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)\n \n struct update_data {\n \tconst char *prefix;\n-\tconst char *displaypath;\n+\tchar *displaypath;\n \tenum submodule_update_type update_default;\n \tstruct object_id suboid;\n \tstruct string_list references;\n@@ -1992,6 +1992,7 @@ struct update_data {\n \n static void update_data_release(struct update_data *ud)\n {\n+\tfree(ud->displaypath);\n \tmodule_list_release(&ud->list);\n }\n \ndiff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh\nindex a4e1a178e0a..1168e9f9982 100755\n--- a/t/t2403-worktree-move.sh\n+++ b/t/t2403-worktree-move.sh\n@@ -2,6 +2,7 @@\n \n test_description='test git worktree move, remove, lock and unlock'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh\nindex 1cfa150768d..2859695c6d2 100755\n--- a/t/t7412-submodule-absorbgitdirs.sh\n+++ b/t/t7412-submodule-absorbgitdirs.sh\n@@ -6,6 +6,7 @@ This test verifies that `git submodue absorbgitdirs` moves a submodules git\n directory into the superproject.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a real submodule' '\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459707","messageId":"patch-v3-21.26-4fb17f0dff1-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 21/26] submodule--helper: rename \"int res\" to \"int ret\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:17Z","receivedAt":"2022-07-21T19:15:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the \"res\" variable added in b3c5f5cb048 (submodule: move core\ncmd_update() logic to C, 2022-03-15) to \"ret\", which is the convention\nin the rest of this file. Subsequent commits will change this code to\na \"goto cleanup\" pattern, let's have the post image look consistent\nwith the rest.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 12 ++++++------\n 1 file changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 378f85e37fa..94eec34c595 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2588,7 +2588,7 @@ static int update_submodule(struct update_data *update_data)\n \tif (update_data->recursive) {\n \t\tstruct child_process cp = CHILD_PROCESS_INIT;\n \t\tstruct update_data next = *update_data;\n-\t\tint res;\n+\t\tint ret;\n \n \t\tnext.prefix = NULL;\n \t\toidcpy(&next.oid, null_oid());\n@@ -2600,14 +2600,14 @@ static int update_submodule(struct update_data *update_data)\n \t\tupdate_data_to_args(&next, &cp.args);\n \n \t\t/* die() if child process die()'d */\n-\t\tres = run_command(&cp);\n-\t\tif (!res)\n+\t\tret = run_command(&cp);\n+\t\tif (!ret)\n \t\t\treturn 0;\n \t\tdie_message(_(\"Failed to recurse into submodule path '%s'\"),\n \t\t\t    update_data->displaypath);\n-\t\tif (res == 128)\n-\t\t\texit(res);\n-\t\telse if (res)\n+\t\tif (ret == 128)\n+\t\t\texit(ret);\n+\t\telse if (ret)\n \t\t\treturn 1;\n \t}\n \n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459708","messageId":"patch-v3-22.26-af83925046b-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 22/26] submodule--helper: add skeleton \"goto cleanup\" to update_submodule()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:18Z","receivedAt":"2022-07-21T19:15:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a skeleton \"goto cleanup\" pattern to update_submodule(), rather\nthan having branches in it \"return\". This is in preparation for doing\nsomething useful with the \"cleanup\" label, but for now we're using it\nas the equivalent of a \"done\" label.\n\nThe \"exit()\" branch is not handled yet, and neither is the exit() that\nrun_update_procedure() might invoke. That'll be handled in a\nsubsequent commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 22 ++++++++++++++--------\n 1 file changed, 14 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 94eec34c595..790f0ccb82e 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2545,6 +2545,8 @@ static void update_data_to_args(struct update_data *update_data, struct strvec *\n \n static int update_submodule(struct update_data *update_data)\n {\n+\tint ret = 1;\n+\n \tensure_core_worktree(update_data->sm_path);\n \n \tupdate_data->displaypath = get_submodule_displaypath(\n@@ -2581,14 +2583,14 @@ static int update_submodule(struct update_data *update_data)\n \t\tfree(remote_ref);\n \t}\n \n-\tif (!oideq(&update_data->oid, &update_data->suboid) || update_data->force)\n+\tif (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) {\n \t\tif (run_update_procedure(update_data))\n-\t\t\treturn 1;\n+\t\t\tgoto cleanup;\n+\t}\n \n \tif (update_data->recursive) {\n \t\tstruct child_process cp = CHILD_PROCESS_INIT;\n \t\tstruct update_data next = *update_data;\n-\t\tint ret;\n \n \t\tnext.prefix = NULL;\n \t\toidcpy(&next.oid, null_oid());\n@@ -2602,16 +2604,20 @@ static int update_submodule(struct update_data *update_data)\n \t\t/* die() if child process die()'d */\n \t\tret = run_command(&cp);\n \t\tif (!ret)\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\tdie_message(_(\"Failed to recurse into submodule path '%s'\"),\n \t\t\t    update_data->displaypath);\n-\t\tif (ret == 128)\n+\t\tif (ret == 128) {\n \t\t\texit(ret);\n-\t\telse if (ret)\n-\t\t\treturn 1;\n+\t\t} else if (ret) {\n+\t\t\tret = 1;\n+\t\t\tgoto cleanup;\n+\t\t}\n \t}\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\treturn ret;\n }\n \n static int update_submodules(struct update_data *update_data)\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459709","messageId":"patch-v3-23.26-4c60784d281-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 23/26] submodule--helper: don't exit() on failure, return","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:19Z","receivedAt":"2022-07-21T19:15:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change code downstream of module_update() to short-circuit and return\nto the top-level on failure, rather than calling exit().\n\nTo do so we need to diligently check whether we \"must_die_on_failure\",\nwhich is a pattern started in c51f8f94e5b (submodule--helper: run\nupdate procedures from C, 2021-08-24), but which hadn't been completed\nto the point where we could avoid calling exit() here.\n\nThis introduces no functional changes, but makes it easier to both\ncall these routines as a library in the future, and to avoid leaking\nmemory.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 36 ++++++++++++++++++++++++++----------\n 1 file changed, 26 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 790f0ccb82e..b65665105e7 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2283,7 +2283,8 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str\n \treturn run_command(&cp);\n }\n \n-static int run_update_command(struct update_data *ud, int subforce)\n+static int run_update_command(struct update_data *ud, int subforce,\n+\t\t\t      int *must_die_on_failurep)\n {\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tchar *oid = oid_to_hex(&ud->oid);\n@@ -2345,8 +2346,10 @@ static int run_update_command(struct update_data *ud, int subforce)\n \t\t\tBUG(\"unexpected update strategy type: %s\",\n \t\t\t    submodule_strategy_to_string(&ud->update_strategy));\n \t\t}\n-\t\tif (must_die_on_failure)\n-\t\t\texit(128);\n+\t\tif (must_die_on_failure) {\n+\t\t\t*must_die_on_failurep = 1;\n+\t\t\treturn 128;\n+\t\t}\n \n \t\t/* the command failed, but update must continue */\n \t\treturn 1;\n@@ -2380,7 +2383,8 @@ static int run_update_command(struct update_data *ud, int subforce)\n \treturn 0;\n }\n \n-static int run_update_procedure(struct update_data *ud)\n+static int run_update_procedure(struct update_data *ud,\n+\t\t\t\tint *must_die_on_failure)\n {\n \tint subforce = is_null_oid(&ud->suboid) || ud->force;\n \n@@ -2407,7 +2411,7 @@ static int run_update_procedure(struct update_data *ud)\n \t\t\t    ud->displaypath, oid_to_hex(&ud->oid));\n \t}\n \n-\treturn run_update_command(ud, subforce);\n+\treturn run_update_command(ud, subforce, must_die_on_failure);\n }\n \n static const char *remote_submodule_branch(const char *path)\n@@ -2543,7 +2547,8 @@ static void update_data_to_args(struct update_data *update_data, struct strvec *\n \t\t\t\t    \"--no-single-branch\");\n }\n \n-static int update_submodule(struct update_data *update_data)\n+static int update_submodule(struct update_data *update_data,\n+\t\t\t    int *must_die_on_failure)\n {\n \tint ret = 1;\n \n@@ -2584,8 +2589,13 @@ static int update_submodule(struct update_data *update_data)\n \t}\n \n \tif (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) {\n-\t\tif (run_update_procedure(update_data))\n+\t\tret = run_update_procedure(update_data, must_die_on_failure);\n+\t\tif (ret && *must_die_on_failure) {\n+\t\t\tgoto cleanup;\n+\t\t} else if (ret) {\n+\t\t\tret = 1;\n \t\t\tgoto cleanup;\n+\t\t}\n \t}\n \n \tif (update_data->recursive) {\n@@ -2608,7 +2618,8 @@ static int update_submodule(struct update_data *update_data)\n \t\tdie_message(_(\"Failed to recurse into submodule path '%s'\"),\n \t\t\t    update_data->displaypath);\n \t\tif (ret == 128) {\n-\t\t\texit(ret);\n+\t\t\t*must_die_on_failure = 1;\n+\t\t\tgoto cleanup;\n \t\t} else if (ret) {\n \t\t\tret = 1;\n \t\t\tgoto cleanup;\n@@ -2646,13 +2657,18 @@ static int update_submodules(struct update_data *update_data)\n \n \tfor (i = 0; i < suc.update_clone_nr; i++) {\n \t\tstruct update_clone_data ucd = suc.update_clone[i];\n+\t\tint code;\n+\t\tint must_die_on_failure = 0;\n \n \t\toidcpy(&update_data->oid, &ucd.oid);\n \t\tupdate_data->just_cloned = ucd.just_cloned;\n \t\tupdate_data->sm_path = ucd.sub->path;\n \n-\t\tif (update_submodule(update_data))\n-\t\t\tres = 1;\n+\t\tcode = update_submodule(update_data, &must_die_on_failure);\n+\t\tif (code)\n+\t\t\tres = code;\n+\t\tif (must_die_on_failure)\n+\t\t\tgoto cleanup;\n \t}\n \n cleanup:\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459710","messageId":"patch-v3-24.26-7551af195ad-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 24/26] submodule--helper: free rest of \"displaypath\" in \"struct update_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:20Z","receivedAt":"2022-07-21T19:15:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in code added in c51f8f94e5b (submodule--helper: run update\nprocedures from C, 2021-08-24), we clobber the \"displaypath\" member of\nthe passed-in \"struct update_data\" both so that die() messages in this\nupdate_submodule() function itself can use it, and for the\nrun_update_procedure() called within this function.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex b65665105e7..4e70a74357c 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2551,10 +2551,11 @@ static int update_submodule(struct update_data *update_data,\n \t\t\t    int *must_die_on_failure)\n {\n \tint ret = 1;\n+\tchar *to_free, *restore = update_data->displaypath;\n \n \tensure_core_worktree(update_data->sm_path);\n \n-\tupdate_data->displaypath = get_submodule_displaypath(\n+\tupdate_data->displaypath = to_free = get_submodule_displaypath(\n \t\tupdate_data->sm_path, update_data->prefix);\n \n \tdetermine_submodule_update_strategy(the_repository, update_data->just_cloned,\n@@ -2628,6 +2629,9 @@ static int update_submodule(struct update_data *update_data,\n \n \tret = 0;\n cleanup:\n+\tfree(to_free);\n+\tupdate_data->displaypath = restore;\n+\n \treturn ret;\n }\n \n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459711","messageId":"patch-v3-25.26-f650716cd7e-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 25/26] submodule--helper: fix bad config API usage","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:21Z","receivedAt":"2022-07-21T19:15:28Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix bad config API usage added in a452128a36c (submodule--helper:\nintroduce add-config subcommand, 2021-08-06). After\ngit_config_get_string() returns successfully we know the \"char **dest\"\nwill be non-NULL.\n\nA coccinelle patch that transforms this turns up a couple of other\nsuch issues, one in fetch-pack.c, and another in upload-pack.c:\n\n\t@@\n\tidentifier F =~ \"^(repo|git)_config_get_string(_tmp)?$\";\n\tidentifier V;\n\t@@\n\t  !F(..., &V)\n\t- && (V)\n\nBut let's focus narrowly on submodule--helper for now, we can fix\nthose some other time.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 4e70a74357c..37c145b3ede 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3306,7 +3306,7 @@ static void configure_added_submodule(struct add_data *add_data)\n \t * is_submodule_active(), since that function needs to find\n \t * out the value of \"submodule.active\" again anyway.\n \t */\n-\tif (!git_config_get_string(\"submodule.active\", &val) && val) {\n+\tif (!git_config_get_string(\"submodule.active\", &val)) {\n \t\t/*\n \t\t * If the submodule being added isn't already covered by the\n \t\t * current configured pathspec, set the submodule's active flag\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459712","messageId":"patch-v3-26.26-581ce0872c0-20220721T191249Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v3 26/26] submodule--helper: fix a configure_added_submodule() leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-21T19:13:22Z","receivedAt":"2022-07-21T19:15:29Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix config API a memory leak added in a452128a36c (submodule--helper:\nintroduce add-config subcommand, 2021-08-06) by using the *_tmp()\nvariant of git_config_get_string().\n\nIn this case we're only checking whether\nthe (repo|git)_config_get_string() call is telling us that the\n\"submodule.active\" key exists.\n\nAs with the preceding commit we'll find many other such patterns in\nthe codebase if we go fishing. E.g. \"git gc\" leaks in the code added\nin 61f7a383d3b (maintenance: use 'incremental' strategy by default,\n2020-10-15). Similar code in \"git gc\" added in\nb08ff1fee00 (maintenance: add --schedule option and config,\n2020-09-11) doesn't leak, but we could avoid the malloc() & free() in\nthat case.\n\nA coccinelle rule to find those would find and fix some leaks, and\ncases where we're doing needless malloc() + free()'s but only care\nabout the key existence, or are copying\nthe (repo|git)_config_get_string() return value right away.\n\nBut as with the preceding commit let's punt on all of that for now,\nand just narrowly fix this specific case in submodule--helper.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c    | 4 ++--\n t/t7413-submodule-is-active.sh | 1 +\n 2 files changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 37c145b3ede..fdf96c5d424 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3261,7 +3261,7 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con\n static void configure_added_submodule(struct add_data *add_data)\n {\n \tchar *key;\n-\tchar *val = NULL;\n+\tconst char *val;\n \tstruct child_process add_submod = CHILD_PROCESS_INIT;\n \tstruct child_process add_gitmodules = CHILD_PROCESS_INIT;\n \n@@ -3306,7 +3306,7 @@ static void configure_added_submodule(struct add_data *add_data)\n \t * is_submodule_active(), since that function needs to find\n \t * out the value of \"submodule.active\" again anyway.\n \t */\n-\tif (!git_config_get_string(\"submodule.active\", &val)) {\n+\tif (!git_config_get_string_tmp(\"submodule.active\", &val)) {\n \t\t/*\n \t\t * If the submodule being added isn't already covered by the\n \t\t * current configured pathspec, set the submodule's active flag\ndiff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh\nindex c8e7e983317..c4c1f86f3d2 100755\n--- a/t/t7413-submodule-is-active.sh\n+++ b/t/t7413-submodule-is-active.sh\n@@ -6,6 +6,7 @@ This test verifies that `git submodue--helper is-active` correctly identifies\n submodules which are \"active\" and interesting to the user.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n-- \n2.37.1.1095.g0bd6f54ba8a\n\n"},{"id":"459718","messageId":"kl6lbktitf6e.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-v2-05.24-a4672aa9c94-20220719T204458Z-avarab@gmail.com","subject":"Re: [PATCH v2 05/24] submodule--helper: \"struct pathspec\" memory leak in module_update()","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-21T19:54:17Z","receivedAt":"2022-07-21T19:54:22Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> The module_update() function calls module_list_compute() twice, which\n> in turn will reset the \"struct pathspec\" passed to it. Let's instead\n> track two of them, and clear them both.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 4 +++-\n>  1 file changed, 3 insertions(+), 1 deletion(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index 28c5fdb8954..7466e781e97 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -2602,6 +2602,7 @@ static int update_submodules(struct update_data *update_data)\n>  static int module_update(int argc, const char **argv, const char *prefix)\n>  {\n>  \tstruct pathspec pathspec = { 0 };\n> +\tstruct pathspec pathspec2 = { 0 };\n>  \tstruct update_data opt = UPDATE_DATA_INIT;\n>  \tstruct list_objects_filter_options filter_options = { 0 };\n>  \tint ret;\n> @@ -2692,7 +2693,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n>  \t\tstruct init_cb info = INIT_CB_INIT;\n>  \n>  \t\tif (module_list_compute(argc, argv, opt.prefix,\n> -\t\t\t\t\t&pathspec, &list) < 0) {\n> +\t\t\t\t\t&pathspec2, &list) < 0) {\n>  \t\t\tret = 1;\n>  \t\t\tgoto cleanup;\n>  \t\t}\n\nThis change looks good, but we should do more refactoring in the future.\n\nThis bit of code inside \"if (opt.init)\" was copied over from\nmodule_init() in 29a5e9e1ff (submodule--helper update-clone: learn\n--init, 2022-03-04). Prior to that commit, we used to just invoke \"git\nsubmodule init\" in git-submodule.sh.\n\nWhat I wished I had done instead is to create a helper function that can\nbe used by both module_init() and module_update(). Something like:\n\n----- >8 --------- >8 --------- >8 --------- >8 --------- >8 ----\n  diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n  index 28c5fdb895..2040cde4ba 100644\n  --- a/builtin/submodule--helper.c\n  +++ b/builtin/submodule--helper.c\n  @@ -481,14 +481,14 @@ static int starts_with_dot_dot_slash(const char *const path)\n          PATH_MATCH_XPLATFORM);\n  }\n\n  -struct init_cb {\n  +struct init_opts {\n    const char *prefix;\n  -\tunsigned int flags;\n  +\tint quiet;\n  };\n  #define INIT_CB_INIT { 0 }\n\n  static void init_submodule(const char *path, const char *prefix,\n  -\t\t\t   unsigned int flags)\n  +\t\t\t   int quiet)\n  {\n    const struct submodule *sub;\n    struct strbuf sb = STRBUF_INIT;\n  @@ -538,7 +538,7 @@ static void init_submodule(const char *path, const char *prefix,\n      if (git_config_set_gently(sb.buf, url))\n        die(_(\"Failed to register url for submodule path '%s'\"),\n            displaypath);\n  -\t\tif (!(flags & OPT_QUIET))\n  +\t\tif (!quiet)\n        fprintf(stderr,\n          _(\"Submodule '%s' (%s) registered for path '%s'\\n\"),\n          sub->name, url, displaypath);\n  @@ -567,15 +567,39 @@ static void init_submodule(const char *path, const char *prefix,\n\n  static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data)\n  {\n  -\tstruct init_cb *info = cb_data;\n  -\tinit_submodule(list_item->name, info->prefix, info->flags);\n  +\tstruct init_opts *info = cb_data;\n  +\tinit_submodule(list_item->name, info->prefix, info->quiet);\n  }\n\n  -static int module_init(int argc, const char **argv, const char *prefix)\n  +static int init_submodules(int argc, const char **argv, struct init_opts *opts)\n  {\n  -\tstruct init_cb info = INIT_CB_INIT;\n  -\tstruct pathspec pathspec = { 0 };\n    struct module_list list = MODULE_LIST_INIT;\n  +\tstruct pathspec pathspec = { 0 };\n  +\tint ret = 0;\n  +\n  +\tif (module_list_compute(argc, argv, opts->prefix,\n  +\t\t\t\t&pathspec, &list) < 0) {\n  +\t\tret = 1;\n  +\t\tgoto cleanup;\n  +\t}\n  +\n  +\t/*\n  +\t * If there are no path args and submodule.active is set then,\n  +\t * by default, only initialize 'active' modules.\n  +\t */\n  +\tif (!argc && git_config_get_value_multi(\"submodule.active\"))\n  +\t\tmodule_list_active(&list);\n  +\n  +\tfor_each_listed_submodule(&list, init_submodule_cb, opts);\n  +\n  + cleanup:\n  +\tclear_pathspec(&pathspec);\n  +\treturn ret;\n  +}\n  +\n  +static int module_init(int argc, const char **argv, const char *prefix)\n  +{\n  +\tstruct init_opts info = INIT_CB_INIT;\n    int quiet = 0;\n\n    struct option module_init_options[] = {\n  @@ -587,33 +611,14 @@ static int module_init(int argc, const char **argv, const char *prefix)\n      N_(\"git submodule init [<options>] [<path>]\"),\n      NULL\n    };\n  -\tint ret;\n\n    argc = parse_options(argc, argv, prefix, module_init_options,\n            git_submodule_helper_usage, 0);\n\n  -\tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {\n  -\t\tret = 1;\n  -\t\tgoto cleanup;\n  -\t}\n  -\n  -\t/*\n  -\t * If there are no path args and submodule.active is set then,\n  -\t * by default, only initialize 'active' modules.\n  -\t */\n  -\tif (!argc && git_config_get_value_multi(\"submodule.active\"))\n  -\t\tmodule_list_active(&list);\n  -\n    info.prefix = prefix;\n  -\tif (quiet)\n  -\t\tinfo.flags |= OPT_QUIET;\n  -\n  -\tfor_each_listed_submodule(&list, init_submodule_cb, &info);\n  +\tinfo.quiet = quiet;\n\n  -\tret = 0;\n  -cleanup:\n  -\tclear_pathspec(&pathspec);\n  -\treturn ret;\n  +\treturn init_submodules(argc, argv, &info);\n  }\n\n  struct status_cb {\n  @@ -2688,27 +2693,13 @@ static int module_update(int argc, const char **argv, const char *prefix)\n      opt.warn_if_uninitialized = 1;\n\n    if (opt.init) {\n  -\t\tstruct module_list list = MODULE_LIST_INIT;\n  -\t\tstruct init_cb info = INIT_CB_INIT;\n  -\n  -\t\tif (module_list_compute(argc, argv, opt.prefix,\n  -\t\t\t\t\t&pathspec, &list) < 0) {\n  -\t\t\tret = 1;\n  +\t\tstruct init_opts init_opts = {\n  +\t\t\t.quiet = opt.quiet,\n  +\t\t\t.prefix = opt.prefix,\n  +\t\t};\n  +\t\tret = init_submodules(argc, argv, &init_opts);\n  +\t\tif (ret)\n        goto cleanup;\n  -\t\t}\n  -\n  -\t\t/*\n  -\t\t * If there are no path args and submodule.active is set then,\n  -\t\t * by default, only initialize 'active' modules.\n  -\t\t */\n  -\t\tif (!argc && git_config_get_value_multi(\"submodule.active\"))\n  -\t\t\tmodule_list_active(&list);\n  -\n  -\t\tinfo.prefix = opt.prefix;\n  -\t\tif (opt.quiet)\n  -\t\t\tinfo.flags |= OPT_QUIET;\n  -\n  -\t\tfor_each_listed_submodule(&list, init_submodule_cb, &info);\n    }\n\n    ret = update_submodules(&opt);\n----- >8 --------- >8 --------- >8 --------- >8 --------- >8 ----\n\nI don't think we need to do this now; it's already pretty noisy for just\na \"leak cleanup\" series, and it isn't even complete (IIRC you mentioned\nthat we could probably drop init_submodule_cb(), but I can't find the\nmessage any more).\n\nHopefully someone will pick this up. I doubt I'll have the time to do\nit, but maybe.\n\n> @@ -2715,6 +2716,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n>  cleanup:\n>  \tlist_objects_filter_release(&filter_options);\n>  \tclear_pathspec(&pathspec);\n> +\tclear_pathspec(&pathspec2);\n>  \treturn ret;\n>  }\n>  \n> -- \n> 2.37.1.1062.g385eac7fccf\n"},{"id":"459726","messageId":"xmqqlesmf9or.fsf@gitster.g","threadId":"58151","inReplyTo":"patch-v3-02.26-32e4ae7ead5-20220721T191249Z-avarab@gmail.com","subject":"Re: [PATCH v3 02/26] submodule--helper: stop conflating \"sb\" in clone_submodule()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-21T21:16:36Z","receivedAt":"2022-07-21T21:16:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> -static int clone_submodule(struct module_clone_data *clone_data)\n> +static char *clone_submodule_sm_gitdir(const char *name)\n>  {\n> -\tchar *p, *sm_gitdir;\n> -\tchar *sm_alternate = NULL, *error_strategy = NULL;\n>  \tstruct strbuf sb = STRBUF_INIT;\n> -\tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\tchar *sm_gitdir;\n>  \n> -\tsubmodule_name_to_gitdir(&sb, the_repository, clone_data->name);\n> +\tsubmodule_name_to_gitdir(&sb, the_repository, name);\n>  \tsm_gitdir = absolute_pathdup(sb.buf);\n> -\tstrbuf_reset(&sb);\n> +\tstrbuf_release(&sb);\n> +\n> +\treturn sm_gitdir;\n> +}\n\nOK.\n\n> +static int clone_submodule(struct module_clone_data *clone_data)\n> +{\n> +\tchar *p;\n> +\tchar *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);\n> +\tchar *sm_alternate = NULL, *error_strategy = NULL;\n> +\tstruct child_process cp = CHILD_PROCESS_INIT;\n>  \n>  \tif (!is_absolute_path(clone_data->path)) {\n> +\t\tstruct strbuf sb = STRBUF_INIT;\n> +\n>  \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n>  \t\tclone_data->path = strbuf_detach(&sb, NULL);\n\nThis looks like a roundabout way to say xstrfmt().\n\n>  \t} else {\n> @@ -1705,6 +1715,8 @@ static int clone_submodule(struct module_clone_data *clone_data)\n>  \t\t\tdie(_(\"clone of '%s' into submodule path '%s' failed\"),\n>  \t\t\t    clone_data->url, clone_data->path);\n>  \t} else {\n> +\t\tstruct strbuf sb = STRBUF_INIT;\n> +\n>  \t\tif (clone_data->require_init && !access(clone_data->path, X_OK) &&\n>  \t\t    !is_empty_dir(clone_data->path))\n>  \t\t\tdie(_(\"directory not empty: '%s'\"), clone_data->path);\n> @@ -1712,7 +1724,7 @@ static int clone_submodule(struct module_clone_data *clone_data)\n>  \t\t\tdie(_(\"could not create directory '%s'\"), clone_data->path);\n>  \t\tstrbuf_addf(&sb, \"%s/index\", sm_gitdir);\n>  \t\tunlink_or_warn(sb.buf);\n> -\t\tstrbuf_reset(&sb);\n> +\t\tstrbuf_release(&sb);\n\nDitto.\n\n>  \t}\n>  \n>  \tconnect_work_tree_and_git_dir(clone_data->path, sm_gitdir, 0);\n> @@ -1734,7 +1746,6 @@ static int clone_submodule(struct module_clone_data *clone_data)\n>  \tfree(sm_alternate);\n>  \tfree(error_strategy);\n>  \n> -\tstrbuf_release(&sb);\n>  \tfree(sm_gitdir);\n>  \tfree(p);\n>  \treturn 0;\n"},{"id":"459728","messageId":"xmqq8romf986.fsf@gitster.g","threadId":"58151","inReplyTo":"patch-v3-03.26-eee6ca28c6f-20220721T191249Z-avarab@gmail.com","subject":"Re: [PATCH v3 03/26] submodule--helper: pass a \"const struct module_clone_data\" to clone_submodule()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-21T21:26:33Z","receivedAt":"2022-07-21T21:26:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> -static int clone_submodule(struct module_clone_data *clone_data)\n> +static int clone_submodule(const struct module_clone_data *clone_data,\n> +\t\t\t   struct string_list *reference)\n>  {\n>  \tchar *p;\n>  \tchar *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);\n>  \tchar *sm_alternate = NULL, *error_strategy = NULL;\n>  \tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\tconst char *clone_data_path;\n>  \n>  \tif (!is_absolute_path(clone_data->path)) {\n>  \t\tstruct strbuf sb = STRBUF_INIT;\n>  \n>  \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n> -\t\tclone_data->path = strbuf_detach(&sb, NULL);\n> +\t\tclone_data_path = strbuf_detach(&sb, NULL);\n\nOK.\n\n>  \t} else {\n> -\t\tclone_data->path = xstrdup(clone_data->path);\n> +\t\tclone_data_path = xstrdup(clone_data_path);\n\nIs the variable we are duplicating by passing it to xstrdup() still\nuninitialized at this point?  How could the compiler not catch this?\n\nApparently there is no test coverage on this codepath; all tests\npass when I replace this else clause with BUG().\n\nLet's read on, pretending that you passed clone_data->path to\nxstrdup(), for now.\n\n> @@ -1674,7 +1676,7 @@ static int clone_submodule(struct module_clone_data *clone_data)\n>  \t\tif (safe_create_leading_directories_const(sm_gitdir) < 0)\n>  \t\t\tdie(_(\"could not create directory '%s'\"), sm_gitdir);\n>  \n> -\t\tprepare_possible_alternates(clone_data->name, &clone_data->reference);\n> +\t\tprepare_possible_alternates(clone_data->name, reference);\n>  \n>  \t\tstrvec_push(&cp.args, \"clone\");\n>  \t\tstrvec_push(&cp.args, \"--no-checkout\");\n> @@ -1684,9 +1686,9 @@ static int clone_submodule(struct module_clone_data *clone_data)\n>  \t\t\tstrvec_push(&cp.args, \"--progress\");\n>  \t\tif (clone_data->depth && *(clone_data->depth))\n>  \t\t\tstrvec_pushl(&cp.args, \"--depth\", clone_data->depth, NULL);\n> -\t\tif (clone_data->reference.nr) {\n> +\t\tif (reference->nr) {\n>  \t\t\tstruct string_list_item *item;\n> -\t\t\tfor_each_string_list_item(item, &clone_data->reference)\n> +\t\t\tfor_each_string_list_item(item, reference)\n>  \t\t\t\tstrvec_pushl(&cp.args, \"--reference\",\n>  \t\t\t\t\t     item->string, NULL);\n>  \t\t}\n\nAll the uses of reference (both above, so below) looks OK.\n\nI wonder if we can simply have a separate string list variable in\nmodule_clone() and add_submodule() and remove clone_data.reference\nmember, which may make the end-result even cleaner, now that we pass\nit as a separate parameter anyway.\n"},{"id":"459729","messageId":"xmqq35euf92d.fsf@gitster.g","threadId":"58151","inReplyTo":"patch-v3-04.26-26f0caf0386-20220721T191249Z-avarab@gmail.com","subject":"Re: [PATCH v3 04/26] submodule--helper: fix a leak in \"clone_submodule\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-21T21:30:02Z","receivedAt":"2022-07-21T21:30:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index 7d5ee6a6261..1ddce8e19c1 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -1658,14 +1658,15 @@ static int clone_submodule(const struct module_clone_data *clone_data,\n>  \tchar *sm_alternate = NULL, *error_strategy = NULL;\n>  \tstruct child_process cp = CHILD_PROCESS_INIT;\n>  \tconst char *clone_data_path;\n> +\tchar *to_free = NULL;\n>  \n>  \tif (!is_absolute_path(clone_data->path)) {\n>  \t\tstruct strbuf sb = STRBUF_INIT;\n>  \n>  \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n> -\t\tclone_data_path = strbuf_detach(&sb, NULL);\n> +\t\tclone_data_path = to_free = strbuf_detach(&sb, NULL);\n>  \t} else {\n> -\t\tclone_data_path = xstrdup(clone_data_path);\n> +\t\tclone_data_path = clone_data->path;\n>  \t}\n\nHeh, the bug I noticed in the previous step is silently fixed here.\n\nThis is why I do not trust a series that is artificially split into\nsteps and sent out without self reviewing or even compiling them.\nForces reviewers to do more work wasting their time reviewing and\nfinding bugs that ends up not mattering because that weren't even\nsomething the author intended to write in the end.\n"},{"id":"459730","messageId":"xmqqr12edu65.fsf@gitster.g","threadId":"58151","inReplyTo":"patch-v3-06.26-7672ef1305f-20220721T191249Z-avarab@gmail.com","subject":"Re: [PATCH v3 06/26] submodule--helper: fix most \"struct pathspec\" memory leaks","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-21T21:37:06Z","receivedAt":"2022-07-21T21:37:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Call clear_pathspec() at the end of various functions that work with\n> and allocate a \"struct pathspec\".\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 84 +++++++++++++++++++++++++------------\n>  1 file changed, 58 insertions(+), 26 deletions(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index 73ac1fcbb9c..a60dc6af178 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -269,7 +269,7 @@ static char *get_up_path(const char *path)\n>  static int module_list(int argc, const char **argv, const char *prefix)\n>  {\n>  \tint i;\n> -\tstruct pathspec pathspec;\n> +\tstruct pathspec pathspec = { 0 };\n>  \tstruct module_list list = MODULE_LIST_INIT;\n>  \n>  \tstruct option module_list_options[] = {\n> @@ -278,6 +278,7 @@ static int module_list(int argc, const char **argv, const char *prefix)\n>  \t\t\t   N_(\"alternative anchor for relative paths\")),\n>  \t\tOPT_END()\n>  \t};\n> +\tint ret = 1;\n\nMove this way above, perhaps next to where \"int i;\" is declared, or\nreplace the blank before where module_list_options[] is declared\nwith this line.\n\nThe same comment applies to changes to module_foreach() and\nstatus_submodule_cb() in this patch.  The change to\nabsorb_git_dirs() gets it right.\n\nThanks.\n"},{"id":"459733","messageId":"xmqqlesmdtsk.fsf@gitster.g","threadId":"58151","inReplyTo":"patch-v3-17.26-7811bdbf149-20220721T191249Z-avarab@gmail.com","subject":"Re: [PATCH v3 17/26] submodule--helper: fix obscure leak in module_add()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-21T21:45:15Z","receivedAt":"2022-07-21T21:45:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Fix an obscure leak in module_add()< if the \"git add\" command we were\n\n\"<\" -> \",\" perhaps?  I dunno.\n\n> piping to failed we'd fail to strbuf_release(&sb). This fixes a leak\n> introduced in a6226fd772b (submodule--helper: convert the bulk of\n> cmd_add() to C, 2021-08-10).\n>\n> In fixing it move to a \"goto cleanup\" pattern, and since we need to\n> introduce a \"ret\" variable to do that let's also get rid of the\n> intermediate \"exit_code\" variable. The initialization to \"-1\" in\n> a6226fd772b has always been redundant, we'd only use the \"exit_code\"\n> value after assigning the return value of pipe_command() to it.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 22 +++++++++++-----------\n>  1 file changed, 11 insertions(+), 11 deletions(-)\n\nThe patch looks good, especially with removal of \"exit_code\" it is\nvery clear what is going on and how errors are handled.\n\nNicely done.\n"},{"id":"459737","messageId":"xmqqedyedthf.fsf@gitster.g","threadId":"58151","inReplyTo":"patch-v3-26.26-581ce0872c0-20220721T191249Z-avarab@gmail.com","subject":"Re: [PATCH v3 26/26] submodule--helper: fix a configure_added_submodule() leak","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-21T21:51:56Z","receivedAt":"2022-07-21T21:52:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n>  static void configure_added_submodule(struct add_data *add_data)\n>  {\n>  \tchar *key;\n> -\tchar *val = NULL;\n> +\tconst char *val;\n>  \tstruct child_process add_submod = CHILD_PROCESS_INIT;\n>  \tstruct child_process add_gitmodules = CHILD_PROCESS_INIT;\n>  \n> @@ -3306,7 +3306,7 @@ static void configure_added_submodule(struct add_data *add_data)\n>  \t * is_submodule_active(), since that function needs to find\n>  \t * out the value of \"submodule.active\" again anyway.\n>  \t */\n> -\tif (!git_config_get_string(\"submodule.active\", &val)) {\n> +\tif (!git_config_get_string_tmp(\"submodule.active\", &val)) {\n\nObviously correct ;-)\n"},{"id":"459771","messageId":"220722.8635ets7q2.gmgdl@evledraar.gmail.com","threadId":"58151","inReplyTo":"xmqq35euf92d.fsf@gitster.g","subject":"Re: [PATCH v3 04/26] submodule--helper: fix a leak in \"clone_submodule\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-22T11:30:07Z","receivedAt":"2022-07-22T11:32:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Jul 21 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n>> index 7d5ee6a6261..1ddce8e19c1 100644\n>> --- a/builtin/submodule--helper.c\n>> +++ b/builtin/submodule--helper.c\n>> @@ -1658,14 +1658,15 @@ static int clone_submodule(const struct module_clone_data *clone_data,\n>>  \tchar *sm_alternate = NULL, *error_strategy = NULL;\n>>  \tstruct child_process cp = CHILD_PROCESS_INIT;\n>>  \tconst char *clone_data_path;\n>> +\tchar *to_free = NULL;\n>>  \n>>  \tif (!is_absolute_path(clone_data->path)) {\n>>  \t\tstruct strbuf sb = STRBUF_INIT;\n>>  \n>>  \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n>> -\t\tclone_data_path = strbuf_detach(&sb, NULL);\n>> +\t\tclone_data_path = to_free = strbuf_detach(&sb, NULL);\n>>  \t} else {\n>> -\t\tclone_data_path = xstrdup(clone_data_path);\n>> +\t\tclone_data_path = clone_data->path;\n>>  \t}\n>\n> Heh, the bug I noticed in the previous step is silently fixed here.\n>\n> This is why I do not trust a series that is artificially split into\n> steps and sent out without self reviewing or even compiling them.\n\nSorry for that mistake, will fix it in a re-roll. It was a result of\ndumb search-replacing.\n\nBut FWIW I did (and do generally) test my changes with \"git rebase -i -x\nmake...\" before sending them out.\n\nBut as you note this doesn't seem to have test coverage, and which gcc\nyells at me about this, clang (13.0.1-3+b2) does not.\n\nI generally test my changes with both gcc & clang before sending them\nout, but usually only use clang for the incremental compiling, I'll\nswitch back to gcc...\n"},{"id":"459778","messageId":"220722.86y1wlqmqr.gmgdl@evledraar.gmail.com","threadId":"58151","inReplyTo":"xmqqlesmf9or.fsf@gitster.g","subject":"Re: [PATCH v3 02/26] submodule--helper: stop conflating \"sb\" in clone_submodule()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-22T13:50:32Z","receivedAt":"2022-07-22T13:51:31Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Jul 21 2022, Junio C Hamano wrote:\n\n>> +static int clone_submodule(struct module_clone_data *clone_data)\n>> +{\n>> +\tchar *p;\n>> +\tchar *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);\n>> +\tchar *sm_alternate = NULL, *error_strategy = NULL;\n>> +\tstruct child_process cp = CHILD_PROCESS_INIT;\n>>  \n>>  \tif (!is_absolute_path(clone_data->path)) {\n>> +\t\tstruct strbuf sb = STRBUF_INIT;\n>> +\n>>  \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n>>  \t\tclone_data->path = strbuf_detach(&sb, NULL);\n>\n> This looks like a roundabout way to say xstrfmt().\n\nYes, I can fix this and others while I'm at it, but a lot of things like\nthat in this code are funny uses of APIs that we could improve.\n\nI think it's probably best to just leave these for now.\n\nBut I also don't mind adding another commit to this already large series\nto search/replace the relevant strbuf_detach() with xstrfmt()....\n\nJust let me know...\n"},{"id":"459782","messageId":"kl6l8rolt7ot.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"220722.86y1wlqmqr.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v3 02/26] submodule--helper: stop conflating \"sb\" in clone_submodule()","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-22T16:48:18Z","receivedAt":"2022-07-22T16:48:23Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> On Thu, Jul 21 2022, Junio C Hamano wrote:\n>\n>>> +static int clone_submodule(struct module_clone_data *clone_data)\n>>> +{\n>>> +\tchar *p;\n>>> +\tchar *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);\n>>> +\tchar *sm_alternate = NULL, *error_strategy = NULL;\n>>> +\tstruct child_process cp = CHILD_PROCESS_INIT;\n>>>  \n>>>  \tif (!is_absolute_path(clone_data->path)) {\n>>> +\t\tstruct strbuf sb = STRBUF_INIT;\n>>> +\n>>>  \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n>>>  \t\tclone_data->path = strbuf_detach(&sb, NULL);\n>>\n>> This looks like a roundabout way to say xstrfmt().\n>\n> Yes, I can fix this and others while I'm at it, but a lot of things like\n> that in this code are funny uses of APIs that we could improve.\n>\n> I think it's probably best to just leave these for now.\n\nYes, this series is already pretty long. For reviewability, perhaps we\ncould to keep this one focused on \"leaks fixes\" and leave the style\nfixes and refactoring for another series (which might include things\nlike [1]). As a bonus, with this series in place, we'll know that our\nrefactoring won't introduce more leaks :)\n\n[1] https://lore.kernel.org/git/kl6lbktitf6e.fsf@chooglen-macbookpro.roam.corp.google.com\n\n> But I also don't mind adding another commit to this already large series\n> to search/replace the relevant strbuf_detach() with xstrfmt()....\n>\n> Just let me know...\n"},{"id":"459785","messageId":"xmqq8rolc7cu.fsf@gitster.g","threadId":"58151","inReplyTo":"220722.86y1wlqmqr.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v3 02/26] submodule--helper: stop conflating \"sb\" in clone_submodule()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-22T18:47:29Z","receivedAt":"2022-07-22T18:47:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n>> This looks like a roundabout way to say xstrfmt().\n>\n> Yes, I can fix this and others while I'm at it, but a lot of things like\n> that in this code are funny uses of APIs that we could improve.\n>\n> I think it's probably best to just leave these for now.\n\nAgreed.  We could instead have a separate series to fix API usage\nbefore these and then build leak-plugging on top, or the other way\naround, and in general \"clean then plug\" would make it easier to\nreview the plugging patches (simply because it would be working on\nclean code, not code that misuses the API in strange ways), but it\nis too late now.  Lets make sure we do not forget to revisit the API\nmisuse but lets avoid mixing it into the series.\n"},{"id":"459897","messageId":"kl6l5yjlt934.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-v3-03.26-eee6ca28c6f-20220721T191249Z-avarab@gmail.com","subject":"Re: [PATCH v3 03/26] submodule--helper: pass a \"const struct module_clone_data\" to clone_submodule()","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-25T17:07:11Z","receivedAt":"2022-07-25T17:07:17Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Add \"const\" to the \"struct module_clone_data\" that we pass to\n> clone_submodule(), which makes the ownership clear, and stops us from\n> clobbering the \"clone_data->path\".\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 38 +++++++++++++++++++------------------\n>  1 file changed, 20 insertions(+), 18 deletions(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index 29641690c8c..7d5ee6a6261 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -1650,20 +1650,22 @@ static char *clone_submodule_sm_gitdir(const char *name)\n>  \treturn sm_gitdir;\n>  }\n>  \n> -static int clone_submodule(struct module_clone_data *clone_data)\n> +static int clone_submodule(const struct module_clone_data *clone_data,\n> +\t\t\t   struct string_list *reference)\n>  {\n>  \tchar *p;\n>  \tchar *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);\n>  \tchar *sm_alternate = NULL, *error_strategy = NULL;\n>  \tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\tconst char *clone_data_path;\n>  \n>  \tif (!is_absolute_path(clone_data->path)) {\n>  \t\tstruct strbuf sb = STRBUF_INIT;\n>  \n>  \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n> -\t\tclone_data->path = strbuf_detach(&sb, NULL);\n> +\t\tclone_data_path = strbuf_detach(&sb, NULL);\n>  \t} else {\n> -\t\tclone_data->path = xstrdup(clone_data->path);\n> +\t\tclone_data_path = xstrdup(clone_data_path);\n>  \t}\n>  \n>  \tif (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)\n\nHm, the leak in the preimage comes from the fact that we assign back to\nclone_data->path, which is a \"const char *\" that initially comes from\nargv. So we didn't free() it even though it always pointing to\nfree()-able memory past this point.\n\nSo now that we're introducing and assigning to a new variable,\nclone_data_path, wouldn't it be simpler to just make it \"char *\" and\nfree it (instead of adding a separate \"char *to_free\" in the next\npatch)?\n"},{"id":"459938","messageId":"kl6lwnc0ss0j.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-v3-11.26-feec1f20bf9-20220721T191249Z-avarab@gmail.com","subject":"Re: [PATCH v3 11/26] submodule--helper: refactor \"errmsg_str\" to be a \"struct strbuf\"","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-25T23:15:56Z","receivedAt":"2022-07-25T23:16:01Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> From: Glen Choo <chooglen@google.com>\n>\n> Refactor code added in e83e3333b57 (submodule: port submodule\n> subcommand 'summary' from shell to C, 2020-08-13) so that \"errmsg\" and\n> \"errmsg_str\" are folded into one. The distinction between the empty\n> string and NULL is something that's tested for by\n> e.g. \"t/t7401-submodule-summary.sh\".\n>\n> This is in preparation for fixing a memory leak the \"struct strbuf\" in\n> the pre-image.\n>\n> Let's also pass a \"const char *\" to print_submodule_summary(), as it\n> should not be modifying the \"errmsg\".\n>\n> Signed-off-by: Glen Choo <chooglen@google.com>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 14 ++++++--------\n>  1 file changed, 6 insertions(+), 8 deletions(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index 21b3abb7b40..f794d2b588b 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -866,7 +866,7 @@ static char *verify_submodule_committish(const char *sm_path,\n>  \treturn strbuf_detach(&result, NULL);\n>  }\n>  \n> -static void print_submodule_summary(struct summary_cb *info, char *errmsg,\n> +static void print_submodule_summary(struct summary_cb *info, const char *errmsg,\n>  \t\t\t\t    int total_commits, const char *displaypath,\n>  \t\t\t\t    const char *src_abbrev, const char *dst_abbrev,\n>  \t\t\t\t    struct module_cb *p)\n> @@ -924,7 +924,7 @@ static void generate_submodule_summary(struct summary_cb *info,\n>  {\n>  \tchar *displaypath, *src_abbrev = NULL, *dst_abbrev;\n>  \tint missing_src = 0, missing_dst = 0;\n> -\tchar *errmsg = NULL;\n> +\tstruct strbuf errmsg = STRBUF_INIT;\n>  \tint total_commits = -1;\n>  \n>  \tif (!info->cached && oideq(&p->oid_dst, null_oid())) {\n> @@ -1024,23 +1024,21 @@ static void generate_submodule_summary(struct summary_cb *info,\n>  \t\t * submodule, i.e., deleted or changed to blob\n>  \t\t */\n>  \t\tif (S_ISGITLINK(p->mod_dst)) {\n> -\t\t\tstruct strbuf errmsg_str = STRBUF_INIT;\n>  \t\t\tif (missing_src && missing_dst) {\n> -\t\t\t\tstrbuf_addf(&errmsg_str, \"  Warn: %s doesn't contain commits %s and %s\\n\",\n> +\t\t\t\tstrbuf_addf(&errmsg, \"  Warn: %s doesn't contain commits %s and %s\\n\",\n>  \t\t\t\t\t    displaypath, oid_to_hex(&p->oid_src),\n>  \t\t\t\t\t    oid_to_hex(&p->oid_dst));\n>  \t\t\t} else {\n> -\t\t\t\tstrbuf_addf(&errmsg_str, \"  Warn: %s doesn't contain commit %s\\n\",\n> +\t\t\t\tstrbuf_addf(&errmsg, \"  Warn: %s doesn't contain commit %s\\n\",\n>  \t\t\t\t\t    displaypath, missing_src ?\n>  \t\t\t\t\t    oid_to_hex(&p->oid_src) :\n>  \t\t\t\t\t    oid_to_hex(&p->oid_dst));\n>  \t\t\t}\n> -\t\t\terrmsg = strbuf_detach(&errmsg_str, NULL);\n>  \t\t}\n>  \t}\n>  \n> -\tprint_submodule_summary(info, errmsg, total_commits,\n> -\t\t\t\tdisplaypath, src_abbrev,\n> +\tprint_submodule_summary(info, errmsg.len ? errmsg.buf : NULL,\n> +\t\t\t\ttotal_commits, displaypath, src_abbrev,\n>  \t\t\t\tdst_abbrev, p);\n>  \n>  \tfree(displaypath);\n\nAh, this is mostly the same as what I sent out, but with the length\ncheck in the same function (instead of in print_submodule_summary()).\nThis makes it easier to tell that the caller is still doing the same\nthing.\n\nLooks good.\n"},{"id":"459943","messageId":"kl6ltu74sq3b.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-v3-23.26-4c60784d281-20220721T191249Z-avarab@gmail.com","subject":"Re: [PATCH v3 23/26] submodule--helper: don't exit() on failure, return","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-25T23:57:28Z","receivedAt":"2022-07-25T23:57:33Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Change code downstream of module_update() to short-circuit and return\n> to the top-level on failure, rather than calling exit().\n>\n> To do so we need to diligently check whether we \"must_die_on_failure\",\n> which is a pattern started in c51f8f94e5b (submodule--helper: run\n> update procedures from C, 2021-08-24), but which hadn't been completed\n> to the point where we could avoid calling exit() here.\n>\n> This introduces no functional changes, but makes it easier to both\n> call these routines as a library in the future, and to avoid leaking\n> memory.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 36 ++++++++++++++++++++++++++----------\n>  1 file changed, 26 insertions(+), 10 deletions(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index 790f0ccb82e..b65665105e7 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -2283,7 +2283,8 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str\n>  \treturn run_command(&cp);\n>  }\n>  \n> -static int run_update_command(struct update_data *ud, int subforce)\n> +static int run_update_command(struct update_data *ud, int subforce,\n> +\t\t\t      int *must_die_on_failurep)\n\nIt's not obvious in the context lines, but there is an auto variable\nnamed \"must_die_on_failure\", so we need the \"p\".\n\n>  {\n>  \tstruct child_process cp = CHILD_PROCESS_INIT;\n>  \tchar *oid = oid_to_hex(&ud->oid);\n> @@ -2345,8 +2346,10 @@ static int run_update_command(struct update_data *ud, int subforce)\n>  \t\t\tBUG(\"unexpected update strategy type: %s\",\n>  \t\t\t    submodule_strategy_to_string(&ud->update_strategy));\n>  \t\t}\n> -\t\tif (must_die_on_failure)\n> -\t\t\texit(128);\n> +\t\tif (must_die_on_failure) {\n> +\t\t\t*must_die_on_failurep = 1;\n> +\t\t\treturn 128;\n> +\t\t}\n\n[...]\n\n>  \n>  \t\t/* the command failed, but update must continue */\n>  \t\treturn 1;\n> @@ -2380,7 +2383,8 @@ static int run_update_command(struct update_data *ud, int subforce)\n>  \treturn 0;\n>  }\n>  \n> -static int run_update_procedure(struct update_data *ud)\n> +static int run_update_procedure(struct update_data *ud,\n> +\t\t\t\tint *must_die_on_failure)\n>  {\n>  \tint subforce = is_null_oid(&ud->suboid) || ud->force;\n>  \n> @@ -2407,7 +2411,7 @@ static int run_update_procedure(struct update_data *ud)\n>  \t\t\t    ud->displaypath, oid_to_hex(&ud->oid));\n>  \t}\n>  \n> -\treturn run_update_command(ud, subforce);\n> +\treturn run_update_command(ud, subforce, must_die_on_failure);\n>  }\n>  \n>  static const char *remote_submodule_branch(const char *path)\n> @@ -2543,7 +2547,8 @@ static void update_data_to_args(struct update_data *update_data, struct strvec *\n>  \t\t\t\t    \"--no-single-branch\");\n>  }\n>  \n> -static int update_submodule(struct update_data *update_data)\n> +static int update_submodule(struct update_data *update_data,\n> +\t\t\t    int *must_die_on_failure)\n>  {\n>  \tint ret = 1;\n>  \n> @@ -2584,8 +2589,13 @@ static int update_submodule(struct update_data *update_data)\n>  \t}\n>  \n>  \tif (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) {\n> -\t\tif (run_update_procedure(update_data))\n> +\t\tret = run_update_procedure(update_data, must_die_on_failure);\n> +\t\tif (ret && *must_die_on_failure) {\n> +\t\t\tgoto cleanup;\n> +\t\t} else if (ret) {\n> +\t\t\tret = 1;\n>  \t\t\tgoto cleanup;\n> +\t\t}\n>  \t}\n>  \n>  \tif (update_data->recursive) {\n> @@ -2608,7 +2618,8 @@ static int update_submodule(struct update_data *update_data)\n>  \t\tdie_message(_(\"Failed to recurse into submodule path '%s'\"),\n>  \t\t\t    update_data->displaypath);\n>  \t\tif (ret == 128) {\n> -\t\t\texit(ret);\n> +\t\t\t*must_die_on_failure = 1;\n> +\t\t\tgoto cleanup;\n\nOne important property in the preimage is that there's an obvious\nconnection between the exit(128) and this section here, i.e. the child\n\"git submodule update\" process failed in a way that the parent needs to\nstop immediately.\n\nWith this patch, this property is no longer obvious because we return\n128 from the lowest level (run_update_command()). By the time we reach\nthe top level (module_update()), it's no longer clear that the return\nvalue was meant to be 128.\n\nTwo ways we can fix this:\n\n1) Just return 128 at all levels to mean \"must die on failure\", which\n   will let us get rid of \"must_die_on_failure\".\n\nor...\n\n>  \t\t} else if (ret) {\n>  \t\t\tret = 1;\n>  \t\t\tgoto cleanup;\n> @@ -2646,13 +2657,18 @@ static int update_submodules(struct update_data *update_data)\n>  \n>  \tfor (i = 0; i < suc.update_clone_nr; i++) {\n>  \t\tstruct update_clone_data ucd = suc.update_clone[i];\n> +\t\tint code;\n> +\t\tint must_die_on_failure = 0;\n>  \n>  \t\toidcpy(&update_data->oid, &ucd.oid);\n>  \t\tupdate_data->just_cloned = ucd.just_cloned;\n>  \t\tupdate_data->sm_path = ucd.sub->path;\n>  \n> -\t\tif (update_submodule(update_data))\n> -\t\t\tres = 1;\n> +\t\tcode = update_submodule(update_data, &must_die_on_failure);\n> +\t\tif (code)\n> +\t\t\tres = code;\n> +\t\tif (must_die_on_failure)\n> +\t\t\tgoto cleanup;\n>  \t}\n\n2) In update_submodules() (i.e. just before module_update()), we make \n   update_submodules() return 128 if must_die_on_failure != 0. Then we\n   can drop the return value of 128 from the rest of the call chain and\n   just use an opaque nonzero return value instead.\n\n>  \n>  cleanup:\n> -- \n> 2.37.1.1095.g0bd6f54ba8a\n\n\n"},{"id":"459948","messageId":"kl6lr128smw2.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-v3-24.26-7551af195ad-20220721T191249Z-avarab@gmail.com","subject":"Re: [PATCH v3 24/26] submodule--helper: free rest of \"displaypath\" in \"struct update_data\"","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-07-26T01:06:37Z","receivedAt":"2022-07-26T01:06:43Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Fix a leak in code added in c51f8f94e5b (submodule--helper: run update\n> procedures from C, 2021-08-24), we clobber the \"displaypath\" member of\n> the passed-in \"struct update_data\" both so that die() messages in this\n> update_submodule() function itself can use it, and for the\n> run_update_procedure() called within this function.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 6 +++++-\n>  1 file changed, 5 insertions(+), 1 deletion(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index b65665105e7..4e70a74357c 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -2551,10 +2551,11 @@ static int update_submodule(struct update_data *update_data,\n>  \t\t\t    int *must_die_on_failure)\n>  {\n>  \tint ret = 1;\n> +\tchar *to_free, *restore = update_data->displaypath;\n>  \n>  \tensure_core_worktree(update_data->sm_path);\n>  \n> -\tupdate_data->displaypath = get_submodule_displaypath(\n> +\tupdate_data->displaypath = to_free = get_submodule_displaypath(\n>  \t\tupdate_data->sm_path, update_data->prefix);\n>\n>  \tdetermine_submodule_update_strategy(the_repository, update_data->just_cloned,\n> @@ -2628,6 +2629,9 @@ static int update_submodule(struct update_data *update_data,\n>  \n>  \tret = 0;\n>  cleanup:\n> +\tfree(to_free);\n> +\tupdate_data->displaypath = restore;\n> +\n>  \treturn ret;\n>  }\n\nI'm not sure why we need to have \"restore\". We set\n\"update_data->displaypath\" so that we can use it inside this function\n(and its call chain), but we don't care about it outside of this call\nchain at all.\n\nIf the goal is to avoid exposing a free()-d pointer, could we just do\n\n   FREE_AND_NULL(update_data->displaypath);\n\ninstead?\n\n>  \n> -- \n> 2.37.1.1095.g0bd6f54ba8a\n"},{"id":"460097","messageId":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com","subject":"[PATCH v4 00/17] submodule--helper: (only) fix memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:29:54Z","receivedAt":"2022-07-28T16:30:25Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This series fixes all of the memory leaks in\nbuiltin/submodule--helper.c that our test suite spots, except for\nthose where the leak is downstream of cmd_submodule__helper() and\ncaused by code that's not in builtin/submodule--helper.c (e.g. leaks\nin the object or config APIs, and in submodule.c).\n\nFor the v3 see [1]. The v3 was 26 patches, but this one's 17. The\nreason for the reduction is that this is now based on a just-submitted\n\"prep\" series[2].\n\nThe sum total is much larger now, but hopefully easier to digest, much\nof the new \"prep\" series is either trivial patches (like whitespace\nfixes), or deletion or moving (into test-tool) of code that this\n\"leak\" series doesn't need to deal with anymore.\n\nI think this should address all outstanding comments on the v3. In the\ncase of Glen's comment at [3] I dealt with my updating a commit\nmessage in the \"prep\" series, see the last two paragraphs of [4] (also\nshown in the range-diff of [2]).\n\n1. https://lore.kernel.org/git/cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com/\n2. https://lore.kernel.org/git/cover-00.20-00000000000-20220728T161116Z-avarab@gmail.com/\n3. https://lore.kernel.org/git/kl6ltu74sq3b.fsf@chooglen-macbookpro.roam.corp.google.com/\n4. https://lore.kernel.org/git/patch-19.20-5a26c9428be-20220728T161116Z-avarab@gmail.com/\n\nÆvar Arnfjörð Bjarmason (17):\n  submodule--helper: fix a leak in \"clone_submodule\"\n  submodule--helper: fix trivial get_default_remote_submodule() leak\n  submodule--helper: fix most \"struct pathspec\" memory leaks\n  submodule--helper: \"struct pathspec\" memory leak in module_update()\n  submodule--helper: don't leak {run,capture}_command() cp.dir argument\n  submodule--helper: add and use *_release() functions\n  submodule--helper: fix \"errmsg_str\" memory leak\n  submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks\n  submodule--helper: fix a leak with repo_clear()\n  submodule--helper: fix a memory leak in get_default_remote_submodule()\n  submodule--helper: fix \"reference\" leak\n  submodule--helper: fix obscure leak in module_add()\n  submodule--helper: fix a leak in module_add()\n  submodule--helper: fix a memory leak in print_status()\n  submodule--helper: free some \"displaypath\" in \"struct update_data\"\n  submodule--helper: free rest of \"displaypath\" in \"struct update_data\"\n  submodule--helper: fix a configure_added_submodule() leak\n\n builtin/submodule--helper.c        | 218 +++++++++++++++++++++--------\n t/t1500-rev-parse.sh               |   1 +\n t/t2403-worktree-move.sh           |   1 +\n t/t6008-rev-list-submodule.sh      |   1 +\n t/t6134-pathspec-in-submodule.sh   |   1 +\n t/t7412-submodule-absorbgitdirs.sh |   1 +\n t/t7413-submodule-is-active.sh     |   1 +\n t/t7414-submodule-mistakes.sh      |   2 +\n t/t7506-status-submodule.sh        |   1 +\n t/t7507-commit-verbose.sh          |   2 +\n 10 files changed, 171 insertions(+), 58 deletions(-)\n\nRange-diff against v3:\n 1:  3c7ae3e0222 <  -:  ----------- submodule--helper: replace memset() with { 0 }-initialization\n 2:  32e4ae7ead5 <  -:  ----------- submodule--helper: stop conflating \"sb\" in clone_submodule()\n 3:  eee6ca28c6f <  -:  ----------- submodule--helper: pass a \"const struct module_clone_data\" to clone_submodule()\n 4:  26f0caf0386 !  1:  aac987a414a submodule--helper: fix a leak in \"clone_submodule\"\n    @@ Commit message\n         on absolute paths, 2016-03-31).\n     \n         For the \"else\" case we don't need to xstrdup() the \"clone_data->path\",\n    -    and we don't need to free our own \"clone_data_path\".\n    +    and we don't need to free our own \"clone_data_path\". We can therefore\n    +    assign the \"clone_data->path\" to our own \"clone_data_path\" right away,\n    +    and only override it (and remember to free it!) if we need to\n    +    xstrfmt() a replacement.\n     \n         In the case of the module_clone() caller it's from \"argv\", and doesn't\n         need to be free'd, and in the case of the add_submodule() caller we\n    @@ Commit message\n     \n      ## builtin/submodule--helper.c ##\n     @@ builtin/submodule--helper.c: static int clone_submodule(const struct module_clone_data *clone_data,\n    + \tchar *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);\n      \tchar *sm_alternate = NULL, *error_strategy = NULL;\n      \tstruct child_process cp = CHILD_PROCESS_INIT;\n    - \tconst char *clone_data_path;\n    +-\tconst char *clone_data_path;\n    ++\tconst char *clone_data_path = clone_data->path;\n     +\tchar *to_free = NULL;\n      \n    - \tif (!is_absolute_path(clone_data->path)) {\n    - \t\tstruct strbuf sb = STRBUF_INIT;\n    - \n    - \t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n    --\t\tclone_data_path = strbuf_detach(&sb, NULL);\n    -+\t\tclone_data_path = to_free = strbuf_detach(&sb, NULL);\n    - \t} else {\n    --\t\tclone_data_path = xstrdup(clone_data_path);\n    -+\t\tclone_data_path = clone_data->path;\n    - \t}\n    + \tif (!is_absolute_path(clone_data->path))\n    +-\t\tclone_data_path = xstrfmt(\"%s/%s\", get_git_work_tree(),\n    +-\t\t\t\t\t  clone_data->path);\n    +-\telse\n    +-\t\tclone_data_path = xstrdup(clone_data->path);\n    ++\t\tclone_data_path = to_free = xstrfmt(\"%s/%s\", get_git_work_tree(),\n    ++\t\t\t\t\t\t    clone_data->path);\n      \n      \tif (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)\n    + \t\tdie(_(\"refusing to create/use '%s' in another submodule's \"\n     @@ builtin/submodule--helper.c: static int clone_submodule(const struct module_clone_data *clone_data,\n      \n      \tfree(sm_gitdir);\n 5:  75775bf4f6c !  2:  390c5174e17 submodule--helper: fix trivial get_default_remote_submodule() leak\n    @@ Commit message\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/submodule--helper.c ##\n    -@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)\n    +@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data,\n      \t\tconst char *branch = remote_submodule_branch(update_data->sm_path);\n      \t\tchar *remote_ref = xstrfmt(\"refs/remotes/%s/%s\", remote_name, branch);\n      \n 6:  7672ef1305f !  3:  529e10233d5 submodule--helper: fix most \"struct pathspec\" memory leaks\n    @@ Commit message\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/submodule--helper.c ##\n    -@@ builtin/submodule--helper.c: static char *get_up_path(const char *path)\n    - static int module_list(int argc, const char **argv, const char *prefix)\n    - {\n    - \tint i;\n    --\tstruct pathspec pathspec;\n    -+\tstruct pathspec pathspec = { 0 };\n    - \tstruct module_list list = MODULE_LIST_INIT;\n    - \n    - \tstruct option module_list_options[] = {\n    -@@ builtin/submodule--helper.c: static int module_list(int argc, const char **argv, const char *prefix)\n    - \t\t\t   N_(\"alternative anchor for relative paths\")),\n    - \t\tOPT_END()\n    - \t};\n    -+\tint ret = 1;\n    - \n    - \tconst char *const git_submodule_helper_usage[] = {\n    - \t\tN_(\"git submodule--helper list [--prefix=<path>] [<path>...]\"),\n    -@@ builtin/submodule--helper.c: static int module_list(int argc, const char **argv, const char *prefix)\n    - \t\t\t     git_submodule_helper_usage, 0);\n    - \n    - \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n    --\t\treturn 1;\n    -+\t\tgoto cleanup;\n    - \n    - \tfor (i = 0; i < list.nr; i++) {\n    - \t\tconst struct cache_entry *ce = list.entries[i];\n    -@@ builtin/submodule--helper.c: static int module_list(int argc, const char **argv, const char *prefix)\n    - \n    - \t\tfprintf(stdout, \"%s\\n\", ce->name);\n    - \t}\n    --\treturn 0;\n    -+\tret = 0;\n    -+cleanup:\n    -+\tclear_pathspec(&pathspec);\n    -+\treturn ret;\n    - }\n    - \n    - static void for_each_listed_submodule(const struct module_list *list,\n     @@ builtin/submodule--helper.c: static void runcommand_in_submodule_cb(const struct cache_entry *list_item,\n      static int module_foreach(int argc, const char **argv, const char *prefix)\n      {\n    @@ builtin/submodule--helper.c: static void runcommand_in_submodule_cb(const struct\n     -\tstruct pathspec pathspec;\n     +\tstruct pathspec pathspec = { 0 };\n      \tstruct module_list list = MODULE_LIST_INIT;\n    - \n      \tstruct option module_foreach_options[] = {\n    + \t\tOPT__QUIET(&info.quiet, N_(\"suppress output of entering each submodule command\")),\n     @@ builtin/submodule--helper.c: static int module_foreach(int argc, const char **argv, const char *prefix)\n      \t\tN_(\"git submodule foreach [--quiet] [--recursive] [--] <command>\"),\n      \t\tNULL\n    @@ builtin/submodule--helper.c: static void init_submodule_cb(const struct cache_en\n     +\tstruct pathspec pathspec = { 0 };\n      \tstruct module_list list = MODULE_LIST_INIT;\n      \tint quiet = 0;\n    - \n    + \tstruct option module_init_options[] = {\n     @@ builtin/submodule--helper.c: static int module_init(int argc, const char **argv, const char *prefix)\n      \t\tN_(\"git submodule init [<options>] [<path>]\"),\n      \t\tNULL\n    @@ builtin/submodule--helper.c: static void status_submodule_cb(const struct cache_\n     +\tstruct pathspec pathspec = { 0 };\n      \tstruct module_list list = MODULE_LIST_INIT;\n      \tint quiet = 0;\n    - \n    + \tstruct option module_status_options[] = {\n     @@ builtin/submodule--helper.c: static int module_status(int argc, const char **argv, const char *prefix)\n      \t\tN_(\"git submodule status [--quiet] [--cached] [--recursive] [<path>...]\"),\n      \t\tNULL\n    @@ builtin/submodule--helper.c: static int module_status(int argc, const char **arg\n     +\treturn ret;\n      }\n      \n    - static int module_name(int argc, const char **argv, const char *prefix)\n    + struct module_cb {\n     @@ builtin/submodule--helper.c: static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data\n      static int module_sync(int argc, const char **argv, const char *prefix)\n      {\n    @@ builtin/submodule--helper.c: static void sync_submodule_cb(const struct cache_en\n      \tstruct module_list list = MODULE_LIST_INIT;\n      \tint quiet = 0;\n      \tint recursive = 0;\n    +@@ builtin/submodule--helper.c: static int module_sync(int argc, const char **argv, const char *prefix)\n    + \t\tN_(\"git submodule sync [--quiet] [--recursive] [<path>]\"),\n    + \t\tNULL\n    + \t};\n     +\tint ret = 1;\n      \n    - \tstruct option module_sync_options[] = {\n    - \t\tOPT__QUIET(&quiet, N_(\"suppress output of synchronizing submodule url\")),\n    -@@ builtin/submodule--helper.c: static int module_sync(int argc, const char **argv, const char *prefix)\n    + \targc = parse_options(argc, argv, prefix, module_sync_options,\n      \t\t\t     git_submodule_helper_usage, 0);\n      \n      \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n    @@ builtin/submodule--helper.c: static int push_check(int argc, const char **argv,\n     +\tstruct pathspec pathspec = { 0 };\n      \tstruct module_list list = MODULE_LIST_INIT;\n      \tunsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;\n    -+\tint ret = 1;\n    - \n      \tstruct option embed_gitdir_options[] = {\n    - \t\tOPT_STRING(0, \"prefix\", &prefix,\n     @@ builtin/submodule--helper.c: static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n    + \t\tN_(\"git submodule absorbgitdirs [<options>] [<path>...]\"),\n    + \t\tNULL\n    + \t};\n    ++\tint ret = 1;\n    + \n    + \targc = parse_options(argc, argv, prefix, embed_gitdir_options,\n      \t\t\t     git_submodule_helper_usage, 0);\n      \n      \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n    @@ builtin/submodule--helper.c: static int absorb_git_dirs(int argc, const char **a\n     +\treturn ret;\n      }\n      \n    - static int is_active(int argc, const char **argv, const char *prefix)\n    + static int module_config(int argc, const char **argv, const char *prefix)\n 7:  325aa1521e2 =  4:  683d327752f submodule--helper: \"struct pathspec\" memory leak in module_update()\n 8:  424b24961b5 !  5:  4e8e9197539 submodule--helper: don't leak {run,capture}_command() cp.dir argument\n    @@ builtin/submodule--helper.c: static int fetch_in_submodule(const char *module_pa\n      \n      \tstrvec_push(&cp.args, \"fetch\");\n      \tif (quiet)\n    -@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)\n    +@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce,\n      \t}\n      \tstrvec_push(&cp.args, oid);\n      \n 9:  abf5c4754a4 <  -:  ----------- submodule--helper: add \"const\" to copy of \"update_data\"\n10:  6ab5aabae35 !  6:  575d3e8d2e2 submodule--helper: add and use *_release() functions\n    @@ builtin/submodule--helper.c: static void module_list_active(struct module_list *\n      \t*list = active_modules;\n      }\n      \n    -@@ builtin/submodule--helper.c: static int module_list(int argc, const char **argv, const char *prefix)\n    - \t}\n    - \tret = 0;\n    - cleanup:\n    -+\tmodule_list_release(&list);\n    - \tclear_pathspec(&pathspec);\n    - \treturn ret;\n    - }\n     @@ builtin/submodule--helper.c: static int module_foreach(int argc, const char **argv, const char *prefix)\n      \n      \tret = 0;\n11:  feec1f20bf9 <  -:  ----------- submodule--helper: refactor \"errmsg_str\" to be a \"struct strbuf\"\n12:  d368db73de7 =  7:  d4c81e04254 submodule--helper: fix \"errmsg_str\" memory leak\n13:  5be941b3d1b =  8:  ef9e29d5bfe submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks\n14:  b8560e8c111 =  9:  0798a00c9ef submodule--helper: fix a leak with repo_clear()\n15:  abfd61f9f05 = 10:  dae2a6f8e07 submodule--helper: fix a memory leak in get_default_remote_submodule()\n16:  b8df96a9cf0 ! 11:  e7352bb8cfa submodule--helper: fix \"reference\" leak is \"module_clone_data\"\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    submodule--helper: fix \"reference\" leak is \"module_clone_data\"\n    +    submodule--helper: fix \"reference\" leak\n     \n    -    Fix leaks in the \"reference\" member of \"struct module_clone_data\" that\n    -    have been with us since 31224cbdc72 (clone: recursive and reference\n    -    option triggers submodule alternates, 2016-08-17) and\n    -    8c8195e9c3e (submodule--helper: introduce add-clone subcommand,\n    -    2021-07-10).\n    +    Fix leaks in the \"reference\" variable declared in add_submodule() and\n    +    module_clone().\n    +\n    +    In preceding commits this variable was refactored out of the \"struct\n    +    module_clone_data\", but the leak has been with us since\n    +    31224cbdc72 (clone: recursive and reference option triggers submodule\n    +    alternates, 2016-08-17) and 8c8195e9c3e (submodule--helper: introduce\n    +    add-clone subcommand, 2021-07-10).\n     \n         Those commits added an xstrdup()'d member of the\n         STRING_LIST_INIT_NODUP'd \"struct string_list\". We need to free()\n    @@ Commit message\n         member, if it has a pointer it's the pointer we'll need to free,\n         otherwise it'll be NULL (i.e. from argv).\n     \n    -    To fix this create a a module_clone_data_release() function to go with\n    -    the MODULE_CLONE_DATA_INIT added in a98b02c1128 (submodule--helper:\n    -    refactor module_clone(), 2021-07-10). We only need to add it to\n    -    add_submodule() to fix the leak, but let's add it to module_clone() as\n    -    well for consistency.\n    +    Note that the free() of the \"util\" member is needed in both\n    +    module_clone() and add_submodule(). The module_clone() function itself\n    +    doesn't populate the \"util\" pointer as add_submodule() does, but\n    +    module_clone() is upstream of the\n    +    add_possible_reference_from_superproject() caller we're modifying\n    +    here, which does do that.\n    +\n    +    This does preclude the use of the \"util\" pointer for any other reasons\n    +    for now, but that's OK. If we ever need to use it for something else\n    +    we could turn it into a small \"struct\" with an optional \"to_free\"\n    +    member, and switch to using string_list_clear_func().\n    +\n    +    Alternatively we could have another \"struct string_list to_free\" which\n    +    would keep a copy of the strings we've dup'd to free(). But for now\n    +    this is perfectly adequate.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/submodule--helper.c ##\n    -@@ builtin/submodule--helper.c: struct module_clone_data {\n    - \t.single_branch = -1, \\\n    - }\n    - \n    -+static void module_clone_data_release(struct module_clone_data *cd)\n    -+{\n    -+\tstring_list_clear(&cd->reference, 1);\n    -+}\n    -+\n    - struct submodule_alternate_setup {\n    - \tconst char *submodule_name;\n    - \tenum SUBMODULE_ALTERNATE_ERROR_MODE {\n     @@ builtin/submodule--helper.c: static int add_possible_reference_from_superproject(\n      \n      \t\tsm_alternate = compute_alternate_path(sb.buf, &err);\n    @@ builtin/submodule--helper.c: static int add_possible_reference_from_superproject\n      \t\t\tswitch (sas->error_mode) {\n     @@ builtin/submodule--helper.c: static int module_clone(int argc, const char **argv, const char *prefix)\n      \n    - \tclone_submodule(&clone_data, &clone_data.reference);\n    + \tclone_submodule(&clone_data, &reference);\n      \tlist_objects_filter_release(&filter_options);\n    -+\tmodule_clone_data_release(&clone_data);\n    ++\tstring_list_clear(&reference, 1);\n      \treturn 0;\n      }\n      \n     @@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add_data)\n    - {\n      \tchar *submod_gitdir_path;\n      \tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n    + \tstruct string_list reference = STRING_LIST_INIT_NODUP;\n     +\tint ret = -1;\n      \n      \t/* perhaps the path already exists and is already a git repo, else clone it */\n    @@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add\n      \t\tclone_data.quiet = add_data->quiet;\n      \t\tclone_data.progress = add_data->progress;\n     -\t\tif (add_data->reference_path)\n    --\t\t\tstring_list_append(&clone_data.reference,\n    +-\t\t\tstring_list_append(&reference,\n     -\t\t\t\t\t   xstrdup(add_data->reference_path));\n     +\t\tif (add_data->reference_path) {\n     +\t\t\tchar *p = xstrdup(add_data->reference_path);\n     +\n    -+\t\t\tstring_list_append(&clone_data.reference, p)->util = p;\n    ++\t\t\tstring_list_append(&reference, p)->util = p;\n     +\t\t}\n      \t\tclone_data.dissociate = add_data->dissociate;\n      \t\tif (add_data->depth >= 0)\n      \t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n      \n    - \t\tif (clone_submodule(&clone_data, &clone_data.reference))\n    + \t\tif (clone_submodule(&clone_data, &reference))\n     -\t\t\treturn -1;\n     +\t\t\tgoto cleanup;\n      \n    @@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add\n     -\treturn 0;\n     +\tret = 0;\n     +cleanup:\n    -+\tmodule_clone_data_release(&clone_data);\n    ++\tstring_list_clear(&reference, 1);\n     +\treturn ret;\n      }\n      \n17:  7811bdbf149 ! 12:  1adb7b66656 submodule--helper: fix obscure leak in module_add()\n    @@ Metadata\n      ## Commit message ##\n         submodule--helper: fix obscure leak in module_add()\n     \n    -    Fix an obscure leak in module_add()< if the \"git add\" command we were\n    +    Fix an obscure leak in module_add(), if the \"git add\" command we were\n         piping to failed we'd fail to strbuf_release(&sb). This fixes a leak\n         introduced in a6226fd772b (submodule--helper: convert the bulk of\n         cmd_add() to C, 2021-08-10).\n    @@ Commit message\n     \n      ## builtin/submodule--helper.c ##\n     @@ builtin/submodule--helper.c: static int module_add(int argc, const char **argv, const char *prefix)\n    - \tint force = 0, quiet = 0, progress = 0, dissociate = 0;\n    - \tstruct add_data add_data = ADD_DATA_INIT;\n    - \tchar *to_free = NULL;\n    + \t\tN_(\"git submodule add [<options>] [--] <repository> [<path>]\"),\n    + \t\tNULL\n    + \t};\n     +\tstruct strbuf sb = STRBUF_INIT;\n     +\tint ret = 1;\n      \n    - \tstruct option options[] = {\n    - \t\tOPT_STRING('b', \"branch\", &add_data.branch, N_(\"branch\"),\n    + \targc = parse_options(argc, argv, prefix, options, usage, 0);\n    + \n     @@ builtin/submodule--helper.c: static int module_add(int argc, const char **argv, const char *prefix)\n      \tdie_on_repo_without_commits(add_data.sm_path);\n      \n    @@ builtin/submodule--helper.c: static int module_add(int argc, const char **argv,\n     -\t\tint exit_code = -1;\n     -\t\tstruct strbuf sb = STRBUF_INIT;\n      \t\tstruct child_process cp = CHILD_PROCESS_INIT;\n    + \n      \t\tcp.git_cmd = 1;\n      \t\tcp.no_stdout = 1;\n      \t\tstrvec_pushl(&cp.args, \"add\", \"--dry-run\", \"--ignore-missing\",\n18:  01566d63926 ! 13:  b27b665d287 submodule--helper: fix a leak in module_add()\n    @@ builtin/submodule--helper.c: static int module_add(int argc, const char **argv,\n     -\t\tadd_data.sm_path = xstrfmt(\"%s%s\", prefix, add_data.sm_path);\n     +\tif (prefix && *prefix && !is_absolute_path(add_data.sm_path)) {\n     +\t\tchar *sm_path = add_data.sm_path;\n    ++\n     +\t\tadd_data.sm_path = xstrfmt(\"%s%s\", prefix, sm_path);\n     +\t\tfree(sm_path);\n     +\t}\n19:  7ef89abed86 = 14:  53ba1705eb6 submodule--helper: fix a memory leak in print_status()\n20:  b8d47fc7d70 = 15:  230e5f8ad14 submodule--helper: free some \"displaypath\" in \"struct update_data\"\n21:  4fb17f0dff1 <  -:  ----------- submodule--helper: rename \"int res\" to \"int ret\"\n22:  af83925046b <  -:  ----------- submodule--helper: add skeleton \"goto cleanup\" to update_submodule()\n23:  4c60784d281 <  -:  ----------- submodule--helper: don't exit() on failure, return\n24:  7551af195ad = 16:  c0fba2f1c56 submodule--helper: free rest of \"displaypath\" in \"struct update_data\"\n25:  f650716cd7e <  -:  ----------- submodule--helper: fix bad config API usage\n26:  581ce0872c0 ! 17:  95f8b68bd41 submodule--helper: fix a configure_added_submodule() leak\n    @@ builtin/submodule--helper.c: static void configure_added_submodule(struct add_da\n      \t\t * current configured pathspec, set the submodule's active flag\n     \n      ## t/t7413-submodule-is-active.sh ##\n    -@@ t/t7413-submodule-is-active.sh: This test verifies that `git submodue--helper is-active` correctly identifies\n    +@@ t/t7413-submodule-is-active.sh: This test verifies that `test-tool submodule is-active` correctly identifies\n      submodules which are \"active\" and interesting to the user.\n      '\n      \n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460098","messageId":"patch-v4-01.17-aac987a414a-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v4 01/17] submodule--helper: fix a leak in \"clone_submodule\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:29:55Z","receivedAt":"2022-07-28T16:30:27Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak of the \"clone_data_path\" variable that we copy or\nderive from the \"struct module_clone_data\" in clone_submodule(). This\ncode was refactored in preceding commits, but the leak has been with\nus since f8eaa0ba98b (submodule--helper, module_clone: always operate\non absolute paths, 2016-03-31).\n\nFor the \"else\" case we don't need to xstrdup() the \"clone_data->path\",\nand we don't need to free our own \"clone_data_path\". We can therefore\nassign the \"clone_data->path\" to our own \"clone_data_path\" right away,\nand only override it (and remember to free it!) if we need to\nxstrfmt() a replacement.\n\nIn the case of the module_clone() caller it's from \"argv\", and doesn't\nneed to be free'd, and in the case of the add_submodule() caller we\nget a pointer to \"sm_path\", which doesn't need to be directly free'd\neither.\n\nFixing this leak makes several tests pass, so let's mark them as\npassing with TEST_PASSES_SANITIZE_LEAK=true.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c   | 10 +++++-----\n t/t1500-rev-parse.sh          |  1 +\n t/t6008-rev-list-submodule.sh |  1 +\n t/t7414-submodule-mistakes.sh |  2 ++\n t/t7506-status-submodule.sh   |  1 +\n t/t7507-commit-verbose.sh     |  2 ++\n 6 files changed, 12 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 22c79353bb0..d3dd47588d5 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1572,13 +1572,12 @@ static int clone_submodule(const struct module_clone_data *clone_data,\n \tchar *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);\n \tchar *sm_alternate = NULL, *error_strategy = NULL;\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n-\tconst char *clone_data_path;\n+\tconst char *clone_data_path = clone_data->path;\n+\tchar *to_free = NULL;\n \n \tif (!is_absolute_path(clone_data->path))\n-\t\tclone_data_path = xstrfmt(\"%s/%s\", get_git_work_tree(),\n-\t\t\t\t\t  clone_data->path);\n-\telse\n-\t\tclone_data_path = xstrdup(clone_data->path);\n+\t\tclone_data_path = to_free = xstrfmt(\"%s/%s\", get_git_work_tree(),\n+\t\t\t\t\t\t    clone_data->path);\n \n \tif (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)\n \t\tdie(_(\"refusing to create/use '%s' in another submodule's \"\n@@ -1663,6 +1662,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,\n \n \tfree(sm_gitdir);\n \tfree(p);\n+\tfree(to_free);\n \treturn 0;\n }\n \ndiff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh\nindex 1c2df08333b..0e13bcb4ebb 100755\n--- a/t/t1500-rev-parse.sh\n+++ b/t/t1500-rev-parse.sh\n@@ -4,6 +4,7 @@ test_description='test git rev-parse'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_one () {\ndiff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh\nindex 3153a0d8910..12e67e187ef 100755\n--- a/t/t6008-rev-list-submodule.sh\n+++ b/t/t6008-rev-list-submodule.sh\n@@ -8,6 +8,7 @@ test_description='git rev-list involving submodules that this repo has'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh\nindex f2e7df59cf2..3269298197c 100755\n--- a/t/t7414-submodule-mistakes.sh\n+++ b/t/t7414-submodule-mistakes.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='handling of common mistakes people may make with submodules'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'create embedded repository' '\ndiff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh\nindex 3fcb44767f5..f5426a8e589 100755\n--- a/t/t7506-status-submodule.sh\n+++ b/t/t7506-status-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='git status for submodule'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_create_repo_with_commit () {\ndiff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh\nindex ed2653d46fe..92462a22374 100755\n--- a/t/t7507-commit-verbose.sh\n+++ b/t/t7507-commit-verbose.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='verbose commit template'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n write_script \"check-for-diff\" <<\\EOF &&\n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460099","messageId":"patch-v4-03.17-529e10233d5-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v4 03/17] submodule--helper: fix most \"struct pathspec\" memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:29:57Z","receivedAt":"2022-07-28T16:30:29Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call clear_pathspec() at the end of various functions that work with\nand allocate a \"struct pathspec\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 74 +++++++++++++++++++++++++------------\n 1 file changed, 51 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d452af40d2d..6c53c83a2af 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -367,7 +367,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,\n static int module_foreach(int argc, const char **argv, const char *prefix)\n {\n \tstruct foreach_cb info = FOREACH_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tstruct option module_foreach_options[] = {\n \t\tOPT__QUIET(&info.quiet, N_(\"suppress output of entering each submodule command\")),\n@@ -379,12 +379,13 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule foreach [--quiet] [--recursive] [--] <command>\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_foreach_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.argc = argc;\n \tinfo.argv = argv;\n@@ -392,7 +393,10 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static int starts_with_dot_slash(const char *const path)\n@@ -502,7 +506,7 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data\n static int module_init(int argc, const char **argv, const char *prefix)\n {\n \tstruct init_cb info = INIT_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tstruct option module_init_options[] = {\n@@ -513,12 +517,13 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule init [<options>] [<path>]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_init_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \t/*\n \t * If there are no path args and submodule.active is set then,\n@@ -533,7 +538,10 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, init_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct status_cb {\n@@ -680,7 +688,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,\n static int module_status(int argc, const char **argv, const char *prefix)\n {\n \tstruct status_cb info = STATUS_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tstruct option module_status_options[] = {\n@@ -693,12 +701,13 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule status [--quiet] [--cached] [--recursive] [<path>...]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_status_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -706,7 +715,10 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, status_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct module_cb {\n@@ -1245,7 +1257,7 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data\n static int module_sync(int argc, const char **argv, const char *prefix)\n {\n \tstruct sync_cb info = SYNC_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tint recursive = 0;\n@@ -1259,12 +1271,13 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule sync [--quiet] [--recursive] [<path>]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_sync_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -1274,7 +1287,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, sync_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct deinit_cb {\n@@ -1383,7 +1399,7 @@ static void deinit_submodule_cb(const struct cache_entry *list_item,\n static int module_deinit(int argc, const char **argv, const char *prefix)\n {\n \tstruct deinit_cb info = DEINIT_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tint force = 0;\n@@ -1398,6 +1414,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_deinit_options,\n \t\t\t     git_submodule_helper_usage, 0);\n@@ -1412,7 +1429,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"Use '--all' if you really want to deinitialize all submodules\"));\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -1422,7 +1439,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, deinit_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct module_clone_data {\n@@ -2516,7 +2536,7 @@ static int update_submodules(struct update_data *update_data)\n \n static int module_update(int argc, const char **argv, const char *prefix)\n {\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct update_data opt = UPDATE_DATA_INIT;\n \tstruct list_objects_filter_options filter_options = { 0 };\n \tint ret;\n@@ -2593,8 +2613,8 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\topt.update_strategy.type = opt.update_default;\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {\n-\t\tlist_objects_filter_release(&filter_options);\n-\t\treturn 1;\n+\t\tret = 1;\n+\t\tgoto cleanup;\n \t}\n \n \tif (pathspec.nr)\n@@ -2605,8 +2625,10 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\tstruct init_cb info = INIT_CB_INIT;\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n-\t\t\t\t\t&pathspec, &list) < 0)\n-\t\t\treturn 1;\n+\t\t\t\t\t&pathspec, &list) < 0) {\n+\t\t\tret = 1;\n+\t\t\tgoto cleanup;\n+\t\t}\n \n \t\t/*\n \t\t * If there are no path args and submodule.active is set then,\n@@ -2623,7 +2645,9 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t}\n \n \tret = update_submodules(&opt);\n+cleanup:\n \tlist_objects_filter_release(&filter_options);\n+\tclear_pathspec(&pathspec);\n \treturn ret;\n }\n \n@@ -2707,7 +2731,7 @@ static int push_check(int argc, const char **argv, const char *prefix)\n static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tunsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;\n \tstruct option embed_gitdir_options[] = {\n@@ -2722,17 +2746,21 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule absorbgitdirs [<options>] [<path>...]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, embed_gitdir_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tfor (i = 0; i < list.nr; i++)\n \t\tabsorb_git_dir_into_superproject(list.entries[i]->name, flags);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static int module_config(int argc, const char **argv, const char *prefix)\n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460100","messageId":"patch-v4-02.17-390c5174e17-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v4 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:29:56Z","receivedAt":"2022-07-28T16:30:31Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in code added in 1012a5cbc3f (submodule--helper\nrun-update-procedure: learn --remote, 2022-03-04), we need to free()\nthe xstrdup()'d string. This gets e.g. t/t7419-submodule-set-branch.sh\ncloser to passing under SANITIZE=leak.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d3dd47588d5..d452af40d2d 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2410,6 +2410,8 @@ static int update_submodule(struct update_data *update_data,\n \t\tconst char *branch = remote_submodule_branch(update_data->sm_path);\n \t\tchar *remote_ref = xstrfmt(\"refs/remotes/%s/%s\", remote_name, branch);\n \n+\t\tfree(remote_name);\n+\n \t\tif (!update_data->nofetch) {\n \t\t\tif (fetch_in_submodule(update_data->sm_path, update_data->depth,\n \t\t\t\t\t      0, NULL))\n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460101","messageId":"patch-v4-04.17-683d327752f-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v4 04/17] submodule--helper: \"struct pathspec\" memory leak in module_update()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:29:58Z","receivedAt":"2022-07-28T16:30:33Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The module_update() function calls module_list_compute() twice, which\nin turn will reset the \"struct pathspec\" passed to it. Let's instead\ntrack two of them, and clear them both.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 6c53c83a2af..057cb6c0d42 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2537,6 +2537,7 @@ static int update_submodules(struct update_data *update_data)\n static int module_update(int argc, const char **argv, const char *prefix)\n {\n \tstruct pathspec pathspec = { 0 };\n+\tstruct pathspec pathspec2 = { 0 };\n \tstruct update_data opt = UPDATE_DATA_INIT;\n \tstruct list_objects_filter_options filter_options = { 0 };\n \tint ret;\n@@ -2625,7 +2626,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\tstruct init_cb info = INIT_CB_INIT;\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n-\t\t\t\t\t&pathspec, &list) < 0) {\n+\t\t\t\t\t&pathspec2, &list) < 0) {\n \t\t\tret = 1;\n \t\t\tgoto cleanup;\n \t\t}\n@@ -2648,6 +2649,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n cleanup:\n \tlist_objects_filter_release(&filter_options);\n \tclear_pathspec(&pathspec);\n+\tclear_pathspec(&pathspec2);\n \treturn ret;\n }\n \n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460102","messageId":"patch-v4-05.17-4e8e9197539-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v4 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:29:59Z","receivedAt":"2022-07-28T16:30:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak in c51f8f94e5b (submodule--helper: run update\nprocedures from C, 2021-08-24) and 3c3558f0953 (submodule--helper: run\nupdate using child process struct, 2022-03-15) by not allocating\nmemory in the first place.\n\nThe \"dir\" member of \"struct child_process\" will not be modified by\nthat API, and it's declared to be \"const char *\". So let's not\nneedlessly duplicate these strings.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 057cb6c0d42..35a168009b7 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2105,7 +2105,7 @@ static int is_tip_reachable(const char *path, struct object_id *oid)\n \tchar *hex = oid_to_hex(oid);\n \n \tcp.git_cmd = 1;\n-\tcp.dir = xstrdup(path);\n+\tcp.dir = path;\n \tcp.no_stderr = 1;\n \tstrvec_pushl(&cp.args, \"rev-list\", \"-n\", \"1\", hex, \"--not\", \"--all\", NULL);\n \n@@ -2123,7 +2123,7 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str\n \n \tprepare_submodule_repo_env(&cp.env);\n \tcp.git_cmd = 1;\n-\tcp.dir = xstrdup(module_path);\n+\tcp.dir = module_path;\n \n \tstrvec_push(&cp.args, \"fetch\");\n \tif (quiet)\n@@ -2180,7 +2180,7 @@ static int run_update_command(struct update_data *ud, int subforce,\n \t}\n \tstrvec_push(&cp.args, oid);\n \n-\tcp.dir = xstrdup(ud->sm_path);\n+\tcp.dir = ud->sm_path;\n \tprepare_submodule_repo_env(&cp.env);\n \tif (run_command(&cp)) {\n \t\tswitch (ud->update_strategy.type) {\n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460103","messageId":"patch-v4-07.17-d4c81e04254-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v4 07/17] submodule--helper: fix \"errmsg_str\" memory leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:30:01Z","receivedAt":"2022-07-28T16:30:45Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak introduced in e83e3333b57 (submodule: port submodule\nsubcommand 'summary' from shell to C, 2020-08-13), we sometimes append\nto the \"errmsg\", and need to free the \"struct strbuf\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 48507a6e0df..5cbe6a41e5f 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -960,6 +960,7 @@ static void generate_submodule_summary(struct summary_cb *info,\n \tfree(displaypath);\n \tfree(src_abbrev);\n \tfree(dst_abbrev);\n+\tstrbuf_release(&errmsg);\n }\n \n static void prepare_submodule_summary(struct summary_cb *info,\n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460104","messageId":"patch-v4-06.17-575d3e8d2e2-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v4 06/17] submodule--helper: add and use *_release() functions","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:30:00Z","receivedAt":"2022-07-28T16:30:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add release functions for \"struct module_list\", \"struct\nsubmodule_update_clone\" and \"struct update_data\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c      | 28 +++++++++++++++++++++++++++-\n t/t6134-pathspec-in-submodule.sh |  1 +\n 2 files changed, 28 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 35a168009b7..48507a6e0df 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -160,6 +160,11 @@ struct module_list {\n };\n #define MODULE_LIST_INIT { 0 }\n \n+static void module_list_release(struct module_list *ml)\n+{\n+\tfree(ml->entries);\n+}\n+\n static int module_list_compute(int argc, const char **argv,\n \t\t\t       const char *prefix,\n \t\t\t       struct pathspec *pathspec,\n@@ -222,7 +227,7 @@ static void module_list_active(struct module_list *list)\n \t\tactive_modules.entries[active_modules.nr++] = ce;\n \t}\n \n-\tfree(list->entries);\n+\tmodule_list_release(list);\n \t*list = active_modules;\n }\n \n@@ -395,6 +400,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -540,6 +546,7 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -717,6 +724,7 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1289,6 +1297,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1441,6 +1450,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1810,6 +1820,12 @@ struct submodule_update_clone {\n };\n #define SUBMODULE_UPDATE_CLONE_INIT { 0 }\n \n+static void submodule_update_clone_release(struct submodule_update_clone *suc)\n+{\n+\tfree(suc->update_clone);\n+\tfree(suc->failed_clones);\n+}\n+\n struct update_data {\n \tconst char *prefix;\n \tconst char *displaypath;\n@@ -1848,6 +1864,11 @@ struct update_data {\n \t.max_jobs = 1, \\\n }\n \n+static void update_data_release(struct update_data *ud)\n+{\n+\tmodule_list_release(&ud->list);\n+}\n+\n static void next_submodule_warn_missing(struct submodule_update_clone *suc,\n \t\tstruct strbuf *out, const char *displaypath)\n {\n@@ -2530,6 +2551,7 @@ static int update_submodules(struct update_data *update_data)\n \t}\n \n cleanup:\n+\tsubmodule_update_clone_release(&suc);\n \tstring_list_clear(&update_data->references, 0);\n \treturn res;\n }\n@@ -2627,6 +2649,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n \t\t\t\t\t&pathspec2, &list) < 0) {\n+\t\t\tmodule_list_release(&list);\n \t\t\tret = 1;\n \t\t\tgoto cleanup;\n \t\t}\n@@ -2643,10 +2666,12 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\t\tinfo.flags |= OPT_QUIET;\n \n \t\tfor_each_listed_submodule(&list, init_submodule_cb, &info);\n+\t\tmodule_list_release(&list);\n \t}\n \n \tret = update_submodules(&opt);\n cleanup:\n+\tupdate_data_release(&opt);\n \tlist_objects_filter_release(&filter_options);\n \tclear_pathspec(&pathspec);\n \tclear_pathspec(&pathspec2);\n@@ -2762,6 +2787,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n \tret = 0;\n cleanup:\n \tclear_pathspec(&pathspec);\n+\tmodule_list_release(&list);\n \treturn ret;\n }\n \ndiff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh\nindex 0f1cb49cedc..3a241f259de 100755\n--- a/t/t6134-pathspec-in-submodule.sh\n+++ b/t/t6134-pathspec-in-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='test case exclude pathspec'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a submodule' '\n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460105","messageId":"patch-v4-08.17-ef9e29d5bfe-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v4 08/17] submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:30:02Z","receivedAt":"2022-07-28T16:30:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix leaks in \"struct module_cb_list\" and the \"struct module_cb\" which\nit contains, these fixes leaks in e83e3333b57 (submodule: port\nsubmodule subcommand 'summary' from shell to C, 2020-08-13).\n\nThe \"sm_path\" should always have been a \"char *\", not a \"const\nchar *\", we always create it with xstrdup().\n\nWe can't mark any tests passing passing with SANITIZE=leak using\n\"TEST_PASSES_SANITIZE_LEAK=true\" as a result of this change, but\n\"t7401-submodule-summary.sh\" gets closer to passing as a result of\nthis change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 21 ++++++++++++++++++++-\n 1 file changed, 20 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 5cbe6a41e5f..becf686e47a 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -735,16 +735,34 @@ struct module_cb {\n \tstruct object_id oid_src;\n \tstruct object_id oid_dst;\n \tchar status;\n-\tconst char *sm_path;\n+\tchar *sm_path;\n };\n #define MODULE_CB_INIT { 0 }\n \n+static void module_cb_release(struct module_cb *mcb)\n+{\n+\tfree(mcb->sm_path);\n+}\n+\n struct module_cb_list {\n \tstruct module_cb **entries;\n \tint alloc, nr;\n };\n #define MODULE_CB_LIST_INIT { 0 }\n \n+static void module_cb_list_release(struct module_cb_list *mcbl)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < mcbl->nr; i++) {\n+\t\tstruct module_cb *mcb = mcbl->entries[i];\n+\n+\t\tmodule_cb_release(mcb);\n+\t\tfree(mcb);\n+\t}\n+\tfree(mcbl->entries);\n+}\n+\n struct summary_cb {\n \tint argc;\n \tconst char **argv;\n@@ -1088,6 +1106,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n cleanup:\n \tstrvec_clear(&diff_args);\n \trelease_revisions(&rev);\n+\tmodule_cb_list_release(&list);\n \treturn ret;\n }\n \n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460106","messageId":"patch-v4-10.17-dae2a6f8e07-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v4 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:30:04Z","receivedAt":"2022-07-28T16:30:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak in the get_default_remote_submodule() function added\nin a77c3fcb5ec (submodule--helper: get remote names from any\nrepository, 2022-03-04), we need to repo_clear() the submodule we\ninitialize.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex dbeafd9787d..f8a92ecf86d 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -62,9 +62,13 @@ static char *repo_get_default_remote(struct repository *repo)\n static char *get_default_remote_submodule(const char *module_path)\n {\n \tstruct repository subrepo;\n+\tchar *ret;\n \n \trepo_submodule_init(&subrepo, the_repository, module_path, null_oid());\n-\treturn repo_get_default_remote(&subrepo);\n+\tret = repo_get_default_remote(&subrepo);\n+\trepo_clear(&subrepo);\n+\n+\treturn ret;\n }\n \n static char *get_default_remote(void)\n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460107","messageId":"patch-v4-11.17-e7352bb8cfa-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v4 11/17] submodule--helper: fix \"reference\" leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:30:05Z","receivedAt":"2022-07-28T16:30:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix leaks in the \"reference\" variable declared in add_submodule() and\nmodule_clone().\n\nIn preceding commits this variable was refactored out of the \"struct\nmodule_clone_data\", but the leak has been with us since\n31224cbdc72 (clone: recursive and reference option triggers submodule\nalternates, 2016-08-17) and 8c8195e9c3e (submodule--helper: introduce\nadd-clone subcommand, 2021-07-10).\n\nThose commits added an xstrdup()'d member of the\nSTRING_LIST_INIT_NODUP'd \"struct string_list\". We need to free()\nthose, but not the ones we get from argv, let's make use of the \"util\"\nmember, if it has a pointer it's the pointer we'll need to free,\notherwise it'll be NULL (i.e. from argv).\n\nNote that the free() of the \"util\" member is needed in both\nmodule_clone() and add_submodule(). The module_clone() function itself\ndoesn't populate the \"util\" pointer as add_submodule() does, but\nmodule_clone() is upstream of the\nadd_possible_reference_from_superproject() caller we're modifying\nhere, which does do that.\n\nThis does preclude the use of the \"util\" pointer for any other reasons\nfor now, but that's OK. If we ever need to use it for something else\nwe could turn it into a small \"struct\" with an optional \"to_free\"\nmember, and switch to using string_list_clear_func().\n\nAlternatively we could have another \"struct string_list to_free\" which\nwould keep a copy of the strings we've dup'd to free(). But for now\nthis is perfectly adequate.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 21 +++++++++++++++------\n 1 file changed, 15 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex f8a92ecf86d..debe3a4a2f5 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1548,7 +1548,9 @@ static int add_possible_reference_from_superproject(\n \n \t\tsm_alternate = compute_alternate_path(sb.buf, &err);\n \t\tif (sm_alternate) {\n-\t\t\tstring_list_append(sas->reference, xstrdup(sb.buf));\n+\t\t\tchar *p = strbuf_detach(&sb, NULL);\n+\n+\t\t\tstring_list_append(sas->reference, p)->util = p;\n \t\t\tfree(sm_alternate);\n \t\t} else {\n \t\t\tswitch (sas->error_mode) {\n@@ -1780,6 +1782,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \n \tclone_submodule(&clone_data, &reference);\n \tlist_objects_filter_release(&filter_options);\n+\tstring_list_clear(&reference, 1);\n \treturn 0;\n }\n \n@@ -3018,6 +3021,7 @@ static int add_submodule(const struct add_data *add_data)\n \tchar *submod_gitdir_path;\n \tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n \tstruct string_list reference = STRING_LIST_INIT_NODUP;\n+\tint ret = -1;\n \n \t/* perhaps the path already exists and is already a git repo, else clone it */\n \tif (is_directory(add_data->sm_path)) {\n@@ -3073,15 +3077,17 @@ static int add_submodule(const struct add_data *add_data)\n \t\tclone_data.url = add_data->realrepo;\n \t\tclone_data.quiet = add_data->quiet;\n \t\tclone_data.progress = add_data->progress;\n-\t\tif (add_data->reference_path)\n-\t\t\tstring_list_append(&reference,\n-\t\t\t\t\t   xstrdup(add_data->reference_path));\n+\t\tif (add_data->reference_path) {\n+\t\t\tchar *p = xstrdup(add_data->reference_path);\n+\n+\t\t\tstring_list_append(&reference, p)->util = p;\n+\t\t}\n \t\tclone_data.dissociate = add_data->dissociate;\n \t\tif (add_data->depth >= 0)\n \t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n \n \t\tif (clone_submodule(&clone_data, &reference))\n-\t\t\treturn -1;\n+\t\t\tgoto cleanup;\n \n \t\tprepare_submodule_repo_env(&cp.env);\n \t\tcp.git_cmd = 1;\n@@ -3100,7 +3106,10 @@ static int add_submodule(const struct add_data *add_data)\n \t\tif (run_command(&cp))\n \t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n \t}\n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tstring_list_clear(&reference, 1);\n+\treturn ret;\n }\n \n static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)\n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460108","messageId":"patch-v4-12.17-1adb7b66656-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v4 12/17] submodule--helper: fix obscure leak in module_add()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:30:06Z","receivedAt":"2022-07-28T16:30:54Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix an obscure leak in module_add(), if the \"git add\" command we were\npiping to failed we'd fail to strbuf_release(&sb). This fixes a leak\nintroduced in a6226fd772b (submodule--helper: convert the bulk of\ncmd_add() to C, 2021-08-10).\n\nIn fixing it move to a \"goto cleanup\" pattern, and since we need to\nintroduce a \"ret\" variable to do that let's also get rid of the\nintermediate \"exit_code\" variable. The initialization to \"-1\" in\na6226fd772b has always been redundant, we'd only use the \"exit_code\"\nvalue after assigning the return value of pipe_command() to it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 22 +++++++++++-----------\n 1 file changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex debe3a4a2f5..0f6c07e3d1e 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3269,6 +3269,8 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule add [<options>] [--] <repository> [<path>]\"),\n \t\tNULL\n \t};\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, options, usage, 0);\n \n@@ -3318,21 +3320,17 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \tdie_on_repo_without_commits(add_data.sm_path);\n \n \tif (!force) {\n-\t\tint exit_code = -1;\n-\t\tstruct strbuf sb = STRBUF_INIT;\n \t\tstruct child_process cp = CHILD_PROCESS_INIT;\n \n \t\tcp.git_cmd = 1;\n \t\tcp.no_stdout = 1;\n \t\tstrvec_pushl(&cp.args, \"add\", \"--dry-run\", \"--ignore-missing\",\n \t\t\t     \"--no-warn-embedded-repo\", add_data.sm_path, NULL);\n-\t\tif ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {\n+\t\tif ((ret = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {\n \t\t\tstrbuf_complete_line(&sb);\n \t\t\tfputs(sb.buf, stderr);\n-\t\t\tfree(add_data.sm_path);\n-\t\t\treturn exit_code;\n+\t\t\tgoto cleanup;\n \t\t}\n-\t\tstrbuf_release(&sb);\n \t}\n \n \tif(!add_data.sm_name)\n@@ -3347,15 +3345,17 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \tadd_data.progress = !!progress;\n \tadd_data.dissociate = !!dissociate;\n \n-\tif (add_submodule(&add_data)) {\n-\t\tfree(add_data.sm_path);\n-\t\treturn 1;\n-\t}\n+\tif (add_submodule(&add_data))\n+\t\tgoto cleanup;\n \tconfigure_added_submodule(&add_data);\n+\n+\tret = 0;\n+cleanup:\n \tfree(add_data.sm_path);\n \tfree(to_free);\n+\tstrbuf_release(&sb);\n \n-\treturn 0;\n+\treturn ret;\n }\n \n #define SUPPORT_SUPER_PREFIX (1<<0)\n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460109","messageId":"patch-v4-09.17-0798a00c9ef-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v4 09/17] submodule--helper: fix a leak with repo_clear()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:30:03Z","receivedAt":"2022-07-28T16:30:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call repo_clear() in ensure_core_worktree() to free the \"struct\nrepository\". Fixes a leak that's been here since\n74d4731da1f (submodule--helper: replace connect-gitdir-workingtree by\nensure-core-worktree, 2018-08-13).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex becf686e47a..dbeafd9787d 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2375,6 +2375,7 @@ static void ensure_core_worktree(const char *path)\n \t\tfree(abs_path);\n \t\tstrbuf_release(&sb);\n \t}\n+\trepo_clear(&subrepo);\n }\n \n static const char *submodule_update_type_to_label(enum submodule_update_type type)\n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460110","messageId":"patch-v4-13.17-b27b665d287-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v4 13/17] submodule--helper: fix a leak in module_add()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:30:07Z","receivedAt":"2022-07-28T16:31:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in module_path(), since a6226fd772b (submodule--helper:\nconvert the bulk of cmd_add() to C, 2021-08-10), we've been freeing\nadd_data.sm_path, but in this case we clobbered it, and didn't free\nthe value we clobbered.\n\nThis makes test 28 of \"t/t7400-submodule-basic.sh\" (\"submodule add in\nsubdirectory\") pass when we're compiled with SANITIZE=leak..\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 0f6c07e3d1e..6077aecd202 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3290,8 +3290,12 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \telse\n \t\tadd_data.sm_path = xstrdup(argv[1]);\n \n-\tif (prefix && *prefix && !is_absolute_path(add_data.sm_path))\n-\t\tadd_data.sm_path = xstrfmt(\"%s%s\", prefix, add_data.sm_path);\n+\tif (prefix && *prefix && !is_absolute_path(add_data.sm_path)) {\n+\t\tchar *sm_path = add_data.sm_path;\n+\n+\t\tadd_data.sm_path = xstrfmt(\"%s%s\", prefix, sm_path);\n+\t\tfree(sm_path);\n+\t}\n \n \tif (starts_with_dot_dot_slash(add_data.repo) ||\n \t    starts_with_dot_slash(add_data.repo)) {\n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460111","messageId":"patch-v4-14.17-53ba1705eb6-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v4 14/17] submodule--helper: fix a memory leak in print_status()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:30:08Z","receivedAt":"2022-07-28T16:31:14Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in print_status(), the compute_rev_name() function\nimplemented in this file will return a strbuf_detach()'d value, or\nNULL.\n\nThis leak has existed since this code was added in\na9f8a37584a (submodule: port submodule subcommand 'status' from shell\nto C, 2017-10-06), but in 0b5e2ea7cf3 (submodule--helper: don't print\nnull in 'submodule status', 2018-04-18) we added a \"const\"\nintermediate variable for the return value, that \"const\" should be\nremoved.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 6077aecd202..b51b60d5c77 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -570,10 +570,11 @@ static void print_status(unsigned int flags, char state, const char *path,\n \tprintf(\"%c%s %s\", state, oid_to_hex(oid), displaypath);\n \n \tif (state == ' ' || state == '+') {\n-\t\tconst char *name = compute_rev_name(path, oid_to_hex(oid));\n+\t\tchar *name = compute_rev_name(path, oid_to_hex(oid));\n \n \t\tif (name)\n \t\t\tprintf(\" (%s)\", name);\n+\t\tfree(name);\n \t}\n \n \tprintf(\"\\n\");\n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460112","messageId":"patch-v4-16.17-c0fba2f1c56-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v4 16/17] submodule--helper: free rest of \"displaypath\" in \"struct update_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:30:10Z","receivedAt":"2022-07-28T16:31:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in code added in c51f8f94e5b (submodule--helper: run update\nprocedures from C, 2021-08-24), we clobber the \"displaypath\" member of\nthe passed-in \"struct update_data\" both so that die() messages in this\nupdate_submodule() function itself can use it, and for the\nrun_update_procedure() called within this function.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 2e39750fb06..2a171d25992 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2460,10 +2460,11 @@ static int update_submodule(struct update_data *update_data,\n \t\t\t    int *must_die_on_failure)\n {\n \tint ret = 1;\n+\tchar *to_free, *restore = update_data->displaypath;\n \n \tensure_core_worktree(update_data->sm_path);\n \n-\tupdate_data->displaypath = get_submodule_displaypath(\n+\tupdate_data->displaypath = to_free = get_submodule_displaypath(\n \t\tupdate_data->sm_path, update_data->prefix);\n \n \tdetermine_submodule_update_strategy(the_repository, update_data->just_cloned,\n@@ -2537,6 +2538,9 @@ static int update_submodule(struct update_data *update_data,\n \n \tret = 0;\n cleanup:\n+\tfree(to_free);\n+\tupdate_data->displaypath = restore;\n+\n \treturn ret;\n }\n \n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460113","messageId":"patch-v4-15.17-230e5f8ad14-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v4 15/17] submodule--helper: free some \"displaypath\" in \"struct update_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:30:09Z","receivedAt":"2022-07-28T16:31:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Make the update_data_release() function free \"displaypath\" member when\nappropriate. The \"displaypath\" member is always ours, the \"const\" on\nthe \"char *\" was wrong to begin with.\n\nThis leaves a leak of \"displaypath\" in update_submodule(), which as\nwe'll see in subsequent commits is harder to deal with than this\ntrivial fix.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c        | 3 ++-\n t/t2403-worktree-move.sh           | 1 +\n t/t7412-submodule-absorbgitdirs.sh | 1 +\n 3 files changed, 4 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex b51b60d5c77..2e39750fb06 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1856,7 +1856,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)\n \n struct update_data {\n \tconst char *prefix;\n-\tconst char *displaypath;\n+\tchar *displaypath;\n \tenum submodule_update_type update_default;\n \tstruct object_id suboid;\n \tstruct string_list references;\n@@ -1894,6 +1894,7 @@ struct update_data {\n \n static void update_data_release(struct update_data *ud)\n {\n+\tfree(ud->displaypath);\n \tmodule_list_release(&ud->list);\n }\n \ndiff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh\nindex a4e1a178e0a..1168e9f9982 100755\n--- a/t/t2403-worktree-move.sh\n+++ b/t/t2403-worktree-move.sh\n@@ -2,6 +2,7 @@\n \n test_description='test git worktree move, remove, lock and unlock'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh\nindex 1cfa150768d..2859695c6d2 100755\n--- a/t/t7412-submodule-absorbgitdirs.sh\n+++ b/t/t7412-submodule-absorbgitdirs.sh\n@@ -6,6 +6,7 @@ This test verifies that `git submodue absorbgitdirs` moves a submodules git\n directory into the superproject.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a real submodule' '\n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460114","messageId":"patch-v4-17.17-95f8b68bd41-20220728T162442Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v4 17/17] submodule--helper: fix a configure_added_submodule() leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:30:11Z","receivedAt":"2022-07-28T16:31:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix config API a memory leak added in a452128a36c (submodule--helper:\nintroduce add-config subcommand, 2021-08-06) by using the *_tmp()\nvariant of git_config_get_string().\n\nIn this case we're only checking whether\nthe (repo|git)_config_get_string() call is telling us that the\n\"submodule.active\" key exists.\n\nAs with the preceding commit we'll find many other such patterns in\nthe codebase if we go fishing. E.g. \"git gc\" leaks in the code added\nin 61f7a383d3b (maintenance: use 'incremental' strategy by default,\n2020-10-15). Similar code in \"git gc\" added in\nb08ff1fee00 (maintenance: add --schedule option and config,\n2020-09-11) doesn't leak, but we could avoid the malloc() & free() in\nthat case.\n\nA coccinelle rule to find those would find and fix some leaks, and\ncases where we're doing needless malloc() + free()'s but only care\nabout the key existence, or are copying\nthe (repo|git)_config_get_string() return value right away.\n\nBut as with the preceding commit let's punt on all of that for now,\nand just narrowly fix this specific case in submodule--helper.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c    | 4 ++--\n t/t7413-submodule-is-active.sh | 1 +\n 2 files changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 2a171d25992..a3e00c9929d 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3136,7 +3136,7 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con\n static void configure_added_submodule(struct add_data *add_data)\n {\n \tchar *key;\n-\tchar *val = NULL;\n+\tconst char *val;\n \tstruct child_process add_submod = CHILD_PROCESS_INIT;\n \tstruct child_process add_gitmodules = CHILD_PROCESS_INIT;\n \n@@ -3181,7 +3181,7 @@ static void configure_added_submodule(struct add_data *add_data)\n \t * is_submodule_active(), since that function needs to find\n \t * out the value of \"submodule.active\" again anyway.\n \t */\n-\tif (!git_config_get_string(\"submodule.active\", &val)) {\n+\tif (!git_config_get_string_tmp(\"submodule.active\", &val)) {\n \t\t/*\n \t\t * If the submodule being added isn't already covered by the\n \t\t * current configured pathspec, set the submodule's active flag\ndiff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh\nindex 9ead083371a..a3c824c3e4b 100755\n--- a/t/t7413-submodule-is-active.sh\n+++ b/t/t7413-submodule-is-active.sh\n@@ -6,6 +6,7 @@ This test verifies that `test-tool submodule is-active` correctly identifies\n submodules which are \"active\" and interesting to the user.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n-- \n2.37.1.1197.g7ed548b7807\n\n"},{"id":"460115","messageId":"220728.86wnbxjixa.gmgdl@evledraar.gmail.com","threadId":"58151","inReplyTo":"xmqq8rolc7cu.fsf@gitster.g","subject":"Re: [PATCH v3 02/26] submodule--helper: stop conflating \"sb\" in clone_submodule()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-28T16:30:28Z","receivedAt":"2022-07-28T16:33:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Jul 22 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>>> This looks like a roundabout way to say xstrfmt().\n>>\n>> Yes, I can fix this and others while I'm at it, but a lot of things like\n>> that in this code are funny uses of APIs that we could improve.\n>>\n>> I think it's probably best to just leave these for now.\n>\n> Agreed.  We could instead have a separate series to fix API usage\n> before these and then build leak-plugging on top, or the other way\n> around, and in general \"clean then plug\" would make it easier to\n> review the plugging patches (simply because it would be working on\n> clean code, not code that misuses the API in strange ways), but it\n> is too late now.  Lets make sure we do not forget to revisit the API\n> misuse but lets avoid mixing it into the series.\n\nI ended up doing the opposite of what you suggested here, but not\nlightly.\n\nWhen re-rolling the v4 of the leak series I noticed that some of what\nwas suggested I'd fix (and thanks a lot to you and Glen for the reviews)\nwas code that was either dead, or should really belong in a \"test-tool\"\nat this point.\n\nSo, I could have addressed those by padding the \"leak\" series with more\ndigressions, but I felt that just cleanly splitting it into a \"prep\"\nseries and \"leak fixes\" was better, those two are the just-submitted:\n\n\thttps://lore.kernel.org/cover-00.20-00000000000-20220728T161116Z-avarab@gmail.com\n\thttps://lore.kernel.org/git/cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com\n\nSorry if that causes any disruption for you. I noticed that you merged\nthe v3 into your \"jch\", but it didn't look anywhere close to \"next\", so\ngetting it more right to begin with seemed like a better trade-off.\n\nThanks!\n"},{"id":"460476","messageId":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com","subject":"[PATCH v5 00/17] submodule--helper: fix memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:24Z","receivedAt":"2022-08-02T15:54:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This series fixes all of the memory leaks in\nbuiltin/submodule--helper.c that our test suite spots, except for\nthose where the leak is downstream of cmd_submodule__helper() and\ncaused by code that's not in builtin/submodule--helper.c (e.g. leaks\nin the object or config APIs, and in submodule.c).\n\nThis re-roll is on top of the just-now re-rolled v2 of the base\ntopic[1]. The only changes are to rebase the changes here on top of\nthose changes.\n\nChanges:\n\n* The base topic renamed another \"res\" variable (one was left behind\n  before).\n\n* The \"goto cleanup\" pattern in update_submodule() is gone now, which\n  makes 16/17 much easier.\n\nFor the CI run & pushed branch see [2].\n\n1. https://lore.kernel.org/git/cover-v2-00.28-00000000000-20220802T154036Z-avarab@gmail.com/\n2. https://github.com/avar/git/tree/avar/submodule--helper-memory-leaks-5\n\nÆvar Arnfjörð Bjarmason (17):\n  submodule--helper: fix a leak in \"clone_submodule\"\n  submodule--helper: fix trivial get_default_remote_submodule() leak\n  submodule--helper: fix most \"struct pathspec\" memory leaks\n  submodule--helper: \"struct pathspec\" memory leak in module_update()\n  submodule--helper: don't leak {run,capture}_command() cp.dir argument\n  submodule--helper: add and use *_release() functions\n  submodule--helper: fix \"errmsg_str\" memory leak\n  submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks\n  submodule--helper: fix a leak with repo_clear()\n  submodule--helper: fix a memory leak in get_default_remote_submodule()\n  submodule--helper: fix \"reference\" leak\n  submodule--helper: fix obscure leak in module_add()\n  submodule--helper: fix a leak in module_add()\n  submodule--helper: fix a memory leak in print_status()\n  submodule--helper: free some \"displaypath\" in \"struct update_data\"\n  submodule--helper: free rest of \"displaypath\" in \"struct update_data\"\n  submodule--helper: fix a configure_added_submodule() leak\n\n builtin/submodule--helper.c        | 239 +++++++++++++++++++++--------\n t/t1500-rev-parse.sh               |   1 +\n t/t2403-worktree-move.sh           |   1 +\n t/t6008-rev-list-submodule.sh      |   1 +\n t/t6134-pathspec-in-submodule.sh   |   1 +\n t/t7412-submodule-absorbgitdirs.sh |   1 +\n t/t7413-submodule-is-active.sh     |   1 +\n t/t7414-submodule-mistakes.sh      |   2 +\n t/t7506-status-submodule.sh        |   1 +\n t/t7507-commit-verbose.sh          |   2 +\n 10 files changed, 185 insertions(+), 65 deletions(-)\n\nRange-diff against v4:\n 1:  aac987a414a =  1:  118e965d401 submodule--helper: fix a leak in \"clone_submodule\"\n 2:  390c5174e17 !  2:  d885e1dd59a submodule--helper: fix trivial get_default_remote_submodule() leak\n    @@ Commit message\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/submodule--helper.c ##\n    -@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data,\n    +@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)\n      \t\tconst char *branch = remote_submodule_branch(update_data->sm_path);\n      \t\tchar *remote_ref = xstrfmt(\"refs/remotes/%s/%s\", remote_name, branch);\n      \n 3:  529e10233d5 =  3:  1f90348d61f submodule--helper: fix most \"struct pathspec\" memory leaks\n 4:  683d327752f =  4:  f768ff2e34d submodule--helper: \"struct pathspec\" memory leak in module_update()\n 5:  4e8e9197539 !  5:  509133c37fa submodule--helper: don't leak {run,capture}_command() cp.dir argument\n    @@ builtin/submodule--helper.c: static int fetch_in_submodule(const char *module_pa\n      \n      \tstrvec_push(&cp.args, \"fetch\");\n      \tif (quiet)\n    -@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce,\n    +@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)\n      \t}\n      \tstrvec_push(&cp.args, oid);\n      \n    @@ builtin/submodule--helper.c: static int run_update_command(struct update_data *u\n     +\tcp.dir = ud->sm_path;\n      \tprepare_submodule_repo_env(&cp.env);\n      \tif (run_command(&cp)) {\n    - \t\tswitch (ud->update_strategy.type) {\n    + \t\tint ret;\n 6:  575d3e8d2e2 !  6:  25377f1d06c submodule--helper: add and use *_release() functions\n    @@ builtin/submodule--helper.c: static int update_submodules(struct update_data *up\n      cleanup:\n     +\tsubmodule_update_clone_release(&suc);\n      \tstring_list_clear(&update_data->references, 0);\n    - \treturn res;\n    + \treturn ret;\n      }\n     @@ builtin/submodule--helper.c: static int module_update(int argc, const char **argv, const char *prefix)\n      \n 7:  d4c81e04254 =  7:  1a85057cd0c submodule--helper: fix \"errmsg_str\" memory leak\n 8:  ef9e29d5bfe =  8:  3c4f734e958 submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks\n 9:  0798a00c9ef !  9:  3aebff9f8e3 submodule--helper: fix a leak with repo_clear()\n    @@ Commit message\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/submodule--helper.c ##\n    -@@ builtin/submodule--helper.c: static void ensure_core_worktree(const char *path)\n    - \t\tfree(abs_path);\n    +@@ builtin/submodule--helper.c: static int ensure_core_worktree(const char *path)\n      \t\tstrbuf_release(&sb);\n      \t}\n    + \n     +\trepo_clear(&subrepo);\n    + \treturn 0;\n      }\n      \n    - static const char *submodule_update_type_to_label(enum submodule_update_type type)\n10:  dae2a6f8e07 = 10:  38345ec76bc submodule--helper: fix a memory leak in get_default_remote_submodule()\n11:  e7352bb8cfa = 11:  4b1e5ced969 submodule--helper: fix \"reference\" leak\n12:  1adb7b66656 = 12:  e74c396073b submodule--helper: fix obscure leak in module_add()\n13:  b27b665d287 = 13:  71a56c59864 submodule--helper: fix a leak in module_add()\n14:  53ba1705eb6 = 14:  2429db2f1c7 submodule--helper: fix a memory leak in print_status()\n15:  230e5f8ad14 = 15:  613d077c4ec submodule--helper: free some \"displaypath\" in \"struct update_data\"\n16:  c0fba2f1c56 ! 16:  8f150a81507 submodule--helper: free rest of \"displaypath\" in \"struct update_data\"\n    @@ Commit message\n         update_submodule() function itself can use it, and for the\n         run_update_procedure() called within this function.\n     \n    +    To make managing that clobbering easier let's wrap the\n    +    update_submodule() in a new update_submodule_outer() function, which\n    +    will do the clobbering and free(to_free) dance for us.\n    +\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/submodule--helper.c ##\n    -@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data,\n    - \t\t\t    int *must_die_on_failure)\n    +@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)\n      {\n    - \tint ret = 1;\n    -+\tchar *to_free, *restore = update_data->displaypath;\n    - \n    - \tensure_core_worktree(update_data->sm_path);\n    + \tint ret;\n      \n    +-\tret = ensure_core_worktree(update_data->sm_path);\n    +-\tif (ret)\n    +-\t\treturn ret;\n    +-\n     -\tupdate_data->displaypath = get_submodule_displaypath(\n    -+\tupdate_data->displaypath = to_free = get_submodule_displaypath(\n    - \t\tupdate_data->sm_path, update_data->prefix);\n    - \n    - \tdetermine_submodule_update_strategy(the_repository, update_data->just_cloned,\n    -@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data,\n    +-\t\tupdate_data->sm_path, update_data->prefix);\n    +-\n    + \tret = determine_submodule_update_strategy(the_repository,\n    + \t\t\t\t\t\t  update_data->just_cloned,\n    + \t\t\t\t\t\t  update_data->sm_path,\n    +@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)\n    + \treturn 0;\n    + }\n      \n    - \tret = 0;\n    - cleanup:\n    ++static int update_submodule_outer(struct update_data *update_data)\n    ++{\n    ++\tchar *to_free, *restore = update_data->displaypath;\n    ++\tint ret;\n    ++\n    ++\tensure_core_worktree(update_data->sm_path);\n    ++\n    ++\tupdate_data->displaypath = to_free = get_submodule_displaypath(\n    ++\t\tupdate_data->sm_path, update_data->prefix);\n    ++\n    ++\tret = update_submodule(update_data);\n    ++\n     +\tfree(to_free);\n     +\tupdate_data->displaypath = restore;\n     +\n    - \treturn ret;\n    - }\n    ++\treturn ret;\n    ++}\n    ++\n    + static int update_submodules(struct update_data *update_data)\n    + {\n    + \tint i, ret = 0;\n    +@@ builtin/submodule--helper.c: static int update_submodules(struct update_data *update_data)\n    + \t\tupdate_data->just_cloned = ucd.just_cloned;\n    + \t\tupdate_data->sm_path = ucd.sub->path;\n      \n    +-\t\tcode = update_submodule(update_data);\n    ++\t\tcode = update_submodule_outer(update_data);\n    + \t\tif (code)\n    + \t\t\tret = code;\n    + \t\tif (code == 128)\n17:  95f8b68bd41 ! 17:  17c77ceba01 submodule--helper: fix a configure_added_submodule() leak\n    @@ builtin/submodule--helper.c: static void configure_added_submodule(struct add_da\n      \t\t * current configured pathspec, set the submodule's active flag\n     \n      ## t/t7413-submodule-is-active.sh ##\n    -@@ t/t7413-submodule-is-active.sh: This test verifies that `test-tool submodule is-active` correctly identifies\n    - submodules which are \"active\" and interesting to the user.\n    +@@ t/t7413-submodule-is-active.sh: This is a unit test of the submodule.c is_submodule_active() function,\n    + which is also indirectly tested elsewhere.\n      '\n      \n     +TEST_PASSES_SANITIZE_LEAK=true\n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460477","messageId":"patch-v5-02.17-d885e1dd59a-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v5 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:26Z","receivedAt":"2022-08-02T15:54:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in code added in 1012a5cbc3f (submodule--helper\nrun-update-procedure: learn --remote, 2022-03-04), we need to free()\nthe xstrdup()'d string. This gets e.g. t/t7419-submodule-set-branch.sh\ncloser to passing under SANITIZE=leak.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d7eaedee491..d958da7dddc 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2437,6 +2437,8 @@ static int update_submodule(struct update_data *update_data)\n \t\tconst char *branch = remote_submodule_branch(update_data->sm_path);\n \t\tchar *remote_ref = xstrfmt(\"refs/remotes/%s/%s\", remote_name, branch);\n \n+\t\tfree(remote_name);\n+\n \t\tif (!update_data->nofetch) {\n \t\t\tif (fetch_in_submodule(update_data->sm_path, update_data->depth,\n \t\t\t\t\t      0, NULL))\n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460478","messageId":"patch-v5-01.17-118e965d401-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v5 01/17] submodule--helper: fix a leak in \"clone_submodule\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:25Z","receivedAt":"2022-08-02T15:55:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak of the \"clone_data_path\" variable that we copy or\nderive from the \"struct module_clone_data\" in clone_submodule(). This\ncode was refactored in preceding commits, but the leak has been with\nus since f8eaa0ba98b (submodule--helper, module_clone: always operate\non absolute paths, 2016-03-31).\n\nFor the \"else\" case we don't need to xstrdup() the \"clone_data->path\",\nand we don't need to free our own \"clone_data_path\". We can therefore\nassign the \"clone_data->path\" to our own \"clone_data_path\" right away,\nand only override it (and remember to free it!) if we need to\nxstrfmt() a replacement.\n\nIn the case of the module_clone() caller it's from \"argv\", and doesn't\nneed to be free'd, and in the case of the add_submodule() caller we\nget a pointer to \"sm_path\", which doesn't need to be directly free'd\neither.\n\nFixing this leak makes several tests pass, so let's mark them as\npassing with TEST_PASSES_SANITIZE_LEAK=true.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c   | 10 +++++-----\n t/t1500-rev-parse.sh          |  1 +\n t/t6008-rev-list-submodule.sh |  1 +\n t/t7414-submodule-mistakes.sh |  2 ++\n t/t7506-status-submodule.sh   |  1 +\n t/t7507-commit-verbose.sh     |  2 ++\n 6 files changed, 12 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 5bc9cc41369..d7eaedee491 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1592,13 +1592,12 @@ static int clone_submodule(const struct module_clone_data *clone_data,\n \tchar *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);\n \tchar *sm_alternate = NULL, *error_strategy = NULL;\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n-\tconst char *clone_data_path;\n+\tconst char *clone_data_path = clone_data->path;\n+\tchar *to_free = NULL;\n \n \tif (!is_absolute_path(clone_data->path))\n-\t\tclone_data_path = xstrfmt(\"%s/%s\", get_git_work_tree(),\n-\t\t\t\t\t  clone_data->path);\n-\telse\n-\t\tclone_data_path = xstrdup(clone_data->path);\n+\t\tclone_data_path = to_free = xstrfmt(\"%s/%s\", get_git_work_tree(),\n+\t\t\t\t\t\t    clone_data->path);\n \n \tif (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)\n \t\tdie(_(\"refusing to create/use '%s' in another submodule's \"\n@@ -1683,6 +1682,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,\n \n \tfree(sm_gitdir);\n \tfree(p);\n+\tfree(to_free);\n \treturn 0;\n }\n \ndiff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh\nindex 1c2df08333b..0e13bcb4ebb 100755\n--- a/t/t1500-rev-parse.sh\n+++ b/t/t1500-rev-parse.sh\n@@ -4,6 +4,7 @@ test_description='test git rev-parse'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_one () {\ndiff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh\nindex 3153a0d8910..12e67e187ef 100755\n--- a/t/t6008-rev-list-submodule.sh\n+++ b/t/t6008-rev-list-submodule.sh\n@@ -8,6 +8,7 @@ test_description='git rev-list involving submodules that this repo has'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh\nindex f2e7df59cf2..3269298197c 100755\n--- a/t/t7414-submodule-mistakes.sh\n+++ b/t/t7414-submodule-mistakes.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='handling of common mistakes people may make with submodules'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'create embedded repository' '\ndiff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh\nindex 3fcb44767f5..f5426a8e589 100755\n--- a/t/t7506-status-submodule.sh\n+++ b/t/t7506-status-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='git status for submodule'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_create_repo_with_commit () {\ndiff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh\nindex ed2653d46fe..92462a22374 100755\n--- a/t/t7507-commit-verbose.sh\n+++ b/t/t7507-commit-verbose.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='verbose commit template'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n write_script \"check-for-diff\" <<\\EOF &&\n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460479","messageId":"patch-v5-04.17-f768ff2e34d-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v5 04/17] submodule--helper: \"struct pathspec\" memory leak in module_update()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:28Z","receivedAt":"2022-08-02T15:55:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The module_update() function calls module_list_compute() twice, which\nin turn will reset the \"struct pathspec\" passed to it. Let's instead\ntrack two of them, and clear them both.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 92d32f2877f..dd9a62d139d 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2552,6 +2552,7 @@ static int update_submodules(struct update_data *update_data)\n static int module_update(int argc, const char **argv, const char *prefix)\n {\n \tstruct pathspec pathspec = { 0 };\n+\tstruct pathspec pathspec2 = { 0 };\n \tstruct update_data opt = UPDATE_DATA_INIT;\n \tstruct list_objects_filter_options filter_options = { 0 };\n \tint ret;\n@@ -2640,7 +2641,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\tstruct init_cb info = INIT_CB_INIT;\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n-\t\t\t\t\t&pathspec, &list) < 0) {\n+\t\t\t\t\t&pathspec2, &list) < 0) {\n \t\t\tret = 1;\n \t\t\tgoto cleanup;\n \t\t}\n@@ -2663,6 +2664,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n cleanup:\n \tlist_objects_filter_release(&filter_options);\n \tclear_pathspec(&pathspec);\n+\tclear_pathspec(&pathspec2);\n \treturn ret;\n }\n \n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460480","messageId":"patch-v5-03.17-1f90348d61f-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v5 03/17] submodule--helper: fix most \"struct pathspec\" memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:27Z","receivedAt":"2022-08-02T15:55:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call clear_pathspec() at the end of various functions that work with\nand allocate a \"struct pathspec\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 74 +++++++++++++++++++++++++------------\n 1 file changed, 51 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d958da7dddc..92d32f2877f 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -367,7 +367,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,\n static int module_foreach(int argc, const char **argv, const char *prefix)\n {\n \tstruct foreach_cb info = FOREACH_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tstruct option module_foreach_options[] = {\n \t\tOPT__QUIET(&info.quiet, N_(\"suppress output of entering each submodule command\")),\n@@ -379,12 +379,13 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule foreach [--quiet] [--recursive] [--] <command>\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_foreach_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.argc = argc;\n \tinfo.argv = argv;\n@@ -392,7 +393,10 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static int starts_with_dot_slash(const char *const path)\n@@ -522,7 +526,7 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data\n static int module_init(int argc, const char **argv, const char *prefix)\n {\n \tstruct init_cb info = INIT_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tstruct option module_init_options[] = {\n@@ -533,12 +537,13 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule init [<options>] [<path>]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_init_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \t/*\n \t * If there are no path args and submodule.active is set then,\n@@ -553,7 +558,10 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, init_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct status_cb {\n@@ -700,7 +708,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,\n static int module_status(int argc, const char **argv, const char *prefix)\n {\n \tstruct status_cb info = STATUS_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tstruct option module_status_options[] = {\n@@ -713,12 +721,13 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule status [--quiet] [--cached] [--recursive] [<path>...]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_status_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -726,7 +735,10 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, status_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct module_cb {\n@@ -1265,7 +1277,7 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data\n static int module_sync(int argc, const char **argv, const char *prefix)\n {\n \tstruct sync_cb info = SYNC_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tint recursive = 0;\n@@ -1279,12 +1291,13 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule sync [--quiet] [--recursive] [<path>]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_sync_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -1294,7 +1307,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, sync_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct deinit_cb {\n@@ -1403,7 +1419,7 @@ static void deinit_submodule_cb(const struct cache_entry *list_item,\n static int module_deinit(int argc, const char **argv, const char *prefix)\n {\n \tstruct deinit_cb info = DEINIT_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tint force = 0;\n@@ -1418,6 +1434,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_deinit_options,\n \t\t\t     git_submodule_helper_usage, 0);\n@@ -1432,7 +1449,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"Use '--all' if you really want to deinitialize all submodules\"));\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -1442,7 +1459,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, deinit_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct module_clone_data {\n@@ -2531,7 +2551,7 @@ static int update_submodules(struct update_data *update_data)\n \n static int module_update(int argc, const char **argv, const char *prefix)\n {\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct update_data opt = UPDATE_DATA_INIT;\n \tstruct list_objects_filter_options filter_options = { 0 };\n \tint ret;\n@@ -2608,8 +2628,8 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\topt.update_strategy.type = opt.update_default;\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {\n-\t\tlist_objects_filter_release(&filter_options);\n-\t\treturn 1;\n+\t\tret = 1;\n+\t\tgoto cleanup;\n \t}\n \n \tif (pathspec.nr)\n@@ -2620,8 +2640,10 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\tstruct init_cb info = INIT_CB_INIT;\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n-\t\t\t\t\t&pathspec, &list) < 0)\n-\t\t\treturn 1;\n+\t\t\t\t\t&pathspec, &list) < 0) {\n+\t\t\tret = 1;\n+\t\t\tgoto cleanup;\n+\t\t}\n \n \t\t/*\n \t\t * If there are no path args and submodule.active is set then,\n@@ -2638,7 +2660,9 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t}\n \n \tret = update_submodules(&opt);\n+cleanup:\n \tlist_objects_filter_release(&filter_options);\n+\tclear_pathspec(&pathspec);\n \treturn ret;\n }\n \n@@ -2722,7 +2746,7 @@ static int push_check(int argc, const char **argv, const char *prefix)\n static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tunsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;\n \tstruct option embed_gitdir_options[] = {\n@@ -2737,17 +2761,21 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule absorbgitdirs [<options>] [<path>...]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, embed_gitdir_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tfor (i = 0; i < list.nr; i++)\n \t\tabsorb_git_dir_into_superproject(list.entries[i]->name, flags);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static int module_config(int argc, const char **argv, const char *prefix)\n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460481","messageId":"patch-v5-05.17-509133c37fa-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v5 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:29Z","receivedAt":"2022-08-02T15:55:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak in c51f8f94e5b (submodule--helper: run update\nprocedures from C, 2021-08-24) and 3c3558f0953 (submodule--helper: run\nupdate using child process struct, 2022-03-15) by not allocating\nmemory in the first place.\n\nThe \"dir\" member of \"struct child_process\" will not be modified by\nthat API, and it's declared to be \"const char *\". So let's not\nneedlessly duplicate these strings.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex dd9a62d139d..3f79908b203 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2131,7 +2131,7 @@ static int is_tip_reachable(const char *path, struct object_id *oid)\n \tchar *hex = oid_to_hex(oid);\n \n \tcp.git_cmd = 1;\n-\tcp.dir = xstrdup(path);\n+\tcp.dir = path;\n \tcp.no_stderr = 1;\n \tstrvec_pushl(&cp.args, \"rev-list\", \"-n\", \"1\", hex, \"--not\", \"--all\", NULL);\n \n@@ -2149,7 +2149,7 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str\n \n \tprepare_submodule_repo_env(&cp.env);\n \tcp.git_cmd = 1;\n-\tcp.dir = xstrdup(module_path);\n+\tcp.dir = module_path;\n \n \tstrvec_push(&cp.args, \"fetch\");\n \tif (quiet)\n@@ -2201,7 +2201,7 @@ static int run_update_command(struct update_data *ud, int subforce)\n \t}\n \tstrvec_push(&cp.args, oid);\n \n-\tcp.dir = xstrdup(ud->sm_path);\n+\tcp.dir = ud->sm_path;\n \tprepare_submodule_repo_env(&cp.env);\n \tif (run_command(&cp)) {\n \t\tint ret;\n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460482","messageId":"patch-v5-06.17-25377f1d06c-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v5 06/17] submodule--helper: add and use *_release() functions","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:30Z","receivedAt":"2022-08-02T15:55:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add release functions for \"struct module_list\", \"struct\nsubmodule_update_clone\" and \"struct update_data\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c      | 28 +++++++++++++++++++++++++++-\n t/t6134-pathspec-in-submodule.sh |  1 +\n 2 files changed, 28 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 3f79908b203..8e9ef58f8be 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -160,6 +160,11 @@ struct module_list {\n };\n #define MODULE_LIST_INIT { 0 }\n \n+static void module_list_release(struct module_list *ml)\n+{\n+\tfree(ml->entries);\n+}\n+\n static int module_list_compute(int argc, const char **argv,\n \t\t\t       const char *prefix,\n \t\t\t       struct pathspec *pathspec,\n@@ -222,7 +227,7 @@ static void module_list_active(struct module_list *list)\n \t\tactive_modules.entries[active_modules.nr++] = ce;\n \t}\n \n-\tfree(list->entries);\n+\tmodule_list_release(list);\n \t*list = active_modules;\n }\n \n@@ -395,6 +400,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -560,6 +566,7 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -737,6 +744,7 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1309,6 +1317,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1461,6 +1470,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1836,6 +1846,12 @@ struct submodule_update_clone {\n };\n #define SUBMODULE_UPDATE_CLONE_INIT { 0 }\n \n+static void submodule_update_clone_release(struct submodule_update_clone *suc)\n+{\n+\tfree(suc->update_clone);\n+\tfree(suc->failed_clones);\n+}\n+\n struct update_data {\n \tconst char *prefix;\n \tconst char *displaypath;\n@@ -1874,6 +1890,11 @@ struct update_data {\n \t.max_jobs = 1, \\\n }\n \n+static void update_data_release(struct update_data *ud)\n+{\n+\tmodule_list_release(&ud->list);\n+}\n+\n static void next_submodule_warn_missing(struct submodule_update_clone *suc,\n \t\tstruct strbuf *out, const char *displaypath)\n {\n@@ -2545,6 +2566,7 @@ static int update_submodules(struct update_data *update_data)\n \t}\n \n cleanup:\n+\tsubmodule_update_clone_release(&suc);\n \tstring_list_clear(&update_data->references, 0);\n \treturn ret;\n }\n@@ -2642,6 +2664,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n \t\t\t\t\t&pathspec2, &list) < 0) {\n+\t\t\tmodule_list_release(&list);\n \t\t\tret = 1;\n \t\t\tgoto cleanup;\n \t\t}\n@@ -2658,10 +2681,12 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\t\tinfo.flags |= OPT_QUIET;\n \n \t\tfor_each_listed_submodule(&list, init_submodule_cb, &info);\n+\t\tmodule_list_release(&list);\n \t}\n \n \tret = update_submodules(&opt);\n cleanup:\n+\tupdate_data_release(&opt);\n \tlist_objects_filter_release(&filter_options);\n \tclear_pathspec(&pathspec);\n \tclear_pathspec(&pathspec2);\n@@ -2777,6 +2802,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n \tret = 0;\n cleanup:\n \tclear_pathspec(&pathspec);\n+\tmodule_list_release(&list);\n \treturn ret;\n }\n \ndiff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh\nindex 0f1cb49cedc..3a241f259de 100755\n--- a/t/t6134-pathspec-in-submodule.sh\n+++ b/t/t6134-pathspec-in-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='test case exclude pathspec'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a submodule' '\n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460483","messageId":"patch-v5-07.17-1a85057cd0c-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v5 07/17] submodule--helper: fix \"errmsg_str\" memory leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:31Z","receivedAt":"2022-08-02T15:55:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak introduced in e83e3333b57 (submodule: port submodule\nsubcommand 'summary' from shell to C, 2020-08-13), we sometimes append\nto the \"errmsg\", and need to free the \"struct strbuf\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 8e9ef58f8be..68bf0f9178f 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -980,6 +980,7 @@ static void generate_submodule_summary(struct summary_cb *info,\n \tfree(displaypath);\n \tfree(src_abbrev);\n \tfree(dst_abbrev);\n+\tstrbuf_release(&errmsg);\n }\n \n static void prepare_submodule_summary(struct summary_cb *info,\n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460484","messageId":"patch-v5-08.17-3c4f734e958-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v5 08/17] submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:32Z","receivedAt":"2022-08-02T15:55:12Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix leaks in \"struct module_cb_list\" and the \"struct module_cb\" which\nit contains, these fixes leaks in e83e3333b57 (submodule: port\nsubmodule subcommand 'summary' from shell to C, 2020-08-13).\n\nThe \"sm_path\" should always have been a \"char *\", not a \"const\nchar *\", we always create it with xstrdup().\n\nWe can't mark any tests passing passing with SANITIZE=leak using\n\"TEST_PASSES_SANITIZE_LEAK=true\" as a result of this change, but\n\"t7401-submodule-summary.sh\" gets closer to passing as a result of\nthis change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 21 ++++++++++++++++++++-\n 1 file changed, 20 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 68bf0f9178f..f332627d19e 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -755,16 +755,34 @@ struct module_cb {\n \tstruct object_id oid_src;\n \tstruct object_id oid_dst;\n \tchar status;\n-\tconst char *sm_path;\n+\tchar *sm_path;\n };\n #define MODULE_CB_INIT { 0 }\n \n+static void module_cb_release(struct module_cb *mcb)\n+{\n+\tfree(mcb->sm_path);\n+}\n+\n struct module_cb_list {\n \tstruct module_cb **entries;\n \tint alloc, nr;\n };\n #define MODULE_CB_LIST_INIT { 0 }\n \n+static void module_cb_list_release(struct module_cb_list *mcbl)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < mcbl->nr; i++) {\n+\t\tstruct module_cb *mcb = mcbl->entries[i];\n+\n+\t\tmodule_cb_release(mcb);\n+\t\tfree(mcb);\n+\t}\n+\tfree(mcbl->entries);\n+}\n+\n struct summary_cb {\n \tint argc;\n \tconst char **argv;\n@@ -1108,6 +1126,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n cleanup:\n \tstrvec_clear(&diff_args);\n \trelease_revisions(&rev);\n+\tmodule_cb_list_release(&list);\n \treturn ret;\n }\n \n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460485","messageId":"patch-v5-09.17-3aebff9f8e3-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v5 09/17] submodule--helper: fix a leak with repo_clear()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:33Z","receivedAt":"2022-08-02T15:55:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call repo_clear() in ensure_core_worktree() to free the \"struct\nrepository\". Fixes a leak that's been here since\n74d4731da1f (submodule--helper: replace connect-gitdir-workingtree by\nensure-core-worktree, 2018-08-13).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex f332627d19e..4e964a2c29c 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2396,6 +2396,7 @@ static int ensure_core_worktree(const char *path)\n \t\tstrbuf_release(&sb);\n \t}\n \n+\trepo_clear(&subrepo);\n \treturn 0;\n }\n \n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460486","messageId":"patch-v5-10.17-38345ec76bc-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v5 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:34Z","receivedAt":"2022-08-02T15:55:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak in the get_default_remote_submodule() function added\nin a77c3fcb5ec (submodule--helper: get remote names from any\nrepository, 2022-03-04), we need to repo_clear() the submodule we\ninitialize.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 4e964a2c29c..902349976da 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -62,9 +62,13 @@ static char *repo_get_default_remote(struct repository *repo)\n static char *get_default_remote_submodule(const char *module_path)\n {\n \tstruct repository subrepo;\n+\tchar *ret;\n \n \trepo_submodule_init(&subrepo, the_repository, module_path, null_oid());\n-\treturn repo_get_default_remote(&subrepo);\n+\tret = repo_get_default_remote(&subrepo);\n+\trepo_clear(&subrepo);\n+\n+\treturn ret;\n }\n \n static char *get_default_remote(void)\n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460487","messageId":"patch-v5-11.17-4b1e5ced969-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v5 11/17] submodule--helper: fix \"reference\" leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:35Z","receivedAt":"2022-08-02T15:55:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix leaks in the \"reference\" variable declared in add_submodule() and\nmodule_clone().\n\nIn preceding commits this variable was refactored out of the \"struct\nmodule_clone_data\", but the leak has been with us since\n31224cbdc72 (clone: recursive and reference option triggers submodule\nalternates, 2016-08-17) and 8c8195e9c3e (submodule--helper: introduce\nadd-clone subcommand, 2021-07-10).\n\nThose commits added an xstrdup()'d member of the\nSTRING_LIST_INIT_NODUP'd \"struct string_list\". We need to free()\nthose, but not the ones we get from argv, let's make use of the \"util\"\nmember, if it has a pointer it's the pointer we'll need to free,\notherwise it'll be NULL (i.e. from argv).\n\nNote that the free() of the \"util\" member is needed in both\nmodule_clone() and add_submodule(). The module_clone() function itself\ndoesn't populate the \"util\" pointer as add_submodule() does, but\nmodule_clone() is upstream of the\nadd_possible_reference_from_superproject() caller we're modifying\nhere, which does do that.\n\nThis does preclude the use of the \"util\" pointer for any other reasons\nfor now, but that's OK. If we ever need to use it for something else\nwe could turn it into a small \"struct\" with an optional \"to_free\"\nmember, and switch to using string_list_clear_func().\n\nAlternatively we could have another \"struct string_list to_free\" which\nwould keep a copy of the strings we've dup'd to free(). But for now\nthis is perfectly adequate.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 21 +++++++++++++++------\n 1 file changed, 15 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 902349976da..a5b0b3859c5 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1568,7 +1568,9 @@ static int add_possible_reference_from_superproject(\n \n \t\tsm_alternate = compute_alternate_path(sb.buf, &err);\n \t\tif (sm_alternate) {\n-\t\t\tstring_list_append(sas->reference, xstrdup(sb.buf));\n+\t\t\tchar *p = strbuf_detach(&sb, NULL);\n+\n+\t\t\tstring_list_append(sas->reference, p)->util = p;\n \t\t\tfree(sm_alternate);\n \t\t} else {\n \t\t\tswitch (sas->error_mode) {\n@@ -1800,6 +1802,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \n \tclone_submodule(&clone_data, &reference);\n \tlist_objects_filter_release(&filter_options);\n+\tstring_list_clear(&reference, 1);\n \treturn 0;\n }\n \n@@ -3033,6 +3036,7 @@ static int add_submodule(const struct add_data *add_data)\n \tchar *submod_gitdir_path;\n \tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n \tstruct string_list reference = STRING_LIST_INIT_NODUP;\n+\tint ret = -1;\n \n \t/* perhaps the path already exists and is already a git repo, else clone it */\n \tif (is_directory(add_data->sm_path)) {\n@@ -3088,15 +3092,17 @@ static int add_submodule(const struct add_data *add_data)\n \t\tclone_data.url = add_data->realrepo;\n \t\tclone_data.quiet = add_data->quiet;\n \t\tclone_data.progress = add_data->progress;\n-\t\tif (add_data->reference_path)\n-\t\t\tstring_list_append(&reference,\n-\t\t\t\t\t   xstrdup(add_data->reference_path));\n+\t\tif (add_data->reference_path) {\n+\t\t\tchar *p = xstrdup(add_data->reference_path);\n+\n+\t\t\tstring_list_append(&reference, p)->util = p;\n+\t\t}\n \t\tclone_data.dissociate = add_data->dissociate;\n \t\tif (add_data->depth >= 0)\n \t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n \n \t\tif (clone_submodule(&clone_data, &reference))\n-\t\t\treturn -1;\n+\t\t\tgoto cleanup;\n \n \t\tprepare_submodule_repo_env(&cp.env);\n \t\tcp.git_cmd = 1;\n@@ -3115,7 +3121,10 @@ static int add_submodule(const struct add_data *add_data)\n \t\tif (run_command(&cp))\n \t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n \t}\n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tstring_list_clear(&reference, 1);\n+\treturn ret;\n }\n \n static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)\n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460488","messageId":"patch-v5-12.17-e74c396073b-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v5 12/17] submodule--helper: fix obscure leak in module_add()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:36Z","receivedAt":"2022-08-02T15:55:25Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix an obscure leak in module_add(), if the \"git add\" command we were\npiping to failed we'd fail to strbuf_release(&sb). This fixes a leak\nintroduced in a6226fd772b (submodule--helper: convert the bulk of\ncmd_add() to C, 2021-08-10).\n\nIn fixing it move to a \"goto cleanup\" pattern, and since we need to\nintroduce a \"ret\" variable to do that let's also get rid of the\nintermediate \"exit_code\" variable. The initialization to \"-1\" in\na6226fd772b has always been redundant, we'd only use the \"exit_code\"\nvalue after assigning the return value of pipe_command() to it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 22 +++++++++++-----------\n 1 file changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex a5b0b3859c5..9cc92b4da9e 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3284,6 +3284,8 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule add [<options>] [--] <repository> [<path>]\"),\n \t\tNULL\n \t};\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, options, usage, 0);\n \n@@ -3333,21 +3335,17 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \tdie_on_repo_without_commits(add_data.sm_path);\n \n \tif (!force) {\n-\t\tint exit_code = -1;\n-\t\tstruct strbuf sb = STRBUF_INIT;\n \t\tstruct child_process cp = CHILD_PROCESS_INIT;\n \n \t\tcp.git_cmd = 1;\n \t\tcp.no_stdout = 1;\n \t\tstrvec_pushl(&cp.args, \"add\", \"--dry-run\", \"--ignore-missing\",\n \t\t\t     \"--no-warn-embedded-repo\", add_data.sm_path, NULL);\n-\t\tif ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {\n+\t\tif ((ret = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {\n \t\t\tstrbuf_complete_line(&sb);\n \t\t\tfputs(sb.buf, stderr);\n-\t\t\tfree(add_data.sm_path);\n-\t\t\treturn exit_code;\n+\t\t\tgoto cleanup;\n \t\t}\n-\t\tstrbuf_release(&sb);\n \t}\n \n \tif(!add_data.sm_name)\n@@ -3362,15 +3360,17 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \tadd_data.progress = !!progress;\n \tadd_data.dissociate = !!dissociate;\n \n-\tif (add_submodule(&add_data)) {\n-\t\tfree(add_data.sm_path);\n-\t\treturn 1;\n-\t}\n+\tif (add_submodule(&add_data))\n+\t\tgoto cleanup;\n \tconfigure_added_submodule(&add_data);\n+\n+\tret = 0;\n+cleanup:\n \tfree(add_data.sm_path);\n \tfree(to_free);\n+\tstrbuf_release(&sb);\n \n-\treturn 0;\n+\treturn ret;\n }\n \n #define SUPPORT_SUPER_PREFIX (1<<0)\n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460489","messageId":"patch-v5-13.17-71a56c59864-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v5 13/17] submodule--helper: fix a leak in module_add()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:37Z","receivedAt":"2022-08-02T15:55:25Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in module_path(), since a6226fd772b (submodule--helper:\nconvert the bulk of cmd_add() to C, 2021-08-10), we've been freeing\nadd_data.sm_path, but in this case we clobbered it, and didn't free\nthe value we clobbered.\n\nThis makes test 28 of \"t/t7400-submodule-basic.sh\" (\"submodule add in\nsubdirectory\") pass when we're compiled with SANITIZE=leak..\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 9cc92b4da9e..2fca6d66bb5 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3305,8 +3305,12 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \telse\n \t\tadd_data.sm_path = xstrdup(argv[1]);\n \n-\tif (prefix && *prefix && !is_absolute_path(add_data.sm_path))\n-\t\tadd_data.sm_path = xstrfmt(\"%s%s\", prefix, add_data.sm_path);\n+\tif (prefix && *prefix && !is_absolute_path(add_data.sm_path)) {\n+\t\tchar *sm_path = add_data.sm_path;\n+\n+\t\tadd_data.sm_path = xstrfmt(\"%s%s\", prefix, sm_path);\n+\t\tfree(sm_path);\n+\t}\n \n \tif (starts_with_dot_dot_slash(add_data.repo) ||\n \t    starts_with_dot_slash(add_data.repo)) {\n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460490","messageId":"patch-v5-15.17-613d077c4ec-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v5 15/17] submodule--helper: free some \"displaypath\" in \"struct update_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:39Z","receivedAt":"2022-08-02T15:55:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Make the update_data_release() function free \"displaypath\" member when\nappropriate. The \"displaypath\" member is always ours, the \"const\" on\nthe \"char *\" was wrong to begin with.\n\nThis leaves a leak of \"displaypath\" in update_submodule(), which as\nwe'll see in subsequent commits is harder to deal with than this\ntrivial fix.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c        | 3 ++-\n t/t2403-worktree-move.sh           | 1 +\n t/t7412-submodule-absorbgitdirs.sh | 1 +\n 3 files changed, 4 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 0f1846021e6..79eca6c439b 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1882,7 +1882,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)\n \n struct update_data {\n \tconst char *prefix;\n-\tconst char *displaypath;\n+\tchar *displaypath;\n \tenum submodule_update_type update_default;\n \tstruct object_id suboid;\n \tstruct string_list references;\n@@ -1920,6 +1920,7 @@ struct update_data {\n \n static void update_data_release(struct update_data *ud)\n {\n+\tfree(ud->displaypath);\n \tmodule_list_release(&ud->list);\n }\n \ndiff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh\nindex a4e1a178e0a..1168e9f9982 100755\n--- a/t/t2403-worktree-move.sh\n+++ b/t/t2403-worktree-move.sh\n@@ -2,6 +2,7 @@\n \n test_description='test git worktree move, remove, lock and unlock'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh\nindex 1cfa150768d..2859695c6d2 100755\n--- a/t/t7412-submodule-absorbgitdirs.sh\n+++ b/t/t7412-submodule-absorbgitdirs.sh\n@@ -6,6 +6,7 @@ This test verifies that `git submodue absorbgitdirs` moves a submodules git\n directory into the superproject.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a real submodule' '\n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460491","messageId":"patch-v5-14.17-2429db2f1c7-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v5 14/17] submodule--helper: fix a memory leak in print_status()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:38Z","receivedAt":"2022-08-02T15:55:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in print_status(), the compute_rev_name() function\nimplemented in this file will return a strbuf_detach()'d value, or\nNULL.\n\nThis leak has existed since this code was added in\na9f8a37584a (submodule: port submodule subcommand 'status' from shell\nto C, 2017-10-06), but in 0b5e2ea7cf3 (submodule--helper: don't print\nnull in 'submodule status', 2018-04-18) we added a \"const\"\nintermediate variable for the return value, that \"const\" should be\nremoved.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 2fca6d66bb5..0f1846021e6 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -590,10 +590,11 @@ static void print_status(unsigned int flags, char state, const char *path,\n \tprintf(\"%c%s %s\", state, oid_to_hex(oid), displaypath);\n \n \tif (state == ' ' || state == '+') {\n-\t\tconst char *name = compute_rev_name(path, oid_to_hex(oid));\n+\t\tchar *name = compute_rev_name(path, oid_to_hex(oid));\n \n \t\tif (name)\n \t\t\tprintf(\" (%s)\", name);\n+\t\tfree(name);\n \t}\n \n \tprintf(\"\\n\");\n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460492","messageId":"patch-v5-16.17-8f150a81507-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v5 16/17] submodule--helper: free rest of \"displaypath\" in \"struct update_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:40Z","receivedAt":"2022-08-02T15:55:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in code added in c51f8f94e5b (submodule--helper: run update\nprocedures from C, 2021-08-24), we clobber the \"displaypath\" member of\nthe passed-in \"struct update_data\" both so that die() messages in this\nupdate_submodule() function itself can use it, and for the\nrun_update_procedure() called within this function.\n\nTo make managing that clobbering easier let's wrap the\nupdate_submodule() in a new update_submodule_outer() function, which\nwill do the clobbering and free(to_free) dance for us.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 27 +++++++++++++++++++--------\n 1 file changed, 19 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 79eca6c439b..cc8f42ae6df 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2482,13 +2482,6 @@ static int update_submodule(struct update_data *update_data)\n {\n \tint ret;\n \n-\tret = ensure_core_worktree(update_data->sm_path);\n-\tif (ret)\n-\t\treturn ret;\n-\n-\tupdate_data->displaypath = get_submodule_displaypath(\n-\t\tupdate_data->sm_path, update_data->prefix);\n-\n \tret = determine_submodule_update_strategy(the_repository,\n \t\t\t\t\t\t  update_data->just_cloned,\n \t\t\t\t\t\t  update_data->sm_path,\n@@ -2554,6 +2547,24 @@ static int update_submodule(struct update_data *update_data)\n \treturn 0;\n }\n \n+static int update_submodule_outer(struct update_data *update_data)\n+{\n+\tchar *to_free, *restore = update_data->displaypath;\n+\tint ret;\n+\n+\tensure_core_worktree(update_data->sm_path);\n+\n+\tupdate_data->displaypath = to_free = get_submodule_displaypath(\n+\t\tupdate_data->sm_path, update_data->prefix);\n+\n+\tret = update_submodule(update_data);\n+\n+\tfree(to_free);\n+\tupdate_data->displaypath = restore;\n+\n+\treturn ret;\n+}\n+\n static int update_submodules(struct update_data *update_data)\n {\n \tint i, ret = 0;\n@@ -2586,7 +2597,7 @@ static int update_submodules(struct update_data *update_data)\n \t\tupdate_data->just_cloned = ucd.just_cloned;\n \t\tupdate_data->sm_path = ucd.sub->path;\n \n-\t\tcode = update_submodule(update_data);\n+\t\tcode = update_submodule_outer(update_data);\n \t\tif (code)\n \t\t\tret = code;\n \t\tif (code == 128)\n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460493","messageId":"patch-v5-17.17-17c77ceba01-20220802T155002Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v5 17/17] submodule--helper: fix a configure_added_submodule() leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-02T15:54:41Z","receivedAt":"2022-08-02T15:56:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix config API a memory leak added in a452128a36c (submodule--helper:\nintroduce add-config subcommand, 2021-08-06) by using the *_tmp()\nvariant of git_config_get_string().\n\nIn this case we're only checking whether\nthe (repo|git)_config_get_string() call is telling us that the\n\"submodule.active\" key exists.\n\nAs with the preceding commit we'll find many other such patterns in\nthe codebase if we go fishing. E.g. \"git gc\" leaks in the code added\nin 61f7a383d3b (maintenance: use 'incremental' strategy by default,\n2020-10-15). Similar code in \"git gc\" added in\nb08ff1fee00 (maintenance: add --schedule option and config,\n2020-09-11) doesn't leak, but we could avoid the malloc() & free() in\nthat case.\n\nA coccinelle rule to find those would find and fix some leaks, and\ncases where we're doing needless malloc() + free()'s but only care\nabout the key existence, or are copying\nthe (repo|git)_config_get_string() return value right away.\n\nBut as with the preceding commit let's punt on all of that for now,\nand just narrowly fix this specific case in submodule--helper.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c    | 4 ++--\n t/t7413-submodule-is-active.sh | 1 +\n 2 files changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex cc8f42ae6df..60757e871e6 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3158,7 +3158,7 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con\n static void configure_added_submodule(struct add_data *add_data)\n {\n \tchar *key;\n-\tchar *val = NULL;\n+\tconst char *val;\n \tstruct child_process add_submod = CHILD_PROCESS_INIT;\n \tstruct child_process add_gitmodules = CHILD_PROCESS_INIT;\n \n@@ -3203,7 +3203,7 @@ static void configure_added_submodule(struct add_data *add_data)\n \t * is_submodule_active(), since that function needs to find\n \t * out the value of \"submodule.active\" again anyway.\n \t */\n-\tif (!git_config_get_string(\"submodule.active\", &val)) {\n+\tif (!git_config_get_string_tmp(\"submodule.active\", &val)) {\n \t\t/*\n \t\t * If the submodule being added isn't already covered by the\n \t\t * current configured pathspec, set the submodule's active flag\ndiff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh\nindex ede6f02dbd5..4dc7d089423 100755\n--- a/t/t7413-submodule-is-active.sh\n+++ b/t/t7413-submodule-is-active.sh\n@@ -9,6 +9,7 @@ This is a unit test of the submodule.c is_submodule_active() function,\n which is also indirectly tested elsewhere.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n-- \n2.37.1.1233.ge8b09efaedc\n\n"},{"id":"460583","messageId":"kl6lbkt1szlk.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-v5-03.17-1f90348d61f-20220802T155002Z-avarab@gmail.com","subject":"Re: [PATCH v5 03/17] submodule--helper: fix most \"struct pathspec\" memory leaks","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-08-03T22:59:19Z","receivedAt":"2022-08-03T22:59:32Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Call clear_pathspec() at the end of various functions that work with\n> and allocate a \"struct pathspec\".\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 74 +++++++++++++++++++++++++------------\n>  1 file changed, 51 insertions(+), 23 deletions(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index d958da7dddc..92d32f2877f 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -367,7 +367,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,\n>  static int module_foreach(int argc, const char **argv, const char *prefix)\n>  {\n>  \tstruct foreach_cb info = FOREACH_CB_INIT;\n> -\tstruct pathspec pathspec;\n> +\tstruct pathspec pathspec = { 0 };\n\nOut of curiousity, does this zero-initialization do anything for us\nleaks-wise?\n\n>  \tstruct module_list list = MODULE_LIST_INIT;\n>  \tstruct option module_foreach_options[] = {\n>  \t\tOPT__QUIET(&info.quiet, N_(\"suppress output of entering each submodule command\")),\n> @@ -379,12 +379,13 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n>  \t\tN_(\"git submodule foreach [--quiet] [--recursive] [--] <command>\"),\n>  \t\tNULL\n>  \t};\n> +\tint ret = 1;\n>  \n>  \targc = parse_options(argc, argv, prefix, module_foreach_options,\n>  \t\t\t     git_submodule_helper_usage, 0);\n>  \n>  \tif (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)\n> -\t\treturn 1;\n> +\t\tgoto cleanup;\n>  \n>  \tinfo.argc = argc;\n>  \tinfo.argv = argv;\n> @@ -392,7 +393,10 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n>  \n>  \tfor_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);\n>  \n> -\treturn 0;\n> +\tret = 0;\n> +cleanup:\n> +\tclear_pathspec(&pathspec);\n> +\treturn ret;\n>  }\n>  \n>  static int starts_with_dot_slash(const char *const path)\n> @@ -522,7 +526,7 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data\n>  static int module_init(int argc, const char **argv, const char *prefix)\n>  {\n>  \tstruct init_cb info = INIT_CB_INIT;\n> -\tstruct pathspec pathspec;\n> +\tstruct pathspec pathspec = { 0 };\n>  \tstruct module_list list = MODULE_LIST_INIT;\n>  \tint quiet = 0;\n>  \tstruct option module_init_options[] = {\n> @@ -533,12 +537,13 @@ static int module_init(int argc, const char **argv, const char *prefix)\n>  \t\tN_(\"git submodule init [<options>] [<path>]\"),\n>  \t\tNULL\n>  \t};\n> +\tint ret = 1;\n>  \n>  \targc = parse_options(argc, argv, prefix, module_init_options,\n>  \t\t\t     git_submodule_helper_usage, 0);\n>  \n>  \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n> -\t\treturn 1;\n> +\t\tgoto cleanup;\n>  \n>  \t/*\n>  \t * If there are no path args and submodule.active is set then,\n> @@ -553,7 +558,10 @@ static int module_init(int argc, const char **argv, const char *prefix)\n>  \n>  \tfor_each_listed_submodule(&list, init_submodule_cb, &info);\n>  \n> -\treturn 0;\n> +\tret = 0;\n> +cleanup:\n> +\tclear_pathspec(&pathspec);\n> +\treturn ret;\n>  }\n>  \n>  struct status_cb {\n> @@ -700,7 +708,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,\n>  static int module_status(int argc, const char **argv, const char *prefix)\n>  {\n>  \tstruct status_cb info = STATUS_CB_INIT;\n> -\tstruct pathspec pathspec;\n> +\tstruct pathspec pathspec = { 0 };\n>  \tstruct module_list list = MODULE_LIST_INIT;\n>  \tint quiet = 0;\n>  \tstruct option module_status_options[] = {\n> @@ -713,12 +721,13 @@ static int module_status(int argc, const char **argv, const char *prefix)\n>  \t\tN_(\"git submodule status [--quiet] [--cached] [--recursive] [<path>...]\"),\n>  \t\tNULL\n>  \t};\n> +\tint ret = 1;\n>  \n>  \targc = parse_options(argc, argv, prefix, module_status_options,\n>  \t\t\t     git_submodule_helper_usage, 0);\n>  \n>  \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n> -\t\treturn 1;\n> +\t\tgoto cleanup;\n>  \n>  \tinfo.prefix = prefix;\n>  \tif (quiet)\n> @@ -726,7 +735,10 @@ static int module_status(int argc, const char **argv, const char *prefix)\n>  \n>  \tfor_each_listed_submodule(&list, status_submodule_cb, &info);\n>  \n> -\treturn 0;\n> +\tret = 0;\n> +cleanup:\n> +\tclear_pathspec(&pathspec);\n> +\treturn ret;\n>  }\n>  \n>  struct module_cb {\n> @@ -1265,7 +1277,7 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data\n>  static int module_sync(int argc, const char **argv, const char *prefix)\n>  {\n>  \tstruct sync_cb info = SYNC_CB_INIT;\n> -\tstruct pathspec pathspec;\n> +\tstruct pathspec pathspec = { 0 };\n>  \tstruct module_list list = MODULE_LIST_INIT;\n>  \tint quiet = 0;\n>  \tint recursive = 0;\n> @@ -1279,12 +1291,13 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n>  \t\tN_(\"git submodule sync [--quiet] [--recursive] [<path>]\"),\n>  \t\tNULL\n>  \t};\n> +\tint ret = 1;\n>  \n>  \targc = parse_options(argc, argv, prefix, module_sync_options,\n>  \t\t\t     git_submodule_helper_usage, 0);\n>  \n>  \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n> -\t\treturn 1;\n> +\t\tgoto cleanup;\n>  \n>  \tinfo.prefix = prefix;\n>  \tif (quiet)\n> @@ -1294,7 +1307,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n>  \n>  \tfor_each_listed_submodule(&list, sync_submodule_cb, &info);\n>  \n> -\treturn 0;\n> +\tret = 0;\n> +cleanup:\n> +\tclear_pathspec(&pathspec);\n> +\treturn ret;\n>  }\n>  \n>  struct deinit_cb {\n> @@ -1403,7 +1419,7 @@ static void deinit_submodule_cb(const struct cache_entry *list_item,\n>  static int module_deinit(int argc, const char **argv, const char *prefix)\n>  {\n>  \tstruct deinit_cb info = DEINIT_CB_INIT;\n> -\tstruct pathspec pathspec;\n> +\tstruct pathspec pathspec = { 0 };\n>  \tstruct module_list list = MODULE_LIST_INIT;\n>  \tint quiet = 0;\n>  \tint force = 0;\n> @@ -1418,6 +1434,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n>  \t\tN_(\"git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]\"),\n>  \t\tNULL\n>  \t};\n> +\tint ret = 1;\n>  \n>  \targc = parse_options(argc, argv, prefix, module_deinit_options,\n>  \t\t\t     git_submodule_helper_usage, 0);\n> @@ -1432,7 +1449,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n>  \t\tdie(_(\"Use '--all' if you really want to deinitialize all submodules\"));\n>  \n>  \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n> -\t\treturn 1;\n> +\t\tgoto cleanup;\n>  \n>  \tinfo.prefix = prefix;\n>  \tif (quiet)\n> @@ -1442,7 +1459,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n>  \n>  \tfor_each_listed_submodule(&list, deinit_submodule_cb, &info);\n>  \n> -\treturn 0;\n> +\tret = 0;\n> +cleanup:\n> +\tclear_pathspec(&pathspec);\n> +\treturn ret;\n>  }\n>  \n>  struct module_clone_data {\n> @@ -2531,7 +2551,7 @@ static int update_submodules(struct update_data *update_data)\n>  \n>  static int module_update(int argc, const char **argv, const char *prefix)\n>  {\n> -\tstruct pathspec pathspec;\n> +\tstruct pathspec pathspec = { 0 };\n>  \tstruct update_data opt = UPDATE_DATA_INIT;\n>  \tstruct list_objects_filter_options filter_options = { 0 };\n>  \tint ret;\n> @@ -2608,8 +2628,8 @@ static int module_update(int argc, const char **argv, const char *prefix)\n>  \t\topt.update_strategy.type = opt.update_default;\n>  \n>  \tif (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {\n> -\t\tlist_objects_filter_release(&filter_options);\n> -\t\treturn 1;\n> +\t\tret = 1;\n> +\t\tgoto cleanup;\n>  \t}\n>  \n>  \tif (pathspec.nr)\n> @@ -2620,8 +2640,10 @@ static int module_update(int argc, const char **argv, const char *prefix)\n>  \t\tstruct init_cb info = INIT_CB_INIT;\n>  \n>  \t\tif (module_list_compute(argc, argv, opt.prefix,\n> -\t\t\t\t\t&pathspec, &list) < 0)\n> -\t\t\treturn 1;\n> +\t\t\t\t\t&pathspec, &list) < 0) {\n> +\t\t\tret = 1;\n> +\t\t\tgoto cleanup;\n> +\t\t}\n>  \n>  \t\t/*\n>  \t\t * If there are no path args and submodule.active is set then,\n> @@ -2638,7 +2660,9 @@ static int module_update(int argc, const char **argv, const char *prefix)\n>  \t}\n>  \n>  \tret = update_submodules(&opt);\n> +cleanup:\n>  \tlist_objects_filter_release(&filter_options);\n> +\tclear_pathspec(&pathspec);\n>  \treturn ret;\n>  }\n>  \n> @@ -2722,7 +2746,7 @@ static int push_check(int argc, const char **argv, const char *prefix)\n>  static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n>  {\n>  \tint i;\n> -\tstruct pathspec pathspec;\n> +\tstruct pathspec pathspec = { 0 };\n>  \tstruct module_list list = MODULE_LIST_INIT;\n>  \tunsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;\n>  \tstruct option embed_gitdir_options[] = {\n> @@ -2737,17 +2761,21 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n>  \t\tN_(\"git submodule absorbgitdirs [<options>] [<path>...]\"),\n>  \t\tNULL\n>  \t};\n> +\tint ret = 1;\n>  \n>  \targc = parse_options(argc, argv, prefix, embed_gitdir_options,\n>  \t\t\t     git_submodule_helper_usage, 0);\n>  \n>  \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n> -\t\treturn 1;\n> +\t\tgoto cleanup;\n>  \n>  \tfor (i = 0; i < list.nr; i++)\n>  \t\tabsorb_git_dir_into_superproject(list.entries[i]->name, flags);\n>  \n> -\treturn 0;\n> +\tret = 0;\n> +cleanup:\n> +\tclear_pathspec(&pathspec);\n> +\treturn ret;\n>  }\n>  \n>  static int module_config(int argc, const char **argv, const char *prefix)\n> -- \n> 2.37.1.1233.ge8b09efaedc\n"},{"id":"460584","messageId":"kl6l8ro4udnj.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-v5-08.17-3c4f734e958-20220802T155002Z-avarab@gmail.com","subject":"Re: [PATCH v5 08/17] submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-08-03T23:10:24Z","receivedAt":"2022-08-03T23:10:29Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Fix leaks in \"struct module_cb_list\" and the \"struct module_cb\" which\n> it contains, these fixes leaks in e83e3333b57 (submodule: port\n\ns/fixes/fix\n\nThe diff itself LGTM.\n\n> submodule subcommand 'summary' from shell to C, 2020-08-13).\n>\n> The \"sm_path\" should always have been a \"char *\", not a \"const\n> char *\", we always create it with xstrdup().\n>\n> We can't mark any tests passing passing with SANITIZE=leak using\n> \"TEST_PASSES_SANITIZE_LEAK=true\" as a result of this change, but\n> \"t7401-submodule-summary.sh\" gets closer to passing as a result of\n> this change.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 21 ++++++++++++++++++++-\n>  1 file changed, 20 insertions(+), 1 deletion(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index 68bf0f9178f..f332627d19e 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -755,16 +755,34 @@ struct module_cb {\n>  \tstruct object_id oid_src;\n>  \tstruct object_id oid_dst;\n>  \tchar status;\n> -\tconst char *sm_path;\n> +\tchar *sm_path;\n>  };\n>  #define MODULE_CB_INIT { 0 }\n>  \n> +static void module_cb_release(struct module_cb *mcb)\n> +{\n> +\tfree(mcb->sm_path);\n> +}\n> +\n>  struct module_cb_list {\n>  \tstruct module_cb **entries;\n>  \tint alloc, nr;\n>  };\n>  #define MODULE_CB_LIST_INIT { 0 }\n>  \n> +static void module_cb_list_release(struct module_cb_list *mcbl)\n> +{\n> +\tint i;\n> +\n> +\tfor (i = 0; i < mcbl->nr; i++) {\n> +\t\tstruct module_cb *mcb = mcbl->entries[i];\n> +\n> +\t\tmodule_cb_release(mcb);\n> +\t\tfree(mcb);\n> +\t}\n> +\tfree(mcbl->entries);\n> +}\n> +\n>  struct summary_cb {\n>  \tint argc;\n>  \tconst char **argv;\n> @@ -1108,6 +1126,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n>  cleanup:\n>  \tstrvec_clear(&diff_args);\n>  \trelease_revisions(&rev);\n> +\tmodule_cb_list_release(&list);\n>  \treturn ret;\n>  }\n>  \n> -- \n> 2.37.1.1233.ge8b09efaedc\n"},{"id":"460603","messageId":"220804.86mtckebca.gmgdl@evledraar.gmail.com","threadId":"58151","inReplyTo":"kl6lbkt1szlk.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH v5 03/17] submodule--helper: fix most \"struct pathspec\" memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-04T07:04:02Z","receivedAt":"2022-08-04T07:07:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Aug 03 2022, Glen Choo wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> Call clear_pathspec() at the end of various functions that work with\n>> and allocate a \"struct pathspec\".\n>>\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>>  builtin/submodule--helper.c | 74 +++++++++++++++++++++++++------------\n>>  1 file changed, 51 insertions(+), 23 deletions(-)\n>>\n>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n>> index d958da7dddc..92d32f2877f 100644\n>> --- a/builtin/submodule--helper.c\n>> +++ b/builtin/submodule--helper.c\n>> @@ -367,7 +367,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,\n>>  static int module_foreach(int argc, const char **argv, const char *prefix)\n>>  {\n>>  \tstruct foreach_cb info = FOREACH_CB_INIT;\n>> -\tstruct pathspec pathspec;\n>> +\tstruct pathspec pathspec = { 0 };\n>\n> Out of curiousity, does this zero-initialization do anything for us\n> leaks-wise?\n\nNo, because if we leak module_list_compute() must have filled the\npathspec with something it allocated, but...\n\n>>  \tstruct module_list list = MODULE_LIST_INIT;\n>>  \tstruct option module_foreach_options[] = {\n>>  \t\tOPT__QUIET(&info.quiet, N_(\"suppress output of entering each submodule command\")),\n>> @@ -379,12 +379,13 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n>>  \t\tN_(\"git submodule foreach [--quiet] [--recursive] [--] <command>\"),\n>>  \t\tNULL\n>>  \t};\n>> +\tint ret = 1;\n>>  \n>>  \targc = parse_options(argc, argv, prefix, module_foreach_options,\n>>  \t\t\t     git_submodule_helper_usage, 0);\n>>  \n>>  \tif (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)\n>> -\t\treturn 1;\n>> +\t\tgoto cleanup;\n\n...if we don't initialize it then we can't \"goto cleanup\".\n\nNow, right now this is redundant, we could just \"return 1\", as we don't\nhave a pathspec yet.\n\nBut it's generally worth just using the same \"cleanup\" pattern\neverywhere, and not worrying about in your cleanunp code that you only\ninit'd N/TOTAL variables already.\n"},{"id":"460651","messageId":"kl6l5yj7ubpt.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-v5-16.17-8f150a81507-20220802T155002Z-avarab@gmail.com","subject":"Re: [PATCH v5 16/17] submodule--helper: free rest of \"displaypath\" in \"struct update_data\"","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-08-04T18:04:30Z","receivedAt":"2022-08-04T18:04:35Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Fix a leak in code added in c51f8f94e5b (submodule--helper: run update\n> procedures from C, 2021-08-24), we clobber the \"displaypath\" member of\n> the passed-in \"struct update_data\" both so that die() messages in this\n> update_submodule() function itself can use it, and for the\n> run_update_procedure() called within this function.\n>\n> To make managing that clobbering easier let's wrap the\n> update_submodule() in a new update_submodule_outer() function, which\n> will do the clobbering and free(to_free) dance for us.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 27 +++++++++++++++++++--------\n>  1 file changed, 19 insertions(+), 8 deletions(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index 79eca6c439b..cc8f42ae6df 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -2482,13 +2482,6 @@ static int update_submodule(struct update_data *update_data)\n>  {\n>  \tint ret;\n>  \n> -\tret = ensure_core_worktree(update_data->sm_path);\n> -\tif (ret)\n> -\t\treturn ret;\n> -\n> -\tupdate_data->displaypath = get_submodule_displaypath(\n> -\t\tupdate_data->sm_path, update_data->prefix);\n> -\n>  \tret = determine_submodule_update_strategy(the_repository,\n>  \t\t\t\t\t\t  update_data->just_cloned,\n>  \t\t\t\t\t\t  update_data->sm_path,\n> @@ -2554,6 +2547,24 @@ static int update_submodule(struct update_data *update_data)\n>  \treturn 0;\n>  }\n>  \n> +static int update_submodule_outer(struct update_data *update_data)\n> +{\n> +\tchar *to_free, *restore = update_data->displaypath;\n> +\tint ret;\n> +\n> +\tensure_core_worktree(update_data->sm_path);\n> +\n> +\tupdate_data->displaypath = to_free = get_submodule_displaypath(\n> +\t\tupdate_data->sm_path, update_data->prefix);\n> +\n> +\tret = update_submodule(update_data);\n> +\n> +\tfree(to_free);\n> +\tupdate_data->displaypath = restore;\n> +\n> +\treturn ret;\n> +}\n> +\n\nI haven't tested the alternatives yet, but on first glance using this\n*_outer() pattern here seems like overkill. At least two things I can\nthink of are:\n\na) Free .displaypath using the \"goto cleanup\" pattern like we did\n   elsewhere\n\n...\n\n>  static int update_submodules(struct update_data *update_data)\n>  {\n>  \tint i, ret = 0;\n> @@ -2586,7 +2597,7 @@ static int update_submodules(struct update_data *update_data)\n>  \t\tupdate_data->just_cloned = ucd.just_cloned;\n>  \t\tupdate_data->sm_path = ucd.sub->path;\n>  \n> -\t\tcode = update_submodule(update_data);\n> +\t\tcode = update_submodule_outer(update_data);\n>  \t\tif (code)\n>  \t\t\tret = code;\n>  \t\tif (code == 128)\n\nb) Assign and FREE_AND_NULL() update_data->displaypath here since this\n   is the only caller and it already does some prep work in this hunk.\n\nI started testing, but then realized that we don't have a\nTEST_PASSES_SANITIZE_LEAK=true for this patch, so I'll spend some more\ntime testing whether these alternatives even work and I'll get back to\nyou later.\n\n> -- \n> 2.37.1.1233.ge8b09efaedc\n"},{"id":"461719","messageId":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v5-00.17-00000000000-20220802T155002Z-avarab@gmail.com","subject":"[PATCH v6 00/17] submodule--helper: fix memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:24Z","receivedAt":"2022-08-21T14:02:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This series fixes all of the memory leaks in\nbuiltin/submodule--helper.c that our test suite spots, except for\nthose where the leak is downstream of cmd_submodule__helper() and\ncaused by code that's not in builtin/submodule--helper.c (e.g. leaks\nin the object or config APIs, and in submodule.c).\n\nThis re-roll is on top of the just-now re-rolled v3 of the base\ntopic[1].\n\nChanges in v6:\n\n * Rebase on top of the base topic.\n * Mark new tests as leak-free within this series (due to changes on\n   \"master\")\n * Various minor changes, see range-diff below.\n\n1. https://lore.kernel.org/git/cover-v3-00.32-00000000000-20220821T130231Z-avarab@gmail.com/\n\nÆvar Arnfjörð Bjarmason (17):\n  submodule--helper: fix a leak in \"clone_submodule\"\n  submodule--helper: fix trivial get_default_remote_submodule() leak\n  submodule--helper: fix most \"struct pathspec\" memory leaks\n  submodule--helper: \"struct pathspec\" memory leak in module_update()\n  submodule--helper: don't leak {run,capture}_command() cp.dir argument\n  submodule--helper: add and use *_release() functions\n  submodule--helper: fix \"errmsg_str\" memory leak\n  submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks\n  submodule--helper: fix a leak with repo_clear()\n  submodule--helper: fix a memory leak in get_default_remote_submodule()\n  submodule--helper: fix \"reference\" leak\n  submodule--helper: fix obscure leak in module_add()\n  submodule--helper: fix a leak in module_add()\n  submodule--helper: fix a memory leak in print_status()\n  submodule--helper: free some \"displaypath\" in \"struct update_data\"\n  submodule--helper: free rest of \"displaypath\" in \"struct update_data\"\n  submodule--helper: fix a configure_added_submodule() leak\n\n builtin/submodule--helper.c        | 239 +++++++++++++++++++++--------\n t/t1500-rev-parse.sh               |   1 +\n t/t2403-worktree-move.sh           |   1 +\n t/t6008-rev-list-submodule.sh      |   1 +\n t/t6134-pathspec-in-submodule.sh   |   1 +\n t/t7401-submodule-summary.sh       |   1 +\n t/t7412-submodule-absorbgitdirs.sh |   1 +\n t/t7413-submodule-is-active.sh     |   1 +\n t/t7414-submodule-mistakes.sh      |   2 +\n t/t7419-submodule-set-branch.sh    |   1 +\n t/t7506-status-submodule.sh        |   1 +\n t/t7507-commit-verbose.sh          |   2 +\n 12 files changed, 187 insertions(+), 65 deletions(-)\n\nRange-diff against v5:\n 1:  118e965d401 =  1:  0f60ea6f7c6 submodule--helper: fix a leak in \"clone_submodule\"\n 2:  d885e1dd59a !  2:  c14e00f39cb submodule--helper: fix trivial get_default_remote_submodule() leak\n    @@ Commit message\n     \n      ## builtin/submodule--helper.c ##\n     @@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)\n    - \t\tconst char *branch = remote_submodule_branch(update_data->sm_path);\n    - \t\tchar *remote_ref = xstrfmt(\"refs/remotes/%s/%s\", remote_name, branch);\n    + \t\t\treturn code;\n    + \t\tremote_ref = xstrfmt(\"refs/remotes/%s/%s\", remote_name, branch);\n      \n     +\t\tfree(remote_name);\n     +\n 3:  1f90348d61f !  3:  a2ecdb301d3 submodule--helper: fix most \"struct pathspec\" memory leaks\n    @@ Commit message\n         Call clear_pathspec() at the end of various functions that work with\n         and allocate a \"struct pathspec\".\n     \n    +    In some cases the zero-initialization here isn't strictly needed, but\n    +    as we're moving to a \"goto cleanup\" pattern let's make sure that it's\n    +    safe to call clear_pathspec(), we don't want the data to be\n    +    uninitialized.\n    +\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/submodule--helper.c ##\n 4:  f768ff2e34d =  4:  8fc3dad316e submodule--helper: \"struct pathspec\" memory leak in module_update()\n 5:  509133c37fa !  5:  a3886d36366 submodule--helper: don't leak {run,capture}_command() cp.dir argument\n    @@ builtin/submodule--helper.c: static int run_update_command(struct update_data *u\n     -\tcp.dir = xstrdup(ud->sm_path);\n     +\tcp.dir = ud->sm_path;\n      \tprepare_submodule_repo_env(&cp.env);\n    - \tif (run_command(&cp)) {\n    - \t\tint ret;\n    + \tif ((ret = run_command(&cp))) {\n    + \t\tswitch (ud->update_strategy.type) {\n 6:  25377f1d06c =  6:  173dbb7d8fc submodule--helper: add and use *_release() functions\n 7:  1a85057cd0c =  7:  cd394814370 submodule--helper: fix \"errmsg_str\" memory leak\n 8:  3c4f734e958 !  8:  2110b991913 submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks\n    @@ Commit message\n         submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks\n     \n         Fix leaks in \"struct module_cb_list\" and the \"struct module_cb\" which\n    -    it contains, these fixes leaks in e83e3333b57 (submodule: port\n    -    submodule subcommand 'summary' from shell to C, 2020-08-13).\n    +    it contains, these fix leaks in e83e3333b57 (submodule: port submodule\n    +    subcommand 'summary' from shell to C, 2020-08-13).\n     \n         The \"sm_path\" should always have been a \"char *\", not a \"const\n         char *\", we always create it with xstrdup().\n    @@ builtin/submodule--helper.c: static int compute_summary_module_list(struct objec\n      \treturn ret;\n      }\n      \n    +\n    + ## t/t7401-submodule-summary.sh ##\n    +@@ t/t7401-submodule-summary.sh: This test script tries to verify the sanity of summary subcommand of git submodu\n    + # various reasons, one of them being that there are lots of commands taking place\n    + # outside of 'test_expect_success' block, which is no longer in good-style.\n    + \n    ++TEST_PASSES_SANITIZE_LEAK=true\n    + . ./test-lib.sh\n    + \n    + add_file () {\n 9:  3aebff9f8e3 =  9:  a0d1a03166f submodule--helper: fix a leak with repo_clear()\n10:  38345ec76bc ! 10:  84f162f320b submodule--helper: fix a memory leak in get_default_remote_submodule()\n    @@ Commit message\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/submodule--helper.c ##\n    -@@ builtin/submodule--helper.c: static char *repo_get_default_remote(struct repository *repo)\n    - static char *get_default_remote_submodule(const char *module_path)\n    +@@ builtin/submodule--helper.c: static int repo_get_default_remote(struct repository *repo, char **default_remot\n    + static int get_default_remote_submodule(const char *module_path, char **default_remote)\n      {\n      \tstruct repository subrepo;\n    -+\tchar *ret;\n    ++\tint ret;\n      \n    - \trepo_submodule_init(&subrepo, the_repository, module_path, null_oid());\n    --\treturn repo_get_default_remote(&subrepo);\n    -+\tret = repo_get_default_remote(&subrepo);\n    + \tif (repo_submodule_init(&subrepo, the_repository, module_path,\n    + \t\t\t\tnull_oid()) < 0)\n    + \t\treturn die_message(_(\"could not get a repository handle for submodule '%s'\"),\n    + \t\t\t\t   module_path);\n    +-\treturn repo_get_default_remote(&subrepo, default_remote);\n    ++\tret = repo_get_default_remote(&subrepo, default_remote);\n     +\trepo_clear(&subrepo);\n     +\n     +\treturn ret;\n11:  4b1e5ced969 = 11:  dc5e8504354 submodule--helper: fix \"reference\" leak\n12:  e74c396073b = 12:  50d58bfb19b submodule--helper: fix obscure leak in module_add()\n13:  71a56c59864 = 13:  56590508180 submodule--helper: fix a leak in module_add()\n14:  2429db2f1c7 = 14:  38ceb0a36ab submodule--helper: fix a memory leak in print_status()\n15:  613d077c4ec ! 15:  f3cf49dd029 submodule--helper: free some \"displaypath\" in \"struct update_data\"\n    @@ t/t7412-submodule-absorbgitdirs.sh: This test verifies that `git submodue absorb\n      . ./test-lib.sh\n      \n      test_expect_success 'setup a real submodule' '\n    +\n    + ## t/t7419-submodule-set-branch.sh ##\n    +@@ t/t7419-submodule-set-branch.sh: This test verifies that the set-branch subcommand of git-submodule is working\n    + as expected.\n    + '\n    + \n    ++TEST_PASSES_SANITIZE_LEAK=true\n    + TEST_NO_CREATE_REPO=1\n    + . ./test-lib.sh\n    + \n16:  8f150a81507 ! 16:  a99a7736fab submodule--helper: free rest of \"displaypath\" in \"struct update_data\"\n    @@ builtin/submodule--helper.c: static int update_submodules(struct update_data *up\n      \n     -\t\tcode = update_submodule(update_data);\n     +\t\tcode = update_submodule_outer(update_data);\n    - \t\tif (code)\n    - \t\t\tret = code;\n    - \t\tif (code == 128)\n    + \t\tif (!code)\n    + \t\t\tcontinue;\n    + \t\tret = code;\n17:  17c77ceba01 = 17:  ec0b02d8ee2 submodule--helper: fix a configure_added_submodule() leak\n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461720","messageId":"patch-v6-01.17-0f60ea6f7c6-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v6 01/17] submodule--helper: fix a leak in \"clone_submodule\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:25Z","receivedAt":"2022-08-21T14:02:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak of the \"clone_data_path\" variable that we copy or\nderive from the \"struct module_clone_data\" in clone_submodule(). This\ncode was refactored in preceding commits, but the leak has been with\nus since f8eaa0ba98b (submodule--helper, module_clone: always operate\non absolute paths, 2016-03-31).\n\nFor the \"else\" case we don't need to xstrdup() the \"clone_data->path\",\nand we don't need to free our own \"clone_data_path\". We can therefore\nassign the \"clone_data->path\" to our own \"clone_data_path\" right away,\nand only override it (and remember to free it!) if we need to\nxstrfmt() a replacement.\n\nIn the case of the module_clone() caller it's from \"argv\", and doesn't\nneed to be free'd, and in the case of the add_submodule() caller we\nget a pointer to \"sm_path\", which doesn't need to be directly free'd\neither.\n\nFixing this leak makes several tests pass, so let's mark them as\npassing with TEST_PASSES_SANITIZE_LEAK=true.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c   | 10 +++++-----\n t/t1500-rev-parse.sh          |  1 +\n t/t6008-rev-list-submodule.sh |  1 +\n t/t7414-submodule-mistakes.sh |  2 ++\n t/t7506-status-submodule.sh   |  1 +\n t/t7507-commit-verbose.sh     |  2 ++\n 6 files changed, 12 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex e489a0f6b10..2774f1884b0 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1590,13 +1590,12 @@ static int clone_submodule(const struct module_clone_data *clone_data,\n \tchar *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);\n \tchar *sm_alternate = NULL, *error_strategy = NULL;\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n-\tconst char *clone_data_path;\n+\tconst char *clone_data_path = clone_data->path;\n+\tchar *to_free = NULL;\n \n \tif (!is_absolute_path(clone_data->path))\n-\t\tclone_data_path = xstrfmt(\"%s/%s\", get_git_work_tree(),\n-\t\t\t\t\t  clone_data->path);\n-\telse\n-\t\tclone_data_path = xstrdup(clone_data->path);\n+\t\tclone_data_path = to_free = xstrfmt(\"%s/%s\", get_git_work_tree(),\n+\t\t\t\t\t\t    clone_data->path);\n \n \tif (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)\n \t\tdie(_(\"refusing to create/use '%s' in another submodule's \"\n@@ -1681,6 +1680,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,\n \n \tfree(sm_gitdir);\n \tfree(p);\n+\tfree(to_free);\n \treturn 0;\n }\n \ndiff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh\nindex 1c2df08333b..0e13bcb4ebb 100755\n--- a/t/t1500-rev-parse.sh\n+++ b/t/t1500-rev-parse.sh\n@@ -4,6 +4,7 @@ test_description='test git rev-parse'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_one () {\ndiff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh\nindex 3153a0d8910..12e67e187ef 100755\n--- a/t/t6008-rev-list-submodule.sh\n+++ b/t/t6008-rev-list-submodule.sh\n@@ -8,6 +8,7 @@ test_description='git rev-list involving submodules that this repo has'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh\nindex f2e7df59cf2..3269298197c 100755\n--- a/t/t7414-submodule-mistakes.sh\n+++ b/t/t7414-submodule-mistakes.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='handling of common mistakes people may make with submodules'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'create embedded repository' '\ndiff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh\nindex 3fcb44767f5..f5426a8e589 100755\n--- a/t/t7506-status-submodule.sh\n+++ b/t/t7506-status-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='git status for submodule'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_create_repo_with_commit () {\ndiff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh\nindex ed2653d46fe..92462a22374 100755\n--- a/t/t7507-commit-verbose.sh\n+++ b/t/t7507-commit-verbose.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='verbose commit template'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n write_script \"check-for-diff\" <<\\EOF &&\n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461721","messageId":"patch-v6-02.17-c14e00f39cb-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v6 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:26Z","receivedAt":"2022-08-21T14:02:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in code added in 1012a5cbc3f (submodule--helper\nrun-update-procedure: learn --remote, 2022-03-04), we need to free()\nthe xstrdup()'d string. This gets e.g. t/t7419-submodule-set-branch.sh\ncloser to passing under SANITIZE=leak.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 2774f1884b0..da9e0016509 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2449,6 +2449,8 @@ static int update_submodule(struct update_data *update_data)\n \t\t\treturn code;\n \t\tremote_ref = xstrfmt(\"refs/remotes/%s/%s\", remote_name, branch);\n \n+\t\tfree(remote_name);\n+\n \t\tif (!update_data->nofetch) {\n \t\t\tif (fetch_in_submodule(update_data->sm_path, update_data->depth,\n \t\t\t\t\t      0, NULL))\n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461722","messageId":"patch-v6-03.17-a2ecdb301d3-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v6 03/17] submodule--helper: fix most \"struct pathspec\" memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:27Z","receivedAt":"2022-08-21T14:02:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call clear_pathspec() at the end of various functions that work with\nand allocate a \"struct pathspec\".\n\nIn some cases the zero-initialization here isn't strictly needed, but\nas we're moving to a \"goto cleanup\" pattern let's make sure that it's\nsafe to call clear_pathspec(), we don't want the data to be\nuninitialized.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 74 +++++++++++++++++++++++++------------\n 1 file changed, 51 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex da9e0016509..a118f615783 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -379,7 +379,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,\n static int module_foreach(int argc, const char **argv, const char *prefix)\n {\n \tstruct foreach_cb info = FOREACH_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tstruct option module_foreach_options[] = {\n \t\tOPT__QUIET(&info.quiet, N_(\"suppress output of entering each submodule command\")),\n@@ -391,12 +391,13 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule foreach [--quiet] [--recursive] [--] <command>\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_foreach_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.argc = argc;\n \tinfo.argv = argv;\n@@ -404,7 +405,10 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static int starts_with_dot_slash(const char *const path)\n@@ -515,7 +519,7 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data\n static int module_init(int argc, const char **argv, const char *prefix)\n {\n \tstruct init_cb info = INIT_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tstruct option module_init_options[] = {\n@@ -526,12 +530,13 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule init [<options>] [<path>]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_init_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \t/*\n \t * If there are no path args and submodule.active is set then,\n@@ -546,7 +551,10 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, init_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct status_cb {\n@@ -693,7 +701,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,\n static int module_status(int argc, const char **argv, const char *prefix)\n {\n \tstruct status_cb info = STATUS_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tstruct option module_status_options[] = {\n@@ -706,12 +714,13 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule status [--quiet] [--cached] [--recursive] [<path>...]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_status_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -719,7 +728,10 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, status_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct module_cb {\n@@ -1261,7 +1273,7 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data\n static int module_sync(int argc, const char **argv, const char *prefix)\n {\n \tstruct sync_cb info = SYNC_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tint recursive = 0;\n@@ -1275,12 +1287,13 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule sync [--quiet] [--recursive] [<path>]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_sync_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -1290,7 +1303,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, sync_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct deinit_cb {\n@@ -1399,7 +1415,7 @@ static void deinit_submodule_cb(const struct cache_entry *list_item,\n static int module_deinit(int argc, const char **argv, const char *prefix)\n {\n \tstruct deinit_cb info = DEINIT_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tint force = 0;\n@@ -1414,6 +1430,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_deinit_options,\n \t\t\t     git_submodule_helper_usage, 0);\n@@ -1428,7 +1445,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"Use '--all' if you really want to deinitialize all submodules\"));\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -1438,7 +1455,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, deinit_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct module_clone_data {\n@@ -2541,7 +2561,7 @@ static int update_submodules(struct update_data *update_data)\n \n static int module_update(int argc, const char **argv, const char *prefix)\n {\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct update_data opt = UPDATE_DATA_INIT;\n \tstruct list_objects_filter_options filter_options = { 0 };\n \tint ret;\n@@ -2618,8 +2638,8 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\topt.update_strategy.type = opt.update_default;\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {\n-\t\tlist_objects_filter_release(&filter_options);\n-\t\treturn 1;\n+\t\tret = 1;\n+\t\tgoto cleanup;\n \t}\n \n \tif (pathspec.nr)\n@@ -2630,8 +2650,10 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\tstruct init_cb info = INIT_CB_INIT;\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n-\t\t\t\t\t&pathspec, &list) < 0)\n-\t\t\treturn 1;\n+\t\t\t\t\t&pathspec, &list) < 0) {\n+\t\t\tret = 1;\n+\t\t\tgoto cleanup;\n+\t\t}\n \n \t\t/*\n \t\t * If there are no path args and submodule.active is set then,\n@@ -2648,7 +2670,9 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t}\n \n \tret = update_submodules(&opt);\n+cleanup:\n \tlist_objects_filter_release(&filter_options);\n+\tclear_pathspec(&pathspec);\n \treturn ret;\n }\n \n@@ -2732,7 +2756,7 @@ static int push_check(int argc, const char **argv, const char *prefix)\n static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tunsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;\n \tstruct option embed_gitdir_options[] = {\n@@ -2747,17 +2771,21 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule absorbgitdirs [<options>] [<path>...]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, embed_gitdir_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tfor (i = 0; i < list.nr; i++)\n \t\tabsorb_git_dir_into_superproject(list.entries[i]->name, flags);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static int module_config(int argc, const char **argv, const char *prefix)\n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461723","messageId":"patch-v6-04.17-8fc3dad316e-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v6 04/17] submodule--helper: \"struct pathspec\" memory leak in module_update()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:28Z","receivedAt":"2022-08-21T14:02:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The module_update() function calls module_list_compute() twice, which\nin turn will reset the \"struct pathspec\" passed to it. Let's instead\ntrack two of them, and clear them both.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex a118f615783..64dc906b5e6 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2562,6 +2562,7 @@ static int update_submodules(struct update_data *update_data)\n static int module_update(int argc, const char **argv, const char *prefix)\n {\n \tstruct pathspec pathspec = { 0 };\n+\tstruct pathspec pathspec2 = { 0 };\n \tstruct update_data opt = UPDATE_DATA_INIT;\n \tstruct list_objects_filter_options filter_options = { 0 };\n \tint ret;\n@@ -2650,7 +2651,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\tstruct init_cb info = INIT_CB_INIT;\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n-\t\t\t\t\t&pathspec, &list) < 0) {\n+\t\t\t\t\t&pathspec2, &list) < 0) {\n \t\t\tret = 1;\n \t\t\tgoto cleanup;\n \t\t}\n@@ -2673,6 +2674,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n cleanup:\n \tlist_objects_filter_release(&filter_options);\n \tclear_pathspec(&pathspec);\n+\tclear_pathspec(&pathspec2);\n \treturn ret;\n }\n \n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461724","messageId":"patch-v6-06.17-173dbb7d8fc-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v6 06/17] submodule--helper: add and use *_release() functions","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:30Z","receivedAt":"2022-08-21T14:02:23Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add release functions for \"struct module_list\", \"struct\nsubmodule_update_clone\" and \"struct update_data\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c      | 28 +++++++++++++++++++++++++++-\n t/t6134-pathspec-in-submodule.sh |  1 +\n 2 files changed, 28 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 5eb6bec4c63..fa320eb2edf 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -172,6 +172,11 @@ struct module_list {\n };\n #define MODULE_LIST_INIT { 0 }\n \n+static void module_list_release(struct module_list *ml)\n+{\n+\tfree(ml->entries);\n+}\n+\n static int module_list_compute(int argc, const char **argv,\n \t\t\t       const char *prefix,\n \t\t\t       struct pathspec *pathspec,\n@@ -234,7 +239,7 @@ static void module_list_active(struct module_list *list)\n \t\tactive_modules.entries[active_modules.nr++] = ce;\n \t}\n \n-\tfree(list->entries);\n+\tmodule_list_release(list);\n \t*list = active_modules;\n }\n \n@@ -407,6 +412,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -553,6 +559,7 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -730,6 +737,7 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1305,6 +1313,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1457,6 +1466,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1834,6 +1844,12 @@ struct submodule_update_clone {\n };\n #define SUBMODULE_UPDATE_CLONE_INIT { 0 }\n \n+static void submodule_update_clone_release(struct submodule_update_clone *suc)\n+{\n+\tfree(suc->update_clone);\n+\tfree(suc->failed_clones);\n+}\n+\n struct update_data {\n \tconst char *prefix;\n \tconst char *displaypath;\n@@ -1872,6 +1888,11 @@ struct update_data {\n \t.max_jobs = 1, \\\n }\n \n+static void update_data_release(struct update_data *ud)\n+{\n+\tmodule_list_release(&ud->list);\n+}\n+\n static void next_submodule_warn_missing(struct submodule_update_clone *suc,\n \t\tstruct strbuf *out, const char *displaypath)\n {\n@@ -2555,6 +2576,7 @@ static int update_submodules(struct update_data *update_data)\n \t}\n \n cleanup:\n+\tsubmodule_update_clone_release(&suc);\n \tstring_list_clear(&update_data->references, 0);\n \treturn ret;\n }\n@@ -2652,6 +2674,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n \t\t\t\t\t&pathspec2, &list) < 0) {\n+\t\t\tmodule_list_release(&list);\n \t\t\tret = 1;\n \t\t\tgoto cleanup;\n \t\t}\n@@ -2668,10 +2691,12 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\t\tinfo.flags |= OPT_QUIET;\n \n \t\tfor_each_listed_submodule(&list, init_submodule_cb, &info);\n+\t\tmodule_list_release(&list);\n \t}\n \n \tret = update_submodules(&opt);\n cleanup:\n+\tupdate_data_release(&opt);\n \tlist_objects_filter_release(&filter_options);\n \tclear_pathspec(&pathspec);\n \tclear_pathspec(&pathspec2);\n@@ -2787,6 +2812,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n \tret = 0;\n cleanup:\n \tclear_pathspec(&pathspec);\n+\tmodule_list_release(&list);\n \treturn ret;\n }\n \ndiff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh\nindex 0f1cb49cedc..3a241f259de 100755\n--- a/t/t6134-pathspec-in-submodule.sh\n+++ b/t/t6134-pathspec-in-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='test case exclude pathspec'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a submodule' '\n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461725","messageId":"patch-v6-05.17-a3886d36366-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v6 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:29Z","receivedAt":"2022-08-21T14:02:23Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak in c51f8f94e5b (submodule--helper: run update\nprocedures from C, 2021-08-24) and 3c3558f0953 (submodule--helper: run\nupdate using child process struct, 2022-03-15) by not allocating\nmemory in the first place.\n\nThe \"dir\" member of \"struct child_process\" will not be modified by\nthat API, and it's declared to be \"const char *\". So let's not\nneedlessly duplicate these strings.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 64dc906b5e6..5eb6bec4c63 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2129,7 +2129,7 @@ static int is_tip_reachable(const char *path, struct object_id *oid)\n \tchar *hex = oid_to_hex(oid);\n \n \tcp.git_cmd = 1;\n-\tcp.dir = xstrdup(path);\n+\tcp.dir = path;\n \tcp.no_stderr = 1;\n \tstrvec_pushl(&cp.args, \"rev-list\", \"-n\", \"1\", hex, \"--not\", \"--all\", NULL);\n \n@@ -2147,7 +2147,7 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str\n \n \tprepare_submodule_repo_env(&cp.env);\n \tcp.git_cmd = 1;\n-\tcp.dir = xstrdup(module_path);\n+\tcp.dir = module_path;\n \n \tstrvec_push(&cp.args, \"fetch\");\n \tif (quiet)\n@@ -2200,7 +2200,7 @@ static int run_update_command(struct update_data *ud, int subforce)\n \t}\n \tstrvec_push(&cp.args, oid);\n \n-\tcp.dir = xstrdup(ud->sm_path);\n+\tcp.dir = ud->sm_path;\n \tprepare_submodule_repo_env(&cp.env);\n \tif ((ret = run_command(&cp))) {\n \t\tswitch (ud->update_strategy.type) {\n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461726","messageId":"patch-v6-07.17-cd394814370-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v6 07/17] submodule--helper: fix \"errmsg_str\" memory leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:31Z","receivedAt":"2022-08-21T14:02:23Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak introduced in e83e3333b57 (submodule: port submodule\nsubcommand 'summary' from shell to C, 2020-08-13), we sometimes append\nto the \"errmsg\", and need to free the \"struct strbuf\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex fa320eb2edf..42ec6127024 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -973,6 +973,7 @@ static void generate_submodule_summary(struct summary_cb *info,\n \tfree(displaypath);\n \tfree(src_abbrev);\n \tfree(dst_abbrev);\n+\tstrbuf_release(&errmsg);\n }\n \n static void prepare_submodule_summary(struct summary_cb *info,\n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461727","messageId":"patch-v6-08.17-2110b991913-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v6 08/17] submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:32Z","receivedAt":"2022-08-21T14:02:26Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix leaks in \"struct module_cb_list\" and the \"struct module_cb\" which\nit contains, these fix leaks in e83e3333b57 (submodule: port submodule\nsubcommand 'summary' from shell to C, 2020-08-13).\n\nThe \"sm_path\" should always have been a \"char *\", not a \"const\nchar *\", we always create it with xstrdup().\n\nWe can't mark any tests passing passing with SANITIZE=leak using\n\"TEST_PASSES_SANITIZE_LEAK=true\" as a result of this change, but\n\"t7401-submodule-summary.sh\" gets closer to passing as a result of\nthis change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c  | 21 ++++++++++++++++++++-\n t/t7401-submodule-summary.sh |  1 +\n 2 files changed, 21 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 42ec6127024..ea925178946 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -748,16 +748,34 @@ struct module_cb {\n \tstruct object_id oid_src;\n \tstruct object_id oid_dst;\n \tchar status;\n-\tconst char *sm_path;\n+\tchar *sm_path;\n };\n #define MODULE_CB_INIT { 0 }\n \n+static void module_cb_release(struct module_cb *mcb)\n+{\n+\tfree(mcb->sm_path);\n+}\n+\n struct module_cb_list {\n \tstruct module_cb **entries;\n \tint alloc, nr;\n };\n #define MODULE_CB_LIST_INIT { 0 }\n \n+static void module_cb_list_release(struct module_cb_list *mcbl)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < mcbl->nr; i++) {\n+\t\tstruct module_cb *mcb = mcbl->entries[i];\n+\n+\t\tmodule_cb_release(mcb);\n+\t\tfree(mcb);\n+\t}\n+\tfree(mcbl->entries);\n+}\n+\n struct summary_cb {\n \tint argc;\n \tconst char **argv;\n@@ -1104,6 +1122,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n cleanup:\n \tstrvec_clear(&diff_args);\n \trelease_revisions(&rev);\n+\tmodule_cb_list_release(&list);\n \treturn ret;\n }\n \ndiff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh\nindex 9c3cc4cf404..542b3331a78 100755\n--- a/t/t7401-submodule-summary.sh\n+++ b/t/t7401-submodule-summary.sh\n@@ -17,6 +17,7 @@ This test script tries to verify the sanity of summary subcommand of git submodu\n # various reasons, one of them being that there are lots of commands taking place\n # outside of 'test_expect_success' block, which is no longer in good-style.\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n add_file () {\n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461728","messageId":"patch-v6-09.17-a0d1a03166f-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v6 09/17] submodule--helper: fix a leak with repo_clear()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:33Z","receivedAt":"2022-08-21T14:02:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call repo_clear() in ensure_core_worktree() to free the \"struct\nrepository\". Fixes a leak that's been here since\n74d4731da1f (submodule--helper: replace connect-gitdir-workingtree by\nensure-core-worktree, 2018-08-13).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex ea925178946..b029e109b47 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2399,6 +2399,7 @@ static int ensure_core_worktree(const char *path)\n \t\tstrbuf_release(&sb);\n \t}\n \n+\trepo_clear(&subrepo);\n \treturn 0;\n }\n \n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461729","messageId":"patch-v6-10.17-84f162f320b-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v6 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:34Z","receivedAt":"2022-08-21T14:02:45Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak in the get_default_remote_submodule() function added\nin a77c3fcb5ec (submodule--helper: get remote names from any\nrepository, 2022-03-04), we need to repo_clear() the submodule we\ninitialize.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex b029e109b47..3f123f014f9 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -65,12 +65,16 @@ static int repo_get_default_remote(struct repository *repo, char **default_remot\n static int get_default_remote_submodule(const char *module_path, char **default_remote)\n {\n \tstruct repository subrepo;\n+\tint ret;\n \n \tif (repo_submodule_init(&subrepo, the_repository, module_path,\n \t\t\t\tnull_oid()) < 0)\n \t\treturn die_message(_(\"could not get a repository handle for submodule '%s'\"),\n \t\t\t\t   module_path);\n-\treturn repo_get_default_remote(&subrepo, default_remote);\n+\tret = repo_get_default_remote(&subrepo, default_remote);\n+\trepo_clear(&subrepo);\n+\n+\treturn ret;\n }\n \n static char *get_default_remote(void)\n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461730","messageId":"patch-v6-11.17-dc5e8504354-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v6 11/17] submodule--helper: fix \"reference\" leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:35Z","receivedAt":"2022-08-21T14:02:46Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix leaks in the \"reference\" variable declared in add_submodule() and\nmodule_clone().\n\nIn preceding commits this variable was refactored out of the \"struct\nmodule_clone_data\", but the leak has been with us since\n31224cbdc72 (clone: recursive and reference option triggers submodule\nalternates, 2016-08-17) and 8c8195e9c3e (submodule--helper: introduce\nadd-clone subcommand, 2021-07-10).\n\nThose commits added an xstrdup()'d member of the\nSTRING_LIST_INIT_NODUP'd \"struct string_list\". We need to free()\nthose, but not the ones we get from argv, let's make use of the \"util\"\nmember, if it has a pointer it's the pointer we'll need to free,\notherwise it'll be NULL (i.e. from argv).\n\nNote that the free() of the \"util\" member is needed in both\nmodule_clone() and add_submodule(). The module_clone() function itself\ndoesn't populate the \"util\" pointer as add_submodule() does, but\nmodule_clone() is upstream of the\nadd_possible_reference_from_superproject() caller we're modifying\nhere, which does do that.\n\nThis does preclude the use of the \"util\" pointer for any other reasons\nfor now, but that's OK. If we ever need to use it for something else\nwe could turn it into a small \"struct\" with an optional \"to_free\"\nmember, and switch to using string_list_clear_func().\n\nAlternatively we could have another \"struct string_list to_free\" which\nwould keep a copy of the strings we've dup'd to free(). But for now\nthis is perfectly adequate.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 21 +++++++++++++++------\n 1 file changed, 15 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 3f123f014f9..d469c571d52 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1566,7 +1566,9 @@ static int add_possible_reference_from_superproject(\n \n \t\tsm_alternate = compute_alternate_path(sb.buf, &err);\n \t\tif (sm_alternate) {\n-\t\t\tstring_list_append(sas->reference, xstrdup(sb.buf));\n+\t\t\tchar *p = strbuf_detach(&sb, NULL);\n+\n+\t\t\tstring_list_append(sas->reference, p)->util = p;\n \t\t\tfree(sm_alternate);\n \t\t} else {\n \t\t\tswitch (sas->error_mode) {\n@@ -1798,6 +1800,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \n \tclone_submodule(&clone_data, &reference);\n \tlist_objects_filter_release(&filter_options);\n+\tstring_list_clear(&reference, 1);\n \treturn 0;\n }\n \n@@ -3043,6 +3046,7 @@ static int add_submodule(const struct add_data *add_data)\n \tchar *submod_gitdir_path;\n \tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n \tstruct string_list reference = STRING_LIST_INIT_NODUP;\n+\tint ret = -1;\n \n \t/* perhaps the path already exists and is already a git repo, else clone it */\n \tif (is_directory(add_data->sm_path)) {\n@@ -3098,15 +3102,17 @@ static int add_submodule(const struct add_data *add_data)\n \t\tclone_data.url = add_data->realrepo;\n \t\tclone_data.quiet = add_data->quiet;\n \t\tclone_data.progress = add_data->progress;\n-\t\tif (add_data->reference_path)\n-\t\t\tstring_list_append(&reference,\n-\t\t\t\t\t   xstrdup(add_data->reference_path));\n+\t\tif (add_data->reference_path) {\n+\t\t\tchar *p = xstrdup(add_data->reference_path);\n+\n+\t\t\tstring_list_append(&reference, p)->util = p;\n+\t\t}\n \t\tclone_data.dissociate = add_data->dissociate;\n \t\tif (add_data->depth >= 0)\n \t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n \n \t\tif (clone_submodule(&clone_data, &reference))\n-\t\t\treturn -1;\n+\t\t\tgoto cleanup;\n \n \t\tprepare_submodule_repo_env(&cp.env);\n \t\tcp.git_cmd = 1;\n@@ -3125,7 +3131,10 @@ static int add_submodule(const struct add_data *add_data)\n \t\tif (run_command(&cp))\n \t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n \t}\n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tstring_list_clear(&reference, 1);\n+\treturn ret;\n }\n \n static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)\n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461731","messageId":"patch-v6-13.17-56590508180-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v6 13/17] submodule--helper: fix a leak in module_add()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:37Z","receivedAt":"2022-08-21T14:02:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in module_path(), since a6226fd772b (submodule--helper:\nconvert the bulk of cmd_add() to C, 2021-08-10), we've been freeing\nadd_data.sm_path, but in this case we clobbered it, and didn't free\nthe value we clobbered.\n\nThis makes test 28 of \"t/t7400-submodule-basic.sh\" (\"submodule add in\nsubdirectory\") pass when we're compiled with SANITIZE=leak..\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d952b9c8616..8d55090ee2e 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3315,8 +3315,12 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \telse\n \t\tadd_data.sm_path = xstrdup(argv[1]);\n \n-\tif (prefix && *prefix && !is_absolute_path(add_data.sm_path))\n-\t\tadd_data.sm_path = xstrfmt(\"%s%s\", prefix, add_data.sm_path);\n+\tif (prefix && *prefix && !is_absolute_path(add_data.sm_path)) {\n+\t\tchar *sm_path = add_data.sm_path;\n+\n+\t\tadd_data.sm_path = xstrfmt(\"%s%s\", prefix, sm_path);\n+\t\tfree(sm_path);\n+\t}\n \n \tif (starts_with_dot_dot_slash(add_data.repo) ||\n \t    starts_with_dot_slash(add_data.repo)) {\n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461732","messageId":"patch-v6-12.17-50d58bfb19b-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v6 12/17] submodule--helper: fix obscure leak in module_add()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:36Z","receivedAt":"2022-08-21T14:02:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix an obscure leak in module_add(), if the \"git add\" command we were\npiping to failed we'd fail to strbuf_release(&sb). This fixes a leak\nintroduced in a6226fd772b (submodule--helper: convert the bulk of\ncmd_add() to C, 2021-08-10).\n\nIn fixing it move to a \"goto cleanup\" pattern, and since we need to\nintroduce a \"ret\" variable to do that let's also get rid of the\nintermediate \"exit_code\" variable. The initialization to \"-1\" in\na6226fd772b has always been redundant, we'd only use the \"exit_code\"\nvalue after assigning the return value of pipe_command() to it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 22 +++++++++++-----------\n 1 file changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d469c571d52..d952b9c8616 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3294,6 +3294,8 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule add [<options>] [--] <repository> [<path>]\"),\n \t\tNULL\n \t};\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, options, usage, 0);\n \n@@ -3343,21 +3345,17 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \tdie_on_repo_without_commits(add_data.sm_path);\n \n \tif (!force) {\n-\t\tint exit_code = -1;\n-\t\tstruct strbuf sb = STRBUF_INIT;\n \t\tstruct child_process cp = CHILD_PROCESS_INIT;\n \n \t\tcp.git_cmd = 1;\n \t\tcp.no_stdout = 1;\n \t\tstrvec_pushl(&cp.args, \"add\", \"--dry-run\", \"--ignore-missing\",\n \t\t\t     \"--no-warn-embedded-repo\", add_data.sm_path, NULL);\n-\t\tif ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {\n+\t\tif ((ret = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {\n \t\t\tstrbuf_complete_line(&sb);\n \t\t\tfputs(sb.buf, stderr);\n-\t\t\tfree(add_data.sm_path);\n-\t\t\treturn exit_code;\n+\t\t\tgoto cleanup;\n \t\t}\n-\t\tstrbuf_release(&sb);\n \t}\n \n \tif(!add_data.sm_name)\n@@ -3372,15 +3370,17 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \tadd_data.progress = !!progress;\n \tadd_data.dissociate = !!dissociate;\n \n-\tif (add_submodule(&add_data)) {\n-\t\tfree(add_data.sm_path);\n-\t\treturn 1;\n-\t}\n+\tif (add_submodule(&add_data))\n+\t\tgoto cleanup;\n \tconfigure_added_submodule(&add_data);\n+\n+\tret = 0;\n+cleanup:\n \tfree(add_data.sm_path);\n \tfree(to_free);\n+\tstrbuf_release(&sb);\n \n-\treturn 0;\n+\treturn ret;\n }\n \n #define SUPPORT_SUPER_PREFIX (1<<0)\n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461733","messageId":"patch-v6-14.17-38ceb0a36ab-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v6 14/17] submodule--helper: fix a memory leak in print_status()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:38Z","receivedAt":"2022-08-21T14:02:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in print_status(), the compute_rev_name() function\nimplemented in this file will return a strbuf_detach()'d value, or\nNULL.\n\nThis leak has existed since this code was added in\na9f8a37584a (submodule: port submodule subcommand 'status' from shell\nto C, 2017-10-06), but in 0b5e2ea7cf3 (submodule--helper: don't print\nnull in 'submodule status', 2018-04-18) we added a \"const\"\nintermediate variable for the return value, that \"const\" should be\nremoved.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 8d55090ee2e..e75d7fa2e10 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -583,10 +583,11 @@ static void print_status(unsigned int flags, char state, const char *path,\n \tprintf(\"%c%s %s\", state, oid_to_hex(oid), displaypath);\n \n \tif (state == ' ' || state == '+') {\n-\t\tconst char *name = compute_rev_name(path, oid_to_hex(oid));\n+\t\tchar *name = compute_rev_name(path, oid_to_hex(oid));\n \n \t\tif (name)\n \t\t\tprintf(\" (%s)\", name);\n+\t\tfree(name);\n \t}\n \n \tprintf(\"\\n\");\n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461734","messageId":"patch-v6-15.17-f3cf49dd029-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v6 15/17] submodule--helper: free some \"displaypath\" in \"struct update_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:39Z","receivedAt":"2022-08-21T14:02:54Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Make the update_data_release() function free \"displaypath\" member when\nappropriate. The \"displaypath\" member is always ours, the \"const\" on\nthe \"char *\" was wrong to begin with.\n\nThis leaves a leak of \"displaypath\" in update_submodule(), which as\nwe'll see in subsequent commits is harder to deal with than this\ntrivial fix.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c        | 3 ++-\n t/t2403-worktree-move.sh           | 1 +\n t/t7412-submodule-absorbgitdirs.sh | 1 +\n t/t7419-submodule-set-branch.sh    | 1 +\n 4 files changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex e75d7fa2e10..dbbe412b24e 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1880,7 +1880,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)\n \n struct update_data {\n \tconst char *prefix;\n-\tconst char *displaypath;\n+\tchar *displaypath;\n \tenum submodule_update_type update_default;\n \tstruct object_id suboid;\n \tstruct string_list references;\n@@ -1918,6 +1918,7 @@ struct update_data {\n \n static void update_data_release(struct update_data *ud)\n {\n+\tfree(ud->displaypath);\n \tmodule_list_release(&ud->list);\n }\n \ndiff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh\nindex a4e1a178e0a..1168e9f9982 100755\n--- a/t/t2403-worktree-move.sh\n+++ b/t/t2403-worktree-move.sh\n@@ -2,6 +2,7 @@\n \n test_description='test git worktree move, remove, lock and unlock'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh\nindex 1cfa150768d..2859695c6d2 100755\n--- a/t/t7412-submodule-absorbgitdirs.sh\n+++ b/t/t7412-submodule-absorbgitdirs.sh\n@@ -6,6 +6,7 @@ This test verifies that `git submodue absorbgitdirs` moves a submodules git\n directory into the superproject.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a real submodule' '\ndiff --git a/t/t7419-submodule-set-branch.sh b/t/t7419-submodule-set-branch.sh\nindex 3b925c302fc..96e98423214 100755\n--- a/t/t7419-submodule-set-branch.sh\n+++ b/t/t7419-submodule-set-branch.sh\n@@ -9,6 +9,7 @@ This test verifies that the set-branch subcommand of git-submodule is working\n as expected.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n TEST_NO_CREATE_REPO=1\n . ./test-lib.sh\n \n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461735","messageId":"patch-v6-16.17-a99a7736fab-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v6 16/17] submodule--helper: free rest of \"displaypath\" in \"struct update_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:40Z","receivedAt":"2022-08-21T14:02:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in code added in c51f8f94e5b (submodule--helper: run update\nprocedures from C, 2021-08-24), we clobber the \"displaypath\" member of\nthe passed-in \"struct update_data\" both so that die() messages in this\nupdate_submodule() function itself can use it, and for the\nrun_update_procedure() called within this function.\n\nTo make managing that clobbering easier let's wrap the\nupdate_submodule() in a new update_submodule_outer() function, which\nwill do the clobbering and free(to_free) dance for us.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 27 +++++++++++++++++++--------\n 1 file changed, 19 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex dbbe412b24e..070e435b13e 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2485,13 +2485,6 @@ static int update_submodule(struct update_data *update_data)\n {\n \tint ret;\n \n-\tret = ensure_core_worktree(update_data->sm_path);\n-\tif (ret)\n-\t\treturn ret;\n-\n-\tupdate_data->displaypath = get_submodule_displaypath(\n-\t\tupdate_data->sm_path, update_data->prefix);\n-\n \tret = determine_submodule_update_strategy(the_repository,\n \t\t\t\t\t\t  update_data->just_cloned,\n \t\t\t\t\t\t  update_data->sm_path,\n@@ -2565,6 +2558,24 @@ static int update_submodule(struct update_data *update_data)\n \treturn 0;\n }\n \n+static int update_submodule_outer(struct update_data *update_data)\n+{\n+\tchar *to_free, *restore = update_data->displaypath;\n+\tint ret;\n+\n+\tensure_core_worktree(update_data->sm_path);\n+\n+\tupdate_data->displaypath = to_free = get_submodule_displaypath(\n+\t\tupdate_data->sm_path, update_data->prefix);\n+\n+\tret = update_submodule(update_data);\n+\n+\tfree(to_free);\n+\tupdate_data->displaypath = restore;\n+\n+\treturn ret;\n+}\n+\n static int update_submodules(struct update_data *update_data)\n {\n \tint i, ret = 0;\n@@ -2597,7 +2608,7 @@ static int update_submodules(struct update_data *update_data)\n \t\tupdate_data->just_cloned = ucd.just_cloned;\n \t\tupdate_data->sm_path = ucd.sub->path;\n \n-\t\tcode = update_submodule(update_data);\n+\t\tcode = update_submodule_outer(update_data);\n \t\tif (!code)\n \t\t\tcontinue;\n \t\tret = code;\n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461736","messageId":"patch-v6-17.17-ec0b02d8ee2-20220821T130415Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v6 17/17] submodule--helper: fix a configure_added_submodule() leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-21T13:59:41Z","receivedAt":"2022-08-21T14:02:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix config API a memory leak added in a452128a36c (submodule--helper:\nintroduce add-config subcommand, 2021-08-06) by using the *_tmp()\nvariant of git_config_get_string().\n\nIn this case we're only checking whether\nthe (repo|git)_config_get_string() call is telling us that the\n\"submodule.active\" key exists.\n\nAs with the preceding commit we'll find many other such patterns in\nthe codebase if we go fishing. E.g. \"git gc\" leaks in the code added\nin 61f7a383d3b (maintenance: use 'incremental' strategy by default,\n2020-10-15). Similar code in \"git gc\" added in\nb08ff1fee00 (maintenance: add --schedule option and config,\n2020-09-11) doesn't leak, but we could avoid the malloc() & free() in\nthat case.\n\nA coccinelle rule to find those would find and fix some leaks, and\ncases where we're doing needless malloc() + free()'s but only care\nabout the key existence, or are copying\nthe (repo|git)_config_get_string() return value right away.\n\nBut as with the preceding commit let's punt on all of that for now,\nand just narrowly fix this specific case in submodule--helper.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c    | 4 ++--\n t/t7413-submodule-is-active.sh | 1 +\n 2 files changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 070e435b13e..3061943e70d 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3168,7 +3168,7 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con\n static void configure_added_submodule(struct add_data *add_data)\n {\n \tchar *key;\n-\tchar *val = NULL;\n+\tconst char *val;\n \tstruct child_process add_submod = CHILD_PROCESS_INIT;\n \tstruct child_process add_gitmodules = CHILD_PROCESS_INIT;\n \n@@ -3213,7 +3213,7 @@ static void configure_added_submodule(struct add_data *add_data)\n \t * is_submodule_active(), since that function needs to find\n \t * out the value of \"submodule.active\" again anyway.\n \t */\n-\tif (!git_config_get_string(\"submodule.active\", &val)) {\n+\tif (!git_config_get_string_tmp(\"submodule.active\", &val)) {\n \t\t/*\n \t\t * If the submodule being added isn't already covered by the\n \t\t * current configured pathspec, set the submodule's active flag\ndiff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh\nindex ede6f02dbd5..4dc7d089423 100755\n--- a/t/t7413-submodule-is-active.sh\n+++ b/t/t7413-submodule-is-active.sh\n@@ -9,6 +9,7 @@ This is a unit test of the submodule.c is_submodule_active() function,\n which is also indirectly tested elsewhere.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n-- \n2.37.2.1279.g64dec4e13cf\n\n"},{"id":"461927","messageId":"kl6l35dl452c.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-v6-16.17-a99a7736fab-20220821T130415Z-avarab@gmail.com","subject":"Re: [PATCH v6 16/17] submodule--helper: free rest of \"displaypath\" in \"struct update_data\"","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-08-24T23:03:55Z","receivedAt":"2022-08-24T23:04:08Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Fix a leak in code added in c51f8f94e5b (submodule--helper: run update\n> procedures from C, 2021-08-24), we clobber the \"displaypath\" member of\n> the passed-in \"struct update_data\" both so that die() messages in this\n> update_submodule() function itself can use it, and for the\n> run_update_procedure() called within this function.\n>\n> To make managing that clobbering easier let's wrap the\n> update_submodule() in a new update_submodule_outer() function, which\n> will do the clobbering and free(to_free) dance for us.\n\nThe only feedback I had on v5 was on this patch\n(https://lore.kernel.org/git/kl6l5yj7ubpt.fsf@chooglen-macbookpro.roam.corp.google.com)\nwhere I said that update_submodule_outer() seemed like overkill and that\nI'd test an alternative.\n\nI tested this approach\n\n  Assign and FREE_AND_NULL() update_data->displaypath [in\n  update_submodules(), but outside of update_submodule()] since this\n  is the only caller and it already does some prep work in this hunk.\n\nby reverting this patch and applying this one\n\n----- >8 --------- >8 --------- >8 --------- >8 --------- >8 ----\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 3557665261..c932c857dd 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2486,9 +2486,6 @@ static int update_submodule(struct update_data *update_data)\n \tif (ret)\n \t\treturn ret;\n \n-\tupdate_data->displaypath = get_submodule_displaypath(\n-\t\tupdate_data->sm_path, update_data->prefix);\n-\n \tret = determine_submodule_update_strategy(the_repository,\n \t\t\t\t\t\t  update_data->just_cloned,\n \t\t\t\t\t\t  update_data->sm_path,\n@@ -2593,8 +2590,11 @@ static int update_submodules(struct update_data *update_data)\n \t\toidcpy(&update_data->oid, &ucd.oid);\n \t\tupdate_data->just_cloned = ucd.just_cloned;\n \t\tupdate_data->sm_path = ucd.sub->path;\n+\t\tupdate_data->displaypath = get_submodule_displaypath(\n+\t\t\tupdate_data->sm_path, update_data->prefix);\n \n \t\tcode = update_submodule(update_data);\n+\t\tFREE_AND_NULL(update_data->displaypath);\n \t\tif (!code)\n \t\t\tcontinue;\n \t\tret = code;\n----- >8 --------- >8 --------- >8 --------- >8 --------- >8 ----\n\nThen I tested the update_submodule_outer() version and the\nFREE_AND_NULL() version by marking t7406-submodule-update.sh as\nTEST_PASSES_SANITIZE_LEAK=true and diffing the test output. I didn't see\nany meaningful difference in the test output, which should mean that\nboth versions fix all of the leaks in \"git submodule update\" that our\ntest suite can catch.\n\nFor good measure, I also tested a version of my code without the\nFREE_AND_NULL() call (albeit on v5, not v6), and I spotted the expected\nmemory leak:\n\n  Direct leak of 10 byte(s) in 1 object(s) allocated from:\n      #0 0x000000000000 in __interceptor_malloc ../../../../src/libsanitizer/lsan/lsan_interceptors.cpp:54\n      #1 0x000000000000 in strdup (/lib/x86_64-linux-gnu/libc.so.6+0x000000000000)\n      #2 0x000000000000 in xstrdup wrapper.c:39\n      #3 0x000000000000 in get_submodule_displaypath builtin/submodule--helper.c:119\n      #4 0x000000000000 in update_submodules builtin/submodule--helper.c:2585\n      #5 0x000000000000 in module_update builtin/submodule--helper.c:2716\n      #6 0x000000000000 in run_builtin git.c:466\n      #7 0x000000000000 in handle_builtin git.c:720\n      #8 0x000000000000 in run_argv git.c:787\n      #9 0x000000000000 in cmd_main git.c:920\n      #10 0x000000000000 in main common-main.c:56\n      #11 0x000000000000 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x000000000000)\n      #12 0x000000000000 in _start (git+0x000000000000)\n\nThe CI runs are at: \n- FREE_AND_NULL(): https://github.com/chooglen/git/actions/runs/2920894120\n- update_submodule_outer(): https://github.com/chooglen/git/actions/runs/2915661611\n- control study: https://github.com/chooglen/git/actions/runs/2915671776\n\nIf FREE_AND_NULL() really plugs all of the leaks, I would strongly\nprefer using that (possibly in a \"goto cleanup\"), over introducing\nupdate_submodule_outer().\n"},{"id":"462336","messageId":"patch-v7-01.17-40d4aef60fa-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"[PATCH v7 01/17] submodule--helper: fix a leak in \"clone_submodule\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:08Z","receivedAt":"2022-08-31T23:15:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak of the \"clone_data_path\" variable that we copy or\nderive from the \"struct module_clone_data\" in clone_submodule(). This\ncode was refactored in preceding commits, but the leak has been with\nus since f8eaa0ba98b (submodule--helper, module_clone: always operate\non absolute paths, 2016-03-31).\n\nFor the \"else\" case we don't need to xstrdup() the \"clone_data->path\",\nand we don't need to free our own \"clone_data_path\". We can therefore\nassign the \"clone_data->path\" to our own \"clone_data_path\" right away,\nand only override it (and remember to free it!) if we need to\nxstrfmt() a replacement.\n\nIn the case of the module_clone() caller it's from \"argv\", and doesn't\nneed to be free'd, and in the case of the add_submodule() caller we\nget a pointer to \"sm_path\", which doesn't need to be directly free'd\neither.\n\nFixing this leak makes several tests pass, so let's mark them as\npassing with TEST_PASSES_SANITIZE_LEAK=true.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c   | 10 +++++-----\n t/t1500-rev-parse.sh          |  1 +\n t/t6008-rev-list-submodule.sh |  1 +\n t/t7414-submodule-mistakes.sh |  2 ++\n t/t7506-status-submodule.sh   |  1 +\n t/t7507-commit-verbose.sh     |  2 ++\n 6 files changed, 12 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex c4149f11e7f..2e042c8a043 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1590,13 +1590,12 @@ static int clone_submodule(const struct module_clone_data *clone_data,\n \tchar *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);\n \tchar *sm_alternate = NULL, *error_strategy = NULL;\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n-\tconst char *clone_data_path;\n+\tconst char *clone_data_path = clone_data->path;\n+\tchar *to_free = NULL;\n \n \tif (!is_absolute_path(clone_data->path))\n-\t\tclone_data_path = xstrfmt(\"%s/%s\", get_git_work_tree(),\n-\t\t\t\t\t  clone_data->path);\n-\telse\n-\t\tclone_data_path = xstrdup(clone_data->path);\n+\t\tclone_data_path = to_free = xstrfmt(\"%s/%s\", get_git_work_tree(),\n+\t\t\t\t\t\t    clone_data->path);\n \n \tif (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)\n \t\tdie(_(\"refusing to create/use '%s' in another submodule's \"\n@@ -1681,6 +1680,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,\n \n \tfree(sm_gitdir);\n \tfree(p);\n+\tfree(to_free);\n \treturn 0;\n }\n \ndiff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh\nindex 1c2df08333b..0e13bcb4ebb 100755\n--- a/t/t1500-rev-parse.sh\n+++ b/t/t1500-rev-parse.sh\n@@ -4,6 +4,7 @@ test_description='test git rev-parse'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_one () {\ndiff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh\nindex 3153a0d8910..12e67e187ef 100755\n--- a/t/t6008-rev-list-submodule.sh\n+++ b/t/t6008-rev-list-submodule.sh\n@@ -8,6 +8,7 @@ test_description='git rev-list involving submodules that this repo has'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh\nindex f2e7df59cf2..3269298197c 100755\n--- a/t/t7414-submodule-mistakes.sh\n+++ b/t/t7414-submodule-mistakes.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='handling of common mistakes people may make with submodules'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'create embedded repository' '\ndiff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh\nindex 3fcb44767f5..f5426a8e589 100755\n--- a/t/t7506-status-submodule.sh\n+++ b/t/t7506-status-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='git status for submodule'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_create_repo_with_commit () {\ndiff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh\nindex ed2653d46fe..92462a22374 100755\n--- a/t/t7507-commit-verbose.sh\n+++ b/t/t7507-commit-verbose.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='verbose commit template'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n write_script \"check-for-diff\" <<\\EOF &&\n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462337","messageId":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v6-00.17-00000000000-20220821T130415Z-avarab@gmail.com","subject":"[PATCH v7 00/17] submodule--helper: fix memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:07Z","receivedAt":"2022-08-31T23:15:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This series fixes all of the memory leaks in\nbuiltin/submodule--helper.c that our test suite spots, except for\nthose where the leak is downstream of cmd_submodule__helper() and\ncaused by code that's not in builtin/submodule--helper.c (e.g. leaks\nin the object or config APIs, and in submodule.c).\n\nThis re-roll is on top of the just-now re-rolled v4 of the base\ntopic[1].\n\nChanges in v7:\n\n * Rebase on top of the base topic.\n * Clarify some questions on v6 in updated commit messages.\n * Go with Glen's suggested \"displaypath\" fix. I also tried another\n   way which the commit discusses. The end result was better, but it\n   requires further submodule--helper cleanups, and this is already\n   quite an Odyssey. So FREE_AND_NULL() will do.\n\n1. https://lore.kernel.org/git/cover-v4-00.33-00000000000-20220831T230519Z-avarab@gmail.com\n\nThis series & passing CI can be seen at:\nhttps://github.com/avar/git/tree/avar/submodule--helper-memory-leaks-7\n\nÆvar Arnfjörð Bjarmason (17):\n  submodule--helper: fix a leak in \"clone_submodule\"\n  submodule--helper: fix trivial get_default_remote_submodule() leak\n  submodule--helper: fix most \"struct pathspec\" memory leaks\n  submodule--helper: \"struct pathspec\" memory leak in module_update()\n  submodule--helper: don't leak {run,capture}_command() cp.dir argument\n  submodule--helper: add and use *_release() functions\n  submodule--helper: fix \"errmsg_str\" memory leak\n  submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks\n  submodule--helper: fix a leak with repo_clear()\n  submodule--helper: fix a memory leak in get_default_remote_submodule()\n  submodule--helper: fix \"reference\" leak\n  submodule--helper: fix obscure leak in module_add()\n  submodule--helper: fix a leak in module_add()\n  submodule--helper: fix a memory leak in print_status()\n  submodule--helper: free some \"displaypath\" in \"struct update_data\"\n  submodule--helper: free rest of \"displaypath\" in \"struct update_data\"\n  submodule--helper: fix a configure_added_submodule() leak\n\n builtin/submodule--helper.c        | 227 +++++++++++++++++++++--------\n t/t1500-rev-parse.sh               |   1 +\n t/t2403-worktree-move.sh           |   1 +\n t/t6008-rev-list-submodule.sh      |   1 +\n t/t6134-pathspec-in-submodule.sh   |   1 +\n t/t7401-submodule-summary.sh       |   1 +\n t/t7412-submodule-absorbgitdirs.sh |   1 +\n t/t7413-submodule-is-active.sh     |   1 +\n t/t7414-submodule-mistakes.sh      |   2 +\n t/t7419-submodule-set-branch.sh    |   1 +\n t/t7506-status-submodule.sh        |   1 +\n t/t7507-commit-verbose.sh          |   2 +\n 12 files changed, 176 insertions(+), 64 deletions(-)\n\nRange-diff against v6:\n 1:  0f60ea6f7c6 =  1:  40d4aef60fa submodule--helper: fix a leak in \"clone_submodule\"\n 2:  c14e00f39cb =  2:  104f0777d50 submodule--helper: fix trivial get_default_remote_submodule() leak\n 3:  a2ecdb301d3 !  3:  3e1948ddc02 submodule--helper: fix most \"struct pathspec\" memory leaks\n    @@ Commit message\n         safe to call clear_pathspec(), we don't want the data to be\n         uninitialized.\n     \n    +    E.g. for module_foreach() we can see from looking at\n    +    module_list_compute() that if it returns non-zero that the \"pathspec\"\n    +    will always have been initialized. But relying on that both assumes\n    +    knowledge about parse_pathspec(), and would set up a fragile pattern\n    +    going forward.\n    +\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/submodule--helper.c ##\n 4:  8fc3dad316e =  4:  505d7d5077e submodule--helper: \"struct pathspec\" memory leak in module_update()\n 5:  a3886d36366 !  5:  41672ffa55c submodule--helper: don't leak {run,capture}_command() cp.dir argument\n    @@ Commit message\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/submodule--helper.c ##\n    -@@ builtin/submodule--helper.c: static int is_tip_reachable(const char *path, struct object_id *oid)\n    +@@ builtin/submodule--helper.c: static int is_tip_reachable(const char *path, const struct object_id *oid)\n      \tchar *hex = oid_to_hex(oid);\n      \n      \tcp.git_cmd = 1;\n    @@ builtin/submodule--helper.c: static int is_tip_reachable(const char *path, struc\n      \tcp.no_stderr = 1;\n      \tstrvec_pushl(&cp.args, \"rev-list\", \"-n\", \"1\", hex, \"--not\", \"--all\", NULL);\n      \n    -@@ builtin/submodule--helper.c: static int fetch_in_submodule(const char *module_path, int depth, int quiet, str\n    +@@ builtin/submodule--helper.c: static int fetch_in_submodule(const char *module_path, int depth, int quiet,\n      \n      \tprepare_submodule_repo_env(&cp.env);\n      \tcp.git_cmd = 1;\n    @@ builtin/submodule--helper.c: static int fetch_in_submodule(const char *module_pa\n      \n      \tstrvec_push(&cp.args, \"fetch\");\n      \tif (quiet)\n    -@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)\n    +@@ builtin/submodule--helper.c: static int run_update_command(const struct update_data *ud, int subforce)\n      \t}\n      \tstrvec_push(&cp.args, oid);\n      \n 6:  173dbb7d8fc =  6:  07a4cda579f submodule--helper: add and use *_release() functions\n 7:  cd394814370 =  7:  600526e1301 submodule--helper: fix \"errmsg_str\" memory leak\n 8:  2110b991913 =  8:  2c5db495189 submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks\n 9:  a0d1a03166f =  9:  71ebaac73c1 submodule--helper: fix a leak with repo_clear()\n10:  84f162f320b = 10:  8a016f3d58c submodule--helper: fix a memory leak in get_default_remote_submodule()\n11:  dc5e8504354 = 11:  6a4c22d07b9 submodule--helper: fix \"reference\" leak\n12:  50d58bfb19b = 12:  b8654647601 submodule--helper: fix obscure leak in module_add()\n13:  56590508180 = 13:  c922e04f3d1 submodule--helper: fix a leak in module_add()\n14:  38ceb0a36ab = 14:  33babb1a165 submodule--helper: fix a memory leak in print_status()\n15:  f3cf49dd029 = 15:  bfafd31746e submodule--helper: free some \"displaypath\" in \"struct update_data\"\n16:  a99a7736fab ! 16:  63ff554e306 submodule--helper: free rest of \"displaypath\" in \"struct update_data\"\n    @@ Commit message\n         update_submodule() function itself can use it, and for the\n         run_update_procedure() called within this function.\n     \n    -    To make managing that clobbering easier let's wrap the\n    -    update_submodule() in a new update_submodule_outer() function, which\n    -    will do the clobbering and free(to_free) dance for us.\n    +    Fix a leak in code added in 51f8f94e5b (submodule--helper: run update\n    +    procedures from C, 2021-08-24). We'd always clobber the old\n    +    \"displaypath\" member of the previously passed-in \"struct update_data\".\n     \n    +    A better fix for this would be to remove the \"displaypath\" member from\n    +    the \"struct update_data\" entirely. Along with \"oid\", \"suboid\",\n    +    \"just_cloned\" and \"sm_path\" it's managing members that mainly need to\n    +    be passed between 1-3 stack frames of functions adjacent to this\n    +    code. But doing so would be a much larger change (I have it locally,\n    +    and fully untangling that in an incremental way is a 10 patch\n    +    journey).\n    +\n    +    So let's go for this much more isolated fix suggested by Glen. We\n    +    FREE_AND_NULL() the \"update_data->displaypath\", the \"AND_NULL()\" part\n    +    of that is needed due to the later \"free(ud->displaypath)\" in\n    +    \"update_data_release()\" introduced in the preceding commit\n    +\n    +    Moving ensure_core_worktree() out of update_submodule() may not be\n    +    strictly required, but in doing so we are left with the exact same\n    +    ordering as before, making this a smaller functional change.\n    +\n    +    Helped-by: Glen Choo <chooglen@google.com>\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/submodule--helper.c ##\n    @@ builtin/submodule--helper.c: static int update_submodule(struct update_data *upd\n      \tret = determine_submodule_update_strategy(the_repository,\n      \t\t\t\t\t\t  update_data->just_cloned,\n      \t\t\t\t\t\t  update_data->sm_path,\n    -@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)\n    - \treturn 0;\n    - }\n    - \n    -+static int update_submodule_outer(struct update_data *update_data)\n    -+{\n    -+\tchar *to_free, *restore = update_data->displaypath;\n    -+\tint ret;\n    -+\n    -+\tensure_core_worktree(update_data->sm_path);\n    -+\n    -+\tupdate_data->displaypath = to_free = get_submodule_displaypath(\n    -+\t\tupdate_data->sm_path, update_data->prefix);\n    -+\n    -+\tret = update_submodule(update_data);\n    -+\n    -+\tfree(to_free);\n    -+\tupdate_data->displaypath = restore;\n    -+\n    -+\treturn ret;\n    -+}\n    -+\n    - static int update_submodules(struct update_data *update_data)\n    - {\n    - \tint i, ret = 0;\n     @@ builtin/submodule--helper.c: static int update_submodules(struct update_data *update_data)\n      \t\tupdate_data->just_cloned = ucd.just_cloned;\n      \t\tupdate_data->sm_path = ucd.sub->path;\n      \n    --\t\tcode = update_submodule(update_data);\n    -+\t\tcode = update_submodule_outer(update_data);\n    ++\t\tcode = ensure_core_worktree(update_data->sm_path);\n    ++\t\tif (code)\n    ++\t\t\tgoto fail;\n    ++\n    ++\t\tupdate_data->displaypath = get_submodule_displaypath(\n    ++\t\t\tupdate_data->sm_path, update_data->prefix);\n    + \t\tcode = update_submodule(update_data);\n    ++\t\tFREE_AND_NULL(update_data->displaypath);\n    ++fail:\n      \t\tif (!code)\n      \t\t\tcontinue;\n      \t\tret = code;\n17:  ec0b02d8ee2 = 17:  3056bfcf607 submodule--helper: fix a configure_added_submodule() leak\n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462338","messageId":"patch-v7-02.17-104f0777d50-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"[PATCH v7 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:09Z","receivedAt":"2022-08-31T23:15:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in code added in 1012a5cbc3f (submodule--helper\nrun-update-procedure: learn --remote, 2022-03-04), we need to free()\nthe xstrdup()'d string. This gets e.g. t/t7419-submodule-set-branch.sh\ncloser to passing under SANITIZE=leak.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 2e042c8a043..b51a5074b42 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2451,6 +2451,8 @@ static int update_submodule(struct update_data *update_data)\n \t\t\treturn code;\n \t\tremote_ref = xstrfmt(\"refs/remotes/%s/%s\", remote_name, branch);\n \n+\t\tfree(remote_name);\n+\n \t\tif (!update_data->nofetch) {\n \t\t\tif (fetch_in_submodule(update_data->sm_path, update_data->depth,\n \t\t\t\t\t      0, NULL))\n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462339","messageId":"patch-v7-05.17-41672ffa55c-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"[PATCH v7 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:12Z","receivedAt":"2022-08-31T23:15:14Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak in c51f8f94e5b (submodule--helper: run update\nprocedures from C, 2021-08-24) and 3c3558f0953 (submodule--helper: run\nupdate using child process struct, 2022-03-15) by not allocating\nmemory in the first place.\n\nThe \"dir\" member of \"struct child_process\" will not be modified by\nthat API, and it's declared to be \"const char *\". So let's not\nneedlessly duplicate these strings.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex e8bc6f17330..0c23dd93d96 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2129,7 +2129,7 @@ static int is_tip_reachable(const char *path, const struct object_id *oid)\n \tchar *hex = oid_to_hex(oid);\n \n \tcp.git_cmd = 1;\n-\tcp.dir = xstrdup(path);\n+\tcp.dir = path;\n \tcp.no_stderr = 1;\n \tstrvec_pushl(&cp.args, \"rev-list\", \"-n\", \"1\", hex, \"--not\", \"--all\", NULL);\n \n@@ -2148,7 +2148,7 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet,\n \n \tprepare_submodule_repo_env(&cp.env);\n \tcp.git_cmd = 1;\n-\tcp.dir = xstrdup(module_path);\n+\tcp.dir = module_path;\n \n \tstrvec_push(&cp.args, \"fetch\");\n \tif (quiet)\n@@ -2201,7 +2201,7 @@ static int run_update_command(const struct update_data *ud, int subforce)\n \t}\n \tstrvec_push(&cp.args, oid);\n \n-\tcp.dir = xstrdup(ud->sm_path);\n+\tcp.dir = ud->sm_path;\n \tprepare_submodule_repo_env(&cp.env);\n \tif ((ret = run_command(&cp))) {\n \t\tswitch (ud->update_strategy.type) {\n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462340","messageId":"patch-v7-03.17-3e1948ddc02-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"[PATCH v7 03/17] submodule--helper: fix most \"struct pathspec\" memory leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:10Z","receivedAt":"2022-08-31T23:15:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call clear_pathspec() at the end of various functions that work with\nand allocate a \"struct pathspec\".\n\nIn some cases the zero-initialization here isn't strictly needed, but\nas we're moving to a \"goto cleanup\" pattern let's make sure that it's\nsafe to call clear_pathspec(), we don't want the data to be\nuninitialized.\n\nE.g. for module_foreach() we can see from looking at\nmodule_list_compute() that if it returns non-zero that the \"pathspec\"\nwill always have been initialized. But relying on that both assumes\nknowledge about parse_pathspec(), and would set up a fragile pattern\ngoing forward.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 74 +++++++++++++++++++++++++------------\n 1 file changed, 51 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex b51a5074b42..2f8603a6bb5 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -379,7 +379,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,\n static int module_foreach(int argc, const char **argv, const char *prefix)\n {\n \tstruct foreach_cb info = FOREACH_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tstruct option module_foreach_options[] = {\n \t\tOPT__QUIET(&info.quiet, N_(\"suppress output of entering each submodule command\")),\n@@ -391,12 +391,13 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule foreach [--quiet] [--recursive] [--] <command>\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_foreach_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.argc = argc;\n \tinfo.argv = argv;\n@@ -404,7 +405,10 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static int starts_with_dot_slash(const char *const path)\n@@ -515,7 +519,7 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data\n static int module_init(int argc, const char **argv, const char *prefix)\n {\n \tstruct init_cb info = INIT_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tstruct option module_init_options[] = {\n@@ -526,12 +530,13 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule init [<options>] [<path>]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_init_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \t/*\n \t * If there are no path args and submodule.active is set then,\n@@ -546,7 +551,10 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, init_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct status_cb {\n@@ -693,7 +701,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,\n static int module_status(int argc, const char **argv, const char *prefix)\n {\n \tstruct status_cb info = STATUS_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tstruct option module_status_options[] = {\n@@ -706,12 +714,13 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule status [--quiet] [--cached] [--recursive] [<path>...]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_status_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -719,7 +728,10 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, status_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct module_cb {\n@@ -1261,7 +1273,7 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data\n static int module_sync(int argc, const char **argv, const char *prefix)\n {\n \tstruct sync_cb info = SYNC_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tint recursive = 0;\n@@ -1275,12 +1287,13 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule sync [--quiet] [--recursive] [<path>]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_sync_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -1290,7 +1303,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, sync_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct deinit_cb {\n@@ -1399,7 +1415,7 @@ static void deinit_submodule_cb(const struct cache_entry *list_item,\n static int module_deinit(int argc, const char **argv, const char *prefix)\n {\n \tstruct deinit_cb info = DEINIT_CB_INIT;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tint quiet = 0;\n \tint force = 0;\n@@ -1414,6 +1430,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, module_deinit_options,\n \t\t\t     git_submodule_helper_usage, 0);\n@@ -1428,7 +1445,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"Use '--all' if you really want to deinitialize all submodules\"));\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tinfo.prefix = prefix;\n \tif (quiet)\n@@ -1438,7 +1455,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \n \tfor_each_listed_submodule(&list, deinit_submodule_cb, &info);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n struct module_clone_data {\n@@ -2543,7 +2563,7 @@ static int update_submodules(struct update_data *update_data)\n \n static int module_update(int argc, const char **argv, const char *prefix)\n {\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct update_data opt = UPDATE_DATA_INIT;\n \tstruct list_objects_filter_options filter_options = { 0 };\n \tint ret;\n@@ -2620,8 +2640,8 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\topt.update_strategy.type = opt.update_default;\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {\n-\t\tlist_objects_filter_release(&filter_options);\n-\t\treturn 1;\n+\t\tret = 1;\n+\t\tgoto cleanup;\n \t}\n \n \tif (pathspec.nr)\n@@ -2632,8 +2652,10 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\tstruct init_cb info = INIT_CB_INIT;\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n-\t\t\t\t\t&pathspec, &list) < 0)\n-\t\t\treturn 1;\n+\t\t\t\t\t&pathspec, &list) < 0) {\n+\t\t\tret = 1;\n+\t\t\tgoto cleanup;\n+\t\t}\n \n \t\t/*\n \t\t * If there are no path args and submodule.active is set then,\n@@ -2650,7 +2672,9 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t}\n \n \tret = update_submodules(&opt);\n+cleanup:\n \tlist_objects_filter_release(&filter_options);\n+\tclear_pathspec(&pathspec);\n \treturn ret;\n }\n \n@@ -2734,7 +2758,7 @@ static int push_check(int argc, const char **argv, const char *prefix)\n static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n-\tstruct pathspec pathspec;\n+\tstruct pathspec pathspec = { 0 };\n \tstruct module_list list = MODULE_LIST_INIT;\n \tunsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;\n \tstruct option embed_gitdir_options[] = {\n@@ -2749,17 +2773,21 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule absorbgitdirs [<options>] [<path>...]\"),\n \t\tNULL\n \t};\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, embed_gitdir_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n \tif (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)\n-\t\treturn 1;\n+\t\tgoto cleanup;\n \n \tfor (i = 0; i < list.nr; i++)\n \t\tabsorb_git_dir_into_superproject(list.entries[i]->name, flags);\n \n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tclear_pathspec(&pathspec);\n+\treturn ret;\n }\n \n static int module_config(int argc, const char **argv, const char *prefix)\n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462341","messageId":"patch-v7-04.17-505d7d5077e-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"[PATCH v7 04/17] submodule--helper: \"struct pathspec\" memory leak in module_update()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:11Z","receivedAt":"2022-08-31T23:15:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The module_update() function calls module_list_compute() twice, which\nin turn will reset the \"struct pathspec\" passed to it. Let's instead\ntrack two of them, and clear them both.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 2f8603a6bb5..e8bc6f17330 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2564,6 +2564,7 @@ static int update_submodules(struct update_data *update_data)\n static int module_update(int argc, const char **argv, const char *prefix)\n {\n \tstruct pathspec pathspec = { 0 };\n+\tstruct pathspec pathspec2 = { 0 };\n \tstruct update_data opt = UPDATE_DATA_INIT;\n \tstruct list_objects_filter_options filter_options = { 0 };\n \tint ret;\n@@ -2652,7 +2653,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\tstruct init_cb info = INIT_CB_INIT;\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n-\t\t\t\t\t&pathspec, &list) < 0) {\n+\t\t\t\t\t&pathspec2, &list) < 0) {\n \t\t\tret = 1;\n \t\t\tgoto cleanup;\n \t\t}\n@@ -2675,6 +2676,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n cleanup:\n \tlist_objects_filter_release(&filter_options);\n \tclear_pathspec(&pathspec);\n+\tclear_pathspec(&pathspec2);\n \treturn ret;\n }\n \n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462342","messageId":"patch-v7-06.17-07a4cda579f-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"[PATCH v7 06/17] submodule--helper: add and use *_release() functions","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:13Z","receivedAt":"2022-08-31T23:15:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add release functions for \"struct module_list\", \"struct\nsubmodule_update_clone\" and \"struct update_data\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c      | 28 +++++++++++++++++++++++++++-\n t/t6134-pathspec-in-submodule.sh |  1 +\n 2 files changed, 28 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 0c23dd93d96..ae918a662b4 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -172,6 +172,11 @@ struct module_list {\n };\n #define MODULE_LIST_INIT { 0 }\n \n+static void module_list_release(struct module_list *ml)\n+{\n+\tfree(ml->entries);\n+}\n+\n static int module_list_compute(int argc, const char **argv,\n \t\t\t       const char *prefix,\n \t\t\t       struct pathspec *pathspec,\n@@ -234,7 +239,7 @@ static void module_list_active(struct module_list *list)\n \t\tactive_modules.entries[active_modules.nr++] = ce;\n \t}\n \n-\tfree(list->entries);\n+\tmodule_list_release(list);\n \t*list = active_modules;\n }\n \n@@ -407,6 +412,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -553,6 +559,7 @@ static int module_init(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -730,6 +737,7 @@ static int module_status(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1305,6 +1313,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1457,6 +1466,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \n \tret = 0;\n cleanup:\n+\tmodule_list_release(&list);\n \tclear_pathspec(&pathspec);\n \treturn ret;\n }\n@@ -1834,6 +1844,12 @@ struct submodule_update_clone {\n };\n #define SUBMODULE_UPDATE_CLONE_INIT { 0 }\n \n+static void submodule_update_clone_release(struct submodule_update_clone *suc)\n+{\n+\tfree(suc->update_clone);\n+\tfree(suc->failed_clones);\n+}\n+\n struct update_data {\n \tconst char *prefix;\n \tconst char *displaypath;\n@@ -1872,6 +1888,11 @@ struct update_data {\n \t.max_jobs = 1, \\\n }\n \n+static void update_data_release(struct update_data *ud)\n+{\n+\tmodule_list_release(&ud->list);\n+}\n+\n static void next_submodule_warn_missing(struct submodule_update_clone *suc,\n \t\tstruct strbuf *out, const char *displaypath)\n {\n@@ -2557,6 +2578,7 @@ static int update_submodules(struct update_data *update_data)\n \t}\n \n cleanup:\n+\tsubmodule_update_clone_release(&suc);\n \tstring_list_clear(&update_data->references, 0);\n \treturn ret;\n }\n@@ -2654,6 +2676,7 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \n \t\tif (module_list_compute(argc, argv, opt.prefix,\n \t\t\t\t\t&pathspec2, &list) < 0) {\n+\t\t\tmodule_list_release(&list);\n \t\t\tret = 1;\n \t\t\tgoto cleanup;\n \t\t}\n@@ -2670,10 +2693,12 @@ static int module_update(int argc, const char **argv, const char *prefix)\n \t\t\tinfo.flags |= OPT_QUIET;\n \n \t\tfor_each_listed_submodule(&list, init_submodule_cb, &info);\n+\t\tmodule_list_release(&list);\n \t}\n \n \tret = update_submodules(&opt);\n cleanup:\n+\tupdate_data_release(&opt);\n \tlist_objects_filter_release(&filter_options);\n \tclear_pathspec(&pathspec);\n \tclear_pathspec(&pathspec2);\n@@ -2789,6 +2814,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)\n \tret = 0;\n cleanup:\n \tclear_pathspec(&pathspec);\n+\tmodule_list_release(&list);\n \treturn ret;\n }\n \ndiff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh\nindex 0f1cb49cedc..3a241f259de 100755\n--- a/t/t6134-pathspec-in-submodule.sh\n+++ b/t/t6134-pathspec-in-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='test case exclude pathspec'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a submodule' '\n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462343","messageId":"patch-v7-07.17-600526e1301-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"[PATCH v7 07/17] submodule--helper: fix \"errmsg_str\" memory leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:14Z","receivedAt":"2022-08-31T23:15:21Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak introduced in e83e3333b57 (submodule: port submodule\nsubcommand 'summary' from shell to C, 2020-08-13), we sometimes append\nto the \"errmsg\", and need to free the \"struct strbuf\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex ae918a662b4..75a5d9d47fd 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -973,6 +973,7 @@ static void generate_submodule_summary(struct summary_cb *info,\n \tfree(displaypath);\n \tfree(src_abbrev);\n \tfree(dst_abbrev);\n+\tstrbuf_release(&errmsg);\n }\n \n static void prepare_submodule_summary(struct summary_cb *info,\n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462344","messageId":"patch-v7-08.17-2c5db495189-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"[PATCH v7 08/17] submodule--helper: fix \"sm_path\" and other \"module_cb_list\" leaks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:15Z","receivedAt":"2022-08-31T23:15:23Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix leaks in \"struct module_cb_list\" and the \"struct module_cb\" which\nit contains, these fix leaks in e83e3333b57 (submodule: port submodule\nsubcommand 'summary' from shell to C, 2020-08-13).\n\nThe \"sm_path\" should always have been a \"char *\", not a \"const\nchar *\", we always create it with xstrdup().\n\nWe can't mark any tests passing passing with SANITIZE=leak using\n\"TEST_PASSES_SANITIZE_LEAK=true\" as a result of this change, but\n\"t7401-submodule-summary.sh\" gets closer to passing as a result of\nthis change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c  | 21 ++++++++++++++++++++-\n t/t7401-submodule-summary.sh |  1 +\n 2 files changed, 21 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 75a5d9d47fd..162e92c033e 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -748,16 +748,34 @@ struct module_cb {\n \tstruct object_id oid_src;\n \tstruct object_id oid_dst;\n \tchar status;\n-\tconst char *sm_path;\n+\tchar *sm_path;\n };\n #define MODULE_CB_INIT { 0 }\n \n+static void module_cb_release(struct module_cb *mcb)\n+{\n+\tfree(mcb->sm_path);\n+}\n+\n struct module_cb_list {\n \tstruct module_cb **entries;\n \tint alloc, nr;\n };\n #define MODULE_CB_LIST_INIT { 0 }\n \n+static void module_cb_list_release(struct module_cb_list *mcbl)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < mcbl->nr; i++) {\n+\t\tstruct module_cb *mcb = mcbl->entries[i];\n+\n+\t\tmodule_cb_release(mcb);\n+\t\tfree(mcb);\n+\t}\n+\tfree(mcbl->entries);\n+}\n+\n struct summary_cb {\n \tint argc;\n \tconst char **argv;\n@@ -1104,6 +1122,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n cleanup:\n \tstrvec_clear(&diff_args);\n \trelease_revisions(&rev);\n+\tmodule_cb_list_release(&list);\n \treturn ret;\n }\n \ndiff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh\nindex 9c3cc4cf404..542b3331a78 100755\n--- a/t/t7401-submodule-summary.sh\n+++ b/t/t7401-submodule-summary.sh\n@@ -17,6 +17,7 @@ This test script tries to verify the sanity of summary subcommand of git submodu\n # various reasons, one of them being that there are lots of commands taking place\n # outside of 'test_expect_success' block, which is no longer in good-style.\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n add_file () {\n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462345","messageId":"patch-v7-10.17-8a016f3d58c-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"[PATCH v7 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:17Z","receivedAt":"2022-08-31T23:15:36Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak in the get_default_remote_submodule() function added\nin a77c3fcb5ec (submodule--helper: get remote names from any\nrepository, 2022-03-04), we need to repo_clear() the submodule we\ninitialize.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 9a277057012..9f9e5f05094 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -65,12 +65,16 @@ static int repo_get_default_remote(struct repository *repo, char **default_remot\n static int get_default_remote_submodule(const char *module_path, char **default_remote)\n {\n \tstruct repository subrepo;\n+\tint ret;\n \n \tif (repo_submodule_init(&subrepo, the_repository, module_path,\n \t\t\t\tnull_oid()) < 0)\n \t\treturn die_message(_(\"could not get a repository handle for submodule '%s'\"),\n \t\t\t\t   module_path);\n-\treturn repo_get_default_remote(&subrepo, default_remote);\n+\tret = repo_get_default_remote(&subrepo, default_remote);\n+\trepo_clear(&subrepo);\n+\n+\treturn ret;\n }\n \n static char *get_default_remote(void)\n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462346","messageId":"patch-v7-11.17-6a4c22d07b9-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"[PATCH v7 11/17] submodule--helper: fix \"reference\" leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:18Z","receivedAt":"2022-08-31T23:15:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix leaks in the \"reference\" variable declared in add_submodule() and\nmodule_clone().\n\nIn preceding commits this variable was refactored out of the \"struct\nmodule_clone_data\", but the leak has been with us since\n31224cbdc72 (clone: recursive and reference option triggers submodule\nalternates, 2016-08-17) and 8c8195e9c3e (submodule--helper: introduce\nadd-clone subcommand, 2021-07-10).\n\nThose commits added an xstrdup()'d member of the\nSTRING_LIST_INIT_NODUP'd \"struct string_list\". We need to free()\nthose, but not the ones we get from argv, let's make use of the \"util\"\nmember, if it has a pointer it's the pointer we'll need to free,\notherwise it'll be NULL (i.e. from argv).\n\nNote that the free() of the \"util\" member is needed in both\nmodule_clone() and add_submodule(). The module_clone() function itself\ndoesn't populate the \"util\" pointer as add_submodule() does, but\nmodule_clone() is upstream of the\nadd_possible_reference_from_superproject() caller we're modifying\nhere, which does do that.\n\nThis does preclude the use of the \"util\" pointer for any other reasons\nfor now, but that's OK. If we ever need to use it for something else\nwe could turn it into a small \"struct\" with an optional \"to_free\"\nmember, and switch to using string_list_clear_func().\n\nAlternatively we could have another \"struct string_list to_free\" which\nwould keep a copy of the strings we've dup'd to free(). But for now\nthis is perfectly adequate.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 21 +++++++++++++++------\n 1 file changed, 15 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 9f9e5f05094..170eaf010ac 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1566,7 +1566,9 @@ static int add_possible_reference_from_superproject(\n \n \t\tsm_alternate = compute_alternate_path(sb.buf, &err);\n \t\tif (sm_alternate) {\n-\t\t\tstring_list_append(sas->reference, xstrdup(sb.buf));\n+\t\t\tchar *p = strbuf_detach(&sb, NULL);\n+\n+\t\t\tstring_list_append(sas->reference, p)->util = p;\n \t\t\tfree(sm_alternate);\n \t\t} else {\n \t\t\tswitch (sas->error_mode) {\n@@ -1798,6 +1800,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \n \tclone_submodule(&clone_data, &reference);\n \tlist_objects_filter_release(&filter_options);\n+\tstring_list_clear(&reference, 1);\n \treturn 0;\n }\n \n@@ -3045,6 +3048,7 @@ static int add_submodule(const struct add_data *add_data)\n \tchar *submod_gitdir_path;\n \tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n \tstruct string_list reference = STRING_LIST_INIT_NODUP;\n+\tint ret = -1;\n \n \t/* perhaps the path already exists and is already a git repo, else clone it */\n \tif (is_directory(add_data->sm_path)) {\n@@ -3100,15 +3104,17 @@ static int add_submodule(const struct add_data *add_data)\n \t\tclone_data.url = add_data->realrepo;\n \t\tclone_data.quiet = add_data->quiet;\n \t\tclone_data.progress = add_data->progress;\n-\t\tif (add_data->reference_path)\n-\t\t\tstring_list_append(&reference,\n-\t\t\t\t\t   xstrdup(add_data->reference_path));\n+\t\tif (add_data->reference_path) {\n+\t\t\tchar *p = xstrdup(add_data->reference_path);\n+\n+\t\t\tstring_list_append(&reference, p)->util = p;\n+\t\t}\n \t\tclone_data.dissociate = add_data->dissociate;\n \t\tif (add_data->depth >= 0)\n \t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n \n \t\tif (clone_submodule(&clone_data, &reference))\n-\t\t\treturn -1;\n+\t\t\tgoto cleanup;\n \n \t\tprepare_submodule_repo_env(&cp.env);\n \t\tcp.git_cmd = 1;\n@@ -3127,7 +3133,10 @@ static int add_submodule(const struct add_data *add_data)\n \t\tif (run_command(&cp))\n \t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n \t}\n-\treturn 0;\n+\tret = 0;\n+cleanup:\n+\tstring_list_clear(&reference, 1);\n+\treturn ret;\n }\n \n static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)\n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462347","messageId":"patch-v7-09.17-71ebaac73c1-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"[PATCH v7 09/17] submodule--helper: fix a leak with repo_clear()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:16Z","receivedAt":"2022-08-31T23:15:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call repo_clear() in ensure_core_worktree() to free the \"struct\nrepository\". Fixes a leak that's been here since\n74d4731da1f (submodule--helper: replace connect-gitdir-workingtree by\nensure-core-worktree, 2018-08-13).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 162e92c033e..9a277057012 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2400,6 +2400,7 @@ static int ensure_core_worktree(const char *path)\n \t\tstrbuf_release(&sb);\n \t}\n \n+\trepo_clear(&subrepo);\n \treturn 0;\n }\n \n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462348","messageId":"patch-v7-12.17-b8654647601-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"[PATCH v7 12/17] submodule--helper: fix obscure leak in module_add()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:19Z","receivedAt":"2022-08-31T23:15:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix an obscure leak in module_add(), if the \"git add\" command we were\npiping to failed we'd fail to strbuf_release(&sb). This fixes a leak\nintroduced in a6226fd772b (submodule--helper: convert the bulk of\ncmd_add() to C, 2021-08-10).\n\nIn fixing it move to a \"goto cleanup\" pattern, and since we need to\nintroduce a \"ret\" variable to do that let's also get rid of the\nintermediate \"exit_code\" variable. The initialization to \"-1\" in\na6226fd772b has always been redundant, we'd only use the \"exit_code\"\nvalue after assigning the return value of pipe_command() to it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 22 +++++++++++-----------\n 1 file changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 170eaf010ac..675dc3f16a3 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3296,6 +3296,8 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \t\tN_(\"git submodule add [<options>] [--] <repository> [<path>]\"),\n \t\tNULL\n \t};\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint ret = 1;\n \n \targc = parse_options(argc, argv, prefix, options, usage, 0);\n \n@@ -3345,21 +3347,17 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \tdie_on_repo_without_commits(add_data.sm_path);\n \n \tif (!force) {\n-\t\tint exit_code = -1;\n-\t\tstruct strbuf sb = STRBUF_INIT;\n \t\tstruct child_process cp = CHILD_PROCESS_INIT;\n \n \t\tcp.git_cmd = 1;\n \t\tcp.no_stdout = 1;\n \t\tstrvec_pushl(&cp.args, \"add\", \"--dry-run\", \"--ignore-missing\",\n \t\t\t     \"--no-warn-embedded-repo\", add_data.sm_path, NULL);\n-\t\tif ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {\n+\t\tif ((ret = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {\n \t\t\tstrbuf_complete_line(&sb);\n \t\t\tfputs(sb.buf, stderr);\n-\t\t\tfree(add_data.sm_path);\n-\t\t\treturn exit_code;\n+\t\t\tgoto cleanup;\n \t\t}\n-\t\tstrbuf_release(&sb);\n \t}\n \n \tif(!add_data.sm_name)\n@@ -3374,15 +3372,17 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \tadd_data.progress = !!progress;\n \tadd_data.dissociate = !!dissociate;\n \n-\tif (add_submodule(&add_data)) {\n-\t\tfree(add_data.sm_path);\n-\t\treturn 1;\n-\t}\n+\tif (add_submodule(&add_data))\n+\t\tgoto cleanup;\n \tconfigure_added_submodule(&add_data);\n+\n+\tret = 0;\n+cleanup:\n \tfree(add_data.sm_path);\n \tfree(to_free);\n+\tstrbuf_release(&sb);\n \n-\treturn 0;\n+\treturn ret;\n }\n \n #define SUPPORT_SUPER_PREFIX (1<<0)\n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462349","messageId":"patch-v7-13.17-c922e04f3d1-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"[PATCH v7 13/17] submodule--helper: fix a leak in module_add()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:20Z","receivedAt":"2022-08-31T23:15:47Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in module_path(), since a6226fd772b (submodule--helper:\nconvert the bulk of cmd_add() to C, 2021-08-10), we've been freeing\nadd_data.sm_path, but in this case we clobbered it, and didn't free\nthe value we clobbered.\n\nThis makes test 28 of \"t/t7400-submodule-basic.sh\" (\"submodule add in\nsubdirectory\") pass when we're compiled with SANITIZE=leak..\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 675dc3f16a3..7c62916da3c 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3317,8 +3317,12 @@ static int module_add(int argc, const char **argv, const char *prefix)\n \telse\n \t\tadd_data.sm_path = xstrdup(argv[1]);\n \n-\tif (prefix && *prefix && !is_absolute_path(add_data.sm_path))\n-\t\tadd_data.sm_path = xstrfmt(\"%s%s\", prefix, add_data.sm_path);\n+\tif (prefix && *prefix && !is_absolute_path(add_data.sm_path)) {\n+\t\tchar *sm_path = add_data.sm_path;\n+\n+\t\tadd_data.sm_path = xstrfmt(\"%s%s\", prefix, sm_path);\n+\t\tfree(sm_path);\n+\t}\n \n \tif (starts_with_dot_dot_slash(add_data.repo) ||\n \t    starts_with_dot_slash(add_data.repo)) {\n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462350","messageId":"patch-v7-14.17-33babb1a165-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"[PATCH v7 14/17] submodule--helper: fix a memory leak in print_status()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:21Z","receivedAt":"2022-08-31T23:16:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in print_status(), the compute_rev_name() function\nimplemented in this file will return a strbuf_detach()'d value, or\nNULL.\n\nThis leak has existed since this code was added in\na9f8a37584a (submodule: port submodule subcommand 'status' from shell\nto C, 2017-10-06), but in 0b5e2ea7cf3 (submodule--helper: don't print\nnull in 'submodule status', 2018-04-18) we added a \"const\"\nintermediate variable for the return value, that \"const\" should be\nremoved.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 7c62916da3c..23c9e28deaa 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -583,10 +583,11 @@ static void print_status(unsigned int flags, char state, const char *path,\n \tprintf(\"%c%s %s\", state, oid_to_hex(oid), displaypath);\n \n \tif (state == ' ' || state == '+') {\n-\t\tconst char *name = compute_rev_name(path, oid_to_hex(oid));\n+\t\tchar *name = compute_rev_name(path, oid_to_hex(oid));\n \n \t\tif (name)\n \t\t\tprintf(\" (%s)\", name);\n+\t\tfree(name);\n \t}\n \n \tprintf(\"\\n\");\n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462351","messageId":"patch-v7-16.17-63ff554e306-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"[PATCH v7 16/17] submodule--helper: free rest of \"displaypath\" in \"struct update_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:23Z","receivedAt":"2022-08-31T23:16:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a leak in code added in c51f8f94e5b (submodule--helper: run update\nprocedures from C, 2021-08-24), we clobber the \"displaypath\" member of\nthe passed-in \"struct update_data\" both so that die() messages in this\nupdate_submodule() function itself can use it, and for the\nrun_update_procedure() called within this function.\n\nFix a leak in code added in 51f8f94e5b (submodule--helper: run update\nprocedures from C, 2021-08-24). We'd always clobber the old\n\"displaypath\" member of the previously passed-in \"struct update_data\".\n\nA better fix for this would be to remove the \"displaypath\" member from\nthe \"struct update_data\" entirely. Along with \"oid\", \"suboid\",\n\"just_cloned\" and \"sm_path\" it's managing members that mainly need to\nbe passed between 1-3 stack frames of functions adjacent to this\ncode. But doing so would be a much larger change (I have it locally,\nand fully untangling that in an incremental way is a 10 patch\njourney).\n\nSo let's go for this much more isolated fix suggested by Glen. We\nFREE_AND_NULL() the \"update_data->displaypath\", the \"AND_NULL()\" part\nof that is needed due to the later \"free(ud->displaypath)\" in\n\"update_data_release()\" introduced in the preceding commit\n\nMoving ensure_core_worktree() out of update_submodule() may not be\nstrictly required, but in doing so we are left with the exact same\nordering as before, making this a smaller functional change.\n\nHelped-by: Glen Choo <chooglen@google.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c | 15 ++++++++-------\n 1 file changed, 8 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 94ebd8ea38e..1650bf0070b 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2487,13 +2487,6 @@ static int update_submodule(struct update_data *update_data)\n {\n \tint ret;\n \n-\tret = ensure_core_worktree(update_data->sm_path);\n-\tif (ret)\n-\t\treturn ret;\n-\n-\tupdate_data->displaypath = get_submodule_displaypath(\n-\t\tupdate_data->sm_path, update_data->prefix);\n-\n \tret = determine_submodule_update_strategy(the_repository,\n \t\t\t\t\t\t  update_data->just_cloned,\n \t\t\t\t\t\t  update_data->sm_path,\n@@ -2599,7 +2592,15 @@ static int update_submodules(struct update_data *update_data)\n \t\tupdate_data->just_cloned = ucd.just_cloned;\n \t\tupdate_data->sm_path = ucd.sub->path;\n \n+\t\tcode = ensure_core_worktree(update_data->sm_path);\n+\t\tif (code)\n+\t\t\tgoto fail;\n+\n+\t\tupdate_data->displaypath = get_submodule_displaypath(\n+\t\t\tupdate_data->sm_path, update_data->prefix);\n \t\tcode = update_submodule(update_data);\n+\t\tFREE_AND_NULL(update_data->displaypath);\n+fail:\n \t\tif (!code)\n \t\t\tcontinue;\n \t\tret = code;\n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462352","messageId":"patch-v7-15.17-bfafd31746e-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"[PATCH v7 15/17] submodule--helper: free some \"displaypath\" in \"struct update_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:22Z","receivedAt":"2022-08-31T23:16:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Make the update_data_release() function free \"displaypath\" member when\nappropriate. The \"displaypath\" member is always ours, the \"const\" on\nthe \"char *\" was wrong to begin with.\n\nThis leaves a leak of \"displaypath\" in update_submodule(), which as\nwe'll see in subsequent commits is harder to deal with than this\ntrivial fix.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c        | 3 ++-\n t/t2403-worktree-move.sh           | 1 +\n t/t7412-submodule-absorbgitdirs.sh | 1 +\n t/t7419-submodule-set-branch.sh    | 1 +\n 4 files changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 23c9e28deaa..94ebd8ea38e 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1880,7 +1880,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)\n \n struct update_data {\n \tconst char *prefix;\n-\tconst char *displaypath;\n+\tchar *displaypath;\n \tenum submodule_update_type update_default;\n \tstruct object_id suboid;\n \tstruct string_list references;\n@@ -1918,6 +1918,7 @@ struct update_data {\n \n static void update_data_release(struct update_data *ud)\n {\n+\tfree(ud->displaypath);\n \tmodule_list_release(&ud->list);\n }\n \ndiff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh\nindex a4e1a178e0a..1168e9f9982 100755\n--- a/t/t2403-worktree-move.sh\n+++ b/t/t2403-worktree-move.sh\n@@ -2,6 +2,7 @@\n \n test_description='test git worktree move, remove, lock and unlock'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh\nindex 1cfa150768d..2859695c6d2 100755\n--- a/t/t7412-submodule-absorbgitdirs.sh\n+++ b/t/t7412-submodule-absorbgitdirs.sh\n@@ -6,6 +6,7 @@ This test verifies that `git submodue absorbgitdirs` moves a submodules git\n directory into the superproject.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a real submodule' '\ndiff --git a/t/t7419-submodule-set-branch.sh b/t/t7419-submodule-set-branch.sh\nindex 3b925c302fc..96e98423214 100755\n--- a/t/t7419-submodule-set-branch.sh\n+++ b/t/t7419-submodule-set-branch.sh\n@@ -9,6 +9,7 @@ This test verifies that the set-branch subcommand of git-submodule is working\n as expected.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n TEST_NO_CREATE_REPO=1\n . ./test-lib.sh\n \n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462353","messageId":"patch-v7-17.17-3056bfcf607-20220831T231003Z-avarab@gmail.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"[PATCH v7 17/17] submodule--helper: fix a configure_added_submodule() leak","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T23:14:24Z","receivedAt":"2022-08-31T23:16:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix config API a memory leak added in a452128a36c (submodule--helper:\nintroduce add-config subcommand, 2021-08-06) by using the *_tmp()\nvariant of git_config_get_string().\n\nIn this case we're only checking whether\nthe (repo|git)_config_get_string() call is telling us that the\n\"submodule.active\" key exists.\n\nAs with the preceding commit we'll find many other such patterns in\nthe codebase if we go fishing. E.g. \"git gc\" leaks in the code added\nin 61f7a383d3b (maintenance: use 'incremental' strategy by default,\n2020-10-15). Similar code in \"git gc\" added in\nb08ff1fee00 (maintenance: add --schedule option and config,\n2020-09-11) doesn't leak, but we could avoid the malloc() & free() in\nthat case.\n\nA coccinelle rule to find those would find and fix some leaks, and\ncases where we're doing needless malloc() + free()'s but only care\nabout the key existence, or are copying\nthe (repo|git)_config_get_string() return value right away.\n\nBut as with the preceding commit let's punt on all of that for now,\nand just narrowly fix this specific case in submodule--helper.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/submodule--helper.c    | 4 ++--\n t/t7413-submodule-is-active.sh | 1 +\n 2 files changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 1650bf0070b..37b6db5cbb4 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -3160,7 +3160,7 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con\n static void configure_added_submodule(struct add_data *add_data)\n {\n \tchar *key;\n-\tchar *val = NULL;\n+\tconst char *val;\n \tstruct child_process add_submod = CHILD_PROCESS_INIT;\n \tstruct child_process add_gitmodules = CHILD_PROCESS_INIT;\n \n@@ -3205,7 +3205,7 @@ static void configure_added_submodule(struct add_data *add_data)\n \t * is_submodule_active(), since that function needs to find\n \t * out the value of \"submodule.active\" again anyway.\n \t */\n-\tif (!git_config_get_string(\"submodule.active\", &val)) {\n+\tif (!git_config_get_string_tmp(\"submodule.active\", &val)) {\n \t\t/*\n \t\t * If the submodule being added isn't already covered by the\n \t\t * current configured pathspec, set the submodule's active flag\ndiff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh\nindex ede6f02dbd5..4dc7d089423 100755\n--- a/t/t7413-submodule-is-active.sh\n+++ b/t/t7413-submodule-is-active.sh\n@@ -9,6 +9,7 @@ This is a unit test of the submodule.c is_submodule_active() function,\n which is also indirectly tested elsewhere.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n-- \n2.37.3.1420.g76f8a3d556c\n\n"},{"id":"462494","messageId":"kl6ltu5qbxma.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"patch-v7-16.17-63ff554e306-20220831T231003Z-avarab@gmail.com","subject":"Re: [PATCH v7 16/17] submodule--helper: free rest of \"displaypath\" in \"struct update_data\"","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-09-01T21:20:13Z","receivedAt":"2022-09-01T21:20:32Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> A better fix for this would be to remove the \"displaypath\" member from\n> the \"struct update_data\" entirely. Along with \"oid\", \"suboid\",\n> \"just_cloned\" and \"sm_path\" it's managing members that mainly need to\n> be passed between 1-3 stack frames of functions adjacent to this\n> code. But doing so would be a much larger change (I have it locally,\n> and fully untangling that in an incremental way is a 10 patch\n> journey).\n\nYeah that does sound like a better fix _and_ too much churn right now.\n\n> So let's go for this much more isolated fix suggested by Glen. We\n> FREE_AND_NULL() the \"update_data->displaypath\", the \"AND_NULL()\" part\n> of that is needed due to the later \"free(ud->displaypath)\" in\n> \"update_data_release()\" introduced in the preceding commit\n>\n> Moving ensure_core_worktree() out of update_submodule() may not be\n> strictly required, but in doing so we are left with the exact same\n> ordering as before, making this a smaller functional change.\n\nOk, it's nice that we don't have to reason about functional changes,\nthough I doubt it will matter in this case.\n\nI worry a bit about whether this is setting up an implicit contract\nwhere we always need to call ensure_core_worktree() before\nupdate_submodule(), but the contract has always been quite fuzzy here,\ne.g. the \"struct update_data\" at this point contains a mix of CLI args +\nclone result; we should fix that at some point, so doing this\nchange in the meantime seems harmless.\n\n> Helped-by: Glen Choo <chooglen@google.com>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 15 ++++++++-------\n>  1 file changed, 8 insertions(+), 7 deletions(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index 94ebd8ea38e..1650bf0070b 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -2487,13 +2487,6 @@ static int update_submodule(struct update_data *update_data)\n>  {\n>  \tint ret;\n>  \n> -\tret = ensure_core_worktree(update_data->sm_path);\n> -\tif (ret)\n> -\t\treturn ret;\n> -\n> -\tupdate_data->displaypath = get_submodule_displaypath(\n> -\t\tupdate_data->sm_path, update_data->prefix);\n> -\n>  \tret = determine_submodule_update_strategy(the_repository,\n>  \t\t\t\t\t\t  update_data->just_cloned,\n>  \t\t\t\t\t\t  update_data->sm_path,\n> @@ -2599,7 +2592,15 @@ static int update_submodules(struct update_data *update_data)\n>  \t\tupdate_data->just_cloned = ucd.just_cloned;\n>  \t\tupdate_data->sm_path = ucd.sub->path;\n>  \n> +\t\tcode = ensure_core_worktree(update_data->sm_path);\n> +\t\tif (code)\n> +\t\t\tgoto fail;\n> +\n> +\t\tupdate_data->displaypath = get_submodule_displaypath(\n> +\t\t\tupdate_data->sm_path, update_data->prefix);\n>  \t\tcode = update_submodule(update_data);\n> +\t\tFREE_AND_NULL(update_data->displaypath);\n> +fail:\n>  \t\tif (!code)\n>  \t\t\tcontinue;\n>  \t\tret = code;\n\nLooks good.\n\n> -- \n> 2.37.3.1420.g76f8a3d556c\n"},{"id":"462496","messageId":"kl6lr10ubxge.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"58151","inReplyTo":"cover-v7-00.17-00000000000-20220831T231003Z-avarab@gmail.com","subject":"Re: [PATCH v7 00/17] submodule--helper: fix memory leaks","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-09-01T21:23:45Z","receivedAt":"2022-09-01T21:23:50Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> This series fixes all of the memory leaks in\n> builtin/submodule--helper.c that our test suite spots, except for\n> those where the leak is downstream of cmd_submodule__helper() and\n> caused by code that's not in builtin/submodule--helper.c (e.g. leaks\n> in the object or config APIs, and in submodule.c).\n>\n> This re-roll is on top of the just-now re-rolled v4 of the base\n> topic[1].\n>\n> Changes in v7:\n>\n>  * Rebase on top of the base topic.\n>  * Clarify some questions on v6 in updated commit messages.\n>  * Go with Glen's suggested \"displaypath\" fix. I also tried another\n>    way which the commit discusses. The end result was better, but it\n>    requires further submodule--helper cleanups, and this is already\n>    quite an Odyssey. So FREE_AND_NULL() will do.\n>\n> 1. https://lore.kernel.org/git/cover-v4-00.33-00000000000-20220831T230519Z-avarab@gmail.com\n>\n> This series & passing CI can be seen at:\n> https://github.com/avar/git/tree/avar/submodule--helper-memory-leaks-7\n\nThanks! And thanks especially for dealing with my stream of comments :)\n\nThis series + the prep series LGTM.\n\n  Reviewed-by: Glen Choo <chooglen@google.com>\n"}]}