Blame SOURCES/git-cve-2019-1387.patch

77e4c1
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
77e4c1
index cc79d059f2..29adde60bd 100644
77e4c1
--- a/builtin/submodule--helper.c
77e4c1
+++ b/builtin/submodule--helper.c
77e4c1
@@ -25,11 +25,32 @@ static int check_name(int argc, const char **argv, const char *prefix)
77e4c1
 	return 0;
77e4c1
 }
77e4c1
 
77e4c1
+/*
77e4c1
+ * Exit non-zero if the proposed submodule repository path is inside
77e4c1
+ * another submodules' git dir.
77e4c1
+ */
77e4c1
+static int validate_git_dir(int argc, const char **argv, const char *prefix)
77e4c1
+{
77e4c1
+	char *sm_gitdir;
77e4c1
+
77e4c1
+	if (argc != 3)
77e4c1
+		usage("git submodule--helper validate-git-dir <path> <name>");
77e4c1
+	sm_gitdir = xstrdup(argv[1]);
77e4c1
+	if (validate_submodule_git_dir(sm_gitdir, argv[2]) < 0) {
77e4c1
+		free(sm_gitdir);
77e4c1
+		return 1;
77e4c1
+	}
77e4c1
+	free(sm_gitdir);
77e4c1
+	return 0;
77e4c1
+}
77e4c1
+
77e4c1
 int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
77e4c1
 {
77e4c1
 	if (argc < 2)
77e4c1
 		usage("git submodule--helper <command>");
77e4c1
 	if (!strcmp(argv[1], "check-name"))
77e4c1
 		return check_name(argc - 1, argv + 1, prefix);
77e4c1
+	if (!strcmp(argv[1], "validate-git-dir"))
77e4c1
+		return validate_git_dir(argc - 1, argv + 1, prefix);
77e4c1
 	die(_("'%s' is not a valid submodule--helper subcommand"), argv[1]);
77e4c1
 }
77e4c1
diff -ruNp a/git-submodule.sh b/git-submodule.sh
77e4c1
--- a/git-submodule.sh	2020-01-09 20:01:48.885647299 +0100
77e4c1
+++ b/git-submodule.sh	2020-01-10 08:42:05.107514269 +0100
77e4c1
@@ -253,6 +253,11 @@ module_clone()
77e4c1
 	gitdir_base="$gitdir/modules/$base_name"
77e4c1
 	gitdir="$gitdir/modules/$name"
77e4c1
 
77e4c1
+        if ! git submodule--helper validate-git-dir "$gitdir" "$name"
77e4c1
+        then
77e4c1
+	        die "$(eval_gettextln "refusing to create/use '\$gitdir' in another submodule's git dir")"
77e4c1
+        fi
77e4c1
+
77e4c1
 	if test -d "$gitdir"
77e4c1
 	then
77e4c1
 		mkdir -p "$sm_path"
77e4c1
77e4c1
diff --git a/git-submodule.sh b/git-submodule.sh
77e4c1
index ca16579c3c..bf5ffdb1f6 100755
77e4c1
--- a/git-submodule.sh
77e4c1
+++ b/git-submodule.sh
77e4c1
@@ -419,6 +419,11 @@ Use -f if you really want to add it." >&2
77e4c1
 		fi
77e4c1
 
77e4c1
 	else
77e4c1
+		sm_gitdir=".git/modules/$sm_name"
77e4c1
+		if ! git submodule--helper validate-git-dir "$sm_gitdir" "$sm_name"
77e4c1
+		then
77e4c1
+			die "$(eval_gettextln "refusing to create/use '\$sm_gitdir' in another submodule's git dir")"
77e4c1
+		fi
77e4c1
 		if test -d ".git/modules/$sm_name"
77e4c1
 		then
77e4c1
 			if test -z "$force"
77e4c1
diff --git a/submodule.c b/submodule.c
77e4c1
index 6337cab091..9927f56a33 100644
77e4c1
--- a/submodule.c
77e4c1
+++ b/submodule.c
77e4c1
@@ -1034,3 +1034,43 @@ int merge_submodule(unsigned char result[20], const char *path,
77e4c1
 	free(merges.objects);
77e4c1
 	return 0;
77e4c1
 }
77e4c1
+int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
77e4c1
+{
77e4c1
+	size_t len = strlen(git_dir), suffix_len = strlen(submodule_name);
77e4c1
+	char *p;
77e4c1
+	int ret = 0;
77e4c1
+
77e4c1
+	if (len <= suffix_len || (p = git_dir + len - suffix_len)[-1] != '/' ||
77e4c1
+	    strcmp(p, submodule_name))
77e4c1
+		die("BUG: submodule name '%s' not a suffix of git dir '%s'",
77e4c1
+		    submodule_name, git_dir);
77e4c1
+
77e4c1
+	/*
77e4c1
+	 * We prevent the contents of sibling submodules' git directories to
77e4c1
+	 * clash.
77e4c1
+	 *
77e4c1
+	 * Example: having a submodule named `hippo` and another one named
77e4c1
+	 * `hippo/hooks` would result in the git directories
77e4c1
+	 * `.git/modules/hippo/` and `.git/modules/hippo/hooks/`, respectively,
77e4c1
+	 * but the latter directory is already designated to contain the hooks
77e4c1
+	 * of the former.
77e4c1
+	 */
77e4c1
+	for (; *p; p++) {
77e4c1
+		if (is_dir_sep(*p)) {
77e4c1
+			char c = *p;
77e4c1
+
77e4c1
+			*p = '\0';
77e4c1
+			if (is_git_directory(git_dir))
77e4c1
+				ret = -1;
77e4c1
+			*p = c;
77e4c1
+
77e4c1
+			if (ret < 0)
77e4c1
+				return error(_("submodule git dir '%s' is "
77e4c1
+					       "inside git dir '%.*s'"),
77e4c1
+					     git_dir,
77e4c1
+					     (int)(p - git_dir), git_dir);
77e4c1
+		}
77e4c1
+	}
77e4c1
+
77e4c1
+	return 0;
77e4c1
+}
77e4c1
diff --git a/submodule.h b/submodule.h
77e4c1
index 59dbdfbd17..9c99457a3f 100644
77e4c1
--- a/submodule.h
77e4c1
+++ b/submodule.h
77e4c1
@@ -37,6 +37,11 @@ int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_nam
77e4c1
 		struct string_list *needs_pushing);
77e4c1
 int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name);
77e4c1
 
77e4c1
+/*
77e4c1
+ * Make sure that no submodule's git dir is nested in a sibling submodule's.
77e4c1
+ */
77e4c1
+int validate_submodule_git_dir(char *git_dir, const char *submodule_name);
77e4c1
+
77e4c1
 /*
77e4c1
  * Returns 0 if the name is syntactically acceptable as a submodule "name"
77e4c1
  * (e.g., that may be found in the subsection of a .gitmodules file) and -1
77e4c1
diff --git a/t/t7415-submodule-names.sh b/t/t7415-submodule-names.sh
77e4c1
index 6456d5ae43..29393de617 100755
77e4c1
--- a/t/t7415-submodule-names.sh
77e4c1
+++ b/t/t7415-submodule-names.sh
77e4c1
@@ -151,4 +151,27 @@ test_expect_success 'fsck detects symlinked .gitmodules file' '
77e4c1
 	)
77e4c1
 '
77e4c1
 
77e4c1
+test_expect_success 'git dirs of sibling submodules must not be nested' '
77e4c1
+	git init nested &&
77e4c1
+	(
77e4c1
+		cd nested &&
77e4c1
+		test_commit nested &&
77e4c1
+		cat >.gitmodules <<-EOF &&
77e4c1
+		[submodule "hippo"]
77e4c1
+			url = .
77e4c1
+			path = thing1
77e4c1
+		[submodule "hippo/hooks"]
77e4c1
+			url = .
77e4c1
+			path = thing2
77e4c1
+		EOF
77e4c1
+		git clone . thing1 &&
77e4c1
+		git clone . thing2 &&
77e4c1
+		git add .gitmodules thing1 thing2 &&
77e4c1
+		test_tick &&
77e4c1
+		git commit -m nested
77e4c1
+	) &&
77e4c1
+	test_must_fail git clone --recurse-submodules nested clone 2>err &&
77e4c1
+	test_i18ngrep "is inside git dir" err
77e4c1
+'
77e4c1
+
77e4c1
 test_done
77e4c1