git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/11] submodule--helper: fix memory leaks
@ 2022-07-13 13:16 Ævar Arnfjörð Bjarmason
  2022-07-13 13:16 ` [PATCH 01/11] submodule.c: free() memory from xgetcwd() Ævar Arnfjörð Bjarmason
                   ` (11 more replies)
  0 siblings, 12 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-13 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

This series fixes most of the memory leaks in
builtin/submodule--helper.c, it needs to go on top of
gc/submodule-use-super-prefix (which in turn goes on top of my
ab/submodule-cleanup.

I'm planning to submit the patches to "git rm git-submodule.sh" after
this series, but fixing these leaks gives us a much better foundation
for doing so.

I.e. we'll eventually want to have this code invoke itself without
needing run_command(), and being able to have clear lifecycle
management of the data it uses makes it possible to eventually call
the relevant code as a utility function instead.

Ævar Arnfjörð Bjarmason (11):
  submodule.c: free() memory from xgetcwd()
  submodule--helper: replace memset() with { 0 }-initialization
  submodule--helper: fix "module_clone_data" memory leaks
  submodule--helper: fix "struct pathspec" memory leaks
  submodule--helper: free() leaking {run,capture}_command() argument
  submodule--helper: add and use *_release() functions
  submodule--helper: fix "errmsg_str" memory leak
  submodule--helper: fix "sm_path" and other "module_cb_list" leaks
  submodule--helper: free "char *" in "struct update_data"
  submodule--helper: fix a leak with repo_clear()
  submodule--helper:fix "reference" leak is "module_clone_data"

Ævar Arnfjörð Bjarmason (11):
  submodule.c: free() memory from xgetcwd()
  submodule--helper: replace memset() with { 0 }-initialization
  submodule--helper: fix "module_clone_data" memory leaks
  submodule--helper: fix "struct pathspec" memory leaks
  submodule--helper: free() leaking {run,capture}_command() argument
  submodule--helper: add and use *_release() functions
  submodule--helper: fix "errmsg_str" memory leak
  submodule--helper: fix "sm_path" and other "module_cb_list" leaks
  submodule--helper: free "char *" in "struct update_data"
  submodule--helper: fix a leak with repo_clear()
  submodule--helper: fix "reference" leak is "module_clone_data"

 builtin/submodule--helper.c        | 260 ++++++++++++++++++++++-------
 submodule.c                        |   3 +-
 t/t2403-worktree-move.sh           |   1 +
 t/t6008-rev-list-submodule.sh      |   1 +
 t/t6134-pathspec-in-submodule.sh   |   1 +
 t/t7412-submodule-absorbgitdirs.sh |   1 +
 t/t7414-submodule-mistakes.sh      |   2 +
 t/t7506-status-submodule.sh        |   1 +
 t/t7507-commit-verbose.sh          |   2 +
 9 files changed, 207 insertions(+), 65 deletions(-)

-- 
2.37.0.932.g7b7031e73bc


^ permalink raw reply	[flat|nested] 186+ messages in thread

* [PATCH 01/11] submodule.c: free() memory from xgetcwd()
  2022-07-13 13:16 [PATCH 00/11] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
@ 2022-07-13 13:16 ` Ævar Arnfjörð Bjarmason
  2022-07-13 13:16 ` [PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization Ævar Arnfjörð Bjarmason
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-13 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak in code added in bf0231c6614 (rev-parse: add
--show-superproject-working-tree, 2017-03-08), we should never have
made the result of xgetcwd() a "const char *", as we return a
strbuf_detach()'d value. Let's fix that and free() it when we're done
with it.

We can't mark any tests passing passing with SANITIZE=leak using
"TEST_PASSES_SANITIZE_LEAK=true" as a result of this change, but
e.g. "t/t1500-rev-parse.sh" now gets closer to passing.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 submodule.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/submodule.c b/submodule.c
index 2af16c647d5..3fa5db3ecdf 100644
--- a/submodule.c
+++ b/submodule.c
@@ -2388,7 +2388,7 @@ int get_superproject_working_tree(struct strbuf *buf)
 	struct child_process cp = CHILD_PROCESS_INIT;
 	struct strbuf sb = STRBUF_INIT;
 	struct strbuf one_up = STRBUF_INIT;
-	const char *cwd = xgetcwd();
+	char *cwd = xgetcwd();
 	int ret = 0;
 	const char *subpath;
 	int code;
@@ -2451,6 +2451,7 @@ int get_superproject_working_tree(struct strbuf *buf)
 		ret = 1;
 		free(super_wt);
 	}
+	free(cwd);
 	strbuf_release(&sb);
 
 	code = finish_command(&cp);
-- 
2.37.0.932.g7b7031e73bc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization
  2022-07-13 13:16 [PATCH 00/11] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  2022-07-13 13:16 ` [PATCH 01/11] submodule.c: free() memory from xgetcwd() Ævar Arnfjörð Bjarmason
@ 2022-07-13 13:16 ` Ævar Arnfjörð Bjarmason
  2022-07-13 21:00   ` Glen Choo
  2022-07-13 13:16 ` [PATCH 03/11] submodule--helper: fix "module_clone_data" memory leaks Ævar Arnfjörð Bjarmason
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-13 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Use the less verbose { 0 }-initialization syntax rather than memset()
in builtin/submodule--helper.c, this doesn't make a difference in
terms of behavior, but as we're about to modify adjacent code makes
this more consistent, and lets us avoid worrying about when the
memset() happens v.s. a "goto cleanup".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index fac52ade5e1..73717be957c 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1744,7 +1744,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 {
 	int dissociate = 0, quiet = 0, progress = 0, require_init = 0;
 	struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
-	struct list_objects_filter_options filter_options;
+	struct list_objects_filter_options filter_options = { 0 };
 
 	struct option module_clone_options[] = {
 		OPT_STRING(0, "prefix", &clone_data.prefix,
@@ -1786,7 +1786,6 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 		NULL
 	};
 
-	memset(&filter_options, 0, sizeof(filter_options));
 	argc = parse_options(argc, argv, prefix, module_clone_options,
 			     git_submodule_helper_usage, 0);
 
@@ -2563,7 +2562,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 {
 	struct pathspec pathspec;
 	struct update_data opt = UPDATE_DATA_INIT;
-	struct list_objects_filter_options filter_options;
+	struct list_objects_filter_options filter_options = { 0 };
 	int ret;
 
 	struct option module_update_options[] = {
@@ -2623,7 +2622,6 @@ static int module_update(int argc, const char **argv, const char *prefix)
 	update_clone_config_from_gitmodules(&opt.max_jobs);
 	git_config(git_update_clone_config, &opt.max_jobs);
 
-	memset(&filter_options, 0, sizeof(filter_options));
 	argc = parse_options(argc, argv, prefix, module_update_options,
 			     git_submodule_helper_usage, 0);
 
-- 
2.37.0.932.g7b7031e73bc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH 03/11] submodule--helper: fix "module_clone_data" memory leaks
  2022-07-13 13:16 [PATCH 00/11] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  2022-07-13 13:16 ` [PATCH 01/11] submodule.c: free() memory from xgetcwd() Ævar Arnfjörð Bjarmason
  2022-07-13 13:16 ` [PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization Ævar Arnfjörð Bjarmason
@ 2022-07-13 13:16 ` Ævar Arnfjörð Bjarmason
  2022-07-13 17:37   ` Glen Choo
  2022-07-13 13:16 ` [PATCH 04/11] submodule--helper: fix "struct pathspec" " Ævar Arnfjörð Bjarmason
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-13 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix memory leaks related to the "struct module_clone_data" by creating
a module_clone_data_release() function to go with the
MODULE_CLONE_DATA_INIT added in a98b02c1128 (submodule--helper:
refactor module_clone(), 2021-07-10).

The "path" member can come from "argv" (i.e. not malloc'd), or it can
be something we determine at runtime. In the latter case let's save
away a pointer to free() to avoid leaking memory.

Fixing this leak makes several tests pass, so let's mark them as
passing with TEST_PASSES_SANITIZE_LEAK=true.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c   | 24 +++++++++++++++++++-----
 t/t6008-rev-list-submodule.sh |  1 +
 t/t7414-submodule-mistakes.sh |  2 ++
 t/t7506-status-submodule.sh   |  1 +
 t/t7507-commit-verbose.sh     |  2 ++
 5 files changed, 25 insertions(+), 5 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 73717be957c..23ab9c7e349 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1511,6 +1511,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 struct module_clone_data {
 	const char *prefix;
 	const char *path;
+	char *path_free;
 	const char *name;
 	const char *url;
 	const char *depth;
@@ -1527,6 +1528,11 @@ struct module_clone_data {
 	.single_branch = -1, \
 }
 
+static void module_clone_data_release(struct module_clone_data *cd)
+{
+	free(cd->path_free);
+}
+
 struct submodule_alternate_setup {
 	const char *submodule_name;
 	enum SUBMODULE_ALTERNATE_ERROR_MODE {
@@ -1651,9 +1657,9 @@ static int clone_submodule(struct module_clone_data *clone_data)
 
 	if (!is_absolute_path(clone_data->path)) {
 		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
-		clone_data->path = strbuf_detach(&sb, NULL);
+		clone_data->path = clone_data->path_free = strbuf_detach(&sb, NULL);
 	} else {
-		clone_data->path = xstrdup(clone_data->path);
+		clone_data->path = clone_data->path_free = xstrdup(clone_data->path);
 	}
 
 	if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)
@@ -1801,6 +1807,8 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 
 	clone_submodule(&clone_data);
 	list_objects_filter_release(&filter_options);
+
+	module_clone_data_release(&clone_data);
 	return 0;
 }
 
@@ -3016,6 +3024,7 @@ static int add_submodule(const struct add_data *add_data)
 {
 	char *submod_gitdir_path;
 	struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
+	int ret;
 
 	/* perhaps the path already exists and is already a git repo, else clone it */
 	if (is_directory(add_data->sm_path)) {
@@ -3077,8 +3086,10 @@ static int add_submodule(const struct add_data *add_data)
 		if (add_data->depth >= 0)
 			clone_data.depth = xstrfmt("%d", add_data->depth);
 
-		if (clone_submodule(&clone_data))
-			return -1;
+		if (clone_submodule(&clone_data)) {
+			ret = -1;
+			goto cleanup;
+		}
 
 		prepare_submodule_repo_env(&cp.env);
 		cp.git_cmd = 1;
@@ -3097,7 +3108,10 @@ static int add_submodule(const struct add_data *add_data)
 		if (run_command(&cp))
 			die(_("unable to checkout submodule '%s'"), add_data->sm_path);
 	}
-	return 0;
+	ret = 0;
+cleanup:
+	module_clone_data_release(&clone_data);
+	return ret;
 }
 
 static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
diff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh
index 3153a0d8910..12e67e187ef 100755
--- a/t/t6008-rev-list-submodule.sh
+++ b/t/t6008-rev-list-submodule.sh
@@ -8,6 +8,7 @@ test_description='git rev-list involving submodules that this repo has'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh
index f2e7df59cf2..3269298197c 100755
--- a/t/t7414-submodule-mistakes.sh
+++ b/t/t7414-submodule-mistakes.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='handling of common mistakes people may make with submodules'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create embedded repository' '
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index 3fcb44767f5..f5426a8e589 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='git status for submodule'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_create_repo_with_commit () {
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index ed2653d46fe..92462a22374 100755
--- a/t/t7507-commit-verbose.sh
+++ b/t/t7507-commit-verbose.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='verbose commit template'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 write_script "check-for-diff" <<\EOF &&
-- 
2.37.0.932.g7b7031e73bc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH 04/11] submodule--helper: fix "struct pathspec" memory leaks
  2022-07-13 13:16 [PATCH 00/11] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                   ` (2 preceding siblings ...)
  2022-07-13 13:16 ` [PATCH 03/11] submodule--helper: fix "module_clone_data" memory leaks Ævar Arnfjörð Bjarmason
@ 2022-07-13 13:16 ` Ævar Arnfjörð Bjarmason
  2022-07-13 13:16 ` [PATCH 05/11] submodule--helper: free() leaking {run,capture}_command() argument Ævar Arnfjörð Bjarmason
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-13 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Call clear_pathspec() at the end of various functions that work with
and allocate a "struct pathspec".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 115 +++++++++++++++++++++++++-----------
 1 file changed, 81 insertions(+), 34 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 23ab9c7e349..a8e439e59b8 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -269,7 +269,7 @@ static char *get_up_path(const char *path)
 static int module_list(int argc, const char **argv, const char *prefix)
 {
 	int i;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 
 	struct option module_list_options[] = {
@@ -278,6 +278,7 @@ static int module_list(int argc, const char **argv, const char *prefix)
 			   N_("alternative anchor for relative paths")),
 		OPT_END()
 	};
+	int ret;
 
 	const char *const git_submodule_helper_usage[] = {
 		N_("git submodule--helper list [--prefix=<path>] [<path>...]"),
@@ -287,8 +288,10 @@ static int module_list(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, module_list_options,
 			     git_submodule_helper_usage, 0);
 
-	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
+		ret = 1;
+		goto cleanup;
+	}
 
 	for (i = 0; i < list.nr; i++) {
 		const struct cache_entry *ce = list.entries[i];
@@ -302,7 +305,10 @@ static int module_list(int argc, const char **argv, const char *prefix)
 
 		fprintf(stdout, "%s\n", ce->name);
 	}
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static void for_each_listed_submodule(const struct module_list *list,
@@ -427,7 +433,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
 static int module_foreach(int argc, const char **argv, const char *prefix)
 {
 	struct foreach_cb info = FOREACH_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 
 	struct option module_foreach_options[] = {
@@ -441,12 +447,15 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 		N_("git submodule foreach [--quiet] [--recursive] [--] <command>"),
 		NULL
 	};
+	int ret;
 
 	argc = parse_options(argc, argv, prefix, module_foreach_options,
 			     git_submodule_helper_usage, 0);
 
-	if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
-		return 1;
+	if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0) {
+		ret = 1;
+		goto cleanup;
+	}
 
 	info.argc = argc;
 	info.argv = argv;
@@ -454,7 +463,10 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static int starts_with_dot_slash(const char *const path)
@@ -562,7 +574,7 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data
 static int module_init(int argc, const char **argv, const char *prefix)
 {
 	struct init_cb info = INIT_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 
@@ -575,12 +587,15 @@ static int module_init(int argc, const char **argv, const char *prefix)
 		N_("git submodule init [<options>] [<path>]"),
 		NULL
 	};
+	int ret;
 
 	argc = parse_options(argc, argv, prefix, module_init_options,
 			     git_submodule_helper_usage, 0);
 
-	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
+		ret = 1;
+		goto cleanup;
+	}
 
 	/*
 	 * If there are no path args and submodule.active is set then,
@@ -595,7 +610,10 @@ static int module_init(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, init_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct status_cb {
@@ -740,7 +758,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,
 static int module_status(int argc, const char **argv, const char *prefix)
 {
 	struct status_cb info = STATUS_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 
@@ -755,12 +773,15 @@ static int module_status(int argc, const char **argv, const char *prefix)
 		N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"),
 		NULL
 	};
+	int ret;
 
 	argc = parse_options(argc, argv, prefix, module_status_options,
 			     git_submodule_helper_usage, 0);
 
-	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
+		ret = 1;
+		goto cleanup;
+	}
 
 	info.prefix = prefix;
 	if (quiet)
@@ -768,7 +789,10 @@ static int module_status(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, status_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static int module_name(int argc, const char **argv, const char *prefix)
@@ -1105,7 +1129,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 	struct strvec diff_args = STRVEC_INIT;
 	struct rev_info rev;
 	struct module_cb_list list = MODULE_CB_LIST_INIT;
-	int ret = 0;
+	int ret;
 
 	strvec_push(&diff_args, get_diff_cmd(diff_cmd));
 	if (info->cached)
@@ -1145,6 +1169,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 	else
 		run_diff_files(&rev, 0);
 	prepare_submodule_summary(info, &list);
+	ret = 0;
 cleanup:
 	strvec_clear(&diff_args);
 	release_revisions(&rev);
@@ -1326,10 +1351,11 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data
 static int module_sync(int argc, const char **argv, const char *prefix)
 {
 	struct sync_cb info = SYNC_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	int recursive = 0;
+	int ret;
 
 	struct option module_sync_options[] = {
 		OPT__QUIET(&quiet, N_("suppress output of synchronizing submodule url")),
@@ -1346,8 +1372,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, module_sync_options,
 			     git_submodule_helper_usage, 0);
 
-	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
+		ret = 1;
+		goto cleanup;
+	}
 
 	info.prefix = prefix;
 	if (quiet)
@@ -1357,7 +1385,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, sync_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct deinit_cb {
@@ -1464,7 +1495,7 @@ static void deinit_submodule_cb(const struct cache_entry *list_item,
 static int module_deinit(int argc, const char **argv, const char *prefix)
 {
 	struct deinit_cb info = DEINIT_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	int force = 0;
@@ -1481,6 +1512,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 		N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
 		NULL
 	};
+	int ret;
 
 	argc = parse_options(argc, argv, prefix, module_deinit_options,
 			     git_submodule_helper_usage, 0);
@@ -1494,8 +1526,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 	if (!argc && !all)
 		die(_("Use '--all' if you really want to deinitialize all submodules"));
 
-	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
+		ret = 1;
+		goto cleanup;
+	}
 
 	info.prefix = prefix;
 	if (quiet)
@@ -1505,7 +1539,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, deinit_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct module_clone_data {
@@ -2568,7 +2605,7 @@ static int update_submodules(struct update_data *update_data)
 
 static int module_update(int argc, const char **argv, const char *prefix)
 {
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct update_data opt = UPDATE_DATA_INIT;
 	struct list_objects_filter_options filter_options = { 0 };
 	int ret;
@@ -2647,8 +2684,8 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		opt.update_strategy.type = opt.update_default;
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {
-		list_objects_filter_release(&filter_options);
-		return 1;
+		ret = 1;
+		goto cleanup;
 	}
 
 	if (pathspec.nr)
@@ -2659,8 +2696,10 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		struct init_cb info = INIT_CB_INIT;
 
 		if (module_list_compute(argc, argv, opt.prefix,
-					&pathspec, &list) < 0)
-			return 1;
+					&pathspec, &list) < 0) {
+			ret = 1;
+			goto cleanup;
+		}
 
 		/*
 		 * If there are no path args and submodule.active is set then,
@@ -2677,7 +2716,9 @@ static int module_update(int argc, const char **argv, const char *prefix)
 	}
 
 	ret = update_submodules(&opt);
+cleanup:
 	list_objects_filter_release(&filter_options);
+	clear_pathspec(&pathspec);
 	return ret;
 }
 
@@ -2761,9 +2802,10 @@ static int push_check(int argc, const char **argv, const char *prefix)
 static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 {
 	int i;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;
+	int ret;
 
 	struct option embed_gitdir_options[] = {
 		OPT_STRING(0, "prefix", &prefix,
@@ -2782,13 +2824,18 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, embed_gitdir_options,
 			     git_submodule_helper_usage, 0);
 
-	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
+		ret = 1;
+		goto cleanup;
+	}
 
 	for (i = 0; i < list.nr; i++)
 		absorb_git_dir_into_superproject(list.entries[i]->name, flags);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static int is_active(int argc, const char **argv, const char *prefix)
-- 
2.37.0.932.g7b7031e73bc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH 05/11] submodule--helper: free() leaking {run,capture}_command() argument
  2022-07-13 13:16 [PATCH 00/11] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                   ` (3 preceding siblings ...)
  2022-07-13 13:16 ` [PATCH 04/11] submodule--helper: fix "struct pathspec" " Ævar Arnfjörð Bjarmason
@ 2022-07-13 13:16 ` Ævar Arnfjörð Bjarmason
  2022-07-14  3:36   ` Glen Choo
  2022-07-13 13:16 ` [PATCH 06/11] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-13 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Free the "dir" member of "struct child_process" that various functions
in submodule-helper.c allocate allocates with xstrdup().

Since the "dir" argument is "const char *" let's keep a
"char *to_free" variable around for this rather than casting when we
call free().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 41 +++++++++++++++++++++++++++----------
 1 file changed, 30 insertions(+), 11 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index a8e439e59b8..2099c5774b2 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2198,27 +2198,36 @@ static int is_tip_reachable(const char *path, struct object_id *oid)
 	struct child_process cp = CHILD_PROCESS_INIT;
 	struct strbuf rev = STRBUF_INIT;
 	char *hex = oid_to_hex(oid);
+	char *to_free;
+	int ret;
 
 	cp.git_cmd = 1;
-	cp.dir = xstrdup(path);
+	cp.dir = to_free = xstrdup(path);
 	cp.no_stderr = 1;
 	strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL);
 
 	prepare_submodule_repo_env(&cp.env);
 
-	if (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len)
-		return 0;
+	if (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len) {
+		ret = 0;
+		goto cleanup;
+	}
 
-	return 1;
+	ret = 1;
+cleanup:
+	free(to_free);
+	return ret;
 }
 
 static int fetch_in_submodule(const char *module_path, int depth, int quiet, struct object_id *oid)
 {
 	struct child_process cp = CHILD_PROCESS_INIT;
+	char *to_free;
+	int ret;
 
 	prepare_submodule_repo_env(&cp.env);
 	cp.git_cmd = 1;
-	cp.dir = xstrdup(module_path);
+	cp.dir = to_free = xstrdup(module_path);
 
 	strvec_push(&cp.args, "fetch");
 	if (quiet)
@@ -2232,7 +2241,9 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str
 		free(remote);
 	}
 
-	return run_command(&cp);
+	ret = run_command(&cp);
+	free(to_free);
+	return ret;
 }
 
 static int run_update_command(struct update_data *ud, int subforce)
@@ -2240,6 +2251,8 @@ static int run_update_command(struct update_data *ud, int subforce)
 	struct child_process cp = CHILD_PROCESS_INIT;
 	char *oid = oid_to_hex(&ud->oid);
 	int must_die_on_failure = 0;
+	char *to_free;
+	int ret;
 
 	switch (ud->update_strategy.type) {
 	case SM_UPDATE_CHECKOUT:
@@ -2273,7 +2286,7 @@ static int run_update_command(struct update_data *ud, int subforce)
 	}
 	strvec_push(&cp.args, oid);
 
-	cp.dir = xstrdup(ud->sm_path);
+	cp.dir = to_free = xstrdup(ud->sm_path);
 	prepare_submodule_repo_env(&cp.env);
 	if (run_command(&cp)) {
 		switch (ud->update_strategy.type) {
@@ -2301,11 +2314,14 @@ static int run_update_command(struct update_data *ud, int subforce)
 			exit(128);
 
 		/* the command failed, but update must continue */
-		return 1;
+		ret = 1;
+		goto cleanup;
 	}
 
-	if (ud->quiet)
-		return 0;
+	if (ud->quiet) {
+		ret = 0;
+		goto cleanup;
+	}
 
 	switch (ud->update_strategy.type) {
 	case SM_UPDATE_CHECKOUT:
@@ -2329,7 +2345,10 @@ static int run_update_command(struct update_data *ud, int subforce)
 		    submodule_strategy_to_string(&ud->update_strategy));
 	}
 
-	return 0;
+	ret = 0;
+cleanup:
+	free(to_free);
+	return ret;
 }
 
 static int run_update_procedure(struct update_data *ud)
-- 
2.37.0.932.g7b7031e73bc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH 06/11] submodule--helper: add and use *_release() functions
  2022-07-13 13:16 [PATCH 00/11] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                   ` (4 preceding siblings ...)
  2022-07-13 13:16 ` [PATCH 05/11] submodule--helper: free() leaking {run,capture}_command() argument Ævar Arnfjörð Bjarmason
@ 2022-07-13 13:16 ` Ævar Arnfjörð Bjarmason
  2022-07-14 18:11   ` Glen Choo
  2022-07-13 13:16 ` [PATCH 07/11] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-13 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Add release functions for "struct module_list", "struct
submodule_update_clone" and "struct update_data". For now we're
leaving some of the "struct update_data" members, we'll deal with
those in a subsequent commit.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c      | 30 +++++++++++++++++++++++++++++-
 t/t6134-pathspec-in-submodule.sh |  1 +
 2 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 2099c5774b2..a964dbeec38 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -182,6 +182,11 @@ struct module_list {
 };
 #define MODULE_LIST_INIT { 0 }
 
+static void module_list_release(struct module_list *ml)
+{
+	free(ml->entries);
+}
+
 static int module_list_compute(int argc, const char **argv,
 			       const char *prefix,
 			       struct pathspec *pathspec,
@@ -243,7 +248,7 @@ static void module_list_active(struct module_list *list)
 		active_modules.entries[active_modules.nr++] = ce;
 	}
 
-	free(list->entries);
+	module_list_release(list);
 	*list = active_modules;
 }
 
@@ -307,6 +312,7 @@ static int module_list(int argc, const char **argv, const char *prefix)
 	}
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -465,6 +471,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -612,6 +619,7 @@ static int module_init(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -791,6 +799,7 @@ static int module_status(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1387,6 +1396,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1541,6 +1551,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1910,6 +1921,13 @@ struct submodule_update_clone {
 };
 #define SUBMODULE_UPDATE_CLONE_INIT { 0 }
 
+static void submodule_update_clone_release(struct submodule_update_clone *suc)
+{
+	/* Not "update_data", owned by update_data_release() */
+	free(suc->update_clone);
+	free(suc->failed_clones);
+}
+
 struct update_data {
 	const char *prefix;
 	const char *displaypath;
@@ -1948,6 +1966,11 @@ struct update_data {
 	.max_jobs = 1, \
 }
 
+static void update_data_release(struct update_data *ud)
+{
+	module_list_release(&ud->list);
+}
+
 static void next_submodule_warn_missing(struct submodule_update_clone *suc,
 		struct strbuf *out, const char *displaypath)
 {
@@ -2618,6 +2641,7 @@ static int update_submodules(struct update_data *update_data)
 	}
 
 cleanup:
+	submodule_update_clone_release(&suc);
 	string_list_clear(&update_data->references, 0);
 	return res;
 }
@@ -2716,6 +2740,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 
 		if (module_list_compute(argc, argv, opt.prefix,
 					&pathspec, &list) < 0) {
+			module_list_release(&list);
 			ret = 1;
 			goto cleanup;
 		}
@@ -2732,10 +2757,12 @@ static int module_update(int argc, const char **argv, const char *prefix)
 			info.flags |= OPT_QUIET;
 
 		for_each_listed_submodule(&list, init_submodule_cb, &info);
+		module_list_release(&list);
 	}
 
 	ret = update_submodules(&opt);
 cleanup:
+	update_data_release(&opt);
 	list_objects_filter_release(&filter_options);
 	clear_pathspec(&pathspec);
 	return ret;
@@ -2854,6 +2881,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 	ret = 0;
 cleanup:
 	clear_pathspec(&pathspec);
+	module_list_release(&list);
 	return ret;
 }
 
diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh
index 0f1cb49cedc..3a241f259de 100755
--- a/t/t6134-pathspec-in-submodule.sh
+++ b/t/t6134-pathspec-in-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='test case exclude pathspec'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a submodule' '
-- 
2.37.0.932.g7b7031e73bc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH 07/11] submodule--helper: fix "errmsg_str" memory leak
  2022-07-13 13:16 [PATCH 00/11] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                   ` (5 preceding siblings ...)
  2022-07-13 13:16 ` [PATCH 06/11] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
@ 2022-07-13 13:16 ` Ævar Arnfjörð Bjarmason
  2022-07-14 18:20   ` Glen Choo
  2022-07-13 13:16 ` [PATCH 08/11] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-13 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak introduced in e83e3333b57 (submodule: port submodule
subcommand 'summary' from shell to C, 2020-08-13), to do that stop
juggling around the "errmsg" and "struct strbuf", let's instead move
the "struct strbuf errmsg" to the top-level.

Now we don't need to strbuf_detach() it anymore, but we do need to
ensure that we pass NULL to print_submodule_summary() when we have no
error message.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index a964dbeec38..a05578a7382 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -932,7 +932,8 @@ static void generate_submodule_summary(struct summary_cb *info,
 {
 	char *displaypath, *src_abbrev = NULL, *dst_abbrev;
 	int missing_src = 0, missing_dst = 0;
-	char *errmsg = NULL;
+	char *errmsg;
+	struct strbuf errmsg_str = STRBUF_INIT;
 	int total_commits = -1;
 
 	if (!info->cached && oideq(&p->oid_dst, null_oid())) {
@@ -1032,7 +1033,6 @@ static void generate_submodule_summary(struct summary_cb *info,
 		 * submodule, i.e., deleted or changed to blob
 		 */
 		if (S_ISGITLINK(p->mod_dst)) {
-			struct strbuf errmsg_str = STRBUF_INIT;
 			if (missing_src && missing_dst) {
 				strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commits %s and %s\n",
 					    displaypath, oid_to_hex(&p->oid_src),
@@ -1043,10 +1043,10 @@ static void generate_submodule_summary(struct summary_cb *info,
 					    oid_to_hex(&p->oid_src) :
 					    oid_to_hex(&p->oid_dst));
 			}
-			errmsg = strbuf_detach(&errmsg_str, NULL);
 		}
 	}
 
+	errmsg = errmsg_str.len ? errmsg_str.buf : NULL;
 	print_submodule_summary(info, errmsg, total_commits,
 				displaypath, src_abbrev,
 				dst_abbrev, p);
@@ -1054,6 +1054,7 @@ static void generate_submodule_summary(struct summary_cb *info,
 	free(displaypath);
 	free(src_abbrev);
 	free(dst_abbrev);
+	strbuf_release(&errmsg_str);
 }
 
 static void prepare_submodule_summary(struct summary_cb *info,
-- 
2.37.0.932.g7b7031e73bc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH 08/11] submodule--helper: fix "sm_path" and other "module_cb_list" leaks
  2022-07-13 13:16 [PATCH 00/11] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                   ` (6 preceding siblings ...)
  2022-07-13 13:16 ` [PATCH 07/11] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
@ 2022-07-13 13:16 ` Ævar Arnfjörð Bjarmason
  2022-07-13 13:16 ` [PATCH 09/11] submodule--helper: free "char *" in "struct update_data" Ævar Arnfjörð Bjarmason
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-13 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix leaks in "struct module_cb_list" and the "struct module_cb" which
it contains, these fixes leaks in e83e3333b57 (submodule: port
submodule subcommand 'summary' from shell to C, 2020-08-13).

The "sm_path" should always have been a "char *", not a "const
char *", we always create it with xstrdup().

We can't mark any tests passing passing with SANITIZE=leak using
"TEST_PASSES_SANITIZE_LEAK=true" as a result of this change, but
"t7401-submodule-summary.sh" gets closer to passing as a result of
this change.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index a05578a7382..2b44f391f15 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -828,9 +828,13 @@ struct module_cb {
 	struct object_id oid_src;
 	struct object_id oid_dst;
 	char status;
-	const char *sm_path;
+	char *sm_path;
 };
 #define MODULE_CB_INIT { 0 }
+static void module_cb_release(struct module_cb *mcb)
+{
+	free(mcb->sm_path);
+}
 
 struct module_cb_list {
 	struct module_cb **entries;
@@ -838,6 +842,19 @@ struct module_cb_list {
 };
 #define MODULE_CB_LIST_INIT { 0 }
 
+static void module_cb_list_release(struct module_cb_list *mcbl)
+{
+	int i;
+
+	for (i = 0; i < mcbl->nr; i++) {
+		struct module_cb *mcb = mcbl->entries[i];
+
+		module_cb_release(mcb);
+		free(mcb);
+	}
+	free(mcbl->entries);
+}
+
 struct summary_cb {
 	int argc;
 	const char **argv;
@@ -1183,6 +1200,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 cleanup:
 	strvec_clear(&diff_args);
 	release_revisions(&rev);
+	module_cb_list_release(&list);
 	return ret;
 }
 
-- 
2.37.0.932.g7b7031e73bc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH 09/11] submodule--helper: free "char *" in "struct update_data"
  2022-07-13 13:16 [PATCH 00/11] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                   ` (7 preceding siblings ...)
  2022-07-13 13:16 ` [PATCH 08/11] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
@ 2022-07-13 13:16 ` Ævar Arnfjörð Bjarmason
  2022-07-14 18:33   ` Glen Choo
  2022-07-13 13:16 ` [PATCH 10/11] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-13 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Make the update_data_release() function free the "recursive_prefix"
and "displaypath" members when appropriate. For the former it could
come from either "argv" or from our own allocation, so we need to keep
track of a "to_free" sibling seperately.

For "displaypath" it's always ours, so the "const char *" type was
wrong to begin with, it should be a "char *" instead.

For update_submodule() we'll free() these as we go along, it's called
in a loop by update_submodules(), and we'll need to free the "last"
one.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 2b44f391f15..0bac39880d2 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1949,7 +1949,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)
 
 struct update_data {
 	const char *prefix;
-	const char *displaypath;
+	char *displaypath;
 	enum submodule_update_type update_default;
 	struct object_id suboid;
 	struct string_list references;
@@ -1987,6 +1987,7 @@ struct update_data {
 
 static void update_data_release(struct update_data *ud)
 {
+	free(ud->displaypath);
 	module_list_release(&ud->list);
 }
 
-- 
2.37.0.932.g7b7031e73bc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH 10/11] submodule--helper: fix a leak with repo_clear()
  2022-07-13 13:16 [PATCH 00/11] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                   ` (8 preceding siblings ...)
  2022-07-13 13:16 ` [PATCH 09/11] submodule--helper: free "char *" in "struct update_data" Ævar Arnfjörð Bjarmason
@ 2022-07-13 13:16 ` Ævar Arnfjörð Bjarmason
  2022-07-13 13:16 ` [PATCH 11/11] submodule--helper: fix "reference" leak is "module_clone_data" Ævar Arnfjörð Bjarmason
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  11 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-13 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Call repo_clear() in ensure_core_worktree() to free the "struct
repository". Fixes a leak that's been here since
74d4731da1f (submodule--helper: replace connect-gitdir-workingtree by
ensure-core-worktree, 2018-08-13).

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c        | 1 +
 t/t2403-worktree-move.sh           | 1 +
 t/t7412-submodule-absorbgitdirs.sh | 1 +
 3 files changed, 3 insertions(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 0bac39880d2..d9fab5d77da 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2486,6 +2486,7 @@ static void ensure_core_worktree(const char *path)
 		free(abs_path);
 		strbuf_release(&sb);
 	}
+	repo_clear(&subrepo);
 }
 
 static const char *submodule_update_type_to_label(enum submodule_update_type type)
diff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh
index a4e1a178e0a..1168e9f9982 100755
--- a/t/t2403-worktree-move.sh
+++ b/t/t2403-worktree-move.sh
@@ -2,6 +2,7 @@
 
 test_description='test git worktree move, remove, lock and unlock'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh
index 1cfa150768d..2859695c6d2 100755
--- a/t/t7412-submodule-absorbgitdirs.sh
+++ b/t/t7412-submodule-absorbgitdirs.sh
@@ -6,6 +6,7 @@ This test verifies that `git submodue absorbgitdirs` moves a submodules git
 directory into the superproject.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a real submodule' '
-- 
2.37.0.932.g7b7031e73bc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH 11/11] submodule--helper: fix "reference" leak is "module_clone_data"
  2022-07-13 13:16 [PATCH 00/11] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                   ` (9 preceding siblings ...)
  2022-07-13 13:16 ` [PATCH 10/11] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
@ 2022-07-13 13:16 ` Ævar Arnfjörð Bjarmason
  2022-07-14 18:42   ` Glen Choo
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  11 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-13 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix leaks in the "reference" member of "struct module_clone_data" that
have been with us since 31224cbdc72 (clone: recursive and reference
option triggers submodule alternates, 2016-08-17) and
8c8195e9c3e (submodule--helper: introduce add-clone subcommand,
2021-07-10).

Those commits added an xstrdup()'d member of the
STRING_LIST_INIT_NODUP'd "struct string_list". We need to free()
those, but not the ones we get from argv, let's make use of the "util"
member, if it has a pointer it's the pointer we'll need to free.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d9fab5d77da..966f543fbd9 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1598,6 +1598,7 @@ struct module_clone_data {
 static void module_clone_data_release(struct module_clone_data *cd)
 {
 	free(cd->path_free);
+	string_list_clear(&cd->reference, 1);
 }
 
 struct submodule_alternate_setup {
@@ -1652,7 +1653,9 @@ static int add_possible_reference_from_superproject(
 
 		sm_alternate = compute_alternate_path(sb.buf, &err);
 		if (sm_alternate) {
-			string_list_append(sas->reference, xstrdup(sb.buf));
+			char *p = strbuf_detach(&sb, NULL);
+
+			string_list_append(sas->reference, p)->util = p;
 			free(sm_alternate);
 		} else {
 			switch (sas->error_mode) {
@@ -3194,9 +3197,11 @@ static int add_submodule(const struct add_data *add_data)
 		clone_data.url = add_data->realrepo;
 		clone_data.quiet = add_data->quiet;
 		clone_data.progress = add_data->progress;
-		if (add_data->reference_path)
-			string_list_append(&clone_data.reference,
-					   xstrdup(add_data->reference_path));
+		if (add_data->reference_path) {
+			char *p = xstrdup(add_data->reference_path);
+
+			string_list_append(&clone_data.reference, p)->util = p;
+		}
 		clone_data.dissociate = add_data->dissociate;
 		if (add_data->depth >= 0)
 			clone_data.depth = xstrfmt("%d", add_data->depth);
-- 
2.37.0.932.g7b7031e73bc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* Re: [PATCH 03/11] submodule--helper: fix "module_clone_data" memory leaks
  2022-07-13 13:16 ` [PATCH 03/11] submodule--helper: fix "module_clone_data" memory leaks Ævar Arnfjörð Bjarmason
@ 2022-07-13 17:37   ` Glen Choo
  2022-07-13 18:05     ` Glen Choo
  0 siblings, 1 reply; 186+ messages in thread
From: Glen Choo @ 2022-07-13 17:37 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> The "path" member can come from "argv" (i.e. not malloc'd), or it can
> be something we determine at runtime. In the latter case let's save
> away a pointer to free() to avoid leaking memory.

[...]

> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 73717be957c..23ab9c7e349 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -1511,6 +1511,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
>  struct module_clone_data {
>  	const char *prefix;
>  	const char *path;
> +	char *path_free;
>  	const char *name;
>  	const char *url;
>  	const char *depth;
> @@ -1527,6 +1528,11 @@ struct module_clone_data {
>  	.single_branch = -1, \
>  }
>  
> +static void module_clone_data_release(struct module_clone_data *cd)
> +{
> +	free(cd->path_free);
> +}
> +
>  struct submodule_alternate_setup {
>  	const char *submodule_name;
>  	enum SUBMODULE_ALTERNATE_ERROR_MODE {
> @@ -1651,9 +1657,9 @@ static int clone_submodule(struct module_clone_data *clone_data)
>  
>  	if (!is_absolute_path(clone_data->path)) {
>  		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
> -		clone_data->path = strbuf_detach(&sb, NULL);
> +		clone_data->path = clone_data->path_free = strbuf_detach(&sb, NULL);
>  	} else {
> -		clone_data->path = xstrdup(clone_data->path);
> +		clone_data->path = clone_data->path_free = xstrdup(clone_data->path);
>  	}

Hm, having .path_free doesn't seem necessary. If I'm reading the code
correctly,

- in module_clone() we set clone_data.path from argv
- then we unconditionally call clone_submodule(), which does all of the
  hard work
- in clone_submodule(), we always hit this conditional, which means that
  past this point, clone_data.path is always free()-able.

which suggests that we don't need clone_data.path_free at all. I suspect
that just this

   static void module_clone_data_release(struct module_clone_data *cd)
   {
   	free(cd->path);
   }

is enough to plug the leak (though admittedly, I haven't properly tested
the leak because it's been a PITA to set up locally).

That's a pretty hacky suggestion though, since we're still using the
same member to hold free()-able and non-free()-able memory. Instead,
maybe we could move this "clone_data.path = freeable" logic into
module_clone(), like:

  diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
  index 73717be957..d67d4b9647 100644
  --- a/builtin/submodule--helper.c
  +++ b/builtin/submodule--helper.c
  @@ -1649,13 +1649,6 @@ static int clone_submodule(struct module_clone_data *clone_data)
    sm_gitdir = absolute_pathdup(sb.buf);
    strbuf_reset(&sb);

  -	if (!is_absolute_path(clone_data->path)) {
  -		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
  -		clone_data->path = strbuf_detach(&sb, NULL);
  -	} else {
  -		clone_data->path = xstrdup(clone_data->path);
  -	}
  -
    if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)
      die(_("refusing to create/use '%s' in another submodule's "
            "git dir"), sm_gitdir);
  @@ -1745,12 +1738,13 @@ static int module_clone(int argc, const char **argv, const char *prefix)
    int dissociate = 0, quiet = 0, progress = 0, require_init = 0;
    struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
    struct list_objects_filter_options filter_options = { 0 };
  +	const char *clone_path;

    struct option module_clone_options[] = {
      OPT_STRING(0, "prefix", &clone_data.prefix,
          N_("path"),
          N_("alternative anchor for relative paths")),
  -		OPT_STRING(0, "path", &clone_data.path,
  +		OPT_STRING(0, "path", &clone_path,
          N_("path"),
          N_("where the new submodule will be cloned to")),
      OPT_STRING(0, "name", &clone_data.name,
  @@ -1795,6 +1789,15 @@ static int module_clone(int argc, const char **argv, const char *prefix)
    clone_data.require_init = !!require_init;
    clone_data.filter_options = &filter_options;

  +	if (!is_absolute_path(clone_path)) {
  +		struct strbuf sb = STRBUF_INIT;
  +		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_path);
  +		clone_data.path = strbuf_detach(&sb, NULL);
  +		strbuf_release(&sb);
  +	} else {
  +		clone_data.path = xstrdup(clone_path);
  +	}
  +
    if (argc || !clone_data.url || !clone_data.path || !*(clone_data.path))
      usage_with_options(git_submodule_helper_usage,
            module_clone_options);

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH 03/11] submodule--helper: fix "module_clone_data" memory leaks
  2022-07-13 17:37   ` Glen Choo
@ 2022-07-13 18:05     ` Glen Choo
  2022-07-13 20:30       ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 186+ messages in thread
From: Glen Choo @ 2022-07-13 18:05 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Glen Choo <chooglen@google.com> writes:

> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
>
>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
>> index 73717be957c..23ab9c7e349 100644
>> --- a/builtin/submodule--helper.c
>> +++ b/builtin/submodule--helper.c
>> @@ -1511,6 +1511,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
>>  struct module_clone_data {
>>  	const char *prefix;
>>  	const char *path;
>> +	char *path_free;
>>  	const char *name;
>>  	const char *url;
>>  	const char *depth;
>> @@ -1527,6 +1528,11 @@ struct module_clone_data {
>>  	.single_branch = -1, \
>>  }
>>  
>> +static void module_clone_data_release(struct module_clone_data *cd)
>> +{
>> +	free(cd->path_free);
>> +}
>> +
>>  struct submodule_alternate_setup {
>>  	const char *submodule_name;
>>  	enum SUBMODULE_ALTERNATE_ERROR_MODE {
>> @@ -1651,9 +1657,9 @@ static int clone_submodule(struct module_clone_data *clone_data)
>>  
>>  	if (!is_absolute_path(clone_data->path)) {
>>  		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
>> -		clone_data->path = strbuf_detach(&sb, NULL);
>> +		clone_data->path = clone_data->path_free = strbuf_detach(&sb, NULL);
>>  	} else {
>> -		clone_data->path = xstrdup(clone_data->path);
>> +		clone_data->path = clone_data->path_free = xstrdup(clone_data->path);
>>  	}
>
> Hm, having .path_free doesn't seem necessary. If I'm reading the code
> correctly,
>
> - in module_clone() we set clone_data.path from argv
> - then we unconditionally call clone_submodule(), which does all of the
>   hard work
> - in clone_submodule(), we always hit this conditional, which means that
>   past this point, clone_data.path is always free()-able.
>
> which suggests that we don't need clone_data.path_free at all. I suspect
> that just this
>
>    static void module_clone_data_release(struct module_clone_data *cd)
>    {
>    	free(cd->path);
>    }
>
> is enough to plug the leak (though admittedly, I haven't properly tested
> the leak because it's been a PITA to set up locally).
>
> That's a pretty hacky suggestion though, since we're still using the
> same member to hold free()-able and non-free()-able memory....

Ah, here's a wacky idea (whose feasibility I haven't thought about at
all) that would make things a lot cleaner. If we had something like
OPT_STRING_DUP, that xstrdup()-s the value from argv when we parse it,
then we could drop the "const" from clone_data.path and just free() it.

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH 03/11] submodule--helper: fix "module_clone_data" memory leaks
  2022-07-13 18:05     ` Glen Choo
@ 2022-07-13 20:30       ` Ævar Arnfjörð Bjarmason
  2022-07-13 23:04         ` Glen Choo
  0 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-13 20:30 UTC (permalink / raw)
  To: Glen Choo; +Cc: git, Junio C Hamano, Atharva Raykar, Prathamesh Chavan


On Wed, Jul 13 2022, Glen Choo wrote:

> Glen Choo <chooglen@google.com> writes:
>
>> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
>>
>>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
>>> index 73717be957c..23ab9c7e349 100644
>>> --- a/builtin/submodule--helper.c
>>> +++ b/builtin/submodule--helper.c
>>> @@ -1511,6 +1511,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
>>>  struct module_clone_data {
>>>  	const char *prefix;
>>>  	const char *path;
>>> +	char *path_free;
>>>  	const char *name;
>>>  	const char *url;
>>>  	const char *depth;
>>> @@ -1527,6 +1528,11 @@ struct module_clone_data {
>>>  	.single_branch = -1, \
>>>  }
>>>  
>>> +static void module_clone_data_release(struct module_clone_data *cd)
>>> +{
>>> +	free(cd->path_free);
>>> +}
>>> +
>>>  struct submodule_alternate_setup {
>>>  	const char *submodule_name;
>>>  	enum SUBMODULE_ALTERNATE_ERROR_MODE {
>>> @@ -1651,9 +1657,9 @@ static int clone_submodule(struct module_clone_data *clone_data)
>>>  
>>>  	if (!is_absolute_path(clone_data->path)) {
>>>  		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
>>> -		clone_data->path = strbuf_detach(&sb, NULL);
>>> +		clone_data->path = clone_data->path_free = strbuf_detach(&sb, NULL);
>>>  	} else {
>>> -		clone_data->path = xstrdup(clone_data->path);
>>> +		clone_data->path = clone_data->path_free = xstrdup(clone_data->path);
>>>  	}
>>
>> Hm, having .path_free doesn't seem necessary. If I'm reading the code
>> correctly,
>>
>> - in module_clone() we set clone_data.path from argv
>> - then we unconditionally call clone_submodule(), which does all of the
>>   hard work
>> - in clone_submodule(), we always hit this conditional, which means that
>>   past this point, clone_data.path is always free()-able.
>>
>> which suggests that we don't need clone_data.path_free at all. I suspect
>> that just this
>>
>>    static void module_clone_data_release(struct module_clone_data *cd)
>>    {
>>    	free(cd->path);
>>    }
>>
>> is enough to plug the leak (though admittedly, I haven't properly tested
>> the leak because it's been a PITA to set up locally).
>>
>> That's a pretty hacky suggestion though, since we're still using the
>> same member to hold free()-able and non-free()-able memory....
>
> Ah, here's a wacky idea (whose feasibility I haven't thought about at
> all) that would make things a lot cleaner. If we had something like
> OPT_STRING_DUP, that xstrdup()-s the value from argv when we parse it,
> then we could drop the "const" from clone_data.path and just free() it.

I suppose so, it might make some things simpler, of course at the cost
of needlessly duplicating things.

But we also have various common patterns such as string_lists where some
elements are dup'd, some aren't, and need to deal with that. I think
just having common idioms for tracking the dupe is usually better,
e.g. in the case of a string list stick the pointer to free in "util".

I think in this case the patch as-is is better than your suggestions,
because it's a less fragile pattern, we explicitly mark when we dup
something that's sometimes a dup, and sometimes not.

Whereas if we do it with the xstrdup() at the start it requires more
moving things around, and if we have a new user who parses the same
argument we'll bug out on that free().

What do you think?

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization
  2022-07-13 13:16 ` [PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization Ævar Arnfjörð Bjarmason
@ 2022-07-13 21:00   ` Glen Choo
  2022-07-13 21:19     ` Junio C Hamano
  0 siblings, 1 reply; 186+ messages in thread
From: Glen Choo @ 2022-07-13 21:00 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> Use the less verbose { 0 }-initialization syntax rather than memset()
> in builtin/submodule--helper.c, this doesn't make a difference in
> terms of behavior, but as we're about to modify adjacent code makes
> this more consistent, and lets us avoid worrying about when the
> memset() happens v.s. a "goto cleanup".

Ok. I wonder if we could reduce this kind of churn in the future by
adding this to CodingGuidelines, e.g. "always use { 0 } for stack
variables". Tangentially, do we require { NULL } when the first element
is a pointer? (I'm not sure because this isn't in CodingGuidelines
either AFAICT.)

> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 6 ++----
>  1 file changed, 2 insertions(+), 4 deletions(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index fac52ade5e1..73717be957c 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -1744,7 +1744,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
>  {
>  	int dissociate = 0, quiet = 0, progress = 0, require_init = 0;
>  	struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
> -	struct list_objects_filter_options filter_options;
> +	struct list_objects_filter_options filter_options = { 0 };
>  
>  	struct option module_clone_options[] = {
>  		OPT_STRING(0, "prefix", &clone_data.prefix,
> @@ -1786,7 +1786,6 @@ static int module_clone(int argc, const char **argv, const char *prefix)
>  		NULL
>  	};
>  
> -	memset(&filter_options, 0, sizeof(filter_options));
>  	argc = parse_options(argc, argv, prefix, module_clone_options,
>  			     git_submodule_helper_usage, 0);
>  
> @@ -2563,7 +2562,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
>  {
>  	struct pathspec pathspec;
>  	struct update_data opt = UPDATE_DATA_INIT;
> -	struct list_objects_filter_options filter_options;
> +	struct list_objects_filter_options filter_options = { 0 };
>  	int ret;
>  
>  	struct option module_update_options[] = {
> @@ -2623,7 +2622,6 @@ static int module_update(int argc, const char **argv, const char *prefix)
>  	update_clone_config_from_gitmodules(&opt.max_jobs);
>  	git_config(git_update_clone_config, &opt.max_jobs);
>  
> -	memset(&filter_options, 0, sizeof(filter_options));
>  	argc = parse_options(argc, argv, prefix, module_update_options,
>  			     git_submodule_helper_usage, 0);
>  
> -- 
> 2.37.0.932.g7b7031e73bc

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization
  2022-07-13 21:00   ` Glen Choo
@ 2022-07-13 21:19     ` Junio C Hamano
  2022-07-13 22:41       ` Glen Choo
  0 siblings, 1 reply; 186+ messages in thread
From: Junio C Hamano @ 2022-07-13 21:19 UTC (permalink / raw)
  To: Glen Choo
  Cc: Ævar Arnfjörð Bjarmason, git, Atharva Raykar,
	Prathamesh Chavan

Glen Choo <chooglen@google.com> writes:

> Ok. I wonder if we could reduce this kind of churn in the future by
> adding this to CodingGuidelines, e.g. "always use { 0 } for stack
> variables". Tangentially, do we require { NULL } when the first element
> is a pointer? (I'm not sure because this isn't in CodingGuidelines
> either AFAICT.)

A valiable can legitimately be left uninitialized, or initialized to
a known value that is not zero using designated initializers.  So
saying something like

    When zero-initializing an auto variable that is a struct or
    union in its definition, use "{ 0 }", whether the first member
    in the struct is of a number, a pointer, or a compound type.

may be OK.  I do not think we would want to say "always use X", as
the world is not that simple..

We do favor designated initializers over traditional initialization
in the order of members these days, so something like

    When declaring a struct/union variable or an array with initial
    value to some members or elements, consider using designated
    initializers, instead of listing the values in the order of
    members in the definition of the struct.

would also be good.

Thanks.

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization
  2022-07-13 21:19     ` Junio C Hamano
@ 2022-07-13 22:41       ` Glen Choo
  2022-07-14 10:25         ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 186+ messages in thread
From: Glen Choo @ 2022-07-13 22:41 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Ævar Arnfjörð Bjarmason, git, Atharva Raykar,
	Prathamesh Chavan

Junio C Hamano <gitster@pobox.com> writes:

> Glen Choo <chooglen@google.com> writes:
>
>> Ok. I wonder if we could reduce this kind of churn in the future by
>> adding this to CodingGuidelines, e.g. "always use { 0 } for stack
>> variables". Tangentially, do we require { NULL } when the first element
>> is a pointer? (I'm not sure because this isn't in CodingGuidelines
>> either AFAICT.)
>
> A valiable can legitimately be left uninitialized, or initialized to
> a known value that is not zero using designated initializers.  So
> saying something like
>
>     When zero-initializing an auto variable that is a struct or
>     union in its definition, use "{ 0 }", whether the first member
>     in the struct is of a number, a pointer, or a compound type.
>
> may be OK.  I do not think we would want to say "always use X", as
> the world is not that simple..

Thanks. Also, I made a typo here, I meant to be more specific with
regards to memset(), like:

  When zero-initializing an auto variable that is a struct or
  union in its definition, use "{ 0 }", whether the first member
  in the struct is of a number, a pointer, or a compound type. _Do not
  use memset()._

Unless there really is a legitimate reason to use memset(&my_auto_var, 0
sizeof(my_autovar)) that I've missed?

> We do favor designated initializers over traditional initialization
> in the order of members these days, so something like
>
>     When declaring a struct/union variable or an array with initial
>     value to some members or elements, consider using designated
>     initializers, instead of listing the values in the order of
>     members in the definition of the struct.
>
> would also be good.

Thanks. If I have time I'll send that proposal to CodingGuidelines, or
someone else can send it (I don't mind either way).

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH 03/11] submodule--helper: fix "module_clone_data" memory leaks
  2022-07-13 20:30       ` Ævar Arnfjörð Bjarmason
@ 2022-07-13 23:04         ` Glen Choo
  2022-07-14  0:06           ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 186+ messages in thread
From: Glen Choo @ 2022-07-13 23:04 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Junio C Hamano, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> On Wed, Jul 13 2022, Glen Choo wrote:
>
>> Glen Choo <chooglen@google.com> writes:
>>
>>> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
>>>
>>>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
>>>> index 73717be957c..23ab9c7e349 100644
>>>> --- a/builtin/submodule--helper.c
>>>> +++ b/builtin/submodule--helper.c
>>>> @@ -1511,6 +1511,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
>>>>  struct module_clone_data {
>>>>  	const char *prefix;
>>>>  	const char *path;
>>>> +	char *path_free;
>>>>  	const char *name;
>>>>  	const char *url;
>>>>  	const char *depth;
>>>> @@ -1527,6 +1528,11 @@ struct module_clone_data {
>>>>  	.single_branch = -1, \
>>>>  }
>>>>  
>>>> +static void module_clone_data_release(struct module_clone_data *cd)
>>>> +{
>>>> +	free(cd->path_free);
>>>> +}
>>>> +
>>>>  struct submodule_alternate_setup {
>>>>  	const char *submodule_name;
>>>>  	enum SUBMODULE_ALTERNATE_ERROR_MODE {
>>>> @@ -1651,9 +1657,9 @@ static int clone_submodule(struct module_clone_data *clone_data)
>>>>  
>>>>  	if (!is_absolute_path(clone_data->path)) {
>>>>  		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
>>>> -		clone_data->path = strbuf_detach(&sb, NULL);
>>>> +		clone_data->path = clone_data->path_free = strbuf_detach(&sb, NULL);
>>>>  	} else {
>>>> -		clone_data->path = xstrdup(clone_data->path);
>>>> +		clone_data->path = clone_data->path_free = xstrdup(clone_data->path);
>>>>  	}
>>>
>>> Hm, having .path_free doesn't seem necessary. If I'm reading the code
>>> correctly,
>>>
>>> - in module_clone() we set clone_data.path from argv
>>> - then we unconditionally call clone_submodule(), which does all of the
>>>   hard work
>>> - in clone_submodule(), we always hit this conditional, which means that
>>>   past this point, clone_data.path is always free()-able.
>>>
>>> which suggests that we don't need clone_data.path_free at all. I suspect
>>> that just this
>>>
>>>    static void module_clone_data_release(struct module_clone_data *cd)
>>>    {
>>>    	free(cd->path);
>>>    }
>>>
>>> is enough to plug the leak (though admittedly, I haven't properly tested
>>> the leak because it's been a PITA to set up locally).
>>>
>>> That's a pretty hacky suggestion though, since we're still using the
>>> same member to hold free()-able and non-free()-able memory....
>>
>> Ah, here's a wacky idea (whose feasibility I haven't thought about at
>> all) that would make things a lot cleaner. If we had something like
>> OPT_STRING_DUP, that xstrdup()-s the value from argv when we parse it,
>> then we could drop the "const" from clone_data.path and just free() it.
>
> I suppose so, it might make some things simpler, of course at the cost
> of needlessly duplicating things.
>
> But we also have various common patterns such as string_lists where some
> elements are dup'd, some aren't, and need to deal with that. I think
> just having common idioms for tracking the dupe is usually better,
> e.g. in the case of a string list stick the pointer to free in "util".

Hm, sounds fair. "Sometimes dup and sometimes not" sounds like an
inevitability. I'm not experienced enough to know better, and folks
whose opinion I sought seem to agree with you.

> I think in this case the patch as-is is better than your suggestions,
> because it's a less fragile pattern, we explicitly mark when we dup
> something that's sometimes a dup, and sometimes not.
>
> Whereas if we do it with the xstrdup() at the start it requires more
> moving things around, and if we have a new user who parses the same
> argument we'll bug out on that free().
>
> What do you think?

Frankly I'm ok with moving things around; I think the code could use
a little cleaning up :) But yeah, I think my suggestion isn't so great -
it's a bit weird to keep around an auto variable that exists only to be
dup-ed to the thing we care about. We can forget about that.

I do think that it's worth avoiding the "sometimes dup, sometimes not"
pattern if we can, though (of course these are just my non-C instincts
talking), and we can do that here if we just choose not to assign back
to .path. Something like:

  struct module_clone_data {
    const char *prefix;
  -	const char *path;
  +	char *path;
  +	const char *path_argv;

  ...

   	if (!is_absolute_path(clone_data->path)) {
  -		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
  +		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path_argv);
  +		clone_data->path = strbuf_detach(&sb, NULL);
   	} else {
  -		clone_data->path = xstrdup(clone_data->path);
  +		clone_data->path = xstrdup(clone_data->path_argv);
   	}

would be clearer to me since the const pointer never points to something
that the struct actually owns.

But if the "= .to_free = " idiom is well-understood and accepted to the
point that we don't need to actively avoid "sometimes dup, sometimes
not", then we should drop my suggestion and just go with your patch :)

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH 03/11] submodule--helper: fix "module_clone_data" memory leaks
  2022-07-13 23:04         ` Glen Choo
@ 2022-07-14  0:06           ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-14  0:06 UTC (permalink / raw)
  To: Glen Choo; +Cc: git, Junio C Hamano, Atharva Raykar, Prathamesh Chavan


On Wed, Jul 13 2022, Glen Choo wrote:

> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
>> [...]
>> What do you think?
>
> Frankly I'm ok with moving things around; I think the code could use
> a little cleaning up :) 

Yeah, but this whole part of it is something we'll be throwing away
entirely anyway, so I wanted to leave it at just fixing the memory leaks
as narrowly as possible for now.

I.e. we invoke "clone" from submodule--helper itself, which we don't
need to do if we just invoke it as a function, in which case this whole
argv v.s. dynamically generated difference goes away.

Well, we'll have a constant string in some cases, but we'll likely
either strdup them all with a strvec, or more likely pass the arguments
with custom "options" struct or something.

> But yeah, I think my suggestion isn't so great -
> it's a bit weird to keep around an auto variable that exists only to be
> dup-ed to the thing we care about. We can forget about that.
>
> I do think that it's worth avoiding the "sometimes dup, sometimes not"
> pattern if we can, though (of course these are just my non-C instincts
> talking), and we can do that here if we just choose not to assign back
> to .path. Something like:
>
>   struct module_clone_data {
>     const char *prefix;
>   -	const char *path;
>   +	char *path;
>   +	const char *path_argv;
>
>   ...
>
>    	if (!is_absolute_path(clone_data->path)) {
>   -		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
>   +		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path_argv);
>   +		clone_data->path = strbuf_detach(&sb, NULL);
>    	} else {
>   -		clone_data->path = xstrdup(clone_data->path);
>   +		clone_data->path = xstrdup(clone_data->path_argv);
>    	}
>
> would be clearer to me since the const pointer never points to something
> that the struct actually owns.

I think that actually makes a lot of sense, I'll probably just change it
to that. I'll mull over this again when I get to re-rolling this
(depending on future comments), thanks!

> But if the "= .to_free = " idiom is well-understood and accepted to the
> point that we don't need to actively avoid "sometimes dup, sometimes
> not", then we should drop my suggestion and just go with your patch :)

FWIW "git grep ' = to_free = ' finds a fair bit of them, but luckily we
usually don't need to play that particular game...

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH 05/11] submodule--helper: free() leaking {run,capture}_command() argument
  2022-07-13 13:16 ` [PATCH 05/11] submodule--helper: free() leaking {run,capture}_command() argument Ævar Arnfjörð Bjarmason
@ 2022-07-14  3:36   ` Glen Choo
  2022-07-14 14:43     ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 186+ messages in thread
From: Glen Choo @ 2022-07-14  3:36 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> Free the "dir" member of "struct child_process" that various functions
> in submodule-helper.c allocate allocates with xstrdup().
>
> Since the "dir" argument is "const char *" let's keep a
> "char *to_free" variable around for this rather than casting when we
> call free().
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 41 +++++++++++++++++++++++++++----------
>  1 file changed, 30 insertions(+), 11 deletions(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index a8e439e59b8..2099c5774b2 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -2198,27 +2198,36 @@ static int is_tip_reachable(const char *path, struct object_id *oid)
>  	struct child_process cp = CHILD_PROCESS_INIT;
>  	struct strbuf rev = STRBUF_INIT;
>  	char *hex = oid_to_hex(oid);
> +	char *to_free;
> +	int ret;
>  
>  	cp.git_cmd = 1;
> -	cp.dir = xstrdup(path);
> +	cp.dir = to_free = xstrdup(path);
>  	cp.no_stderr = 1;
>  	strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL);
>  
>  	prepare_submodule_repo_env(&cp.env);
>  
> -	if (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len)
> -		return 0;
> +	if (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len) {
> +		ret = 0;
> +		goto cleanup;
> +	}
>  
> -	return 1;
> +	ret = 1;
> +cleanup:
> +	free(to_free);
> +	return ret;
>  }
>  
>  static int fetch_in_submodule(const char *module_path, int depth, int quiet, struct object_id *oid)
>  {
>  	struct child_process cp = CHILD_PROCESS_INIT;
> +	char *to_free;
> +	int ret;
>  
>  	prepare_submodule_repo_env(&cp.env);
>  	cp.git_cmd = 1;
> -	cp.dir = xstrdup(module_path);
> +	cp.dir = to_free = xstrdup(module_path);
>  
>  	strvec_push(&cp.args, "fetch");
>  	if (quiet)
> @@ -2232,7 +2241,9 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str
>  		free(remote);
>  	}
>  
> -	return run_command(&cp);
> +	ret = run_command(&cp);
> +	free(to_free);
> +	return ret;
>  }
>  
>  static int run_update_command(struct update_data *ud, int subforce)
> @@ -2240,6 +2251,8 @@ static int run_update_command(struct update_data *ud, int subforce)
>  	struct child_process cp = CHILD_PROCESS_INIT;
>  	char *oid = oid_to_hex(&ud->oid);
>  	int must_die_on_failure = 0;
> +	char *to_free;
> +	int ret;
>  
>  	switch (ud->update_strategy.type) {
>  	case SM_UPDATE_CHECKOUT:
> @@ -2273,7 +2286,7 @@ static int run_update_command(struct update_data *ud, int subforce)
>  	}
>  	strvec_push(&cp.args, oid);
>  
> -	cp.dir = xstrdup(ud->sm_path);
> +	cp.dir = to_free = xstrdup(ud->sm_path);
>  	prepare_submodule_repo_env(&cp.env);
>  	if (run_command(&cp)) {
>  		switch (ud->update_strategy.type) {
> @@ -2301,11 +2314,14 @@ static int run_update_command(struct update_data *ud, int subforce)
>  			exit(128);
>  
>  		/* the command failed, but update must continue */
> -		return 1;
> +		ret = 1;
> +		goto cleanup;
>  	}
>  
> -	if (ud->quiet)
> -		return 0;
> +	if (ud->quiet) {
> +		ret = 0;
> +		goto cleanup;
> +	}
>  
>  	switch (ud->update_strategy.type) {
>  	case SM_UPDATE_CHECKOUT:
> @@ -2329,7 +2345,10 @@ static int run_update_command(struct update_data *ud, int subforce)
>  		    submodule_strategy_to_string(&ud->update_strategy));
>  	}
>  
> -	return 0;
> +	ret = 0;
> +cleanup:
> +	free(to_free);
> +	return ret;
>  }
>  
>  static int run_update_procedure(struct update_data *ud)

I assume I'm missing something, but couldn't we achieve the same result
by just removing the xstrdup() calls? i.e. we only leak .dir (which we
don't clear because it's const), so couldn't we just assign to it
without xstrdup() and not have to worry about free()-ing it?

I didn't see a correctness reason for us to xstrdup(), and indeed, t7406
passes with the change I just described (which I believe covers all of
the sites here). In fact, we already have one site that does exactly
this in the recursive part of update_submodule():

	if (update_data->recursive) {
		struct child_process cp = CHILD_PROCESS_INIT;
		struct update_data next = *update_data;
		int res;

		next.prefix = NULL;
		oidcpy(&next.oid, null_oid());
		oidcpy(&next.suboid, null_oid());

		cp.dir = update_data->sm_path;

Tangentially related question (primarily for my own learning): in all of
these hunks, the string being xstrdup()-ed is update_data.sm_path, which
is only ever set in update_submodules():

		update_data->sm_path = ucd.sub->path;

where ucd.sub->path is a "const char *". So we never have to worry about
free()-ing update_data.sm_path, right? (Your patch doesn't attempt to
free() it, which sounds correct.)

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization
  2022-07-13 22:41       ` Glen Choo
@ 2022-07-14 10:25         ` Ævar Arnfjörð Bjarmason
  2022-07-14 17:19           ` Junio C Hamano
  2022-07-15  0:17           ` Glen Choo
  0 siblings, 2 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-14 10:25 UTC (permalink / raw)
  To: Glen Choo; +Cc: Junio C Hamano, git, Atharva Raykar, Prathamesh Chavan


On Wed, Jul 13 2022, Glen Choo wrote:

> Junio C Hamano <gitster@pobox.com> writes:
>
>> Glen Choo <chooglen@google.com> writes:
>>
>>> Ok. I wonder if we could reduce this kind of churn in the future by
>>> adding this to CodingGuidelines, e.g. "always use { 0 } for stack
>>> variables". Tangentially, do we require { NULL } when the first element
>>> is a pointer? (I'm not sure because this isn't in CodingGuidelines
>>> either AFAICT.)
>>
>> A valiable can legitimately be left uninitialized, or initialized to
>> a known value that is not zero using designated initializers.  So
>> saying something like
>>
>>     When zero-initializing an auto variable that is a struct or
>>     union in its definition, use "{ 0 }", whether the first member
>>     in the struct is of a number, a pointer, or a compound type.
>>
>> may be OK.  I do not think we would want to say "always use X", as
>> the world is not that simple..
>
> Thanks. Also, I made a typo here, I meant to be more specific with
> regards to memset(), like:
>
>   When zero-initializing an auto variable that is a struct or
>   union in its definition, use "{ 0 }", whether the first member
>   in the struct is of a number, a pointer, or a compound type. _Do not
>   use memset()._

It's curious that the { 0 } v.s. { NULL } form jumps out at people, but
seemingly not that you don't memset(&x, NULL, ...). I.e. that we're
already dealing with a form where C's "0 is NULL in pointer context"
rules kick in :)

So I wonder if we should say anything about the first member at all. On
the other hand this is quite an odd bit of C syntax, and reveals a bit
of on edge case or wart that I'm not happy with.

It's why I believe GCC has a syntax extension so you can just do:

	struct foo x = {};

I.e. 6.7.8.10 and 6.7.8.21 disuss the semantics of this, basically that
if there are fewer initializers than elements or members that the
structure is initialized as though it were "static".

But for the first member we rely on 0 and NULL being the same in pointer
context (even though NULL my not be (void*)NULL!). So it's a tad nasty,
it's basically doing the same as:

    const char *foo = 0; /* not NULL */

As an aside I wonder if we should say "C says this is undefined, but
c'mon, let's just assume '#define NULL (void*)0'". I think in practice
it's like the recently standardized 2's compliment, in that almost
nobody can even remember a platform where it isn't true, and none are in
current use (but maybe I'm wrong...).

Anyway, I was curious about this so I tried the following locally:
	
	@@
	type T;
	identifier I;
	@@
	- T I;
	+ T I = { 0 };
	... when strict
	    when != \( I \| &I \)
	(
	- memset(&I, 0, sizeof(I));
	|
	- memset(&I, 0, sizeof(T));
	)
	

Which aside from whitespace issues (that I've asked the cocci ML about)
yields a sane result.

What *doesn't* yield a sane result is getting rid of the "when strict"
there, i.e. we must not do this in xdiff/xhistogram.c's
histogram_diff(), as we "goto" to the memset() to re-zero-out the
variable.

But removing the "when strict" yields a change to 45 more files, the
initial one with "when strict" is 76 files. With the exception of that
histogram_diff() case (and I manually skimmed the code for the rest) it
passes all tests at least... :)

> Unless there really is a legitimate reason to use memset(&my_auto_var, 0
> sizeof(my_autovar)) that I've missed?

Basically no, except when you need to do the init N times, as noted
above.

>> We do favor designated initializers over traditional initialization
>> in the order of members these days, so something like
>>
>>     When declaring a struct/union variable or an array with initial
>>     value to some members or elements, consider using designated
>>     initializers, instead of listing the values in the order of
>>     members in the definition of the struct.
>>
>> would also be good.
>
> Thanks. If I have time I'll send that proposal to CodingGuidelines, or
> someone else can send it (I don't mind either way).

Sounds good!

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH 05/11] submodule--helper: free() leaking {run,capture}_command() argument
  2022-07-14  3:36   ` Glen Choo
@ 2022-07-14 14:43     ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-14 14:43 UTC (permalink / raw)
  To: Glen Choo; +Cc: git, Junio C Hamano, Atharva Raykar, Prathamesh Chavan


On Wed, Jul 13 2022, Glen Choo wrote:

> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
>
>> Free the "dir" member of "struct child_process" that various functions
>> in submodule-helper.c allocate allocates with xstrdup().
>>
>> Since the "dir" argument is "const char *" let's keep a
>> "char *to_free" variable around for this rather than casting when we
>> call free().
>>
>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
>> ---
>>  builtin/submodule--helper.c | 41 +++++++++++++++++++++++++++----------
>>  1 file changed, 30 insertions(+), 11 deletions(-)
>>
>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
>> index a8e439e59b8..2099c5774b2 100644
>> --- a/builtin/submodule--helper.c
>> +++ b/builtin/submodule--helper.c
>> @@ -2198,27 +2198,36 @@ static int is_tip_reachable(const char *path, struct object_id *oid)
>>  	struct child_process cp = CHILD_PROCESS_INIT;
>>  	struct strbuf rev = STRBUF_INIT;
>>  	char *hex = oid_to_hex(oid);
>> +	char *to_free;
>> +	int ret;
>>  
>>  	cp.git_cmd = 1;
>> -	cp.dir = xstrdup(path);
>> +	cp.dir = to_free = xstrdup(path);
>>  	cp.no_stderr = 1;
>>  	strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL);
>>  
>>  	prepare_submodule_repo_env(&cp.env);
>>  
>> -	if (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len)
>> -		return 0;
>> +	if (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len) {
>> +		ret = 0;
>> +		goto cleanup;
>> +	}
>>  
>> -	return 1;
>> +	ret = 1;
>> +cleanup:
>> +	free(to_free);
>> +	return ret;
>>  }
>>  
>>  static int fetch_in_submodule(const char *module_path, int depth, int quiet, struct object_id *oid)
>>  {
>>  	struct child_process cp = CHILD_PROCESS_INIT;
>> +	char *to_free;
>> +	int ret;
>>  
>>  	prepare_submodule_repo_env(&cp.env);
>>  	cp.git_cmd = 1;
>> -	cp.dir = xstrdup(module_path);
>> +	cp.dir = to_free = xstrdup(module_path);
>>  
>>  	strvec_push(&cp.args, "fetch");
>>  	if (quiet)
>> @@ -2232,7 +2241,9 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str
>>  		free(remote);
>>  	}
>>  
>> -	return run_command(&cp);
>> +	ret = run_command(&cp);
>> +	free(to_free);
>> +	return ret;
>>  }
>>  
>>  static int run_update_command(struct update_data *ud, int subforce)
>> @@ -2240,6 +2251,8 @@ static int run_update_command(struct update_data *ud, int subforce)
>>  	struct child_process cp = CHILD_PROCESS_INIT;
>>  	char *oid = oid_to_hex(&ud->oid);
>>  	int must_die_on_failure = 0;
>> +	char *to_free;
>> +	int ret;
>>  
>>  	switch (ud->update_strategy.type) {
>>  	case SM_UPDATE_CHECKOUT:
>> @@ -2273,7 +2286,7 @@ static int run_update_command(struct update_data *ud, int subforce)
>>  	}
>>  	strvec_push(&cp.args, oid);
>>  
>> -	cp.dir = xstrdup(ud->sm_path);
>> +	cp.dir = to_free = xstrdup(ud->sm_path);
>>  	prepare_submodule_repo_env(&cp.env);
>>  	if (run_command(&cp)) {
>>  		switch (ud->update_strategy.type) {
>> @@ -2301,11 +2314,14 @@ static int run_update_command(struct update_data *ud, int subforce)
>>  			exit(128);
>>  
>>  		/* the command failed, but update must continue */
>> -		return 1;
>> +		ret = 1;
>> +		goto cleanup;
>>  	}
>>  
>> -	if (ud->quiet)
>> -		return 0;
>> +	if (ud->quiet) {
>> +		ret = 0;
>> +		goto cleanup;
>> +	}
>>  
>>  	switch (ud->update_strategy.type) {
>>  	case SM_UPDATE_CHECKOUT:
>> @@ -2329,7 +2345,10 @@ static int run_update_command(struct update_data *ud, int subforce)
>>  		    submodule_strategy_to_string(&ud->update_strategy));
>>  	}
>>  
>> -	return 0;
>> +	ret = 0;
>> +cleanup:
>> +	free(to_free);
>> +	return ret;
>>  }
>>  
>>  static int run_update_procedure(struct update_data *ud)
>
> I assume I'm missing something, but couldn't we achieve the same result
> by just removing the xstrdup() calls? i.e. we only leak .dir (which we
> don't clear because it's const), so couldn't we just assign to it
> without xstrdup() and not have to worry about free()-ing it?
>
> I didn't see a correctness reason for us to xstrdup(), and indeed, t7406
> passes with the change I just described (which I believe covers all of
> the sites here). In fact, we already have one site that does exactly
> this in the recursive part of update_submodule():
>
> 	if (update_data->recursive) {
> 		struct child_process cp = CHILD_PROCESS_INIT;
> 		struct update_data next = *update_data;
> 		int res;
>
> 		next.prefix = NULL;
> 		oidcpy(&next.oid, null_oid());
> 		oidcpy(&next.suboid, null_oid());
>
> 		cp.dir = update_data->sm_path;
>
> Tangentially related question (primarily for my own learning): in all of
> these hunks, the string being xstrdup()-ed is update_data.sm_path, which
> is only ever set in update_submodules():
>
> 		update_data->sm_path = ucd.sub->path;
>
> where ucd.sub->path is a "const char *". So we never have to worry about
> free()-ing update_data.sm_path, right? (Your patch doesn't attempt to
> free() it, which sounds correct.)

Yes, do'h! That's a much better fix, and only 3 lines of getting rid of
the xstrdup(). I'll re-roll with that.

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization
  2022-07-14 10:25         ` Ævar Arnfjörð Bjarmason
@ 2022-07-14 17:19           ` Junio C Hamano
  2022-07-15  0:17           ` Glen Choo
  1 sibling, 0 replies; 186+ messages in thread
From: Junio C Hamano @ 2022-07-14 17:19 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: Glen Choo, git, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> It's curious that the { 0 } v.s. { NULL } form jumps out at people, but
> seemingly not that you don't memset(&x, NULL, ...). I.e. that we're
> already dealing with a form where C's "0 is NULL in pointer context"
> rules kick in :)

Strictly speaking there are different mechanisms at play here.
Literal "0" spelled in source code and assigned to a pointer
variable assigns a NULL to the variable even on architectures where
the representation of NULL is *not* "all 0-bit filling the width of
the variable" and C language is what guarantees "NULL pointer is
spelled as integer 0 in the source code".

Also, the language, not runtime and pointer representation, is how
second and subsequent members of a struct that are pointers are
initialized to NULL (not necessarily to "all 0-bit filling the width
of the member").

memset(&ptr, '\0', sizeof(ptr)), where ptr is a pointer variable or
a struct/union with a pointer member, on the other hand, is unaware
of how a NULL pointer is represented on the platform.  All it can
(and should) do is to fill the thing "ptr" with all 0-bit.  On an
exotic architecture, where NULL is not "all 0-bit filling the width
of the variable", I do not think it would work.

So from that point of view, using memset() as a replacement for zero
initialization is simply wrong, but in practice people do not work
on such a platform that the distinction matters anymore, hopefully?

> So I wonder if we should say anything about the first member at all.

The mention of the first member of the struct is historically
important only because some checkers like sparse used to complain
about

    struct { TYPE *member0; ... } var = { 0 };

the same way as

    char *string;
    string = 0;

before they got fixed so that they know about "{ 0 }" convention to
be silent about the former, while still warning about "even though C
language says literal 0 is a valid way to spell NULL, you shouldn't
do that" to the latter.

These days, we can safely write "{ 0 }" without having to worry
about the type of the first member.


^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH 06/11] submodule--helper: add and use *_release() functions
  2022-07-13 13:16 ` [PATCH 06/11] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
@ 2022-07-14 18:11   ` Glen Choo
  0 siblings, 0 replies; 186+ messages in thread
From: Glen Choo @ 2022-07-14 18:11 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> Add release functions for "struct module_list", "struct
> submodule_update_clone" and "struct update_data". For now we're
> leaving some of the "struct update_data" members, we'll deal with
> those in a subsequent commit.
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 2099c5774b2..a964dbeec38 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -1910,6 +1921,13 @@ struct submodule_update_clone {
>  };
>  #define SUBMODULE_UPDATE_CLONE_INIT { 0 }
>  
> +static void submodule_update_clone_release(struct submodule_update_clone *suc)
> +{
> +	/* Not "update_data", owned by update_data_release() */
> +	free(suc->update_clone);
> +	free(suc->failed_clones);
> +}
> +

To make this ownership clearer, should we also make
submodule_update_clone.update_data const? i.e.

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index a964dbeec3..79fd901e5a 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1906,7 +1906,7 @@ struct submodule_update_clone {
 	int current;
 
 	/* configuration parameters which are passed on to the children */
-	struct update_data *update_data;
+	const struct update_data *update_data;
 
 	/* to be consumed by update_submodule() */
 	struct update_clone_data *update_clone;
@@ -2003,7 +2003,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
 	const char *update_string;
 	enum submodule_update_type update_type;
 	char *key;
-	struct update_data *ud = suc->update_data;
+	const struct update_data *ud = suc->update_data;
 	char *displaypath = get_submodule_displaypath(ce->name, ud->prefix);
 	struct strbuf sb = STRBUF_INIT;
 	int needs_cloning = 0;

^ permalink raw reply related	[flat|nested] 186+ messages in thread

* Re: [PATCH 07/11] submodule--helper: fix "errmsg_str" memory leak
  2022-07-13 13:16 ` [PATCH 07/11] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
@ 2022-07-14 18:20   ` Glen Choo
  0 siblings, 0 replies; 186+ messages in thread
From: Glen Choo @ 2022-07-14 18:20 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> Fix a memory leak introduced in e83e3333b57 (submodule: port submodule
> subcommand 'summary' from shell to C, 2020-08-13), to do that stop
> juggling around the "errmsg" and "struct strbuf", let's instead move
> the "struct strbuf errmsg" to the top-level.
>
> Now we don't need to strbuf_detach() it anymore, but we do need to
> ensure that we pass NULL to print_submodule_summary() when we have no
> error message.
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 7 ++++---
>  1 file changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index a964dbeec38..a05578a7382 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -932,7 +932,8 @@ static void generate_submodule_summary(struct summary_cb *info,
>  {
>  	char *displaypath, *src_abbrev = NULL, *dst_abbrev;
>  	int missing_src = 0, missing_dst = 0;
> -	char *errmsg = NULL;
> +	char *errmsg;
> +	struct strbuf errmsg_str = STRBUF_INIT;
>  	int total_commits = -1;
>  
>  	if (!info->cached && oideq(&p->oid_dst, null_oid())) {
> @@ -1032,7 +1033,6 @@ static void generate_submodule_summary(struct summary_cb *info,
>  		 * submodule, i.e., deleted or changed to blob
>  		 */
>  		if (S_ISGITLINK(p->mod_dst)) {
> -			struct strbuf errmsg_str = STRBUF_INIT;
>  			if (missing_src && missing_dst) {
>  				strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commits %s and %s\n",
>  					    displaypath, oid_to_hex(&p->oid_src),
> @@ -1043,10 +1043,10 @@ static void generate_submodule_summary(struct summary_cb *info,
>  					    oid_to_hex(&p->oid_src) :
>  					    oid_to_hex(&p->oid_dst));
>  			}
> -			errmsg = strbuf_detach(&errmsg_str, NULL);
>  		}
>  	}
>  
> +	errmsg = errmsg_str.len ? errmsg_str.buf : NULL;
>  	print_submodule_summary(info, errmsg, total_commits,
>  				displaypath, src_abbrev,
>  				dst_abbrev, p);
> @@ -1054,6 +1054,7 @@ static void generate_submodule_summary(struct summary_cb *info,
>  	free(displaypath);
>  	free(src_abbrev);
>  	free(dst_abbrev);
> +	strbuf_release(&errmsg_str);
>  }
>  
>  static void prepare_submodule_summary(struct summary_cb *info,
> -- 
> 2.37.0.932.g7b7031e73bc

What do you think of getting rid of "char *errmsg" altogether? We can
replace it with errmsg_str.buf and do the length check in
print_submodule_summary():

  diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
  index a964dbeec3..f9f0de7e83 100644
  --- a/builtin/submodule--helper.c
  +++ b/builtin/submodule--helper.c
  @@ -896,7 +896,7 @@ static void print_submodule_summary(struct summary_cb *info, char *errmsg,
    else
      printf(" (%d):\n", total_commits);

  -	if (errmsg) {
  +	if (errmsg && *errmsg) {
      printf(_("%s"), errmsg);
    } else if (total_commits > 0) {
      struct child_process cp_log = CHILD_PROCESS_INIT;
  @@ -932,7 +932,7 @@ static void generate_submodule_summary(struct summary_cb *info,
  {
    char *displaypath, *src_abbrev = NULL, *dst_abbrev;
    int missing_src = 0, missing_dst = 0;
  -	char *errmsg = NULL;
  +	struct strbuf errmsg_str = STRBUF_INIT;
    int total_commits = -1;

    if (!info->cached && oideq(&p->oid_dst, null_oid())) {
  @@ -1032,7 +1032,6 @@ static void generate_submodule_summary(struct summary_cb *info,
      * submodule, i.e., deleted or changed to blob
      */
      if (S_ISGITLINK(p->mod_dst)) {
  -			struct strbuf errmsg_str = STRBUF_INIT;
        if (missing_src && missing_dst) {
          strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commits %s and %s\n",
                displaypath, oid_to_hex(&p->oid_src),
  @@ -1043,17 +1042,17 @@ static void generate_submodule_summary(struct summary_cb *info,
                oid_to_hex(&p->oid_src) :
                oid_to_hex(&p->oid_dst));
        }
  -			errmsg = strbuf_detach(&errmsg_str, NULL);
      }
    }

  -	print_submodule_summary(info, errmsg, total_commits,
  +	print_submodule_summary(info, errmsg_str.buf, total_commits,
          displaypath, src_abbrev,
          dst_abbrev, p);

    free(displaypath);
    free(src_abbrev);
    free(dst_abbrev);
  +	strbuf_release(&errmsg_str);
  }

  static void prepare_submodule_summary(struct summary_cb *info,


^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH 09/11] submodule--helper: free "char *" in "struct update_data"
  2022-07-13 13:16 ` [PATCH 09/11] submodule--helper: free "char *" in "struct update_data" Ævar Arnfjörð Bjarmason
@ 2022-07-14 18:33   ` Glen Choo
  2022-07-18 16:10     ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 186+ messages in thread
From: Glen Choo @ 2022-07-14 18:33 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> Make the update_data_release() function free the "recursive_prefix"
> and "displaypath" members when appropriate. For the former it could
> come from either "argv" or from our own allocation, so we need to keep
> track of a "to_free" sibling seperately.

Obsolete message probably? "recursive_prefix" no longer exists as of
gc/submodule-use-super-prefix ;)

> For "displaypath" it's always ours, so the "const char *" type was
> wrong to begin with, it should be a "char *" instead.

Ok.

> For update_submodule() we'll free() these as we go along, it's called
> in a loop by update_submodules(), and we'll need to free the "last"
> one.

Hm, I don't follow this part. Does "as we go along" mean "as we go along
freeing things in update_submodules()", or "we'll do this later on"?

I'm assuming it's the latter since this patch only frees the "last" one
and doesn't free inside of update_submodule(), but maybe it's not so
hard to do? I think it's just:

  diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
  index 0bac39880d..34b54e97d1 100644
  --- a/builtin/submodule--helper.c
  +++ b/builtin/submodule--helper.c
  @@ -2560,6 +2560,7 @@ static int update_submodule(struct update_data *update_data)
  {
    ensure_core_worktree(update_data->sm_path);

  +	free(update_data->displaypath);
    update_data->displaypath = get_submodule_displaypath(
      update_data->sm_path, update_data->prefix);

> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 2b44f391f15..0bac39880d2 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -1949,7 +1949,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)
>  
>  struct update_data {
>  	const char *prefix;
> -	const char *displaypath;
> +	char *displaypath;
>  	enum submodule_update_type update_default;
>  	struct object_id suboid;
>  	struct string_list references;
> @@ -1987,6 +1987,7 @@ struct update_data {
>  
>  static void update_data_release(struct update_data *ud)
>  {
> +	free(ud->displaypath);
>  	module_list_release(&ud->list);
>  }
>  
> -- 
> 2.37.0.932.g7b7031e73bc

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH 11/11] submodule--helper: fix "reference" leak is "module_clone_data"
  2022-07-13 13:16 ` [PATCH 11/11] submodule--helper: fix "reference" leak is "module_clone_data" Ævar Arnfjörð Bjarmason
@ 2022-07-14 18:42   ` Glen Choo
  0 siblings, 0 replies; 186+ messages in thread
From: Glen Choo @ 2022-07-14 18:42 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> Fix leaks in the "reference" member of "struct module_clone_data" that
> have been with us since 31224cbdc72 (clone: recursive and reference
> option triggers submodule alternates, 2016-08-17) and
> 8c8195e9c3e (submodule--helper: introduce add-clone subcommand,
> 2021-07-10).
>
> Those commits added an xstrdup()'d member of the
> STRING_LIST_INIT_NODUP'd "struct string_list". We need to free()
> those, but not the ones we get from argv, let's make use of the "util"
> member, if it has a pointer it's the pointer we'll need to free.

Yes, this is a clear-cut case of "sometimes free()-able, sometimes not"
(the others upthread were much less clear), so the approach sounds good.

Well, we could make this always free()-able with some wacky
OPT_STRING_LIST_DUP thing, but I don't think that idea is going anywhere
;)

> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 13 +++++++++----
>  1 file changed, 9 insertions(+), 4 deletions(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index d9fab5d77da..966f543fbd9 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -1598,6 +1598,7 @@ struct module_clone_data {
>  static void module_clone_data_release(struct module_clone_data *cd)
>  {
>  	free(cd->path_free);
> +	string_list_clear(&cd->reference, 1);
>  }
>  
>  struct submodule_alternate_setup {
> @@ -1652,7 +1653,9 @@ static int add_possible_reference_from_superproject(
>  
>  		sm_alternate = compute_alternate_path(sb.buf, &err);
>  		if (sm_alternate) {
> -			string_list_append(sas->reference, xstrdup(sb.buf));
> +			char *p = strbuf_detach(&sb, NULL);
> +
> +			string_list_append(sas->reference, p)->util = p;
>  			free(sm_alternate);
>  		} else {
>  			switch (sas->error_mode) {
> @@ -3194,9 +3197,11 @@ static int add_submodule(const struct add_data *add_data)
>  		clone_data.url = add_data->realrepo;
>  		clone_data.quiet = add_data->quiet;
>  		clone_data.progress = add_data->progress;
> -		if (add_data->reference_path)
> -			string_list_append(&clone_data.reference,
> -					   xstrdup(add_data->reference_path));
> +		if (add_data->reference_path) {
> +			char *p = xstrdup(add_data->reference_path);
> +
> +			string_list_append(&clone_data.reference, p)->util = p;
> +		}
>  		clone_data.dissociate = add_data->dissociate;
>  		if (add_data->depth >= 0)
>  			clone_data.depth = xstrfmt("%d", add_data->depth);
> -- 
> 2.37.0.932.g7b7031e73bc

Ok.

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization
  2022-07-14 10:25         ` Ævar Arnfjörð Bjarmason
  2022-07-14 17:19           ` Junio C Hamano
@ 2022-07-15  0:17           ` Glen Choo
  1 sibling, 0 replies; 186+ messages in thread
From: Glen Choo @ 2022-07-15  0:17 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: Junio C Hamano, git, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> On Wed, Jul 13 2022, Glen Choo wrote:
> Anyway, I was curious about this so I tried the following locally:
> 	
> 	@@
> 	type T;
> 	identifier I;
> 	@@
> 	- T I;
> 	+ T I = { 0 };
> 	... when strict
> 	    when != \( I \| &I \)
> 	(
> 	- memset(&I, 0, sizeof(I));
> 	|
> 	- memset(&I, 0, sizeof(T));
> 	)
> 	
>
> Which aside from whitespace issues (that I've asked the cocci ML about)
> yields a sane result...
>> ....... If I have time I'll send that proposal to CodingGuidelines, or
>> someone else can send it (I don't mind either way).

Adding an extra cocci check sounds like a great addition alongside the
CodingGuidelines change (automatically checking the rule is way better
than doing it manually of course). The fact that { 0 } is wrapped around
two lines is annoying, e.g.

  -	struct update_callback_data data;
  +	struct update_callback_data data = {
  +		0
  +	};
    struct rev_info rev;

  -	memset(&data, 0, sizeof(data));
    data.flags = flags;

but since it goes away with "make style", I'm tempted to say that this
check is worth having. Is it too confusing to have coccinelle recommend
something that we expect users to fix afterwards?

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH 09/11] submodule--helper: free "char *" in "struct update_data"
  2022-07-14 18:33   ` Glen Choo
@ 2022-07-18 16:10     ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-18 16:10 UTC (permalink / raw)
  To: Glen Choo; +Cc: git, Junio C Hamano, Atharva Raykar, Prathamesh Chavan


On Thu, Jul 14 2022, Glen Choo wrote:

> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
>
>> Make the update_data_release() function free the "recursive_prefix"
>> and "displaypath" members when appropriate. For the former it could
>> come from either "argv" or from our own allocation, so we need to keep
>> track of a "to_free" sibling seperately.
>
> Obsolete message probably? "recursive_prefix" no longer exists as of
> gc/submodule-use-super-prefix ;)

Thanks, will fix!

>> For "displaypath" it's always ours, so the "const char *" type was
>> wrong to begin with, it should be a "char *" instead.
>
> Ok.
>
>> For update_submodule() we'll free() these as we go along, it's called
>> in a loop by update_submodules(), and we'll need to free the "last"
>> one.
>
> Hm, I don't follow this part. Does "as we go along" mean "as we go along
> freeing things in update_submodules()", or "we'll do this later on"?

You know what? After looking at this again I have no idea what I was
talking about here, and I think this makes no sense... :)

> I'm assuming it's the latter since this patch only frees the "last" one
> and doesn't free inside of update_submodule(), but maybe it's not so
> hard to do? I think it's just:
>
>   diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
>   index 0bac39880d..34b54e97d1 100644
>   --- a/builtin/submodule--helper.c
>   +++ b/builtin/submodule--helper.c
>   @@ -2560,6 +2560,7 @@ static int update_submodule(struct update_data *update_data)
>   {
>     ensure_core_worktree(update_data->sm_path);
>
>   +	free(update_data->displaypath);
>     update_data->displaypath = get_submodule_displaypath(
>       update_data->sm_path, update_data->prefix);

Thanks, I'll fix this case in a re-roll, fixing it generally turned out
to be a lot trickier though, but in the process I fixed a lot of the
control flow issues. Am currently running extensive tests on it.

Thanks a lot for this & other reviews, it's been really useful, as
always.

^ permalink raw reply	[flat|nested] 186+ messages in thread

* [PATCH v2 00/24] submodule--helper: fix memory leaks
  2022-07-13 13:16 [PATCH 00/11] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                   ` (10 preceding siblings ...)
  2022-07-13 13:16 ` [PATCH 11/11] submodule--helper: fix "reference" leak is "module_clone_data" Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:46 ` Ævar Arnfjörð Bjarmason
  2022-07-19 20:46   ` [PATCH v2 01/24] submodule--helper: replace memset() with { 0 }-initialization Ævar Arnfjörð Bjarmason
                     ` (24 more replies)
  11 siblings, 25 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:46 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

This series fixes all of the memory leaks in
builtin/submodule--helper.c. The v1 went on top of
gc/submodule-use-super-prefix (which in turn was on top ofmy
ab/submodule-cleanup). Both of those have landed on "master", so this
is directly on master.

Thanks a lot to everyone who looked at this & especially to Glen Choo,
I think this re-roll addresses all of his comments.

This is now 24 patches, up from 11, but there's not much of an
increase in complexity here. The growth in size is because I chased
down the rest of the leaks from "submodule--helper", and to show the
progression for certain fixes. E.g. 19-22/24 are all working towards
22/24, but doing so stepwise, to make it easier to reason about each
individual step.

There are leaks that are left that "submodule--helper" runs into, but
it's not in the submodule--helper.c code. E.g. there's one in
submodule & branch.c, and another to do with the object code, etc.. I
thought it was best to leave those for now.

Glen Choo (2):
  submodule--helper: add "const" to copy of "update_data"
  submodule--helper: refactor "errmsg_str" to be a "struct strbuf"

Ævar Arnfjörð Bjarmason (22):
  submodule--helper: replace memset() with { 0 }-initialization
  submodule--helper: fix a leak in "clone_submodule"
  submodule--helper: fix trivial get_default_remote_submodule() leak
  submodule--helper: fix most "struct pathspec" memory leaks
  submodule--helper: "struct pathspec" memory leak in module_update()
  submodule--helper: don't leak {run,capture}_command() cp.dir argument
  submodule--helper: add and use *_release() functions
  submodule--helper: fix "errmsg_str" memory leak
  submodule--helper: fix "sm_path" and other "module_cb_list" leaks
  submodule--helper: fix a leak with repo_clear()
  submodule--helper: fix a memory leak in get_default_remote_submodule()
  submodule--helper: fix "reference" leak is "module_clone_data"
  submodule--helper: fix obscure leak in module_add()
  submodule--helper: fix a leak in module_add()
  submodule--helper: fix a memory leak in print_status()
  submodule--helper: free some "displaypath" in "struct update_data"
  submodule--helper: rename "int res" to "int ret"
  submodule--helper: add skeleton "goto cleanup" to update_submodule()
  submodule--helper: don't exit() on failure, return
  submodule--helper: free rest of "displaypath" in "struct update_data"
  submodule--helper: fix bad config API usage
  submodule--helper: fix a configure_added_submodule() leak

 builtin/submodule--helper.c        | 347 +++++++++++++++++++++--------
 t/t1500-rev-parse.sh               |   1 +
 t/t2403-worktree-move.sh           |   1 +
 t/t6008-rev-list-submodule.sh      |   1 +
 t/t6134-pathspec-in-submodule.sh   |   1 +
 t/t7412-submodule-absorbgitdirs.sh |   1 +
 t/t7413-submodule-is-active.sh     |   1 +
 t/t7414-submodule-mistakes.sh      |   2 +
 t/t7506-status-submodule.sh        |   1 +
 t/t7507-commit-verbose.sh          |   2 +
 10 files changed, 259 insertions(+), 99 deletions(-)

Range-diff against v1:
 2:  4049362e9b4 =  1:  fcdf4a2e2d9 submodule--helper: replace memset() with { 0 }-initialization
 3:  e5ec6945409 !  2:  130a396b837 submodule--helper: fix "module_clone_data" memory leaks
    @@ Metadata
     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## Commit message ##
    -    submodule--helper: fix "module_clone_data" memory leaks
    +    submodule--helper: fix a leak in "clone_submodule"
     
    -    Fix memory leaks related to the "struct module_clone_data" by creating
    -    a module_clone_data_release() function to go with the
    -    MODULE_CLONE_DATA_INIT added in a98b02c1128 (submodule--helper:
    -    refactor module_clone(), 2021-07-10).
    +    Fix a memory leak of the "path" member of the "struct
    +    module_clone_data" in clone_submodule(). This fixes leaks in code
    +    added in f8eaa0ba98b (submodule--helper, module_clone: always operate
    +    on absolute paths, 2016-03-31).
     
    -    The "path" member can come from "argv" (i.e. not malloc'd), or it can
    -    be something we determine at runtime. In the latter case let's save
    -    away a pointer to free() to avoid leaking memory.
    +    For the "else" case we don't need to xstrdup() the "clone_data->path",
    +    if we're not creating a new one we'll leave it to our caller to keep
    +    track of it.
    +
    +    In the case of the module_clone() caller it's from "argv", and doesn't
    +    need to be free'd, and in the case of the add_submodule() caller we
    +    get a pointer to "sm_path", which doesn't need to be directly free'd
    +    either.
     
         Fixing this leak makes several tests pass, so let's mark them as
         passing with TEST_PASSES_SANITIZE_LEAK=true.
    @@ Commit message
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/submodule--helper.c ##
    -@@ builtin/submodule--helper.c: static int module_deinit(int argc, const char **argv, const char *prefix)
    - struct module_clone_data {
    - 	const char *prefix;
    - 	const char *path;
    -+	char *path_free;
    - 	const char *name;
    - 	const char *url;
    - 	const char *depth;
    -@@ builtin/submodule--helper.c: struct module_clone_data {
    - 	.single_branch = -1, \
    - }
    +@@ builtin/submodule--helper.c: static int clone_submodule(struct module_clone_data *clone_data)
    + 	char *sm_alternate = NULL, *error_strategy = NULL;
    + 	struct strbuf sb = STRBUF_INIT;
    + 	struct child_process cp = CHILD_PROCESS_INIT;
    ++	char *to_free = NULL;
      
    -+static void module_clone_data_release(struct module_clone_data *cd)
    -+{
    -+	free(cd->path_free);
    -+}
    -+
    - struct submodule_alternate_setup {
    - 	const char *submodule_name;
    - 	enum SUBMODULE_ALTERNATE_ERROR_MODE {
    + 	submodule_name_to_gitdir(&sb, the_repository, clone_data->name);
    + 	sm_gitdir = absolute_pathdup(sb.buf);
     @@ builtin/submodule--helper.c: static int clone_submodule(struct module_clone_data *clone_data)
      
      	if (!is_absolute_path(clone_data->path)) {
      		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
     -		clone_data->path = strbuf_detach(&sb, NULL);
    -+		clone_data->path = clone_data->path_free = strbuf_detach(&sb, NULL);
    ++		clone_data->path = to_free = strbuf_detach(&sb, NULL);
      	} else {
     -		clone_data->path = xstrdup(clone_data->path);
    -+		clone_data->path = clone_data->path_free = xstrdup(clone_data->path);
    ++		clone_data->path = clone_data->path;
      	}
      
      	if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)
    -@@ builtin/submodule--helper.c: static int module_clone(int argc, const char **argv, const char *prefix)
    - 
    - 	clone_submodule(&clone_data);
    - 	list_objects_filter_release(&filter_options);
    -+
    -+	module_clone_data_release(&clone_data);
    +@@ builtin/submodule--helper.c: static int clone_submodule(struct module_clone_data *clone_data)
    + 	strbuf_release(&sb);
    + 	free(sm_gitdir);
    + 	free(p);
    ++	free(to_free);
      	return 0;
      }
      
    -@@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add_data)
    - {
    - 	char *submod_gitdir_path;
    - 	struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
    -+	int ret;
    - 
    - 	/* perhaps the path already exists and is already a git repo, else clone it */
    - 	if (is_directory(add_data->sm_path)) {
    -@@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add_data)
    - 		if (add_data->depth >= 0)
    - 			clone_data.depth = xstrfmt("%d", add_data->depth);
    - 
    --		if (clone_submodule(&clone_data))
    --			return -1;
    -+		if (clone_submodule(&clone_data)) {
    -+			ret = -1;
    -+			goto cleanup;
    -+		}
    +
    + ## t/t1500-rev-parse.sh ##
    +@@ t/t1500-rev-parse.sh: test_description='test git rev-parse'
    + GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
    + export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
      
    - 		prepare_submodule_repo_env(&cp.env);
    - 		cp.git_cmd = 1;
    -@@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add_data)
    - 		if (run_command(&cp))
    - 			die(_("unable to checkout submodule '%s'"), add_data->sm_path);
    - 	}
    --	return 0;
    -+	ret = 0;
    -+cleanup:
    -+	module_clone_data_release(&clone_data);
    -+	return ret;
    - }
    ++TEST_PASSES_SANITIZE_LEAK=true
    + . ./test-lib.sh
      
    - static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
    + test_one () {
     
      ## t/t6008-rev-list-submodule.sh ##
     @@ t/t6008-rev-list-submodule.sh: test_description='git rev-list involving submodules that this repo has'
 -:  ----------- >  3:  25e17bbb058 submodule--helper: fix trivial get_default_remote_submodule() leak
 4:  cdefd283c23 !  4:  9fb60485c3e submodule--helper: fix "struct pathspec" memory leaks
    @@ Metadata
     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## Commit message ##
    -    submodule--helper: fix "struct pathspec" memory leaks
    +    submodule--helper: fix most "struct pathspec" memory leaks
     
         Call clear_pathspec() at the end of various functions that work with
         and allocate a "struct pathspec".
 -:  ----------- >  5:  a4672aa9c94 submodule--helper: "struct pathspec" memory leak in module_update()
 5:  76eab92c8b6 !  6:  7925db18b33 submodule--helper: free() leaking {run,capture}_command() argument
    @@ Metadata
     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## Commit message ##
    -    submodule--helper: free() leaking {run,capture}_command() argument
    +    submodule--helper: don't leak {run,capture}_command() cp.dir argument
     
    -    Free the "dir" member of "struct child_process" that various functions
    -    in submodule-helper.c allocate allocates with xstrdup().
    +    Fix a memory leak in c51f8f94e5b (submodule--helper: run update
    +    procedures from C, 2021-08-24) and 3c3558f0953 (submodule--helper: run
    +    update using child process struct, 2022-03-15) by not allocating
    +    memory in the first place.
     
    -    Since the "dir" argument is "const char *" let's keep a
    -    "char *to_free" variable around for this rather than casting when we
    -    call free().
    +    The "dir" member of "struct child_process" will not be modified by
    +    that API, and it's declared to be "const char *". So let's not
    +    needlessly duplicate these strings.
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/submodule--helper.c ##
     @@ builtin/submodule--helper.c: static int is_tip_reachable(const char *path, struct object_id *oid)
    - 	struct child_process cp = CHILD_PROCESS_INIT;
    - 	struct strbuf rev = STRBUF_INIT;
      	char *hex = oid_to_hex(oid);
    -+	char *to_free;
    -+	int ret;
      
      	cp.git_cmd = 1;
     -	cp.dir = xstrdup(path);
    -+	cp.dir = to_free = xstrdup(path);
    ++	cp.dir = path;
      	cp.no_stderr = 1;
      	strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL);
      
    - 	prepare_submodule_repo_env(&cp.env);
    - 
    --	if (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len)
    --		return 0;
    -+	if (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len) {
    -+		ret = 0;
    -+		goto cleanup;
    -+	}
    - 
    --	return 1;
    -+	ret = 1;
    -+cleanup:
    -+	free(to_free);
    -+	return ret;
    - }
    - 
    - static int fetch_in_submodule(const char *module_path, int depth, int quiet, struct object_id *oid)
    - {
    - 	struct child_process cp = CHILD_PROCESS_INIT;
    -+	char *to_free;
    -+	int ret;
    +@@ builtin/submodule--helper.c: static int fetch_in_submodule(const char *module_path, int depth, int quiet, str
      
      	prepare_submodule_repo_env(&cp.env);
      	cp.git_cmd = 1;
     -	cp.dir = xstrdup(module_path);
    -+	cp.dir = to_free = xstrdup(module_path);
    ++	cp.dir = module_path;
      
      	strvec_push(&cp.args, "fetch");
      	if (quiet)
    -@@ builtin/submodule--helper.c: static int fetch_in_submodule(const char *module_path, int depth, int quiet, str
    - 		free(remote);
    - 	}
    - 
    --	return run_command(&cp);
    -+	ret = run_command(&cp);
    -+	free(to_free);
    -+	return ret;
    - }
    - 
    - static int run_update_command(struct update_data *ud, int subforce)
    -@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)
    - 	struct child_process cp = CHILD_PROCESS_INIT;
    - 	char *oid = oid_to_hex(&ud->oid);
    - 	int must_die_on_failure = 0;
    -+	char *to_free;
    -+	int ret;
    - 
    - 	switch (ud->update_strategy.type) {
    - 	case SM_UPDATE_CHECKOUT:
     @@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)
      	}
      	strvec_push(&cp.args, oid);
      
     -	cp.dir = xstrdup(ud->sm_path);
    -+	cp.dir = to_free = xstrdup(ud->sm_path);
    ++	cp.dir = ud->sm_path;
      	prepare_submodule_repo_env(&cp.env);
      	if (run_command(&cp)) {
      		switch (ud->update_strategy.type) {
    -@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)
    - 			exit(128);
    - 
    - 		/* the command failed, but update must continue */
    --		return 1;
    -+		ret = 1;
    -+		goto cleanup;
    - 	}
    - 
    --	if (ud->quiet)
    --		return 0;
    -+	if (ud->quiet) {
    -+		ret = 0;
    -+		goto cleanup;
    -+	}
    - 
    - 	switch (ud->update_strategy.type) {
    - 	case SM_UPDATE_CHECKOUT:
    -@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)
    - 		    submodule_strategy_to_string(&ud->update_strategy));
    - 	}
    - 
    --	return 0;
    -+	ret = 0;
    -+cleanup:
    -+	free(to_free);
    -+	return ret;
    - }
    - 
    - static int run_update_procedure(struct update_data *ud)
 -:  ----------- >  7:  30883f3aa01 submodule--helper: add "const" to copy of "update_data"
 6:  b9984e982db !  8:  b7582391c91 submodule--helper: add and use *_release() functions
    @@ Commit message
         submodule--helper: add and use *_release() functions
     
         Add release functions for "struct module_list", "struct
    -    submodule_update_clone" and "struct update_data". For now we're
    -    leaving some of the "struct update_data" members, we'll deal with
    -    those in a subsequent commit.
    +    submodule_update_clone" and "struct update_data".
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
    @@ builtin/submodule--helper.c: struct submodule_update_clone {
      
     +static void submodule_update_clone_release(struct submodule_update_clone *suc)
     +{
    -+	/* Not "update_data", owned by update_data_release() */
     +	free(suc->update_clone);
     +	free(suc->failed_clones);
     +}
    @@ builtin/submodule--helper.c: static int update_submodules(struct update_data *up
     @@ builtin/submodule--helper.c: static int module_update(int argc, const char **argv, const char *prefix)
      
      		if (module_list_compute(argc, argv, opt.prefix,
    - 					&pathspec, &list) < 0) {
    + 					&pathspec2, &list) < 0) {
     +			module_list_release(&list);
      			ret = 1;
      			goto cleanup;
    @@ builtin/submodule--helper.c: static int module_update(int argc, const char **arg
     +	update_data_release(&opt);
      	list_objects_filter_release(&filter_options);
      	clear_pathspec(&pathspec);
    - 	return ret;
    + 	clear_pathspec(&pathspec2);
     @@ builtin/submodule--helper.c: static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
      	ret = 0;
      cleanup:
 7:  f8c20bbf266 !  9:  92737916083 submodule--helper: fix "errmsg_str" memory leak
    @@
      ## Metadata ##
    -Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
    +Author: Glen Choo <chooglen@google.com>
     
      ## Commit message ##
    -    submodule--helper: fix "errmsg_str" memory leak
    +    submodule--helper: refactor "errmsg_str" to be a "struct strbuf"
     
    -    Fix a memory leak introduced in e83e3333b57 (submodule: port submodule
    -    subcommand 'summary' from shell to C, 2020-08-13), to do that stop
    -    juggling around the "errmsg" and "struct strbuf", let's instead move
    -    the "struct strbuf errmsg" to the top-level.
    +    Refactor code added in e83e3333b57 (submodule: port submodule
    +    subcommand 'summary' from shell to C, 2020-08-13) so that "errmsg" and
    +    "errmsg_str" are folded into one. The distinction between the empty
    +    string and NULL is something that's tested for by
    +    e.g. "t/t7401-submodule-summary.sh".
     
    -    Now we don't need to strbuf_detach() it anymore, but we do need to
    -    ensure that we pass NULL to print_submodule_summary() when we have no
    -    error message.
    +    This is in preparation for fixing a memory leak the "struct strbuf" in
    +    the pre-image.
     
    +    Let's also pass a "const char *" to print_submodule_summary(), as it
    +    should not be modifying the "errmsg".
    +
    +    Signed-off-by: Glen Choo <chooglen@google.com>
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/submodule--helper.c ##
    +@@ builtin/submodule--helper.c: static char *verify_submodule_committish(const char *sm_path,
    + 	return strbuf_detach(&result, NULL);
    + }
    + 
    +-static void print_submodule_summary(struct summary_cb *info, char *errmsg,
    ++static void print_submodule_summary(struct summary_cb *info, const char *errmsg,
    + 				    int total_commits, const char *displaypath,
    + 				    const char *src_abbrev, const char *dst_abbrev,
    + 				    struct module_cb *p)
     @@ builtin/submodule--helper.c: static void generate_submodule_summary(struct summary_cb *info,
      {
      	char *displaypath, *src_abbrev = NULL, *dst_abbrev;
      	int missing_src = 0, missing_dst = 0;
     -	char *errmsg = NULL;
    -+	char *errmsg;
    -+	struct strbuf errmsg_str = STRBUF_INIT;
    ++	struct strbuf errmsg = STRBUF_INIT;
      	int total_commits = -1;
      
      	if (!info->cached && oideq(&p->oid_dst, null_oid())) {
    @@ builtin/submodule--helper.c: static void generate_submodule_summary(struct summa
      		if (S_ISGITLINK(p->mod_dst)) {
     -			struct strbuf errmsg_str = STRBUF_INIT;
      			if (missing_src && missing_dst) {
    - 				strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commits %s and %s\n",
    +-				strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commits %s and %s\n",
    ++				strbuf_addf(&errmsg, "  Warn: %s doesn't contain commits %s and %s\n",
      					    displaypath, oid_to_hex(&p->oid_src),
    -@@ builtin/submodule--helper.c: static void generate_submodule_summary(struct summary_cb *info,
    + 					    oid_to_hex(&p->oid_dst));
    + 			} else {
    +-				strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commit %s\n",
    ++				strbuf_addf(&errmsg, "  Warn: %s doesn't contain commit %s\n",
    + 					    displaypath, missing_src ?
      					    oid_to_hex(&p->oid_src) :
      					    oid_to_hex(&p->oid_dst));
      			}
    @@ builtin/submodule--helper.c: static void generate_submodule_summary(struct summa
      		}
      	}
      
    -+	errmsg = errmsg_str.len ? errmsg_str.buf : NULL;
    - 	print_submodule_summary(info, errmsg, total_commits,
    - 				displaypath, src_abbrev,
    +-	print_submodule_summary(info, errmsg, total_commits,
    +-				displaypath, src_abbrev,
    ++	print_submodule_summary(info, errmsg.len ? errmsg.buf : NULL,
    ++				total_commits, displaypath, src_abbrev,
      				dst_abbrev, p);
    -@@ builtin/submodule--helper.c: static void generate_submodule_summary(struct summary_cb *info,
    - 	free(displaypath);
    - 	free(src_abbrev);
    - 	free(dst_abbrev);
    -+	strbuf_release(&errmsg_str);
    - }
      
    - static void prepare_submodule_summary(struct summary_cb *info,
    + 	free(displaypath);
 -:  ----------- > 10:  c81a4f69179 submodule--helper: fix "errmsg_str" memory leak
 8:  c8bb7bb7bb3 = 11:  c25b55c9528 submodule--helper: fix "sm_path" and other "module_cb_list" leaks
10:  e6c633256f3 ! 12:  303447bd4f5 submodule--helper: fix a leak with repo_clear()
    @@ builtin/submodule--helper.c: static void ensure_core_worktree(const char *path)
      }
      
      static const char *submodule_update_type_to_label(enum submodule_update_type type)
    -
    - ## t/t2403-worktree-move.sh ##
    -@@
    - 
    - test_description='test git worktree move, remove, lock and unlock'
    - 
    -+TEST_PASSES_SANITIZE_LEAK=true
    - . ./test-lib.sh
    - 
    - test_expect_success 'setup' '
    -
    - ## t/t7412-submodule-absorbgitdirs.sh ##
    -@@ t/t7412-submodule-absorbgitdirs.sh: This test verifies that `git submodue absorbgitdirs` moves a submodules git
    - directory into the superproject.
    - '
    - 
    -+TEST_PASSES_SANITIZE_LEAK=true
    - . ./test-lib.sh
    - 
    - test_expect_success 'setup a real submodule' '
 -:  ----------- > 13:  f8ededcdf77 submodule--helper: fix a memory leak in get_default_remote_submodule()
11:  475b1196a93 ! 14:  c7610088968 submodule--helper: fix "reference" leak is "module_clone_data"
    @@ Commit message
         those, but not the ones we get from argv, let's make use of the "util"
         member, if it has a pointer it's the pointer we'll need to free.
     
    +    To fix this create a a module_clone_data_release() function to go with
    +    the MODULE_CLONE_DATA_INIT added in a98b02c1128 (submodule--helper:
    +    refactor module_clone(), 2021-07-10). We only need to add it to
    +    add_submodule() to fix the leak, but let's add it to module_clone() as
    +    well for consistency.
    +
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/submodule--helper.c ##
     @@ builtin/submodule--helper.c: struct module_clone_data {
    - static void module_clone_data_release(struct module_clone_data *cd)
    - {
    - 	free(cd->path_free);
    -+	string_list_clear(&cd->reference, 1);
    + 	.single_branch = -1, \
      }
      
    ++static void module_clone_data_release(struct module_clone_data *cd)
    ++{
    ++	string_list_clear(&cd->reference, 1);
    ++}
    ++
      struct submodule_alternate_setup {
    + 	const char *submodule_name;
    + 	enum SUBMODULE_ALTERNATE_ERROR_MODE {
     @@ builtin/submodule--helper.c: static int add_possible_reference_from_superproject(
      
      		sm_alternate = compute_alternate_path(sb.buf, &err);
    @@ builtin/submodule--helper.c: static int add_possible_reference_from_superproject
      			free(sm_alternate);
      		} else {
      			switch (sas->error_mode) {
    +@@ builtin/submodule--helper.c: static int module_clone(int argc, const char **argv, const char *prefix)
    + 
    + 	clone_submodule(&clone_data);
    + 	list_objects_filter_release(&filter_options);
    ++	module_clone_data_release(&clone_data);
    + 	return 0;
    + }
    + 
    +@@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add_data)
    + {
    + 	char *submod_gitdir_path;
    + 	struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
    ++	int ret;
    + 
    + 	/* perhaps the path already exists and is already a git repo, else clone it */
    + 	if (is_directory(add_data->sm_path)) {
     @@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add_data)
      		clone_data.url = add_data->realrepo;
      		clone_data.quiet = add_data->quiet;
    @@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add
      		clone_data.dissociate = add_data->dissociate;
      		if (add_data->depth >= 0)
      			clone_data.depth = xstrfmt("%d", add_data->depth);
    + 
    +-		if (clone_submodule(&clone_data))
    +-			return -1;
    ++		if (clone_submodule(&clone_data)) {
    ++			ret = -1;
    ++			goto cleanup;
    ++		}
    + 
    + 		prepare_submodule_repo_env(&cp.env);
    + 		cp.git_cmd = 1;
    +@@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add_data)
    + 		if (run_command(&cp))
    + 			die(_("unable to checkout submodule '%s'"), add_data->sm_path);
    + 	}
    +-	return 0;
    ++	ret = 0;
    ++cleanup:
    ++	module_clone_data_release(&clone_data);
    ++	return ret;
    + }
    + 
    + static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
 -:  ----------- > 15:  abd8e2eef3a submodule--helper: fix obscure leak in module_add()
 -:  ----------- > 16:  1f01203d154 submodule--helper: fix a leak in module_add()
 -:  ----------- > 17:  12b8a945486 submodule--helper: fix a memory leak in print_status()
 9:  7b36f71879e ! 18:  fac2c4491f3 submodule--helper: free "char *" in "struct update_data"
    @@ Metadata
     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## Commit message ##
    -    submodule--helper: free "char *" in "struct update_data"
    +    submodule--helper: free some "displaypath" in "struct update_data"
     
    -    Make the update_data_release() function free the "recursive_prefix"
    -    and "displaypath" members when appropriate. For the former it could
    -    come from either "argv" or from our own allocation, so we need to keep
    -    track of a "to_free" sibling seperately.
    +    Make the update_data_release() function free "displaypath" member when
    +    appropriate. The "displaypath" member is always ours, the "const" on
    +    the "char *" was wrong to begin with.
     
    -    For "displaypath" it's always ours, so the "const char *" type was
    -    wrong to begin with, it should be a "char *" instead.
    -
    -    For update_submodule() we'll free() these as we go along, it's called
    -    in a loop by update_submodules(), and we'll need to free the "last"
    -    one.
    +    This leaves a leak of "displaypath" in update_submodule(), which as
    +    we'll see in subsequent commits is harder to deal with than this
    +    trivial fix.
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
    @@ builtin/submodule--helper.c: struct update_data {
      	module_list_release(&ud->list);
      }
      
    +
    + ## t/t2403-worktree-move.sh ##
    +@@
    + 
    + test_description='test git worktree move, remove, lock and unlock'
    + 
    ++TEST_PASSES_SANITIZE_LEAK=true
    + . ./test-lib.sh
    + 
    + test_expect_success 'setup' '
    +
    + ## t/t7412-submodule-absorbgitdirs.sh ##
    +@@ t/t7412-submodule-absorbgitdirs.sh: This test verifies that `git submodue absorbgitdirs` moves a submodules git
    + directory into the superproject.
    + '
    + 
    ++TEST_PASSES_SANITIZE_LEAK=true
    + . ./test-lib.sh
    + 
    + test_expect_success 'setup a real submodule' '
 -:  ----------- > 19:  cf0c8851954 submodule--helper: rename "int res" to "int ret"
 -:  ----------- > 20:  7882e33cdca submodule--helper: add skeleton "goto cleanup" to update_submodule()
 1:  f1c847ad613 ! 21:  31395a2b4f8 submodule.c: free() memory from xgetcwd()
    @@ Metadata
     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## Commit message ##
    -    submodule.c: free() memory from xgetcwd()
    +    submodule--helper: don't exit() on failure, return
     
    -    Fix a memory leak in code added in bf0231c6614 (rev-parse: add
    -    --show-superproject-working-tree, 2017-03-08), we should never have
    -    made the result of xgetcwd() a "const char *", as we return a
    -    strbuf_detach()'d value. Let's fix that and free() it when we're done
    -    with it.
    +    Change code downstream of module_update() to short-circuit and return
    +    to the top-level on failure, rather than calling exit().
     
    -    We can't mark any tests passing passing with SANITIZE=leak using
    -    "TEST_PASSES_SANITIZE_LEAK=true" as a result of this change, but
    -    e.g. "t/t1500-rev-parse.sh" now gets closer to passing.
    +    To do so we need to diligently check whether we "must_die_on_failure",
    +    which is a pattern started in c51f8f94e5b (submodule--helper: run
    +    update procedures from C, 2021-08-24), but which hadn't been completed
    +    to the point where we could avoid calling exit() here.
    +
    +    This introduces no functional changes, but makes it easier to both
    +    call these routines as a library in the future, and to avoid leaking
    +    memory.
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
    - ## submodule.c ##
    -@@ submodule.c: int get_superproject_working_tree(struct strbuf *buf)
    + ## builtin/submodule--helper.c ##
    +@@ builtin/submodule--helper.c: static int fetch_in_submodule(const char *module_path, int depth, int quiet, str
    + 	return run_command(&cp);
    + }
    + 
    +-static int run_update_command(struct update_data *ud, int subforce)
    ++static int run_update_command(struct update_data *ud, int subforce,
    ++			      int *must_die_on_failurep)
    + {
      	struct child_process cp = CHILD_PROCESS_INIT;
    - 	struct strbuf sb = STRBUF_INIT;
    - 	struct strbuf one_up = STRBUF_INIT;
    --	const char *cwd = xgetcwd();
    -+	char *cwd = xgetcwd();
    - 	int ret = 0;
    - 	const char *subpath;
    - 	int code;
    -@@ submodule.c: int get_superproject_working_tree(struct strbuf *buf)
    - 		ret = 1;
    - 		free(super_wt);
    + 	char *oid = oid_to_hex(&ud->oid);
    +@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)
    + 			BUG("unexpected update strategy type: %s",
    + 			    submodule_strategy_to_string(&ud->update_strategy));
    + 		}
    +-		if (must_die_on_failure)
    +-			exit(128);
    ++		if (must_die_on_failure) {
    ++			*must_die_on_failurep = 1;
    ++			return 128;
    ++		}
    + 
    + 		/* the command failed, but update must continue */
    + 		return 1;
    +@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)
    + 	return 0;
    + }
    + 
    +-static int run_update_procedure(struct update_data *ud)
    ++static int run_update_procedure(struct update_data *ud,
    ++				int *must_die_on_failure)
    + {
    + 	int subforce = is_null_oid(&ud->suboid) || ud->force;
    + 
    +@@ builtin/submodule--helper.c: static int run_update_procedure(struct update_data *ud)
    + 			    ud->displaypath, oid_to_hex(&ud->oid));
    + 	}
    + 
    +-	return run_update_command(ud, subforce);
    ++	return run_update_command(ud, subforce, must_die_on_failure);
    + }
    + 
    + static const char *remote_submodule_branch(const char *path)
    +@@ builtin/submodule--helper.c: static void update_data_to_args(struct update_data *update_data, struct strvec *
    + 				    "--no-single-branch");
    + }
    + 
    +-static int update_submodule(struct update_data *update_data)
    ++static int update_submodule(struct update_data *update_data,
    ++			    int *must_die_on_failure)
    + {
    + 	int ret;
    + 
    +@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)
    + 	}
    + 
    + 	if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) {
    +-		ret = run_update_procedure(update_data);
    +-		if (ret) {
    ++		ret = run_update_procedure(update_data, must_die_on_failure);
    ++		if (ret && *must_die_on_failure) {
    ++			goto cleanup;
    ++		} else if (ret) {
    + 			ret = 1;
    + 			goto cleanup;
    + 		}
    +@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)
    + 		die_message(_("Failed to recurse into submodule path '%s'"),
    + 			    update_data->displaypath);
    + 		if (ret == 128) {
    +-			exit(ret);
    ++			*must_die_on_failure = 1;
    ++			goto cleanup;
    + 		} else if (ret) {
    + 			ret = 1;
    + 			goto cleanup;
    +@@ builtin/submodule--helper.c: static int update_submodules(struct update_data *update_data)
    + 
    + 	for (i = 0; i < suc.update_clone_nr; i++) {
    + 		struct update_clone_data ucd = suc.update_clone[i];
    ++		int code;
    ++		int must_die_on_failure = 0;
    + 
    + 		oidcpy(&update_data->oid, &ucd.oid);
    + 		update_data->just_cloned = ucd.just_cloned;
    + 		update_data->sm_path = ucd.sub->path;
    + 
    +-		if (update_submodule(update_data))
    +-			res = 1;
    ++		code = update_submodule(update_data, &must_die_on_failure);
    ++		if (code)
    ++			res = code;
    ++		if (must_die_on_failure)
    ++			goto cleanup;
      	}
    -+	free(cwd);
    - 	strbuf_release(&sb);
      
    - 	code = finish_command(&cp);
    + cleanup:
 -:  ----------- > 22:  a2168cf1378 submodule--helper: free rest of "displaypath" in "struct update_data"
 -:  ----------- > 23:  d77c6665ca9 submodule--helper: fix bad config API usage
 -:  ----------- > 24:  531db4ddae6 submodule--helper: fix a configure_added_submodule() leak
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply	[flat|nested] 186+ messages in thread

* [PATCH v2 01/24] submodule--helper: replace memset() with { 0 }-initialization
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:46   ` Ævar Arnfjörð Bjarmason
  2022-07-19 21:21     ` Junio C Hamano
  2022-07-19 20:46   ` [PATCH v2 02/24] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
                     ` (23 subsequent siblings)
  24 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:46 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Use the less verbose { 0 }-initialization syntax rather than memset()
in builtin/submodule--helper.c, this doesn't make a difference in
terms of behavior, but as we're about to modify adjacent code makes
this more consistent, and lets us avoid worrying about when the
memset() happens v.s. a "goto cleanup".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index fac52ade5e1..73717be957c 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1744,7 +1744,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 {
 	int dissociate = 0, quiet = 0, progress = 0, require_init = 0;
 	struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
-	struct list_objects_filter_options filter_options;
+	struct list_objects_filter_options filter_options = { 0 };
 
 	struct option module_clone_options[] = {
 		OPT_STRING(0, "prefix", &clone_data.prefix,
@@ -1786,7 +1786,6 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 		NULL
 	};
 
-	memset(&filter_options, 0, sizeof(filter_options));
 	argc = parse_options(argc, argv, prefix, module_clone_options,
 			     git_submodule_helper_usage, 0);
 
@@ -2563,7 +2562,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 {
 	struct pathspec pathspec;
 	struct update_data opt = UPDATE_DATA_INIT;
-	struct list_objects_filter_options filter_options;
+	struct list_objects_filter_options filter_options = { 0 };
 	int ret;
 
 	struct option module_update_options[] = {
@@ -2623,7 +2622,6 @@ static int module_update(int argc, const char **argv, const char *prefix)
 	update_clone_config_from_gitmodules(&opt.max_jobs);
 	git_config(git_update_clone_config, &opt.max_jobs);
 
-	memset(&filter_options, 0, sizeof(filter_options));
 	argc = parse_options(argc, argv, prefix, module_update_options,
 			     git_submodule_helper_usage, 0);
 
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 02/24] submodule--helper: fix a leak in "clone_submodule"
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  2022-07-19 20:46   ` [PATCH v2 01/24] submodule--helper: replace memset() with { 0 }-initialization Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:46   ` Ævar Arnfjörð Bjarmason
  2022-07-19 21:31     ` Junio C Hamano
  2022-07-19 20:46   ` [PATCH v2 03/24] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
                     ` (22 subsequent siblings)
  24 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:46 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak of the "path" member of the "struct
module_clone_data" in clone_submodule(). This fixes leaks in code
added in f8eaa0ba98b (submodule--helper, module_clone: always operate
on absolute paths, 2016-03-31).

For the "else" case we don't need to xstrdup() the "clone_data->path",
if we're not creating a new one we'll leave it to our caller to keep
track of it.

In the case of the module_clone() caller it's from "argv", and doesn't
need to be free'd, and in the case of the add_submodule() caller we
get a pointer to "sm_path", which doesn't need to be directly free'd
either.

Fixing this leak makes several tests pass, so let's mark them as
passing with TEST_PASSES_SANITIZE_LEAK=true.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c   | 6 ++++--
 t/t1500-rev-parse.sh          | 1 +
 t/t6008-rev-list-submodule.sh | 1 +
 t/t7414-submodule-mistakes.sh | 2 ++
 t/t7506-status-submodule.sh   | 1 +
 t/t7507-commit-verbose.sh     | 2 ++
 6 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 73717be957c..4155d2450e0 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1644,6 +1644,7 @@ static int clone_submodule(struct module_clone_data *clone_data)
 	char *sm_alternate = NULL, *error_strategy = NULL;
 	struct strbuf sb = STRBUF_INIT;
 	struct child_process cp = CHILD_PROCESS_INIT;
+	char *to_free = NULL;
 
 	submodule_name_to_gitdir(&sb, the_repository, clone_data->name);
 	sm_gitdir = absolute_pathdup(sb.buf);
@@ -1651,9 +1652,9 @@ static int clone_submodule(struct module_clone_data *clone_data)
 
 	if (!is_absolute_path(clone_data->path)) {
 		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
-		clone_data->path = strbuf_detach(&sb, NULL);
+		clone_data->path = to_free = strbuf_detach(&sb, NULL);
 	} else {
-		clone_data->path = xstrdup(clone_data->path);
+		clone_data->path = clone_data->path;
 	}
 
 	if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)
@@ -1737,6 +1738,7 @@ static int clone_submodule(struct module_clone_data *clone_data)
 	strbuf_release(&sb);
 	free(sm_gitdir);
 	free(p);
+	free(to_free);
 	return 0;
 }
 
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 1c2df08333b..0e13bcb4ebb 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -4,6 +4,7 @@ test_description='test git rev-parse'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_one () {
diff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh
index 3153a0d8910..12e67e187ef 100755
--- a/t/t6008-rev-list-submodule.sh
+++ b/t/t6008-rev-list-submodule.sh
@@ -8,6 +8,7 @@ test_description='git rev-list involving submodules that this repo has'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh
index f2e7df59cf2..3269298197c 100755
--- a/t/t7414-submodule-mistakes.sh
+++ b/t/t7414-submodule-mistakes.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='handling of common mistakes people may make with submodules'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create embedded repository' '
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index 3fcb44767f5..f5426a8e589 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='git status for submodule'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_create_repo_with_commit () {
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index ed2653d46fe..92462a22374 100755
--- a/t/t7507-commit-verbose.sh
+++ b/t/t7507-commit-verbose.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='verbose commit template'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 write_script "check-for-diff" <<\EOF &&
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 03/24] submodule--helper: fix trivial get_default_remote_submodule() leak
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  2022-07-19 20:46   ` [PATCH v2 01/24] submodule--helper: replace memset() with { 0 }-initialization Ævar Arnfjörð Bjarmason
  2022-07-19 20:46   ` [PATCH v2 02/24] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:46   ` Ævar Arnfjörð Bjarmason
  2022-07-19 20:46   ` [PATCH v2 04/24] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
                     ` (21 subsequent siblings)
  24 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:46 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in code added in 1012a5cbc3f (submodule--helper
run-update-procedure: learn --remote, 2022-03-04), we need to free()
the xstrdup()'d string. This gets e.g. t/t7419-submodule-set-branch.sh
closer to passing under SANITIZE=leak.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 4155d2450e0..b36919b66c5 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2473,6 +2473,8 @@ static int update_submodule(struct update_data *update_data)
 		const char *branch = remote_submodule_branch(update_data->sm_path);
 		char *remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch);
 
+		free(remote_name);
+
 		if (!update_data->nofetch) {
 			if (fetch_in_submodule(update_data->sm_path, update_data->depth,
 					      0, NULL))
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 04/24] submodule--helper: fix most "struct pathspec" memory leaks
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (2 preceding siblings ...)
  2022-07-19 20:46   ` [PATCH v2 03/24] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:46   ` Ævar Arnfjörð Bjarmason
  2022-07-20 16:33     ` Junio C Hamano
  2022-07-21 17:49     ` Glen Choo
  2022-07-19 20:46   ` [PATCH v2 05/24] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
                     ` (20 subsequent siblings)
  24 siblings, 2 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:46 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Call clear_pathspec() at the end of various functions that work with
and allocate a "struct pathspec".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 115 +++++++++++++++++++++++++-----------
 1 file changed, 81 insertions(+), 34 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index b36919b66c5..28c5fdb8954 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -269,7 +269,7 @@ static char *get_up_path(const char *path)
 static int module_list(int argc, const char **argv, const char *prefix)
 {
 	int i;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 
 	struct option module_list_options[] = {
@@ -278,6 +278,7 @@ static int module_list(int argc, const char **argv, const char *prefix)
 			   N_("alternative anchor for relative paths")),
 		OPT_END()
 	};
+	int ret;
 
 	const char *const git_submodule_helper_usage[] = {
 		N_("git submodule--helper list [--prefix=<path>] [<path>...]"),
@@ -287,8 +288,10 @@ static int module_list(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, module_list_options,
 			     git_submodule_helper_usage, 0);
 
-	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
+		ret = 1;
+		goto cleanup;
+	}
 
 	for (i = 0; i < list.nr; i++) {
 		const struct cache_entry *ce = list.entries[i];
@@ -302,7 +305,10 @@ static int module_list(int argc, const char **argv, const char *prefix)
 
 		fprintf(stdout, "%s\n", ce->name);
 	}
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static void for_each_listed_submodule(const struct module_list *list,
@@ -427,7 +433,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
 static int module_foreach(int argc, const char **argv, const char *prefix)
 {
 	struct foreach_cb info = FOREACH_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 
 	struct option module_foreach_options[] = {
@@ -441,12 +447,15 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 		N_("git submodule foreach [--quiet] [--recursive] [--] <command>"),
 		NULL
 	};
+	int ret;
 
 	argc = parse_options(argc, argv, prefix, module_foreach_options,
 			     git_submodule_helper_usage, 0);
 
-	if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
-		return 1;
+	if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0) {
+		ret = 1;
+		goto cleanup;
+	}
 
 	info.argc = argc;
 	info.argv = argv;
@@ -454,7 +463,10 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static int starts_with_dot_slash(const char *const path)
@@ -562,7 +574,7 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data
 static int module_init(int argc, const char **argv, const char *prefix)
 {
 	struct init_cb info = INIT_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 
@@ -575,12 +587,15 @@ static int module_init(int argc, const char **argv, const char *prefix)
 		N_("git submodule init [<options>] [<path>]"),
 		NULL
 	};
+	int ret;
 
 	argc = parse_options(argc, argv, prefix, module_init_options,
 			     git_submodule_helper_usage, 0);
 
-	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
+		ret = 1;
+		goto cleanup;
+	}
 
 	/*
 	 * If there are no path args and submodule.active is set then,
@@ -595,7 +610,10 @@ static int module_init(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, init_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct status_cb {
@@ -740,7 +758,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,
 static int module_status(int argc, const char **argv, const char *prefix)
 {
 	struct status_cb info = STATUS_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 
@@ -755,12 +773,15 @@ static int module_status(int argc, const char **argv, const char *prefix)
 		N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"),
 		NULL
 	};
+	int ret;
 
 	argc = parse_options(argc, argv, prefix, module_status_options,
 			     git_submodule_helper_usage, 0);
 
-	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
+		ret = 1;
+		goto cleanup;
+	}
 
 	info.prefix = prefix;
 	if (quiet)
@@ -768,7 +789,10 @@ static int module_status(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, status_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static int module_name(int argc, const char **argv, const char *prefix)
@@ -1105,7 +1129,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 	struct strvec diff_args = STRVEC_INIT;
 	struct rev_info rev;
 	struct module_cb_list list = MODULE_CB_LIST_INIT;
-	int ret = 0;
+	int ret;
 
 	strvec_push(&diff_args, get_diff_cmd(diff_cmd));
 	if (info->cached)
@@ -1145,6 +1169,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 	else
 		run_diff_files(&rev, 0);
 	prepare_submodule_summary(info, &list);
+	ret = 0;
 cleanup:
 	strvec_clear(&diff_args);
 	release_revisions(&rev);
@@ -1326,10 +1351,11 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data
 static int module_sync(int argc, const char **argv, const char *prefix)
 {
 	struct sync_cb info = SYNC_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	int recursive = 0;
+	int ret;
 
 	struct option module_sync_options[] = {
 		OPT__QUIET(&quiet, N_("suppress output of synchronizing submodule url")),
@@ -1346,8 +1372,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, module_sync_options,
 			     git_submodule_helper_usage, 0);
 
-	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
+		ret = 1;
+		goto cleanup;
+	}
 
 	info.prefix = prefix;
 	if (quiet)
@@ -1357,7 +1385,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, sync_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct deinit_cb {
@@ -1464,7 +1495,7 @@ static void deinit_submodule_cb(const struct cache_entry *list_item,
 static int module_deinit(int argc, const char **argv, const char *prefix)
 {
 	struct deinit_cb info = DEINIT_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	int force = 0;
@@ -1481,6 +1512,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 		N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
 		NULL
 	};
+	int ret;
 
 	argc = parse_options(argc, argv, prefix, module_deinit_options,
 			     git_submodule_helper_usage, 0);
@@ -1494,8 +1526,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 	if (!argc && !all)
 		die(_("Use '--all' if you really want to deinitialize all submodules"));
 
-	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
+		ret = 1;
+		goto cleanup;
+	}
 
 	info.prefix = prefix;
 	if (quiet)
@@ -1505,7 +1539,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, deinit_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct module_clone_data {
@@ -2564,7 +2601,7 @@ static int update_submodules(struct update_data *update_data)
 
 static int module_update(int argc, const char **argv, const char *prefix)
 {
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct update_data opt = UPDATE_DATA_INIT;
 	struct list_objects_filter_options filter_options = { 0 };
 	int ret;
@@ -2643,8 +2680,8 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		opt.update_strategy.type = opt.update_default;
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {
-		list_objects_filter_release(&filter_options);
-		return 1;
+		ret = 1;
+		goto cleanup;
 	}
 
 	if (pathspec.nr)
@@ -2655,8 +2692,10 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		struct init_cb info = INIT_CB_INIT;
 
 		if (module_list_compute(argc, argv, opt.prefix,
-					&pathspec, &list) < 0)
-			return 1;
+					&pathspec, &list) < 0) {
+			ret = 1;
+			goto cleanup;
+		}
 
 		/*
 		 * If there are no path args and submodule.active is set then,
@@ -2673,7 +2712,9 @@ static int module_update(int argc, const char **argv, const char *prefix)
 	}
 
 	ret = update_submodules(&opt);
+cleanup:
 	list_objects_filter_release(&filter_options);
+	clear_pathspec(&pathspec);
 	return ret;
 }
 
@@ -2757,9 +2798,10 @@ static int push_check(int argc, const char **argv, const char *prefix)
 static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 {
 	int i;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;
+	int ret;
 
 	struct option embed_gitdir_options[] = {
 		OPT_STRING(0, "prefix", &prefix,
@@ -2778,13 +2820,18 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, embed_gitdir_options,
 			     git_submodule_helper_usage, 0);
 
-	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
+		ret = 1;
+		goto cleanup;
+	}
 
 	for (i = 0; i < list.nr; i++)
 		absorb_git_dir_into_superproject(list.entries[i]->name, flags);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static int is_active(int argc, const char **argv, const char *prefix)
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 05/24] submodule--helper: "struct pathspec" memory leak in module_update()
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (3 preceding siblings ...)
  2022-07-19 20:46   ` [PATCH v2 04/24] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:46   ` Ævar Arnfjörð Bjarmason
  2022-07-20 16:43     ` Junio C Hamano
  2022-07-21 19:54     ` Glen Choo
  2022-07-19 20:46   ` [PATCH v2 06/24] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
                     ` (19 subsequent siblings)
  24 siblings, 2 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:46 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

The module_update() function calls module_list_compute() twice, which
in turn will reset the "struct pathspec" passed to it. Let's instead
track two of them, and clear them both.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 28c5fdb8954..7466e781e97 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2602,6 +2602,7 @@ static int update_submodules(struct update_data *update_data)
 static int module_update(int argc, const char **argv, const char *prefix)
 {
 	struct pathspec pathspec = { 0 };
+	struct pathspec pathspec2 = { 0 };
 	struct update_data opt = UPDATE_DATA_INIT;
 	struct list_objects_filter_options filter_options = { 0 };
 	int ret;
@@ -2692,7 +2693,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		struct init_cb info = INIT_CB_INIT;
 
 		if (module_list_compute(argc, argv, opt.prefix,
-					&pathspec, &list) < 0) {
+					&pathspec2, &list) < 0) {
 			ret = 1;
 			goto cleanup;
 		}
@@ -2715,6 +2716,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 cleanup:
 	list_objects_filter_release(&filter_options);
 	clear_pathspec(&pathspec);
+	clear_pathspec(&pathspec2);
 	return ret;
 }
 
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 06/24] submodule--helper: don't leak {run,capture}_command() cp.dir argument
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (4 preceding siblings ...)
  2022-07-19 20:46   ` [PATCH v2 05/24] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:46   ` Ævar Arnfjörð Bjarmason
  2022-07-19 20:46   ` [PATCH v2 07/24] submodule--helper: add "const" to copy of "update_data" Ævar Arnfjörð Bjarmason
                     ` (18 subsequent siblings)
  24 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:46 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak in c51f8f94e5b (submodule--helper: run update
procedures from C, 2021-08-24) and 3c3558f0953 (submodule--helper: run
update using child process struct, 2022-03-15) by not allocating
memory in the first place.

The "dir" member of "struct child_process" will not be modified by
that API, and it's declared to be "const char *". So let's not
needlessly duplicate these strings.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 7466e781e97..3d32fdb3960 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2194,7 +2194,7 @@ static int is_tip_reachable(const char *path, struct object_id *oid)
 	char *hex = oid_to_hex(oid);
 
 	cp.git_cmd = 1;
-	cp.dir = xstrdup(path);
+	cp.dir = path;
 	cp.no_stderr = 1;
 	strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL);
 
@@ -2212,7 +2212,7 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str
 
 	prepare_submodule_repo_env(&cp.env);
 	cp.git_cmd = 1;
-	cp.dir = xstrdup(module_path);
+	cp.dir = module_path;
 
 	strvec_push(&cp.args, "fetch");
 	if (quiet)
@@ -2267,7 +2267,7 @@ static int run_update_command(struct update_data *ud, int subforce)
 	}
 	strvec_push(&cp.args, oid);
 
-	cp.dir = xstrdup(ud->sm_path);
+	cp.dir = ud->sm_path;
 	prepare_submodule_repo_env(&cp.env);
 	if (run_command(&cp)) {
 		switch (ud->update_strategy.type) {
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 07/24] submodule--helper: add "const" to copy of "update_data"
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (5 preceding siblings ...)
  2022-07-19 20:46   ` [PATCH v2 06/24] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:46   ` Ævar Arnfjörð Bjarmason
  2022-07-19 20:46   ` [PATCH v2 08/24] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
                     ` (17 subsequent siblings)
  24 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:46 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

From: Glen Choo <chooglen@google.com>

Add a "const" to the copy of "struct update_data" that's tracked by
the "struct submodule_update_clone", as it neither owns nor modifies
it.

Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 3d32fdb3960..7e9af9999bb 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1889,7 +1889,7 @@ struct submodule_update_clone {
 	int current;
 
 	/* configuration parameters which are passed on to the children */
-	struct update_data *update_data;
+	const struct update_data *update_data;
 
 	/* to be consumed by update_submodule() */
 	struct update_clone_data *update_clone;
@@ -1974,7 +1974,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
 	const char *update_string;
 	enum submodule_update_type update_type;
 	char *key;
-	struct update_data *ud = suc->update_data;
+	const struct update_data *ud = suc->update_data;
 	char *displaypath = get_submodule_displaypath(ce->name, ud->prefix);
 	struct strbuf sb = STRBUF_INIT;
 	int needs_cloning = 0;
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 08/24] submodule--helper: add and use *_release() functions
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (6 preceding siblings ...)
  2022-07-19 20:46   ` [PATCH v2 07/24] submodule--helper: add "const" to copy of "update_data" Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:46   ` Ævar Arnfjörð Bjarmason
  2022-07-20 17:06     ` Junio C Hamano
  2022-07-19 20:47   ` [PATCH v2 09/24] submodule--helper: refactor "errmsg_str" to be a "struct strbuf" Ævar Arnfjörð Bjarmason
                     ` (16 subsequent siblings)
  24 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:46 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Add release functions for "struct module_list", "struct
submodule_update_clone" and "struct update_data".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c      | 29 ++++++++++++++++++++++++++++-
 t/t6134-pathspec-in-submodule.sh |  1 +
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 7e9af9999bb..5d26e188560 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -182,6 +182,11 @@ struct module_list {
 };
 #define MODULE_LIST_INIT { 0 }
 
+static void module_list_release(struct module_list *ml)
+{
+	free(ml->entries);
+}
+
 static int module_list_compute(int argc, const char **argv,
 			       const char *prefix,
 			       struct pathspec *pathspec,
@@ -243,7 +248,7 @@ static void module_list_active(struct module_list *list)
 		active_modules.entries[active_modules.nr++] = ce;
 	}
 
-	free(list->entries);
+	module_list_release(list);
 	*list = active_modules;
 }
 
@@ -307,6 +312,7 @@ static int module_list(int argc, const char **argv, const char *prefix)
 	}
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -465,6 +471,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -612,6 +619,7 @@ static int module_init(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -791,6 +799,7 @@ static int module_status(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1387,6 +1396,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1541,6 +1551,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1904,6 +1915,12 @@ struct submodule_update_clone {
 };
 #define SUBMODULE_UPDATE_CLONE_INIT { 0 }
 
+static void submodule_update_clone_release(struct submodule_update_clone *suc)
+{
+	free(suc->update_clone);
+	free(suc->failed_clones);
+}
+
 struct update_data {
 	const char *prefix;
 	const char *displaypath;
@@ -1942,6 +1959,11 @@ struct update_data {
 	.max_jobs = 1, \
 }
 
+static void update_data_release(struct update_data *ud)
+{
+	module_list_release(&ud->list);
+}
+
 static void next_submodule_warn_missing(struct submodule_update_clone *suc,
 		struct strbuf *out, const char *displaypath)
 {
@@ -2595,6 +2617,7 @@ static int update_submodules(struct update_data *update_data)
 	}
 
 cleanup:
+	submodule_update_clone_release(&suc);
 	string_list_clear(&update_data->references, 0);
 	return res;
 }
@@ -2694,6 +2717,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 
 		if (module_list_compute(argc, argv, opt.prefix,
 					&pathspec2, &list) < 0) {
+			module_list_release(&list);
 			ret = 1;
 			goto cleanup;
 		}
@@ -2710,10 +2734,12 @@ static int module_update(int argc, const char **argv, const char *prefix)
 			info.flags |= OPT_QUIET;
 
 		for_each_listed_submodule(&list, init_submodule_cb, &info);
+		module_list_release(&list);
 	}
 
 	ret = update_submodules(&opt);
 cleanup:
+	update_data_release(&opt);
 	list_objects_filter_release(&filter_options);
 	clear_pathspec(&pathspec);
 	clear_pathspec(&pathspec2);
@@ -2833,6 +2859,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 	ret = 0;
 cleanup:
 	clear_pathspec(&pathspec);
+	module_list_release(&list);
 	return ret;
 }
 
diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh
index 0f1cb49cedc..3a241f259de 100755
--- a/t/t6134-pathspec-in-submodule.sh
+++ b/t/t6134-pathspec-in-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='test case exclude pathspec'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a submodule' '
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 09/24] submodule--helper: refactor "errmsg_str" to be a "struct strbuf"
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (7 preceding siblings ...)
  2022-07-19 20:46   ` [PATCH v2 08/24] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:47   ` Ævar Arnfjörð Bjarmason
  2022-07-19 20:47   ` [PATCH v2 10/24] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
                     ` (15 subsequent siblings)
  24 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

From: Glen Choo <chooglen@google.com>

Refactor code added in e83e3333b57 (submodule: port submodule
subcommand 'summary' from shell to C, 2020-08-13) so that "errmsg" and
"errmsg_str" are folded into one. The distinction between the empty
string and NULL is something that's tested for by
e.g. "t/t7401-submodule-summary.sh".

This is in preparation for fixing a memory leak the "struct strbuf" in
the pre-image.

Let's also pass a "const char *" to print_submodule_summary(), as it
should not be modifying the "errmsg".

Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 5d26e188560..c5127c1d50e 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -874,7 +874,7 @@ static char *verify_submodule_committish(const char *sm_path,
 	return strbuf_detach(&result, NULL);
 }
 
-static void print_submodule_summary(struct summary_cb *info, char *errmsg,
+static void print_submodule_summary(struct summary_cb *info, const char *errmsg,
 				    int total_commits, const char *displaypath,
 				    const char *src_abbrev, const char *dst_abbrev,
 				    struct module_cb *p)
@@ -932,7 +932,7 @@ static void generate_submodule_summary(struct summary_cb *info,
 {
 	char *displaypath, *src_abbrev = NULL, *dst_abbrev;
 	int missing_src = 0, missing_dst = 0;
-	char *errmsg = NULL;
+	struct strbuf errmsg = STRBUF_INIT;
 	int total_commits = -1;
 
 	if (!info->cached && oideq(&p->oid_dst, null_oid())) {
@@ -1032,23 +1032,21 @@ static void generate_submodule_summary(struct summary_cb *info,
 		 * submodule, i.e., deleted or changed to blob
 		 */
 		if (S_ISGITLINK(p->mod_dst)) {
-			struct strbuf errmsg_str = STRBUF_INIT;
 			if (missing_src && missing_dst) {
-				strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commits %s and %s\n",
+				strbuf_addf(&errmsg, "  Warn: %s doesn't contain commits %s and %s\n",
 					    displaypath, oid_to_hex(&p->oid_src),
 					    oid_to_hex(&p->oid_dst));
 			} else {
-				strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commit %s\n",
+				strbuf_addf(&errmsg, "  Warn: %s doesn't contain commit %s\n",
 					    displaypath, missing_src ?
 					    oid_to_hex(&p->oid_src) :
 					    oid_to_hex(&p->oid_dst));
 			}
-			errmsg = strbuf_detach(&errmsg_str, NULL);
 		}
 	}
 
-	print_submodule_summary(info, errmsg, total_commits,
-				displaypath, src_abbrev,
+	print_submodule_summary(info, errmsg.len ? errmsg.buf : NULL,
+				total_commits, displaypath, src_abbrev,
 				dst_abbrev, p);
 
 	free(displaypath);
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 10/24] submodule--helper: fix "errmsg_str" memory leak
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (8 preceding siblings ...)
  2022-07-19 20:47   ` [PATCH v2 09/24] submodule--helper: refactor "errmsg_str" to be a "struct strbuf" Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:47   ` Ævar Arnfjörð Bjarmason
  2022-07-19 20:47   ` [PATCH v2 11/24] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
                     ` (14 subsequent siblings)
  24 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak introduced in e83e3333b57 (submodule: port submodule
subcommand 'summary' from shell to C, 2020-08-13), we sometimes append
to the "errmsg", and need to free the "struct strbuf".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index c5127c1d50e..e51640d020c 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1052,6 +1052,7 @@ static void generate_submodule_summary(struct summary_cb *info,
 	free(displaypath);
 	free(src_abbrev);
 	free(dst_abbrev);
+	strbuf_release(&errmsg);
 }
 
 static void prepare_submodule_summary(struct summary_cb *info,
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 11/24] submodule--helper: fix "sm_path" and other "module_cb_list" leaks
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (9 preceding siblings ...)
  2022-07-19 20:47   ` [PATCH v2 10/24] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:47   ` Ævar Arnfjörð Bjarmason
  2022-07-20 22:35     ` Junio C Hamano
  2022-07-19 20:47   ` [PATCH v2 12/24] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
                     ` (13 subsequent siblings)
  24 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix leaks in "struct module_cb_list" and the "struct module_cb" which
it contains, these fixes leaks in e83e3333b57 (submodule: port
submodule subcommand 'summary' from shell to C, 2020-08-13).

The "sm_path" should always have been a "char *", not a "const
char *", we always create it with xstrdup().

We can't mark any tests passing passing with SANITIZE=leak using
"TEST_PASSES_SANITIZE_LEAK=true" as a result of this change, but
"t7401-submodule-summary.sh" gets closer to passing as a result of
this change.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index e51640d020c..a95231b1698 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -828,9 +828,13 @@ struct module_cb {
 	struct object_id oid_src;
 	struct object_id oid_dst;
 	char status;
-	const char *sm_path;
+	char *sm_path;
 };
 #define MODULE_CB_INIT { 0 }
+static void module_cb_release(struct module_cb *mcb)
+{
+	free(mcb->sm_path);
+}
 
 struct module_cb_list {
 	struct module_cb **entries;
@@ -838,6 +842,19 @@ struct module_cb_list {
 };
 #define MODULE_CB_LIST_INIT { 0 }
 
+static void module_cb_list_release(struct module_cb_list *mcbl)
+{
+	int i;
+
+	for (i = 0; i < mcbl->nr; i++) {
+		struct module_cb *mcb = mcbl->entries[i];
+
+		module_cb_release(mcb);
+		free(mcb);
+	}
+	free(mcbl->entries);
+}
+
 struct summary_cb {
 	int argc;
 	const char **argv;
@@ -1181,6 +1198,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 cleanup:
 	strvec_clear(&diff_args);
 	release_revisions(&rev);
+	module_cb_list_release(&list);
 	return ret;
 }
 
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 12/24] submodule--helper: fix a leak with repo_clear()
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (10 preceding siblings ...)
  2022-07-19 20:47   ` [PATCH v2 11/24] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:47   ` Ævar Arnfjörð Bjarmason
  2022-07-19 20:47   ` [PATCH v2 13/24] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
                     ` (12 subsequent siblings)
  24 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Call repo_clear() in ensure_core_worktree() to free the "struct
repository". Fixes a leak that's been here since
74d4731da1f (submodule--helper: replace connect-gitdir-workingtree by
ensure-core-worktree, 2018-08-13).

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index a95231b1698..339ba94e658 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2457,6 +2457,7 @@ static void ensure_core_worktree(const char *path)
 		free(abs_path);
 		strbuf_release(&sb);
 	}
+	repo_clear(&subrepo);
 }
 
 static const char *submodule_update_type_to_label(enum submodule_update_type type)
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 13/24] submodule--helper: fix a memory leak in get_default_remote_submodule()
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (11 preceding siblings ...)
  2022-07-19 20:47   ` [PATCH v2 12/24] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:47   ` Ævar Arnfjörð Bjarmason
  2022-07-19 20:47   ` [PATCH v2 14/24] submodule--helper: fix "reference" leak is "module_clone_data" Ævar Arnfjörð Bjarmason
                     ` (11 subsequent siblings)
  24 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak in the get_default_remote_submodule() function added
in a77c3fcb5ec (submodule--helper: get remote names from any
repository, 2022-03-04), we need to repo_clear() the submodule we
initialize.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 339ba94e658..cef8f14efb5 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -62,9 +62,13 @@ static char *repo_get_default_remote(struct repository *repo)
 static char *get_default_remote_submodule(const char *module_path)
 {
 	struct repository subrepo;
+	char *ret;
 
 	repo_submodule_init(&subrepo, the_repository, module_path, null_oid());
-	return repo_get_default_remote(&subrepo);
+	ret = repo_get_default_remote(&subrepo);
+	repo_clear(&subrepo);
+
+	return ret;
 }
 
 static char *get_default_remote(void)
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 14/24] submodule--helper: fix "reference" leak is "module_clone_data"
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (12 preceding siblings ...)
  2022-07-19 20:47   ` [PATCH v2 13/24] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:47   ` Ævar Arnfjörð Bjarmason
  2022-07-21  0:50     ` Junio C Hamano
  2022-07-19 20:47   ` [PATCH v2 15/24] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
                     ` (10 subsequent siblings)
  24 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix leaks in the "reference" member of "struct module_clone_data" that
have been with us since 31224cbdc72 (clone: recursive and reference
option triggers submodule alternates, 2016-08-17) and
8c8195e9c3e (submodule--helper: introduce add-clone subcommand,
2021-07-10).

Those commits added an xstrdup()'d member of the
STRING_LIST_INIT_NODUP'd "struct string_list". We need to free()
those, but not the ones we get from argv, let's make use of the "util"
member, if it has a pointer it's the pointer we'll need to free.

To fix this create a a module_clone_data_release() function to go with
the MODULE_CLONE_DATA_INIT added in a98b02c1128 (submodule--helper:
refactor module_clone(), 2021-07-10). We only need to add it to
add_submodule() to fix the leak, but let's add it to module_clone() as
well for consistency.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 30 +++++++++++++++++++++++-------
 1 file changed, 23 insertions(+), 7 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index cef8f14efb5..eb2f09d1178 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1596,6 +1596,11 @@ struct module_clone_data {
 	.single_branch = -1, \
 }
 
+static void module_clone_data_release(struct module_clone_data *cd)
+{
+	string_list_clear(&cd->reference, 1);
+}
+
 struct submodule_alternate_setup {
 	const char *submodule_name;
 	enum SUBMODULE_ALTERNATE_ERROR_MODE {
@@ -1648,7 +1653,9 @@ static int add_possible_reference_from_superproject(
 
 		sm_alternate = compute_alternate_path(sb.buf, &err);
 		if (sm_alternate) {
-			string_list_append(sas->reference, xstrdup(sb.buf));
+			char *p = strbuf_detach(&sb, NULL);
+
+			string_list_append(sas->reference, p)->util = p;
 			free(sm_alternate);
 		} else {
 			switch (sas->error_mode) {
@@ -1872,6 +1879,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 
 	clone_submodule(&clone_data);
 	list_objects_filter_release(&filter_options);
+	module_clone_data_release(&clone_data);
 	return 0;
 }
 
@@ -3118,6 +3126,7 @@ static int add_submodule(const struct add_data *add_data)
 {
 	char *submod_gitdir_path;
 	struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
+	int ret;
 
 	/* perhaps the path already exists and is already a git repo, else clone it */
 	if (is_directory(add_data->sm_path)) {
@@ -3172,15 +3181,19 @@ static int add_submodule(const struct add_data *add_data)
 		clone_data.url = add_data->realrepo;
 		clone_data.quiet = add_data->quiet;
 		clone_data.progress = add_data->progress;
-		if (add_data->reference_path)
-			string_list_append(&clone_data.reference,
-					   xstrdup(add_data->reference_path));
+		if (add_data->reference_path) {
+			char *p = xstrdup(add_data->reference_path);
+
+			string_list_append(&clone_data.reference, p)->util = p;
+		}
 		clone_data.dissociate = add_data->dissociate;
 		if (add_data->depth >= 0)
 			clone_data.depth = xstrfmt("%d", add_data->depth);
 
-		if (clone_submodule(&clone_data))
-			return -1;
+		if (clone_submodule(&clone_data)) {
+			ret = -1;
+			goto cleanup;
+		}
 
 		prepare_submodule_repo_env(&cp.env);
 		cp.git_cmd = 1;
@@ -3199,7 +3212,10 @@ static int add_submodule(const struct add_data *add_data)
 		if (run_command(&cp))
 			die(_("unable to checkout submodule '%s'"), add_data->sm_path);
 	}
-	return 0;
+	ret = 0;
+cleanup:
+	module_clone_data_release(&clone_data);
+	return ret;
 }
 
 static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 15/24] submodule--helper: fix obscure leak in module_add()
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (13 preceding siblings ...)
  2022-07-19 20:47   ` [PATCH v2 14/24] submodule--helper: fix "reference" leak is "module_clone_data" Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:47   ` Ævar Arnfjörð Bjarmason
  2022-07-19 20:47   ` [PATCH v2 16/24] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
                     ` (9 subsequent siblings)
  24 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix an obscure leak in module_add()< if the "git add" command we were
piping to failed we'd fail to strbuf_release(&sb). This fixes a leak
introduced in a6226fd772b (submodule--helper: convert the bulk of
cmd_add() to C, 2021-08-10).

In fixing it move to a "goto cleanup" pattern, and since we need to
introduce a "ret" variable to do that let's also get rid of the
intermediate "exit_code" variable. The initialization to "-1" in
a6226fd772b has always been redundant, we'd only use the "exit_code"
value after assigning the return value of pipe_command() to it.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index eb2f09d1178..46685bf0610 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3355,6 +3355,8 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	int force = 0, quiet = 0, progress = 0, dissociate = 0;
 	struct add_data add_data = ADD_DATA_INIT;
 	char *to_free = NULL;
+	struct strbuf sb = STRBUF_INIT;
+	int ret;
 
 	struct option options[] = {
 		OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
@@ -3426,20 +3428,16 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	die_on_repo_without_commits(add_data.sm_path);
 
 	if (!force) {
-		int exit_code = -1;
-		struct strbuf sb = STRBUF_INIT;
 		struct child_process cp = CHILD_PROCESS_INIT;
 		cp.git_cmd = 1;
 		cp.no_stdout = 1;
 		strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
 			     "--no-warn-embedded-repo", add_data.sm_path, NULL);
-		if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
+		if ((ret = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
 			strbuf_complete_line(&sb);
 			fputs(sb.buf, stderr);
-			free(add_data.sm_path);
-			return exit_code;
+			goto cleanup;
 		}
-		strbuf_release(&sb);
 	}
 
 	if(!add_data.sm_name)
@@ -3455,14 +3453,18 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	add_data.dissociate = !!dissociate;
 
 	if (add_submodule(&add_data)) {
-		free(add_data.sm_path);
-		return 1;
+		ret = 1;
+		goto cleanup;
 	}
 	configure_added_submodule(&add_data);
+
+	ret = 0;
+cleanup:
 	free(add_data.sm_path);
 	free(to_free);
+	strbuf_release(&sb);
 
-	return 0;
+	return ret;
 }
 
 #define SUPPORT_SUPER_PREFIX (1<<0)
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 16/24] submodule--helper: fix a leak in module_add()
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (14 preceding siblings ...)
  2022-07-19 20:47   ` [PATCH v2 15/24] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:47   ` Ævar Arnfjörð Bjarmason
  2022-07-19 20:47   ` [PATCH v2 17/24] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
                     ` (8 subsequent siblings)
  24 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in module_path(), since a6226fd772b (submodule--helper:
convert the bulk of cmd_add() to C, 2021-08-10), we've been freeing
add_data.sm_path, but in this case we clobbered it, and didn't free
the value we clobbered.

This makes test 28 of "t/t7400-submodule-basic.sh" ("submodule add in
subdirectory") pass when we're compiled with SANITIZE=leak..

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 46685bf0610..642595cc3f4 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3398,8 +3398,11 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	else
 		add_data.sm_path = xstrdup(argv[1]);
 
-	if (prefix && *prefix && !is_absolute_path(add_data.sm_path))
-		add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
+	if (prefix && *prefix && !is_absolute_path(add_data.sm_path)) {
+		char *sm_path = add_data.sm_path;
+		add_data.sm_path = xstrfmt("%s%s", prefix, sm_path);
+		free(sm_path);
+	}
 
 	if (starts_with_dot_dot_slash(add_data.repo) ||
 	    starts_with_dot_slash(add_data.repo)) {
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 17/24] submodule--helper: fix a memory leak in print_status()
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (15 preceding siblings ...)
  2022-07-19 20:47   ` [PATCH v2 16/24] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:47   ` Ævar Arnfjörð Bjarmason
  2022-07-19 20:47   ` [PATCH v2 18/24] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
                     ` (7 subsequent siblings)
  24 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in print_status(), the compute_rev_name() function
implemented in this file will return a strbuf_detach()'d value, or
NULL.

This leak has existed since this code was added in
a9f8a37584a (submodule: port submodule subcommand 'status' from shell
to C, 2017-10-06), but in 0b5e2ea7cf3 (submodule--helper: don't print
null in 'submodule status', 2018-04-18) we added a "const"
intermediate variable for the return value, that "const" should be
removed.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 642595cc3f4..75e842af5ca 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -643,10 +643,11 @@ static void print_status(unsigned int flags, char state, const char *path,
 	printf("%c%s %s", state, oid_to_hex(oid), displaypath);
 
 	if (state == ' ' || state == '+') {
-		const char *name = compute_rev_name(path, oid_to_hex(oid));
+		char *name = compute_rev_name(path, oid_to_hex(oid));
 
 		if (name)
 			printf(" (%s)", name);
+		free(name);
 	}
 
 	printf("\n");
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 18/24] submodule--helper: free some "displaypath" in "struct update_data"
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (16 preceding siblings ...)
  2022-07-19 20:47   ` [PATCH v2 17/24] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:47   ` Ævar Arnfjörð Bjarmason
  2022-07-19 20:47   ` [PATCH v2 19/24] submodule--helper: rename "int res" to "int ret" Ævar Arnfjörð Bjarmason
                     ` (6 subsequent siblings)
  24 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Make the update_data_release() function free "displaypath" member when
appropriate. The "displaypath" member is always ours, the "const" on
the "char *" was wrong to begin with.

This leaves a leak of "displaypath" in update_submodule(), which as
we'll see in subsequent commits is harder to deal with than this
trivial fix.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c        | 3 ++-
 t/t2403-worktree-move.sh           | 1 +
 t/t7412-submodule-absorbgitdirs.sh | 1 +
 3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 75e842af5ca..dc27e28e98c 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1953,7 +1953,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)
 
 struct update_data {
 	const char *prefix;
-	const char *displaypath;
+	char *displaypath;
 	enum submodule_update_type update_default;
 	struct object_id suboid;
 	struct string_list references;
@@ -1991,6 +1991,7 @@ struct update_data {
 
 static void update_data_release(struct update_data *ud)
 {
+	free(ud->displaypath);
 	module_list_release(&ud->list);
 }
 
diff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh
index a4e1a178e0a..1168e9f9982 100755
--- a/t/t2403-worktree-move.sh
+++ b/t/t2403-worktree-move.sh
@@ -2,6 +2,7 @@
 
 test_description='test git worktree move, remove, lock and unlock'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh
index 1cfa150768d..2859695c6d2 100755
--- a/t/t7412-submodule-absorbgitdirs.sh
+++ b/t/t7412-submodule-absorbgitdirs.sh
@@ -6,6 +6,7 @@ This test verifies that `git submodue absorbgitdirs` moves a submodules git
 directory into the superproject.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a real submodule' '
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 19/24] submodule--helper: rename "int res" to "int ret"
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (17 preceding siblings ...)
  2022-07-19 20:47   ` [PATCH v2 18/24] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:47   ` Ævar Arnfjörð Bjarmason
  2022-07-19 20:47   ` [PATCH v2 20/24] submodule--helper: add skeleton "goto cleanup" to update_submodule() Ævar Arnfjörð Bjarmason
                     ` (5 subsequent siblings)
  24 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Rename the "res" variable added in b3c5f5cb048 (submodule: move core
cmd_update() logic to C, 2022-03-15) to "ret", which is the convention
in the rest of this file. Subsequent commits will change this code to
a "goto cleanup" pattern, let's have the post image look consistent
with the rest.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index dc27e28e98c..900f89f549d 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2587,7 +2587,7 @@ static int update_submodule(struct update_data *update_data)
 	if (update_data->recursive) {
 		struct child_process cp = CHILD_PROCESS_INIT;
 		struct update_data next = *update_data;
-		int res;
+		int ret;
 
 		next.prefix = NULL;
 		oidcpy(&next.oid, null_oid());
@@ -2599,14 +2599,14 @@ static int update_submodule(struct update_data *update_data)
 		update_data_to_args(&next, &cp.args);
 
 		/* die() if child process die()'d */
-		res = run_command(&cp);
-		if (!res)
+		ret = run_command(&cp);
+		if (!ret)
 			return 0;
 		die_message(_("Failed to recurse into submodule path '%s'"),
 			    update_data->displaypath);
-		if (res == 128)
-			exit(res);
-		else if (res)
+		if (ret == 128)
+			exit(ret);
+		else if (ret)
 			return 1;
 	}
 
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 20/24] submodule--helper: add skeleton "goto cleanup" to update_submodule()
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (18 preceding siblings ...)
  2022-07-19 20:47   ` [PATCH v2 19/24] submodule--helper: rename "int res" to "int ret" Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:47   ` Ævar Arnfjörð Bjarmason
  2022-07-19 20:47   ` [PATCH v2 21/24] submodule--helper: don't exit() on failure, return Ævar Arnfjörð Bjarmason
                     ` (4 subsequent siblings)
  24 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Add a skeleton "goto cleanup" pattern to update_submodule(), rather
than having branches in it "return". This is in preparation for doing
something useful with the "cleanup" label, but for now we're using it
as the equivalent of a "done" label.

The "exit()" branch is not handled yet, and neither is the exit() that
run_update_procedure() might invoke. That'll be handled in a
subsequent commit.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 27 ++++++++++++++++++---------
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 900f89f549d..3b0f46ad3f6 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2544,6 +2544,8 @@ static void update_data_to_args(struct update_data *update_data, struct strvec *
 
 static int update_submodule(struct update_data *update_data)
 {
+	int ret;
+
 	ensure_core_worktree(update_data->sm_path);
 
 	update_data->displaypath = get_submodule_displaypath(
@@ -2580,14 +2582,17 @@ static int update_submodule(struct update_data *update_data)
 		free(remote_ref);
 	}
 
-	if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force)
-		if (run_update_procedure(update_data))
-			return 1;
+	if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) {
+		ret = run_update_procedure(update_data);
+		if (ret) {
+			ret = 1;
+			goto cleanup;
+		}
+	}
 
 	if (update_data->recursive) {
 		struct child_process cp = CHILD_PROCESS_INIT;
 		struct update_data next = *update_data;
-		int ret;
 
 		next.prefix = NULL;
 		oidcpy(&next.oid, null_oid());
@@ -2601,16 +2606,20 @@ static int update_submodule(struct update_data *update_data)
 		/* die() if child process die()'d */
 		ret = run_command(&cp);
 		if (!ret)
-			return 0;
+			goto cleanup;
 		die_message(_("Failed to recurse into submodule path '%s'"),
 			    update_data->displaypath);
-		if (ret == 128)
+		if (ret == 128) {
 			exit(ret);
-		else if (ret)
-			return 1;
+		} else if (ret) {
+			ret = 1;
+			goto cleanup;
+		}
 	}
 
-	return 0;
+	ret = 0;
+cleanup:
+	return ret;
 }
 
 static int update_submodules(struct update_data *update_data)
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 21/24] submodule--helper: don't exit() on failure, return
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (19 preceding siblings ...)
  2022-07-19 20:47   ` [PATCH v2 20/24] submodule--helper: add skeleton "goto cleanup" to update_submodule() Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:47   ` Ævar Arnfjörð Bjarmason
  2022-07-19 20:47   ` [PATCH v2 22/24] submodule--helper: free rest of "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
                     ` (3 subsequent siblings)
  24 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Change code downstream of module_update() to short-circuit and return
to the top-level on failure, rather than calling exit().

To do so we need to diligently check whether we "must_die_on_failure",
which is a pattern started in c51f8f94e5b (submodule--helper: run
update procedures from C, 2021-08-24), but which hadn't been completed
to the point where we could avoid calling exit() here.

This introduces no functional changes, but makes it easier to both
call these routines as a library in the future, and to avoid leaking
memory.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 35 ++++++++++++++++++++++++-----------
 1 file changed, 24 insertions(+), 11 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 3b0f46ad3f6..707c5027961 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2282,7 +2282,8 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str
 	return run_command(&cp);
 }
 
-static int run_update_command(struct update_data *ud, int subforce)
+static int run_update_command(struct update_data *ud, int subforce,
+			      int *must_die_on_failurep)
 {
 	struct child_process cp = CHILD_PROCESS_INIT;
 	char *oid = oid_to_hex(&ud->oid);
@@ -2344,8 +2345,10 @@ static int run_update_command(struct update_data *ud, int subforce)
 			BUG("unexpected update strategy type: %s",
 			    submodule_strategy_to_string(&ud->update_strategy));
 		}
-		if (must_die_on_failure)
-			exit(128);
+		if (must_die_on_failure) {
+			*must_die_on_failurep = 1;
+			return 128;
+		}
 
 		/* the command failed, but update must continue */
 		return 1;
@@ -2379,7 +2382,8 @@ static int run_update_command(struct update_data *ud, int subforce)
 	return 0;
 }
 
-static int run_update_procedure(struct update_data *ud)
+static int run_update_procedure(struct update_data *ud,
+				int *must_die_on_failure)
 {
 	int subforce = is_null_oid(&ud->suboid) || ud->force;
 
@@ -2406,7 +2410,7 @@ static int run_update_procedure(struct update_data *ud)
 			    ud->displaypath, oid_to_hex(&ud->oid));
 	}
 
-	return run_update_command(ud, subforce);
+	return run_update_command(ud, subforce, must_die_on_failure);
 }
 
 static const char *remote_submodule_branch(const char *path)
@@ -2542,7 +2546,8 @@ static void update_data_to_args(struct update_data *update_data, struct strvec *
 				    "--no-single-branch");
 }
 
-static int update_submodule(struct update_data *update_data)
+static int update_submodule(struct update_data *update_data,
+			    int *must_die_on_failure)
 {
 	int ret;
 
@@ -2583,8 +2588,10 @@ static int update_submodule(struct update_data *update_data)
 	}
 
 	if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) {
-		ret = run_update_procedure(update_data);
-		if (ret) {
+		ret = run_update_procedure(update_data, must_die_on_failure);
+		if (ret && *must_die_on_failure) {
+			goto cleanup;
+		} else if (ret) {
 			ret = 1;
 			goto cleanup;
 		}
@@ -2610,7 +2617,8 @@ static int update_submodule(struct update_data *update_data)
 		die_message(_("Failed to recurse into submodule path '%s'"),
 			    update_data->displaypath);
 		if (ret == 128) {
-			exit(ret);
+			*must_die_on_failure = 1;
+			goto cleanup;
 		} else if (ret) {
 			ret = 1;
 			goto cleanup;
@@ -2648,13 +2656,18 @@ static int update_submodules(struct update_data *update_data)
 
 	for (i = 0; i < suc.update_clone_nr; i++) {
 		struct update_clone_data ucd = suc.update_clone[i];
+		int code;
+		int must_die_on_failure = 0;
 
 		oidcpy(&update_data->oid, &ucd.oid);
 		update_data->just_cloned = ucd.just_cloned;
 		update_data->sm_path = ucd.sub->path;
 
-		if (update_submodule(update_data))
-			res = 1;
+		code = update_submodule(update_data, &must_die_on_failure);
+		if (code)
+			res = code;
+		if (must_die_on_failure)
+			goto cleanup;
 	}
 
 cleanup:
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 22/24] submodule--helper: free rest of "displaypath" in "struct update_data"
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (20 preceding siblings ...)
  2022-07-19 20:47   ` [PATCH v2 21/24] submodule--helper: don't exit() on failure, return Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:47   ` Ævar Arnfjörð Bjarmason
  2022-07-19 20:47   ` [PATCH v2 23/24] submodule--helper: fix bad config API usage Ævar Arnfjörð Bjarmason
                     ` (2 subsequent siblings)
  24 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in code added in c51f8f94e5b (submodule--helper: run update
procedures from C, 2021-08-24), we clobber the "displaypath" member of
the passed-in "struct update_data" both so that die() messages in this
update_submodule() function itself can use it, and for the
run_update_procedure() called within this function.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 707c5027961..37d414258cc 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2550,10 +2550,11 @@ static int update_submodule(struct update_data *update_data,
 			    int *must_die_on_failure)
 {
 	int ret;
+	char *to_free, *restore = update_data->displaypath;
 
 	ensure_core_worktree(update_data->sm_path);
 
-	update_data->displaypath = get_submodule_displaypath(
+	update_data->displaypath = to_free = get_submodule_displaypath(
 		update_data->sm_path, update_data->prefix);
 
 	determine_submodule_update_strategy(the_repository, update_data->just_cloned,
@@ -2627,6 +2628,9 @@ static int update_submodule(struct update_data *update_data,
 
 	ret = 0;
 cleanup:
+	free(to_free);
+	update_data->displaypath = restore;
+
 	return ret;
 }
 
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 23/24] submodule--helper: fix bad config API usage
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (21 preceding siblings ...)
  2022-07-19 20:47   ` [PATCH v2 22/24] submodule--helper: free rest of "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:47   ` Ævar Arnfjörð Bjarmason
  2022-07-19 20:47   ` [PATCH v2 24/24] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  24 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix bad config API usage added in a452128a36c (submodule--helper:
introduce add-config subcommand, 2021-08-06). After
git_config_get_string() returns successfully we know the "char **dest"
will be non-NULL.

A coccinelle patch that transforms this turns up a couple of other
such issues, one in fetch-pack.c, and another in upload-pack.c:

	@@
	identifier F =~ "^(repo|git)_config_get_string(_tmp)?$";
	identifier V;
	@@
	  !F(..., &V)
	- && (V)

But let's focus narrowly on submodule--helper for now, we can fix
those some other time.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 37d414258cc..a944f6fa442 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3309,7 +3309,7 @@ static void configure_added_submodule(struct add_data *add_data)
 	 * is_submodule_active(), since that function needs to find
 	 * out the value of "submodule.active" again anyway.
 	 */
-	if (!git_config_get_string("submodule.active", &val) && val) {
+	if (!git_config_get_string("submodule.active", &val)) {
 		/*
 		 * If the submodule being added isn't already covered by the
 		 * current configured pathspec, set the submodule's active flag
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v2 24/24] submodule--helper: fix a configure_added_submodule() leak
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (22 preceding siblings ...)
  2022-07-19 20:47   ` [PATCH v2 23/24] submodule--helper: fix bad config API usage Ævar Arnfjörð Bjarmason
@ 2022-07-19 20:47   ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  24 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-19 20:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix config API a memory leak added in a452128a36c (submodule--helper:
introduce add-config subcommand, 2021-08-06) by using the *_tmp()
variant of git_config_get_string().

In this case we're only checking whether
the (repo|git)_config_get_string() call is telling us that the
"submodule.active" key exists.

As with the preceding commit we'll find many other such patterns in
the codebase if we go fishing. E.g. "git gc" leaks in the code added
in 61f7a383d3b (maintenance: use 'incremental' strategy by default,
2020-10-15). Similar code in "git gc" added in
b08ff1fee00 (maintenance: add --schedule option and config,
2020-09-11) doesn't leak, but we could avoid the malloc() & free() in
that case.

A coccinelle rule to find those would find and fix some leaks, and
cases where we're doing needless malloc() + free()'s but only care
about the key existence, or are copying
the (repo|git)_config_get_string() return value right away.

But as with the preceding commit let's punt on all of that for now,
and just narrowly fix this specific case in submodule--helper.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c    | 4 ++--
 t/t7413-submodule-is-active.sh | 1 +
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index a944f6fa442..09b8a1c2947 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3264,7 +3264,7 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con
 static void configure_added_submodule(struct add_data *add_data)
 {
 	char *key;
-	char *val = NULL;
+	const char *val;
 	struct child_process add_submod = CHILD_PROCESS_INIT;
 	struct child_process add_gitmodules = CHILD_PROCESS_INIT;
 
@@ -3309,7 +3309,7 @@ static void configure_added_submodule(struct add_data *add_data)
 	 * is_submodule_active(), since that function needs to find
 	 * out the value of "submodule.active" again anyway.
 	 */
-	if (!git_config_get_string("submodule.active", &val)) {
+	if (!git_config_get_string_tmp("submodule.active", &val)) {
 		/*
 		 * If the submodule being added isn't already covered by the
 		 * current configured pathspec, set the submodule's active flag
diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh
index c8e7e983317..c4c1f86f3d2 100755
--- a/t/t7413-submodule-is-active.sh
+++ b/t/t7413-submodule-is-active.sh
@@ -6,6 +6,7 @@ This test verifies that `git submodue--helper is-active` correctly identifies
 submodules which are "active" and interesting to the user.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-- 
2.37.1.1062.g385eac7fccf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* Re: [PATCH v2 01/24] submodule--helper: replace memset() with { 0 }-initialization
  2022-07-19 20:46   ` [PATCH v2 01/24] submodule--helper: replace memset() with { 0 }-initialization Ævar Arnfjörð Bjarmason
@ 2022-07-19 21:21     ` Junio C Hamano
  0 siblings, 0 replies; 186+ messages in thread
From: Junio C Hamano @ 2022-07-19 21:21 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> Use the less verbose { 0 }-initialization syntax rather than memset()
> in builtin/submodule--helper.c, this doesn't make a difference in
> terms of behavior, but as we're about to modify adjacent code makes
> this more consistent, and lets us avoid worrying about when the
> memset() happens v.s. a "goto cleanup".
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 6 ++----
>  1 file changed, 2 insertions(+), 4 deletions(-)

These four hunks form two pairs of two hunks, one declaring (and in
the new code, initializing) a variable, the other clearing (and in
the new code, there is no need to) the variable.

> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index fac52ade5e1..73717be957c 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -1744,7 +1744,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
>  {
>  	int dissociate = 0, quiet = 0, progress = 0, require_init = 0;
>  	struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
> -	struct list_objects_filter_options filter_options;
> +	struct list_objects_filter_options filter_options = { 0 };
>  
>  	struct option module_clone_options[] = {
>  		OPT_STRING(0, "prefix", &clone_data.prefix,
> @@ -1786,7 +1786,6 @@ static int module_clone(int argc, const char **argv, const char *prefix)
>  		NULL
>  	};
>  
> -	memset(&filter_options, 0, sizeof(filter_options));
>  	argc = parse_options(argc, argv, prefix, module_clone_options,
>  			     git_submodule_helper_usage, 0);
> @@ -2563,7 +2562,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
>  {
>  	struct pathspec pathspec;
>  	struct update_data opt = UPDATE_DATA_INIT;
> -	struct list_objects_filter_options filter_options;
> +	struct list_objects_filter_options filter_options = { 0 };
>  	int ret;
>  
>  	struct option module_update_options[] = {
> @@ -2623,7 +2622,6 @@ static int module_update(int argc, const char **argv, const char *prefix)
>  	update_clone_config_from_gitmodules(&opt.max_jobs);
>  	git_config(git_update_clone_config, &opt.max_jobs);
>  
> -	memset(&filter_options, 0, sizeof(filter_options));
>  	argc = parse_options(argc, argv, prefix, module_update_options,
>  			     git_submodule_helper_usage, 0);

The fact that this patch is harder to read without wider context
tells us that these two changes are good idea.  Absolutely nothing
happens in the first function between the declaration of the
variable and memset() but it is hard to see in a patch because they
are so far apart.  In the second function, some things happen but
they are not using the variable before they get initialized, but
again, it is hard to see in a patch without wider context.

The post-image of this patch allows readers easily to see that
nobody uses these variables without initialization, which is a great
improvement.

Thanks, will queue.

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v2 02/24] submodule--helper: fix a leak in "clone_submodule"
  2022-07-19 20:46   ` [PATCH v2 02/24] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
@ 2022-07-19 21:31     ` Junio C Hamano
  0 siblings, 0 replies; 186+ messages in thread
From: Junio C Hamano @ 2022-07-19 21:31 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 73717be957c..4155d2450e0 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -1644,6 +1644,7 @@ static int clone_submodule(struct module_clone_data *clone_data)
>  	char *sm_alternate = NULL, *error_strategy = NULL;
>  	struct strbuf sb = STRBUF_INIT;
>  	struct child_process cp = CHILD_PROCESS_INIT;
> +	char *to_free = NULL;
>  
>  	submodule_name_to_gitdir(&sb, the_repository, clone_data->name);
>  	sm_gitdir = absolute_pathdup(sb.buf);
> @@ -1651,9 +1652,9 @@ static int clone_submodule(struct module_clone_data *clone_data)
>  
>  	if (!is_absolute_path(clone_data->path)) {
>  		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
> -		clone_data->path = strbuf_detach(&sb, NULL);
> +		clone_data->path = to_free = strbuf_detach(&sb, NULL);
>  	} else {
> -		clone_data->path = xstrdup(clone_data->path);
> +		clone_data->path = clone_data->path;
>  	}

WTH?  Shouldn't the entire else-clause just go?

>  
>  	if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)
> @@ -1737,6 +1738,7 @@ static int clone_submodule(struct module_clone_data *clone_data)
>  	strbuf_release(&sb);
>  	free(sm_gitdir);
>  	free(p);
> +	free(to_free);
>  	return 0;
>  }

The caller passes clone_data to us, we may have stuffed an allocated
piece of memory in there when we had to make it absolute, and we
free it but let the clone_data structure smuggle the now-stale
pointer out of the function, so that the caller may be able to abuse
it?

That leaves a bad taste in my mouth.  Doesn't it in yours?

If the caller is *NOT* allowed to rely on the value in
clone_data->path after we return, perhaps

+	free(to_free);
+	clone_data->path = NULL;

But stepping back a bit, would it make more sense to introduce a
local variable "path" and leave clone_data->path alone, after we
decide to either compute an absolute path out of it, or we decide
to use the path as is, i.e.

	if (!is_absolute_path(...)) {
		...
		to_free = path = strbuf_detach(&sb, NULL);
	} ... {
		path = clone_data->path;
		to_free = NULL;
	}

and after that, never use clone_data->path but work solely on the
local "path"?  A quick scan tells me that no line in the rest of the
function passes the whole clone_data to other helpers, so it may just
be a matter of s/clone_data->path/path/g perhaps?

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v2 04/24] submodule--helper: fix most "struct pathspec" memory leaks
  2022-07-19 20:46   ` [PATCH v2 04/24] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
@ 2022-07-20 16:33     ` Junio C Hamano
  2022-07-21 17:49     ` Glen Choo
  1 sibling, 0 replies; 186+ messages in thread
From: Junio C Hamano @ 2022-07-20 16:33 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> @@ -269,7 +269,7 @@ static char *get_up_path(const char *path)
>  static int module_list(int argc, const char **argv, const char *prefix)
>  {
>  	int i;
> -	struct pathspec pathspec;
> +	struct pathspec pathspec = { 0 };
>  	struct module_list list = MODULE_LIST_INIT;
>  
>  	struct option module_list_options[] = {
> @@ -278,6 +278,7 @@ static int module_list(int argc, const char **argv, const char *prefix)
>  			   N_("alternative anchor for relative paths")),
>  		OPT_END()
>  	};
> +	int ret;

Adding such a simple variable near the top, perhaps next to "int i",
would probably make it easier to read, especially because you employ
a good pattern below to let the compiler detect unintended use of
the variable uninitialized, i.e. "ret = 1; goto cleanup;" is placed
at each error-exit codepath, and "ret = 0;" is added immediately
before the "cleanup" label.  If somebody jumps to the cleanup label
without setting "ret", the compiler will catch it.

But you can do better by eliminating the need to check.  Initialize
"ret" to non-zero (i.e. "assume failure"), and have each error-exit
codepath just "goto cleanup".  Keep the "ret = 0;" this patch uses
immediately before the "cleanup" label.

> @@ -287,8 +288,10 @@ static int module_list(int argc, const char **argv, const char *prefix)
>  	argc = parse_options(argc, argv, prefix, module_list_options,
>  			     git_submodule_helper_usage, 0);
>  
> -	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
> -		return 1;
> +	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
> +		ret = 1;
> +		goto cleanup;
> +	}

Which will simplify this hunk.

The same exact pattern repeats throughout this patch, and the above
commits apply to all of the hunks, I think.

Thanks.

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v2 05/24] submodule--helper: "struct pathspec" memory leak in module_update()
  2022-07-19 20:46   ` [PATCH v2 05/24] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
@ 2022-07-20 16:43     ` Junio C Hamano
  2022-07-21 19:54     ` Glen Choo
  1 sibling, 0 replies; 186+ messages in thread
From: Junio C Hamano @ 2022-07-20 16:43 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> The module_update() function calls module_list_compute() twice, which
> in turn will reset the "struct pathspec" passed to it. Let's instead
> track two of them, and clear them both.

Looks correct.  I wish there were an easy way to limit the scope of
this new pathspec2 to the block guarded by "if (opt.init)", but with
the "jump to error-exit" idiom, there unfortunately isn't.

Thanks.


>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 28c5fdb8954..7466e781e97 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -2602,6 +2602,7 @@ static int update_submodules(struct update_data *update_data)
>  static int module_update(int argc, const char **argv, const char *prefix)
>  {
>  	struct pathspec pathspec = { 0 };
> +	struct pathspec pathspec2 = { 0 };
>  	struct update_data opt = UPDATE_DATA_INIT;
>  	struct list_objects_filter_options filter_options = { 0 };
>  	int ret;
> @@ -2692,7 +2693,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
>  		struct init_cb info = INIT_CB_INIT;
>  
>  		if (module_list_compute(argc, argv, opt.prefix,
> -					&pathspec, &list) < 0) {
> +					&pathspec2, &list) < 0) {
>  			ret = 1;
>  			goto cleanup;
>  		}
> @@ -2715,6 +2716,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
>  cleanup:
>  	list_objects_filter_release(&filter_options);
>  	clear_pathspec(&pathspec);
> +	clear_pathspec(&pathspec2);
>  	return ret;
>  }

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v2 08/24] submodule--helper: add and use *_release() functions
  2022-07-19 20:46   ` [PATCH v2 08/24] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
@ 2022-07-20 17:06     ` Junio C Hamano
  2022-07-21 17:44       ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 186+ messages in thread
From: Junio C Hamano @ 2022-07-20 17:06 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> Add release functions for "struct module_list", "struct
> submodule_update_clone" and "struct update_data".

OK, this does a lot of things, but in short, nobody bothered to free
module_list (i.e. list of cache entries borrowed from the index to
represent the paths we are interested in that are gitlinks),
update_data (which has module_list among other things that do not
need to be freed), and submodule_update_clone (which has
update_data, update_clone_data and failed_clones list) in the
original (in other words, these release helpers had to be invented
by this patch, not factored out from some codepaths that free them).

I think the earlier part of the patch that deals with module_list is
correct.  I also think the last one that clears update_data in
module_update() is correct.

I am not sure about the helper that releases suc, especially how
suc->update_data is left alone by update_submodules().  Presumably
the caller is responsible for releasing the resources held by
update_data member alone, but the interaction makes me feel dirty.
Luckily there is only one caller of suc_release(), so we can design
however dirty interface for convenience, but still it feels wrong to
design a release() helper that pretends to be usable by general
public, yet they have to be aware of the fact that some members in
the struct are still their responsibility to release.  I dunno.


^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v2 11/24] submodule--helper: fix "sm_path" and other "module_cb_list" leaks
  2022-07-19 20:47   ` [PATCH v2 11/24] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
@ 2022-07-20 22:35     ` Junio C Hamano
  0 siblings, 0 replies; 186+ messages in thread
From: Junio C Hamano @ 2022-07-20 22:35 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> -	const char *sm_path;
> +	char *sm_path;
>  };
>  #define MODULE_CB_INIT { 0 }
> +static void module_cb_release(struct module_cb *mcb)
> +{
> +	free(mcb->sm_path);
> +}

It would be nice to have a blank line before the new helper
function, just like you did for the next hunk.

>  struct module_cb_list {
>  	struct module_cb **entries;
> @@ -838,6 +842,19 @@ struct module_cb_list {
>  };
>  #define MODULE_CB_LIST_INIT { 0 }
>  
> +static void module_cb_list_release(struct module_cb_list *mcbl)
> +{
> +	int i;
> +
> +	for (i = 0; i < mcbl->nr; i++) {
> +		struct module_cb *mcb = mcbl->entries[i];
> +
> +		module_cb_release(mcb);
> +		free(mcb);
> +	}
> +	free(mcbl->entries);
> +}

OK, this was populated in the diff callback function and each
element of the list->entries[] array is a module_cb data, which has
its own release() helper above.

Looking good.


>  struct summary_cb {
>  	int argc;
>  	const char **argv;
> @@ -1181,6 +1198,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
>  cleanup:
>  	strvec_clear(&diff_args);
>  	release_revisions(&rev);
> +	module_cb_list_release(&list);
>  	return ret;
>  }

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v2 14/24] submodule--helper: fix "reference" leak is "module_clone_data"
  2022-07-19 20:47   ` [PATCH v2 14/24] submodule--helper: fix "reference" leak is "module_clone_data" Ævar Arnfjörð Bjarmason
@ 2022-07-21  0:50     ` Junio C Hamano
  0 siblings, 0 replies; 186+ messages in thread
From: Junio C Hamano @ 2022-07-21  0:50 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> Those commits added an xstrdup()'d member of the
> STRING_LIST_INIT_NODUP'd "struct string_list". We need to free()
> those, but not the ones we get from argv, let's make use of the "util"
> member, if it has a pointer it's the pointer we'll need to free.

I find this description misleading; do we sometimes put the same 'p'
as the key to the util member, and sometimes leave the util member
NULL?

I have seen some hacky codepaths that add keys with strdup_strings
set to false, and then call string_list_clear after setting it set
to true, to force the keys to be released (cf. bisect_clean_state()
uses the hack to clear refs_fo_removal).  Compared to that, using
the util member is probably less hacky, but it would not extend to
cases where the string_list needs to associate real payload to the
key strings.

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v2 08/24] submodule--helper: add and use *_release() functions
  2022-07-20 17:06     ` Junio C Hamano
@ 2022-07-21 17:44       ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 17:44 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan


On Wed, Jul 20 2022, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>
>> Add release functions for "struct module_list", "struct
>> submodule_update_clone" and "struct update_data".
>
> OK, this does a lot of things, but in short, nobody bothered to free
> module_list (i.e. list of cache entries borrowed from the index to
> represent the paths we are interested in that are gitlinks),
> update_data (which has module_list among other things that do not
> need to be freed), and submodule_update_clone (which has
> update_data, update_clone_data and failed_clones list) in the
> original (in other words, these release helpers had to be invented
> by this patch, not factored out from some codepaths that free them).
>
> I think the earlier part of the patch that deals with module_list is
> correct.  I also think the last one that clears update_data in
> module_update() is correct.

...

> I am not sure about the helper that releases suc, especially how
> suc->update_data is left alone by update_submodules().  Presumably
> the caller is responsible for releasing the resources held by
> update_data member alone, but the interaction makes me feel dirty.
> Luckily there is only one caller of suc_release(), so we can design
> however dirty interface for convenience, but still it feels wrong to
> design a release() helper that pretends to be usable by general
> public, yet they have to be aware of the fact that some members in
> the struct are still their responsibility to release.  I dunno.

But isn't this fine because that "update_data" member has a "const" on
it:

	/* configuration parameters which are passed on to the children */
	const struct update_data *update_data;

I fix other similar ownership issues you pointed out for a re-roll I'm
preparing, but I don't see why this one is a problem as long as that
"const" is there, it's a slot we use for passing things in externally &
to ferry it along.

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v2 04/24] submodule--helper: fix most "struct pathspec" memory leaks
  2022-07-19 20:46   ` [PATCH v2 04/24] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
  2022-07-20 16:33     ` Junio C Hamano
@ 2022-07-21 17:49     ` Glen Choo
  1 sibling, 0 replies; 186+ messages in thread
From: Glen Choo @ 2022-07-21 17:49 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> Call clear_pathspec() at the end of various functions that work with
> and allocate a "struct pathspec".
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 115 +++++++++++++++++++++++++-----------
>  1 file changed, 81 insertions(+), 34 deletions(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index b36919b66c5..28c5fdb8954 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -1105,7 +1129,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
>  	struct strvec diff_args = STRVEC_INIT;
>  	struct rev_info rev;
>  	struct module_cb_list list = MODULE_CB_LIST_INIT;
> -	int ret = 0;
> +	int ret;
>  
>  	strvec_push(&diff_args, get_diff_cmd(diff_cmd));
>  	if (info->cached)
> @@ -1145,6 +1169,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
>  	else
>  		run_diff_files(&rev, 0);
>  	prepare_submodule_summary(info, &list);
> +	ret = 0;
>  cleanup:
>  	strvec_clear(&diff_args);
>  	release_revisions(&rev);

This function doesn't use a pathspec at all. Was it changed for
consistency reasons or perhaps an accidental inclusion?

All the other hunks look correct.

^ permalink raw reply	[flat|nested] 186+ messages in thread

* [PATCH v3 00/26] submodule--helper: fix memory leaks
  2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                     ` (23 preceding siblings ...)
  2022-07-19 20:47   ` [PATCH v2 24/24] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:12   ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:12     ` [PATCH v3 01/26] submodule--helper: replace memset() with { 0 }-initialization Ævar Arnfjörð Bjarmason
                       ` (26 more replies)
  24 siblings, 27 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:12 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

This series fixes all of the memory leaks in submodule--helper.c that
the test suite spots (which aren't really in submodule.c, branch.c,
object code etc.).

For the v2, see:
https://lore.kernel.org/git/cover-v2-00.24-00000000000-20220719T204458Z-avarab@gmail.com/

Junio: I only replied to one feedback E-Mail of yours, but I think
this re-roll addresses all the rest, as well as the "pathspec" comment
Glen sent in earlier today.

Glen Choo (2):
  submodule--helper: add "const" to copy of "update_data"
  submodule--helper: refactor "errmsg_str" to be a "struct strbuf"

Ævar Arnfjörð Bjarmason (24):
  submodule--helper: replace memset() with { 0 }-initialization
  submodule--helper: stop conflating "sb" in clone_submodule()
  submodule--helper: pass a "const struct module_clone_data" to
    clone_submodule()
  submodule--helper: fix a leak in "clone_submodule"
  submodule--helper: fix trivial get_default_remote_submodule() leak
  submodule--helper: fix most "struct pathspec" memory leaks
  submodule--helper: "struct pathspec" memory leak in module_update()
  submodule--helper: don't leak {run,capture}_command() cp.dir argument
  submodule--helper: add and use *_release() functions
  submodule--helper: fix "errmsg_str" memory leak
  submodule--helper: fix "sm_path" and other "module_cb_list" leaks
  submodule--helper: fix a leak with repo_clear()
  submodule--helper: fix a memory leak in get_default_remote_submodule()
  submodule--helper: fix "reference" leak is "module_clone_data"
  submodule--helper: fix obscure leak in module_add()
  submodule--helper: fix a leak in module_add()
  submodule--helper: fix a memory leak in print_status()
  submodule--helper: free some "displaypath" in "struct update_data"
  submodule--helper: rename "int res" to "int ret"
  submodule--helper: add skeleton "goto cleanup" to update_submodule()
  submodule--helper: don't exit() on failure, return
  submodule--helper: free rest of "displaypath" in "struct update_data"
  submodule--helper: fix bad config API usage
  submodule--helper: fix a configure_added_submodule() leak

 builtin/submodule--helper.c        | 374 ++++++++++++++++++++---------
 t/t1500-rev-parse.sh               |   1 +
 t/t2403-worktree-move.sh           |   1 +
 t/t6008-rev-list-submodule.sh      |   1 +
 t/t6134-pathspec-in-submodule.sh   |   1 +
 t/t7412-submodule-absorbgitdirs.sh |   1 +
 t/t7413-submodule-is-active.sh     |   1 +
 t/t7414-submodule-mistakes.sh      |   2 +
 t/t7506-status-submodule.sh        |   1 +
 t/t7507-commit-verbose.sh          |   2 +
 10 files changed, 270 insertions(+), 115 deletions(-)

Range-diff against v2:
 1:  fcdf4a2e2d9 =  1:  3c7ae3e0222 submodule--helper: replace memset() with { 0 }-initialization
 -:  ----------- >  2:  32e4ae7ead5 submodule--helper: stop conflating "sb" in clone_submodule()
 -:  ----------- >  3:  eee6ca28c6f submodule--helper: pass a "const struct module_clone_data" to clone_submodule()
 2:  130a396b837 !  4:  26f0caf0386 submodule--helper: fix a leak in "clone_submodule"
    @@ Metadata
      ## Commit message ##
         submodule--helper: fix a leak in "clone_submodule"
     
    -    Fix a memory leak of the "path" member of the "struct
    -    module_clone_data" in clone_submodule(). This fixes leaks in code
    -    added in f8eaa0ba98b (submodule--helper, module_clone: always operate
    +    Fix a memory leak of the "clone_data_path" variable that we copy or
    +    derive from the "struct module_clone_data" in clone_submodule(). This
    +    code was refactored in preceding commits, but the leak has been with
    +    us since f8eaa0ba98b (submodule--helper, module_clone: always operate
         on absolute paths, 2016-03-31).
     
         For the "else" case we don't need to xstrdup() the "clone_data->path",
    -    if we're not creating a new one we'll leave it to our caller to keep
    -    track of it.
    +    and we don't need to free our own "clone_data_path".
     
         In the case of the module_clone() caller it's from "argv", and doesn't
         need to be free'd, and in the case of the add_submodule() caller we
    @@ Commit message
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/submodule--helper.c ##
    -@@ builtin/submodule--helper.c: static int clone_submodule(struct module_clone_data *clone_data)
    +@@ builtin/submodule--helper.c: static int clone_submodule(const struct module_clone_data *clone_data,
      	char *sm_alternate = NULL, *error_strategy = NULL;
    - 	struct strbuf sb = STRBUF_INIT;
      	struct child_process cp = CHILD_PROCESS_INIT;
    + 	const char *clone_data_path;
     +	char *to_free = NULL;
      
    - 	submodule_name_to_gitdir(&sb, the_repository, clone_data->name);
    - 	sm_gitdir = absolute_pathdup(sb.buf);
    -@@ builtin/submodule--helper.c: static int clone_submodule(struct module_clone_data *clone_data)
    - 
      	if (!is_absolute_path(clone_data->path)) {
    + 		struct strbuf sb = STRBUF_INIT;
    + 
      		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
    --		clone_data->path = strbuf_detach(&sb, NULL);
    -+		clone_data->path = to_free = strbuf_detach(&sb, NULL);
    +-		clone_data_path = strbuf_detach(&sb, NULL);
    ++		clone_data_path = to_free = strbuf_detach(&sb, NULL);
      	} else {
    --		clone_data->path = xstrdup(clone_data->path);
    -+		clone_data->path = clone_data->path;
    +-		clone_data_path = xstrdup(clone_data_path);
    ++		clone_data_path = clone_data->path;
      	}
      
      	if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)
    -@@ builtin/submodule--helper.c: static int clone_submodule(struct module_clone_data *clone_data)
    - 	strbuf_release(&sb);
    +@@ builtin/submodule--helper.c: static int clone_submodule(const struct module_clone_data *clone_data,
    + 
      	free(sm_gitdir);
      	free(p);
     +	free(to_free);
 3:  25e17bbb058 =  5:  75775bf4f6c submodule--helper: fix trivial get_default_remote_submodule() leak
 4:  9fb60485c3e !  6:  7672ef1305f submodule--helper: fix most "struct pathspec" memory leaks
    @@ builtin/submodule--helper.c: static int module_list(int argc, const char **argv,
      			   N_("alternative anchor for relative paths")),
      		OPT_END()
      	};
    -+	int ret;
    ++	int ret = 1;
      
      	const char *const git_submodule_helper_usage[] = {
      		N_("git submodule--helper list [--prefix=<path>] [<path>...]"),
     @@ builtin/submodule--helper.c: static int module_list(int argc, const char **argv, const char *prefix)
    - 	argc = parse_options(argc, argv, prefix, module_list_options,
      			     git_submodule_helper_usage, 0);
      
    --	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
    + 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
     -		return 1;
    -+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
    -+		ret = 1;
     +		goto cleanup;
    -+	}
      
      	for (i = 0; i < list.nr; i++) {
      		const struct cache_entry *ce = list.entries[i];
    @@ builtin/submodule--helper.c: static int module_foreach(int argc, const char **ar
      		N_("git submodule foreach [--quiet] [--recursive] [--] <command>"),
      		NULL
      	};
    -+	int ret;
    ++	int ret = 1;
      
      	argc = parse_options(argc, argv, prefix, module_foreach_options,
      			     git_submodule_helper_usage, 0);
      
    --	if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
    + 	if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
     -		return 1;
    -+	if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0) {
    -+		ret = 1;
     +		goto cleanup;
    -+	}
      
      	info.argc = argc;
      	info.argv = argv;
    @@ builtin/submodule--helper.c: static int module_init(int argc, const char **argv,
      		N_("git submodule init [<options>] [<path>]"),
      		NULL
      	};
    -+	int ret;
    ++	int ret = 1;
      
      	argc = parse_options(argc, argv, prefix, module_init_options,
      			     git_submodule_helper_usage, 0);
      
    --	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
    + 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
     -		return 1;
    -+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
    -+		ret = 1;
     +		goto cleanup;
    -+	}
      
      	/*
      	 * If there are no path args and submodule.active is set then,
    @@ builtin/submodule--helper.c: static int module_status(int argc, const char **arg
      		N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"),
      		NULL
      	};
    -+	int ret;
    ++	int ret = 1;
      
      	argc = parse_options(argc, argv, prefix, module_status_options,
      			     git_submodule_helper_usage, 0);
      
    --	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
    + 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
     -		return 1;
    -+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
    -+		ret = 1;
     +		goto cleanup;
    -+	}
      
      	info.prefix = prefix;
      	if (quiet)
    @@ builtin/submodule--helper.c: static int module_status(int argc, const char **arg
      }
      
      static int module_name(int argc, const char **argv, const char *prefix)
    -@@ builtin/submodule--helper.c: static int compute_summary_module_list(struct object_id *head_oid,
    - 	struct strvec diff_args = STRVEC_INIT;
    - 	struct rev_info rev;
    - 	struct module_cb_list list = MODULE_CB_LIST_INIT;
    --	int ret = 0;
    -+	int ret;
    - 
    - 	strvec_push(&diff_args, get_diff_cmd(diff_cmd));
    - 	if (info->cached)
    -@@ builtin/submodule--helper.c: static int compute_summary_module_list(struct object_id *head_oid,
    - 	else
    - 		run_diff_files(&rev, 0);
    - 	prepare_submodule_summary(info, &list);
    -+	ret = 0;
    - cleanup:
    - 	strvec_clear(&diff_args);
    - 	release_revisions(&rev);
     @@ builtin/submodule--helper.c: static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data
      static int module_sync(int argc, const char **argv, const char *prefix)
      {
    @@ builtin/submodule--helper.c: static void sync_submodule_cb(const struct cache_en
      	struct module_list list = MODULE_LIST_INIT;
      	int quiet = 0;
      	int recursive = 0;
    -+	int ret;
    ++	int ret = 1;
      
      	struct option module_sync_options[] = {
      		OPT__QUIET(&quiet, N_("suppress output of synchronizing submodule url")),
     @@ builtin/submodule--helper.c: static int module_sync(int argc, const char **argv, const char *prefix)
    - 	argc = parse_options(argc, argv, prefix, module_sync_options,
      			     git_submodule_helper_usage, 0);
      
    --	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
    + 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
     -		return 1;
    -+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
    -+		ret = 1;
     +		goto cleanup;
    -+	}
      
      	info.prefix = prefix;
      	if (quiet)
    @@ builtin/submodule--helper.c: static int module_deinit(int argc, const char **arg
      		N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
      		NULL
      	};
    -+	int ret;
    ++	int ret = 1;
      
      	argc = parse_options(argc, argv, prefix, module_deinit_options,
      			     git_submodule_helper_usage, 0);
     @@ builtin/submodule--helper.c: static int module_deinit(int argc, const char **argv, const char *prefix)
    - 	if (!argc && !all)
      		die(_("Use '--all' if you really want to deinitialize all submodules"));
      
    --	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
    + 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
     -		return 1;
    -+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
    -+		ret = 1;
     +		goto cleanup;
    -+	}
      
      	info.prefix = prefix;
      	if (quiet)
    @@ builtin/submodule--helper.c: static int push_check(int argc, const char **argv,
     +	struct pathspec pathspec = { 0 };
      	struct module_list list = MODULE_LIST_INIT;
      	unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;
    -+	int ret;
    ++	int ret = 1;
      
      	struct option embed_gitdir_options[] = {
      		OPT_STRING(0, "prefix", &prefix,
     @@ builtin/submodule--helper.c: static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
    - 	argc = parse_options(argc, argv, prefix, embed_gitdir_options,
      			     git_submodule_helper_usage, 0);
      
    --	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
    + 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
     -		return 1;
    -+	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
    -+		ret = 1;
     +		goto cleanup;
    -+	}
      
      	for (i = 0; i < list.nr; i++)
      		absorb_git_dir_into_superproject(list.entries[i]->name, flags);
 5:  a4672aa9c94 =  7:  325aa1521e2 submodule--helper: "struct pathspec" memory leak in module_update()
 6:  7925db18b33 =  8:  424b24961b5 submodule--helper: don't leak {run,capture}_command() cp.dir argument
 7:  30883f3aa01 =  9:  abf5c4754a4 submodule--helper: add "const" to copy of "update_data"
 8:  b7582391c91 = 10:  6ab5aabae35 submodule--helper: add and use *_release() functions
 9:  92737916083 = 11:  feec1f20bf9 submodule--helper: refactor "errmsg_str" to be a "struct strbuf"
10:  c81a4f69179 = 12:  d368db73de7 submodule--helper: fix "errmsg_str" memory leak
11:  c25b55c9528 ! 13:  5be941b3d1b submodule--helper: fix "sm_path" and other "module_cb_list" leaks
    @@ builtin/submodule--helper.c: struct module_cb {
     +	char *sm_path;
      };
      #define MODULE_CB_INIT { 0 }
    + 
     +static void module_cb_release(struct module_cb *mcb)
     +{
     +	free(mcb->sm_path);
     +}
    - 
    ++
      struct module_cb_list {
      	struct module_cb **entries;
    -@@ builtin/submodule--helper.c: struct module_cb_list {
    + 	int alloc, nr;
      };
      #define MODULE_CB_LIST_INIT { 0 }
      
12:  303447bd4f5 = 14:  b8560e8c111 submodule--helper: fix a leak with repo_clear()
13:  f8ededcdf77 = 15:  abfd61f9f05 submodule--helper: fix a memory leak in get_default_remote_submodule()
14:  c7610088968 ! 16:  b8df96a9cf0 submodule--helper: fix "reference" leak is "module_clone_data"
    @@ Commit message
         Those commits added an xstrdup()'d member of the
         STRING_LIST_INIT_NODUP'd "struct string_list". We need to free()
         those, but not the ones we get from argv, let's make use of the "util"
    -    member, if it has a pointer it's the pointer we'll need to free.
    +    member, if it has a pointer it's the pointer we'll need to free,
    +    otherwise it'll be NULL (i.e. from argv).
     
         To fix this create a a module_clone_data_release() function to go with
         the MODULE_CLONE_DATA_INIT added in a98b02c1128 (submodule--helper:
    @@ builtin/submodule--helper.c: static int add_possible_reference_from_superproject
      			switch (sas->error_mode) {
     @@ builtin/submodule--helper.c: static int module_clone(int argc, const char **argv, const char *prefix)
      
    - 	clone_submodule(&clone_data);
    + 	clone_submodule(&clone_data, &clone_data.reference);
      	list_objects_filter_release(&filter_options);
     +	module_clone_data_release(&clone_data);
      	return 0;
    @@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add
      {
      	char *submod_gitdir_path;
      	struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
    -+	int ret;
    ++	int ret = -1;
      
      	/* perhaps the path already exists and is already a git repo, else clone it */
      	if (is_directory(add_data->sm_path)) {
    @@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add
      		if (add_data->depth >= 0)
      			clone_data.depth = xstrfmt("%d", add_data->depth);
      
    --		if (clone_submodule(&clone_data))
    + 		if (clone_submodule(&clone_data, &clone_data.reference))
     -			return -1;
    -+		if (clone_submodule(&clone_data)) {
    -+			ret = -1;
     +			goto cleanup;
    -+		}
      
      		prepare_submodule_repo_env(&cp.env);
      		cp.git_cmd = 1;
15:  abd8e2eef3a ! 17:  7811bdbf149 submodule--helper: fix obscure leak in module_add()
    @@ builtin/submodule--helper.c: static int module_add(int argc, const char **argv,
      	struct add_data add_data = ADD_DATA_INIT;
      	char *to_free = NULL;
     +	struct strbuf sb = STRBUF_INIT;
    -+	int ret;
    ++	int ret = 1;
      
      	struct option options[] = {
      		OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
    @@ builtin/submodule--helper.c: static int module_add(int argc, const char **argv,
      
      	if(!add_data.sm_name)
     @@ builtin/submodule--helper.c: static int module_add(int argc, const char **argv, const char *prefix)
    + 	add_data.progress = !!progress;
      	add_data.dissociate = !!dissociate;
      
    - 	if (add_submodule(&add_data)) {
    +-	if (add_submodule(&add_data)) {
     -		free(add_data.sm_path);
     -		return 1;
    -+		ret = 1;
    +-	}
    ++	if (add_submodule(&add_data))
     +		goto cleanup;
    - 	}
      	configure_added_submodule(&add_data);
     +
     +	ret = 0;
16:  1f01203d154 = 18:  01566d63926 submodule--helper: fix a leak in module_add()
17:  12b8a945486 = 19:  7ef89abed86 submodule--helper: fix a memory leak in print_status()
18:  fac2c4491f3 = 20:  b8d47fc7d70 submodule--helper: free some "displaypath" in "struct update_data"
19:  cf0c8851954 = 21:  4fb17f0dff1 submodule--helper: rename "int res" to "int ret"
20:  7882e33cdca ! 22:  af83925046b submodule--helper: add skeleton "goto cleanup" to update_submodule()
    @@ builtin/submodule--helper.c: static void update_data_to_args(struct update_data
      
      static int update_submodule(struct update_data *update_data)
      {
    -+	int ret;
    ++	int ret = 1;
     +
      	ensure_core_worktree(update_data->sm_path);
      
    @@ builtin/submodule--helper.c: static int update_submodule(struct update_data *upd
      	}
      
     -	if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force)
    --		if (run_update_procedure(update_data))
    --			return 1;
     +	if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) {
    -+		ret = run_update_procedure(update_data);
    -+		if (ret) {
    -+			ret = 1;
    + 		if (run_update_procedure(update_data))
    +-			return 1;
     +			goto cleanup;
    -+		}
     +	}
      
      	if (update_data->recursive) {
21:  31395a2b4f8 ! 23:  4c60784d281 submodule--helper: don't exit() on failure, return
    @@ builtin/submodule--helper.c: static void update_data_to_args(struct update_data
     +static int update_submodule(struct update_data *update_data,
     +			    int *must_die_on_failure)
      {
    - 	int ret;
    + 	int ret = 1;
      
     @@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)
      	}
      
      	if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) {
    --		ret = run_update_procedure(update_data);
    --		if (ret) {
    +-		if (run_update_procedure(update_data))
     +		ret = run_update_procedure(update_data, must_die_on_failure);
     +		if (ret && *must_die_on_failure) {
     +			goto cleanup;
     +		} else if (ret) {
    - 			ret = 1;
    ++			ret = 1;
      			goto cleanup;
    - 		}
    ++		}
    + 	}
    + 
    + 	if (update_data->recursive) {
     @@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)
      		die_message(_("Failed to recurse into submodule path '%s'"),
      			    update_data->displaypath);
22:  a2168cf1378 ! 24:  7551af195ad submodule--helper: free rest of "displaypath" in "struct update_data"
    @@ builtin/submodule--helper.c
     @@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data,
      			    int *must_die_on_failure)
      {
    - 	int ret;
    + 	int ret = 1;
     +	char *to_free, *restore = update_data->displaypath;
      
      	ensure_core_worktree(update_data->sm_path);
23:  d77c6665ca9 = 25:  f650716cd7e submodule--helper: fix bad config API usage
24:  531db4ddae6 = 26:  581ce0872c0 submodule--helper: fix a configure_added_submodule() leak
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply	[flat|nested] 186+ messages in thread

* [PATCH v3 01/26] submodule--helper: replace memset() with { 0 }-initialization
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:12     ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:12     ` [PATCH v3 02/26] submodule--helper: stop conflating "sb" in clone_submodule() Ævar Arnfjörð Bjarmason
                       ` (25 subsequent siblings)
  26 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:12 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Use the less verbose { 0 }-initialization syntax rather than memset()
in builtin/submodule--helper.c, this doesn't make a difference in
terms of behavior, but as we're about to modify adjacent code makes
this more consistent, and lets us avoid worrying about when the
memset() happens v.s. a "goto cleanup".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index fac52ade5e1..73717be957c 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1744,7 +1744,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 {
 	int dissociate = 0, quiet = 0, progress = 0, require_init = 0;
 	struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
-	struct list_objects_filter_options filter_options;
+	struct list_objects_filter_options filter_options = { 0 };
 
 	struct option module_clone_options[] = {
 		OPT_STRING(0, "prefix", &clone_data.prefix,
@@ -1786,7 +1786,6 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 		NULL
 	};
 
-	memset(&filter_options, 0, sizeof(filter_options));
 	argc = parse_options(argc, argv, prefix, module_clone_options,
 			     git_submodule_helper_usage, 0);
 
@@ -2563,7 +2562,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 {
 	struct pathspec pathspec;
 	struct update_data opt = UPDATE_DATA_INIT;
-	struct list_objects_filter_options filter_options;
+	struct list_objects_filter_options filter_options = { 0 };
 	int ret;
 
 	struct option module_update_options[] = {
@@ -2623,7 +2622,6 @@ static int module_update(int argc, const char **argv, const char *prefix)
 	update_clone_config_from_gitmodules(&opt.max_jobs);
 	git_config(git_update_clone_config, &opt.max_jobs);
 
-	memset(&filter_options, 0, sizeof(filter_options));
 	argc = parse_options(argc, argv, prefix, module_update_options,
 			     git_submodule_helper_usage, 0);
 
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 02/26] submodule--helper: stop conflating "sb" in clone_submodule()
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  2022-07-21 19:12     ` [PATCH v3 01/26] submodule--helper: replace memset() with { 0 }-initialization Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:12     ` Ævar Arnfjörð Bjarmason
  2022-07-21 21:16       ` Junio C Hamano
  2022-07-21 19:12     ` [PATCH v3 03/26] submodule--helper: pass a "const struct module_clone_data" to clone_submodule() Ævar Arnfjörð Bjarmason
                       ` (24 subsequent siblings)
  26 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:12 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Refactor the three uses of a "struct strbuf sb" such that each of them
exists in its own scope. This makes the control flow clearer.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 27 +++++++++++++++++++--------
 1 file changed, 19 insertions(+), 8 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 73717be957c..29641690c8c 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1638,18 +1638,28 @@ static void prepare_possible_alternates(const char *sm_name,
 	free(error_strategy);
 }
 
-static int clone_submodule(struct module_clone_data *clone_data)
+static char *clone_submodule_sm_gitdir(const char *name)
 {
-	char *p, *sm_gitdir;
-	char *sm_alternate = NULL, *error_strategy = NULL;
 	struct strbuf sb = STRBUF_INIT;
-	struct child_process cp = CHILD_PROCESS_INIT;
+	char *sm_gitdir;
 
-	submodule_name_to_gitdir(&sb, the_repository, clone_data->name);
+	submodule_name_to_gitdir(&sb, the_repository, name);
 	sm_gitdir = absolute_pathdup(sb.buf);
-	strbuf_reset(&sb);
+	strbuf_release(&sb);
+
+	return sm_gitdir;
+}
+
+static int clone_submodule(struct module_clone_data *clone_data)
+{
+	char *p;
+	char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);
+	char *sm_alternate = NULL, *error_strategy = NULL;
+	struct child_process cp = CHILD_PROCESS_INIT;
 
 	if (!is_absolute_path(clone_data->path)) {
+		struct strbuf sb = STRBUF_INIT;
+
 		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
 		clone_data->path = strbuf_detach(&sb, NULL);
 	} else {
@@ -1705,6 +1715,8 @@ static int clone_submodule(struct module_clone_data *clone_data)
 			die(_("clone of '%s' into submodule path '%s' failed"),
 			    clone_data->url, clone_data->path);
 	} else {
+		struct strbuf sb = STRBUF_INIT;
+
 		if (clone_data->require_init && !access(clone_data->path, X_OK) &&
 		    !is_empty_dir(clone_data->path))
 			die(_("directory not empty: '%s'"), clone_data->path);
@@ -1712,7 +1724,7 @@ static int clone_submodule(struct module_clone_data *clone_data)
 			die(_("could not create directory '%s'"), clone_data->path);
 		strbuf_addf(&sb, "%s/index", sm_gitdir);
 		unlink_or_warn(sb.buf);
-		strbuf_reset(&sb);
+		strbuf_release(&sb);
 	}
 
 	connect_work_tree_and_git_dir(clone_data->path, sm_gitdir, 0);
@@ -1734,7 +1746,6 @@ static int clone_submodule(struct module_clone_data *clone_data)
 	free(sm_alternate);
 	free(error_strategy);
 
-	strbuf_release(&sb);
 	free(sm_gitdir);
 	free(p);
 	return 0;
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 03/26] submodule--helper: pass a "const struct module_clone_data" to clone_submodule()
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  2022-07-21 19:12     ` [PATCH v3 01/26] submodule--helper: replace memset() with { 0 }-initialization Ævar Arnfjörð Bjarmason
  2022-07-21 19:12     ` [PATCH v3 02/26] submodule--helper: stop conflating "sb" in clone_submodule() Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:12     ` Ævar Arnfjörð Bjarmason
  2022-07-21 21:26       ` Junio C Hamano
  2022-07-25 17:07       ` Glen Choo
  2022-07-21 19:13     ` [PATCH v3 04/26] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
                       ` (23 subsequent siblings)
  26 siblings, 2 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:12 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Add "const" to the "struct module_clone_data" that we pass to
clone_submodule(), which makes the ownership clear, and stops us from
clobbering the "clone_data->path".

We still need to add to the "reference" member, which is a "struct
string_list". We could do this by having clone_submodule() create its
own, and copy the contents over, but let's instead pass it as a
separate parameter. The main point of doing this is to make it clear
that e.g. "clone_data->path" always comes from the "argv", there's no
ambiguity about whether we can eventually free() the "struct
string_list".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 38 +++++++++++++++++++------------------
 1 file changed, 20 insertions(+), 18 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 29641690c8c..7d5ee6a6261 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1650,20 +1650,22 @@ static char *clone_submodule_sm_gitdir(const char *name)
 	return sm_gitdir;
 }
 
-static int clone_submodule(struct module_clone_data *clone_data)
+static int clone_submodule(const struct module_clone_data *clone_data,
+			   struct string_list *reference)
 {
 	char *p;
 	char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);
 	char *sm_alternate = NULL, *error_strategy = NULL;
 	struct child_process cp = CHILD_PROCESS_INIT;
+	const char *clone_data_path;
 
 	if (!is_absolute_path(clone_data->path)) {
 		struct strbuf sb = STRBUF_INIT;
 
 		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
-		clone_data->path = strbuf_detach(&sb, NULL);
+		clone_data_path = strbuf_detach(&sb, NULL);
 	} else {
-		clone_data->path = xstrdup(clone_data->path);
+		clone_data_path = xstrdup(clone_data_path);
 	}
 
 	if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)
@@ -1674,7 +1676,7 @@ static int clone_submodule(struct module_clone_data *clone_data)
 		if (safe_create_leading_directories_const(sm_gitdir) < 0)
 			die(_("could not create directory '%s'"), sm_gitdir);
 
-		prepare_possible_alternates(clone_data->name, &clone_data->reference);
+		prepare_possible_alternates(clone_data->name, reference);
 
 		strvec_push(&cp.args, "clone");
 		strvec_push(&cp.args, "--no-checkout");
@@ -1684,9 +1686,9 @@ static int clone_submodule(struct module_clone_data *clone_data)
 			strvec_push(&cp.args, "--progress");
 		if (clone_data->depth && *(clone_data->depth))
 			strvec_pushl(&cp.args, "--depth", clone_data->depth, NULL);
-		if (clone_data->reference.nr) {
+		if (reference->nr) {
 			struct string_list_item *item;
-			for_each_string_list_item(item, &clone_data->reference)
+			for_each_string_list_item(item, reference)
 				strvec_pushl(&cp.args, "--reference",
 					     item->string, NULL);
 		}
@@ -1705,7 +1707,7 @@ static int clone_submodule(struct module_clone_data *clone_data)
 
 		strvec_push(&cp.args, "--");
 		strvec_push(&cp.args, clone_data->url);
-		strvec_push(&cp.args, clone_data->path);
+		strvec_push(&cp.args, clone_data_path);
 
 		cp.git_cmd = 1;
 		prepare_submodule_repo_env(&cp.env);
@@ -1713,25 +1715,25 @@ static int clone_submodule(struct module_clone_data *clone_data)
 
 		if(run_command(&cp))
 			die(_("clone of '%s' into submodule path '%s' failed"),
-			    clone_data->url, clone_data->path);
+			    clone_data->url, clone_data_path);
 	} else {
 		struct strbuf sb = STRBUF_INIT;
 
-		if (clone_data->require_init && !access(clone_data->path, X_OK) &&
-		    !is_empty_dir(clone_data->path))
-			die(_("directory not empty: '%s'"), clone_data->path);
-		if (safe_create_leading_directories_const(clone_data->path) < 0)
-			die(_("could not create directory '%s'"), clone_data->path);
+		if (clone_data->require_init && !access(clone_data_path, X_OK) &&
+		    !is_empty_dir(clone_data_path))
+			die(_("directory not empty: '%s'"), clone_data_path);
+		if (safe_create_leading_directories_const(clone_data_path) < 0)
+			die(_("could not create directory '%s'"), clone_data_path);
 		strbuf_addf(&sb, "%s/index", sm_gitdir);
 		unlink_or_warn(sb.buf);
 		strbuf_release(&sb);
 	}
 
-	connect_work_tree_and_git_dir(clone_data->path, sm_gitdir, 0);
+	connect_work_tree_and_git_dir(clone_data_path, sm_gitdir, 0);
 
-	p = git_pathdup_submodule(clone_data->path, "config");
+	p = git_pathdup_submodule(clone_data_path, "config");
 	if (!p)
-		die(_("could not get submodule directory for '%s'"), clone_data->path);
+		die(_("could not get submodule directory for '%s'"), clone_data_path);
 
 	/* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */
 	git_config_get_string("submodule.alternateLocation", &sm_alternate);
@@ -1810,7 +1812,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 		usage_with_options(git_submodule_helper_usage,
 				   module_clone_options);
 
-	clone_submodule(&clone_data);
+	clone_submodule(&clone_data, &clone_data.reference);
 	list_objects_filter_release(&filter_options);
 	return 0;
 }
@@ -3088,7 +3090,7 @@ static int add_submodule(const struct add_data *add_data)
 		if (add_data->depth >= 0)
 			clone_data.depth = xstrfmt("%d", add_data->depth);
 
-		if (clone_submodule(&clone_data))
+		if (clone_submodule(&clone_data, &clone_data.reference))
 			return -1;
 
 		prepare_submodule_repo_env(&cp.env);
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 04/26] submodule--helper: fix a leak in "clone_submodule"
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (2 preceding siblings ...)
  2022-07-21 19:12     ` [PATCH v3 03/26] submodule--helper: pass a "const struct module_clone_data" to clone_submodule() Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 21:30       ` Junio C Hamano
  2022-07-21 19:13     ` [PATCH v3 05/26] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
                       ` (22 subsequent siblings)
  26 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak of the "clone_data_path" variable that we copy or
derive from the "struct module_clone_data" in clone_submodule(). This
code was refactored in preceding commits, but the leak has been with
us since f8eaa0ba98b (submodule--helper, module_clone: always operate
on absolute paths, 2016-03-31).

For the "else" case we don't need to xstrdup() the "clone_data->path",
and we don't need to free our own "clone_data_path".

In the case of the module_clone() caller it's from "argv", and doesn't
need to be free'd, and in the case of the add_submodule() caller we
get a pointer to "sm_path", which doesn't need to be directly free'd
either.

Fixing this leak makes several tests pass, so let's mark them as
passing with TEST_PASSES_SANITIZE_LEAK=true.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c   | 6 ++++--
 t/t1500-rev-parse.sh          | 1 +
 t/t6008-rev-list-submodule.sh | 1 +
 t/t7414-submodule-mistakes.sh | 2 ++
 t/t7506-status-submodule.sh   | 1 +
 t/t7507-commit-verbose.sh     | 2 ++
 6 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 7d5ee6a6261..1ddce8e19c1 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1658,14 +1658,15 @@ static int clone_submodule(const struct module_clone_data *clone_data,
 	char *sm_alternate = NULL, *error_strategy = NULL;
 	struct child_process cp = CHILD_PROCESS_INIT;
 	const char *clone_data_path;
+	char *to_free = NULL;
 
 	if (!is_absolute_path(clone_data->path)) {
 		struct strbuf sb = STRBUF_INIT;
 
 		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
-		clone_data_path = strbuf_detach(&sb, NULL);
+		clone_data_path = to_free = strbuf_detach(&sb, NULL);
 	} else {
-		clone_data_path = xstrdup(clone_data_path);
+		clone_data_path = clone_data->path;
 	}
 
 	if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)
@@ -1750,6 +1751,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,
 
 	free(sm_gitdir);
 	free(p);
+	free(to_free);
 	return 0;
 }
 
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 1c2df08333b..0e13bcb4ebb 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -4,6 +4,7 @@ test_description='test git rev-parse'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_one () {
diff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh
index 3153a0d8910..12e67e187ef 100755
--- a/t/t6008-rev-list-submodule.sh
+++ b/t/t6008-rev-list-submodule.sh
@@ -8,6 +8,7 @@ test_description='git rev-list involving submodules that this repo has'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh
index f2e7df59cf2..3269298197c 100755
--- a/t/t7414-submodule-mistakes.sh
+++ b/t/t7414-submodule-mistakes.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='handling of common mistakes people may make with submodules'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create embedded repository' '
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index 3fcb44767f5..f5426a8e589 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='git status for submodule'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_create_repo_with_commit () {
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index ed2653d46fe..92462a22374 100755
--- a/t/t7507-commit-verbose.sh
+++ b/t/t7507-commit-verbose.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='verbose commit template'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 write_script "check-for-diff" <<\EOF &&
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 05/26] submodule--helper: fix trivial get_default_remote_submodule() leak
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (3 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 04/26] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:13     ` [PATCH v3 06/26] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
                       ` (21 subsequent siblings)
  26 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in code added in 1012a5cbc3f (submodule--helper
run-update-procedure: learn --remote, 2022-03-04), we need to free()
the xstrdup()'d string. This gets e.g. t/t7419-submodule-set-branch.sh
closer to passing under SANITIZE=leak.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 1ddce8e19c1..73ac1fcbb9c 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2486,6 +2486,8 @@ static int update_submodule(struct update_data *update_data)
 		const char *branch = remote_submodule_branch(update_data->sm_path);
 		char *remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch);
 
+		free(remote_name);
+
 		if (!update_data->nofetch) {
 			if (fetch_in_submodule(update_data->sm_path, update_data->depth,
 					      0, NULL))
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 06/26] submodule--helper: fix most "struct pathspec" memory leaks
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (4 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 05/26] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 21:37       ` Junio C Hamano
  2022-07-21 19:13     ` [PATCH v3 07/26] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
                       ` (20 subsequent siblings)
  26 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Call clear_pathspec() at the end of various functions that work with
and allocate a "struct pathspec".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 84 +++++++++++++++++++++++++------------
 1 file changed, 58 insertions(+), 26 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 73ac1fcbb9c..a60dc6af178 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -269,7 +269,7 @@ static char *get_up_path(const char *path)
 static int module_list(int argc, const char **argv, const char *prefix)
 {
 	int i;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 
 	struct option module_list_options[] = {
@@ -278,6 +278,7 @@ static int module_list(int argc, const char **argv, const char *prefix)
 			   N_("alternative anchor for relative paths")),
 		OPT_END()
 	};
+	int ret = 1;
 
 	const char *const git_submodule_helper_usage[] = {
 		N_("git submodule--helper list [--prefix=<path>] [<path>...]"),
@@ -288,7 +289,7 @@ static int module_list(int argc, const char **argv, const char *prefix)
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	for (i = 0; i < list.nr; i++) {
 		const struct cache_entry *ce = list.entries[i];
@@ -302,7 +303,10 @@ static int module_list(int argc, const char **argv, const char *prefix)
 
 		fprintf(stdout, "%s\n", ce->name);
 	}
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static void for_each_listed_submodule(const struct module_list *list,
@@ -427,7 +431,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
 static int module_foreach(int argc, const char **argv, const char *prefix)
 {
 	struct foreach_cb info = FOREACH_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 
 	struct option module_foreach_options[] = {
@@ -441,12 +445,13 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 		N_("git submodule foreach [--quiet] [--recursive] [--] <command>"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_foreach_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.argc = argc;
 	info.argv = argv;
@@ -454,7 +459,10 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static int starts_with_dot_slash(const char *const path)
@@ -562,7 +570,7 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data
 static int module_init(int argc, const char **argv, const char *prefix)
 {
 	struct init_cb info = INIT_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 
@@ -575,12 +583,13 @@ static int module_init(int argc, const char **argv, const char *prefix)
 		N_("git submodule init [<options>] [<path>]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_init_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	/*
 	 * If there are no path args and submodule.active is set then,
@@ -595,7 +604,10 @@ static int module_init(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, init_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct status_cb {
@@ -740,7 +752,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,
 static int module_status(int argc, const char **argv, const char *prefix)
 {
 	struct status_cb info = STATUS_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 
@@ -755,12 +767,13 @@ static int module_status(int argc, const char **argv, const char *prefix)
 		N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_status_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.prefix = prefix;
 	if (quiet)
@@ -768,7 +781,10 @@ static int module_status(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, status_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static int module_name(int argc, const char **argv, const char *prefix)
@@ -1326,10 +1342,11 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data
 static int module_sync(int argc, const char **argv, const char *prefix)
 {
 	struct sync_cb info = SYNC_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	int recursive = 0;
+	int ret = 1;
 
 	struct option module_sync_options[] = {
 		OPT__QUIET(&quiet, N_("suppress output of synchronizing submodule url")),
@@ -1347,7 +1364,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.prefix = prefix;
 	if (quiet)
@@ -1357,7 +1374,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, sync_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct deinit_cb {
@@ -1464,7 +1484,7 @@ static void deinit_submodule_cb(const struct cache_entry *list_item,
 static int module_deinit(int argc, const char **argv, const char *prefix)
 {
 	struct deinit_cb info = DEINIT_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	int force = 0;
@@ -1481,6 +1501,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 		N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_deinit_options,
 			     git_submodule_helper_usage, 0);
@@ -1495,7 +1516,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 		die(_("Use '--all' if you really want to deinitialize all submodules"));
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.prefix = prefix;
 	if (quiet)
@@ -1505,7 +1526,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, deinit_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct module_clone_data {
@@ -2577,7 +2601,7 @@ static int update_submodules(struct update_data *update_data)
 
 static int module_update(int argc, const char **argv, const char *prefix)
 {
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct update_data opt = UPDATE_DATA_INIT;
 	struct list_objects_filter_options filter_options = { 0 };
 	int ret;
@@ -2656,8 +2680,8 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		opt.update_strategy.type = opt.update_default;
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {
-		list_objects_filter_release(&filter_options);
-		return 1;
+		ret = 1;
+		goto cleanup;
 	}
 
 	if (pathspec.nr)
@@ -2668,8 +2692,10 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		struct init_cb info = INIT_CB_INIT;
 
 		if (module_list_compute(argc, argv, opt.prefix,
-					&pathspec, &list) < 0)
-			return 1;
+					&pathspec, &list) < 0) {
+			ret = 1;
+			goto cleanup;
+		}
 
 		/*
 		 * If there are no path args and submodule.active is set then,
@@ -2686,7 +2712,9 @@ static int module_update(int argc, const char **argv, const char *prefix)
 	}
 
 	ret = update_submodules(&opt);
+cleanup:
 	list_objects_filter_release(&filter_options);
+	clear_pathspec(&pathspec);
 	return ret;
 }
 
@@ -2770,9 +2798,10 @@ static int push_check(int argc, const char **argv, const char *prefix)
 static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 {
 	int i;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;
+	int ret = 1;
 
 	struct option embed_gitdir_options[] = {
 		OPT_STRING(0, "prefix", &prefix,
@@ -2792,12 +2821,15 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	for (i = 0; i < list.nr; i++)
 		absorb_git_dir_into_superproject(list.entries[i]->name, flags);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static int is_active(int argc, const char **argv, const char *prefix)
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 07/26] submodule--helper: "struct pathspec" memory leak in module_update()
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (5 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 06/26] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:13     ` [PATCH v3 08/26] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
                       ` (19 subsequent siblings)
  26 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

The module_update() function calls module_list_compute() twice, which
in turn will reset the "struct pathspec" passed to it. Let's instead
track two of them, and clear them both.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index a60dc6af178..f317236f016 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2602,6 +2602,7 @@ static int update_submodules(struct update_data *update_data)
 static int module_update(int argc, const char **argv, const char *prefix)
 {
 	struct pathspec pathspec = { 0 };
+	struct pathspec pathspec2 = { 0 };
 	struct update_data opt = UPDATE_DATA_INIT;
 	struct list_objects_filter_options filter_options = { 0 };
 	int ret;
@@ -2692,7 +2693,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		struct init_cb info = INIT_CB_INIT;
 
 		if (module_list_compute(argc, argv, opt.prefix,
-					&pathspec, &list) < 0) {
+					&pathspec2, &list) < 0) {
 			ret = 1;
 			goto cleanup;
 		}
@@ -2715,6 +2716,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 cleanup:
 	list_objects_filter_release(&filter_options);
 	clear_pathspec(&pathspec);
+	clear_pathspec(&pathspec2);
 	return ret;
 }
 
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 08/26] submodule--helper: don't leak {run,capture}_command() cp.dir argument
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (6 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 07/26] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:13     ` [PATCH v3 09/26] submodule--helper: add "const" to copy of "update_data" Ævar Arnfjörð Bjarmason
                       ` (18 subsequent siblings)
  26 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak in c51f8f94e5b (submodule--helper: run update
procedures from C, 2021-08-24) and 3c3558f0953 (submodule--helper: run
update using child process struct, 2022-03-15) by not allocating
memory in the first place.

The "dir" member of "struct child_process" will not be modified by
that API, and it's declared to be "const char *". So let's not
needlessly duplicate these strings.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f317236f016..fb3102f024f 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2194,7 +2194,7 @@ static int is_tip_reachable(const char *path, struct object_id *oid)
 	char *hex = oid_to_hex(oid);
 
 	cp.git_cmd = 1;
-	cp.dir = xstrdup(path);
+	cp.dir = path;
 	cp.no_stderr = 1;
 	strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL);
 
@@ -2212,7 +2212,7 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str
 
 	prepare_submodule_repo_env(&cp.env);
 	cp.git_cmd = 1;
-	cp.dir = xstrdup(module_path);
+	cp.dir = module_path;
 
 	strvec_push(&cp.args, "fetch");
 	if (quiet)
@@ -2267,7 +2267,7 @@ static int run_update_command(struct update_data *ud, int subforce)
 	}
 	strvec_push(&cp.args, oid);
 
-	cp.dir = xstrdup(ud->sm_path);
+	cp.dir = ud->sm_path;
 	prepare_submodule_repo_env(&cp.env);
 	if (run_command(&cp)) {
 		switch (ud->update_strategy.type) {
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 09/26] submodule--helper: add "const" to copy of "update_data"
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (7 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 08/26] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:13     ` [PATCH v3 10/26] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
                       ` (17 subsequent siblings)
  26 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

From: Glen Choo <chooglen@google.com>

Add a "const" to the copy of "struct update_data" that's tracked by
the "struct submodule_update_clone", as it neither owns nor modifies
it.

Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index fb3102f024f..3223c93f91a 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1889,7 +1889,7 @@ struct submodule_update_clone {
 	int current;
 
 	/* configuration parameters which are passed on to the children */
-	struct update_data *update_data;
+	const struct update_data *update_data;
 
 	/* to be consumed by update_submodule() */
 	struct update_clone_data *update_clone;
@@ -1974,7 +1974,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
 	const char *update_string;
 	enum submodule_update_type update_type;
 	char *key;
-	struct update_data *ud = suc->update_data;
+	const struct update_data *ud = suc->update_data;
 	char *displaypath = get_submodule_displaypath(ce->name, ud->prefix);
 	struct strbuf sb = STRBUF_INIT;
 	int needs_cloning = 0;
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 10/26] submodule--helper: add and use *_release() functions
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (8 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 09/26] submodule--helper: add "const" to copy of "update_data" Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:13     ` [PATCH v3 11/26] submodule--helper: refactor "errmsg_str" to be a "struct strbuf" Ævar Arnfjörð Bjarmason
                       ` (16 subsequent siblings)
  26 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Add release functions for "struct module_list", "struct
submodule_update_clone" and "struct update_data".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c      | 29 ++++++++++++++++++++++++++++-
 t/t6134-pathspec-in-submodule.sh |  1 +
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 3223c93f91a..21b3abb7b40 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -182,6 +182,11 @@ struct module_list {
 };
 #define MODULE_LIST_INIT { 0 }
 
+static void module_list_release(struct module_list *ml)
+{
+	free(ml->entries);
+}
+
 static int module_list_compute(int argc, const char **argv,
 			       const char *prefix,
 			       struct pathspec *pathspec,
@@ -243,7 +248,7 @@ static void module_list_active(struct module_list *list)
 		active_modules.entries[active_modules.nr++] = ce;
 	}
 
-	free(list->entries);
+	module_list_release(list);
 	*list = active_modules;
 }
 
@@ -305,6 +310,7 @@ static int module_list(int argc, const char **argv, const char *prefix)
 	}
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -461,6 +467,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -606,6 +613,7 @@ static int module_init(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -783,6 +791,7 @@ static int module_status(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1376,6 +1385,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1528,6 +1538,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1904,6 +1915,12 @@ struct submodule_update_clone {
 };
 #define SUBMODULE_UPDATE_CLONE_INIT { 0 }
 
+static void submodule_update_clone_release(struct submodule_update_clone *suc)
+{
+	free(suc->update_clone);
+	free(suc->failed_clones);
+}
+
 struct update_data {
 	const char *prefix;
 	const char *displaypath;
@@ -1942,6 +1959,11 @@ struct update_data {
 	.max_jobs = 1, \
 }
 
+static void update_data_release(struct update_data *ud)
+{
+	module_list_release(&ud->list);
+}
+
 static void next_submodule_warn_missing(struct submodule_update_clone *suc,
 		struct strbuf *out, const char *displaypath)
 {
@@ -2595,6 +2617,7 @@ static int update_submodules(struct update_data *update_data)
 	}
 
 cleanup:
+	submodule_update_clone_release(&suc);
 	string_list_clear(&update_data->references, 0);
 	return res;
 }
@@ -2694,6 +2717,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 
 		if (module_list_compute(argc, argv, opt.prefix,
 					&pathspec2, &list) < 0) {
+			module_list_release(&list);
 			ret = 1;
 			goto cleanup;
 		}
@@ -2710,10 +2734,12 @@ static int module_update(int argc, const char **argv, const char *prefix)
 			info.flags |= OPT_QUIET;
 
 		for_each_listed_submodule(&list, init_submodule_cb, &info);
+		module_list_release(&list);
 	}
 
 	ret = update_submodules(&opt);
 cleanup:
+	update_data_release(&opt);
 	list_objects_filter_release(&filter_options);
 	clear_pathspec(&pathspec);
 	clear_pathspec(&pathspec2);
@@ -2831,6 +2857,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 	ret = 0;
 cleanup:
 	clear_pathspec(&pathspec);
+	module_list_release(&list);
 	return ret;
 }
 
diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh
index 0f1cb49cedc..3a241f259de 100755
--- a/t/t6134-pathspec-in-submodule.sh
+++ b/t/t6134-pathspec-in-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='test case exclude pathspec'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a submodule' '
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 11/26] submodule--helper: refactor "errmsg_str" to be a "struct strbuf"
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (9 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 10/26] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-25 23:15       ` Glen Choo
  2022-07-21 19:13     ` [PATCH v3 12/26] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
                       ` (15 subsequent siblings)
  26 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

From: Glen Choo <chooglen@google.com>

Refactor code added in e83e3333b57 (submodule: port submodule
subcommand 'summary' from shell to C, 2020-08-13) so that "errmsg" and
"errmsg_str" are folded into one. The distinction between the empty
string and NULL is something that's tested for by
e.g. "t/t7401-submodule-summary.sh".

This is in preparation for fixing a memory leak the "struct strbuf" in
the pre-image.

Let's also pass a "const char *" to print_submodule_summary(), as it
should not be modifying the "errmsg".

Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 21b3abb7b40..f794d2b588b 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -866,7 +866,7 @@ static char *verify_submodule_committish(const char *sm_path,
 	return strbuf_detach(&result, NULL);
 }
 
-static void print_submodule_summary(struct summary_cb *info, char *errmsg,
+static void print_submodule_summary(struct summary_cb *info, const char *errmsg,
 				    int total_commits, const char *displaypath,
 				    const char *src_abbrev, const char *dst_abbrev,
 				    struct module_cb *p)
@@ -924,7 +924,7 @@ static void generate_submodule_summary(struct summary_cb *info,
 {
 	char *displaypath, *src_abbrev = NULL, *dst_abbrev;
 	int missing_src = 0, missing_dst = 0;
-	char *errmsg = NULL;
+	struct strbuf errmsg = STRBUF_INIT;
 	int total_commits = -1;
 
 	if (!info->cached && oideq(&p->oid_dst, null_oid())) {
@@ -1024,23 +1024,21 @@ static void generate_submodule_summary(struct summary_cb *info,
 		 * submodule, i.e., deleted or changed to blob
 		 */
 		if (S_ISGITLINK(p->mod_dst)) {
-			struct strbuf errmsg_str = STRBUF_INIT;
 			if (missing_src && missing_dst) {
-				strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commits %s and %s\n",
+				strbuf_addf(&errmsg, "  Warn: %s doesn't contain commits %s and %s\n",
 					    displaypath, oid_to_hex(&p->oid_src),
 					    oid_to_hex(&p->oid_dst));
 			} else {
-				strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commit %s\n",
+				strbuf_addf(&errmsg, "  Warn: %s doesn't contain commit %s\n",
 					    displaypath, missing_src ?
 					    oid_to_hex(&p->oid_src) :
 					    oid_to_hex(&p->oid_dst));
 			}
-			errmsg = strbuf_detach(&errmsg_str, NULL);
 		}
 	}
 
-	print_submodule_summary(info, errmsg, total_commits,
-				displaypath, src_abbrev,
+	print_submodule_summary(info, errmsg.len ? errmsg.buf : NULL,
+				total_commits, displaypath, src_abbrev,
 				dst_abbrev, p);
 
 	free(displaypath);
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 12/26] submodule--helper: fix "errmsg_str" memory leak
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (10 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 11/26] submodule--helper: refactor "errmsg_str" to be a "struct strbuf" Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:13     ` [PATCH v3 13/26] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
                       ` (14 subsequent siblings)
  26 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak introduced in e83e3333b57 (submodule: port submodule
subcommand 'summary' from shell to C, 2020-08-13), we sometimes append
to the "errmsg", and need to free the "struct strbuf".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f794d2b588b..ad11ff20022 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1044,6 +1044,7 @@ static void generate_submodule_summary(struct summary_cb *info,
 	free(displaypath);
 	free(src_abbrev);
 	free(dst_abbrev);
+	strbuf_release(&errmsg);
 }
 
 static void prepare_submodule_summary(struct summary_cb *info,
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 13/26] submodule--helper: fix "sm_path" and other "module_cb_list" leaks
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (11 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 12/26] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:13     ` [PATCH v3 14/26] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
                       ` (13 subsequent siblings)
  26 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix leaks in "struct module_cb_list" and the "struct module_cb" which
it contains, these fixes leaks in e83e3333b57 (submodule: port
submodule subcommand 'summary' from shell to C, 2020-08-13).

The "sm_path" should always have been a "char *", not a "const
char *", we always create it with xstrdup().

We can't mark any tests passing passing with SANITIZE=leak using
"TEST_PASSES_SANITIZE_LEAK=true" as a result of this change, but
"t7401-submodule-summary.sh" gets closer to passing as a result of
this change.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index ad11ff20022..a5492c6d89a 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -820,16 +820,34 @@ struct module_cb {
 	struct object_id oid_src;
 	struct object_id oid_dst;
 	char status;
-	const char *sm_path;
+	char *sm_path;
 };
 #define MODULE_CB_INIT { 0 }
 
+static void module_cb_release(struct module_cb *mcb)
+{
+	free(mcb->sm_path);
+}
+
 struct module_cb_list {
 	struct module_cb **entries;
 	int alloc, nr;
 };
 #define MODULE_CB_LIST_INIT { 0 }
 
+static void module_cb_list_release(struct module_cb_list *mcbl)
+{
+	int i;
+
+	for (i = 0; i < mcbl->nr; i++) {
+		struct module_cb *mcb = mcbl->entries[i];
+
+		module_cb_release(mcb);
+		free(mcb);
+	}
+	free(mcbl->entries);
+}
+
 struct summary_cb {
 	int argc;
 	const char **argv;
@@ -1172,6 +1190,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 cleanup:
 	strvec_clear(&diff_args);
 	release_revisions(&rev);
+	module_cb_list_release(&list);
 	return ret;
 }
 
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 14/26] submodule--helper: fix a leak with repo_clear()
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (12 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 13/26] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:13     ` [PATCH v3 15/26] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
                       ` (12 subsequent siblings)
  26 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Call repo_clear() in ensure_core_worktree() to free the "struct
repository". Fixes a leak that's been here since
74d4731da1f (submodule--helper: replace connect-gitdir-workingtree by
ensure-core-worktree, 2018-08-13).

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index a5492c6d89a..a3de17754fd 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2458,6 +2458,7 @@ static void ensure_core_worktree(const char *path)
 		free(abs_path);
 		strbuf_release(&sb);
 	}
+	repo_clear(&subrepo);
 }
 
 static const char *submodule_update_type_to_label(enum submodule_update_type type)
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 15/26] submodule--helper: fix a memory leak in get_default_remote_submodule()
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (13 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 14/26] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:13     ` [PATCH v3 16/26] submodule--helper: fix "reference" leak is "module_clone_data" Ævar Arnfjörð Bjarmason
                       ` (11 subsequent siblings)
  26 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak in the get_default_remote_submodule() function added
in a77c3fcb5ec (submodule--helper: get remote names from any
repository, 2022-03-04), we need to repo_clear() the submodule we
initialize.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index a3de17754fd..fe490c8d44d 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -62,9 +62,13 @@ static char *repo_get_default_remote(struct repository *repo)
 static char *get_default_remote_submodule(const char *module_path)
 {
 	struct repository subrepo;
+	char *ret;
 
 	repo_submodule_init(&subrepo, the_repository, module_path, null_oid());
-	return repo_get_default_remote(&subrepo);
+	ret = repo_get_default_remote(&subrepo);
+	repo_clear(&subrepo);
+
+	return ret;
 }
 
 static char *get_default_remote(void)
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 16/26] submodule--helper: fix "reference" leak is "module_clone_data"
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (14 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 15/26] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:13     ` [PATCH v3 17/26] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
                       ` (10 subsequent siblings)
  26 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix leaks in the "reference" member of "struct module_clone_data" that
have been with us since 31224cbdc72 (clone: recursive and reference
option triggers submodule alternates, 2016-08-17) and
8c8195e9c3e (submodule--helper: introduce add-clone subcommand,
2021-07-10).

Those commits added an xstrdup()'d member of the
STRING_LIST_INIT_NODUP'd "struct string_list". We need to free()
those, but not the ones we get from argv, let's make use of the "util"
member, if it has a pointer it's the pointer we'll need to free,
otherwise it'll be NULL (i.e. from argv).

To fix this create a a module_clone_data_release() function to go with
the MODULE_CLONE_DATA_INIT added in a98b02c1128 (submodule--helper:
refactor module_clone(), 2021-07-10). We only need to add it to
add_submodule() to fix the leak, but let's add it to module_clone() as
well for consistency.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index fe490c8d44d..80c1c1d4918 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1584,6 +1584,11 @@ struct module_clone_data {
 	.single_branch = -1, \
 }
 
+static void module_clone_data_release(struct module_clone_data *cd)
+{
+	string_list_clear(&cd->reference, 1);
+}
+
 struct submodule_alternate_setup {
 	const char *submodule_name;
 	enum SUBMODULE_ALTERNATE_ERROR_MODE {
@@ -1636,7 +1641,9 @@ static int add_possible_reference_from_superproject(
 
 		sm_alternate = compute_alternate_path(sb.buf, &err);
 		if (sm_alternate) {
-			string_list_append(sas->reference, xstrdup(sb.buf));
+			char *p = strbuf_detach(&sb, NULL);
+
+			string_list_append(sas->reference, p)->util = p;
 			free(sm_alternate);
 		} else {
 			switch (sas->error_mode) {
@@ -1873,6 +1880,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 
 	clone_submodule(&clone_data, &clone_data.reference);
 	list_objects_filter_release(&filter_options);
+	module_clone_data_release(&clone_data);
 	return 0;
 }
 
@@ -3117,6 +3125,7 @@ static int add_submodule(const struct add_data *add_data)
 {
 	char *submod_gitdir_path;
 	struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
+	int ret = -1;
 
 	/* perhaps the path already exists and is already a git repo, else clone it */
 	if (is_directory(add_data->sm_path)) {
@@ -3171,15 +3180,17 @@ static int add_submodule(const struct add_data *add_data)
 		clone_data.url = add_data->realrepo;
 		clone_data.quiet = add_data->quiet;
 		clone_data.progress = add_data->progress;
-		if (add_data->reference_path)
-			string_list_append(&clone_data.reference,
-					   xstrdup(add_data->reference_path));
+		if (add_data->reference_path) {
+			char *p = xstrdup(add_data->reference_path);
+
+			string_list_append(&clone_data.reference, p)->util = p;
+		}
 		clone_data.dissociate = add_data->dissociate;
 		if (add_data->depth >= 0)
 			clone_data.depth = xstrfmt("%d", add_data->depth);
 
 		if (clone_submodule(&clone_data, &clone_data.reference))
-			return -1;
+			goto cleanup;
 
 		prepare_submodule_repo_env(&cp.env);
 		cp.git_cmd = 1;
@@ -3198,7 +3209,10 @@ static int add_submodule(const struct add_data *add_data)
 		if (run_command(&cp))
 			die(_("unable to checkout submodule '%s'"), add_data->sm_path);
 	}
-	return 0;
+	ret = 0;
+cleanup:
+	module_clone_data_release(&clone_data);
+	return ret;
 }
 
 static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 17/26] submodule--helper: fix obscure leak in module_add()
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (15 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 16/26] submodule--helper: fix "reference" leak is "module_clone_data" Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 21:45       ` Junio C Hamano
  2022-07-21 19:13     ` [PATCH v3 18/26] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
                       ` (9 subsequent siblings)
  26 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix an obscure leak in module_add()< if the "git add" command we were
piping to failed we'd fail to strbuf_release(&sb). This fixes a leak
introduced in a6226fd772b (submodule--helper: convert the bulk of
cmd_add() to C, 2021-08-10).

In fixing it move to a "goto cleanup" pattern, and since we need to
introduce a "ret" variable to do that let's also get rid of the
intermediate "exit_code" variable. The initialization to "-1" in
a6226fd772b has always been redundant, we'd only use the "exit_code"
value after assigning the return value of pipe_command() to it.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 80c1c1d4918..04b5141c0f9 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3352,6 +3352,8 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	int force = 0, quiet = 0, progress = 0, dissociate = 0;
 	struct add_data add_data = ADD_DATA_INIT;
 	char *to_free = NULL;
+	struct strbuf sb = STRBUF_INIT;
+	int ret = 1;
 
 	struct option options[] = {
 		OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
@@ -3423,20 +3425,16 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	die_on_repo_without_commits(add_data.sm_path);
 
 	if (!force) {
-		int exit_code = -1;
-		struct strbuf sb = STRBUF_INIT;
 		struct child_process cp = CHILD_PROCESS_INIT;
 		cp.git_cmd = 1;
 		cp.no_stdout = 1;
 		strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
 			     "--no-warn-embedded-repo", add_data.sm_path, NULL);
-		if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
+		if ((ret = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
 			strbuf_complete_line(&sb);
 			fputs(sb.buf, stderr);
-			free(add_data.sm_path);
-			return exit_code;
+			goto cleanup;
 		}
-		strbuf_release(&sb);
 	}
 
 	if(!add_data.sm_name)
@@ -3451,15 +3449,17 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	add_data.progress = !!progress;
 	add_data.dissociate = !!dissociate;
 
-	if (add_submodule(&add_data)) {
-		free(add_data.sm_path);
-		return 1;
-	}
+	if (add_submodule(&add_data))
+		goto cleanup;
 	configure_added_submodule(&add_data);
+
+	ret = 0;
+cleanup:
 	free(add_data.sm_path);
 	free(to_free);
+	strbuf_release(&sb);
 
-	return 0;
+	return ret;
 }
 
 #define SUPPORT_SUPER_PREFIX (1<<0)
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 18/26] submodule--helper: fix a leak in module_add()
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (16 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 17/26] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:13     ` [PATCH v3 19/26] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
                       ` (8 subsequent siblings)
  26 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in module_path(), since a6226fd772b (submodule--helper:
convert the bulk of cmd_add() to C, 2021-08-10), we've been freeing
add_data.sm_path, but in this case we clobbered it, and didn't free
the value we clobbered.

This makes test 28 of "t/t7400-submodule-basic.sh" ("submodule add in
subdirectory") pass when we're compiled with SANITIZE=leak..

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 04b5141c0f9..0fb720d3a16 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3395,8 +3395,11 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	else
 		add_data.sm_path = xstrdup(argv[1]);
 
-	if (prefix && *prefix && !is_absolute_path(add_data.sm_path))
-		add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
+	if (prefix && *prefix && !is_absolute_path(add_data.sm_path)) {
+		char *sm_path = add_data.sm_path;
+		add_data.sm_path = xstrfmt("%s%s", prefix, sm_path);
+		free(sm_path);
+	}
 
 	if (starts_with_dot_dot_slash(add_data.repo) ||
 	    starts_with_dot_slash(add_data.repo)) {
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 19/26] submodule--helper: fix a memory leak in print_status()
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (17 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 18/26] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:13     ` [PATCH v3 20/26] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
                       ` (7 subsequent siblings)
  26 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in print_status(), the compute_rev_name() function
implemented in this file will return a strbuf_detach()'d value, or
NULL.

This leak has existed since this code was added in
a9f8a37584a (submodule: port submodule subcommand 'status' from shell
to C, 2017-10-06), but in 0b5e2ea7cf3 (submodule--helper: don't print
null in 'submodule status', 2018-04-18) we added a "const"
intermediate variable for the return value, that "const" should be
removed.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 0fb720d3a16..4decbdcdc5c 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -637,10 +637,11 @@ static void print_status(unsigned int flags, char state, const char *path,
 	printf("%c%s %s", state, oid_to_hex(oid), displaypath);
 
 	if (state == ' ' || state == '+') {
-		const char *name = compute_rev_name(path, oid_to_hex(oid));
+		char *name = compute_rev_name(path, oid_to_hex(oid));
 
 		if (name)
 			printf(" (%s)", name);
+		free(name);
 	}
 
 	printf("\n");
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 20/26] submodule--helper: free some "displaypath" in "struct update_data"
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (18 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 19/26] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:13     ` [PATCH v3 21/26] submodule--helper: rename "int res" to "int ret" Ævar Arnfjörð Bjarmason
                       ` (6 subsequent siblings)
  26 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Make the update_data_release() function free "displaypath" member when
appropriate. The "displaypath" member is always ours, the "const" on
the "char *" was wrong to begin with.

This leaves a leak of "displaypath" in update_submodule(), which as
we'll see in subsequent commits is harder to deal with than this
trivial fix.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c        | 3 ++-
 t/t2403-worktree-move.sh           | 1 +
 t/t7412-submodule-absorbgitdirs.sh | 1 +
 3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 4decbdcdc5c..378f85e37fa 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1954,7 +1954,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)
 
 struct update_data {
 	const char *prefix;
-	const char *displaypath;
+	char *displaypath;
 	enum submodule_update_type update_default;
 	struct object_id suboid;
 	struct string_list references;
@@ -1992,6 +1992,7 @@ struct update_data {
 
 static void update_data_release(struct update_data *ud)
 {
+	free(ud->displaypath);
 	module_list_release(&ud->list);
 }
 
diff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh
index a4e1a178e0a..1168e9f9982 100755
--- a/t/t2403-worktree-move.sh
+++ b/t/t2403-worktree-move.sh
@@ -2,6 +2,7 @@
 
 test_description='test git worktree move, remove, lock and unlock'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh
index 1cfa150768d..2859695c6d2 100755
--- a/t/t7412-submodule-absorbgitdirs.sh
+++ b/t/t7412-submodule-absorbgitdirs.sh
@@ -6,6 +6,7 @@ This test verifies that `git submodue absorbgitdirs` moves a submodules git
 directory into the superproject.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a real submodule' '
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 21/26] submodule--helper: rename "int res" to "int ret"
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (19 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 20/26] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:13     ` [PATCH v3 22/26] submodule--helper: add skeleton "goto cleanup" to update_submodule() Ævar Arnfjörð Bjarmason
                       ` (5 subsequent siblings)
  26 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Rename the "res" variable added in b3c5f5cb048 (submodule: move core
cmd_update() logic to C, 2022-03-15) to "ret", which is the convention
in the rest of this file. Subsequent commits will change this code to
a "goto cleanup" pattern, let's have the post image look consistent
with the rest.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 378f85e37fa..94eec34c595 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2588,7 +2588,7 @@ static int update_submodule(struct update_data *update_data)
 	if (update_data->recursive) {
 		struct child_process cp = CHILD_PROCESS_INIT;
 		struct update_data next = *update_data;
-		int res;
+		int ret;
 
 		next.prefix = NULL;
 		oidcpy(&next.oid, null_oid());
@@ -2600,14 +2600,14 @@ static int update_submodule(struct update_data *update_data)
 		update_data_to_args(&next, &cp.args);
 
 		/* die() if child process die()'d */
-		res = run_command(&cp);
-		if (!res)
+		ret = run_command(&cp);
+		if (!ret)
 			return 0;
 		die_message(_("Failed to recurse into submodule path '%s'"),
 			    update_data->displaypath);
-		if (res == 128)
-			exit(res);
-		else if (res)
+		if (ret == 128)
+			exit(ret);
+		else if (ret)
 			return 1;
 	}
 
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 22/26] submodule--helper: add skeleton "goto cleanup" to update_submodule()
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (20 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 21/26] submodule--helper: rename "int res" to "int ret" Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:13     ` [PATCH v3 23/26] submodule--helper: don't exit() on failure, return Ævar Arnfjörð Bjarmason
                       ` (4 subsequent siblings)
  26 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Add a skeleton "goto cleanup" pattern to update_submodule(), rather
than having branches in it "return". This is in preparation for doing
something useful with the "cleanup" label, but for now we're using it
as the equivalent of a "done" label.

The "exit()" branch is not handled yet, and neither is the exit() that
run_update_procedure() might invoke. That'll be handled in a
subsequent commit.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 94eec34c595..790f0ccb82e 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2545,6 +2545,8 @@ static void update_data_to_args(struct update_data *update_data, struct strvec *
 
 static int update_submodule(struct update_data *update_data)
 {
+	int ret = 1;
+
 	ensure_core_worktree(update_data->sm_path);
 
 	update_data->displaypath = get_submodule_displaypath(
@@ -2581,14 +2583,14 @@ static int update_submodule(struct update_data *update_data)
 		free(remote_ref);
 	}
 
-	if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force)
+	if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) {
 		if (run_update_procedure(update_data))
-			return 1;
+			goto cleanup;
+	}
 
 	if (update_data->recursive) {
 		struct child_process cp = CHILD_PROCESS_INIT;
 		struct update_data next = *update_data;
-		int ret;
 
 		next.prefix = NULL;
 		oidcpy(&next.oid, null_oid());
@@ -2602,16 +2604,20 @@ static int update_submodule(struct update_data *update_data)
 		/* die() if child process die()'d */
 		ret = run_command(&cp);
 		if (!ret)
-			return 0;
+			goto cleanup;
 		die_message(_("Failed to recurse into submodule path '%s'"),
 			    update_data->displaypath);
-		if (ret == 128)
+		if (ret == 128) {
 			exit(ret);
-		else if (ret)
-			return 1;
+		} else if (ret) {
+			ret = 1;
+			goto cleanup;
+		}
 	}
 
-	return 0;
+	ret = 0;
+cleanup:
+	return ret;
 }
 
 static int update_submodules(struct update_data *update_data)
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 23/26] submodule--helper: don't exit() on failure, return
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (21 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 22/26] submodule--helper: add skeleton "goto cleanup" to update_submodule() Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-25 23:57       ` Glen Choo
  2022-07-21 19:13     ` [PATCH v3 24/26] submodule--helper: free rest of "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
                       ` (3 subsequent siblings)
  26 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Change code downstream of module_update() to short-circuit and return
to the top-level on failure, rather than calling exit().

To do so we need to diligently check whether we "must_die_on_failure",
which is a pattern started in c51f8f94e5b (submodule--helper: run
update procedures from C, 2021-08-24), but which hadn't been completed
to the point where we could avoid calling exit() here.

This introduces no functional changes, but makes it easier to both
call these routines as a library in the future, and to avoid leaking
memory.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 36 ++++++++++++++++++++++++++----------
 1 file changed, 26 insertions(+), 10 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 790f0ccb82e..b65665105e7 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2283,7 +2283,8 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str
 	return run_command(&cp);
 }
 
-static int run_update_command(struct update_data *ud, int subforce)
+static int run_update_command(struct update_data *ud, int subforce,
+			      int *must_die_on_failurep)
 {
 	struct child_process cp = CHILD_PROCESS_INIT;
 	char *oid = oid_to_hex(&ud->oid);
@@ -2345,8 +2346,10 @@ static int run_update_command(struct update_data *ud, int subforce)
 			BUG("unexpected update strategy type: %s",
 			    submodule_strategy_to_string(&ud->update_strategy));
 		}
-		if (must_die_on_failure)
-			exit(128);
+		if (must_die_on_failure) {
+			*must_die_on_failurep = 1;
+			return 128;
+		}
 
 		/* the command failed, but update must continue */
 		return 1;
@@ -2380,7 +2383,8 @@ static int run_update_command(struct update_data *ud, int subforce)
 	return 0;
 }
 
-static int run_update_procedure(struct update_data *ud)
+static int run_update_procedure(struct update_data *ud,
+				int *must_die_on_failure)
 {
 	int subforce = is_null_oid(&ud->suboid) || ud->force;
 
@@ -2407,7 +2411,7 @@ static int run_update_procedure(struct update_data *ud)
 			    ud->displaypath, oid_to_hex(&ud->oid));
 	}
 
-	return run_update_command(ud, subforce);
+	return run_update_command(ud, subforce, must_die_on_failure);
 }
 
 static const char *remote_submodule_branch(const char *path)
@@ -2543,7 +2547,8 @@ static void update_data_to_args(struct update_data *update_data, struct strvec *
 				    "--no-single-branch");
 }
 
-static int update_submodule(struct update_data *update_data)
+static int update_submodule(struct update_data *update_data,
+			    int *must_die_on_failure)
 {
 	int ret = 1;
 
@@ -2584,8 +2589,13 @@ static int update_submodule(struct update_data *update_data)
 	}
 
 	if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) {
-		if (run_update_procedure(update_data))
+		ret = run_update_procedure(update_data, must_die_on_failure);
+		if (ret && *must_die_on_failure) {
+			goto cleanup;
+		} else if (ret) {
+			ret = 1;
 			goto cleanup;
+		}
 	}
 
 	if (update_data->recursive) {
@@ -2608,7 +2618,8 @@ static int update_submodule(struct update_data *update_data)
 		die_message(_("Failed to recurse into submodule path '%s'"),
 			    update_data->displaypath);
 		if (ret == 128) {
-			exit(ret);
+			*must_die_on_failure = 1;
+			goto cleanup;
 		} else if (ret) {
 			ret = 1;
 			goto cleanup;
@@ -2646,13 +2657,18 @@ static int update_submodules(struct update_data *update_data)
 
 	for (i = 0; i < suc.update_clone_nr; i++) {
 		struct update_clone_data ucd = suc.update_clone[i];
+		int code;
+		int must_die_on_failure = 0;
 
 		oidcpy(&update_data->oid, &ucd.oid);
 		update_data->just_cloned = ucd.just_cloned;
 		update_data->sm_path = ucd.sub->path;
 
-		if (update_submodule(update_data))
-			res = 1;
+		code = update_submodule(update_data, &must_die_on_failure);
+		if (code)
+			res = code;
+		if (must_die_on_failure)
+			goto cleanup;
 	}
 
 cleanup:
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 24/26] submodule--helper: free rest of "displaypath" in "struct update_data"
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (22 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 23/26] submodule--helper: don't exit() on failure, return Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-26  1:06       ` Glen Choo
  2022-07-21 19:13     ` [PATCH v3 25/26] submodule--helper: fix bad config API usage Ævar Arnfjörð Bjarmason
                       ` (2 subsequent siblings)
  26 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in code added in c51f8f94e5b (submodule--helper: run update
procedures from C, 2021-08-24), we clobber the "displaypath" member of
the passed-in "struct update_data" both so that die() messages in this
update_submodule() function itself can use it, and for the
run_update_procedure() called within this function.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index b65665105e7..4e70a74357c 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2551,10 +2551,11 @@ static int update_submodule(struct update_data *update_data,
 			    int *must_die_on_failure)
 {
 	int ret = 1;
+	char *to_free, *restore = update_data->displaypath;
 
 	ensure_core_worktree(update_data->sm_path);
 
-	update_data->displaypath = get_submodule_displaypath(
+	update_data->displaypath = to_free = get_submodule_displaypath(
 		update_data->sm_path, update_data->prefix);
 
 	determine_submodule_update_strategy(the_repository, update_data->just_cloned,
@@ -2628,6 +2629,9 @@ static int update_submodule(struct update_data *update_data,
 
 	ret = 0;
 cleanup:
+	free(to_free);
+	update_data->displaypath = restore;
+
 	return ret;
 }
 
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 25/26] submodule--helper: fix bad config API usage
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (23 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 24/26] submodule--helper: free rest of "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 19:13     ` [PATCH v3 26/26] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
  26 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix bad config API usage added in a452128a36c (submodule--helper:
introduce add-config subcommand, 2021-08-06). After
git_config_get_string() returns successfully we know the "char **dest"
will be non-NULL.

A coccinelle patch that transforms this turns up a couple of other
such issues, one in fetch-pack.c, and another in upload-pack.c:

	@@
	identifier F =~ "^(repo|git)_config_get_string(_tmp)?$";
	identifier V;
	@@
	  !F(..., &V)
	- && (V)

But let's focus narrowly on submodule--helper for now, we can fix
those some other time.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 4e70a74357c..37c145b3ede 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3306,7 +3306,7 @@ static void configure_added_submodule(struct add_data *add_data)
 	 * is_submodule_active(), since that function needs to find
 	 * out the value of "submodule.active" again anyway.
 	 */
-	if (!git_config_get_string("submodule.active", &val) && val) {
+	if (!git_config_get_string("submodule.active", &val)) {
 		/*
 		 * If the submodule being added isn't already covered by the
 		 * current configured pathspec, set the submodule's active flag
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v3 26/26] submodule--helper: fix a configure_added_submodule() leak
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (24 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 25/26] submodule--helper: fix bad config API usage Ævar Arnfjörð Bjarmason
@ 2022-07-21 19:13     ` Ævar Arnfjörð Bjarmason
  2022-07-21 21:51       ` Junio C Hamano
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
  26 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-21 19:13 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix config API a memory leak added in a452128a36c (submodule--helper:
introduce add-config subcommand, 2021-08-06) by using the *_tmp()
variant of git_config_get_string().

In this case we're only checking whether
the (repo|git)_config_get_string() call is telling us that the
"submodule.active" key exists.

As with the preceding commit we'll find many other such patterns in
the codebase if we go fishing. E.g. "git gc" leaks in the code added
in 61f7a383d3b (maintenance: use 'incremental' strategy by default,
2020-10-15). Similar code in "git gc" added in
b08ff1fee00 (maintenance: add --schedule option and config,
2020-09-11) doesn't leak, but we could avoid the malloc() & free() in
that case.

A coccinelle rule to find those would find and fix some leaks, and
cases where we're doing needless malloc() + free()'s but only care
about the key existence, or are copying
the (repo|git)_config_get_string() return value right away.

But as with the preceding commit let's punt on all of that for now,
and just narrowly fix this specific case in submodule--helper.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c    | 4 ++--
 t/t7413-submodule-is-active.sh | 1 +
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 37c145b3ede..fdf96c5d424 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3261,7 +3261,7 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con
 static void configure_added_submodule(struct add_data *add_data)
 {
 	char *key;
-	char *val = NULL;
+	const char *val;
 	struct child_process add_submod = CHILD_PROCESS_INIT;
 	struct child_process add_gitmodules = CHILD_PROCESS_INIT;
 
@@ -3306,7 +3306,7 @@ static void configure_added_submodule(struct add_data *add_data)
 	 * is_submodule_active(), since that function needs to find
 	 * out the value of "submodule.active" again anyway.
 	 */
-	if (!git_config_get_string("submodule.active", &val)) {
+	if (!git_config_get_string_tmp("submodule.active", &val)) {
 		/*
 		 * If the submodule being added isn't already covered by the
 		 * current configured pathspec, set the submodule's active flag
diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh
index c8e7e983317..c4c1f86f3d2 100755
--- a/t/t7413-submodule-is-active.sh
+++ b/t/t7413-submodule-is-active.sh
@@ -6,6 +6,7 @@ This test verifies that `git submodue--helper is-active` correctly identifies
 submodules which are "active" and interesting to the user.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-- 
2.37.1.1095.g0bd6f54ba8a


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* Re: [PATCH v2 05/24] submodule--helper: "struct pathspec" memory leak in module_update()
  2022-07-19 20:46   ` [PATCH v2 05/24] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
  2022-07-20 16:43     ` Junio C Hamano
@ 2022-07-21 19:54     ` Glen Choo
  1 sibling, 0 replies; 186+ messages in thread
From: Glen Choo @ 2022-07-21 19:54 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> The module_update() function calls module_list_compute() twice, which
> in turn will reset the "struct pathspec" passed to it. Let's instead
> track two of them, and clear them both.
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 28c5fdb8954..7466e781e97 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -2602,6 +2602,7 @@ static int update_submodules(struct update_data *update_data)
>  static int module_update(int argc, const char **argv, const char *prefix)
>  {
>  	struct pathspec pathspec = { 0 };
> +	struct pathspec pathspec2 = { 0 };
>  	struct update_data opt = UPDATE_DATA_INIT;
>  	struct list_objects_filter_options filter_options = { 0 };
>  	int ret;
> @@ -2692,7 +2693,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
>  		struct init_cb info = INIT_CB_INIT;
>  
>  		if (module_list_compute(argc, argv, opt.prefix,
> -					&pathspec, &list) < 0) {
> +					&pathspec2, &list) < 0) {
>  			ret = 1;
>  			goto cleanup;
>  		}

This change looks good, but we should do more refactoring in the future.

This bit of code inside "if (opt.init)" was copied over from
module_init() in 29a5e9e1ff (submodule--helper update-clone: learn
--init, 2022-03-04). Prior to that commit, we used to just invoke "git
submodule init" in git-submodule.sh.

What I wished I had done instead is to create a helper function that can
be used by both module_init() and module_update(). Something like:

----- >8 --------- >8 --------- >8 --------- >8 --------- >8 ----
  diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
  index 28c5fdb895..2040cde4ba 100644
  --- a/builtin/submodule--helper.c
  +++ b/builtin/submodule--helper.c
  @@ -481,14 +481,14 @@ static int starts_with_dot_dot_slash(const char *const path)
          PATH_MATCH_XPLATFORM);
  }

  -struct init_cb {
  +struct init_opts {
    const char *prefix;
  -	unsigned int flags;
  +	int quiet;
  };
  #define INIT_CB_INIT { 0 }

  static void init_submodule(const char *path, const char *prefix,
  -			   unsigned int flags)
  +			   int quiet)
  {
    const struct submodule *sub;
    struct strbuf sb = STRBUF_INIT;
  @@ -538,7 +538,7 @@ static void init_submodule(const char *path, const char *prefix,
      if (git_config_set_gently(sb.buf, url))
        die(_("Failed to register url for submodule path '%s'"),
            displaypath);
  -		if (!(flags & OPT_QUIET))
  +		if (!quiet)
        fprintf(stderr,
          _("Submodule '%s' (%s) registered for path '%s'\n"),
          sub->name, url, displaypath);
  @@ -567,15 +567,39 @@ static void init_submodule(const char *path, const char *prefix,

  static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data)
  {
  -	struct init_cb *info = cb_data;
  -	init_submodule(list_item->name, info->prefix, info->flags);
  +	struct init_opts *info = cb_data;
  +	init_submodule(list_item->name, info->prefix, info->quiet);
  }

  -static int module_init(int argc, const char **argv, const char *prefix)
  +static int init_submodules(int argc, const char **argv, struct init_opts *opts)
  {
  -	struct init_cb info = INIT_CB_INIT;
  -	struct pathspec pathspec = { 0 };
    struct module_list list = MODULE_LIST_INIT;
  +	struct pathspec pathspec = { 0 };
  +	int ret = 0;
  +
  +	if (module_list_compute(argc, argv, opts->prefix,
  +				&pathspec, &list) < 0) {
  +		ret = 1;
  +		goto cleanup;
  +	}
  +
  +	/*
  +	 * If there are no path args and submodule.active is set then,
  +	 * by default, only initialize 'active' modules.
  +	 */
  +	if (!argc && git_config_get_value_multi("submodule.active"))
  +		module_list_active(&list);
  +
  +	for_each_listed_submodule(&list, init_submodule_cb, opts);
  +
  + cleanup:
  +	clear_pathspec(&pathspec);
  +	return ret;
  +}
  +
  +static int module_init(int argc, const char **argv, const char *prefix)
  +{
  +	struct init_opts info = INIT_CB_INIT;
    int quiet = 0;

    struct option module_init_options[] = {
  @@ -587,33 +611,14 @@ static int module_init(int argc, const char **argv, const char *prefix)
      N_("git submodule init [<options>] [<path>]"),
      NULL
    };
  -	int ret;

    argc = parse_options(argc, argv, prefix, module_init_options,
            git_submodule_helper_usage, 0);

  -	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
  -		ret = 1;
  -		goto cleanup;
  -	}
  -
  -	/*
  -	 * If there are no path args and submodule.active is set then,
  -	 * by default, only initialize 'active' modules.
  -	 */
  -	if (!argc && git_config_get_value_multi("submodule.active"))
  -		module_list_active(&list);
  -
    info.prefix = prefix;
  -	if (quiet)
  -		info.flags |= OPT_QUIET;
  -
  -	for_each_listed_submodule(&list, init_submodule_cb, &info);
  +	info.quiet = quiet;

  -	ret = 0;
  -cleanup:
  -	clear_pathspec(&pathspec);
  -	return ret;
  +	return init_submodules(argc, argv, &info);
  }

  struct status_cb {
  @@ -2688,27 +2693,13 @@ static int module_update(int argc, const char **argv, const char *prefix)
      opt.warn_if_uninitialized = 1;

    if (opt.init) {
  -		struct module_list list = MODULE_LIST_INIT;
  -		struct init_cb info = INIT_CB_INIT;
  -
  -		if (module_list_compute(argc, argv, opt.prefix,
  -					&pathspec, &list) < 0) {
  -			ret = 1;
  +		struct init_opts init_opts = {
  +			.quiet = opt.quiet,
  +			.prefix = opt.prefix,
  +		};
  +		ret = init_submodules(argc, argv, &init_opts);
  +		if (ret)
        goto cleanup;
  -		}
  -
  -		/*
  -		 * If there are no path args and submodule.active is set then,
  -		 * by default, only initialize 'active' modules.
  -		 */
  -		if (!argc && git_config_get_value_multi("submodule.active"))
  -			module_list_active(&list);
  -
  -		info.prefix = opt.prefix;
  -		if (opt.quiet)
  -			info.flags |= OPT_QUIET;
  -
  -		for_each_listed_submodule(&list, init_submodule_cb, &info);
    }

    ret = update_submodules(&opt);
----- >8 --------- >8 --------- >8 --------- >8 --------- >8 ----

I don't think we need to do this now; it's already pretty noisy for just
a "leak cleanup" series, and it isn't even complete (IIRC you mentioned
that we could probably drop init_submodule_cb(), but I can't find the
message any more).

Hopefully someone will pick this up. I doubt I'll have the time to do
it, but maybe.

> @@ -2715,6 +2716,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
>  cleanup:
>  	list_objects_filter_release(&filter_options);
>  	clear_pathspec(&pathspec);
> +	clear_pathspec(&pathspec2);
>  	return ret;
>  }
>  
> -- 
> 2.37.1.1062.g385eac7fccf

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v3 02/26] submodule--helper: stop conflating "sb" in clone_submodule()
  2022-07-21 19:12     ` [PATCH v3 02/26] submodule--helper: stop conflating "sb" in clone_submodule() Ævar Arnfjörð Bjarmason
@ 2022-07-21 21:16       ` Junio C Hamano
  2022-07-22 13:50         ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 186+ messages in thread
From: Junio C Hamano @ 2022-07-21 21:16 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> -static int clone_submodule(struct module_clone_data *clone_data)
> +static char *clone_submodule_sm_gitdir(const char *name)
>  {
> -	char *p, *sm_gitdir;
> -	char *sm_alternate = NULL, *error_strategy = NULL;
>  	struct strbuf sb = STRBUF_INIT;
> -	struct child_process cp = CHILD_PROCESS_INIT;
> +	char *sm_gitdir;
>  
> -	submodule_name_to_gitdir(&sb, the_repository, clone_data->name);
> +	submodule_name_to_gitdir(&sb, the_repository, name);
>  	sm_gitdir = absolute_pathdup(sb.buf);
> -	strbuf_reset(&sb);
> +	strbuf_release(&sb);
> +
> +	return sm_gitdir;
> +}

OK.

> +static int clone_submodule(struct module_clone_data *clone_data)
> +{
> +	char *p;
> +	char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);
> +	char *sm_alternate = NULL, *error_strategy = NULL;
> +	struct child_process cp = CHILD_PROCESS_INIT;
>  
>  	if (!is_absolute_path(clone_data->path)) {
> +		struct strbuf sb = STRBUF_INIT;
> +
>  		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
>  		clone_data->path = strbuf_detach(&sb, NULL);

This looks like a roundabout way to say xstrfmt().

>  	} else {
> @@ -1705,6 +1715,8 @@ static int clone_submodule(struct module_clone_data *clone_data)
>  			die(_("clone of '%s' into submodule path '%s' failed"),
>  			    clone_data->url, clone_data->path);
>  	} else {
> +		struct strbuf sb = STRBUF_INIT;
> +
>  		if (clone_data->require_init && !access(clone_data->path, X_OK) &&
>  		    !is_empty_dir(clone_data->path))
>  			die(_("directory not empty: '%s'"), clone_data->path);
> @@ -1712,7 +1724,7 @@ static int clone_submodule(struct module_clone_data *clone_data)
>  			die(_("could not create directory '%s'"), clone_data->path);
>  		strbuf_addf(&sb, "%s/index", sm_gitdir);
>  		unlink_or_warn(sb.buf);
> -		strbuf_reset(&sb);
> +		strbuf_release(&sb);

Ditto.

>  	}
>  
>  	connect_work_tree_and_git_dir(clone_data->path, sm_gitdir, 0);
> @@ -1734,7 +1746,6 @@ static int clone_submodule(struct module_clone_data *clone_data)
>  	free(sm_alternate);
>  	free(error_strategy);
>  
> -	strbuf_release(&sb);
>  	free(sm_gitdir);
>  	free(p);
>  	return 0;

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v3 03/26] submodule--helper: pass a "const struct module_clone_data" to clone_submodule()
  2022-07-21 19:12     ` [PATCH v3 03/26] submodule--helper: pass a "const struct module_clone_data" to clone_submodule() Ævar Arnfjörð Bjarmason
@ 2022-07-21 21:26       ` Junio C Hamano
  2022-07-25 17:07       ` Glen Choo
  1 sibling, 0 replies; 186+ messages in thread
From: Junio C Hamano @ 2022-07-21 21:26 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> -static int clone_submodule(struct module_clone_data *clone_data)
> +static int clone_submodule(const struct module_clone_data *clone_data,
> +			   struct string_list *reference)
>  {
>  	char *p;
>  	char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);
>  	char *sm_alternate = NULL, *error_strategy = NULL;
>  	struct child_process cp = CHILD_PROCESS_INIT;
> +	const char *clone_data_path;
>  
>  	if (!is_absolute_path(clone_data->path)) {
>  		struct strbuf sb = STRBUF_INIT;
>  
>  		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
> -		clone_data->path = strbuf_detach(&sb, NULL);
> +		clone_data_path = strbuf_detach(&sb, NULL);

OK.

>  	} else {
> -		clone_data->path = xstrdup(clone_data->path);
> +		clone_data_path = xstrdup(clone_data_path);

Is the variable we are duplicating by passing it to xstrdup() still
uninitialized at this point?  How could the compiler not catch this?

Apparently there is no test coverage on this codepath; all tests
pass when I replace this else clause with BUG().

Let's read on, pretending that you passed clone_data->path to
xstrdup(), for now.

> @@ -1674,7 +1676,7 @@ static int clone_submodule(struct module_clone_data *clone_data)
>  		if (safe_create_leading_directories_const(sm_gitdir) < 0)
>  			die(_("could not create directory '%s'"), sm_gitdir);
>  
> -		prepare_possible_alternates(clone_data->name, &clone_data->reference);
> +		prepare_possible_alternates(clone_data->name, reference);
>  
>  		strvec_push(&cp.args, "clone");
>  		strvec_push(&cp.args, "--no-checkout");
> @@ -1684,9 +1686,9 @@ static int clone_submodule(struct module_clone_data *clone_data)
>  			strvec_push(&cp.args, "--progress");
>  		if (clone_data->depth && *(clone_data->depth))
>  			strvec_pushl(&cp.args, "--depth", clone_data->depth, NULL);
> -		if (clone_data->reference.nr) {
> +		if (reference->nr) {
>  			struct string_list_item *item;
> -			for_each_string_list_item(item, &clone_data->reference)
> +			for_each_string_list_item(item, reference)
>  				strvec_pushl(&cp.args, "--reference",
>  					     item->string, NULL);
>  		}

All the uses of reference (both above, so below) looks OK.

I wonder if we can simply have a separate string list variable in
module_clone() and add_submodule() and remove clone_data.reference
member, which may make the end-result even cleaner, now that we pass
it as a separate parameter anyway.

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v3 04/26] submodule--helper: fix a leak in "clone_submodule"
  2022-07-21 19:13     ` [PATCH v3 04/26] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
@ 2022-07-21 21:30       ` Junio C Hamano
  2022-07-22 11:30         ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 186+ messages in thread
From: Junio C Hamano @ 2022-07-21 21:30 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 7d5ee6a6261..1ddce8e19c1 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -1658,14 +1658,15 @@ static int clone_submodule(const struct module_clone_data *clone_data,
>  	char *sm_alternate = NULL, *error_strategy = NULL;
>  	struct child_process cp = CHILD_PROCESS_INIT;
>  	const char *clone_data_path;
> +	char *to_free = NULL;
>  
>  	if (!is_absolute_path(clone_data->path)) {
>  		struct strbuf sb = STRBUF_INIT;
>  
>  		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
> -		clone_data_path = strbuf_detach(&sb, NULL);
> +		clone_data_path = to_free = strbuf_detach(&sb, NULL);
>  	} else {
> -		clone_data_path = xstrdup(clone_data_path);
> +		clone_data_path = clone_data->path;
>  	}

Heh, the bug I noticed in the previous step is silently fixed here.

This is why I do not trust a series that is artificially split into
steps and sent out without self reviewing or even compiling them.
Forces reviewers to do more work wasting their time reviewing and
finding bugs that ends up not mattering because that weren't even
something the author intended to write in the end.

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v3 06/26] submodule--helper: fix most "struct pathspec" memory leaks
  2022-07-21 19:13     ` [PATCH v3 06/26] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
@ 2022-07-21 21:37       ` Junio C Hamano
  0 siblings, 0 replies; 186+ messages in thread
From: Junio C Hamano @ 2022-07-21 21:37 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> Call clear_pathspec() at the end of various functions that work with
> and allocate a "struct pathspec".
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 84 +++++++++++++++++++++++++------------
>  1 file changed, 58 insertions(+), 26 deletions(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 73ac1fcbb9c..a60dc6af178 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -269,7 +269,7 @@ static char *get_up_path(const char *path)
>  static int module_list(int argc, const char **argv, const char *prefix)
>  {
>  	int i;
> -	struct pathspec pathspec;
> +	struct pathspec pathspec = { 0 };
>  	struct module_list list = MODULE_LIST_INIT;
>  
>  	struct option module_list_options[] = {
> @@ -278,6 +278,7 @@ static int module_list(int argc, const char **argv, const char *prefix)
>  			   N_("alternative anchor for relative paths")),
>  		OPT_END()
>  	};
> +	int ret = 1;

Move this way above, perhaps next to where "int i;" is declared, or
replace the blank before where module_list_options[] is declared
with this line.

The same comment applies to changes to module_foreach() and
status_submodule_cb() in this patch.  The change to
absorb_git_dirs() gets it right.

Thanks.

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v3 17/26] submodule--helper: fix obscure leak in module_add()
  2022-07-21 19:13     ` [PATCH v3 17/26] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
@ 2022-07-21 21:45       ` Junio C Hamano
  0 siblings, 0 replies; 186+ messages in thread
From: Junio C Hamano @ 2022-07-21 21:45 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> Fix an obscure leak in module_add()< if the "git add" command we were

"<" -> "," perhaps?  I dunno.

> piping to failed we'd fail to strbuf_release(&sb). This fixes a leak
> introduced in a6226fd772b (submodule--helper: convert the bulk of
> cmd_add() to C, 2021-08-10).
>
> In fixing it move to a "goto cleanup" pattern, and since we need to
> introduce a "ret" variable to do that let's also get rid of the
> intermediate "exit_code" variable. The initialization to "-1" in
> a6226fd772b has always been redundant, we'd only use the "exit_code"
> value after assigning the return value of pipe_command() to it.
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 22 +++++++++++-----------
>  1 file changed, 11 insertions(+), 11 deletions(-)

The patch looks good, especially with removal of "exit_code" it is
very clear what is going on and how errors are handled.

Nicely done.

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v3 26/26] submodule--helper: fix a configure_added_submodule() leak
  2022-07-21 19:13     ` [PATCH v3 26/26] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
@ 2022-07-21 21:51       ` Junio C Hamano
  0 siblings, 0 replies; 186+ messages in thread
From: Junio C Hamano @ 2022-07-21 21:51 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

>  static void configure_added_submodule(struct add_data *add_data)
>  {
>  	char *key;
> -	char *val = NULL;
> +	const char *val;
>  	struct child_process add_submod = CHILD_PROCESS_INIT;
>  	struct child_process add_gitmodules = CHILD_PROCESS_INIT;
>  
> @@ -3306,7 +3306,7 @@ static void configure_added_submodule(struct add_data *add_data)
>  	 * is_submodule_active(), since that function needs to find
>  	 * out the value of "submodule.active" again anyway.
>  	 */
> -	if (!git_config_get_string("submodule.active", &val)) {
> +	if (!git_config_get_string_tmp("submodule.active", &val)) {

Obviously correct ;-)

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v3 04/26] submodule--helper: fix a leak in "clone_submodule"
  2022-07-21 21:30       ` Junio C Hamano
@ 2022-07-22 11:30         ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-22 11:30 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan


On Thu, Jul 21 2022, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>
>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
>> index 7d5ee6a6261..1ddce8e19c1 100644
>> --- a/builtin/submodule--helper.c
>> +++ b/builtin/submodule--helper.c
>> @@ -1658,14 +1658,15 @@ static int clone_submodule(const struct module_clone_data *clone_data,
>>  	char *sm_alternate = NULL, *error_strategy = NULL;
>>  	struct child_process cp = CHILD_PROCESS_INIT;
>>  	const char *clone_data_path;
>> +	char *to_free = NULL;
>>  
>>  	if (!is_absolute_path(clone_data->path)) {
>>  		struct strbuf sb = STRBUF_INIT;
>>  
>>  		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
>> -		clone_data_path = strbuf_detach(&sb, NULL);
>> +		clone_data_path = to_free = strbuf_detach(&sb, NULL);
>>  	} else {
>> -		clone_data_path = xstrdup(clone_data_path);
>> +		clone_data_path = clone_data->path;
>>  	}
>
> Heh, the bug I noticed in the previous step is silently fixed here.
>
> This is why I do not trust a series that is artificially split into
> steps and sent out without self reviewing or even compiling them.

Sorry for that mistake, will fix it in a re-roll. It was a result of
dumb search-replacing.

But FWIW I did (and do generally) test my changes with "git rebase -i -x
make..." before sending them out.

But as you note this doesn't seem to have test coverage, and which gcc
yells at me about this, clang (13.0.1-3+b2) does not.

I generally test my changes with both gcc & clang before sending them
out, but usually only use clang for the incremental compiling, I'll
switch back to gcc...

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v3 02/26] submodule--helper: stop conflating "sb" in clone_submodule()
  2022-07-21 21:16       ` Junio C Hamano
@ 2022-07-22 13:50         ` Ævar Arnfjörð Bjarmason
  2022-07-22 16:48           ` Glen Choo
  2022-07-22 18:47           ` Junio C Hamano
  0 siblings, 2 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-22 13:50 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan


On Thu, Jul 21 2022, Junio C Hamano wrote:

>> +static int clone_submodule(struct module_clone_data *clone_data)
>> +{
>> +	char *p;
>> +	char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);
>> +	char *sm_alternate = NULL, *error_strategy = NULL;
>> +	struct child_process cp = CHILD_PROCESS_INIT;
>>  
>>  	if (!is_absolute_path(clone_data->path)) {
>> +		struct strbuf sb = STRBUF_INIT;
>> +
>>  		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
>>  		clone_data->path = strbuf_detach(&sb, NULL);
>
> This looks like a roundabout way to say xstrfmt().

Yes, I can fix this and others while I'm at it, but a lot of things like
that in this code are funny uses of APIs that we could improve.

I think it's probably best to just leave these for now.

But I also don't mind adding another commit to this already large series
to search/replace the relevant strbuf_detach() with xstrfmt()....

Just let me know...

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v3 02/26] submodule--helper: stop conflating "sb" in clone_submodule()
  2022-07-22 13:50         ` Ævar Arnfjörð Bjarmason
@ 2022-07-22 16:48           ` Glen Choo
  2022-07-22 18:47           ` Junio C Hamano
  1 sibling, 0 replies; 186+ messages in thread
From: Glen Choo @ 2022-07-22 16:48 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, Junio C Hamano
  Cc: git, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> On Thu, Jul 21 2022, Junio C Hamano wrote:
>
>>> +static int clone_submodule(struct module_clone_data *clone_data)
>>> +{
>>> +	char *p;
>>> +	char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);
>>> +	char *sm_alternate = NULL, *error_strategy = NULL;
>>> +	struct child_process cp = CHILD_PROCESS_INIT;
>>>  
>>>  	if (!is_absolute_path(clone_data->path)) {
>>> +		struct strbuf sb = STRBUF_INIT;
>>> +
>>>  		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
>>>  		clone_data->path = strbuf_detach(&sb, NULL);
>>
>> This looks like a roundabout way to say xstrfmt().
>
> Yes, I can fix this and others while I'm at it, but a lot of things like
> that in this code are funny uses of APIs that we could improve.
>
> I think it's probably best to just leave these for now.

Yes, this series is already pretty long. For reviewability, perhaps we
could to keep this one focused on "leaks fixes" and leave the style
fixes and refactoring for another series (which might include things
like [1]). As a bonus, with this series in place, we'll know that our
refactoring won't introduce more leaks :)

[1] https://lore.kernel.org/git/kl6lbktitf6e.fsf@chooglen-macbookpro.roam.corp.google.com

> But I also don't mind adding another commit to this already large series
> to search/replace the relevant strbuf_detach() with xstrfmt()....
>
> Just let me know...

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v3 02/26] submodule--helper: stop conflating "sb" in clone_submodule()
  2022-07-22 13:50         ` Ævar Arnfjörð Bjarmason
  2022-07-22 16:48           ` Glen Choo
@ 2022-07-22 18:47           ` Junio C Hamano
  2022-07-28 16:30             ` Ævar Arnfjörð Bjarmason
  1 sibling, 1 reply; 186+ messages in thread
From: Junio C Hamano @ 2022-07-22 18:47 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

>> This looks like a roundabout way to say xstrfmt().
>
> Yes, I can fix this and others while I'm at it, but a lot of things like
> that in this code are funny uses of APIs that we could improve.
>
> I think it's probably best to just leave these for now.

Agreed.  We could instead have a separate series to fix API usage
before these and then build leak-plugging on top, or the other way
around, and in general "clean then plug" would make it easier to
review the plugging patches (simply because it would be working on
clean code, not code that misuses the API in strange ways), but it
is too late now.  Lets make sure we do not forget to revisit the API
misuse but lets avoid mixing it into the series.

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v3 03/26] submodule--helper: pass a "const struct module_clone_data" to clone_submodule()
  2022-07-21 19:12     ` [PATCH v3 03/26] submodule--helper: pass a "const struct module_clone_data" to clone_submodule() Ævar Arnfjörð Bjarmason
  2022-07-21 21:26       ` Junio C Hamano
@ 2022-07-25 17:07       ` Glen Choo
  1 sibling, 0 replies; 186+ messages in thread
From: Glen Choo @ 2022-07-25 17:07 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> Add "const" to the "struct module_clone_data" that we pass to
> clone_submodule(), which makes the ownership clear, and stops us from
> clobbering the "clone_data->path".
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 38 +++++++++++++++++++------------------
>  1 file changed, 20 insertions(+), 18 deletions(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 29641690c8c..7d5ee6a6261 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -1650,20 +1650,22 @@ static char *clone_submodule_sm_gitdir(const char *name)
>  	return sm_gitdir;
>  }
>  
> -static int clone_submodule(struct module_clone_data *clone_data)
> +static int clone_submodule(const struct module_clone_data *clone_data,
> +			   struct string_list *reference)
>  {
>  	char *p;
>  	char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);
>  	char *sm_alternate = NULL, *error_strategy = NULL;
>  	struct child_process cp = CHILD_PROCESS_INIT;
> +	const char *clone_data_path;
>  
>  	if (!is_absolute_path(clone_data->path)) {
>  		struct strbuf sb = STRBUF_INIT;
>  
>  		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
> -		clone_data->path = strbuf_detach(&sb, NULL);
> +		clone_data_path = strbuf_detach(&sb, NULL);
>  	} else {
> -		clone_data->path = xstrdup(clone_data->path);
> +		clone_data_path = xstrdup(clone_data_path);
>  	}
>  
>  	if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)

Hm, the leak in the preimage comes from the fact that we assign back to
clone_data->path, which is a "const char *" that initially comes from
argv. So we didn't free() it even though it always pointing to
free()-able memory past this point.

So now that we're introducing and assigning to a new variable,
clone_data_path, wouldn't it be simpler to just make it "char *" and
free it (instead of adding a separate "char *to_free" in the next
patch)?

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v3 11/26] submodule--helper: refactor "errmsg_str" to be a "struct strbuf"
  2022-07-21 19:13     ` [PATCH v3 11/26] submodule--helper: refactor "errmsg_str" to be a "struct strbuf" Ævar Arnfjörð Bjarmason
@ 2022-07-25 23:15       ` Glen Choo
  0 siblings, 0 replies; 186+ messages in thread
From: Glen Choo @ 2022-07-25 23:15 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> From: Glen Choo <chooglen@google.com>
>
> Refactor code added in e83e3333b57 (submodule: port submodule
> subcommand 'summary' from shell to C, 2020-08-13) so that "errmsg" and
> "errmsg_str" are folded into one. The distinction between the empty
> string and NULL is something that's tested for by
> e.g. "t/t7401-submodule-summary.sh".
>
> This is in preparation for fixing a memory leak the "struct strbuf" in
> the pre-image.
>
> Let's also pass a "const char *" to print_submodule_summary(), as it
> should not be modifying the "errmsg".
>
> Signed-off-by: Glen Choo <chooglen@google.com>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 14 ++++++--------
>  1 file changed, 6 insertions(+), 8 deletions(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 21b3abb7b40..f794d2b588b 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -866,7 +866,7 @@ static char *verify_submodule_committish(const char *sm_path,
>  	return strbuf_detach(&result, NULL);
>  }
>  
> -static void print_submodule_summary(struct summary_cb *info, char *errmsg,
> +static void print_submodule_summary(struct summary_cb *info, const char *errmsg,
>  				    int total_commits, const char *displaypath,
>  				    const char *src_abbrev, const char *dst_abbrev,
>  				    struct module_cb *p)
> @@ -924,7 +924,7 @@ static void generate_submodule_summary(struct summary_cb *info,
>  {
>  	char *displaypath, *src_abbrev = NULL, *dst_abbrev;
>  	int missing_src = 0, missing_dst = 0;
> -	char *errmsg = NULL;
> +	struct strbuf errmsg = STRBUF_INIT;
>  	int total_commits = -1;
>  
>  	if (!info->cached && oideq(&p->oid_dst, null_oid())) {
> @@ -1024,23 +1024,21 @@ static void generate_submodule_summary(struct summary_cb *info,
>  		 * submodule, i.e., deleted or changed to blob
>  		 */
>  		if (S_ISGITLINK(p->mod_dst)) {
> -			struct strbuf errmsg_str = STRBUF_INIT;
>  			if (missing_src && missing_dst) {
> -				strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commits %s and %s\n",
> +				strbuf_addf(&errmsg, "  Warn: %s doesn't contain commits %s and %s\n",
>  					    displaypath, oid_to_hex(&p->oid_src),
>  					    oid_to_hex(&p->oid_dst));
>  			} else {
> -				strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commit %s\n",
> +				strbuf_addf(&errmsg, "  Warn: %s doesn't contain commit %s\n",
>  					    displaypath, missing_src ?
>  					    oid_to_hex(&p->oid_src) :
>  					    oid_to_hex(&p->oid_dst));
>  			}
> -			errmsg = strbuf_detach(&errmsg_str, NULL);
>  		}
>  	}
>  
> -	print_submodule_summary(info, errmsg, total_commits,
> -				displaypath, src_abbrev,
> +	print_submodule_summary(info, errmsg.len ? errmsg.buf : NULL,
> +				total_commits, displaypath, src_abbrev,
>  				dst_abbrev, p);
>  
>  	free(displaypath);

Ah, this is mostly the same as what I sent out, but with the length
check in the same function (instead of in print_submodule_summary()).
This makes it easier to tell that the caller is still doing the same
thing.

Looks good.

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v3 23/26] submodule--helper: don't exit() on failure, return
  2022-07-21 19:13     ` [PATCH v3 23/26] submodule--helper: don't exit() on failure, return Ævar Arnfjörð Bjarmason
@ 2022-07-25 23:57       ` Glen Choo
  0 siblings, 0 replies; 186+ messages in thread
From: Glen Choo @ 2022-07-25 23:57 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> Change code downstream of module_update() to short-circuit and return
> to the top-level on failure, rather than calling exit().
>
> To do so we need to diligently check whether we "must_die_on_failure",
> which is a pattern started in c51f8f94e5b (submodule--helper: run
> update procedures from C, 2021-08-24), but which hadn't been completed
> to the point where we could avoid calling exit() here.
>
> This introduces no functional changes, but makes it easier to both
> call these routines as a library in the future, and to avoid leaking
> memory.
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 36 ++++++++++++++++++++++++++----------
>  1 file changed, 26 insertions(+), 10 deletions(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 790f0ccb82e..b65665105e7 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -2283,7 +2283,8 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str
>  	return run_command(&cp);
>  }
>  
> -static int run_update_command(struct update_data *ud, int subforce)
> +static int run_update_command(struct update_data *ud, int subforce,
> +			      int *must_die_on_failurep)

It's not obvious in the context lines, but there is an auto variable
named "must_die_on_failure", so we need the "p".

>  {
>  	struct child_process cp = CHILD_PROCESS_INIT;
>  	char *oid = oid_to_hex(&ud->oid);
> @@ -2345,8 +2346,10 @@ static int run_update_command(struct update_data *ud, int subforce)
>  			BUG("unexpected update strategy type: %s",
>  			    submodule_strategy_to_string(&ud->update_strategy));
>  		}
> -		if (must_die_on_failure)
> -			exit(128);
> +		if (must_die_on_failure) {
> +			*must_die_on_failurep = 1;
> +			return 128;
> +		}

[...]

>  
>  		/* the command failed, but update must continue */
>  		return 1;
> @@ -2380,7 +2383,8 @@ static int run_update_command(struct update_data *ud, int subforce)
>  	return 0;
>  }
>  
> -static int run_update_procedure(struct update_data *ud)
> +static int run_update_procedure(struct update_data *ud,
> +				int *must_die_on_failure)
>  {
>  	int subforce = is_null_oid(&ud->suboid) || ud->force;
>  
> @@ -2407,7 +2411,7 @@ static int run_update_procedure(struct update_data *ud)
>  			    ud->displaypath, oid_to_hex(&ud->oid));
>  	}
>  
> -	return run_update_command(ud, subforce);
> +	return run_update_command(ud, subforce, must_die_on_failure);
>  }
>  
>  static const char *remote_submodule_branch(const char *path)
> @@ -2543,7 +2547,8 @@ static void update_data_to_args(struct update_data *update_data, struct strvec *
>  				    "--no-single-branch");
>  }
>  
> -static int update_submodule(struct update_data *update_data)
> +static int update_submodule(struct update_data *update_data,
> +			    int *must_die_on_failure)
>  {
>  	int ret = 1;
>  
> @@ -2584,8 +2589,13 @@ static int update_submodule(struct update_data *update_data)
>  	}
>  
>  	if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) {
> -		if (run_update_procedure(update_data))
> +		ret = run_update_procedure(update_data, must_die_on_failure);
> +		if (ret && *must_die_on_failure) {
> +			goto cleanup;
> +		} else if (ret) {
> +			ret = 1;
>  			goto cleanup;
> +		}
>  	}
>  
>  	if (update_data->recursive) {
> @@ -2608,7 +2618,8 @@ static int update_submodule(struct update_data *update_data)
>  		die_message(_("Failed to recurse into submodule path '%s'"),
>  			    update_data->displaypath);
>  		if (ret == 128) {
> -			exit(ret);
> +			*must_die_on_failure = 1;
> +			goto cleanup;

One important property in the preimage is that there's an obvious
connection between the exit(128) and this section here, i.e. the child
"git submodule update" process failed in a way that the parent needs to
stop immediately.

With this patch, this property is no longer obvious because we return
128 from the lowest level (run_update_command()). By the time we reach
the top level (module_update()), it's no longer clear that the return
value was meant to be 128.

Two ways we can fix this:

1) Just return 128 at all levels to mean "must die on failure", which
   will let us get rid of "must_die_on_failure".

or...

>  		} else if (ret) {
>  			ret = 1;
>  			goto cleanup;
> @@ -2646,13 +2657,18 @@ static int update_submodules(struct update_data *update_data)
>  
>  	for (i = 0; i < suc.update_clone_nr; i++) {
>  		struct update_clone_data ucd = suc.update_clone[i];
> +		int code;
> +		int must_die_on_failure = 0;
>  
>  		oidcpy(&update_data->oid, &ucd.oid);
>  		update_data->just_cloned = ucd.just_cloned;
>  		update_data->sm_path = ucd.sub->path;
>  
> -		if (update_submodule(update_data))
> -			res = 1;
> +		code = update_submodule(update_data, &must_die_on_failure);
> +		if (code)
> +			res = code;
> +		if (must_die_on_failure)
> +			goto cleanup;
>  	}

2) In update_submodules() (i.e. just before module_update()), we make 
   update_submodules() return 128 if must_die_on_failure != 0. Then we
   can drop the return value of 128 from the rest of the call chain and
   just use an opaque nonzero return value instead.

>  
>  cleanup:
> -- 
> 2.37.1.1095.g0bd6f54ba8a



^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v3 24/26] submodule--helper: free rest of "displaypath" in "struct update_data"
  2022-07-21 19:13     ` [PATCH v3 24/26] submodule--helper: free rest of "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
@ 2022-07-26  1:06       ` Glen Choo
  0 siblings, 0 replies; 186+ messages in thread
From: Glen Choo @ 2022-07-26  1:06 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> Fix a leak in code added in c51f8f94e5b (submodule--helper: run update
> procedures from C, 2021-08-24), we clobber the "displaypath" member of
> the passed-in "struct update_data" both so that die() messages in this
> update_submodule() function itself can use it, and for the
> run_update_procedure() called within this function.
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 6 +++++-
>  1 file changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index b65665105e7..4e70a74357c 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -2551,10 +2551,11 @@ static int update_submodule(struct update_data *update_data,
>  			    int *must_die_on_failure)
>  {
>  	int ret = 1;
> +	char *to_free, *restore = update_data->displaypath;
>  
>  	ensure_core_worktree(update_data->sm_path);
>  
> -	update_data->displaypath = get_submodule_displaypath(
> +	update_data->displaypath = to_free = get_submodule_displaypath(
>  		update_data->sm_path, update_data->prefix);
>
>  	determine_submodule_update_strategy(the_repository, update_data->just_cloned,
> @@ -2628,6 +2629,9 @@ static int update_submodule(struct update_data *update_data,
>  
>  	ret = 0;
>  cleanup:
> +	free(to_free);
> +	update_data->displaypath = restore;
> +
>  	return ret;
>  }

I'm not sure why we need to have "restore". We set
"update_data->displaypath" so that we can use it inside this function
(and its call chain), but we don't care about it outside of this call
chain at all.

If the goal is to avoid exposing a free()-d pointer, could we just do

   FREE_AND_NULL(update_data->displaypath);

instead?

>  
> -- 
> 2.37.1.1095.g0bd6f54ba8a

^ permalink raw reply	[flat|nested] 186+ messages in thread

* [PATCH v4 00/17] submodule--helper: (only) fix memory leaks
  2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                       ` (25 preceding siblings ...)
  2022-07-21 19:13     ` [PATCH v3 26/26] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:29     ` Ævar Arnfjörð Bjarmason
  2022-07-28 16:29       ` [PATCH v4 01/17] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
                         ` (17 more replies)
  26 siblings, 18 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:29 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

This series fixes all of the memory leaks in
builtin/submodule--helper.c that our test suite spots, except for
those where the leak is downstream of cmd_submodule__helper() and
caused by code that's not in builtin/submodule--helper.c (e.g. leaks
in the object or config APIs, and in submodule.c).

For the v3 see [1]. The v3 was 26 patches, but this one's 17. The
reason for the reduction is that this is now based on a just-submitted
"prep" series[2].

The sum total is much larger now, but hopefully easier to digest, much
of the new "prep" series is either trivial patches (like whitespace
fixes), or deletion or moving (into test-tool) of code that this
"leak" series doesn't need to deal with anymore.

I think this should address all outstanding comments on the v3. In the
case of Glen's comment at [3] I dealt with my updating a commit
message in the "prep" series, see the last two paragraphs of [4] (also
shown in the range-diff of [2]).

1. https://lore.kernel.org/git/cover-v3-00.26-00000000000-20220721T191249Z-avarab@gmail.com/
2. https://lore.kernel.org/git/cover-00.20-00000000000-20220728T161116Z-avarab@gmail.com/
3. https://lore.kernel.org/git/kl6ltu74sq3b.fsf@chooglen-macbookpro.roam.corp.google.com/
4. https://lore.kernel.org/git/patch-19.20-5a26c9428be-20220728T161116Z-avarab@gmail.com/

Ævar Arnfjörð Bjarmason (17):
  submodule--helper: fix a leak in "clone_submodule"
  submodule--helper: fix trivial get_default_remote_submodule() leak
  submodule--helper: fix most "struct pathspec" memory leaks
  submodule--helper: "struct pathspec" memory leak in module_update()
  submodule--helper: don't leak {run,capture}_command() cp.dir argument
  submodule--helper: add and use *_release() functions
  submodule--helper: fix "errmsg_str" memory leak
  submodule--helper: fix "sm_path" and other "module_cb_list" leaks
  submodule--helper: fix a leak with repo_clear()
  submodule--helper: fix a memory leak in get_default_remote_submodule()
  submodule--helper: fix "reference" leak
  submodule--helper: fix obscure leak in module_add()
  submodule--helper: fix a leak in module_add()
  submodule--helper: fix a memory leak in print_status()
  submodule--helper: free some "displaypath" in "struct update_data"
  submodule--helper: free rest of "displaypath" in "struct update_data"
  submodule--helper: fix a configure_added_submodule() leak

 builtin/submodule--helper.c        | 218 +++++++++++++++++++++--------
 t/t1500-rev-parse.sh               |   1 +
 t/t2403-worktree-move.sh           |   1 +
 t/t6008-rev-list-submodule.sh      |   1 +
 t/t6134-pathspec-in-submodule.sh   |   1 +
 t/t7412-submodule-absorbgitdirs.sh |   1 +
 t/t7413-submodule-is-active.sh     |   1 +
 t/t7414-submodule-mistakes.sh      |   2 +
 t/t7506-status-submodule.sh        |   1 +
 t/t7507-commit-verbose.sh          |   2 +
 10 files changed, 171 insertions(+), 58 deletions(-)

Range-diff against v3:
 1:  3c7ae3e0222 <  -:  ----------- submodule--helper: replace memset() with { 0 }-initialization
 2:  32e4ae7ead5 <  -:  ----------- submodule--helper: stop conflating "sb" in clone_submodule()
 3:  eee6ca28c6f <  -:  ----------- submodule--helper: pass a "const struct module_clone_data" to clone_submodule()
 4:  26f0caf0386 !  1:  aac987a414a submodule--helper: fix a leak in "clone_submodule"
    @@ Commit message
         on absolute paths, 2016-03-31).
     
         For the "else" case we don't need to xstrdup() the "clone_data->path",
    -    and we don't need to free our own "clone_data_path".
    +    and we don't need to free our own "clone_data_path". We can therefore
    +    assign the "clone_data->path" to our own "clone_data_path" right away,
    +    and only override it (and remember to free it!) if we need to
    +    xstrfmt() a replacement.
     
         In the case of the module_clone() caller it's from "argv", and doesn't
         need to be free'd, and in the case of the add_submodule() caller we
    @@ Commit message
     
      ## builtin/submodule--helper.c ##
     @@ builtin/submodule--helper.c: static int clone_submodule(const struct module_clone_data *clone_data,
    + 	char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);
      	char *sm_alternate = NULL, *error_strategy = NULL;
      	struct child_process cp = CHILD_PROCESS_INIT;
    - 	const char *clone_data_path;
    +-	const char *clone_data_path;
    ++	const char *clone_data_path = clone_data->path;
     +	char *to_free = NULL;
      
    - 	if (!is_absolute_path(clone_data->path)) {
    - 		struct strbuf sb = STRBUF_INIT;
    - 
    - 		strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
    --		clone_data_path = strbuf_detach(&sb, NULL);
    -+		clone_data_path = to_free = strbuf_detach(&sb, NULL);
    - 	} else {
    --		clone_data_path = xstrdup(clone_data_path);
    -+		clone_data_path = clone_data->path;
    - 	}
    + 	if (!is_absolute_path(clone_data->path))
    +-		clone_data_path = xstrfmt("%s/%s", get_git_work_tree(),
    +-					  clone_data->path);
    +-	else
    +-		clone_data_path = xstrdup(clone_data->path);
    ++		clone_data_path = to_free = xstrfmt("%s/%s", get_git_work_tree(),
    ++						    clone_data->path);
      
      	if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)
    + 		die(_("refusing to create/use '%s' in another submodule's "
     @@ builtin/submodule--helper.c: static int clone_submodule(const struct module_clone_data *clone_data,
      
      	free(sm_gitdir);
 5:  75775bf4f6c !  2:  390c5174e17 submodule--helper: fix trivial get_default_remote_submodule() leak
    @@ Commit message
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/submodule--helper.c ##
    -@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)
    +@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data,
      		const char *branch = remote_submodule_branch(update_data->sm_path);
      		char *remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch);
      
 6:  7672ef1305f !  3:  529e10233d5 submodule--helper: fix most "struct pathspec" memory leaks
    @@ Commit message
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/submodule--helper.c ##
    -@@ builtin/submodule--helper.c: static char *get_up_path(const char *path)
    - static int module_list(int argc, const char **argv, const char *prefix)
    - {
    - 	int i;
    --	struct pathspec pathspec;
    -+	struct pathspec pathspec = { 0 };
    - 	struct module_list list = MODULE_LIST_INIT;
    - 
    - 	struct option module_list_options[] = {
    -@@ builtin/submodule--helper.c: static int module_list(int argc, const char **argv, const char *prefix)
    - 			   N_("alternative anchor for relative paths")),
    - 		OPT_END()
    - 	};
    -+	int ret = 1;
    - 
    - 	const char *const git_submodule_helper_usage[] = {
    - 		N_("git submodule--helper list [--prefix=<path>] [<path>...]"),
    -@@ builtin/submodule--helper.c: static int module_list(int argc, const char **argv, const char *prefix)
    - 			     git_submodule_helper_usage, 0);
    - 
    - 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
    --		return 1;
    -+		goto cleanup;
    - 
    - 	for (i = 0; i < list.nr; i++) {
    - 		const struct cache_entry *ce = list.entries[i];
    -@@ builtin/submodule--helper.c: static int module_list(int argc, const char **argv, const char *prefix)
    - 
    - 		fprintf(stdout, "%s\n", ce->name);
    - 	}
    --	return 0;
    -+	ret = 0;
    -+cleanup:
    -+	clear_pathspec(&pathspec);
    -+	return ret;
    - }
    - 
    - static void for_each_listed_submodule(const struct module_list *list,
     @@ builtin/submodule--helper.c: static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
      static int module_foreach(int argc, const char **argv, const char *prefix)
      {
    @@ builtin/submodule--helper.c: static void runcommand_in_submodule_cb(const struct
     -	struct pathspec pathspec;
     +	struct pathspec pathspec = { 0 };
      	struct module_list list = MODULE_LIST_INIT;
    - 
      	struct option module_foreach_options[] = {
    + 		OPT__QUIET(&info.quiet, N_("suppress output of entering each submodule command")),
     @@ builtin/submodule--helper.c: static int module_foreach(int argc, const char **argv, const char *prefix)
      		N_("git submodule foreach [--quiet] [--recursive] [--] <command>"),
      		NULL
    @@ builtin/submodule--helper.c: static void init_submodule_cb(const struct cache_en
     +	struct pathspec pathspec = { 0 };
      	struct module_list list = MODULE_LIST_INIT;
      	int quiet = 0;
    - 
    + 	struct option module_init_options[] = {
     @@ builtin/submodule--helper.c: static int module_init(int argc, const char **argv, const char *prefix)
      		N_("git submodule init [<options>] [<path>]"),
      		NULL
    @@ builtin/submodule--helper.c: static void status_submodule_cb(const struct cache_
     +	struct pathspec pathspec = { 0 };
      	struct module_list list = MODULE_LIST_INIT;
      	int quiet = 0;
    - 
    + 	struct option module_status_options[] = {
     @@ builtin/submodule--helper.c: static int module_status(int argc, const char **argv, const char *prefix)
      		N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"),
      		NULL
    @@ builtin/submodule--helper.c: static int module_status(int argc, const char **arg
     +	return ret;
      }
      
    - static int module_name(int argc, const char **argv, const char *prefix)
    + struct module_cb {
     @@ builtin/submodule--helper.c: static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data
      static int module_sync(int argc, const char **argv, const char *prefix)
      {
    @@ builtin/submodule--helper.c: static void sync_submodule_cb(const struct cache_en
      	struct module_list list = MODULE_LIST_INIT;
      	int quiet = 0;
      	int recursive = 0;
    +@@ builtin/submodule--helper.c: static int module_sync(int argc, const char **argv, const char *prefix)
    + 		N_("git submodule sync [--quiet] [--recursive] [<path>]"),
    + 		NULL
    + 	};
     +	int ret = 1;
      
    - 	struct option module_sync_options[] = {
    - 		OPT__QUIET(&quiet, N_("suppress output of synchronizing submodule url")),
    -@@ builtin/submodule--helper.c: static int module_sync(int argc, const char **argv, const char *prefix)
    + 	argc = parse_options(argc, argv, prefix, module_sync_options,
      			     git_submodule_helper_usage, 0);
      
      	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
    @@ builtin/submodule--helper.c: static int push_check(int argc, const char **argv,
     +	struct pathspec pathspec = { 0 };
      	struct module_list list = MODULE_LIST_INIT;
      	unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;
    -+	int ret = 1;
    - 
      	struct option embed_gitdir_options[] = {
    - 		OPT_STRING(0, "prefix", &prefix,
     @@ builtin/submodule--helper.c: static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
    + 		N_("git submodule absorbgitdirs [<options>] [<path>...]"),
    + 		NULL
    + 	};
    ++	int ret = 1;
    + 
    + 	argc = parse_options(argc, argv, prefix, embed_gitdir_options,
      			     git_submodule_helper_usage, 0);
      
      	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
    @@ builtin/submodule--helper.c: static int absorb_git_dirs(int argc, const char **a
     +	return ret;
      }
      
    - static int is_active(int argc, const char **argv, const char *prefix)
    + static int module_config(int argc, const char **argv, const char *prefix)
 7:  325aa1521e2 =  4:  683d327752f submodule--helper: "struct pathspec" memory leak in module_update()
 8:  424b24961b5 !  5:  4e8e9197539 submodule--helper: don't leak {run,capture}_command() cp.dir argument
    @@ builtin/submodule--helper.c: static int fetch_in_submodule(const char *module_pa
      
      	strvec_push(&cp.args, "fetch");
      	if (quiet)
    -@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)
    +@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce,
      	}
      	strvec_push(&cp.args, oid);
      
 9:  abf5c4754a4 <  -:  ----------- submodule--helper: add "const" to copy of "update_data"
10:  6ab5aabae35 !  6:  575d3e8d2e2 submodule--helper: add and use *_release() functions
    @@ builtin/submodule--helper.c: static void module_list_active(struct module_list *
      	*list = active_modules;
      }
      
    -@@ builtin/submodule--helper.c: static int module_list(int argc, const char **argv, const char *prefix)
    - 	}
    - 	ret = 0;
    - cleanup:
    -+	module_list_release(&list);
    - 	clear_pathspec(&pathspec);
    - 	return ret;
    - }
     @@ builtin/submodule--helper.c: static int module_foreach(int argc, const char **argv, const char *prefix)
      
      	ret = 0;
11:  feec1f20bf9 <  -:  ----------- submodule--helper: refactor "errmsg_str" to be a "struct strbuf"
12:  d368db73de7 =  7:  d4c81e04254 submodule--helper: fix "errmsg_str" memory leak
13:  5be941b3d1b =  8:  ef9e29d5bfe submodule--helper: fix "sm_path" and other "module_cb_list" leaks
14:  b8560e8c111 =  9:  0798a00c9ef submodule--helper: fix a leak with repo_clear()
15:  abfd61f9f05 = 10:  dae2a6f8e07 submodule--helper: fix a memory leak in get_default_remote_submodule()
16:  b8df96a9cf0 ! 11:  e7352bb8cfa submodule--helper: fix "reference" leak is "module_clone_data"
    @@ Metadata
     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## Commit message ##
    -    submodule--helper: fix "reference" leak is "module_clone_data"
    +    submodule--helper: fix "reference" leak
     
    -    Fix leaks in the "reference" member of "struct module_clone_data" that
    -    have been with us since 31224cbdc72 (clone: recursive and reference
    -    option triggers submodule alternates, 2016-08-17) and
    -    8c8195e9c3e (submodule--helper: introduce add-clone subcommand,
    -    2021-07-10).
    +    Fix leaks in the "reference" variable declared in add_submodule() and
    +    module_clone().
    +
    +    In preceding commits this variable was refactored out of the "struct
    +    module_clone_data", but the leak has been with us since
    +    31224cbdc72 (clone: recursive and reference option triggers submodule
    +    alternates, 2016-08-17) and 8c8195e9c3e (submodule--helper: introduce
    +    add-clone subcommand, 2021-07-10).
     
         Those commits added an xstrdup()'d member of the
         STRING_LIST_INIT_NODUP'd "struct string_list". We need to free()
    @@ Commit message
         member, if it has a pointer it's the pointer we'll need to free,
         otherwise it'll be NULL (i.e. from argv).
     
    -    To fix this create a a module_clone_data_release() function to go with
    -    the MODULE_CLONE_DATA_INIT added in a98b02c1128 (submodule--helper:
    -    refactor module_clone(), 2021-07-10). We only need to add it to
    -    add_submodule() to fix the leak, but let's add it to module_clone() as
    -    well for consistency.
    +    Note that the free() of the "util" member is needed in both
    +    module_clone() and add_submodule(). The module_clone() function itself
    +    doesn't populate the "util" pointer as add_submodule() does, but
    +    module_clone() is upstream of the
    +    add_possible_reference_from_superproject() caller we're modifying
    +    here, which does do that.
    +
    +    This does preclude the use of the "util" pointer for any other reasons
    +    for now, but that's OK. If we ever need to use it for something else
    +    we could turn it into a small "struct" with an optional "to_free"
    +    member, and switch to using string_list_clear_func().
    +
    +    Alternatively we could have another "struct string_list to_free" which
    +    would keep a copy of the strings we've dup'd to free(). But for now
    +    this is perfectly adequate.
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/submodule--helper.c ##
    -@@ builtin/submodule--helper.c: struct module_clone_data {
    - 	.single_branch = -1, \
    - }
    - 
    -+static void module_clone_data_release(struct module_clone_data *cd)
    -+{
    -+	string_list_clear(&cd->reference, 1);
    -+}
    -+
    - struct submodule_alternate_setup {
    - 	const char *submodule_name;
    - 	enum SUBMODULE_ALTERNATE_ERROR_MODE {
     @@ builtin/submodule--helper.c: static int add_possible_reference_from_superproject(
      
      		sm_alternate = compute_alternate_path(sb.buf, &err);
    @@ builtin/submodule--helper.c: static int add_possible_reference_from_superproject
      			switch (sas->error_mode) {
     @@ builtin/submodule--helper.c: static int module_clone(int argc, const char **argv, const char *prefix)
      
    - 	clone_submodule(&clone_data, &clone_data.reference);
    + 	clone_submodule(&clone_data, &reference);
      	list_objects_filter_release(&filter_options);
    -+	module_clone_data_release(&clone_data);
    ++	string_list_clear(&reference, 1);
      	return 0;
      }
      
     @@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add_data)
    - {
      	char *submod_gitdir_path;
      	struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
    + 	struct string_list reference = STRING_LIST_INIT_NODUP;
     +	int ret = -1;
      
      	/* perhaps the path already exists and is already a git repo, else clone it */
    @@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add
      		clone_data.quiet = add_data->quiet;
      		clone_data.progress = add_data->progress;
     -		if (add_data->reference_path)
    --			string_list_append(&clone_data.reference,
    +-			string_list_append(&reference,
     -					   xstrdup(add_data->reference_path));
     +		if (add_data->reference_path) {
     +			char *p = xstrdup(add_data->reference_path);
     +
    -+			string_list_append(&clone_data.reference, p)->util = p;
    ++			string_list_append(&reference, p)->util = p;
     +		}
      		clone_data.dissociate = add_data->dissociate;
      		if (add_data->depth >= 0)
      			clone_data.depth = xstrfmt("%d", add_data->depth);
      
    - 		if (clone_submodule(&clone_data, &clone_data.reference))
    + 		if (clone_submodule(&clone_data, &reference))
     -			return -1;
     +			goto cleanup;
      
    @@ builtin/submodule--helper.c: static int add_submodule(const struct add_data *add
     -	return 0;
     +	ret = 0;
     +cleanup:
    -+	module_clone_data_release(&clone_data);
    ++	string_list_clear(&reference, 1);
     +	return ret;
      }
      
17:  7811bdbf149 ! 12:  1adb7b66656 submodule--helper: fix obscure leak in module_add()
    @@ Metadata
      ## Commit message ##
         submodule--helper: fix obscure leak in module_add()
     
    -    Fix an obscure leak in module_add()< if the "git add" command we were
    +    Fix an obscure leak in module_add(), if the "git add" command we were
         piping to failed we'd fail to strbuf_release(&sb). This fixes a leak
         introduced in a6226fd772b (submodule--helper: convert the bulk of
         cmd_add() to C, 2021-08-10).
    @@ Commit message
     
      ## builtin/submodule--helper.c ##
     @@ builtin/submodule--helper.c: static int module_add(int argc, const char **argv, const char *prefix)
    - 	int force = 0, quiet = 0, progress = 0, dissociate = 0;
    - 	struct add_data add_data = ADD_DATA_INIT;
    - 	char *to_free = NULL;
    + 		N_("git submodule add [<options>] [--] <repository> [<path>]"),
    + 		NULL
    + 	};
     +	struct strbuf sb = STRBUF_INIT;
     +	int ret = 1;
      
    - 	struct option options[] = {
    - 		OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
    + 	argc = parse_options(argc, argv, prefix, options, usage, 0);
    + 
     @@ builtin/submodule--helper.c: static int module_add(int argc, const char **argv, const char *prefix)
      	die_on_repo_without_commits(add_data.sm_path);
      
    @@ builtin/submodule--helper.c: static int module_add(int argc, const char **argv,
     -		int exit_code = -1;
     -		struct strbuf sb = STRBUF_INIT;
      		struct child_process cp = CHILD_PROCESS_INIT;
    + 
      		cp.git_cmd = 1;
      		cp.no_stdout = 1;
      		strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
18:  01566d63926 ! 13:  b27b665d287 submodule--helper: fix a leak in module_add()
    @@ builtin/submodule--helper.c: static int module_add(int argc, const char **argv,
     -		add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
     +	if (prefix && *prefix && !is_absolute_path(add_data.sm_path)) {
     +		char *sm_path = add_data.sm_path;
    ++
     +		add_data.sm_path = xstrfmt("%s%s", prefix, sm_path);
     +		free(sm_path);
     +	}
19:  7ef89abed86 = 14:  53ba1705eb6 submodule--helper: fix a memory leak in print_status()
20:  b8d47fc7d70 = 15:  230e5f8ad14 submodule--helper: free some "displaypath" in "struct update_data"
21:  4fb17f0dff1 <  -:  ----------- submodule--helper: rename "int res" to "int ret"
22:  af83925046b <  -:  ----------- submodule--helper: add skeleton "goto cleanup" to update_submodule()
23:  4c60784d281 <  -:  ----------- submodule--helper: don't exit() on failure, return
24:  7551af195ad = 16:  c0fba2f1c56 submodule--helper: free rest of "displaypath" in "struct update_data"
25:  f650716cd7e <  -:  ----------- submodule--helper: fix bad config API usage
26:  581ce0872c0 ! 17:  95f8b68bd41 submodule--helper: fix a configure_added_submodule() leak
    @@ builtin/submodule--helper.c: static void configure_added_submodule(struct add_da
      		 * current configured pathspec, set the submodule's active flag
     
      ## t/t7413-submodule-is-active.sh ##
    -@@ t/t7413-submodule-is-active.sh: This test verifies that `git submodue--helper is-active` correctly identifies
    +@@ t/t7413-submodule-is-active.sh: This test verifies that `test-tool submodule is-active` correctly identifies
      submodules which are "active" and interesting to the user.
      '
      
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply	[flat|nested] 186+ messages in thread

* [PATCH v4 01/17] submodule--helper: fix a leak in "clone_submodule"
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:29       ` Ævar Arnfjörð Bjarmason
  2022-07-28 16:29       ` [PATCH v4 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
                         ` (16 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:29 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak of the "clone_data_path" variable that we copy or
derive from the "struct module_clone_data" in clone_submodule(). This
code was refactored in preceding commits, but the leak has been with
us since f8eaa0ba98b (submodule--helper, module_clone: always operate
on absolute paths, 2016-03-31).

For the "else" case we don't need to xstrdup() the "clone_data->path",
and we don't need to free our own "clone_data_path". We can therefore
assign the "clone_data->path" to our own "clone_data_path" right away,
and only override it (and remember to free it!) if we need to
xstrfmt() a replacement.

In the case of the module_clone() caller it's from "argv", and doesn't
need to be free'd, and in the case of the add_submodule() caller we
get a pointer to "sm_path", which doesn't need to be directly free'd
either.

Fixing this leak makes several tests pass, so let's mark them as
passing with TEST_PASSES_SANITIZE_LEAK=true.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c   | 10 +++++-----
 t/t1500-rev-parse.sh          |  1 +
 t/t6008-rev-list-submodule.sh |  1 +
 t/t7414-submodule-mistakes.sh |  2 ++
 t/t7506-status-submodule.sh   |  1 +
 t/t7507-commit-verbose.sh     |  2 ++
 6 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 22c79353bb0..d3dd47588d5 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1572,13 +1572,12 @@ static int clone_submodule(const struct module_clone_data *clone_data,
 	char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);
 	char *sm_alternate = NULL, *error_strategy = NULL;
 	struct child_process cp = CHILD_PROCESS_INIT;
-	const char *clone_data_path;
+	const char *clone_data_path = clone_data->path;
+	char *to_free = NULL;
 
 	if (!is_absolute_path(clone_data->path))
-		clone_data_path = xstrfmt("%s/%s", get_git_work_tree(),
-					  clone_data->path);
-	else
-		clone_data_path = xstrdup(clone_data->path);
+		clone_data_path = to_free = xstrfmt("%s/%s", get_git_work_tree(),
+						    clone_data->path);
 
 	if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)
 		die(_("refusing to create/use '%s' in another submodule's "
@@ -1663,6 +1662,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,
 
 	free(sm_gitdir);
 	free(p);
+	free(to_free);
 	return 0;
 }
 
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 1c2df08333b..0e13bcb4ebb 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -4,6 +4,7 @@ test_description='test git rev-parse'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_one () {
diff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh
index 3153a0d8910..12e67e187ef 100755
--- a/t/t6008-rev-list-submodule.sh
+++ b/t/t6008-rev-list-submodule.sh
@@ -8,6 +8,7 @@ test_description='git rev-list involving submodules that this repo has'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh
index f2e7df59cf2..3269298197c 100755
--- a/t/t7414-submodule-mistakes.sh
+++ b/t/t7414-submodule-mistakes.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='handling of common mistakes people may make with submodules'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create embedded repository' '
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index 3fcb44767f5..f5426a8e589 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='git status for submodule'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_create_repo_with_commit () {
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index ed2653d46fe..92462a22374 100755
--- a/t/t7507-commit-verbose.sh
+++ b/t/t7507-commit-verbose.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='verbose commit template'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 write_script "check-for-diff" <<\EOF &&
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v4 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
  2022-07-28 16:29       ` [PATCH v4 01/17] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:29       ` Ævar Arnfjörð Bjarmason
  2022-07-28 16:29       ` [PATCH v4 03/17] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
                         ` (15 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:29 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in code added in 1012a5cbc3f (submodule--helper
run-update-procedure: learn --remote, 2022-03-04), we need to free()
the xstrdup()'d string. This gets e.g. t/t7419-submodule-set-branch.sh
closer to passing under SANITIZE=leak.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d3dd47588d5..d452af40d2d 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2410,6 +2410,8 @@ static int update_submodule(struct update_data *update_data,
 		const char *branch = remote_submodule_branch(update_data->sm_path);
 		char *remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch);
 
+		free(remote_name);
+
 		if (!update_data->nofetch) {
 			if (fetch_in_submodule(update_data->sm_path, update_data->depth,
 					      0, NULL))
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v4 03/17] submodule--helper: fix most "struct pathspec" memory leaks
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
  2022-07-28 16:29       ` [PATCH v4 01/17] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
  2022-07-28 16:29       ` [PATCH v4 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:29       ` Ævar Arnfjörð Bjarmason
  2022-07-28 16:29       ` [PATCH v4 04/17] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
                         ` (14 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:29 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Call clear_pathspec() at the end of various functions that work with
and allocate a "struct pathspec".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 74 +++++++++++++++++++++++++------------
 1 file changed, 51 insertions(+), 23 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d452af40d2d..6c53c83a2af 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -367,7 +367,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
 static int module_foreach(int argc, const char **argv, const char *prefix)
 {
 	struct foreach_cb info = FOREACH_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	struct option module_foreach_options[] = {
 		OPT__QUIET(&info.quiet, N_("suppress output of entering each submodule command")),
@@ -379,12 +379,13 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 		N_("git submodule foreach [--quiet] [--recursive] [--] <command>"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_foreach_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.argc = argc;
 	info.argv = argv;
@@ -392,7 +393,10 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static int starts_with_dot_slash(const char *const path)
@@ -502,7 +506,7 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data
 static int module_init(int argc, const char **argv, const char *prefix)
 {
 	struct init_cb info = INIT_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	struct option module_init_options[] = {
@@ -513,12 +517,13 @@ static int module_init(int argc, const char **argv, const char *prefix)
 		N_("git submodule init [<options>] [<path>]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_init_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	/*
 	 * If there are no path args and submodule.active is set then,
@@ -533,7 +538,10 @@ static int module_init(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, init_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct status_cb {
@@ -680,7 +688,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,
 static int module_status(int argc, const char **argv, const char *prefix)
 {
 	struct status_cb info = STATUS_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	struct option module_status_options[] = {
@@ -693,12 +701,13 @@ static int module_status(int argc, const char **argv, const char *prefix)
 		N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_status_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.prefix = prefix;
 	if (quiet)
@@ -706,7 +715,10 @@ static int module_status(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, status_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct module_cb {
@@ -1245,7 +1257,7 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data
 static int module_sync(int argc, const char **argv, const char *prefix)
 {
 	struct sync_cb info = SYNC_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	int recursive = 0;
@@ -1259,12 +1271,13 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 		N_("git submodule sync [--quiet] [--recursive] [<path>]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_sync_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.prefix = prefix;
 	if (quiet)
@@ -1274,7 +1287,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, sync_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct deinit_cb {
@@ -1383,7 +1399,7 @@ static void deinit_submodule_cb(const struct cache_entry *list_item,
 static int module_deinit(int argc, const char **argv, const char *prefix)
 {
 	struct deinit_cb info = DEINIT_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	int force = 0;
@@ -1398,6 +1414,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 		N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_deinit_options,
 			     git_submodule_helper_usage, 0);
@@ -1412,7 +1429,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 		die(_("Use '--all' if you really want to deinitialize all submodules"));
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.prefix = prefix;
 	if (quiet)
@@ -1422,7 +1439,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, deinit_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct module_clone_data {
@@ -2516,7 +2536,7 @@ static int update_submodules(struct update_data *update_data)
 
 static int module_update(int argc, const char **argv, const char *prefix)
 {
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct update_data opt = UPDATE_DATA_INIT;
 	struct list_objects_filter_options filter_options = { 0 };
 	int ret;
@@ -2593,8 +2613,8 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		opt.update_strategy.type = opt.update_default;
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {
-		list_objects_filter_release(&filter_options);
-		return 1;
+		ret = 1;
+		goto cleanup;
 	}
 
 	if (pathspec.nr)
@@ -2605,8 +2625,10 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		struct init_cb info = INIT_CB_INIT;
 
 		if (module_list_compute(argc, argv, opt.prefix,
-					&pathspec, &list) < 0)
-			return 1;
+					&pathspec, &list) < 0) {
+			ret = 1;
+			goto cleanup;
+		}
 
 		/*
 		 * If there are no path args and submodule.active is set then,
@@ -2623,7 +2645,9 @@ static int module_update(int argc, const char **argv, const char *prefix)
 	}
 
 	ret = update_submodules(&opt);
+cleanup:
 	list_objects_filter_release(&filter_options);
+	clear_pathspec(&pathspec);
 	return ret;
 }
 
@@ -2707,7 +2731,7 @@ static int push_check(int argc, const char **argv, const char *prefix)
 static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 {
 	int i;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;
 	struct option embed_gitdir_options[] = {
@@ -2722,17 +2746,21 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 		N_("git submodule absorbgitdirs [<options>] [<path>...]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, embed_gitdir_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	for (i = 0; i < list.nr; i++)
 		absorb_git_dir_into_superproject(list.entries[i]->name, flags);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static int module_config(int argc, const char **argv, const char *prefix)
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v4 04/17] submodule--helper: "struct pathspec" memory leak in module_update()
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
                         ` (2 preceding siblings ...)
  2022-07-28 16:29       ` [PATCH v4 03/17] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:29       ` Ævar Arnfjörð Bjarmason
  2022-07-28 16:29       ` [PATCH v4 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
                         ` (13 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:29 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

The module_update() function calls module_list_compute() twice, which
in turn will reset the "struct pathspec" passed to it. Let's instead
track two of them, and clear them both.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 6c53c83a2af..057cb6c0d42 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2537,6 +2537,7 @@ static int update_submodules(struct update_data *update_data)
 static int module_update(int argc, const char **argv, const char *prefix)
 {
 	struct pathspec pathspec = { 0 };
+	struct pathspec pathspec2 = { 0 };
 	struct update_data opt = UPDATE_DATA_INIT;
 	struct list_objects_filter_options filter_options = { 0 };
 	int ret;
@@ -2625,7 +2626,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		struct init_cb info = INIT_CB_INIT;
 
 		if (module_list_compute(argc, argv, opt.prefix,
-					&pathspec, &list) < 0) {
+					&pathspec2, &list) < 0) {
 			ret = 1;
 			goto cleanup;
 		}
@@ -2648,6 +2649,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 cleanup:
 	list_objects_filter_release(&filter_options);
 	clear_pathspec(&pathspec);
+	clear_pathspec(&pathspec2);
 	return ret;
 }
 
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v4 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
                         ` (3 preceding siblings ...)
  2022-07-28 16:29       ` [PATCH v4 04/17] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:29       ` Ævar Arnfjörð Bjarmason
  2022-07-28 16:30       ` [PATCH v4 06/17] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
                         ` (12 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:29 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak in c51f8f94e5b (submodule--helper: run update
procedures from C, 2021-08-24) and 3c3558f0953 (submodule--helper: run
update using child process struct, 2022-03-15) by not allocating
memory in the first place.

The "dir" member of "struct child_process" will not be modified by
that API, and it's declared to be "const char *". So let's not
needlessly duplicate these strings.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 057cb6c0d42..35a168009b7 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2105,7 +2105,7 @@ static int is_tip_reachable(const char *path, struct object_id *oid)
 	char *hex = oid_to_hex(oid);
 
 	cp.git_cmd = 1;
-	cp.dir = xstrdup(path);
+	cp.dir = path;
 	cp.no_stderr = 1;
 	strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL);
 
@@ -2123,7 +2123,7 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str
 
 	prepare_submodule_repo_env(&cp.env);
 	cp.git_cmd = 1;
-	cp.dir = xstrdup(module_path);
+	cp.dir = module_path;
 
 	strvec_push(&cp.args, "fetch");
 	if (quiet)
@@ -2180,7 +2180,7 @@ static int run_update_command(struct update_data *ud, int subforce,
 	}
 	strvec_push(&cp.args, oid);
 
-	cp.dir = xstrdup(ud->sm_path);
+	cp.dir = ud->sm_path;
 	prepare_submodule_repo_env(&cp.env);
 	if (run_command(&cp)) {
 		switch (ud->update_strategy.type) {
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v4 06/17] submodule--helper: add and use *_release() functions
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
                         ` (4 preceding siblings ...)
  2022-07-28 16:29       ` [PATCH v4 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:30       ` Ævar Arnfjörð Bjarmason
  2022-07-28 16:30       ` [PATCH v4 07/17] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
                         ` (11 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:30 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Add release functions for "struct module_list", "struct
submodule_update_clone" and "struct update_data".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c      | 28 +++++++++++++++++++++++++++-
 t/t6134-pathspec-in-submodule.sh |  1 +
 2 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 35a168009b7..48507a6e0df 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -160,6 +160,11 @@ struct module_list {
 };
 #define MODULE_LIST_INIT { 0 }
 
+static void module_list_release(struct module_list *ml)
+{
+	free(ml->entries);
+}
+
 static int module_list_compute(int argc, const char **argv,
 			       const char *prefix,
 			       struct pathspec *pathspec,
@@ -222,7 +227,7 @@ static void module_list_active(struct module_list *list)
 		active_modules.entries[active_modules.nr++] = ce;
 	}
 
-	free(list->entries);
+	module_list_release(list);
 	*list = active_modules;
 }
 
@@ -395,6 +400,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -540,6 +546,7 @@ static int module_init(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -717,6 +724,7 @@ static int module_status(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1289,6 +1297,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1441,6 +1450,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1810,6 +1820,12 @@ struct submodule_update_clone {
 };
 #define SUBMODULE_UPDATE_CLONE_INIT { 0 }
 
+static void submodule_update_clone_release(struct submodule_update_clone *suc)
+{
+	free(suc->update_clone);
+	free(suc->failed_clones);
+}
+
 struct update_data {
 	const char *prefix;
 	const char *displaypath;
@@ -1848,6 +1864,11 @@ struct update_data {
 	.max_jobs = 1, \
 }
 
+static void update_data_release(struct update_data *ud)
+{
+	module_list_release(&ud->list);
+}
+
 static void next_submodule_warn_missing(struct submodule_update_clone *suc,
 		struct strbuf *out, const char *displaypath)
 {
@@ -2530,6 +2551,7 @@ static int update_submodules(struct update_data *update_data)
 	}
 
 cleanup:
+	submodule_update_clone_release(&suc);
 	string_list_clear(&update_data->references, 0);
 	return res;
 }
@@ -2627,6 +2649,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 
 		if (module_list_compute(argc, argv, opt.prefix,
 					&pathspec2, &list) < 0) {
+			module_list_release(&list);
 			ret = 1;
 			goto cleanup;
 		}
@@ -2643,10 +2666,12 @@ static int module_update(int argc, const char **argv, const char *prefix)
 			info.flags |= OPT_QUIET;
 
 		for_each_listed_submodule(&list, init_submodule_cb, &info);
+		module_list_release(&list);
 	}
 
 	ret = update_submodules(&opt);
 cleanup:
+	update_data_release(&opt);
 	list_objects_filter_release(&filter_options);
 	clear_pathspec(&pathspec);
 	clear_pathspec(&pathspec2);
@@ -2762,6 +2787,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 	ret = 0;
 cleanup:
 	clear_pathspec(&pathspec);
+	module_list_release(&list);
 	return ret;
 }
 
diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh
index 0f1cb49cedc..3a241f259de 100755
--- a/t/t6134-pathspec-in-submodule.sh
+++ b/t/t6134-pathspec-in-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='test case exclude pathspec'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a submodule' '
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v4 07/17] submodule--helper: fix "errmsg_str" memory leak
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
                         ` (5 preceding siblings ...)
  2022-07-28 16:30       ` [PATCH v4 06/17] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:30       ` Ævar Arnfjörð Bjarmason
  2022-07-28 16:30       ` [PATCH v4 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
                         ` (10 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:30 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak introduced in e83e3333b57 (submodule: port submodule
subcommand 'summary' from shell to C, 2020-08-13), we sometimes append
to the "errmsg", and need to free the "struct strbuf".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 48507a6e0df..5cbe6a41e5f 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -960,6 +960,7 @@ static void generate_submodule_summary(struct summary_cb *info,
 	free(displaypath);
 	free(src_abbrev);
 	free(dst_abbrev);
+	strbuf_release(&errmsg);
 }
 
 static void prepare_submodule_summary(struct summary_cb *info,
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v4 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
                         ` (6 preceding siblings ...)
  2022-07-28 16:30       ` [PATCH v4 07/17] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:30       ` Ævar Arnfjörð Bjarmason
  2022-07-28 16:30       ` [PATCH v4 09/17] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
                         ` (9 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:30 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix leaks in "struct module_cb_list" and the "struct module_cb" which
it contains, these fixes leaks in e83e3333b57 (submodule: port
submodule subcommand 'summary' from shell to C, 2020-08-13).

The "sm_path" should always have been a "char *", not a "const
char *", we always create it with xstrdup().

We can't mark any tests passing passing with SANITIZE=leak using
"TEST_PASSES_SANITIZE_LEAK=true" as a result of this change, but
"t7401-submodule-summary.sh" gets closer to passing as a result of
this change.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 5cbe6a41e5f..becf686e47a 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -735,16 +735,34 @@ struct module_cb {
 	struct object_id oid_src;
 	struct object_id oid_dst;
 	char status;
-	const char *sm_path;
+	char *sm_path;
 };
 #define MODULE_CB_INIT { 0 }
 
+static void module_cb_release(struct module_cb *mcb)
+{
+	free(mcb->sm_path);
+}
+
 struct module_cb_list {
 	struct module_cb **entries;
 	int alloc, nr;
 };
 #define MODULE_CB_LIST_INIT { 0 }
 
+static void module_cb_list_release(struct module_cb_list *mcbl)
+{
+	int i;
+
+	for (i = 0; i < mcbl->nr; i++) {
+		struct module_cb *mcb = mcbl->entries[i];
+
+		module_cb_release(mcb);
+		free(mcb);
+	}
+	free(mcbl->entries);
+}
+
 struct summary_cb {
 	int argc;
 	const char **argv;
@@ -1088,6 +1106,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 cleanup:
 	strvec_clear(&diff_args);
 	release_revisions(&rev);
+	module_cb_list_release(&list);
 	return ret;
 }
 
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v4 09/17] submodule--helper: fix a leak with repo_clear()
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
                         ` (7 preceding siblings ...)
  2022-07-28 16:30       ` [PATCH v4 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:30       ` Ævar Arnfjörð Bjarmason
  2022-07-28 16:30       ` [PATCH v4 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
                         ` (8 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:30 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Call repo_clear() in ensure_core_worktree() to free the "struct
repository". Fixes a leak that's been here since
74d4731da1f (submodule--helper: replace connect-gitdir-workingtree by
ensure-core-worktree, 2018-08-13).

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index becf686e47a..dbeafd9787d 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2375,6 +2375,7 @@ static void ensure_core_worktree(const char *path)
 		free(abs_path);
 		strbuf_release(&sb);
 	}
+	repo_clear(&subrepo);
 }
 
 static const char *submodule_update_type_to_label(enum submodule_update_type type)
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v4 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule()
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
                         ` (8 preceding siblings ...)
  2022-07-28 16:30       ` [PATCH v4 09/17] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:30       ` Ævar Arnfjörð Bjarmason
  2022-07-28 16:30       ` [PATCH v4 11/17] submodule--helper: fix "reference" leak Ævar Arnfjörð Bjarmason
                         ` (7 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:30 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak in the get_default_remote_submodule() function added
in a77c3fcb5ec (submodule--helper: get remote names from any
repository, 2022-03-04), we need to repo_clear() the submodule we
initialize.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index dbeafd9787d..f8a92ecf86d 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -62,9 +62,13 @@ static char *repo_get_default_remote(struct repository *repo)
 static char *get_default_remote_submodule(const char *module_path)
 {
 	struct repository subrepo;
+	char *ret;
 
 	repo_submodule_init(&subrepo, the_repository, module_path, null_oid());
-	return repo_get_default_remote(&subrepo);
+	ret = repo_get_default_remote(&subrepo);
+	repo_clear(&subrepo);
+
+	return ret;
 }
 
 static char *get_default_remote(void)
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v4 11/17] submodule--helper: fix "reference" leak
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
                         ` (9 preceding siblings ...)
  2022-07-28 16:30       ` [PATCH v4 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:30       ` Ævar Arnfjörð Bjarmason
  2022-07-28 16:30       ` [PATCH v4 12/17] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
                         ` (6 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:30 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix leaks in the "reference" variable declared in add_submodule() and
module_clone().

In preceding commits this variable was refactored out of the "struct
module_clone_data", but the leak has been with us since
31224cbdc72 (clone: recursive and reference option triggers submodule
alternates, 2016-08-17) and 8c8195e9c3e (submodule--helper: introduce
add-clone subcommand, 2021-07-10).

Those commits added an xstrdup()'d member of the
STRING_LIST_INIT_NODUP'd "struct string_list". We need to free()
those, but not the ones we get from argv, let's make use of the "util"
member, if it has a pointer it's the pointer we'll need to free,
otherwise it'll be NULL (i.e. from argv).

Note that the free() of the "util" member is needed in both
module_clone() and add_submodule(). The module_clone() function itself
doesn't populate the "util" pointer as add_submodule() does, but
module_clone() is upstream of the
add_possible_reference_from_superproject() caller we're modifying
here, which does do that.

This does preclude the use of the "util" pointer for any other reasons
for now, but that's OK. If we ever need to use it for something else
we could turn it into a small "struct" with an optional "to_free"
member, and switch to using string_list_clear_func().

Alternatively we could have another "struct string_list to_free" which
would keep a copy of the strings we've dup'd to free(). But for now
this is perfectly adequate.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f8a92ecf86d..debe3a4a2f5 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1548,7 +1548,9 @@ static int add_possible_reference_from_superproject(
 
 		sm_alternate = compute_alternate_path(sb.buf, &err);
 		if (sm_alternate) {
-			string_list_append(sas->reference, xstrdup(sb.buf));
+			char *p = strbuf_detach(&sb, NULL);
+
+			string_list_append(sas->reference, p)->util = p;
 			free(sm_alternate);
 		} else {
 			switch (sas->error_mode) {
@@ -1780,6 +1782,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 
 	clone_submodule(&clone_data, &reference);
 	list_objects_filter_release(&filter_options);
+	string_list_clear(&reference, 1);
 	return 0;
 }
 
@@ -3018,6 +3021,7 @@ static int add_submodule(const struct add_data *add_data)
 	char *submod_gitdir_path;
 	struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
 	struct string_list reference = STRING_LIST_INIT_NODUP;
+	int ret = -1;
 
 	/* perhaps the path already exists and is already a git repo, else clone it */
 	if (is_directory(add_data->sm_path)) {
@@ -3073,15 +3077,17 @@ static int add_submodule(const struct add_data *add_data)
 		clone_data.url = add_data->realrepo;
 		clone_data.quiet = add_data->quiet;
 		clone_data.progress = add_data->progress;
-		if (add_data->reference_path)
-			string_list_append(&reference,
-					   xstrdup(add_data->reference_path));
+		if (add_data->reference_path) {
+			char *p = xstrdup(add_data->reference_path);
+
+			string_list_append(&reference, p)->util = p;
+		}
 		clone_data.dissociate = add_data->dissociate;
 		if (add_data->depth >= 0)
 			clone_data.depth = xstrfmt("%d", add_data->depth);
 
 		if (clone_submodule(&clone_data, &reference))
-			return -1;
+			goto cleanup;
 
 		prepare_submodule_repo_env(&cp.env);
 		cp.git_cmd = 1;
@@ -3100,7 +3106,10 @@ static int add_submodule(const struct add_data *add_data)
 		if (run_command(&cp))
 			die(_("unable to checkout submodule '%s'"), add_data->sm_path);
 	}
-	return 0;
+	ret = 0;
+cleanup:
+	string_list_clear(&reference, 1);
+	return ret;
 }
 
 static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v4 12/17] submodule--helper: fix obscure leak in module_add()
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
                         ` (10 preceding siblings ...)
  2022-07-28 16:30       ` [PATCH v4 11/17] submodule--helper: fix "reference" leak Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:30       ` Ævar Arnfjörð Bjarmason
  2022-07-28 16:30       ` [PATCH v4 13/17] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
                         ` (5 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:30 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix an obscure leak in module_add(), if the "git add" command we were
piping to failed we'd fail to strbuf_release(&sb). This fixes a leak
introduced in a6226fd772b (submodule--helper: convert the bulk of
cmd_add() to C, 2021-08-10).

In fixing it move to a "goto cleanup" pattern, and since we need to
introduce a "ret" variable to do that let's also get rid of the
intermediate "exit_code" variable. The initialization to "-1" in
a6226fd772b has always been redundant, we'd only use the "exit_code"
value after assigning the return value of pipe_command() to it.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index debe3a4a2f5..0f6c07e3d1e 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3269,6 +3269,8 @@ static int module_add(int argc, const char **argv, const char *prefix)
 		N_("git submodule add [<options>] [--] <repository> [<path>]"),
 		NULL
 	};
+	struct strbuf sb = STRBUF_INIT;
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 
@@ -3318,21 +3320,17 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	die_on_repo_without_commits(add_data.sm_path);
 
 	if (!force) {
-		int exit_code = -1;
-		struct strbuf sb = STRBUF_INIT;
 		struct child_process cp = CHILD_PROCESS_INIT;
 
 		cp.git_cmd = 1;
 		cp.no_stdout = 1;
 		strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
 			     "--no-warn-embedded-repo", add_data.sm_path, NULL);
-		if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
+		if ((ret = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
 			strbuf_complete_line(&sb);
 			fputs(sb.buf, stderr);
-			free(add_data.sm_path);
-			return exit_code;
+			goto cleanup;
 		}
-		strbuf_release(&sb);
 	}
 
 	if(!add_data.sm_name)
@@ -3347,15 +3345,17 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	add_data.progress = !!progress;
 	add_data.dissociate = !!dissociate;
 
-	if (add_submodule(&add_data)) {
-		free(add_data.sm_path);
-		return 1;
-	}
+	if (add_submodule(&add_data))
+		goto cleanup;
 	configure_added_submodule(&add_data);
+
+	ret = 0;
+cleanup:
 	free(add_data.sm_path);
 	free(to_free);
+	strbuf_release(&sb);
 
-	return 0;
+	return ret;
 }
 
 #define SUPPORT_SUPER_PREFIX (1<<0)
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v4 13/17] submodule--helper: fix a leak in module_add()
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
                         ` (11 preceding siblings ...)
  2022-07-28 16:30       ` [PATCH v4 12/17] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:30       ` Ævar Arnfjörð Bjarmason
  2022-07-28 16:30       ` [PATCH v4 14/17] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
                         ` (4 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:30 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in module_path(), since a6226fd772b (submodule--helper:
convert the bulk of cmd_add() to C, 2021-08-10), we've been freeing
add_data.sm_path, but in this case we clobbered it, and didn't free
the value we clobbered.

This makes test 28 of "t/t7400-submodule-basic.sh" ("submodule add in
subdirectory") pass when we're compiled with SANITIZE=leak..

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 0f6c07e3d1e..6077aecd202 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3290,8 +3290,12 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	else
 		add_data.sm_path = xstrdup(argv[1]);
 
-	if (prefix && *prefix && !is_absolute_path(add_data.sm_path))
-		add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
+	if (prefix && *prefix && !is_absolute_path(add_data.sm_path)) {
+		char *sm_path = add_data.sm_path;
+
+		add_data.sm_path = xstrfmt("%s%s", prefix, sm_path);
+		free(sm_path);
+	}
 
 	if (starts_with_dot_dot_slash(add_data.repo) ||
 	    starts_with_dot_slash(add_data.repo)) {
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v4 14/17] submodule--helper: fix a memory leak in print_status()
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
                         ` (12 preceding siblings ...)
  2022-07-28 16:30       ` [PATCH v4 13/17] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:30       ` Ævar Arnfjörð Bjarmason
  2022-07-28 16:30       ` [PATCH v4 15/17] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
                         ` (3 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:30 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in print_status(), the compute_rev_name() function
implemented in this file will return a strbuf_detach()'d value, or
NULL.

This leak has existed since this code was added in
a9f8a37584a (submodule: port submodule subcommand 'status' from shell
to C, 2017-10-06), but in 0b5e2ea7cf3 (submodule--helper: don't print
null in 'submodule status', 2018-04-18) we added a "const"
intermediate variable for the return value, that "const" should be
removed.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 6077aecd202..b51b60d5c77 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -570,10 +570,11 @@ static void print_status(unsigned int flags, char state, const char *path,
 	printf("%c%s %s", state, oid_to_hex(oid), displaypath);
 
 	if (state == ' ' || state == '+') {
-		const char *name = compute_rev_name(path, oid_to_hex(oid));
+		char *name = compute_rev_name(path, oid_to_hex(oid));
 
 		if (name)
 			printf(" (%s)", name);
+		free(name);
 	}
 
 	printf("\n");
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v4 15/17] submodule--helper: free some "displaypath" in "struct update_data"
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
                         ` (13 preceding siblings ...)
  2022-07-28 16:30       ` [PATCH v4 14/17] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:30       ` Ævar Arnfjörð Bjarmason
  2022-07-28 16:30       ` [PATCH v4 16/17] submodule--helper: free rest of " Ævar Arnfjörð Bjarmason
                         ` (2 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:30 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Make the update_data_release() function free "displaypath" member when
appropriate. The "displaypath" member is always ours, the "const" on
the "char *" was wrong to begin with.

This leaves a leak of "displaypath" in update_submodule(), which as
we'll see in subsequent commits is harder to deal with than this
trivial fix.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c        | 3 ++-
 t/t2403-worktree-move.sh           | 1 +
 t/t7412-submodule-absorbgitdirs.sh | 1 +
 3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index b51b60d5c77..2e39750fb06 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1856,7 +1856,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)
 
 struct update_data {
 	const char *prefix;
-	const char *displaypath;
+	char *displaypath;
 	enum submodule_update_type update_default;
 	struct object_id suboid;
 	struct string_list references;
@@ -1894,6 +1894,7 @@ struct update_data {
 
 static void update_data_release(struct update_data *ud)
 {
+	free(ud->displaypath);
 	module_list_release(&ud->list);
 }
 
diff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh
index a4e1a178e0a..1168e9f9982 100755
--- a/t/t2403-worktree-move.sh
+++ b/t/t2403-worktree-move.sh
@@ -2,6 +2,7 @@
 
 test_description='test git worktree move, remove, lock and unlock'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh
index 1cfa150768d..2859695c6d2 100755
--- a/t/t7412-submodule-absorbgitdirs.sh
+++ b/t/t7412-submodule-absorbgitdirs.sh
@@ -6,6 +6,7 @@ This test verifies that `git submodue absorbgitdirs` moves a submodules git
 directory into the superproject.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a real submodule' '
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v4 16/17] submodule--helper: free rest of "displaypath" in "struct update_data"
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
                         ` (14 preceding siblings ...)
  2022-07-28 16:30       ` [PATCH v4 15/17] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:30       ` Ævar Arnfjörð Bjarmason
  2022-07-28 16:30       ` [PATCH v4 17/17] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:30 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in code added in c51f8f94e5b (submodule--helper: run update
procedures from C, 2021-08-24), we clobber the "displaypath" member of
the passed-in "struct update_data" both so that die() messages in this
update_submodule() function itself can use it, and for the
run_update_procedure() called within this function.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 2e39750fb06..2a171d25992 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2460,10 +2460,11 @@ static int update_submodule(struct update_data *update_data,
 			    int *must_die_on_failure)
 {
 	int ret = 1;
+	char *to_free, *restore = update_data->displaypath;
 
 	ensure_core_worktree(update_data->sm_path);
 
-	update_data->displaypath = get_submodule_displaypath(
+	update_data->displaypath = to_free = get_submodule_displaypath(
 		update_data->sm_path, update_data->prefix);
 
 	determine_submodule_update_strategy(the_repository, update_data->just_cloned,
@@ -2537,6 +2538,9 @@ static int update_submodule(struct update_data *update_data,
 
 	ret = 0;
 cleanup:
+	free(to_free);
+	update_data->displaypath = restore;
+
 	return ret;
 }
 
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v4 17/17] submodule--helper: fix a configure_added_submodule() leak
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
                         ` (15 preceding siblings ...)
  2022-07-28 16:30       ` [PATCH v4 16/17] submodule--helper: free rest of " Ævar Arnfjörð Bjarmason
@ 2022-07-28 16:30       ` Ævar Arnfjörð Bjarmason
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:30 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix config API a memory leak added in a452128a36c (submodule--helper:
introduce add-config subcommand, 2021-08-06) by using the *_tmp()
variant of git_config_get_string().

In this case we're only checking whether
the (repo|git)_config_get_string() call is telling us that the
"submodule.active" key exists.

As with the preceding commit we'll find many other such patterns in
the codebase if we go fishing. E.g. "git gc" leaks in the code added
in 61f7a383d3b (maintenance: use 'incremental' strategy by default,
2020-10-15). Similar code in "git gc" added in
b08ff1fee00 (maintenance: add --schedule option and config,
2020-09-11) doesn't leak, but we could avoid the malloc() & free() in
that case.

A coccinelle rule to find those would find and fix some leaks, and
cases where we're doing needless malloc() + free()'s but only care
about the key existence, or are copying
the (repo|git)_config_get_string() return value right away.

But as with the preceding commit let's punt on all of that for now,
and just narrowly fix this specific case in submodule--helper.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c    | 4 ++--
 t/t7413-submodule-is-active.sh | 1 +
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 2a171d25992..a3e00c9929d 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3136,7 +3136,7 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con
 static void configure_added_submodule(struct add_data *add_data)
 {
 	char *key;
-	char *val = NULL;
+	const char *val;
 	struct child_process add_submod = CHILD_PROCESS_INIT;
 	struct child_process add_gitmodules = CHILD_PROCESS_INIT;
 
@@ -3181,7 +3181,7 @@ static void configure_added_submodule(struct add_data *add_data)
 	 * is_submodule_active(), since that function needs to find
 	 * out the value of "submodule.active" again anyway.
 	 */
-	if (!git_config_get_string("submodule.active", &val)) {
+	if (!git_config_get_string_tmp("submodule.active", &val)) {
 		/*
 		 * If the submodule being added isn't already covered by the
 		 * current configured pathspec, set the submodule's active flag
diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh
index 9ead083371a..a3c824c3e4b 100755
--- a/t/t7413-submodule-is-active.sh
+++ b/t/t7413-submodule-is-active.sh
@@ -6,6 +6,7 @@ This test verifies that `test-tool submodule is-active` correctly identifies
 submodules which are "active" and interesting to the user.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-- 
2.37.1.1197.g7ed548b7807


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* Re: [PATCH v3 02/26] submodule--helper: stop conflating "sb" in clone_submodule()
  2022-07-22 18:47           ` Junio C Hamano
@ 2022-07-28 16:30             ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-28 16:30 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Glen Choo, Atharva Raykar, Prathamesh Chavan


On Fri, Jul 22 2022, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
>
>>> This looks like a roundabout way to say xstrfmt().
>>
>> Yes, I can fix this and others while I'm at it, but a lot of things like
>> that in this code are funny uses of APIs that we could improve.
>>
>> I think it's probably best to just leave these for now.
>
> Agreed.  We could instead have a separate series to fix API usage
> before these and then build leak-plugging on top, or the other way
> around, and in general "clean then plug" would make it easier to
> review the plugging patches (simply because it would be working on
> clean code, not code that misuses the API in strange ways), but it
> is too late now.  Lets make sure we do not forget to revisit the API
> misuse but lets avoid mixing it into the series.

I ended up doing the opposite of what you suggested here, but not
lightly.

When re-rolling the v4 of the leak series I noticed that some of what
was suggested I'd fix (and thanks a lot to you and Glen for the reviews)
was code that was either dead, or should really belong in a "test-tool"
at this point.

So, I could have addressed those by padding the "leak" series with more
digressions, but I felt that just cleanly splitting it into a "prep"
series and "leak fixes" was better, those two are the just-submitted:

	https://lore.kernel.org/cover-00.20-00000000000-20220728T161116Z-avarab@gmail.com
	https://lore.kernel.org/git/cover-v4-00.17-00000000000-20220728T162442Z-avarab@gmail.com

Sorry if that causes any disruption for you. I noticed that you merged
the v3 into your "jch", but it didn't look anywhere close to "next", so
getting it more right to begin with seemed like a better trade-off.

Thanks!

^ permalink raw reply	[flat|nested] 186+ messages in thread

* [PATCH v5 00/17] submodule--helper: fix memory leaks
  2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
                         ` (16 preceding siblings ...)
  2022-07-28 16:30       ` [PATCH v4 17/17] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54       ` Ævar Arnfjörð Bjarmason
  2022-08-02 15:54         ` [PATCH v5 01/17] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
                           ` (17 more replies)
  17 siblings, 18 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

This series fixes all of the memory leaks in
builtin/submodule--helper.c that our test suite spots, except for
those where the leak is downstream of cmd_submodule__helper() and
caused by code that's not in builtin/submodule--helper.c (e.g. leaks
in the object or config APIs, and in submodule.c).

This re-roll is on top of the just-now re-rolled v2 of the base
topic[1]. The only changes are to rebase the changes here on top of
those changes.

Changes:

* The base topic renamed another "res" variable (one was left behind
  before).

* The "goto cleanup" pattern in update_submodule() is gone now, which
  makes 16/17 much easier.

For the CI run & pushed branch see [2].

1. https://lore.kernel.org/git/cover-v2-00.28-00000000000-20220802T154036Z-avarab@gmail.com/
2. https://github.com/avar/git/tree/avar/submodule--helper-memory-leaks-5

Ævar Arnfjörð Bjarmason (17):
  submodule--helper: fix a leak in "clone_submodule"
  submodule--helper: fix trivial get_default_remote_submodule() leak
  submodule--helper: fix most "struct pathspec" memory leaks
  submodule--helper: "struct pathspec" memory leak in module_update()
  submodule--helper: don't leak {run,capture}_command() cp.dir argument
  submodule--helper: add and use *_release() functions
  submodule--helper: fix "errmsg_str" memory leak
  submodule--helper: fix "sm_path" and other "module_cb_list" leaks
  submodule--helper: fix a leak with repo_clear()
  submodule--helper: fix a memory leak in get_default_remote_submodule()
  submodule--helper: fix "reference" leak
  submodule--helper: fix obscure leak in module_add()
  submodule--helper: fix a leak in module_add()
  submodule--helper: fix a memory leak in print_status()
  submodule--helper: free some "displaypath" in "struct update_data"
  submodule--helper: free rest of "displaypath" in "struct update_data"
  submodule--helper: fix a configure_added_submodule() leak

 builtin/submodule--helper.c        | 239 +++++++++++++++++++++--------
 t/t1500-rev-parse.sh               |   1 +
 t/t2403-worktree-move.sh           |   1 +
 t/t6008-rev-list-submodule.sh      |   1 +
 t/t6134-pathspec-in-submodule.sh   |   1 +
 t/t7412-submodule-absorbgitdirs.sh |   1 +
 t/t7413-submodule-is-active.sh     |   1 +
 t/t7414-submodule-mistakes.sh      |   2 +
 t/t7506-status-submodule.sh        |   1 +
 t/t7507-commit-verbose.sh          |   2 +
 10 files changed, 185 insertions(+), 65 deletions(-)

Range-diff against v4:
 1:  aac987a414a =  1:  118e965d401 submodule--helper: fix a leak in "clone_submodule"
 2:  390c5174e17 !  2:  d885e1dd59a submodule--helper: fix trivial get_default_remote_submodule() leak
    @@ Commit message
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/submodule--helper.c ##
    -@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data,
    +@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)
      		const char *branch = remote_submodule_branch(update_data->sm_path);
      		char *remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch);
      
 3:  529e10233d5 =  3:  1f90348d61f submodule--helper: fix most "struct pathspec" memory leaks
 4:  683d327752f =  4:  f768ff2e34d submodule--helper: "struct pathspec" memory leak in module_update()
 5:  4e8e9197539 !  5:  509133c37fa submodule--helper: don't leak {run,capture}_command() cp.dir argument
    @@ builtin/submodule--helper.c: static int fetch_in_submodule(const char *module_pa
      
      	strvec_push(&cp.args, "fetch");
      	if (quiet)
    -@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce,
    +@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)
      	}
      	strvec_push(&cp.args, oid);
      
    @@ builtin/submodule--helper.c: static int run_update_command(struct update_data *u
     +	cp.dir = ud->sm_path;
      	prepare_submodule_repo_env(&cp.env);
      	if (run_command(&cp)) {
    - 		switch (ud->update_strategy.type) {
    + 		int ret;
 6:  575d3e8d2e2 !  6:  25377f1d06c submodule--helper: add and use *_release() functions
    @@ builtin/submodule--helper.c: static int update_submodules(struct update_data *up
      cleanup:
     +	submodule_update_clone_release(&suc);
      	string_list_clear(&update_data->references, 0);
    - 	return res;
    + 	return ret;
      }
     @@ builtin/submodule--helper.c: static int module_update(int argc, const char **argv, const char *prefix)
      
 7:  d4c81e04254 =  7:  1a85057cd0c submodule--helper: fix "errmsg_str" memory leak
 8:  ef9e29d5bfe =  8:  3c4f734e958 submodule--helper: fix "sm_path" and other "module_cb_list" leaks
 9:  0798a00c9ef !  9:  3aebff9f8e3 submodule--helper: fix a leak with repo_clear()
    @@ Commit message
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/submodule--helper.c ##
    -@@ builtin/submodule--helper.c: static void ensure_core_worktree(const char *path)
    - 		free(abs_path);
    +@@ builtin/submodule--helper.c: static int ensure_core_worktree(const char *path)
      		strbuf_release(&sb);
      	}
    + 
     +	repo_clear(&subrepo);
    + 	return 0;
      }
      
    - static const char *submodule_update_type_to_label(enum submodule_update_type type)
10:  dae2a6f8e07 = 10:  38345ec76bc submodule--helper: fix a memory leak in get_default_remote_submodule()
11:  e7352bb8cfa = 11:  4b1e5ced969 submodule--helper: fix "reference" leak
12:  1adb7b66656 = 12:  e74c396073b submodule--helper: fix obscure leak in module_add()
13:  b27b665d287 = 13:  71a56c59864 submodule--helper: fix a leak in module_add()
14:  53ba1705eb6 = 14:  2429db2f1c7 submodule--helper: fix a memory leak in print_status()
15:  230e5f8ad14 = 15:  613d077c4ec submodule--helper: free some "displaypath" in "struct update_data"
16:  c0fba2f1c56 ! 16:  8f150a81507 submodule--helper: free rest of "displaypath" in "struct update_data"
    @@ Commit message
         update_submodule() function itself can use it, and for the
         run_update_procedure() called within this function.
     
    +    To make managing that clobbering easier let's wrap the
    +    update_submodule() in a new update_submodule_outer() function, which
    +    will do the clobbering and free(to_free) dance for us.
    +
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/submodule--helper.c ##
    -@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data,
    - 			    int *must_die_on_failure)
    +@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)
      {
    - 	int ret = 1;
    -+	char *to_free, *restore = update_data->displaypath;
    - 
    - 	ensure_core_worktree(update_data->sm_path);
    + 	int ret;
      
    +-	ret = ensure_core_worktree(update_data->sm_path);
    +-	if (ret)
    +-		return ret;
    +-
     -	update_data->displaypath = get_submodule_displaypath(
    -+	update_data->displaypath = to_free = get_submodule_displaypath(
    - 		update_data->sm_path, update_data->prefix);
    - 
    - 	determine_submodule_update_strategy(the_repository, update_data->just_cloned,
    -@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data,
    +-		update_data->sm_path, update_data->prefix);
    +-
    + 	ret = determine_submodule_update_strategy(the_repository,
    + 						  update_data->just_cloned,
    + 						  update_data->sm_path,
    +@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)
    + 	return 0;
    + }
      
    - 	ret = 0;
    - cleanup:
    ++static int update_submodule_outer(struct update_data *update_data)
    ++{
    ++	char *to_free, *restore = update_data->displaypath;
    ++	int ret;
    ++
    ++	ensure_core_worktree(update_data->sm_path);
    ++
    ++	update_data->displaypath = to_free = get_submodule_displaypath(
    ++		update_data->sm_path, update_data->prefix);
    ++
    ++	ret = update_submodule(update_data);
    ++
     +	free(to_free);
     +	update_data->displaypath = restore;
     +
    - 	return ret;
    - }
    ++	return ret;
    ++}
    ++
    + static int update_submodules(struct update_data *update_data)
    + {
    + 	int i, ret = 0;
    +@@ builtin/submodule--helper.c: static int update_submodules(struct update_data *update_data)
    + 		update_data->just_cloned = ucd.just_cloned;
    + 		update_data->sm_path = ucd.sub->path;
      
    +-		code = update_submodule(update_data);
    ++		code = update_submodule_outer(update_data);
    + 		if (code)
    + 			ret = code;
    + 		if (code == 128)
17:  95f8b68bd41 ! 17:  17c77ceba01 submodule--helper: fix a configure_added_submodule() leak
    @@ builtin/submodule--helper.c: static void configure_added_submodule(struct add_da
      		 * current configured pathspec, set the submodule's active flag
     
      ## t/t7413-submodule-is-active.sh ##
    -@@ t/t7413-submodule-is-active.sh: This test verifies that `test-tool submodule is-active` correctly identifies
    - submodules which are "active" and interesting to the user.
    +@@ t/t7413-submodule-is-active.sh: This is a unit test of the submodule.c is_submodule_active() function,
    + which is also indirectly tested elsewhere.
      '
      
     +TEST_PASSES_SANITIZE_LEAK=true
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply	[flat|nested] 186+ messages in thread

* [PATCH v5 01/17] submodule--helper: fix a leak in "clone_submodule"
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54         ` Ævar Arnfjörð Bjarmason
  2022-08-02 15:54         ` [PATCH v5 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
                           ` (16 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak of the "clone_data_path" variable that we copy or
derive from the "struct module_clone_data" in clone_submodule(). This
code was refactored in preceding commits, but the leak has been with
us since f8eaa0ba98b (submodule--helper, module_clone: always operate
on absolute paths, 2016-03-31).

For the "else" case we don't need to xstrdup() the "clone_data->path",
and we don't need to free our own "clone_data_path". We can therefore
assign the "clone_data->path" to our own "clone_data_path" right away,
and only override it (and remember to free it!) if we need to
xstrfmt() a replacement.

In the case of the module_clone() caller it's from "argv", and doesn't
need to be free'd, and in the case of the add_submodule() caller we
get a pointer to "sm_path", which doesn't need to be directly free'd
either.

Fixing this leak makes several tests pass, so let's mark them as
passing with TEST_PASSES_SANITIZE_LEAK=true.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c   | 10 +++++-----
 t/t1500-rev-parse.sh          |  1 +
 t/t6008-rev-list-submodule.sh |  1 +
 t/t7414-submodule-mistakes.sh |  2 ++
 t/t7506-status-submodule.sh   |  1 +
 t/t7507-commit-verbose.sh     |  2 ++
 6 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 5bc9cc41369..d7eaedee491 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1592,13 +1592,12 @@ static int clone_submodule(const struct module_clone_data *clone_data,
 	char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);
 	char *sm_alternate = NULL, *error_strategy = NULL;
 	struct child_process cp = CHILD_PROCESS_INIT;
-	const char *clone_data_path;
+	const char *clone_data_path = clone_data->path;
+	char *to_free = NULL;
 
 	if (!is_absolute_path(clone_data->path))
-		clone_data_path = xstrfmt("%s/%s", get_git_work_tree(),
-					  clone_data->path);
-	else
-		clone_data_path = xstrdup(clone_data->path);
+		clone_data_path = to_free = xstrfmt("%s/%s", get_git_work_tree(),
+						    clone_data->path);
 
 	if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)
 		die(_("refusing to create/use '%s' in another submodule's "
@@ -1683,6 +1682,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,
 
 	free(sm_gitdir);
 	free(p);
+	free(to_free);
 	return 0;
 }
 
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 1c2df08333b..0e13bcb4ebb 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -4,6 +4,7 @@ test_description='test git rev-parse'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_one () {
diff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh
index 3153a0d8910..12e67e187ef 100755
--- a/t/t6008-rev-list-submodule.sh
+++ b/t/t6008-rev-list-submodule.sh
@@ -8,6 +8,7 @@ test_description='git rev-list involving submodules that this repo has'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh
index f2e7df59cf2..3269298197c 100755
--- a/t/t7414-submodule-mistakes.sh
+++ b/t/t7414-submodule-mistakes.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='handling of common mistakes people may make with submodules'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create embedded repository' '
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index 3fcb44767f5..f5426a8e589 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='git status for submodule'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_create_repo_with_commit () {
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index ed2653d46fe..92462a22374 100755
--- a/t/t7507-commit-verbose.sh
+++ b/t/t7507-commit-verbose.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='verbose commit template'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 write_script "check-for-diff" <<\EOF &&
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v5 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  2022-08-02 15:54         ` [PATCH v5 01/17] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54         ` Ævar Arnfjörð Bjarmason
  2022-08-02 15:54         ` [PATCH v5 03/17] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
                           ` (15 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in code added in 1012a5cbc3f (submodule--helper
run-update-procedure: learn --remote, 2022-03-04), we need to free()
the xstrdup()'d string. This gets e.g. t/t7419-submodule-set-branch.sh
closer to passing under SANITIZE=leak.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d7eaedee491..d958da7dddc 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2437,6 +2437,8 @@ static int update_submodule(struct update_data *update_data)
 		const char *branch = remote_submodule_branch(update_data->sm_path);
 		char *remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch);
 
+		free(remote_name);
+
 		if (!update_data->nofetch) {
 			if (fetch_in_submodule(update_data->sm_path, update_data->depth,
 					      0, NULL))
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v5 03/17] submodule--helper: fix most "struct pathspec" memory leaks
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  2022-08-02 15:54         ` [PATCH v5 01/17] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
  2022-08-02 15:54         ` [PATCH v5 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54         ` Ævar Arnfjörð Bjarmason
  2022-08-03 22:59           ` Glen Choo
  2022-08-02 15:54         ` [PATCH v5 04/17] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
                           ` (14 subsequent siblings)
  17 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Call clear_pathspec() at the end of various functions that work with
and allocate a "struct pathspec".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 74 +++++++++++++++++++++++++------------
 1 file changed, 51 insertions(+), 23 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d958da7dddc..92d32f2877f 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -367,7 +367,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
 static int module_foreach(int argc, const char **argv, const char *prefix)
 {
 	struct foreach_cb info = FOREACH_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	struct option module_foreach_options[] = {
 		OPT__QUIET(&info.quiet, N_("suppress output of entering each submodule command")),
@@ -379,12 +379,13 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 		N_("git submodule foreach [--quiet] [--recursive] [--] <command>"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_foreach_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.argc = argc;
 	info.argv = argv;
@@ -392,7 +393,10 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static int starts_with_dot_slash(const char *const path)
@@ -522,7 +526,7 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data
 static int module_init(int argc, const char **argv, const char *prefix)
 {
 	struct init_cb info = INIT_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	struct option module_init_options[] = {
@@ -533,12 +537,13 @@ static int module_init(int argc, const char **argv, const char *prefix)
 		N_("git submodule init [<options>] [<path>]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_init_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	/*
 	 * If there are no path args and submodule.active is set then,
@@ -553,7 +558,10 @@ static int module_init(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, init_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct status_cb {
@@ -700,7 +708,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,
 static int module_status(int argc, const char **argv, const char *prefix)
 {
 	struct status_cb info = STATUS_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	struct option module_status_options[] = {
@@ -713,12 +721,13 @@ static int module_status(int argc, const char **argv, const char *prefix)
 		N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_status_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.prefix = prefix;
 	if (quiet)
@@ -726,7 +735,10 @@ static int module_status(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, status_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct module_cb {
@@ -1265,7 +1277,7 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data
 static int module_sync(int argc, const char **argv, const char *prefix)
 {
 	struct sync_cb info = SYNC_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	int recursive = 0;
@@ -1279,12 +1291,13 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 		N_("git submodule sync [--quiet] [--recursive] [<path>]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_sync_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.prefix = prefix;
 	if (quiet)
@@ -1294,7 +1307,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, sync_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct deinit_cb {
@@ -1403,7 +1419,7 @@ static void deinit_submodule_cb(const struct cache_entry *list_item,
 static int module_deinit(int argc, const char **argv, const char *prefix)
 {
 	struct deinit_cb info = DEINIT_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	int force = 0;
@@ -1418,6 +1434,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 		N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_deinit_options,
 			     git_submodule_helper_usage, 0);
@@ -1432,7 +1449,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 		die(_("Use '--all' if you really want to deinitialize all submodules"));
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.prefix = prefix;
 	if (quiet)
@@ -1442,7 +1459,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, deinit_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct module_clone_data {
@@ -2531,7 +2551,7 @@ static int update_submodules(struct update_data *update_data)
 
 static int module_update(int argc, const char **argv, const char *prefix)
 {
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct update_data opt = UPDATE_DATA_INIT;
 	struct list_objects_filter_options filter_options = { 0 };
 	int ret;
@@ -2608,8 +2628,8 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		opt.update_strategy.type = opt.update_default;
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {
-		list_objects_filter_release(&filter_options);
-		return 1;
+		ret = 1;
+		goto cleanup;
 	}
 
 	if (pathspec.nr)
@@ -2620,8 +2640,10 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		struct init_cb info = INIT_CB_INIT;
 
 		if (module_list_compute(argc, argv, opt.prefix,
-					&pathspec, &list) < 0)
-			return 1;
+					&pathspec, &list) < 0) {
+			ret = 1;
+			goto cleanup;
+		}
 
 		/*
 		 * If there are no path args and submodule.active is set then,
@@ -2638,7 +2660,9 @@ static int module_update(int argc, const char **argv, const char *prefix)
 	}
 
 	ret = update_submodules(&opt);
+cleanup:
 	list_objects_filter_release(&filter_options);
+	clear_pathspec(&pathspec);
 	return ret;
 }
 
@@ -2722,7 +2746,7 @@ static int push_check(int argc, const char **argv, const char *prefix)
 static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 {
 	int i;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;
 	struct option embed_gitdir_options[] = {
@@ -2737,17 +2761,21 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 		N_("git submodule absorbgitdirs [<options>] [<path>...]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, embed_gitdir_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	for (i = 0; i < list.nr; i++)
 		absorb_git_dir_into_superproject(list.entries[i]->name, flags);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static int module_config(int argc, const char **argv, const char *prefix)
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v5 04/17] submodule--helper: "struct pathspec" memory leak in module_update()
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                           ` (2 preceding siblings ...)
  2022-08-02 15:54         ` [PATCH v5 03/17] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54         ` Ævar Arnfjörð Bjarmason
  2022-08-02 15:54         ` [PATCH v5 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
                           ` (13 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

The module_update() function calls module_list_compute() twice, which
in turn will reset the "struct pathspec" passed to it. Let's instead
track two of them, and clear them both.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 92d32f2877f..dd9a62d139d 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2552,6 +2552,7 @@ static int update_submodules(struct update_data *update_data)
 static int module_update(int argc, const char **argv, const char *prefix)
 {
 	struct pathspec pathspec = { 0 };
+	struct pathspec pathspec2 = { 0 };
 	struct update_data opt = UPDATE_DATA_INIT;
 	struct list_objects_filter_options filter_options = { 0 };
 	int ret;
@@ -2640,7 +2641,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		struct init_cb info = INIT_CB_INIT;
 
 		if (module_list_compute(argc, argv, opt.prefix,
-					&pathspec, &list) < 0) {
+					&pathspec2, &list) < 0) {
 			ret = 1;
 			goto cleanup;
 		}
@@ -2663,6 +2664,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 cleanup:
 	list_objects_filter_release(&filter_options);
 	clear_pathspec(&pathspec);
+	clear_pathspec(&pathspec2);
 	return ret;
 }
 
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v5 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                           ` (3 preceding siblings ...)
  2022-08-02 15:54         ` [PATCH v5 04/17] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54         ` Ævar Arnfjörð Bjarmason
  2022-08-02 15:54         ` [PATCH v5 06/17] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
                           ` (12 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak in c51f8f94e5b (submodule--helper: run update
procedures from C, 2021-08-24) and 3c3558f0953 (submodule--helper: run
update using child process struct, 2022-03-15) by not allocating
memory in the first place.

The "dir" member of "struct child_process" will not be modified by
that API, and it's declared to be "const char *". So let's not
needlessly duplicate these strings.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index dd9a62d139d..3f79908b203 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2131,7 +2131,7 @@ static int is_tip_reachable(const char *path, struct object_id *oid)
 	char *hex = oid_to_hex(oid);
 
 	cp.git_cmd = 1;
-	cp.dir = xstrdup(path);
+	cp.dir = path;
 	cp.no_stderr = 1;
 	strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL);
 
@@ -2149,7 +2149,7 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str
 
 	prepare_submodule_repo_env(&cp.env);
 	cp.git_cmd = 1;
-	cp.dir = xstrdup(module_path);
+	cp.dir = module_path;
 
 	strvec_push(&cp.args, "fetch");
 	if (quiet)
@@ -2201,7 +2201,7 @@ static int run_update_command(struct update_data *ud, int subforce)
 	}
 	strvec_push(&cp.args, oid);
 
-	cp.dir = xstrdup(ud->sm_path);
+	cp.dir = ud->sm_path;
 	prepare_submodule_repo_env(&cp.env);
 	if (run_command(&cp)) {
 		int ret;
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v5 06/17] submodule--helper: add and use *_release() functions
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                           ` (4 preceding siblings ...)
  2022-08-02 15:54         ` [PATCH v5 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54         ` Ævar Arnfjörð Bjarmason
  2022-08-02 15:54         ` [PATCH v5 07/17] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
                           ` (11 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Add release functions for "struct module_list", "struct
submodule_update_clone" and "struct update_data".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c      | 28 +++++++++++++++++++++++++++-
 t/t6134-pathspec-in-submodule.sh |  1 +
 2 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 3f79908b203..8e9ef58f8be 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -160,6 +160,11 @@ struct module_list {
 };
 #define MODULE_LIST_INIT { 0 }
 
+static void module_list_release(struct module_list *ml)
+{
+	free(ml->entries);
+}
+
 static int module_list_compute(int argc, const char **argv,
 			       const char *prefix,
 			       struct pathspec *pathspec,
@@ -222,7 +227,7 @@ static void module_list_active(struct module_list *list)
 		active_modules.entries[active_modules.nr++] = ce;
 	}
 
-	free(list->entries);
+	module_list_release(list);
 	*list = active_modules;
 }
 
@@ -395,6 +400,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -560,6 +566,7 @@ static int module_init(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -737,6 +744,7 @@ static int module_status(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1309,6 +1317,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1461,6 +1470,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1836,6 +1846,12 @@ struct submodule_update_clone {
 };
 #define SUBMODULE_UPDATE_CLONE_INIT { 0 }
 
+static void submodule_update_clone_release(struct submodule_update_clone *suc)
+{
+	free(suc->update_clone);
+	free(suc->failed_clones);
+}
+
 struct update_data {
 	const char *prefix;
 	const char *displaypath;
@@ -1874,6 +1890,11 @@ struct update_data {
 	.max_jobs = 1, \
 }
 
+static void update_data_release(struct update_data *ud)
+{
+	module_list_release(&ud->list);
+}
+
 static void next_submodule_warn_missing(struct submodule_update_clone *suc,
 		struct strbuf *out, const char *displaypath)
 {
@@ -2545,6 +2566,7 @@ static int update_submodules(struct update_data *update_data)
 	}
 
 cleanup:
+	submodule_update_clone_release(&suc);
 	string_list_clear(&update_data->references, 0);
 	return ret;
 }
@@ -2642,6 +2664,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 
 		if (module_list_compute(argc, argv, opt.prefix,
 					&pathspec2, &list) < 0) {
+			module_list_release(&list);
 			ret = 1;
 			goto cleanup;
 		}
@@ -2658,10 +2681,12 @@ static int module_update(int argc, const char **argv, const char *prefix)
 			info.flags |= OPT_QUIET;
 
 		for_each_listed_submodule(&list, init_submodule_cb, &info);
+		module_list_release(&list);
 	}
 
 	ret = update_submodules(&opt);
 cleanup:
+	update_data_release(&opt);
 	list_objects_filter_release(&filter_options);
 	clear_pathspec(&pathspec);
 	clear_pathspec(&pathspec2);
@@ -2777,6 +2802,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 	ret = 0;
 cleanup:
 	clear_pathspec(&pathspec);
+	module_list_release(&list);
 	return ret;
 }
 
diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh
index 0f1cb49cedc..3a241f259de 100755
--- a/t/t6134-pathspec-in-submodule.sh
+++ b/t/t6134-pathspec-in-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='test case exclude pathspec'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a submodule' '
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v5 07/17] submodule--helper: fix "errmsg_str" memory leak
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                           ` (5 preceding siblings ...)
  2022-08-02 15:54         ` [PATCH v5 06/17] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54         ` Ævar Arnfjörð Bjarmason
  2022-08-02 15:54         ` [PATCH v5 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
                           ` (10 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak introduced in e83e3333b57 (submodule: port submodule
subcommand 'summary' from shell to C, 2020-08-13), we sometimes append
to the "errmsg", and need to free the "struct strbuf".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 8e9ef58f8be..68bf0f9178f 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -980,6 +980,7 @@ static void generate_submodule_summary(struct summary_cb *info,
 	free(displaypath);
 	free(src_abbrev);
 	free(dst_abbrev);
+	strbuf_release(&errmsg);
 }
 
 static void prepare_submodule_summary(struct summary_cb *info,
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v5 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                           ` (6 preceding siblings ...)
  2022-08-02 15:54         ` [PATCH v5 07/17] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54         ` Ævar Arnfjörð Bjarmason
  2022-08-03 23:10           ` Glen Choo
  2022-08-02 15:54         ` [PATCH v5 09/17] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
                           ` (9 subsequent siblings)
  17 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix leaks in "struct module_cb_list" and the "struct module_cb" which
it contains, these fixes leaks in e83e3333b57 (submodule: port
submodule subcommand 'summary' from shell to C, 2020-08-13).

The "sm_path" should always have been a "char *", not a "const
char *", we always create it with xstrdup().

We can't mark any tests passing passing with SANITIZE=leak using
"TEST_PASSES_SANITIZE_LEAK=true" as a result of this change, but
"t7401-submodule-summary.sh" gets closer to passing as a result of
this change.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 68bf0f9178f..f332627d19e 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -755,16 +755,34 @@ struct module_cb {
 	struct object_id oid_src;
 	struct object_id oid_dst;
 	char status;
-	const char *sm_path;
+	char *sm_path;
 };
 #define MODULE_CB_INIT { 0 }
 
+static void module_cb_release(struct module_cb *mcb)
+{
+	free(mcb->sm_path);
+}
+
 struct module_cb_list {
 	struct module_cb **entries;
 	int alloc, nr;
 };
 #define MODULE_CB_LIST_INIT { 0 }
 
+static void module_cb_list_release(struct module_cb_list *mcbl)
+{
+	int i;
+
+	for (i = 0; i < mcbl->nr; i++) {
+		struct module_cb *mcb = mcbl->entries[i];
+
+		module_cb_release(mcb);
+		free(mcb);
+	}
+	free(mcbl->entries);
+}
+
 struct summary_cb {
 	int argc;
 	const char **argv;
@@ -1108,6 +1126,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 cleanup:
 	strvec_clear(&diff_args);
 	release_revisions(&rev);
+	module_cb_list_release(&list);
 	return ret;
 }
 
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v5 09/17] submodule--helper: fix a leak with repo_clear()
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                           ` (7 preceding siblings ...)
  2022-08-02 15:54         ` [PATCH v5 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54         ` Ævar Arnfjörð Bjarmason
  2022-08-02 15:54         ` [PATCH v5 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
                           ` (8 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Call repo_clear() in ensure_core_worktree() to free the "struct
repository". Fixes a leak that's been here since
74d4731da1f (submodule--helper: replace connect-gitdir-workingtree by
ensure-core-worktree, 2018-08-13).

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f332627d19e..4e964a2c29c 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2396,6 +2396,7 @@ static int ensure_core_worktree(const char *path)
 		strbuf_release(&sb);
 	}
 
+	repo_clear(&subrepo);
 	return 0;
 }
 
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v5 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule()
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                           ` (8 preceding siblings ...)
  2022-08-02 15:54         ` [PATCH v5 09/17] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54         ` Ævar Arnfjörð Bjarmason
  2022-08-02 15:54         ` [PATCH v5 11/17] submodule--helper: fix "reference" leak Ævar Arnfjörð Bjarmason
                           ` (7 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak in the get_default_remote_submodule() function added
in a77c3fcb5ec (submodule--helper: get remote names from any
repository, 2022-03-04), we need to repo_clear() the submodule we
initialize.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 4e964a2c29c..902349976da 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -62,9 +62,13 @@ static char *repo_get_default_remote(struct repository *repo)
 static char *get_default_remote_submodule(const char *module_path)
 {
 	struct repository subrepo;
+	char *ret;
 
 	repo_submodule_init(&subrepo, the_repository, module_path, null_oid());
-	return repo_get_default_remote(&subrepo);
+	ret = repo_get_default_remote(&subrepo);
+	repo_clear(&subrepo);
+
+	return ret;
 }
 
 static char *get_default_remote(void)
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v5 11/17] submodule--helper: fix "reference" leak
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                           ` (9 preceding siblings ...)
  2022-08-02 15:54         ` [PATCH v5 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54         ` Ævar Arnfjörð Bjarmason
  2022-08-02 15:54         ` [PATCH v5 12/17] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
                           ` (6 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix leaks in the "reference" variable declared in add_submodule() and
module_clone().

In preceding commits this variable was refactored out of the "struct
module_clone_data", but the leak has been with us since
31224cbdc72 (clone: recursive and reference option triggers submodule
alternates, 2016-08-17) and 8c8195e9c3e (submodule--helper: introduce
add-clone subcommand, 2021-07-10).

Those commits added an xstrdup()'d member of the
STRING_LIST_INIT_NODUP'd "struct string_list". We need to free()
those, but not the ones we get from argv, let's make use of the "util"
member, if it has a pointer it's the pointer we'll need to free,
otherwise it'll be NULL (i.e. from argv).

Note that the free() of the "util" member is needed in both
module_clone() and add_submodule(). The module_clone() function itself
doesn't populate the "util" pointer as add_submodule() does, but
module_clone() is upstream of the
add_possible_reference_from_superproject() caller we're modifying
here, which does do that.

This does preclude the use of the "util" pointer for any other reasons
for now, but that's OK. If we ever need to use it for something else
we could turn it into a small "struct" with an optional "to_free"
member, and switch to using string_list_clear_func().

Alternatively we could have another "struct string_list to_free" which
would keep a copy of the strings we've dup'd to free(). But for now
this is perfectly adequate.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 902349976da..a5b0b3859c5 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1568,7 +1568,9 @@ static int add_possible_reference_from_superproject(
 
 		sm_alternate = compute_alternate_path(sb.buf, &err);
 		if (sm_alternate) {
-			string_list_append(sas->reference, xstrdup(sb.buf));
+			char *p = strbuf_detach(&sb, NULL);
+
+			string_list_append(sas->reference, p)->util = p;
 			free(sm_alternate);
 		} else {
 			switch (sas->error_mode) {
@@ -1800,6 +1802,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 
 	clone_submodule(&clone_data, &reference);
 	list_objects_filter_release(&filter_options);
+	string_list_clear(&reference, 1);
 	return 0;
 }
 
@@ -3033,6 +3036,7 @@ static int add_submodule(const struct add_data *add_data)
 	char *submod_gitdir_path;
 	struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
 	struct string_list reference = STRING_LIST_INIT_NODUP;
+	int ret = -1;
 
 	/* perhaps the path already exists and is already a git repo, else clone it */
 	if (is_directory(add_data->sm_path)) {
@@ -3088,15 +3092,17 @@ static int add_submodule(const struct add_data *add_data)
 		clone_data.url = add_data->realrepo;
 		clone_data.quiet = add_data->quiet;
 		clone_data.progress = add_data->progress;
-		if (add_data->reference_path)
-			string_list_append(&reference,
-					   xstrdup(add_data->reference_path));
+		if (add_data->reference_path) {
+			char *p = xstrdup(add_data->reference_path);
+
+			string_list_append(&reference, p)->util = p;
+		}
 		clone_data.dissociate = add_data->dissociate;
 		if (add_data->depth >= 0)
 			clone_data.depth = xstrfmt("%d", add_data->depth);
 
 		if (clone_submodule(&clone_data, &reference))
-			return -1;
+			goto cleanup;
 
 		prepare_submodule_repo_env(&cp.env);
 		cp.git_cmd = 1;
@@ -3115,7 +3121,10 @@ static int add_submodule(const struct add_data *add_data)
 		if (run_command(&cp))
 			die(_("unable to checkout submodule '%s'"), add_data->sm_path);
 	}
-	return 0;
+	ret = 0;
+cleanup:
+	string_list_clear(&reference, 1);
+	return ret;
 }
 
 static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v5 12/17] submodule--helper: fix obscure leak in module_add()
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                           ` (10 preceding siblings ...)
  2022-08-02 15:54         ` [PATCH v5 11/17] submodule--helper: fix "reference" leak Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54         ` Ævar Arnfjörð Bjarmason
  2022-08-02 15:54         ` [PATCH v5 13/17] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
                           ` (5 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix an obscure leak in module_add(), if the "git add" command we were
piping to failed we'd fail to strbuf_release(&sb). This fixes a leak
introduced in a6226fd772b (submodule--helper: convert the bulk of
cmd_add() to C, 2021-08-10).

In fixing it move to a "goto cleanup" pattern, and since we need to
introduce a "ret" variable to do that let's also get rid of the
intermediate "exit_code" variable. The initialization to "-1" in
a6226fd772b has always been redundant, we'd only use the "exit_code"
value after assigning the return value of pipe_command() to it.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index a5b0b3859c5..9cc92b4da9e 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3284,6 +3284,8 @@ static int module_add(int argc, const char **argv, const char *prefix)
 		N_("git submodule add [<options>] [--] <repository> [<path>]"),
 		NULL
 	};
+	struct strbuf sb = STRBUF_INIT;
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 
@@ -3333,21 +3335,17 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	die_on_repo_without_commits(add_data.sm_path);
 
 	if (!force) {
-		int exit_code = -1;
-		struct strbuf sb = STRBUF_INIT;
 		struct child_process cp = CHILD_PROCESS_INIT;
 
 		cp.git_cmd = 1;
 		cp.no_stdout = 1;
 		strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
 			     "--no-warn-embedded-repo", add_data.sm_path, NULL);
-		if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
+		if ((ret = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
 			strbuf_complete_line(&sb);
 			fputs(sb.buf, stderr);
-			free(add_data.sm_path);
-			return exit_code;
+			goto cleanup;
 		}
-		strbuf_release(&sb);
 	}
 
 	if(!add_data.sm_name)
@@ -3362,15 +3360,17 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	add_data.progress = !!progress;
 	add_data.dissociate = !!dissociate;
 
-	if (add_submodule(&add_data)) {
-		free(add_data.sm_path);
-		return 1;
-	}
+	if (add_submodule(&add_data))
+		goto cleanup;
 	configure_added_submodule(&add_data);
+
+	ret = 0;
+cleanup:
 	free(add_data.sm_path);
 	free(to_free);
+	strbuf_release(&sb);
 
-	return 0;
+	return ret;
 }
 
 #define SUPPORT_SUPER_PREFIX (1<<0)
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v5 13/17] submodule--helper: fix a leak in module_add()
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                           ` (11 preceding siblings ...)
  2022-08-02 15:54         ` [PATCH v5 12/17] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54         ` Ævar Arnfjörð Bjarmason
  2022-08-02 15:54         ` [PATCH v5 14/17] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
                           ` (4 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in module_path(), since a6226fd772b (submodule--helper:
convert the bulk of cmd_add() to C, 2021-08-10), we've been freeing
add_data.sm_path, but in this case we clobbered it, and didn't free
the value we clobbered.

This makes test 28 of "t/t7400-submodule-basic.sh" ("submodule add in
subdirectory") pass when we're compiled with SANITIZE=leak..

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 9cc92b4da9e..2fca6d66bb5 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3305,8 +3305,12 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	else
 		add_data.sm_path = xstrdup(argv[1]);
 
-	if (prefix && *prefix && !is_absolute_path(add_data.sm_path))
-		add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
+	if (prefix && *prefix && !is_absolute_path(add_data.sm_path)) {
+		char *sm_path = add_data.sm_path;
+
+		add_data.sm_path = xstrfmt("%s%s", prefix, sm_path);
+		free(sm_path);
+	}
 
 	if (starts_with_dot_dot_slash(add_data.repo) ||
 	    starts_with_dot_slash(add_data.repo)) {
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v5 14/17] submodule--helper: fix a memory leak in print_status()
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                           ` (12 preceding siblings ...)
  2022-08-02 15:54         ` [PATCH v5 13/17] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54         ` Ævar Arnfjörð Bjarmason
  2022-08-02 15:54         ` [PATCH v5 15/17] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
                           ` (3 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in print_status(), the compute_rev_name() function
implemented in this file will return a strbuf_detach()'d value, or
NULL.

This leak has existed since this code was added in
a9f8a37584a (submodule: port submodule subcommand 'status' from shell
to C, 2017-10-06), but in 0b5e2ea7cf3 (submodule--helper: don't print
null in 'submodule status', 2018-04-18) we added a "const"
intermediate variable for the return value, that "const" should be
removed.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 2fca6d66bb5..0f1846021e6 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -590,10 +590,11 @@ static void print_status(unsigned int flags, char state, const char *path,
 	printf("%c%s %s", state, oid_to_hex(oid), displaypath);
 
 	if (state == ' ' || state == '+') {
-		const char *name = compute_rev_name(path, oid_to_hex(oid));
+		char *name = compute_rev_name(path, oid_to_hex(oid));
 
 		if (name)
 			printf(" (%s)", name);
+		free(name);
 	}
 
 	printf("\n");
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v5 15/17] submodule--helper: free some "displaypath" in "struct update_data"
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                           ` (13 preceding siblings ...)
  2022-08-02 15:54         ` [PATCH v5 14/17] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54         ` Ævar Arnfjörð Bjarmason
  2022-08-02 15:54         ` [PATCH v5 16/17] submodule--helper: free rest of " Ævar Arnfjörð Bjarmason
                           ` (2 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Make the update_data_release() function free "displaypath" member when
appropriate. The "displaypath" member is always ours, the "const" on
the "char *" was wrong to begin with.

This leaves a leak of "displaypath" in update_submodule(), which as
we'll see in subsequent commits is harder to deal with than this
trivial fix.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c        | 3 ++-
 t/t2403-worktree-move.sh           | 1 +
 t/t7412-submodule-absorbgitdirs.sh | 1 +
 3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 0f1846021e6..79eca6c439b 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1882,7 +1882,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)
 
 struct update_data {
 	const char *prefix;
-	const char *displaypath;
+	char *displaypath;
 	enum submodule_update_type update_default;
 	struct object_id suboid;
 	struct string_list references;
@@ -1920,6 +1920,7 @@ struct update_data {
 
 static void update_data_release(struct update_data *ud)
 {
+	free(ud->displaypath);
 	module_list_release(&ud->list);
 }
 
diff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh
index a4e1a178e0a..1168e9f9982 100755
--- a/t/t2403-worktree-move.sh
+++ b/t/t2403-worktree-move.sh
@@ -2,6 +2,7 @@
 
 test_description='test git worktree move, remove, lock and unlock'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh
index 1cfa150768d..2859695c6d2 100755
--- a/t/t7412-submodule-absorbgitdirs.sh
+++ b/t/t7412-submodule-absorbgitdirs.sh
@@ -6,6 +6,7 @@ This test verifies that `git submodue absorbgitdirs` moves a submodules git
 directory into the superproject.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a real submodule' '
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v5 16/17] submodule--helper: free rest of "displaypath" in "struct update_data"
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                           ` (14 preceding siblings ...)
  2022-08-02 15:54         ` [PATCH v5 15/17] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54         ` Ævar Arnfjörð Bjarmason
  2022-08-04 18:04           ` Glen Choo
  2022-08-02 15:54         ` [PATCH v5 17/17] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  17 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in code added in c51f8f94e5b (submodule--helper: run update
procedures from C, 2021-08-24), we clobber the "displaypath" member of
the passed-in "struct update_data" both so that die() messages in this
update_submodule() function itself can use it, and for the
run_update_procedure() called within this function.

To make managing that clobbering easier let's wrap the
update_submodule() in a new update_submodule_outer() function, which
will do the clobbering and free(to_free) dance for us.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 27 +++++++++++++++++++--------
 1 file changed, 19 insertions(+), 8 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 79eca6c439b..cc8f42ae6df 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2482,13 +2482,6 @@ static int update_submodule(struct update_data *update_data)
 {
 	int ret;
 
-	ret = ensure_core_worktree(update_data->sm_path);
-	if (ret)
-		return ret;
-
-	update_data->displaypath = get_submodule_displaypath(
-		update_data->sm_path, update_data->prefix);
-
 	ret = determine_submodule_update_strategy(the_repository,
 						  update_data->just_cloned,
 						  update_data->sm_path,
@@ -2554,6 +2547,24 @@ static int update_submodule(struct update_data *update_data)
 	return 0;
 }
 
+static int update_submodule_outer(struct update_data *update_data)
+{
+	char *to_free, *restore = update_data->displaypath;
+	int ret;
+
+	ensure_core_worktree(update_data->sm_path);
+
+	update_data->displaypath = to_free = get_submodule_displaypath(
+		update_data->sm_path, update_data->prefix);
+
+	ret = update_submodule(update_data);
+
+	free(to_free);
+	update_data->displaypath = restore;
+
+	return ret;
+}
+
 static int update_submodules(struct update_data *update_data)
 {
 	int i, ret = 0;
@@ -2586,7 +2597,7 @@ static int update_submodules(struct update_data *update_data)
 		update_data->just_cloned = ucd.just_cloned;
 		update_data->sm_path = ucd.sub->path;
 
-		code = update_submodule(update_data);
+		code = update_submodule_outer(update_data);
 		if (code)
 			ret = code;
 		if (code == 128)
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v5 17/17] submodule--helper: fix a configure_added_submodule() leak
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                           ` (15 preceding siblings ...)
  2022-08-02 15:54         ` [PATCH v5 16/17] submodule--helper: free rest of " Ævar Arnfjörð Bjarmason
@ 2022-08-02 15:54         ` Ævar Arnfjörð Bjarmason
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-02 15:54 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix config API a memory leak added in a452128a36c (submodule--helper:
introduce add-config subcommand, 2021-08-06) by using the *_tmp()
variant of git_config_get_string().

In this case we're only checking whether
the (repo|git)_config_get_string() call is telling us that the
"submodule.active" key exists.

As with the preceding commit we'll find many other such patterns in
the codebase if we go fishing. E.g. "git gc" leaks in the code added
in 61f7a383d3b (maintenance: use 'incremental' strategy by default,
2020-10-15). Similar code in "git gc" added in
b08ff1fee00 (maintenance: add --schedule option and config,
2020-09-11) doesn't leak, but we could avoid the malloc() & free() in
that case.

A coccinelle rule to find those would find and fix some leaks, and
cases where we're doing needless malloc() + free()'s but only care
about the key existence, or are copying
the (repo|git)_config_get_string() return value right away.

But as with the preceding commit let's punt on all of that for now,
and just narrowly fix this specific case in submodule--helper.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c    | 4 ++--
 t/t7413-submodule-is-active.sh | 1 +
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index cc8f42ae6df..60757e871e6 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3158,7 +3158,7 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con
 static void configure_added_submodule(struct add_data *add_data)
 {
 	char *key;
-	char *val = NULL;
+	const char *val;
 	struct child_process add_submod = CHILD_PROCESS_INIT;
 	struct child_process add_gitmodules = CHILD_PROCESS_INIT;
 
@@ -3203,7 +3203,7 @@ static void configure_added_submodule(struct add_data *add_data)
 	 * is_submodule_active(), since that function needs to find
 	 * out the value of "submodule.active" again anyway.
 	 */
-	if (!git_config_get_string("submodule.active", &val)) {
+	if (!git_config_get_string_tmp("submodule.active", &val)) {
 		/*
 		 * If the submodule being added isn't already covered by the
 		 * current configured pathspec, set the submodule's active flag
diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh
index ede6f02dbd5..4dc7d089423 100755
--- a/t/t7413-submodule-is-active.sh
+++ b/t/t7413-submodule-is-active.sh
@@ -9,6 +9,7 @@ This is a unit test of the submodule.c is_submodule_active() function,
 which is also indirectly tested elsewhere.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-- 
2.37.1.1233.ge8b09efaedc


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* Re: [PATCH v5 03/17] submodule--helper: fix most "struct pathspec" memory leaks
  2022-08-02 15:54         ` [PATCH v5 03/17] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
@ 2022-08-03 22:59           ` Glen Choo
  2022-08-04  7:04             ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 186+ messages in thread
From: Glen Choo @ 2022-08-03 22:59 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> Call clear_pathspec() at the end of various functions that work with
> and allocate a "struct pathspec".
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 74 +++++++++++++++++++++++++------------
>  1 file changed, 51 insertions(+), 23 deletions(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index d958da7dddc..92d32f2877f 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -367,7 +367,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
>  static int module_foreach(int argc, const char **argv, const char *prefix)
>  {
>  	struct foreach_cb info = FOREACH_CB_INIT;
> -	struct pathspec pathspec;
> +	struct pathspec pathspec = { 0 };

Out of curiousity, does this zero-initialization do anything for us
leaks-wise?

>  	struct module_list list = MODULE_LIST_INIT;
>  	struct option module_foreach_options[] = {
>  		OPT__QUIET(&info.quiet, N_("suppress output of entering each submodule command")),
> @@ -379,12 +379,13 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
>  		N_("git submodule foreach [--quiet] [--recursive] [--] <command>"),
>  		NULL
>  	};
> +	int ret = 1;
>  
>  	argc = parse_options(argc, argv, prefix, module_foreach_options,
>  			     git_submodule_helper_usage, 0);
>  
>  	if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
> -		return 1;
> +		goto cleanup;
>  
>  	info.argc = argc;
>  	info.argv = argv;
> @@ -392,7 +393,10 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
>  
>  	for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);
>  
> -	return 0;
> +	ret = 0;
> +cleanup:
> +	clear_pathspec(&pathspec);
> +	return ret;
>  }
>  
>  static int starts_with_dot_slash(const char *const path)
> @@ -522,7 +526,7 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data
>  static int module_init(int argc, const char **argv, const char *prefix)
>  {
>  	struct init_cb info = INIT_CB_INIT;
> -	struct pathspec pathspec;
> +	struct pathspec pathspec = { 0 };
>  	struct module_list list = MODULE_LIST_INIT;
>  	int quiet = 0;
>  	struct option module_init_options[] = {
> @@ -533,12 +537,13 @@ static int module_init(int argc, const char **argv, const char *prefix)
>  		N_("git submodule init [<options>] [<path>]"),
>  		NULL
>  	};
> +	int ret = 1;
>  
>  	argc = parse_options(argc, argv, prefix, module_init_options,
>  			     git_submodule_helper_usage, 0);
>  
>  	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
> -		return 1;
> +		goto cleanup;
>  
>  	/*
>  	 * If there are no path args and submodule.active is set then,
> @@ -553,7 +558,10 @@ static int module_init(int argc, const char **argv, const char *prefix)
>  
>  	for_each_listed_submodule(&list, init_submodule_cb, &info);
>  
> -	return 0;
> +	ret = 0;
> +cleanup:
> +	clear_pathspec(&pathspec);
> +	return ret;
>  }
>  
>  struct status_cb {
> @@ -700,7 +708,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,
>  static int module_status(int argc, const char **argv, const char *prefix)
>  {
>  	struct status_cb info = STATUS_CB_INIT;
> -	struct pathspec pathspec;
> +	struct pathspec pathspec = { 0 };
>  	struct module_list list = MODULE_LIST_INIT;
>  	int quiet = 0;
>  	struct option module_status_options[] = {
> @@ -713,12 +721,13 @@ static int module_status(int argc, const char **argv, const char *prefix)
>  		N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"),
>  		NULL
>  	};
> +	int ret = 1;
>  
>  	argc = parse_options(argc, argv, prefix, module_status_options,
>  			     git_submodule_helper_usage, 0);
>  
>  	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
> -		return 1;
> +		goto cleanup;
>  
>  	info.prefix = prefix;
>  	if (quiet)
> @@ -726,7 +735,10 @@ static int module_status(int argc, const char **argv, const char *prefix)
>  
>  	for_each_listed_submodule(&list, status_submodule_cb, &info);
>  
> -	return 0;
> +	ret = 0;
> +cleanup:
> +	clear_pathspec(&pathspec);
> +	return ret;
>  }
>  
>  struct module_cb {
> @@ -1265,7 +1277,7 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data
>  static int module_sync(int argc, const char **argv, const char *prefix)
>  {
>  	struct sync_cb info = SYNC_CB_INIT;
> -	struct pathspec pathspec;
> +	struct pathspec pathspec = { 0 };
>  	struct module_list list = MODULE_LIST_INIT;
>  	int quiet = 0;
>  	int recursive = 0;
> @@ -1279,12 +1291,13 @@ static int module_sync(int argc, const char **argv, const char *prefix)
>  		N_("git submodule sync [--quiet] [--recursive] [<path>]"),
>  		NULL
>  	};
> +	int ret = 1;
>  
>  	argc = parse_options(argc, argv, prefix, module_sync_options,
>  			     git_submodule_helper_usage, 0);
>  
>  	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
> -		return 1;
> +		goto cleanup;
>  
>  	info.prefix = prefix;
>  	if (quiet)
> @@ -1294,7 +1307,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)
>  
>  	for_each_listed_submodule(&list, sync_submodule_cb, &info);
>  
> -	return 0;
> +	ret = 0;
> +cleanup:
> +	clear_pathspec(&pathspec);
> +	return ret;
>  }
>  
>  struct deinit_cb {
> @@ -1403,7 +1419,7 @@ static void deinit_submodule_cb(const struct cache_entry *list_item,
>  static int module_deinit(int argc, const char **argv, const char *prefix)
>  {
>  	struct deinit_cb info = DEINIT_CB_INIT;
> -	struct pathspec pathspec;
> +	struct pathspec pathspec = { 0 };
>  	struct module_list list = MODULE_LIST_INIT;
>  	int quiet = 0;
>  	int force = 0;
> @@ -1418,6 +1434,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
>  		N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
>  		NULL
>  	};
> +	int ret = 1;
>  
>  	argc = parse_options(argc, argv, prefix, module_deinit_options,
>  			     git_submodule_helper_usage, 0);
> @@ -1432,7 +1449,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
>  		die(_("Use '--all' if you really want to deinitialize all submodules"));
>  
>  	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
> -		return 1;
> +		goto cleanup;
>  
>  	info.prefix = prefix;
>  	if (quiet)
> @@ -1442,7 +1459,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
>  
>  	for_each_listed_submodule(&list, deinit_submodule_cb, &info);
>  
> -	return 0;
> +	ret = 0;
> +cleanup:
> +	clear_pathspec(&pathspec);
> +	return ret;
>  }
>  
>  struct module_clone_data {
> @@ -2531,7 +2551,7 @@ static int update_submodules(struct update_data *update_data)
>  
>  static int module_update(int argc, const char **argv, const char *prefix)
>  {
> -	struct pathspec pathspec;
> +	struct pathspec pathspec = { 0 };
>  	struct update_data opt = UPDATE_DATA_INIT;
>  	struct list_objects_filter_options filter_options = { 0 };
>  	int ret;
> @@ -2608,8 +2628,8 @@ static int module_update(int argc, const char **argv, const char *prefix)
>  		opt.update_strategy.type = opt.update_default;
>  
>  	if (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {
> -		list_objects_filter_release(&filter_options);
> -		return 1;
> +		ret = 1;
> +		goto cleanup;
>  	}
>  
>  	if (pathspec.nr)
> @@ -2620,8 +2640,10 @@ static int module_update(int argc, const char **argv, const char *prefix)
>  		struct init_cb info = INIT_CB_INIT;
>  
>  		if (module_list_compute(argc, argv, opt.prefix,
> -					&pathspec, &list) < 0)
> -			return 1;
> +					&pathspec, &list) < 0) {
> +			ret = 1;
> +			goto cleanup;
> +		}
>  
>  		/*
>  		 * If there are no path args and submodule.active is set then,
> @@ -2638,7 +2660,9 @@ static int module_update(int argc, const char **argv, const char *prefix)
>  	}
>  
>  	ret = update_submodules(&opt);
> +cleanup:
>  	list_objects_filter_release(&filter_options);
> +	clear_pathspec(&pathspec);
>  	return ret;
>  }
>  
> @@ -2722,7 +2746,7 @@ static int push_check(int argc, const char **argv, const char *prefix)
>  static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
>  {
>  	int i;
> -	struct pathspec pathspec;
> +	struct pathspec pathspec = { 0 };
>  	struct module_list list = MODULE_LIST_INIT;
>  	unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;
>  	struct option embed_gitdir_options[] = {
> @@ -2737,17 +2761,21 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
>  		N_("git submodule absorbgitdirs [<options>] [<path>...]"),
>  		NULL
>  	};
> +	int ret = 1;
>  
>  	argc = parse_options(argc, argv, prefix, embed_gitdir_options,
>  			     git_submodule_helper_usage, 0);
>  
>  	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
> -		return 1;
> +		goto cleanup;
>  
>  	for (i = 0; i < list.nr; i++)
>  		absorb_git_dir_into_superproject(list.entries[i]->name, flags);
>  
> -	return 0;
> +	ret = 0;
> +cleanup:
> +	clear_pathspec(&pathspec);
> +	return ret;
>  }
>  
>  static int module_config(int argc, const char **argv, const char *prefix)
> -- 
> 2.37.1.1233.ge8b09efaedc

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v5 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks
  2022-08-02 15:54         ` [PATCH v5 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
@ 2022-08-03 23:10           ` Glen Choo
  0 siblings, 0 replies; 186+ messages in thread
From: Glen Choo @ 2022-08-03 23:10 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> Fix leaks in "struct module_cb_list" and the "struct module_cb" which
> it contains, these fixes leaks in e83e3333b57 (submodule: port

s/fixes/fix

The diff itself LGTM.

> submodule subcommand 'summary' from shell to C, 2020-08-13).
>
> The "sm_path" should always have been a "char *", not a "const
> char *", we always create it with xstrdup().
>
> We can't mark any tests passing passing with SANITIZE=leak using
> "TEST_PASSES_SANITIZE_LEAK=true" as a result of this change, but
> "t7401-submodule-summary.sh" gets closer to passing as a result of
> this change.
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 21 ++++++++++++++++++++-
>  1 file changed, 20 insertions(+), 1 deletion(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 68bf0f9178f..f332627d19e 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -755,16 +755,34 @@ struct module_cb {
>  	struct object_id oid_src;
>  	struct object_id oid_dst;
>  	char status;
> -	const char *sm_path;
> +	char *sm_path;
>  };
>  #define MODULE_CB_INIT { 0 }
>  
> +static void module_cb_release(struct module_cb *mcb)
> +{
> +	free(mcb->sm_path);
> +}
> +
>  struct module_cb_list {
>  	struct module_cb **entries;
>  	int alloc, nr;
>  };
>  #define MODULE_CB_LIST_INIT { 0 }
>  
> +static void module_cb_list_release(struct module_cb_list *mcbl)
> +{
> +	int i;
> +
> +	for (i = 0; i < mcbl->nr; i++) {
> +		struct module_cb *mcb = mcbl->entries[i];
> +
> +		module_cb_release(mcb);
> +		free(mcb);
> +	}
> +	free(mcbl->entries);
> +}
> +
>  struct summary_cb {
>  	int argc;
>  	const char **argv;
> @@ -1108,6 +1126,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
>  cleanup:
>  	strvec_clear(&diff_args);
>  	release_revisions(&rev);
> +	module_cb_list_release(&list);
>  	return ret;
>  }
>  
> -- 
> 2.37.1.1233.ge8b09efaedc

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v5 03/17] submodule--helper: fix most "struct pathspec" memory leaks
  2022-08-03 22:59           ` Glen Choo
@ 2022-08-04  7:04             ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-04  7:04 UTC (permalink / raw)
  To: Glen Choo; +Cc: git, Junio C Hamano, Atharva Raykar, Prathamesh Chavan


On Wed, Aug 03 2022, Glen Choo wrote:

> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
>
>> Call clear_pathspec() at the end of various functions that work with
>> and allocate a "struct pathspec".
>>
>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
>> ---
>>  builtin/submodule--helper.c | 74 +++++++++++++++++++++++++------------
>>  1 file changed, 51 insertions(+), 23 deletions(-)
>>
>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
>> index d958da7dddc..92d32f2877f 100644
>> --- a/builtin/submodule--helper.c
>> +++ b/builtin/submodule--helper.c
>> @@ -367,7 +367,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
>>  static int module_foreach(int argc, const char **argv, const char *prefix)
>>  {
>>  	struct foreach_cb info = FOREACH_CB_INIT;
>> -	struct pathspec pathspec;
>> +	struct pathspec pathspec = { 0 };
>
> Out of curiousity, does this zero-initialization do anything for us
> leaks-wise?

No, because if we leak module_list_compute() must have filled the
pathspec with something it allocated, but...

>>  	struct module_list list = MODULE_LIST_INIT;
>>  	struct option module_foreach_options[] = {
>>  		OPT__QUIET(&info.quiet, N_("suppress output of entering each submodule command")),
>> @@ -379,12 +379,13 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
>>  		N_("git submodule foreach [--quiet] [--recursive] [--] <command>"),
>>  		NULL
>>  	};
>> +	int ret = 1;
>>  
>>  	argc = parse_options(argc, argv, prefix, module_foreach_options,
>>  			     git_submodule_helper_usage, 0);
>>  
>>  	if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
>> -		return 1;
>> +		goto cleanup;

...if we don't initialize it then we can't "goto cleanup".

Now, right now this is redundant, we could just "return 1", as we don't
have a pathspec yet.

But it's generally worth just using the same "cleanup" pattern
everywhere, and not worrying about in your cleanunp code that you only
init'd N/TOTAL variables already.

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v5 16/17] submodule--helper: free rest of "displaypath" in "struct update_data"
  2022-08-02 15:54         ` [PATCH v5 16/17] submodule--helper: free rest of " Ævar Arnfjörð Bjarmason
@ 2022-08-04 18:04           ` Glen Choo
  0 siblings, 0 replies; 186+ messages in thread
From: Glen Choo @ 2022-08-04 18:04 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> Fix a leak in code added in c51f8f94e5b (submodule--helper: run update
> procedures from C, 2021-08-24), we clobber the "displaypath" member of
> the passed-in "struct update_data" both so that die() messages in this
> update_submodule() function itself can use it, and for the
> run_update_procedure() called within this function.
>
> To make managing that clobbering easier let's wrap the
> update_submodule() in a new update_submodule_outer() function, which
> will do the clobbering and free(to_free) dance for us.
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 27 +++++++++++++++++++--------
>  1 file changed, 19 insertions(+), 8 deletions(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 79eca6c439b..cc8f42ae6df 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -2482,13 +2482,6 @@ static int update_submodule(struct update_data *update_data)
>  {
>  	int ret;
>  
> -	ret = ensure_core_worktree(update_data->sm_path);
> -	if (ret)
> -		return ret;
> -
> -	update_data->displaypath = get_submodule_displaypath(
> -		update_data->sm_path, update_data->prefix);
> -
>  	ret = determine_submodule_update_strategy(the_repository,
>  						  update_data->just_cloned,
>  						  update_data->sm_path,
> @@ -2554,6 +2547,24 @@ static int update_submodule(struct update_data *update_data)
>  	return 0;
>  }
>  
> +static int update_submodule_outer(struct update_data *update_data)
> +{
> +	char *to_free, *restore = update_data->displaypath;
> +	int ret;
> +
> +	ensure_core_worktree(update_data->sm_path);
> +
> +	update_data->displaypath = to_free = get_submodule_displaypath(
> +		update_data->sm_path, update_data->prefix);
> +
> +	ret = update_submodule(update_data);
> +
> +	free(to_free);
> +	update_data->displaypath = restore;
> +
> +	return ret;
> +}
> +

I haven't tested the alternatives yet, but on first glance using this
*_outer() pattern here seems like overkill. At least two things I can
think of are:

a) Free .displaypath using the "goto cleanup" pattern like we did
   elsewhere

...

>  static int update_submodules(struct update_data *update_data)
>  {
>  	int i, ret = 0;
> @@ -2586,7 +2597,7 @@ static int update_submodules(struct update_data *update_data)
>  		update_data->just_cloned = ucd.just_cloned;
>  		update_data->sm_path = ucd.sub->path;
>  
> -		code = update_submodule(update_data);
> +		code = update_submodule_outer(update_data);
>  		if (code)
>  			ret = code;
>  		if (code == 128)

b) Assign and FREE_AND_NULL() update_data->displaypath here since this
   is the only caller and it already does some prep work in this hunk.

I started testing, but then realized that we don't have a
TEST_PASSES_SANITIZE_LEAK=true for this patch, so I'll spend some more
time testing whether these alternatives even work and I'll get back to
you later.

> -- 
> 2.37.1.1233.ge8b09efaedc

^ permalink raw reply	[flat|nested] 186+ messages in thread

* [PATCH v6 00/17] submodule--helper: fix memory leaks
  2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                           ` (16 preceding siblings ...)
  2022-08-02 15:54         ` [PATCH v5 17/17] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59         ` Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 01/17] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
                             ` (17 more replies)
  17 siblings, 18 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

This series fixes all of the memory leaks in
builtin/submodule--helper.c that our test suite spots, except for
those where the leak is downstream of cmd_submodule__helper() and
caused by code that's not in builtin/submodule--helper.c (e.g. leaks
in the object or config APIs, and in submodule.c).

This re-roll is on top of the just-now re-rolled v3 of the base
topic[1].

Changes in v6:

 * Rebase on top of the base topic.
 * Mark new tests as leak-free within this series (due to changes on
   "master")
 * Various minor changes, see range-diff below.

1. https://lore.kernel.org/git/cover-v3-00.32-00000000000-20220821T130231Z-avarab@gmail.com/

Ævar Arnfjörð Bjarmason (17):
  submodule--helper: fix a leak in "clone_submodule"
  submodule--helper: fix trivial get_default_remote_submodule() leak
  submodule--helper: fix most "struct pathspec" memory leaks
  submodule--helper: "struct pathspec" memory leak in module_update()
  submodule--helper: don't leak {run,capture}_command() cp.dir argument
  submodule--helper: add and use *_release() functions
  submodule--helper: fix "errmsg_str" memory leak
  submodule--helper: fix "sm_path" and other "module_cb_list" leaks
  submodule--helper: fix a leak with repo_clear()
  submodule--helper: fix a memory leak in get_default_remote_submodule()
  submodule--helper: fix "reference" leak
  submodule--helper: fix obscure leak in module_add()
  submodule--helper: fix a leak in module_add()
  submodule--helper: fix a memory leak in print_status()
  submodule--helper: free some "displaypath" in "struct update_data"
  submodule--helper: free rest of "displaypath" in "struct update_data"
  submodule--helper: fix a configure_added_submodule() leak

 builtin/submodule--helper.c        | 239 +++++++++++++++++++++--------
 t/t1500-rev-parse.sh               |   1 +
 t/t2403-worktree-move.sh           |   1 +
 t/t6008-rev-list-submodule.sh      |   1 +
 t/t6134-pathspec-in-submodule.sh   |   1 +
 t/t7401-submodule-summary.sh       |   1 +
 t/t7412-submodule-absorbgitdirs.sh |   1 +
 t/t7413-submodule-is-active.sh     |   1 +
 t/t7414-submodule-mistakes.sh      |   2 +
 t/t7419-submodule-set-branch.sh    |   1 +
 t/t7506-status-submodule.sh        |   1 +
 t/t7507-commit-verbose.sh          |   2 +
 12 files changed, 187 insertions(+), 65 deletions(-)

Range-diff against v5:
 1:  118e965d401 =  1:  0f60ea6f7c6 submodule--helper: fix a leak in "clone_submodule"
 2:  d885e1dd59a !  2:  c14e00f39cb submodule--helper: fix trivial get_default_remote_submodule() leak
    @@ Commit message
     
      ## builtin/submodule--helper.c ##
     @@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)
    - 		const char *branch = remote_submodule_branch(update_data->sm_path);
    - 		char *remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch);
    + 			return code;
    + 		remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch);
      
     +		free(remote_name);
     +
 3:  1f90348d61f !  3:  a2ecdb301d3 submodule--helper: fix most "struct pathspec" memory leaks
    @@ Commit message
         Call clear_pathspec() at the end of various functions that work with
         and allocate a "struct pathspec".
     
    +    In some cases the zero-initialization here isn't strictly needed, but
    +    as we're moving to a "goto cleanup" pattern let's make sure that it's
    +    safe to call clear_pathspec(), we don't want the data to be
    +    uninitialized.
    +
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/submodule--helper.c ##
 4:  f768ff2e34d =  4:  8fc3dad316e submodule--helper: "struct pathspec" memory leak in module_update()
 5:  509133c37fa !  5:  a3886d36366 submodule--helper: don't leak {run,capture}_command() cp.dir argument
    @@ builtin/submodule--helper.c: static int run_update_command(struct update_data *u
     -	cp.dir = xstrdup(ud->sm_path);
     +	cp.dir = ud->sm_path;
      	prepare_submodule_repo_env(&cp.env);
    - 	if (run_command(&cp)) {
    - 		int ret;
    + 	if ((ret = run_command(&cp))) {
    + 		switch (ud->update_strategy.type) {
 6:  25377f1d06c =  6:  173dbb7d8fc submodule--helper: add and use *_release() functions
 7:  1a85057cd0c =  7:  cd394814370 submodule--helper: fix "errmsg_str" memory leak
 8:  3c4f734e958 !  8:  2110b991913 submodule--helper: fix "sm_path" and other "module_cb_list" leaks
    @@ Commit message
         submodule--helper: fix "sm_path" and other "module_cb_list" leaks
     
         Fix leaks in "struct module_cb_list" and the "struct module_cb" which
    -    it contains, these fixes leaks in e83e3333b57 (submodule: port
    -    submodule subcommand 'summary' from shell to C, 2020-08-13).
    +    it contains, these fix leaks in e83e3333b57 (submodule: port submodule
    +    subcommand 'summary' from shell to C, 2020-08-13).
     
         The "sm_path" should always have been a "char *", not a "const
         char *", we always create it with xstrdup().
    @@ builtin/submodule--helper.c: static int compute_summary_module_list(struct objec
      	return ret;
      }
      
    +
    + ## t/t7401-submodule-summary.sh ##
    +@@ t/t7401-submodule-summary.sh: This test script tries to verify the sanity of summary subcommand of git submodu
    + # various reasons, one of them being that there are lots of commands taking place
    + # outside of 'test_expect_success' block, which is no longer in good-style.
    + 
    ++TEST_PASSES_SANITIZE_LEAK=true
    + . ./test-lib.sh
    + 
    + add_file () {
 9:  3aebff9f8e3 =  9:  a0d1a03166f submodule--helper: fix a leak with repo_clear()
10:  38345ec76bc ! 10:  84f162f320b submodule--helper: fix a memory leak in get_default_remote_submodule()
    @@ Commit message
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/submodule--helper.c ##
    -@@ builtin/submodule--helper.c: static char *repo_get_default_remote(struct repository *repo)
    - static char *get_default_remote_submodule(const char *module_path)
    +@@ builtin/submodule--helper.c: static int repo_get_default_remote(struct repository *repo, char **default_remot
    + static int get_default_remote_submodule(const char *module_path, char **default_remote)
      {
      	struct repository subrepo;
    -+	char *ret;
    ++	int ret;
      
    - 	repo_submodule_init(&subrepo, the_repository, module_path, null_oid());
    --	return repo_get_default_remote(&subrepo);
    -+	ret = repo_get_default_remote(&subrepo);
    + 	if (repo_submodule_init(&subrepo, the_repository, module_path,
    + 				null_oid()) < 0)
    + 		return die_message(_("could not get a repository handle for submodule '%s'"),
    + 				   module_path);
    +-	return repo_get_default_remote(&subrepo, default_remote);
    ++	ret = repo_get_default_remote(&subrepo, default_remote);
     +	repo_clear(&subrepo);
     +
     +	return ret;
11:  4b1e5ced969 = 11:  dc5e8504354 submodule--helper: fix "reference" leak
12:  e74c396073b = 12:  50d58bfb19b submodule--helper: fix obscure leak in module_add()
13:  71a56c59864 = 13:  56590508180 submodule--helper: fix a leak in module_add()
14:  2429db2f1c7 = 14:  38ceb0a36ab submodule--helper: fix a memory leak in print_status()
15:  613d077c4ec ! 15:  f3cf49dd029 submodule--helper: free some "displaypath" in "struct update_data"
    @@ t/t7412-submodule-absorbgitdirs.sh: This test verifies that `git submodue absorb
      . ./test-lib.sh
      
      test_expect_success 'setup a real submodule' '
    +
    + ## t/t7419-submodule-set-branch.sh ##
    +@@ t/t7419-submodule-set-branch.sh: This test verifies that the set-branch subcommand of git-submodule is working
    + as expected.
    + '
    + 
    ++TEST_PASSES_SANITIZE_LEAK=true
    + TEST_NO_CREATE_REPO=1
    + . ./test-lib.sh
    + 
16:  8f150a81507 ! 16:  a99a7736fab submodule--helper: free rest of "displaypath" in "struct update_data"
    @@ builtin/submodule--helper.c: static int update_submodules(struct update_data *up
      
     -		code = update_submodule(update_data);
     +		code = update_submodule_outer(update_data);
    - 		if (code)
    - 			ret = code;
    - 		if (code == 128)
    + 		if (!code)
    + 			continue;
    + 		ret = code;
17:  17c77ceba01 = 17:  ec0b02d8ee2 submodule--helper: fix a configure_added_submodule() leak
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply	[flat|nested] 186+ messages in thread

* [PATCH v6 01/17] submodule--helper: fix a leak in "clone_submodule"
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59           ` Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
                             ` (16 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak of the "clone_data_path" variable that we copy or
derive from the "struct module_clone_data" in clone_submodule(). This
code was refactored in preceding commits, but the leak has been with
us since f8eaa0ba98b (submodule--helper, module_clone: always operate
on absolute paths, 2016-03-31).

For the "else" case we don't need to xstrdup() the "clone_data->path",
and we don't need to free our own "clone_data_path". We can therefore
assign the "clone_data->path" to our own "clone_data_path" right away,
and only override it (and remember to free it!) if we need to
xstrfmt() a replacement.

In the case of the module_clone() caller it's from "argv", and doesn't
need to be free'd, and in the case of the add_submodule() caller we
get a pointer to "sm_path", which doesn't need to be directly free'd
either.

Fixing this leak makes several tests pass, so let's mark them as
passing with TEST_PASSES_SANITIZE_LEAK=true.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c   | 10 +++++-----
 t/t1500-rev-parse.sh          |  1 +
 t/t6008-rev-list-submodule.sh |  1 +
 t/t7414-submodule-mistakes.sh |  2 ++
 t/t7506-status-submodule.sh   |  1 +
 t/t7507-commit-verbose.sh     |  2 ++
 6 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index e489a0f6b10..2774f1884b0 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1590,13 +1590,12 @@ static int clone_submodule(const struct module_clone_data *clone_data,
 	char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);
 	char *sm_alternate = NULL, *error_strategy = NULL;
 	struct child_process cp = CHILD_PROCESS_INIT;
-	const char *clone_data_path;
+	const char *clone_data_path = clone_data->path;
+	char *to_free = NULL;
 
 	if (!is_absolute_path(clone_data->path))
-		clone_data_path = xstrfmt("%s/%s", get_git_work_tree(),
-					  clone_data->path);
-	else
-		clone_data_path = xstrdup(clone_data->path);
+		clone_data_path = to_free = xstrfmt("%s/%s", get_git_work_tree(),
+						    clone_data->path);
 
 	if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)
 		die(_("refusing to create/use '%s' in another submodule's "
@@ -1681,6 +1680,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,
 
 	free(sm_gitdir);
 	free(p);
+	free(to_free);
 	return 0;
 }
 
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 1c2df08333b..0e13bcb4ebb 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -4,6 +4,7 @@ test_description='test git rev-parse'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_one () {
diff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh
index 3153a0d8910..12e67e187ef 100755
--- a/t/t6008-rev-list-submodule.sh
+++ b/t/t6008-rev-list-submodule.sh
@@ -8,6 +8,7 @@ test_description='git rev-list involving submodules that this repo has'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh
index f2e7df59cf2..3269298197c 100755
--- a/t/t7414-submodule-mistakes.sh
+++ b/t/t7414-submodule-mistakes.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='handling of common mistakes people may make with submodules'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create embedded repository' '
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index 3fcb44767f5..f5426a8e589 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='git status for submodule'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_create_repo_with_commit () {
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index ed2653d46fe..92462a22374 100755
--- a/t/t7507-commit-verbose.sh
+++ b/t/t7507-commit-verbose.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='verbose commit template'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 write_script "check-for-diff" <<\EOF &&
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v6 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 01/17] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59           ` Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 03/17] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
                             ` (15 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in code added in 1012a5cbc3f (submodule--helper
run-update-procedure: learn --remote, 2022-03-04), we need to free()
the xstrdup()'d string. This gets e.g. t/t7419-submodule-set-branch.sh
closer to passing under SANITIZE=leak.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 2774f1884b0..da9e0016509 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2449,6 +2449,8 @@ static int update_submodule(struct update_data *update_data)
 			return code;
 		remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch);
 
+		free(remote_name);
+
 		if (!update_data->nofetch) {
 			if (fetch_in_submodule(update_data->sm_path, update_data->depth,
 					      0, NULL))
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v6 03/17] submodule--helper: fix most "struct pathspec" memory leaks
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 01/17] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59           ` Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 04/17] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
                             ` (14 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Call clear_pathspec() at the end of various functions that work with
and allocate a "struct pathspec".

In some cases the zero-initialization here isn't strictly needed, but
as we're moving to a "goto cleanup" pattern let's make sure that it's
safe to call clear_pathspec(), we don't want the data to be
uninitialized.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 74 +++++++++++++++++++++++++------------
 1 file changed, 51 insertions(+), 23 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index da9e0016509..a118f615783 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -379,7 +379,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
 static int module_foreach(int argc, const char **argv, const char *prefix)
 {
 	struct foreach_cb info = FOREACH_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	struct option module_foreach_options[] = {
 		OPT__QUIET(&info.quiet, N_("suppress output of entering each submodule command")),
@@ -391,12 +391,13 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 		N_("git submodule foreach [--quiet] [--recursive] [--] <command>"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_foreach_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.argc = argc;
 	info.argv = argv;
@@ -404,7 +405,10 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static int starts_with_dot_slash(const char *const path)
@@ -515,7 +519,7 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data
 static int module_init(int argc, const char **argv, const char *prefix)
 {
 	struct init_cb info = INIT_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	struct option module_init_options[] = {
@@ -526,12 +530,13 @@ static int module_init(int argc, const char **argv, const char *prefix)
 		N_("git submodule init [<options>] [<path>]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_init_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	/*
 	 * If there are no path args and submodule.active is set then,
@@ -546,7 +551,10 @@ static int module_init(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, init_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct status_cb {
@@ -693,7 +701,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,
 static int module_status(int argc, const char **argv, const char *prefix)
 {
 	struct status_cb info = STATUS_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	struct option module_status_options[] = {
@@ -706,12 +714,13 @@ static int module_status(int argc, const char **argv, const char *prefix)
 		N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_status_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.prefix = prefix;
 	if (quiet)
@@ -719,7 +728,10 @@ static int module_status(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, status_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct module_cb {
@@ -1261,7 +1273,7 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data
 static int module_sync(int argc, const char **argv, const char *prefix)
 {
 	struct sync_cb info = SYNC_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	int recursive = 0;
@@ -1275,12 +1287,13 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 		N_("git submodule sync [--quiet] [--recursive] [<path>]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_sync_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.prefix = prefix;
 	if (quiet)
@@ -1290,7 +1303,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, sync_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct deinit_cb {
@@ -1399,7 +1415,7 @@ static void deinit_submodule_cb(const struct cache_entry *list_item,
 static int module_deinit(int argc, const char **argv, const char *prefix)
 {
 	struct deinit_cb info = DEINIT_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	int force = 0;
@@ -1414,6 +1430,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 		N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_deinit_options,
 			     git_submodule_helper_usage, 0);
@@ -1428,7 +1445,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 		die(_("Use '--all' if you really want to deinitialize all submodules"));
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.prefix = prefix;
 	if (quiet)
@@ -1438,7 +1455,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, deinit_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct module_clone_data {
@@ -2541,7 +2561,7 @@ static int update_submodules(struct update_data *update_data)
 
 static int module_update(int argc, const char **argv, const char *prefix)
 {
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct update_data opt = UPDATE_DATA_INIT;
 	struct list_objects_filter_options filter_options = { 0 };
 	int ret;
@@ -2618,8 +2638,8 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		opt.update_strategy.type = opt.update_default;
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {
-		list_objects_filter_release(&filter_options);
-		return 1;
+		ret = 1;
+		goto cleanup;
 	}
 
 	if (pathspec.nr)
@@ -2630,8 +2650,10 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		struct init_cb info = INIT_CB_INIT;
 
 		if (module_list_compute(argc, argv, opt.prefix,
-					&pathspec, &list) < 0)
-			return 1;
+					&pathspec, &list) < 0) {
+			ret = 1;
+			goto cleanup;
+		}
 
 		/*
 		 * If there are no path args and submodule.active is set then,
@@ -2648,7 +2670,9 @@ static int module_update(int argc, const char **argv, const char *prefix)
 	}
 
 	ret = update_submodules(&opt);
+cleanup:
 	list_objects_filter_release(&filter_options);
+	clear_pathspec(&pathspec);
 	return ret;
 }
 
@@ -2732,7 +2756,7 @@ static int push_check(int argc, const char **argv, const char *prefix)
 static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 {
 	int i;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;
 	struct option embed_gitdir_options[] = {
@@ -2747,17 +2771,21 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 		N_("git submodule absorbgitdirs [<options>] [<path>...]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, embed_gitdir_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	for (i = 0; i < list.nr; i++)
 		absorb_git_dir_into_superproject(list.entries[i]->name, flags);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static int module_config(int argc, const char **argv, const char *prefix)
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v6 04/17] submodule--helper: "struct pathspec" memory leak in module_update()
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                             ` (2 preceding siblings ...)
  2022-08-21 13:59           ` [PATCH v6 03/17] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59           ` Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
                             ` (13 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

The module_update() function calls module_list_compute() twice, which
in turn will reset the "struct pathspec" passed to it. Let's instead
track two of them, and clear them both.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index a118f615783..64dc906b5e6 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2562,6 +2562,7 @@ static int update_submodules(struct update_data *update_data)
 static int module_update(int argc, const char **argv, const char *prefix)
 {
 	struct pathspec pathspec = { 0 };
+	struct pathspec pathspec2 = { 0 };
 	struct update_data opt = UPDATE_DATA_INIT;
 	struct list_objects_filter_options filter_options = { 0 };
 	int ret;
@@ -2650,7 +2651,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		struct init_cb info = INIT_CB_INIT;
 
 		if (module_list_compute(argc, argv, opt.prefix,
-					&pathspec, &list) < 0) {
+					&pathspec2, &list) < 0) {
 			ret = 1;
 			goto cleanup;
 		}
@@ -2673,6 +2674,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 cleanup:
 	list_objects_filter_release(&filter_options);
 	clear_pathspec(&pathspec);
+	clear_pathspec(&pathspec2);
 	return ret;
 }
 
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v6 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                             ` (3 preceding siblings ...)
  2022-08-21 13:59           ` [PATCH v6 04/17] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59           ` Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 06/17] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
                             ` (12 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak in c51f8f94e5b (submodule--helper: run update
procedures from C, 2021-08-24) and 3c3558f0953 (submodule--helper: run
update using child process struct, 2022-03-15) by not allocating
memory in the first place.

The "dir" member of "struct child_process" will not be modified by
that API, and it's declared to be "const char *". So let's not
needlessly duplicate these strings.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 64dc906b5e6..5eb6bec4c63 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2129,7 +2129,7 @@ static int is_tip_reachable(const char *path, struct object_id *oid)
 	char *hex = oid_to_hex(oid);
 
 	cp.git_cmd = 1;
-	cp.dir = xstrdup(path);
+	cp.dir = path;
 	cp.no_stderr = 1;
 	strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL);
 
@@ -2147,7 +2147,7 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str
 
 	prepare_submodule_repo_env(&cp.env);
 	cp.git_cmd = 1;
-	cp.dir = xstrdup(module_path);
+	cp.dir = module_path;
 
 	strvec_push(&cp.args, "fetch");
 	if (quiet)
@@ -2200,7 +2200,7 @@ static int run_update_command(struct update_data *ud, int subforce)
 	}
 	strvec_push(&cp.args, oid);
 
-	cp.dir = xstrdup(ud->sm_path);
+	cp.dir = ud->sm_path;
 	prepare_submodule_repo_env(&cp.env);
 	if ((ret = run_command(&cp))) {
 		switch (ud->update_strategy.type) {
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v6 06/17] submodule--helper: add and use *_release() functions
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                             ` (4 preceding siblings ...)
  2022-08-21 13:59           ` [PATCH v6 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59           ` Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 07/17] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
                             ` (11 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Add release functions for "struct module_list", "struct
submodule_update_clone" and "struct update_data".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c      | 28 +++++++++++++++++++++++++++-
 t/t6134-pathspec-in-submodule.sh |  1 +
 2 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 5eb6bec4c63..fa320eb2edf 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -172,6 +172,11 @@ struct module_list {
 };
 #define MODULE_LIST_INIT { 0 }
 
+static void module_list_release(struct module_list *ml)
+{
+	free(ml->entries);
+}
+
 static int module_list_compute(int argc, const char **argv,
 			       const char *prefix,
 			       struct pathspec *pathspec,
@@ -234,7 +239,7 @@ static void module_list_active(struct module_list *list)
 		active_modules.entries[active_modules.nr++] = ce;
 	}
 
-	free(list->entries);
+	module_list_release(list);
 	*list = active_modules;
 }
 
@@ -407,6 +412,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -553,6 +559,7 @@ static int module_init(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -730,6 +737,7 @@ static int module_status(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1305,6 +1313,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1457,6 +1466,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1834,6 +1844,12 @@ struct submodule_update_clone {
 };
 #define SUBMODULE_UPDATE_CLONE_INIT { 0 }
 
+static void submodule_update_clone_release(struct submodule_update_clone *suc)
+{
+	free(suc->update_clone);
+	free(suc->failed_clones);
+}
+
 struct update_data {
 	const char *prefix;
 	const char *displaypath;
@@ -1872,6 +1888,11 @@ struct update_data {
 	.max_jobs = 1, \
 }
 
+static void update_data_release(struct update_data *ud)
+{
+	module_list_release(&ud->list);
+}
+
 static void next_submodule_warn_missing(struct submodule_update_clone *suc,
 		struct strbuf *out, const char *displaypath)
 {
@@ -2555,6 +2576,7 @@ static int update_submodules(struct update_data *update_data)
 	}
 
 cleanup:
+	submodule_update_clone_release(&suc);
 	string_list_clear(&update_data->references, 0);
 	return ret;
 }
@@ -2652,6 +2674,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 
 		if (module_list_compute(argc, argv, opt.prefix,
 					&pathspec2, &list) < 0) {
+			module_list_release(&list);
 			ret = 1;
 			goto cleanup;
 		}
@@ -2668,10 +2691,12 @@ static int module_update(int argc, const char **argv, const char *prefix)
 			info.flags |= OPT_QUIET;
 
 		for_each_listed_submodule(&list, init_submodule_cb, &info);
+		module_list_release(&list);
 	}
 
 	ret = update_submodules(&opt);
 cleanup:
+	update_data_release(&opt);
 	list_objects_filter_release(&filter_options);
 	clear_pathspec(&pathspec);
 	clear_pathspec(&pathspec2);
@@ -2787,6 +2812,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 	ret = 0;
 cleanup:
 	clear_pathspec(&pathspec);
+	module_list_release(&list);
 	return ret;
 }
 
diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh
index 0f1cb49cedc..3a241f259de 100755
--- a/t/t6134-pathspec-in-submodule.sh
+++ b/t/t6134-pathspec-in-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='test case exclude pathspec'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a submodule' '
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v6 07/17] submodule--helper: fix "errmsg_str" memory leak
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                             ` (5 preceding siblings ...)
  2022-08-21 13:59           ` [PATCH v6 06/17] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59           ` Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
                             ` (10 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak introduced in e83e3333b57 (submodule: port submodule
subcommand 'summary' from shell to C, 2020-08-13), we sometimes append
to the "errmsg", and need to free the "struct strbuf".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index fa320eb2edf..42ec6127024 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -973,6 +973,7 @@ static void generate_submodule_summary(struct summary_cb *info,
 	free(displaypath);
 	free(src_abbrev);
 	free(dst_abbrev);
+	strbuf_release(&errmsg);
 }
 
 static void prepare_submodule_summary(struct summary_cb *info,
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v6 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                             ` (6 preceding siblings ...)
  2022-08-21 13:59           ` [PATCH v6 07/17] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59           ` Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 09/17] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
                             ` (9 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix leaks in "struct module_cb_list" and the "struct module_cb" which
it contains, these fix leaks in e83e3333b57 (submodule: port submodule
subcommand 'summary' from shell to C, 2020-08-13).

The "sm_path" should always have been a "char *", not a "const
char *", we always create it with xstrdup().

We can't mark any tests passing passing with SANITIZE=leak using
"TEST_PASSES_SANITIZE_LEAK=true" as a result of this change, but
"t7401-submodule-summary.sh" gets closer to passing as a result of
this change.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c  | 21 ++++++++++++++++++++-
 t/t7401-submodule-summary.sh |  1 +
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 42ec6127024..ea925178946 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -748,16 +748,34 @@ struct module_cb {
 	struct object_id oid_src;
 	struct object_id oid_dst;
 	char status;
-	const char *sm_path;
+	char *sm_path;
 };
 #define MODULE_CB_INIT { 0 }
 
+static void module_cb_release(struct module_cb *mcb)
+{
+	free(mcb->sm_path);
+}
+
 struct module_cb_list {
 	struct module_cb **entries;
 	int alloc, nr;
 };
 #define MODULE_CB_LIST_INIT { 0 }
 
+static void module_cb_list_release(struct module_cb_list *mcbl)
+{
+	int i;
+
+	for (i = 0; i < mcbl->nr; i++) {
+		struct module_cb *mcb = mcbl->entries[i];
+
+		module_cb_release(mcb);
+		free(mcb);
+	}
+	free(mcbl->entries);
+}
+
 struct summary_cb {
 	int argc;
 	const char **argv;
@@ -1104,6 +1122,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 cleanup:
 	strvec_clear(&diff_args);
 	release_revisions(&rev);
+	module_cb_list_release(&list);
 	return ret;
 }
 
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
index 9c3cc4cf404..542b3331a78 100755
--- a/t/t7401-submodule-summary.sh
+++ b/t/t7401-submodule-summary.sh
@@ -17,6 +17,7 @@ This test script tries to verify the sanity of summary subcommand of git submodu
 # various reasons, one of them being that there are lots of commands taking place
 # outside of 'test_expect_success' block, which is no longer in good-style.
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 add_file () {
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v6 09/17] submodule--helper: fix a leak with repo_clear()
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                             ` (7 preceding siblings ...)
  2022-08-21 13:59           ` [PATCH v6 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59           ` Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
                             ` (8 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Call repo_clear() in ensure_core_worktree() to free the "struct
repository". Fixes a leak that's been here since
74d4731da1f (submodule--helper: replace connect-gitdir-workingtree by
ensure-core-worktree, 2018-08-13).

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index ea925178946..b029e109b47 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2399,6 +2399,7 @@ static int ensure_core_worktree(const char *path)
 		strbuf_release(&sb);
 	}
 
+	repo_clear(&subrepo);
 	return 0;
 }
 
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v6 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule()
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                             ` (8 preceding siblings ...)
  2022-08-21 13:59           ` [PATCH v6 09/17] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59           ` Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 11/17] submodule--helper: fix "reference" leak Ævar Arnfjörð Bjarmason
                             ` (7 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak in the get_default_remote_submodule() function added
in a77c3fcb5ec (submodule--helper: get remote names from any
repository, 2022-03-04), we need to repo_clear() the submodule we
initialize.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index b029e109b47..3f123f014f9 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -65,12 +65,16 @@ static int repo_get_default_remote(struct repository *repo, char **default_remot
 static int get_default_remote_submodule(const char *module_path, char **default_remote)
 {
 	struct repository subrepo;
+	int ret;
 
 	if (repo_submodule_init(&subrepo, the_repository, module_path,
 				null_oid()) < 0)
 		return die_message(_("could not get a repository handle for submodule '%s'"),
 				   module_path);
-	return repo_get_default_remote(&subrepo, default_remote);
+	ret = repo_get_default_remote(&subrepo, default_remote);
+	repo_clear(&subrepo);
+
+	return ret;
 }
 
 static char *get_default_remote(void)
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v6 11/17] submodule--helper: fix "reference" leak
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                             ` (9 preceding siblings ...)
  2022-08-21 13:59           ` [PATCH v6 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59           ` Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 12/17] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
                             ` (6 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix leaks in the "reference" variable declared in add_submodule() and
module_clone().

In preceding commits this variable was refactored out of the "struct
module_clone_data", but the leak has been with us since
31224cbdc72 (clone: recursive and reference option triggers submodule
alternates, 2016-08-17) and 8c8195e9c3e (submodule--helper: introduce
add-clone subcommand, 2021-07-10).

Those commits added an xstrdup()'d member of the
STRING_LIST_INIT_NODUP'd "struct string_list". We need to free()
those, but not the ones we get from argv, let's make use of the "util"
member, if it has a pointer it's the pointer we'll need to free,
otherwise it'll be NULL (i.e. from argv).

Note that the free() of the "util" member is needed in both
module_clone() and add_submodule(). The module_clone() function itself
doesn't populate the "util" pointer as add_submodule() does, but
module_clone() is upstream of the
add_possible_reference_from_superproject() caller we're modifying
here, which does do that.

This does preclude the use of the "util" pointer for any other reasons
for now, but that's OK. If we ever need to use it for something else
we could turn it into a small "struct" with an optional "to_free"
member, and switch to using string_list_clear_func().

Alternatively we could have another "struct string_list to_free" which
would keep a copy of the strings we've dup'd to free(). But for now
this is perfectly adequate.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 3f123f014f9..d469c571d52 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1566,7 +1566,9 @@ static int add_possible_reference_from_superproject(
 
 		sm_alternate = compute_alternate_path(sb.buf, &err);
 		if (sm_alternate) {
-			string_list_append(sas->reference, xstrdup(sb.buf));
+			char *p = strbuf_detach(&sb, NULL);
+
+			string_list_append(sas->reference, p)->util = p;
 			free(sm_alternate);
 		} else {
 			switch (sas->error_mode) {
@@ -1798,6 +1800,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 
 	clone_submodule(&clone_data, &reference);
 	list_objects_filter_release(&filter_options);
+	string_list_clear(&reference, 1);
 	return 0;
 }
 
@@ -3043,6 +3046,7 @@ static int add_submodule(const struct add_data *add_data)
 	char *submod_gitdir_path;
 	struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
 	struct string_list reference = STRING_LIST_INIT_NODUP;
+	int ret = -1;
 
 	/* perhaps the path already exists and is already a git repo, else clone it */
 	if (is_directory(add_data->sm_path)) {
@@ -3098,15 +3102,17 @@ static int add_submodule(const struct add_data *add_data)
 		clone_data.url = add_data->realrepo;
 		clone_data.quiet = add_data->quiet;
 		clone_data.progress = add_data->progress;
-		if (add_data->reference_path)
-			string_list_append(&reference,
-					   xstrdup(add_data->reference_path));
+		if (add_data->reference_path) {
+			char *p = xstrdup(add_data->reference_path);
+
+			string_list_append(&reference, p)->util = p;
+		}
 		clone_data.dissociate = add_data->dissociate;
 		if (add_data->depth >= 0)
 			clone_data.depth = xstrfmt("%d", add_data->depth);
 
 		if (clone_submodule(&clone_data, &reference))
-			return -1;
+			goto cleanup;
 
 		prepare_submodule_repo_env(&cp.env);
 		cp.git_cmd = 1;
@@ -3125,7 +3131,10 @@ static int add_submodule(const struct add_data *add_data)
 		if (run_command(&cp))
 			die(_("unable to checkout submodule '%s'"), add_data->sm_path);
 	}
-	return 0;
+	ret = 0;
+cleanup:
+	string_list_clear(&reference, 1);
+	return ret;
 }
 
 static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v6 12/17] submodule--helper: fix obscure leak in module_add()
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                             ` (10 preceding siblings ...)
  2022-08-21 13:59           ` [PATCH v6 11/17] submodule--helper: fix "reference" leak Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59           ` Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 13/17] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
                             ` (5 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix an obscure leak in module_add(), if the "git add" command we were
piping to failed we'd fail to strbuf_release(&sb). This fixes a leak
introduced in a6226fd772b (submodule--helper: convert the bulk of
cmd_add() to C, 2021-08-10).

In fixing it move to a "goto cleanup" pattern, and since we need to
introduce a "ret" variable to do that let's also get rid of the
intermediate "exit_code" variable. The initialization to "-1" in
a6226fd772b has always been redundant, we'd only use the "exit_code"
value after assigning the return value of pipe_command() to it.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d469c571d52..d952b9c8616 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3294,6 +3294,8 @@ static int module_add(int argc, const char **argv, const char *prefix)
 		N_("git submodule add [<options>] [--] <repository> [<path>]"),
 		NULL
 	};
+	struct strbuf sb = STRBUF_INIT;
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 
@@ -3343,21 +3345,17 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	die_on_repo_without_commits(add_data.sm_path);
 
 	if (!force) {
-		int exit_code = -1;
-		struct strbuf sb = STRBUF_INIT;
 		struct child_process cp = CHILD_PROCESS_INIT;
 
 		cp.git_cmd = 1;
 		cp.no_stdout = 1;
 		strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
 			     "--no-warn-embedded-repo", add_data.sm_path, NULL);
-		if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
+		if ((ret = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
 			strbuf_complete_line(&sb);
 			fputs(sb.buf, stderr);
-			free(add_data.sm_path);
-			return exit_code;
+			goto cleanup;
 		}
-		strbuf_release(&sb);
 	}
 
 	if(!add_data.sm_name)
@@ -3372,15 +3370,17 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	add_data.progress = !!progress;
 	add_data.dissociate = !!dissociate;
 
-	if (add_submodule(&add_data)) {
-		free(add_data.sm_path);
-		return 1;
-	}
+	if (add_submodule(&add_data))
+		goto cleanup;
 	configure_added_submodule(&add_data);
+
+	ret = 0;
+cleanup:
 	free(add_data.sm_path);
 	free(to_free);
+	strbuf_release(&sb);
 
-	return 0;
+	return ret;
 }
 
 #define SUPPORT_SUPER_PREFIX (1<<0)
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v6 13/17] submodule--helper: fix a leak in module_add()
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                             ` (11 preceding siblings ...)
  2022-08-21 13:59           ` [PATCH v6 12/17] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59           ` Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 14/17] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
                             ` (4 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in module_path(), since a6226fd772b (submodule--helper:
convert the bulk of cmd_add() to C, 2021-08-10), we've been freeing
add_data.sm_path, but in this case we clobbered it, and didn't free
the value we clobbered.

This makes test 28 of "t/t7400-submodule-basic.sh" ("submodule add in
subdirectory") pass when we're compiled with SANITIZE=leak..

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d952b9c8616..8d55090ee2e 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3315,8 +3315,12 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	else
 		add_data.sm_path = xstrdup(argv[1]);
 
-	if (prefix && *prefix && !is_absolute_path(add_data.sm_path))
-		add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
+	if (prefix && *prefix && !is_absolute_path(add_data.sm_path)) {
+		char *sm_path = add_data.sm_path;
+
+		add_data.sm_path = xstrfmt("%s%s", prefix, sm_path);
+		free(sm_path);
+	}
 
 	if (starts_with_dot_dot_slash(add_data.repo) ||
 	    starts_with_dot_slash(add_data.repo)) {
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v6 14/17] submodule--helper: fix a memory leak in print_status()
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                             ` (12 preceding siblings ...)
  2022-08-21 13:59           ` [PATCH v6 13/17] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59           ` Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 15/17] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
                             ` (3 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in print_status(), the compute_rev_name() function
implemented in this file will return a strbuf_detach()'d value, or
NULL.

This leak has existed since this code was added in
a9f8a37584a (submodule: port submodule subcommand 'status' from shell
to C, 2017-10-06), but in 0b5e2ea7cf3 (submodule--helper: don't print
null in 'submodule status', 2018-04-18) we added a "const"
intermediate variable for the return value, that "const" should be
removed.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 8d55090ee2e..e75d7fa2e10 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -583,10 +583,11 @@ static void print_status(unsigned int flags, char state, const char *path,
 	printf("%c%s %s", state, oid_to_hex(oid), displaypath);
 
 	if (state == ' ' || state == '+') {
-		const char *name = compute_rev_name(path, oid_to_hex(oid));
+		char *name = compute_rev_name(path, oid_to_hex(oid));
 
 		if (name)
 			printf(" (%s)", name);
+		free(name);
 	}
 
 	printf("\n");
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v6 15/17] submodule--helper: free some "displaypath" in "struct update_data"
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                             ` (13 preceding siblings ...)
  2022-08-21 13:59           ` [PATCH v6 14/17] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59           ` Ævar Arnfjörð Bjarmason
  2022-08-21 13:59           ` [PATCH v6 16/17] submodule--helper: free rest of " Ævar Arnfjörð Bjarmason
                             ` (2 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Make the update_data_release() function free "displaypath" member when
appropriate. The "displaypath" member is always ours, the "const" on
the "char *" was wrong to begin with.

This leaves a leak of "displaypath" in update_submodule(), which as
we'll see in subsequent commits is harder to deal with than this
trivial fix.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c        | 3 ++-
 t/t2403-worktree-move.sh           | 1 +
 t/t7412-submodule-absorbgitdirs.sh | 1 +
 t/t7419-submodule-set-branch.sh    | 1 +
 4 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index e75d7fa2e10..dbbe412b24e 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1880,7 +1880,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)
 
 struct update_data {
 	const char *prefix;
-	const char *displaypath;
+	char *displaypath;
 	enum submodule_update_type update_default;
 	struct object_id suboid;
 	struct string_list references;
@@ -1918,6 +1918,7 @@ struct update_data {
 
 static void update_data_release(struct update_data *ud)
 {
+	free(ud->displaypath);
 	module_list_release(&ud->list);
 }
 
diff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh
index a4e1a178e0a..1168e9f9982 100755
--- a/t/t2403-worktree-move.sh
+++ b/t/t2403-worktree-move.sh
@@ -2,6 +2,7 @@
 
 test_description='test git worktree move, remove, lock and unlock'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh
index 1cfa150768d..2859695c6d2 100755
--- a/t/t7412-submodule-absorbgitdirs.sh
+++ b/t/t7412-submodule-absorbgitdirs.sh
@@ -6,6 +6,7 @@ This test verifies that `git submodue absorbgitdirs` moves a submodules git
 directory into the superproject.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a real submodule' '
diff --git a/t/t7419-submodule-set-branch.sh b/t/t7419-submodule-set-branch.sh
index 3b925c302fc..96e98423214 100755
--- a/t/t7419-submodule-set-branch.sh
+++ b/t/t7419-submodule-set-branch.sh
@@ -9,6 +9,7 @@ This test verifies that the set-branch subcommand of git-submodule is working
 as expected.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 TEST_NO_CREATE_REPO=1
 . ./test-lib.sh
 
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v6 16/17] submodule--helper: free rest of "displaypath" in "struct update_data"
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                             ` (14 preceding siblings ...)
  2022-08-21 13:59           ` [PATCH v6 15/17] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59           ` Ævar Arnfjörð Bjarmason
  2022-08-24 23:03             ` Glen Choo
  2022-08-21 13:59           ` [PATCH v6 17/17] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  17 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in code added in c51f8f94e5b (submodule--helper: run update
procedures from C, 2021-08-24), we clobber the "displaypath" member of
the passed-in "struct update_data" both so that die() messages in this
update_submodule() function itself can use it, and for the
run_update_procedure() called within this function.

To make managing that clobbering easier let's wrap the
update_submodule() in a new update_submodule_outer() function, which
will do the clobbering and free(to_free) dance for us.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 27 +++++++++++++++++++--------
 1 file changed, 19 insertions(+), 8 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index dbbe412b24e..070e435b13e 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2485,13 +2485,6 @@ static int update_submodule(struct update_data *update_data)
 {
 	int ret;
 
-	ret = ensure_core_worktree(update_data->sm_path);
-	if (ret)
-		return ret;
-
-	update_data->displaypath = get_submodule_displaypath(
-		update_data->sm_path, update_data->prefix);
-
 	ret = determine_submodule_update_strategy(the_repository,
 						  update_data->just_cloned,
 						  update_data->sm_path,
@@ -2565,6 +2558,24 @@ static int update_submodule(struct update_data *update_data)
 	return 0;
 }
 
+static int update_submodule_outer(struct update_data *update_data)
+{
+	char *to_free, *restore = update_data->displaypath;
+	int ret;
+
+	ensure_core_worktree(update_data->sm_path);
+
+	update_data->displaypath = to_free = get_submodule_displaypath(
+		update_data->sm_path, update_data->prefix);
+
+	ret = update_submodule(update_data);
+
+	free(to_free);
+	update_data->displaypath = restore;
+
+	return ret;
+}
+
 static int update_submodules(struct update_data *update_data)
 {
 	int i, ret = 0;
@@ -2597,7 +2608,7 @@ static int update_submodules(struct update_data *update_data)
 		update_data->just_cloned = ucd.just_cloned;
 		update_data->sm_path = ucd.sub->path;
 
-		code = update_submodule(update_data);
+		code = update_submodule_outer(update_data);
 		if (!code)
 			continue;
 		ret = code;
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v6 17/17] submodule--helper: fix a configure_added_submodule() leak
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                             ` (15 preceding siblings ...)
  2022-08-21 13:59           ` [PATCH v6 16/17] submodule--helper: free rest of " Ævar Arnfjörð Bjarmason
@ 2022-08-21 13:59           ` Ævar Arnfjörð Bjarmason
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-21 13:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix config API a memory leak added in a452128a36c (submodule--helper:
introduce add-config subcommand, 2021-08-06) by using the *_tmp()
variant of git_config_get_string().

In this case we're only checking whether
the (repo|git)_config_get_string() call is telling us that the
"submodule.active" key exists.

As with the preceding commit we'll find many other such patterns in
the codebase if we go fishing. E.g. "git gc" leaks in the code added
in 61f7a383d3b (maintenance: use 'incremental' strategy by default,
2020-10-15). Similar code in "git gc" added in
b08ff1fee00 (maintenance: add --schedule option and config,
2020-09-11) doesn't leak, but we could avoid the malloc() & free() in
that case.

A coccinelle rule to find those would find and fix some leaks, and
cases where we're doing needless malloc() + free()'s but only care
about the key existence, or are copying
the (repo|git)_config_get_string() return value right away.

But as with the preceding commit let's punt on all of that for now,
and just narrowly fix this specific case in submodule--helper.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c    | 4 ++--
 t/t7413-submodule-is-active.sh | 1 +
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 070e435b13e..3061943e70d 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3168,7 +3168,7 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con
 static void configure_added_submodule(struct add_data *add_data)
 {
 	char *key;
-	char *val = NULL;
+	const char *val;
 	struct child_process add_submod = CHILD_PROCESS_INIT;
 	struct child_process add_gitmodules = CHILD_PROCESS_INIT;
 
@@ -3213,7 +3213,7 @@ static void configure_added_submodule(struct add_data *add_data)
 	 * is_submodule_active(), since that function needs to find
 	 * out the value of "submodule.active" again anyway.
 	 */
-	if (!git_config_get_string("submodule.active", &val)) {
+	if (!git_config_get_string_tmp("submodule.active", &val)) {
 		/*
 		 * If the submodule being added isn't already covered by the
 		 * current configured pathspec, set the submodule's active flag
diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh
index ede6f02dbd5..4dc7d089423 100755
--- a/t/t7413-submodule-is-active.sh
+++ b/t/t7413-submodule-is-active.sh
@@ -9,6 +9,7 @@ This is a unit test of the submodule.c is_submodule_active() function,
 which is also indirectly tested elsewhere.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-- 
2.37.2.1279.g64dec4e13cf


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* Re: [PATCH v6 16/17] submodule--helper: free rest of "displaypath" in "struct update_data"
  2022-08-21 13:59           ` [PATCH v6 16/17] submodule--helper: free rest of " Ævar Arnfjörð Bjarmason
@ 2022-08-24 23:03             ` Glen Choo
  0 siblings, 0 replies; 186+ messages in thread
From: Glen Choo @ 2022-08-24 23:03 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> Fix a leak in code added in c51f8f94e5b (submodule--helper: run update
> procedures from C, 2021-08-24), we clobber the "displaypath" member of
> the passed-in "struct update_data" both so that die() messages in this
> update_submodule() function itself can use it, and for the
> run_update_procedure() called within this function.
>
> To make managing that clobbering easier let's wrap the
> update_submodule() in a new update_submodule_outer() function, which
> will do the clobbering and free(to_free) dance for us.

The only feedback I had on v5 was on this patch
(https://lore.kernel.org/git/kl6l5yj7ubpt.fsf@chooglen-macbookpro.roam.corp.google.com)
where I said that update_submodule_outer() seemed like overkill and that
I'd test an alternative.

I tested this approach

  Assign and FREE_AND_NULL() update_data->displaypath [in
  update_submodules(), but outside of update_submodule()] since this
  is the only caller and it already does some prep work in this hunk.

by reverting this patch and applying this one

----- >8 --------- >8 --------- >8 --------- >8 --------- >8 ----
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 3557665261..c932c857dd 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2486,9 +2486,6 @@ static int update_submodule(struct update_data *update_data)
 	if (ret)
 		return ret;
 
-	update_data->displaypath = get_submodule_displaypath(
-		update_data->sm_path, update_data->prefix);
-
 	ret = determine_submodule_update_strategy(the_repository,
 						  update_data->just_cloned,
 						  update_data->sm_path,
@@ -2593,8 +2590,11 @@ static int update_submodules(struct update_data *update_data)
 		oidcpy(&update_data->oid, &ucd.oid);
 		update_data->just_cloned = ucd.just_cloned;
 		update_data->sm_path = ucd.sub->path;
+		update_data->displaypath = get_submodule_displaypath(
+			update_data->sm_path, update_data->prefix);
 
 		code = update_submodule(update_data);
+		FREE_AND_NULL(update_data->displaypath);
 		if (!code)
 			continue;
 		ret = code;
----- >8 --------- >8 --------- >8 --------- >8 --------- >8 ----

Then I tested the update_submodule_outer() version and the
FREE_AND_NULL() version by marking t7406-submodule-update.sh as
TEST_PASSES_SANITIZE_LEAK=true and diffing the test output. I didn't see
any meaningful difference in the test output, which should mean that
both versions fix all of the leaks in "git submodule update" that our
test suite can catch.

For good measure, I also tested a version of my code without the
FREE_AND_NULL() call (albeit on v5, not v6), and I spotted the expected
memory leak:

  Direct leak of 10 byte(s) in 1 object(s) allocated from:
      #0 0x000000000000 in __interceptor_malloc ../../../../src/libsanitizer/lsan/lsan_interceptors.cpp:54
      #1 0x000000000000 in strdup (/lib/x86_64-linux-gnu/libc.so.6+0x000000000000)
      #2 0x000000000000 in xstrdup wrapper.c:39
      #3 0x000000000000 in get_submodule_displaypath builtin/submodule--helper.c:119
      #4 0x000000000000 in update_submodules builtin/submodule--helper.c:2585
      #5 0x000000000000 in module_update builtin/submodule--helper.c:2716
      #6 0x000000000000 in run_builtin git.c:466
      #7 0x000000000000 in handle_builtin git.c:720
      #8 0x000000000000 in run_argv git.c:787
      #9 0x000000000000 in cmd_main git.c:920
      #10 0x000000000000 in main common-main.c:56
      #11 0x000000000000 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x000000000000)
      #12 0x000000000000 in _start (git+0x000000000000)

The CI runs are at: 
- FREE_AND_NULL(): https://github.com/chooglen/git/actions/runs/2920894120
- update_submodule_outer(): https://github.com/chooglen/git/actions/runs/2915661611
- control study: https://github.com/chooglen/git/actions/runs/2915671776

If FREE_AND_NULL() really plugs all of the leaks, I would strongly
prefer using that (possibly in a "goto cleanup"), over introducing
update_submodule_outer().

^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v7 00/17] submodule--helper: fix memory leaks
  2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                             ` (16 preceding siblings ...)
  2022-08-21 13:59           ` [PATCH v6 17/17] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14           ` Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 01/17] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
                               ` (17 more replies)
  17 siblings, 18 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

This series fixes all of the memory leaks in
builtin/submodule--helper.c that our test suite spots, except for
those where the leak is downstream of cmd_submodule__helper() and
caused by code that's not in builtin/submodule--helper.c (e.g. leaks
in the object or config APIs, and in submodule.c).

This re-roll is on top of the just-now re-rolled v4 of the base
topic[1].

Changes in v7:

 * Rebase on top of the base topic.
 * Clarify some questions on v6 in updated commit messages.
 * Go with Glen's suggested "displaypath" fix. I also tried another
   way which the commit discusses. The end result was better, but it
   requires further submodule--helper cleanups, and this is already
   quite an Odyssey. So FREE_AND_NULL() will do.

1. https://lore.kernel.org/git/cover-v4-00.33-00000000000-20220831T230519Z-avarab@gmail.com

This series & passing CI can be seen at:
https://github.com/avar/git/tree/avar/submodule--helper-memory-leaks-7

Ævar Arnfjörð Bjarmason (17):
  submodule--helper: fix a leak in "clone_submodule"
  submodule--helper: fix trivial get_default_remote_submodule() leak
  submodule--helper: fix most "struct pathspec" memory leaks
  submodule--helper: "struct pathspec" memory leak in module_update()
  submodule--helper: don't leak {run,capture}_command() cp.dir argument
  submodule--helper: add and use *_release() functions
  submodule--helper: fix "errmsg_str" memory leak
  submodule--helper: fix "sm_path" and other "module_cb_list" leaks
  submodule--helper: fix a leak with repo_clear()
  submodule--helper: fix a memory leak in get_default_remote_submodule()
  submodule--helper: fix "reference" leak
  submodule--helper: fix obscure leak in module_add()
  submodule--helper: fix a leak in module_add()
  submodule--helper: fix a memory leak in print_status()
  submodule--helper: free some "displaypath" in "struct update_data"
  submodule--helper: free rest of "displaypath" in "struct update_data"
  submodule--helper: fix a configure_added_submodule() leak

 builtin/submodule--helper.c        | 227 +++++++++++++++++++++--------
 t/t1500-rev-parse.sh               |   1 +
 t/t2403-worktree-move.sh           |   1 +
 t/t6008-rev-list-submodule.sh      |   1 +
 t/t6134-pathspec-in-submodule.sh   |   1 +
 t/t7401-submodule-summary.sh       |   1 +
 t/t7412-submodule-absorbgitdirs.sh |   1 +
 t/t7413-submodule-is-active.sh     |   1 +
 t/t7414-submodule-mistakes.sh      |   2 +
 t/t7419-submodule-set-branch.sh    |   1 +
 t/t7506-status-submodule.sh        |   1 +
 t/t7507-commit-verbose.sh          |   2 +
 12 files changed, 176 insertions(+), 64 deletions(-)

Range-diff against v6:
 1:  0f60ea6f7c6 =  1:  40d4aef60fa submodule--helper: fix a leak in "clone_submodule"
 2:  c14e00f39cb =  2:  104f0777d50 submodule--helper: fix trivial get_default_remote_submodule() leak
 3:  a2ecdb301d3 !  3:  3e1948ddc02 submodule--helper: fix most "struct pathspec" memory leaks
    @@ Commit message
         safe to call clear_pathspec(), we don't want the data to be
         uninitialized.
     
    +    E.g. for module_foreach() we can see from looking at
    +    module_list_compute() that if it returns non-zero that the "pathspec"
    +    will always have been initialized. But relying on that both assumes
    +    knowledge about parse_pathspec(), and would set up a fragile pattern
    +    going forward.
    +
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/submodule--helper.c ##
 4:  8fc3dad316e =  4:  505d7d5077e submodule--helper: "struct pathspec" memory leak in module_update()
 5:  a3886d36366 !  5:  41672ffa55c submodule--helper: don't leak {run,capture}_command() cp.dir argument
    @@ Commit message
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/submodule--helper.c ##
    -@@ builtin/submodule--helper.c: static int is_tip_reachable(const char *path, struct object_id *oid)
    +@@ builtin/submodule--helper.c: static int is_tip_reachable(const char *path, const struct object_id *oid)
      	char *hex = oid_to_hex(oid);
      
      	cp.git_cmd = 1;
    @@ builtin/submodule--helper.c: static int is_tip_reachable(const char *path, struc
      	cp.no_stderr = 1;
      	strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL);
      
    -@@ builtin/submodule--helper.c: static int fetch_in_submodule(const char *module_path, int depth, int quiet, str
    +@@ builtin/submodule--helper.c: static int fetch_in_submodule(const char *module_path, int depth, int quiet,
      
      	prepare_submodule_repo_env(&cp.env);
      	cp.git_cmd = 1;
    @@ builtin/submodule--helper.c: static int fetch_in_submodule(const char *module_pa
      
      	strvec_push(&cp.args, "fetch");
      	if (quiet)
    -@@ builtin/submodule--helper.c: static int run_update_command(struct update_data *ud, int subforce)
    +@@ builtin/submodule--helper.c: static int run_update_command(const struct update_data *ud, int subforce)
      	}
      	strvec_push(&cp.args, oid);
      
 6:  173dbb7d8fc =  6:  07a4cda579f submodule--helper: add and use *_release() functions
 7:  cd394814370 =  7:  600526e1301 submodule--helper: fix "errmsg_str" memory leak
 8:  2110b991913 =  8:  2c5db495189 submodule--helper: fix "sm_path" and other "module_cb_list" leaks
 9:  a0d1a03166f =  9:  71ebaac73c1 submodule--helper: fix a leak with repo_clear()
10:  84f162f320b = 10:  8a016f3d58c submodule--helper: fix a memory leak in get_default_remote_submodule()
11:  dc5e8504354 = 11:  6a4c22d07b9 submodule--helper: fix "reference" leak
12:  50d58bfb19b = 12:  b8654647601 submodule--helper: fix obscure leak in module_add()
13:  56590508180 = 13:  c922e04f3d1 submodule--helper: fix a leak in module_add()
14:  38ceb0a36ab = 14:  33babb1a165 submodule--helper: fix a memory leak in print_status()
15:  f3cf49dd029 = 15:  bfafd31746e submodule--helper: free some "displaypath" in "struct update_data"
16:  a99a7736fab ! 16:  63ff554e306 submodule--helper: free rest of "displaypath" in "struct update_data"
    @@ Commit message
         update_submodule() function itself can use it, and for the
         run_update_procedure() called within this function.
     
    -    To make managing that clobbering easier let's wrap the
    -    update_submodule() in a new update_submodule_outer() function, which
    -    will do the clobbering and free(to_free) dance for us.
    +    Fix a leak in code added in 51f8f94e5b (submodule--helper: run update
    +    procedures from C, 2021-08-24). We'd always clobber the old
    +    "displaypath" member of the previously passed-in "struct update_data".
     
    +    A better fix for this would be to remove the "displaypath" member from
    +    the "struct update_data" entirely. Along with "oid", "suboid",
    +    "just_cloned" and "sm_path" it's managing members that mainly need to
    +    be passed between 1-3 stack frames of functions adjacent to this
    +    code. But doing so would be a much larger change (I have it locally,
    +    and fully untangling that in an incremental way is a 10 patch
    +    journey).
    +
    +    So let's go for this much more isolated fix suggested by Glen. We
    +    FREE_AND_NULL() the "update_data->displaypath", the "AND_NULL()" part
    +    of that is needed due to the later "free(ud->displaypath)" in
    +    "update_data_release()" introduced in the preceding commit
    +
    +    Moving ensure_core_worktree() out of update_submodule() may not be
    +    strictly required, but in doing so we are left with the exact same
    +    ordering as before, making this a smaller functional change.
    +
    +    Helped-by: Glen Choo <chooglen@google.com>
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/submodule--helper.c ##
    @@ builtin/submodule--helper.c: static int update_submodule(struct update_data *upd
      	ret = determine_submodule_update_strategy(the_repository,
      						  update_data->just_cloned,
      						  update_data->sm_path,
    -@@ builtin/submodule--helper.c: static int update_submodule(struct update_data *update_data)
    - 	return 0;
    - }
    - 
    -+static int update_submodule_outer(struct update_data *update_data)
    -+{
    -+	char *to_free, *restore = update_data->displaypath;
    -+	int ret;
    -+
    -+	ensure_core_worktree(update_data->sm_path);
    -+
    -+	update_data->displaypath = to_free = get_submodule_displaypath(
    -+		update_data->sm_path, update_data->prefix);
    -+
    -+	ret = update_submodule(update_data);
    -+
    -+	free(to_free);
    -+	update_data->displaypath = restore;
    -+
    -+	return ret;
    -+}
    -+
    - static int update_submodules(struct update_data *update_data)
    - {
    - 	int i, ret = 0;
     @@ builtin/submodule--helper.c: static int update_submodules(struct update_data *update_data)
      		update_data->just_cloned = ucd.just_cloned;
      		update_data->sm_path = ucd.sub->path;
      
    --		code = update_submodule(update_data);
    -+		code = update_submodule_outer(update_data);
    ++		code = ensure_core_worktree(update_data->sm_path);
    ++		if (code)
    ++			goto fail;
    ++
    ++		update_data->displaypath = get_submodule_displaypath(
    ++			update_data->sm_path, update_data->prefix);
    + 		code = update_submodule(update_data);
    ++		FREE_AND_NULL(update_data->displaypath);
    ++fail:
      		if (!code)
      			continue;
      		ret = code;
17:  ec0b02d8ee2 = 17:  3056bfcf607 submodule--helper: fix a configure_added_submodule() leak
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply	[flat|nested] 186+ messages in thread

* [PATCH v7 01/17] submodule--helper: fix a leak in "clone_submodule"
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14             ` Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
                               ` (16 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak of the "clone_data_path" variable that we copy or
derive from the "struct module_clone_data" in clone_submodule(). This
code was refactored in preceding commits, but the leak has been with
us since f8eaa0ba98b (submodule--helper, module_clone: always operate
on absolute paths, 2016-03-31).

For the "else" case we don't need to xstrdup() the "clone_data->path",
and we don't need to free our own "clone_data_path". We can therefore
assign the "clone_data->path" to our own "clone_data_path" right away,
and only override it (and remember to free it!) if we need to
xstrfmt() a replacement.

In the case of the module_clone() caller it's from "argv", and doesn't
need to be free'd, and in the case of the add_submodule() caller we
get a pointer to "sm_path", which doesn't need to be directly free'd
either.

Fixing this leak makes several tests pass, so let's mark them as
passing with TEST_PASSES_SANITIZE_LEAK=true.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c   | 10 +++++-----
 t/t1500-rev-parse.sh          |  1 +
 t/t6008-rev-list-submodule.sh |  1 +
 t/t7414-submodule-mistakes.sh |  2 ++
 t/t7506-status-submodule.sh   |  1 +
 t/t7507-commit-verbose.sh     |  2 ++
 6 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index c4149f11e7f..2e042c8a043 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1590,13 +1590,12 @@ static int clone_submodule(const struct module_clone_data *clone_data,
 	char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);
 	char *sm_alternate = NULL, *error_strategy = NULL;
 	struct child_process cp = CHILD_PROCESS_INIT;
-	const char *clone_data_path;
+	const char *clone_data_path = clone_data->path;
+	char *to_free = NULL;
 
 	if (!is_absolute_path(clone_data->path))
-		clone_data_path = xstrfmt("%s/%s", get_git_work_tree(),
-					  clone_data->path);
-	else
-		clone_data_path = xstrdup(clone_data->path);
+		clone_data_path = to_free = xstrfmt("%s/%s", get_git_work_tree(),
+						    clone_data->path);
 
 	if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)
 		die(_("refusing to create/use '%s' in another submodule's "
@@ -1681,6 +1680,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,
 
 	free(sm_gitdir);
 	free(p);
+	free(to_free);
 	return 0;
 }
 
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 1c2df08333b..0e13bcb4ebb 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -4,6 +4,7 @@ test_description='test git rev-parse'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_one () {
diff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh
index 3153a0d8910..12e67e187ef 100755
--- a/t/t6008-rev-list-submodule.sh
+++ b/t/t6008-rev-list-submodule.sh
@@ -8,6 +8,7 @@ test_description='git rev-list involving submodules that this repo has'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh
index f2e7df59cf2..3269298197c 100755
--- a/t/t7414-submodule-mistakes.sh
+++ b/t/t7414-submodule-mistakes.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='handling of common mistakes people may make with submodules'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create embedded repository' '
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index 3fcb44767f5..f5426a8e589 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='git status for submodule'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_create_repo_with_commit () {
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index ed2653d46fe..92462a22374 100755
--- a/t/t7507-commit-verbose.sh
+++ b/t/t7507-commit-verbose.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='verbose commit template'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 write_script "check-for-diff" <<\EOF &&
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v7 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 01/17] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14             ` Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 03/17] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
                               ` (15 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in code added in 1012a5cbc3f (submodule--helper
run-update-procedure: learn --remote, 2022-03-04), we need to free()
the xstrdup()'d string. This gets e.g. t/t7419-submodule-set-branch.sh
closer to passing under SANITIZE=leak.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 2e042c8a043..b51a5074b42 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2451,6 +2451,8 @@ static int update_submodule(struct update_data *update_data)
 			return code;
 		remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch);
 
+		free(remote_name);
+
 		if (!update_data->nofetch) {
 			if (fetch_in_submodule(update_data->sm_path, update_data->depth,
 					      0, NULL))
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v7 03/17] submodule--helper: fix most "struct pathspec" memory leaks
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 01/17] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14             ` Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 04/17] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
                               ` (14 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Call clear_pathspec() at the end of various functions that work with
and allocate a "struct pathspec".

In some cases the zero-initialization here isn't strictly needed, but
as we're moving to a "goto cleanup" pattern let's make sure that it's
safe to call clear_pathspec(), we don't want the data to be
uninitialized.

E.g. for module_foreach() we can see from looking at
module_list_compute() that if it returns non-zero that the "pathspec"
will always have been initialized. But relying on that both assumes
knowledge about parse_pathspec(), and would set up a fragile pattern
going forward.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 74 +++++++++++++++++++++++++------------
 1 file changed, 51 insertions(+), 23 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index b51a5074b42..2f8603a6bb5 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -379,7 +379,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
 static int module_foreach(int argc, const char **argv, const char *prefix)
 {
 	struct foreach_cb info = FOREACH_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	struct option module_foreach_options[] = {
 		OPT__QUIET(&info.quiet, N_("suppress output of entering each submodule command")),
@@ -391,12 +391,13 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 		N_("git submodule foreach [--quiet] [--recursive] [--] <command>"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_foreach_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.argc = argc;
 	info.argv = argv;
@@ -404,7 +405,10 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static int starts_with_dot_slash(const char *const path)
@@ -515,7 +519,7 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data
 static int module_init(int argc, const char **argv, const char *prefix)
 {
 	struct init_cb info = INIT_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	struct option module_init_options[] = {
@@ -526,12 +530,13 @@ static int module_init(int argc, const char **argv, const char *prefix)
 		N_("git submodule init [<options>] [<path>]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_init_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	/*
 	 * If there are no path args and submodule.active is set then,
@@ -546,7 +551,10 @@ static int module_init(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, init_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct status_cb {
@@ -693,7 +701,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,
 static int module_status(int argc, const char **argv, const char *prefix)
 {
 	struct status_cb info = STATUS_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	struct option module_status_options[] = {
@@ -706,12 +714,13 @@ static int module_status(int argc, const char **argv, const char *prefix)
 		N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_status_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.prefix = prefix;
 	if (quiet)
@@ -719,7 +728,10 @@ static int module_status(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, status_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct module_cb {
@@ -1261,7 +1273,7 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data
 static int module_sync(int argc, const char **argv, const char *prefix)
 {
 	struct sync_cb info = SYNC_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	int recursive = 0;
@@ -1275,12 +1287,13 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 		N_("git submodule sync [--quiet] [--recursive] [<path>]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_sync_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.prefix = prefix;
 	if (quiet)
@@ -1290,7 +1303,10 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, sync_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct deinit_cb {
@@ -1399,7 +1415,7 @@ static void deinit_submodule_cb(const struct cache_entry *list_item,
 static int module_deinit(int argc, const char **argv, const char *prefix)
 {
 	struct deinit_cb info = DEINIT_CB_INIT;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	int force = 0;
@@ -1414,6 +1430,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 		N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, module_deinit_options,
 			     git_submodule_helper_usage, 0);
@@ -1428,7 +1445,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 		die(_("Use '--all' if you really want to deinitialize all submodules"));
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	info.prefix = prefix;
 	if (quiet)
@@ -1438,7 +1455,10 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 
 	for_each_listed_submodule(&list, deinit_submodule_cb, &info);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 struct module_clone_data {
@@ -2543,7 +2563,7 @@ static int update_submodules(struct update_data *update_data)
 
 static int module_update(int argc, const char **argv, const char *prefix)
 {
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct update_data opt = UPDATE_DATA_INIT;
 	struct list_objects_filter_options filter_options = { 0 };
 	int ret;
@@ -2620,8 +2640,8 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		opt.update_strategy.type = opt.update_default;
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) {
-		list_objects_filter_release(&filter_options);
-		return 1;
+		ret = 1;
+		goto cleanup;
 	}
 
 	if (pathspec.nr)
@@ -2632,8 +2652,10 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		struct init_cb info = INIT_CB_INIT;
 
 		if (module_list_compute(argc, argv, opt.prefix,
-					&pathspec, &list) < 0)
-			return 1;
+					&pathspec, &list) < 0) {
+			ret = 1;
+			goto cleanup;
+		}
 
 		/*
 		 * If there are no path args and submodule.active is set then,
@@ -2650,7 +2672,9 @@ static int module_update(int argc, const char **argv, const char *prefix)
 	}
 
 	ret = update_submodules(&opt);
+cleanup:
 	list_objects_filter_release(&filter_options);
+	clear_pathspec(&pathspec);
 	return ret;
 }
 
@@ -2734,7 +2758,7 @@ static int push_check(int argc, const char **argv, const char *prefix)
 static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 {
 	int i;
-	struct pathspec pathspec;
+	struct pathspec pathspec = { 0 };
 	struct module_list list = MODULE_LIST_INIT;
 	unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;
 	struct option embed_gitdir_options[] = {
@@ -2749,17 +2773,21 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 		N_("git submodule absorbgitdirs [<options>] [<path>...]"),
 		NULL
 	};
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, embed_gitdir_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
-		return 1;
+		goto cleanup;
 
 	for (i = 0; i < list.nr; i++)
 		absorb_git_dir_into_superproject(list.entries[i]->name, flags);
 
-	return 0;
+	ret = 0;
+cleanup:
+	clear_pathspec(&pathspec);
+	return ret;
 }
 
 static int module_config(int argc, const char **argv, const char *prefix)
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v7 04/17] submodule--helper: "struct pathspec" memory leak in module_update()
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                               ` (2 preceding siblings ...)
  2022-08-31 23:14             ` [PATCH v7 03/17] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14             ` Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
                               ` (13 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

The module_update() function calls module_list_compute() twice, which
in turn will reset the "struct pathspec" passed to it. Let's instead
track two of them, and clear them both.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 2f8603a6bb5..e8bc6f17330 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2564,6 +2564,7 @@ static int update_submodules(struct update_data *update_data)
 static int module_update(int argc, const char **argv, const char *prefix)
 {
 	struct pathspec pathspec = { 0 };
+	struct pathspec pathspec2 = { 0 };
 	struct update_data opt = UPDATE_DATA_INIT;
 	struct list_objects_filter_options filter_options = { 0 };
 	int ret;
@@ -2652,7 +2653,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 		struct init_cb info = INIT_CB_INIT;
 
 		if (module_list_compute(argc, argv, opt.prefix,
-					&pathspec, &list) < 0) {
+					&pathspec2, &list) < 0) {
 			ret = 1;
 			goto cleanup;
 		}
@@ -2675,6 +2676,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 cleanup:
 	list_objects_filter_release(&filter_options);
 	clear_pathspec(&pathspec);
+	clear_pathspec(&pathspec2);
 	return ret;
 }
 
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v7 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                               ` (3 preceding siblings ...)
  2022-08-31 23:14             ` [PATCH v7 04/17] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14             ` Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 06/17] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
                               ` (12 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak in c51f8f94e5b (submodule--helper: run update
procedures from C, 2021-08-24) and 3c3558f0953 (submodule--helper: run
update using child process struct, 2022-03-15) by not allocating
memory in the first place.

The "dir" member of "struct child_process" will not be modified by
that API, and it's declared to be "const char *". So let's not
needlessly duplicate these strings.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index e8bc6f17330..0c23dd93d96 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2129,7 +2129,7 @@ static int is_tip_reachable(const char *path, const struct object_id *oid)
 	char *hex = oid_to_hex(oid);
 
 	cp.git_cmd = 1;
-	cp.dir = xstrdup(path);
+	cp.dir = path;
 	cp.no_stderr = 1;
 	strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL);
 
@@ -2148,7 +2148,7 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet,
 
 	prepare_submodule_repo_env(&cp.env);
 	cp.git_cmd = 1;
-	cp.dir = xstrdup(module_path);
+	cp.dir = module_path;
 
 	strvec_push(&cp.args, "fetch");
 	if (quiet)
@@ -2201,7 +2201,7 @@ static int run_update_command(const struct update_data *ud, int subforce)
 	}
 	strvec_push(&cp.args, oid);
 
-	cp.dir = xstrdup(ud->sm_path);
+	cp.dir = ud->sm_path;
 	prepare_submodule_repo_env(&cp.env);
 	if ((ret = run_command(&cp))) {
 		switch (ud->update_strategy.type) {
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v7 06/17] submodule--helper: add and use *_release() functions
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                               ` (4 preceding siblings ...)
  2022-08-31 23:14             ` [PATCH v7 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14             ` Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 07/17] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
                               ` (11 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Add release functions for "struct module_list", "struct
submodule_update_clone" and "struct update_data".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c      | 28 +++++++++++++++++++++++++++-
 t/t6134-pathspec-in-submodule.sh |  1 +
 2 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 0c23dd93d96..ae918a662b4 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -172,6 +172,11 @@ struct module_list {
 };
 #define MODULE_LIST_INIT { 0 }
 
+static void module_list_release(struct module_list *ml)
+{
+	free(ml->entries);
+}
+
 static int module_list_compute(int argc, const char **argv,
 			       const char *prefix,
 			       struct pathspec *pathspec,
@@ -234,7 +239,7 @@ static void module_list_active(struct module_list *list)
 		active_modules.entries[active_modules.nr++] = ce;
 	}
 
-	free(list->entries);
+	module_list_release(list);
 	*list = active_modules;
 }
 
@@ -407,6 +412,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -553,6 +559,7 @@ static int module_init(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -730,6 +737,7 @@ static int module_status(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1305,6 +1313,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1457,6 +1466,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1834,6 +1844,12 @@ struct submodule_update_clone {
 };
 #define SUBMODULE_UPDATE_CLONE_INIT { 0 }
 
+static void submodule_update_clone_release(struct submodule_update_clone *suc)
+{
+	free(suc->update_clone);
+	free(suc->failed_clones);
+}
+
 struct update_data {
 	const char *prefix;
 	const char *displaypath;
@@ -1872,6 +1888,11 @@ struct update_data {
 	.max_jobs = 1, \
 }
 
+static void update_data_release(struct update_data *ud)
+{
+	module_list_release(&ud->list);
+}
+
 static void next_submodule_warn_missing(struct submodule_update_clone *suc,
 		struct strbuf *out, const char *displaypath)
 {
@@ -2557,6 +2578,7 @@ static int update_submodules(struct update_data *update_data)
 	}
 
 cleanup:
+	submodule_update_clone_release(&suc);
 	string_list_clear(&update_data->references, 0);
 	return ret;
 }
@@ -2654,6 +2676,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 
 		if (module_list_compute(argc, argv, opt.prefix,
 					&pathspec2, &list) < 0) {
+			module_list_release(&list);
 			ret = 1;
 			goto cleanup;
 		}
@@ -2670,10 +2693,12 @@ static int module_update(int argc, const char **argv, const char *prefix)
 			info.flags |= OPT_QUIET;
 
 		for_each_listed_submodule(&list, init_submodule_cb, &info);
+		module_list_release(&list);
 	}
 
 	ret = update_submodules(&opt);
 cleanup:
+	update_data_release(&opt);
 	list_objects_filter_release(&filter_options);
 	clear_pathspec(&pathspec);
 	clear_pathspec(&pathspec2);
@@ -2789,6 +2814,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 	ret = 0;
 cleanup:
 	clear_pathspec(&pathspec);
+	module_list_release(&list);
 	return ret;
 }
 
diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh
index 0f1cb49cedc..3a241f259de 100755
--- a/t/t6134-pathspec-in-submodule.sh
+++ b/t/t6134-pathspec-in-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='test case exclude pathspec'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a submodule' '
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v7 07/17] submodule--helper: fix "errmsg_str" memory leak
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                               ` (5 preceding siblings ...)
  2022-08-31 23:14             ` [PATCH v7 06/17] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14             ` Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
                               ` (10 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak introduced in e83e3333b57 (submodule: port submodule
subcommand 'summary' from shell to C, 2020-08-13), we sometimes append
to the "errmsg", and need to free the "struct strbuf".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index ae918a662b4..75a5d9d47fd 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -973,6 +973,7 @@ static void generate_submodule_summary(struct summary_cb *info,
 	free(displaypath);
 	free(src_abbrev);
 	free(dst_abbrev);
+	strbuf_release(&errmsg);
 }
 
 static void prepare_submodule_summary(struct summary_cb *info,
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v7 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                               ` (6 preceding siblings ...)
  2022-08-31 23:14             ` [PATCH v7 07/17] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14             ` Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 09/17] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
                               ` (9 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix leaks in "struct module_cb_list" and the "struct module_cb" which
it contains, these fix leaks in e83e3333b57 (submodule: port submodule
subcommand 'summary' from shell to C, 2020-08-13).

The "sm_path" should always have been a "char *", not a "const
char *", we always create it with xstrdup().

We can't mark any tests passing passing with SANITIZE=leak using
"TEST_PASSES_SANITIZE_LEAK=true" as a result of this change, but
"t7401-submodule-summary.sh" gets closer to passing as a result of
this change.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c  | 21 ++++++++++++++++++++-
 t/t7401-submodule-summary.sh |  1 +
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 75a5d9d47fd..162e92c033e 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -748,16 +748,34 @@ struct module_cb {
 	struct object_id oid_src;
 	struct object_id oid_dst;
 	char status;
-	const char *sm_path;
+	char *sm_path;
 };
 #define MODULE_CB_INIT { 0 }
 
+static void module_cb_release(struct module_cb *mcb)
+{
+	free(mcb->sm_path);
+}
+
 struct module_cb_list {
 	struct module_cb **entries;
 	int alloc, nr;
 };
 #define MODULE_CB_LIST_INIT { 0 }
 
+static void module_cb_list_release(struct module_cb_list *mcbl)
+{
+	int i;
+
+	for (i = 0; i < mcbl->nr; i++) {
+		struct module_cb *mcb = mcbl->entries[i];
+
+		module_cb_release(mcb);
+		free(mcb);
+	}
+	free(mcbl->entries);
+}
+
 struct summary_cb {
 	int argc;
 	const char **argv;
@@ -1104,6 +1122,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 cleanup:
 	strvec_clear(&diff_args);
 	release_revisions(&rev);
+	module_cb_list_release(&list);
 	return ret;
 }
 
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
index 9c3cc4cf404..542b3331a78 100755
--- a/t/t7401-submodule-summary.sh
+++ b/t/t7401-submodule-summary.sh
@@ -17,6 +17,7 @@ This test script tries to verify the sanity of summary subcommand of git submodu
 # various reasons, one of them being that there are lots of commands taking place
 # outside of 'test_expect_success' block, which is no longer in good-style.
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 add_file () {
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v7 09/17] submodule--helper: fix a leak with repo_clear()
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                               ` (7 preceding siblings ...)
  2022-08-31 23:14             ` [PATCH v7 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14             ` Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
                               ` (8 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Call repo_clear() in ensure_core_worktree() to free the "struct
repository". Fixes a leak that's been here since
74d4731da1f (submodule--helper: replace connect-gitdir-workingtree by
ensure-core-worktree, 2018-08-13).

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 162e92c033e..9a277057012 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2400,6 +2400,7 @@ static int ensure_core_worktree(const char *path)
 		strbuf_release(&sb);
 	}
 
+	repo_clear(&subrepo);
 	return 0;
 }
 
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v7 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule()
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                               ` (8 preceding siblings ...)
  2022-08-31 23:14             ` [PATCH v7 09/17] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14             ` Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 11/17] submodule--helper: fix "reference" leak Ævar Arnfjörð Bjarmason
                               ` (7 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a memory leak in the get_default_remote_submodule() function added
in a77c3fcb5ec (submodule--helper: get remote names from any
repository, 2022-03-04), we need to repo_clear() the submodule we
initialize.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 9a277057012..9f9e5f05094 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -65,12 +65,16 @@ static int repo_get_default_remote(struct repository *repo, char **default_remot
 static int get_default_remote_submodule(const char *module_path, char **default_remote)
 {
 	struct repository subrepo;
+	int ret;
 
 	if (repo_submodule_init(&subrepo, the_repository, module_path,
 				null_oid()) < 0)
 		return die_message(_("could not get a repository handle for submodule '%s'"),
 				   module_path);
-	return repo_get_default_remote(&subrepo, default_remote);
+	ret = repo_get_default_remote(&subrepo, default_remote);
+	repo_clear(&subrepo);
+
+	return ret;
 }
 
 static char *get_default_remote(void)
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v7 11/17] submodule--helper: fix "reference" leak
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                               ` (9 preceding siblings ...)
  2022-08-31 23:14             ` [PATCH v7 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14             ` Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 12/17] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
                               ` (6 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix leaks in the "reference" variable declared in add_submodule() and
module_clone().

In preceding commits this variable was refactored out of the "struct
module_clone_data", but the leak has been with us since
31224cbdc72 (clone: recursive and reference option triggers submodule
alternates, 2016-08-17) and 8c8195e9c3e (submodule--helper: introduce
add-clone subcommand, 2021-07-10).

Those commits added an xstrdup()'d member of the
STRING_LIST_INIT_NODUP'd "struct string_list". We need to free()
those, but not the ones we get from argv, let's make use of the "util"
member, if it has a pointer it's the pointer we'll need to free,
otherwise it'll be NULL (i.e. from argv).

Note that the free() of the "util" member is needed in both
module_clone() and add_submodule(). The module_clone() function itself
doesn't populate the "util" pointer as add_submodule() does, but
module_clone() is upstream of the
add_possible_reference_from_superproject() caller we're modifying
here, which does do that.

This does preclude the use of the "util" pointer for any other reasons
for now, but that's OK. If we ever need to use it for something else
we could turn it into a small "struct" with an optional "to_free"
member, and switch to using string_list_clear_func().

Alternatively we could have another "struct string_list to_free" which
would keep a copy of the strings we've dup'd to free(). But for now
this is perfectly adequate.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 9f9e5f05094..170eaf010ac 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1566,7 +1566,9 @@ static int add_possible_reference_from_superproject(
 
 		sm_alternate = compute_alternate_path(sb.buf, &err);
 		if (sm_alternate) {
-			string_list_append(sas->reference, xstrdup(sb.buf));
+			char *p = strbuf_detach(&sb, NULL);
+
+			string_list_append(sas->reference, p)->util = p;
 			free(sm_alternate);
 		} else {
 			switch (sas->error_mode) {
@@ -1798,6 +1800,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 
 	clone_submodule(&clone_data, &reference);
 	list_objects_filter_release(&filter_options);
+	string_list_clear(&reference, 1);
 	return 0;
 }
 
@@ -3045,6 +3048,7 @@ static int add_submodule(const struct add_data *add_data)
 	char *submod_gitdir_path;
 	struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
 	struct string_list reference = STRING_LIST_INIT_NODUP;
+	int ret = -1;
 
 	/* perhaps the path already exists and is already a git repo, else clone it */
 	if (is_directory(add_data->sm_path)) {
@@ -3100,15 +3104,17 @@ static int add_submodule(const struct add_data *add_data)
 		clone_data.url = add_data->realrepo;
 		clone_data.quiet = add_data->quiet;
 		clone_data.progress = add_data->progress;
-		if (add_data->reference_path)
-			string_list_append(&reference,
-					   xstrdup(add_data->reference_path));
+		if (add_data->reference_path) {
+			char *p = xstrdup(add_data->reference_path);
+
+			string_list_append(&reference, p)->util = p;
+		}
 		clone_data.dissociate = add_data->dissociate;
 		if (add_data->depth >= 0)
 			clone_data.depth = xstrfmt("%d", add_data->depth);
 
 		if (clone_submodule(&clone_data, &reference))
-			return -1;
+			goto cleanup;
 
 		prepare_submodule_repo_env(&cp.env);
 		cp.git_cmd = 1;
@@ -3127,7 +3133,10 @@ static int add_submodule(const struct add_data *add_data)
 		if (run_command(&cp))
 			die(_("unable to checkout submodule '%s'"), add_data->sm_path);
 	}
-	return 0;
+	ret = 0;
+cleanup:
+	string_list_clear(&reference, 1);
+	return ret;
 }
 
 static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v7 12/17] submodule--helper: fix obscure leak in module_add()
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                               ` (10 preceding siblings ...)
  2022-08-31 23:14             ` [PATCH v7 11/17] submodule--helper: fix "reference" leak Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14             ` Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 13/17] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
                               ` (5 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix an obscure leak in module_add(), if the "git add" command we were
piping to failed we'd fail to strbuf_release(&sb). This fixes a leak
introduced in a6226fd772b (submodule--helper: convert the bulk of
cmd_add() to C, 2021-08-10).

In fixing it move to a "goto cleanup" pattern, and since we need to
introduce a "ret" variable to do that let's also get rid of the
intermediate "exit_code" variable. The initialization to "-1" in
a6226fd772b has always been redundant, we'd only use the "exit_code"
value after assigning the return value of pipe_command() to it.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 170eaf010ac..675dc3f16a3 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3296,6 +3296,8 @@ static int module_add(int argc, const char **argv, const char *prefix)
 		N_("git submodule add [<options>] [--] <repository> [<path>]"),
 		NULL
 	};
+	struct strbuf sb = STRBUF_INIT;
+	int ret = 1;
 
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 
@@ -3345,21 +3347,17 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	die_on_repo_without_commits(add_data.sm_path);
 
 	if (!force) {
-		int exit_code = -1;
-		struct strbuf sb = STRBUF_INIT;
 		struct child_process cp = CHILD_PROCESS_INIT;
 
 		cp.git_cmd = 1;
 		cp.no_stdout = 1;
 		strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
 			     "--no-warn-embedded-repo", add_data.sm_path, NULL);
-		if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
+		if ((ret = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
 			strbuf_complete_line(&sb);
 			fputs(sb.buf, stderr);
-			free(add_data.sm_path);
-			return exit_code;
+			goto cleanup;
 		}
-		strbuf_release(&sb);
 	}
 
 	if(!add_data.sm_name)
@@ -3374,15 +3372,17 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	add_data.progress = !!progress;
 	add_data.dissociate = !!dissociate;
 
-	if (add_submodule(&add_data)) {
-		free(add_data.sm_path);
-		return 1;
-	}
+	if (add_submodule(&add_data))
+		goto cleanup;
 	configure_added_submodule(&add_data);
+
+	ret = 0;
+cleanup:
 	free(add_data.sm_path);
 	free(to_free);
+	strbuf_release(&sb);
 
-	return 0;
+	return ret;
 }
 
 #define SUPPORT_SUPER_PREFIX (1<<0)
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v7 13/17] submodule--helper: fix a leak in module_add()
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                               ` (11 preceding siblings ...)
  2022-08-31 23:14             ` [PATCH v7 12/17] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14             ` Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 14/17] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
                               ` (4 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in module_path(), since a6226fd772b (submodule--helper:
convert the bulk of cmd_add() to C, 2021-08-10), we've been freeing
add_data.sm_path, but in this case we clobbered it, and didn't free
the value we clobbered.

This makes test 28 of "t/t7400-submodule-basic.sh" ("submodule add in
subdirectory") pass when we're compiled with SANITIZE=leak..

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 675dc3f16a3..7c62916da3c 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3317,8 +3317,12 @@ static int module_add(int argc, const char **argv, const char *prefix)
 	else
 		add_data.sm_path = xstrdup(argv[1]);
 
-	if (prefix && *prefix && !is_absolute_path(add_data.sm_path))
-		add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
+	if (prefix && *prefix && !is_absolute_path(add_data.sm_path)) {
+		char *sm_path = add_data.sm_path;
+
+		add_data.sm_path = xstrfmt("%s%s", prefix, sm_path);
+		free(sm_path);
+	}
 
 	if (starts_with_dot_dot_slash(add_data.repo) ||
 	    starts_with_dot_slash(add_data.repo)) {
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v7 14/17] submodule--helper: fix a memory leak in print_status()
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                               ` (12 preceding siblings ...)
  2022-08-31 23:14             ` [PATCH v7 13/17] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14             ` Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 15/17] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
                               ` (3 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in print_status(), the compute_rev_name() function
implemented in this file will return a strbuf_detach()'d value, or
NULL.

This leak has existed since this code was added in
a9f8a37584a (submodule: port submodule subcommand 'status' from shell
to C, 2017-10-06), but in 0b5e2ea7cf3 (submodule--helper: don't print
null in 'submodule status', 2018-04-18) we added a "const"
intermediate variable for the return value, that "const" should be
removed.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 7c62916da3c..23c9e28deaa 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -583,10 +583,11 @@ static void print_status(unsigned int flags, char state, const char *path,
 	printf("%c%s %s", state, oid_to_hex(oid), displaypath);
 
 	if (state == ' ' || state == '+') {
-		const char *name = compute_rev_name(path, oid_to_hex(oid));
+		char *name = compute_rev_name(path, oid_to_hex(oid));
 
 		if (name)
 			printf(" (%s)", name);
+		free(name);
 	}
 
 	printf("\n");
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v7 15/17] submodule--helper: free some "displaypath" in "struct update_data"
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                               ` (13 preceding siblings ...)
  2022-08-31 23:14             ` [PATCH v7 14/17] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14             ` Ævar Arnfjörð Bjarmason
  2022-08-31 23:14             ` [PATCH v7 16/17] submodule--helper: free rest of " Ævar Arnfjörð Bjarmason
                               ` (2 subsequent siblings)
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Make the update_data_release() function free "displaypath" member when
appropriate. The "displaypath" member is always ours, the "const" on
the "char *" was wrong to begin with.

This leaves a leak of "displaypath" in update_submodule(), which as
we'll see in subsequent commits is harder to deal with than this
trivial fix.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c        | 3 ++-
 t/t2403-worktree-move.sh           | 1 +
 t/t7412-submodule-absorbgitdirs.sh | 1 +
 t/t7419-submodule-set-branch.sh    | 1 +
 4 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 23c9e28deaa..94ebd8ea38e 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1880,7 +1880,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)
 
 struct update_data {
 	const char *prefix;
-	const char *displaypath;
+	char *displaypath;
 	enum submodule_update_type update_default;
 	struct object_id suboid;
 	struct string_list references;
@@ -1918,6 +1918,7 @@ struct update_data {
 
 static void update_data_release(struct update_data *ud)
 {
+	free(ud->displaypath);
 	module_list_release(&ud->list);
 }
 
diff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh
index a4e1a178e0a..1168e9f9982 100755
--- a/t/t2403-worktree-move.sh
+++ b/t/t2403-worktree-move.sh
@@ -2,6 +2,7 @@
 
 test_description='test git worktree move, remove, lock and unlock'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh
index 1cfa150768d..2859695c6d2 100755
--- a/t/t7412-submodule-absorbgitdirs.sh
+++ b/t/t7412-submodule-absorbgitdirs.sh
@@ -6,6 +6,7 @@ This test verifies that `git submodue absorbgitdirs` moves a submodules git
 directory into the superproject.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a real submodule' '
diff --git a/t/t7419-submodule-set-branch.sh b/t/t7419-submodule-set-branch.sh
index 3b925c302fc..96e98423214 100755
--- a/t/t7419-submodule-set-branch.sh
+++ b/t/t7419-submodule-set-branch.sh
@@ -9,6 +9,7 @@ This test verifies that the set-branch subcommand of git-submodule is working
 as expected.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 TEST_NO_CREATE_REPO=1
 . ./test-lib.sh
 
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v7 16/17] submodule--helper: free rest of "displaypath" in "struct update_data"
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                               ` (14 preceding siblings ...)
  2022-08-31 23:14             ` [PATCH v7 15/17] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14             ` Ævar Arnfjörð Bjarmason
  2022-09-01 21:20               ` Glen Choo
  2022-08-31 23:14             ` [PATCH v7 17/17] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
  2022-09-01 21:23             ` [PATCH v7 00/17] submodule--helper: fix memory leaks Glen Choo
  17 siblings, 1 reply; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix a leak in code added in c51f8f94e5b (submodule--helper: run update
procedures from C, 2021-08-24), we clobber the "displaypath" member of
the passed-in "struct update_data" both so that die() messages in this
update_submodule() function itself can use it, and for the
run_update_procedure() called within this function.

Fix a leak in code added in 51f8f94e5b (submodule--helper: run update
procedures from C, 2021-08-24). We'd always clobber the old
"displaypath" member of the previously passed-in "struct update_data".

A better fix for this would be to remove the "displaypath" member from
the "struct update_data" entirely. Along with "oid", "suboid",
"just_cloned" and "sm_path" it's managing members that mainly need to
be passed between 1-3 stack frames of functions adjacent to this
code. But doing so would be a much larger change (I have it locally,
and fully untangling that in an incremental way is a 10 patch
journey).

So let's go for this much more isolated fix suggested by Glen. We
FREE_AND_NULL() the "update_data->displaypath", the "AND_NULL()" part
of that is needed due to the later "free(ud->displaypath)" in
"update_data_release()" introduced in the preceding commit

Moving ensure_core_worktree() out of update_submodule() may not be
strictly required, but in doing so we are left with the exact same
ordering as before, making this a smaller functional change.

Helped-by: Glen Choo <chooglen@google.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 94ebd8ea38e..1650bf0070b 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2487,13 +2487,6 @@ static int update_submodule(struct update_data *update_data)
 {
 	int ret;
 
-	ret = ensure_core_worktree(update_data->sm_path);
-	if (ret)
-		return ret;
-
-	update_data->displaypath = get_submodule_displaypath(
-		update_data->sm_path, update_data->prefix);
-
 	ret = determine_submodule_update_strategy(the_repository,
 						  update_data->just_cloned,
 						  update_data->sm_path,
@@ -2599,7 +2592,15 @@ static int update_submodules(struct update_data *update_data)
 		update_data->just_cloned = ucd.just_cloned;
 		update_data->sm_path = ucd.sub->path;
 
+		code = ensure_core_worktree(update_data->sm_path);
+		if (code)
+			goto fail;
+
+		update_data->displaypath = get_submodule_displaypath(
+			update_data->sm_path, update_data->prefix);
 		code = update_submodule(update_data);
+		FREE_AND_NULL(update_data->displaypath);
+fail:
 		if (!code)
 			continue;
 		ret = code;
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* [PATCH v7 17/17] submodule--helper: fix a configure_added_submodule() leak
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                               ` (15 preceding siblings ...)
  2022-08-31 23:14             ` [PATCH v7 16/17] submodule--helper: free rest of " Ævar Arnfjörð Bjarmason
@ 2022-08-31 23:14             ` Ævar Arnfjörð Bjarmason
  2022-09-01 21:23             ` [PATCH v7 00/17] submodule--helper: fix memory leaks Glen Choo
  17 siblings, 0 replies; 186+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-08-31 23:14 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Glen Choo, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Fix config API a memory leak added in a452128a36c (submodule--helper:
introduce add-config subcommand, 2021-08-06) by using the *_tmp()
variant of git_config_get_string().

In this case we're only checking whether
the (repo|git)_config_get_string() call is telling us that the
"submodule.active" key exists.

As with the preceding commit we'll find many other such patterns in
the codebase if we go fishing. E.g. "git gc" leaks in the code added
in 61f7a383d3b (maintenance: use 'incremental' strategy by default,
2020-10-15). Similar code in "git gc" added in
b08ff1fee00 (maintenance: add --schedule option and config,
2020-09-11) doesn't leak, but we could avoid the malloc() & free() in
that case.

A coccinelle rule to find those would find and fix some leaks, and
cases where we're doing needless malloc() + free()'s but only care
about the key existence, or are copying
the (repo|git)_config_get_string() return value right away.

But as with the preceding commit let's punt on all of that for now,
and just narrowly fix this specific case in submodule--helper.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c    | 4 ++--
 t/t7413-submodule-is-active.sh | 1 +
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 1650bf0070b..37b6db5cbb4 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3160,7 +3160,7 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con
 static void configure_added_submodule(struct add_data *add_data)
 {
 	char *key;
-	char *val = NULL;
+	const char *val;
 	struct child_process add_submod = CHILD_PROCESS_INIT;
 	struct child_process add_gitmodules = CHILD_PROCESS_INIT;
 
@@ -3205,7 +3205,7 @@ static void configure_added_submodule(struct add_data *add_data)
 	 * is_submodule_active(), since that function needs to find
 	 * out the value of "submodule.active" again anyway.
 	 */
-	if (!git_config_get_string("submodule.active", &val)) {
+	if (!git_config_get_string_tmp("submodule.active", &val)) {
 		/*
 		 * If the submodule being added isn't already covered by the
 		 * current configured pathspec, set the submodule's active flag
diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh
index ede6f02dbd5..4dc7d089423 100755
--- a/t/t7413-submodule-is-active.sh
+++ b/t/t7413-submodule-is-active.sh
@@ -9,6 +9,7 @@ This is a unit test of the submodule.c is_submodule_active() function,
 which is also indirectly tested elsewhere.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-- 
2.37.3.1420.g76f8a3d556c


^ permalink raw reply related	[flat|nested] 186+ messages in thread

* Re: [PATCH v7 16/17] submodule--helper: free rest of "displaypath" in "struct update_data"
  2022-08-31 23:14             ` [PATCH v7 16/17] submodule--helper: free rest of " Ævar Arnfjörð Bjarmason
@ 2022-09-01 21:20               ` Glen Choo
  0 siblings, 0 replies; 186+ messages in thread
From: Glen Choo @ 2022-09-01 21:20 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> A better fix for this would be to remove the "displaypath" member from
> the "struct update_data" entirely. Along with "oid", "suboid",
> "just_cloned" and "sm_path" it's managing members that mainly need to
> be passed between 1-3 stack frames of functions adjacent to this
> code. But doing so would be a much larger change (I have it locally,
> and fully untangling that in an incremental way is a 10 patch
> journey).

Yeah that does sound like a better fix _and_ too much churn right now.

> So let's go for this much more isolated fix suggested by Glen. We
> FREE_AND_NULL() the "update_data->displaypath", the "AND_NULL()" part
> of that is needed due to the later "free(ud->displaypath)" in
> "update_data_release()" introduced in the preceding commit
>
> Moving ensure_core_worktree() out of update_submodule() may not be
> strictly required, but in doing so we are left with the exact same
> ordering as before, making this a smaller functional change.

Ok, it's nice that we don't have to reason about functional changes,
though I doubt it will matter in this case.

I worry a bit about whether this is setting up an implicit contract
where we always need to call ensure_core_worktree() before
update_submodule(), but the contract has always been quite fuzzy here,
e.g. the "struct update_data" at this point contains a mix of CLI args +
clone result; we should fix that at some point, so doing this
change in the meantime seems harmless.

> Helped-by: Glen Choo <chooglen@google.com>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/submodule--helper.c | 15 ++++++++-------
>  1 file changed, 8 insertions(+), 7 deletions(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 94ebd8ea38e..1650bf0070b 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -2487,13 +2487,6 @@ static int update_submodule(struct update_data *update_data)
>  {
>  	int ret;
>  
> -	ret = ensure_core_worktree(update_data->sm_path);
> -	if (ret)
> -		return ret;
> -
> -	update_data->displaypath = get_submodule_displaypath(
> -		update_data->sm_path, update_data->prefix);
> -
>  	ret = determine_submodule_update_strategy(the_repository,
>  						  update_data->just_cloned,
>  						  update_data->sm_path,
> @@ -2599,7 +2592,15 @@ static int update_submodules(struct update_data *update_data)
>  		update_data->just_cloned = ucd.just_cloned;
>  		update_data->sm_path = ucd.sub->path;
>  
> +		code = ensure_core_worktree(update_data->sm_path);
> +		if (code)
> +			goto fail;
> +
> +		update_data->displaypath = get_submodule_displaypath(
> +			update_data->sm_path, update_data->prefix);
>  		code = update_submodule(update_data);
> +		FREE_AND_NULL(update_data->displaypath);
> +fail:
>  		if (!code)
>  			continue;
>  		ret = code;

Looks good.

> -- 
> 2.37.3.1420.g76f8a3d556c

^ permalink raw reply	[flat|nested] 186+ messages in thread

* Re: [PATCH v7 00/17] submodule--helper: fix memory leaks
  2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
                               ` (16 preceding siblings ...)
  2022-08-31 23:14             ` [PATCH v7 17/17] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
@ 2022-09-01 21:23             ` Glen Choo
  17 siblings, 0 replies; 186+ messages in thread
From: Glen Choo @ 2022-09-01 21:23 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Atharva Raykar, Prathamesh Chavan,
	Ævar Arnfjörð Bjarmason

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> This series fixes all of the memory leaks in
> builtin/submodule--helper.c that our test suite spots, except for
> those where the leak is downstream of cmd_submodule__helper() and
> caused by code that's not in builtin/submodule--helper.c (e.g. leaks
> in the object or config APIs, and in submodule.c).
>
> This re-roll is on top of the just-now re-rolled v4 of the base
> topic[1].
>
> Changes in v7:
>
>  * Rebase on top of the base topic.
>  * Clarify some questions on v6 in updated commit messages.
>  * Go with Glen's suggested "displaypath" fix. I also tried another
>    way which the commit discusses. The end result was better, but it
>    requires further submodule--helper cleanups, and this is already
>    quite an Odyssey. So FREE_AND_NULL() will do.
>
> 1. https://lore.kernel.org/git/cover-v4-00.33-00000000000-20220831T230519Z-avarab@gmail.com
>
> This series & passing CI can be seen at:
> https://github.com/avar/git/tree/avar/submodule--helper-memory-leaks-7

Thanks! And thanks especially for dealing with my stream of comments :)

This series + the prep series LGTM.

  Reviewed-by: Glen Choo <chooglen@google.com>

^ permalink raw reply	[flat|nested] 186+ messages in thread

end of thread, other threads:[~2022-09-01 21:23 UTC | newest]

Thread overview: 186+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-07-13 13:16 [PATCH 00/11] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
2022-07-13 13:16 ` [PATCH 01/11] submodule.c: free() memory from xgetcwd() Ævar Arnfjörð Bjarmason
2022-07-13 13:16 ` [PATCH 02/11] submodule--helper: replace memset() with { 0 }-initialization Ævar Arnfjörð Bjarmason
2022-07-13 21:00   ` Glen Choo
2022-07-13 21:19     ` Junio C Hamano
2022-07-13 22:41       ` Glen Choo
2022-07-14 10:25         ` Ævar Arnfjörð Bjarmason
2022-07-14 17:19           ` Junio C Hamano
2022-07-15  0:17           ` Glen Choo
2022-07-13 13:16 ` [PATCH 03/11] submodule--helper: fix "module_clone_data" memory leaks Ævar Arnfjörð Bjarmason
2022-07-13 17:37   ` Glen Choo
2022-07-13 18:05     ` Glen Choo
2022-07-13 20:30       ` Ævar Arnfjörð Bjarmason
2022-07-13 23:04         ` Glen Choo
2022-07-14  0:06           ` Ævar Arnfjörð Bjarmason
2022-07-13 13:16 ` [PATCH 04/11] submodule--helper: fix "struct pathspec" " Ævar Arnfjörð Bjarmason
2022-07-13 13:16 ` [PATCH 05/11] submodule--helper: free() leaking {run,capture}_command() argument Ævar Arnfjörð Bjarmason
2022-07-14  3:36   ` Glen Choo
2022-07-14 14:43     ` Ævar Arnfjörð Bjarmason
2022-07-13 13:16 ` [PATCH 06/11] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
2022-07-14 18:11   ` Glen Choo
2022-07-13 13:16 ` [PATCH 07/11] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
2022-07-14 18:20   ` Glen Choo
2022-07-13 13:16 ` [PATCH 08/11] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
2022-07-13 13:16 ` [PATCH 09/11] submodule--helper: free "char *" in "struct update_data" Ævar Arnfjörð Bjarmason
2022-07-14 18:33   ` Glen Choo
2022-07-18 16:10     ` Ævar Arnfjörð Bjarmason
2022-07-13 13:16 ` [PATCH 10/11] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
2022-07-13 13:16 ` [PATCH 11/11] submodule--helper: fix "reference" leak is "module_clone_data" Ævar Arnfjörð Bjarmason
2022-07-14 18:42   ` Glen Choo
2022-07-19 20:46 ` [PATCH v2 00/24] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
2022-07-19 20:46   ` [PATCH v2 01/24] submodule--helper: replace memset() with { 0 }-initialization Ævar Arnfjörð Bjarmason
2022-07-19 21:21     ` Junio C Hamano
2022-07-19 20:46   ` [PATCH v2 02/24] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
2022-07-19 21:31     ` Junio C Hamano
2022-07-19 20:46   ` [PATCH v2 03/24] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
2022-07-19 20:46   ` [PATCH v2 04/24] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
2022-07-20 16:33     ` Junio C Hamano
2022-07-21 17:49     ` Glen Choo
2022-07-19 20:46   ` [PATCH v2 05/24] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
2022-07-20 16:43     ` Junio C Hamano
2022-07-21 19:54     ` Glen Choo
2022-07-19 20:46   ` [PATCH v2 06/24] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
2022-07-19 20:46   ` [PATCH v2 07/24] submodule--helper: add "const" to copy of "update_data" Ævar Arnfjörð Bjarmason
2022-07-19 20:46   ` [PATCH v2 08/24] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
2022-07-20 17:06     ` Junio C Hamano
2022-07-21 17:44       ` Ævar Arnfjörð Bjarmason
2022-07-19 20:47   ` [PATCH v2 09/24] submodule--helper: refactor "errmsg_str" to be a "struct strbuf" Ævar Arnfjörð Bjarmason
2022-07-19 20:47   ` [PATCH v2 10/24] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
2022-07-19 20:47   ` [PATCH v2 11/24] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
2022-07-20 22:35     ` Junio C Hamano
2022-07-19 20:47   ` [PATCH v2 12/24] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
2022-07-19 20:47   ` [PATCH v2 13/24] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
2022-07-19 20:47   ` [PATCH v2 14/24] submodule--helper: fix "reference" leak is "module_clone_data" Ævar Arnfjörð Bjarmason
2022-07-21  0:50     ` Junio C Hamano
2022-07-19 20:47   ` [PATCH v2 15/24] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
2022-07-19 20:47   ` [PATCH v2 16/24] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
2022-07-19 20:47   ` [PATCH v2 17/24] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
2022-07-19 20:47   ` [PATCH v2 18/24] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
2022-07-19 20:47   ` [PATCH v2 19/24] submodule--helper: rename "int res" to "int ret" Ævar Arnfjörð Bjarmason
2022-07-19 20:47   ` [PATCH v2 20/24] submodule--helper: add skeleton "goto cleanup" to update_submodule() Ævar Arnfjörð Bjarmason
2022-07-19 20:47   ` [PATCH v2 21/24] submodule--helper: don't exit() on failure, return Ævar Arnfjörð Bjarmason
2022-07-19 20:47   ` [PATCH v2 22/24] submodule--helper: free rest of "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
2022-07-19 20:47   ` [PATCH v2 23/24] submodule--helper: fix bad config API usage Ævar Arnfjörð Bjarmason
2022-07-19 20:47   ` [PATCH v2 24/24] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
2022-07-21 19:12   ` [PATCH v3 00/26] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
2022-07-21 19:12     ` [PATCH v3 01/26] submodule--helper: replace memset() with { 0 }-initialization Ævar Arnfjörð Bjarmason
2022-07-21 19:12     ` [PATCH v3 02/26] submodule--helper: stop conflating "sb" in clone_submodule() Ævar Arnfjörð Bjarmason
2022-07-21 21:16       ` Junio C Hamano
2022-07-22 13:50         ` Ævar Arnfjörð Bjarmason
2022-07-22 16:48           ` Glen Choo
2022-07-22 18:47           ` Junio C Hamano
2022-07-28 16:30             ` Ævar Arnfjörð Bjarmason
2022-07-21 19:12     ` [PATCH v3 03/26] submodule--helper: pass a "const struct module_clone_data" to clone_submodule() Ævar Arnfjörð Bjarmason
2022-07-21 21:26       ` Junio C Hamano
2022-07-25 17:07       ` Glen Choo
2022-07-21 19:13     ` [PATCH v3 04/26] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
2022-07-21 21:30       ` Junio C Hamano
2022-07-22 11:30         ` Ævar Arnfjörð Bjarmason
2022-07-21 19:13     ` [PATCH v3 05/26] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
2022-07-21 19:13     ` [PATCH v3 06/26] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
2022-07-21 21:37       ` Junio C Hamano
2022-07-21 19:13     ` [PATCH v3 07/26] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
2022-07-21 19:13     ` [PATCH v3 08/26] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
2022-07-21 19:13     ` [PATCH v3 09/26] submodule--helper: add "const" to copy of "update_data" Ævar Arnfjörð Bjarmason
2022-07-21 19:13     ` [PATCH v3 10/26] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
2022-07-21 19:13     ` [PATCH v3 11/26] submodule--helper: refactor "errmsg_str" to be a "struct strbuf" Ævar Arnfjörð Bjarmason
2022-07-25 23:15       ` Glen Choo
2022-07-21 19:13     ` [PATCH v3 12/26] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
2022-07-21 19:13     ` [PATCH v3 13/26] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
2022-07-21 19:13     ` [PATCH v3 14/26] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
2022-07-21 19:13     ` [PATCH v3 15/26] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
2022-07-21 19:13     ` [PATCH v3 16/26] submodule--helper: fix "reference" leak is "module_clone_data" Ævar Arnfjörð Bjarmason
2022-07-21 19:13     ` [PATCH v3 17/26] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
2022-07-21 21:45       ` Junio C Hamano
2022-07-21 19:13     ` [PATCH v3 18/26] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
2022-07-21 19:13     ` [PATCH v3 19/26] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
2022-07-21 19:13     ` [PATCH v3 20/26] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
2022-07-21 19:13     ` [PATCH v3 21/26] submodule--helper: rename "int res" to "int ret" Ævar Arnfjörð Bjarmason
2022-07-21 19:13     ` [PATCH v3 22/26] submodule--helper: add skeleton "goto cleanup" to update_submodule() Ævar Arnfjörð Bjarmason
2022-07-21 19:13     ` [PATCH v3 23/26] submodule--helper: don't exit() on failure, return Ævar Arnfjörð Bjarmason
2022-07-25 23:57       ` Glen Choo
2022-07-21 19:13     ` [PATCH v3 24/26] submodule--helper: free rest of "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
2022-07-26  1:06       ` Glen Choo
2022-07-21 19:13     ` [PATCH v3 25/26] submodule--helper: fix bad config API usage Ævar Arnfjörð Bjarmason
2022-07-21 19:13     ` [PATCH v3 26/26] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
2022-07-21 21:51       ` Junio C Hamano
2022-07-28 16:29     ` [PATCH v4 00/17] submodule--helper: (only) fix memory leaks Ævar Arnfjörð Bjarmason
2022-07-28 16:29       ` [PATCH v4 01/17] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
2022-07-28 16:29       ` [PATCH v4 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
2022-07-28 16:29       ` [PATCH v4 03/17] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
2022-07-28 16:29       ` [PATCH v4 04/17] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
2022-07-28 16:29       ` [PATCH v4 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
2022-07-28 16:30       ` [PATCH v4 06/17] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
2022-07-28 16:30       ` [PATCH v4 07/17] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
2022-07-28 16:30       ` [PATCH v4 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
2022-07-28 16:30       ` [PATCH v4 09/17] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
2022-07-28 16:30       ` [PATCH v4 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
2022-07-28 16:30       ` [PATCH v4 11/17] submodule--helper: fix "reference" leak Ævar Arnfjörð Bjarmason
2022-07-28 16:30       ` [PATCH v4 12/17] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
2022-07-28 16:30       ` [PATCH v4 13/17] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
2022-07-28 16:30       ` [PATCH v4 14/17] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
2022-07-28 16:30       ` [PATCH v4 15/17] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
2022-07-28 16:30       ` [PATCH v4 16/17] submodule--helper: free rest of " Ævar Arnfjörð Bjarmason
2022-07-28 16:30       ` [PATCH v4 17/17] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
2022-08-02 15:54       ` [PATCH v5 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
2022-08-02 15:54         ` [PATCH v5 01/17] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
2022-08-02 15:54         ` [PATCH v5 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
2022-08-02 15:54         ` [PATCH v5 03/17] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
2022-08-03 22:59           ` Glen Choo
2022-08-04  7:04             ` Ævar Arnfjörð Bjarmason
2022-08-02 15:54         ` [PATCH v5 04/17] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
2022-08-02 15:54         ` [PATCH v5 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
2022-08-02 15:54         ` [PATCH v5 06/17] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
2022-08-02 15:54         ` [PATCH v5 07/17] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
2022-08-02 15:54         ` [PATCH v5 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
2022-08-03 23:10           ` Glen Choo
2022-08-02 15:54         ` [PATCH v5 09/17] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
2022-08-02 15:54         ` [PATCH v5 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
2022-08-02 15:54         ` [PATCH v5 11/17] submodule--helper: fix "reference" leak Ævar Arnfjörð Bjarmason
2022-08-02 15:54         ` [PATCH v5 12/17] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
2022-08-02 15:54         ` [PATCH v5 13/17] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
2022-08-02 15:54         ` [PATCH v5 14/17] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
2022-08-02 15:54         ` [PATCH v5 15/17] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
2022-08-02 15:54         ` [PATCH v5 16/17] submodule--helper: free rest of " Ævar Arnfjörð Bjarmason
2022-08-04 18:04           ` Glen Choo
2022-08-02 15:54         ` [PATCH v5 17/17] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
2022-08-21 13:59         ` [PATCH v6 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
2022-08-21 13:59           ` [PATCH v6 01/17] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
2022-08-21 13:59           ` [PATCH v6 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
2022-08-21 13:59           ` [PATCH v6 03/17] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
2022-08-21 13:59           ` [PATCH v6 04/17] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
2022-08-21 13:59           ` [PATCH v6 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
2022-08-21 13:59           ` [PATCH v6 06/17] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
2022-08-21 13:59           ` [PATCH v6 07/17] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
2022-08-21 13:59           ` [PATCH v6 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
2022-08-21 13:59           ` [PATCH v6 09/17] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
2022-08-21 13:59           ` [PATCH v6 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
2022-08-21 13:59           ` [PATCH v6 11/17] submodule--helper: fix "reference" leak Ævar Arnfjörð Bjarmason
2022-08-21 13:59           ` [PATCH v6 12/17] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
2022-08-21 13:59           ` [PATCH v6 13/17] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
2022-08-21 13:59           ` [PATCH v6 14/17] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
2022-08-21 13:59           ` [PATCH v6 15/17] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
2022-08-21 13:59           ` [PATCH v6 16/17] submodule--helper: free rest of " Ævar Arnfjörð Bjarmason
2022-08-24 23:03             ` Glen Choo
2022-08-21 13:59           ` [PATCH v6 17/17] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
2022-08-31 23:14           ` [PATCH v7 00/17] submodule--helper: fix memory leaks Ævar Arnfjörð Bjarmason
2022-08-31 23:14             ` [PATCH v7 01/17] submodule--helper: fix a leak in "clone_submodule" Ævar Arnfjörð Bjarmason
2022-08-31 23:14             ` [PATCH v7 02/17] submodule--helper: fix trivial get_default_remote_submodule() leak Ævar Arnfjörð Bjarmason
2022-08-31 23:14             ` [PATCH v7 03/17] submodule--helper: fix most "struct pathspec" memory leaks Ævar Arnfjörð Bjarmason
2022-08-31 23:14             ` [PATCH v7 04/17] submodule--helper: "struct pathspec" memory leak in module_update() Ævar Arnfjörð Bjarmason
2022-08-31 23:14             ` [PATCH v7 05/17] submodule--helper: don't leak {run,capture}_command() cp.dir argument Ævar Arnfjörð Bjarmason
2022-08-31 23:14             ` [PATCH v7 06/17] submodule--helper: add and use *_release() functions Ævar Arnfjörð Bjarmason
2022-08-31 23:14             ` [PATCH v7 07/17] submodule--helper: fix "errmsg_str" memory leak Ævar Arnfjörð Bjarmason
2022-08-31 23:14             ` [PATCH v7 08/17] submodule--helper: fix "sm_path" and other "module_cb_list" leaks Ævar Arnfjörð Bjarmason
2022-08-31 23:14             ` [PATCH v7 09/17] submodule--helper: fix a leak with repo_clear() Ævar Arnfjörð Bjarmason
2022-08-31 23:14             ` [PATCH v7 10/17] submodule--helper: fix a memory leak in get_default_remote_submodule() Ævar Arnfjörð Bjarmason
2022-08-31 23:14             ` [PATCH v7 11/17] submodule--helper: fix "reference" leak Ævar Arnfjörð Bjarmason
2022-08-31 23:14             ` [PATCH v7 12/17] submodule--helper: fix obscure leak in module_add() Ævar Arnfjörð Bjarmason
2022-08-31 23:14             ` [PATCH v7 13/17] submodule--helper: fix a " Ævar Arnfjörð Bjarmason
2022-08-31 23:14             ` [PATCH v7 14/17] submodule--helper: fix a memory leak in print_status() Ævar Arnfjörð Bjarmason
2022-08-31 23:14             ` [PATCH v7 15/17] submodule--helper: free some "displaypath" in "struct update_data" Ævar Arnfjörð Bjarmason
2022-08-31 23:14             ` [PATCH v7 16/17] submodule--helper: free rest of " Ævar Arnfjörð Bjarmason
2022-09-01 21:20               ` Glen Choo
2022-08-31 23:14             ` [PATCH v7 17/17] submodule--helper: fix a configure_added_submodule() leak Ævar Arnfjörð Bjarmason
2022-09-01 21:23             ` [PATCH v7 00/17] submodule--helper: fix memory leaks Glen Choo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).