Skip to content

Commit 76a681c

Browse files
committed
Merge branch 'dubiously-nested-submodules'
Recursive clones are currently affected by a vulnerability that is caused by too-lax validation of submodule names. This topic branch fixes that. Signed-off-by: Johannes Schindelin <[email protected]>
2 parents dd53ea7 + a8dee3c commit 76a681c

File tree

4 files changed

+79
-2
lines changed

4 files changed

+79
-2
lines changed

builtin/submodule--helper.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,10 @@ static int module_clone(int argc, const char **argv, const char *prefix)
678678
} else
679679
path = xstrdup(path);
680680

681+
if (validate_submodule_git_dir(sm_gitdir, name) < 0)
682+
die(_("refusing to create/use '%s' in another submodule's "
683+
"git dir"), sm_gitdir);
684+
681685
if (!file_exists(sm_gitdir)) {
682686
if (safe_create_leading_directories_const(sm_gitdir) < 0)
683687
die(_("could not create directory '%s'"), sm_gitdir);

submodule.c

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1842,6 +1842,47 @@ int parallel_submodules(void)
18421842
return parallel_jobs;
18431843
}
18441844

1845+
int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
1846+
{
1847+
size_t len = strlen(git_dir), suffix_len = strlen(submodule_name);
1848+
char *p;
1849+
int ret = 0;
1850+
1851+
if (len <= suffix_len || (p = git_dir + len - suffix_len)[-1] != '/' ||
1852+
strcmp(p, submodule_name))
1853+
BUG("submodule name '%s' not a suffix of git dir '%s'",
1854+
submodule_name, git_dir);
1855+
1856+
/*
1857+
* We prevent the contents of sibling submodules' git directories to
1858+
* clash.
1859+
*
1860+
* Example: having a submodule named `hippo` and another one named
1861+
* `hippo/hooks` would result in the git directories
1862+
* `.git/modules/hippo/` and `.git/modules/hippo/hooks/`, respectively,
1863+
* but the latter directory is already designated to contain the hooks
1864+
* of the former.
1865+
*/
1866+
for (; *p; p++) {
1867+
if (is_dir_sep(*p)) {
1868+
char c = *p;
1869+
1870+
*p = '\0';
1871+
if (is_git_directory(git_dir))
1872+
ret = -1;
1873+
*p = c;
1874+
1875+
if (ret < 0)
1876+
return error(_("submodule git dir '%s' is "
1877+
"inside git dir '%.*s'"),
1878+
git_dir,
1879+
(int)(p - git_dir), git_dir);
1880+
}
1881+
}
1882+
1883+
return 0;
1884+
}
1885+
18451886
/*
18461887
* Embeds a single submodules git directory into the superprojects git dir,
18471888
* non recursively.
@@ -1850,7 +1891,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
18501891
const char *path)
18511892
{
18521893
char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL;
1853-
const char *new_git_dir;
1894+
char *new_git_dir;
18541895
const struct submodule *sub;
18551896

18561897
if (submodule_uses_worktrees(path))
@@ -1868,10 +1909,14 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
18681909
if (!sub)
18691910
die(_("could not lookup name for submodule '%s'"), path);
18701911

1871-
new_git_dir = git_path("modules/%s", sub->name);
1912+
new_git_dir = git_pathdup("modules/%s", sub->name);
1913+
if (validate_submodule_git_dir(new_git_dir, sub->name) < 0)
1914+
die(_("refusing to move '%s' into an existing git dir"),
1915+
real_old_git_dir);
18721916
if (safe_create_leading_directories_const(new_git_dir) < 0)
18731917
die(_("could not create directory '%s'"), new_git_dir);
18741918
real_new_git_dir = real_pathdup(new_git_dir, 1);
1919+
free(new_git_dir);
18751920

18761921
fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
18771922
get_super_prefix_or_empty(), path,

submodule.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ extern int parallel_submodules(void);
120120
*/
121121
int submodule_to_gitdir(struct strbuf *buf, const char *submodule);
122122

123+
/*
124+
* Make sure that no submodule's git dir is nested in a sibling submodule's.
125+
*/
126+
int validate_submodule_git_dir(char *git_dir, const char *submodule_name);
127+
123128
#define SUBMODULE_MOVE_HEAD_DRY_RUN (1<<0)
124129
#define SUBMODULE_MOVE_HEAD_FORCE (1<<1)
125130
extern int submodule_move_head(const char *path,

t/t7415-submodule-names.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,27 @@ test_expect_success MINGW 'prevent git~1 squatting on Windows' '
106106
! grep gitdir squatting-clone/d/a/git~2
107107
'
108108

109+
test_expect_success 'git dirs of sibling submodules must not be nested' '
110+
git init nested &&
111+
test_commit -C nested nested &&
112+
(
113+
cd nested &&
114+
cat >.gitmodules <<-EOF &&
115+
[submodule "hippo"]
116+
url = .
117+
path = thing1
118+
[submodule "hippo/hooks"]
119+
url = .
120+
path = thing2
121+
EOF
122+
git clone . thing1 &&
123+
git clone . thing2 &&
124+
git add .gitmodules thing1 thing2 &&
125+
test_tick &&
126+
git commit -m nested
127+
) &&
128+
test_must_fail git clone --recurse-submodules nested clone 2>err &&
129+
test_i18ngrep "is inside git dir" err
130+
'
131+
109132
test_done

0 commit comments

Comments
 (0)