From 6fac5b2f352efc8c246d6d5be63a66b7b0fc0209 Mon Sep 17 00:00:00 2001
From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Date: Thu, 1 Sep 2022 01:17:56 +0200
Subject: [PATCH] submodule--helper: add "const" to passed "module_clone_data"

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". Let's do this by having clone_submodule() create its
own, and copy the contents over, allowing us to pass it as a
separate parameter.

This new "struct string_list" still leaks memory, just as the "struct
module_clone_data" did before. let's not fix that for now, to fix that
we'll need to add some "goto cleanup" to the relevant code. That will
eventually be done in follow-up commits, this change makes it easier
to fix the memory leak.

The scope of the new "reference" variable in add_submodule() could be
narrowed to the "else" block, but as we'll eventually free it with a
"goto cleanup" let's declare it at the start of the function.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Reviewed-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

CVE: CVE-2024-32002

Upstream-Status: Backport [https://github.com/git/git/commit/6fac5b2f352efc8c246d6d5be63a66b7b0fc0209]

Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
---
 builtin/submodule--helper.c | 52 ++++++++++++++++++++-----------------
 1 file changed, 28 insertions(+), 24 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 79868c3..d1d64db 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1630,14 +1630,13 @@ struct module_clone_data {
	const char *name;
	const char *url;
	const char *depth;
-	struct string_list reference;
	unsigned int quiet: 1;
	unsigned int progress: 1;
	unsigned int dissociate: 1;
	unsigned int require_init: 1;
	int single_branch;
 };
-#define MODULE_CLONE_DATA_INIT { .reference = STRING_LIST_INIT_NODUP, .single_branch = -1 }
+#define MODULE_CLONE_DATA_INIT { .single_branch = -1 }

 struct submodule_alternate_setup {
	const char *submodule_name;
@@ -1750,22 +1749,24 @@ static void prepare_possible_alternates(const char *sm_name,
	free(error_strategy);
 }

-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, *sm_gitdir;
	char *sm_alternate = NULL, *error_strategy = NULL;
	struct strbuf sb = STRBUF_INIT;
	struct child_process cp = CHILD_PROCESS_INIT;
+	const char *clone_data_path;

	submodule_name_to_gitdir(&sb, the_repository, clone_data->name);
	sm_gitdir = absolute_pathdup(sb.buf);
	strbuf_reset(&sb);

	if (!is_absolute_path(clone_data->path))
-		clone_data->path = xstrfmt("%s/%s", get_git_work_tree(),
-					   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 = 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 "
@@ -1775,7 +1776,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");
@@ -1784,10 +1785,10 @@ static int clone_submodule(struct module_clone_data *clone_data)
		if (clone_data->progress)
			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) {
+				strvec_pushl(&cp.args, "--depth", clone_data->depth, NULL);
+		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);
		}
@@ -1802,7 +1803,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_array);
@@ -1810,25 +1811,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 {
		char *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);
+		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);
		path = xstrfmt("%s/index", sm_gitdir);
		unlink_or_warn(path);
		free(path);
	}

-	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);
@@ -1853,6 +1854,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 string_list reference = STRING_LIST_INIT_NODUP;

	struct option module_clone_options[] = {
		OPT_STRING(0, "prefix", &clone_data.prefix,
@@ -1867,7 +1869,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
		OPT_STRING(0, "url", &clone_data.url,
			   N_("string"),
			   N_("url where to clone the submodule from")),
-		OPT_STRING_LIST(0, "reference", &clone_data.reference,
+		OPT_STRING_LIST(0, "reference", &reference,
			   N_("repo"),
			   N_("reference repository")),
		OPT_BOOL(0, "dissociate", &dissociate,
@@ -1905,7 +1907,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, &reference);
	return 0;
 }

@@ -3029,6 +3031,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;

	/* perhaps the path already exists and is already a git repo, else clone it */
	if (is_directory(add_data->sm_path)) {
@@ -3045,6 +3048,7 @@ static int add_submodule(const struct add_data *add_data)
		free(submod_gitdir_path);
	} else {
		struct child_process cp = CHILD_PROCESS_INIT;
+
		submod_gitdir_path = xstrfmt(".git/modules/%s", add_data->sm_name);

		if (is_directory(submod_gitdir_path)) {
@@ -3084,13 +3088,13 @@ static int add_submodule(const struct add_data *add_data)
		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));
		clone_data.dissociate = add_data->dissociate;
		if (add_data->depth >= 0)
			clone_data.depth = xstrfmt("%d", add_data->depth);

-		if (clone_submodule(&clone_data))
+		if (clone_submodule(&clone_data, &reference))
			return -1;

		prepare_submodule_repo_env(&cp.env_array);
--
2.40.0
