@@ -294,6 +294,9 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
294294 struct child_process cp = CHILD_PROCESS_INIT ;
295295 char * displaypath ;
296296
297+ if (validate_submodule_path (path ) < 0 )
298+ exit (128 );
299+
297300 displaypath = get_submodule_displaypath (path , info -> prefix );
298301
299302 sub = submodule_from_path (the_repository , null_oid (), path );
@@ -620,6 +623,9 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
620623 .free_removed_argv_elements = 1 ,
621624 };
622625
626+ if (validate_submodule_path (path ) < 0 )
627+ exit (128 );
628+
623629 if (!submodule_from_path (the_repository , null_oid (), path ))
624630 die (_ ("no submodule mapping found in .gitmodules for path '%s'" ),
625631 path );
@@ -1220,6 +1226,9 @@ static void sync_submodule(const char *path, const char *prefix,
12201226 if (!is_submodule_active (the_repository , path ))
12211227 return ;
12221228
1229+ if (validate_submodule_path (path ) < 0 )
1230+ exit (128 );
1231+
12231232 sub = submodule_from_path (the_repository , null_oid (), path );
12241233
12251234 if (sub && sub -> url ) {
@@ -1360,6 +1369,9 @@ static void deinit_submodule(const char *path, const char *prefix,
13601369 struct strbuf sb_config = STRBUF_INIT ;
13611370 char * sub_git_dir = xstrfmt ("%s/.git" , path );
13621371
1372+ if (validate_submodule_path (path ) < 0 )
1373+ exit (128 );
1374+
13631375 sub = submodule_from_path (the_repository , null_oid (), path );
13641376
13651377 if (!sub || !sub -> name )
@@ -1641,16 +1653,42 @@ static char *clone_submodule_sm_gitdir(const char *name)
16411653 return sm_gitdir ;
16421654}
16431655
1656+ static int dir_contains_only_dotgit (const char * path )
1657+ {
1658+ DIR * dir = opendir (path );
1659+ struct dirent * e ;
1660+ int ret = 1 ;
1661+
1662+ if (!dir )
1663+ return 0 ;
1664+
1665+ e = readdir_skip_dot_and_dotdot (dir );
1666+ if (!e )
1667+ ret = 0 ;
1668+ else if (strcmp (DEFAULT_GIT_DIR_ENVIRONMENT , e -> d_name ) ||
1669+ (e = readdir_skip_dot_and_dotdot (dir ))) {
1670+ error ("unexpected item '%s' in '%s'" , e -> d_name , path );
1671+ ret = 0 ;
1672+ }
1673+
1674+ closedir (dir );
1675+ return ret ;
1676+ }
1677+
16441678static int clone_submodule (const struct module_clone_data * clone_data ,
16451679 struct string_list * reference )
16461680{
16471681 char * p ;
16481682 char * sm_gitdir = clone_submodule_sm_gitdir (clone_data -> name );
16491683 char * sm_alternate = NULL , * error_strategy = NULL ;
1684+ struct stat st ;
16501685 struct child_process cp = CHILD_PROCESS_INIT ;
16511686 const char * clone_data_path = clone_data -> path ;
16521687 char * to_free = NULL ;
16531688
1689+ if (validate_submodule_path (clone_data_path ) < 0 )
1690+ exit (128 );
1691+
16541692 if (!is_absolute_path (clone_data -> path ))
16551693 clone_data_path = to_free = xstrfmt ("%s/%s" , get_git_work_tree (),
16561694 clone_data -> path );
@@ -1660,6 +1698,10 @@ static int clone_submodule(const struct module_clone_data *clone_data,
16601698 "git dir" ), sm_gitdir );
16611699
16621700 if (!file_exists (sm_gitdir )) {
1701+ if (clone_data -> require_init && !stat (clone_data_path , & st ) &&
1702+ !is_empty_dir (clone_data_path ))
1703+ die (_ ("directory not empty: '%s'" ), clone_data_path );
1704+
16631705 if (safe_create_leading_directories_const (sm_gitdir ) < 0 )
16641706 die (_ ("could not create directory '%s'" ), sm_gitdir );
16651707
@@ -1704,10 +1746,18 @@ static int clone_submodule(const struct module_clone_data *clone_data,
17041746 if (run_command (& cp ))
17051747 die (_ ("clone of '%s' into submodule path '%s' failed" ),
17061748 clone_data -> url , clone_data_path );
1749+
1750+ if (clone_data -> require_init && !stat (clone_data_path , & st ) &&
1751+ !dir_contains_only_dotgit (clone_data_path )) {
1752+ char * dot_git = xstrfmt ("%s/.git" , clone_data_path );
1753+ unlink (dot_git );
1754+ free (dot_git );
1755+ die (_ ("directory not empty: '%s'" ), clone_data_path );
1756+ }
17071757 } else {
17081758 char * path ;
17091759
1710- if (clone_data -> require_init && !access (clone_data_path , X_OK ) &&
1760+ if (clone_data -> require_init && !stat (clone_data_path , & st ) &&
17111761 !is_empty_dir (clone_data_path ))
17121762 die (_ ("directory not empty: '%s'" ), clone_data_path );
17131763 if (safe_create_leading_directories_const (clone_data_path ) < 0 )
@@ -1717,6 +1767,23 @@ static int clone_submodule(const struct module_clone_data *clone_data,
17171767 free (path );
17181768 }
17191769
1770+ /*
1771+ * We already performed this check at the beginning of this function,
1772+ * before cloning the objects. This tries to detect racy behavior e.g.
1773+ * in parallel clones, where another process could easily have made the
1774+ * gitdir nested _after_ it was created.
1775+ *
1776+ * To prevent further harm coming from this unintentionally-nested
1777+ * gitdir, let's disable it by deleting the `HEAD` file.
1778+ */
1779+ if (validate_submodule_git_dir (sm_gitdir , clone_data -> name ) < 0 ) {
1780+ char * head = xstrfmt ("%s/HEAD" , sm_gitdir );
1781+ unlink (head );
1782+ free (head );
1783+ die (_ ("refusing to create/use '%s' in another submodule's "
1784+ "git dir" ), sm_gitdir );
1785+ }
1786+
17201787 connect_work_tree_and_git_dir (clone_data_path , sm_gitdir , 0 );
17211788
17221789 p = git_pathdup_submodule (clone_data_path , "config" );
@@ -2490,6 +2557,9 @@ static int update_submodule(struct update_data *update_data)
24902557{
24912558 int ret ;
24922559
2560+ if (validate_submodule_path (update_data -> sm_path ) < 0 )
2561+ return -1 ;
2562+
24932563 ret = determine_submodule_update_strategy (the_repository ,
24942564 update_data -> just_cloned ,
24952565 update_data -> sm_path ,
@@ -2597,12 +2667,21 @@ static int update_submodules(struct update_data *update_data)
25972667
25982668 for (i = 0 ; i < suc .update_clone_nr ; i ++ ) {
25992669 struct update_clone_data ucd = suc .update_clone [i ];
2600- int code ;
2670+ int code = 128 ;
26012671
26022672 oidcpy (& update_data -> oid , & ucd .oid );
26032673 update_data -> just_cloned = ucd .just_cloned ;
26042674 update_data -> sm_path = ucd .sub -> path ;
26052675
2676+ /*
2677+ * Verify that the submodule path does not contain any
2678+ * symlinks; if it does, it might have been tampered with.
2679+ * TODO: allow exempting it via
2680+ * `safe.submodule.path` or something
2681+ */
2682+ if (validate_submodule_path (update_data -> sm_path ) < 0 )
2683+ goto fail ;
2684+
26062685 code = ensure_core_worktree (update_data -> sm_path );
26072686 if (code )
26082687 goto fail ;
@@ -3309,6 +3388,9 @@ static int module_add(int argc, const char **argv, const char *prefix)
33093388 normalize_path_copy (add_data .sm_path , add_data .sm_path );
33103389 strip_dir_trailing_slashes (add_data .sm_path );
33113390
3391+ if (validate_submodule_path (add_data .sm_path ) < 0 )
3392+ exit (128 );
3393+
33123394 die_on_index_match (add_data .sm_path , force );
33133395 die_on_repo_without_commits (add_data .sm_path );
33143396
0 commit comments