Skip to content

Commit 9e0f06d

Browse files
committed
Sync with Git 2.14.4
* maint-2.14: Git 2.14.4 Git 2.13.7 verify_path: disallow symlinks in .gitmodules update-index: stat updated files earlier verify_dotfile: mention case-insensitivity in comment verify_path: drop clever fallthrough skip_prefix: add case-insensitive variant is_{hfs,ntfs}_dotgitmodules: add tests is_ntfs_dotgit: match other .git files is_hfs_dotgit: match other .git files is_ntfs_dotgit: use a size_t for traversing string submodule-config: verify submodule names as paths
2 parents 3013dff + 4dde7b8 commit 9e0f06d

17 files changed

+497
-41
lines changed

Documentation/RelNotes/2.13.7.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Git v2.13.7 Release Notes
2+
=========================
3+
4+
Fixes since v2.13.6
5+
-------------------
6+
7+
* Submodule "names" come from the untrusted .gitmodules file, but we
8+
blindly append them to $GIT_DIR/modules to create our on-disk repo
9+
paths. This means you can do bad things by putting "../" into the
10+
name. We now enforce some rules for submodule names which will cause
11+
Git to ignore these malicious names (CVE-2018-11235).
12+
13+
Credit for finding this vulnerability and the proof of concept from
14+
which the test script was adapted goes to Etienne Stalmans.
15+
16+
* It was possible to trick the code that sanity-checks paths on NTFS
17+
into reading random piece of memory (CVE-2018-11233).
18+
19+
Credit for fixing for these bugs goes to Jeff King, Johannes
20+
Schindelin and others.

Documentation/RelNotes/2.14.4.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Git v2.14.4 Release Notes
2+
=========================
3+
4+
This release is to forward-port the fixes made in the v2.13.7 version
5+
of Git. See its release notes for details.

apply.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3865,9 +3865,9 @@ static int check_unsafe_path(struct patch *patch)
38653865
if (!patch->is_delete)
38663866
new_name = patch->new_name;
38673867

3868-
if (old_name && !verify_path(old_name))
3868+
if (old_name && !verify_path(old_name, patch->old_mode))
38693869
return error(_("invalid path '%s'"), old_name);
3870-
if (new_name && !verify_path(new_name))
3870+
if (new_name && !verify_path(new_name, patch->new_mode))
38713871
return error(_("invalid path '%s'"), new_name);
38723872
return 0;
38733873
}

builtin/submodule--helper.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,29 @@ static int is_active(int argc, const char **argv, const char *prefix)
12421242
return !is_submodule_active(the_repository, argv[1]);
12431243
}
12441244

1245+
/*
1246+
* Exit non-zero if any of the submodule names given on the command line is
1247+
* invalid. If no names are given, filter stdin to print only valid names
1248+
* (which is primarily intended for testing).
1249+
*/
1250+
static int check_name(int argc, const char **argv, const char *prefix)
1251+
{
1252+
if (argc > 1) {
1253+
while (*++argv) {
1254+
if (check_submodule_name(*argv) < 0)
1255+
return 1;
1256+
}
1257+
} else {
1258+
struct strbuf buf = STRBUF_INIT;
1259+
while (strbuf_getline(&buf, stdin) != EOF) {
1260+
if (!check_submodule_name(buf.buf))
1261+
printf("%s\n", buf.buf);
1262+
}
1263+
strbuf_release(&buf);
1264+
}
1265+
return 0;
1266+
}
1267+
12451268
#define SUPPORT_SUPER_PREFIX (1<<0)
12461269

12471270
struct cmd_struct {
@@ -1263,6 +1286,7 @@ static struct cmd_struct commands[] = {
12631286
{"push-check", push_check, 0},
12641287
{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
12651288
{"is-active", is_active, 0},
1289+
{"check-name", check_name, 0},
12661290
};
12671291

12681292
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)

builtin/update-index.c

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -361,10 +361,9 @@ static int process_directory(const char *path, int len, struct stat *st)
361361
return error("%s: is a directory - add files inside instead", path);
362362
}
363363

364-
static int process_path(const char *path)
364+
static int process_path(const char *path, struct stat *st, int stat_errno)
365365
{
366366
int pos, len;
367-
struct stat st;
368367
const struct cache_entry *ce;
369368

370369
len = strlen(path);
@@ -388,13 +387,13 @@ static int process_path(const char *path)
388387
* First things first: get the stat information, to decide
389388
* what to do about the pathname!
390389
*/
391-
if (lstat(path, &st) < 0)
392-
return process_lstat_error(path, errno);
390+
if (stat_errno)
391+
return process_lstat_error(path, stat_errno);
393392

394-
if (S_ISDIR(st.st_mode))
395-
return process_directory(path, len, &st);
393+
if (S_ISDIR(st->st_mode))
394+
return process_directory(path, len, st);
396395

397-
return add_one_path(ce, path, len, &st);
396+
return add_one_path(ce, path, len, st);
398397
}
399398

400399
static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
@@ -403,7 +402,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
403402
int size, len, option;
404403
struct cache_entry *ce;
405404

406-
if (!verify_path(path))
405+
if (!verify_path(path, mode))
407406
return error("Invalid path '%s'", path);
408407

409408
len = strlen(path);
@@ -446,7 +445,17 @@ static void chmod_path(char flip, const char *path)
446445

447446
static void update_one(const char *path)
448447
{
449-
if (!verify_path(path)) {
448+
int stat_errno = 0;
449+
struct stat st;
450+
451+
if (mark_valid_only || mark_skip_worktree_only || force_remove)
452+
st.st_mode = 0;
453+
else if (lstat(path, &st) < 0) {
454+
st.st_mode = 0;
455+
stat_errno = errno;
456+
} /* else stat is valid */
457+
458+
if (!verify_path(path, st.st_mode)) {
450459
fprintf(stderr, "Ignoring path %s\n", path);
451460
return;
452461
}
@@ -467,7 +476,7 @@ static void update_one(const char *path)
467476
report("remove '%s'", path);
468477
return;
469478
}
470-
if (process_path(path))
479+
if (process_path(path, &st, stat_errno))
471480
die("Unable to process path %s", path);
472481
report("add '%s'", path);
473482
}
@@ -537,7 +546,7 @@ static void read_index_info(int nul_term_line)
537546
path_name = uq.buf;
538547
}
539548

540-
if (!verify_path(path_name)) {
549+
if (!verify_path(path_name, mode)) {
541550
fprintf(stderr, "Ignoring path %s\n", path_name);
542551
continue;
543552
}

cache.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ extern int write_locked_index(struct index_state *, struct lock_file *lock, unsi
608608
extern int discard_index(struct index_state *);
609609
extern void move_index_extensions(struct index_state *dst, struct index_state *src);
610610
extern int unmerged_index(const struct index_state *);
611-
extern int verify_path(const char *path);
611+
extern int verify_path(const char *path, unsigned mode);
612612
extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
613613
extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
614614
extern void adjust_dirname_case(struct index_state *istate, char *name);
@@ -1130,7 +1130,15 @@ int normalize_path_copy(char *dst, const char *src);
11301130
int longest_ancestor_length(const char *path, struct string_list *prefixes);
11311131
char *strip_path_suffix(const char *path, const char *suffix);
11321132
int daemon_avoid_alias(const char *path);
1133-
extern int is_ntfs_dotgit(const char *name);
1133+
1134+
/*
1135+
* These functions match their is_hfs_dotgit() counterparts; see utf8.h for
1136+
* details.
1137+
*/
1138+
int is_ntfs_dotgit(const char *name);
1139+
int is_ntfs_dotgitmodules(const char *name);
1140+
int is_ntfs_dotgitignore(const char *name);
1141+
int is_ntfs_dotgitattributes(const char *name);
11341142

11351143
/*
11361144
* Returns true iff "str" could be confused as a command-line option when

git-compat-util.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -978,6 +978,23 @@ static inline int sane_iscase(int x, int is_lower)
978978
return (x & 0x20) == 0;
979979
}
980980

981+
/*
982+
* Like skip_prefix, but compare case-insensitively. Note that the comparison
983+
* is done via tolower(), so it is strictly ASCII (no multi-byte characters or
984+
* locale-specific conversions).
985+
*/
986+
static inline int skip_iprefix(const char *str, const char *prefix,
987+
const char **out)
988+
{
989+
do {
990+
if (!*prefix) {
991+
*out = str;
992+
return 1;
993+
}
994+
} while (tolower(*str++) == tolower(*prefix++));
995+
return 0;
996+
}
997+
981998
static inline int strtoul_ui(char const *s, int base, unsigned int *result)
982999
{
9831000
unsigned long ul;

git-submodule.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,11 @@ Use -f if you really want to add it." >&2
229229
sm_name="$sm_path"
230230
fi
231231

232+
if ! git submodule--helper check-name "$sm_name"
233+
then
234+
die "$(eval_gettext "'$sm_name' is not a valid submodule name")"
235+
fi
236+
232237
# perhaps the path exists and is already a git repo, else clone it
233238
if test -e "$sm_path"
234239
then

path.c

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1305,7 +1305,7 @@ static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
13051305

13061306
int is_ntfs_dotgit(const char *name)
13071307
{
1308-
int len;
1308+
size_t len;
13091309

13101310
for (len = 0; ; len++)
13111311
if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) {
@@ -1322,6 +1322,90 @@ int is_ntfs_dotgit(const char *name)
13221322
}
13231323
}
13241324

1325+
static int is_ntfs_dot_generic(const char *name,
1326+
const char *dotgit_name,
1327+
size_t len,
1328+
const char *dotgit_ntfs_shortname_prefix)
1329+
{
1330+
int saw_tilde;
1331+
size_t i;
1332+
1333+
if ((name[0] == '.' && !strncasecmp(name + 1, dotgit_name, len))) {
1334+
i = len + 1;
1335+
only_spaces_and_periods:
1336+
for (;;) {
1337+
char c = name[i++];
1338+
if (!c)
1339+
return 1;
1340+
if (c != ' ' && c != '.')
1341+
return 0;
1342+
}
1343+
}
1344+
1345+
/*
1346+
* Is it a regular NTFS short name, i.e. shortened to 6 characters,
1347+
* followed by ~1, ... ~4?
1348+
*/
1349+
if (!strncasecmp(name, dotgit_name, 6) && name[6] == '~' &&
1350+
name[7] >= '1' && name[7] <= '4') {
1351+
i = 8;
1352+
goto only_spaces_and_periods;
1353+
}
1354+
1355+
/*
1356+
* Is it a fall-back NTFS short name (for details, see
1357+
* https://en.wikipedia.org/wiki/8.3_filename?
1358+
*/
1359+
for (i = 0, saw_tilde = 0; i < 8; i++)
1360+
if (name[i] == '\0')
1361+
return 0;
1362+
else if (saw_tilde) {
1363+
if (name[i] < '0' || name[i] > '9')
1364+
return 0;
1365+
} else if (name[i] == '~') {
1366+
if (name[++i] < '1' || name[i] > '9')
1367+
return 0;
1368+
saw_tilde = 1;
1369+
} else if (i >= 6)
1370+
return 0;
1371+
else if (name[i] < 0) {
1372+
/*
1373+
* We know our needles contain only ASCII, so we clamp
1374+
* here to make the results of tolower() sane.
1375+
*/
1376+
return 0;
1377+
} else if (tolower(name[i]) != dotgit_ntfs_shortname_prefix[i])
1378+
return 0;
1379+
1380+
goto only_spaces_and_periods;
1381+
}
1382+
1383+
/*
1384+
* Inline helper to make sure compiler resolves strlen() on literals at
1385+
* compile time.
1386+
*/
1387+
static inline int is_ntfs_dot_str(const char *name, const char *dotgit_name,
1388+
const char *dotgit_ntfs_shortname_prefix)
1389+
{
1390+
return is_ntfs_dot_generic(name, dotgit_name, strlen(dotgit_name),
1391+
dotgit_ntfs_shortname_prefix);
1392+
}
1393+
1394+
int is_ntfs_dotgitmodules(const char *name)
1395+
{
1396+
return is_ntfs_dot_str(name, "gitmodules", "gi7eba");
1397+
}
1398+
1399+
int is_ntfs_dotgitignore(const char *name)
1400+
{
1401+
return is_ntfs_dot_str(name, "gitignore", "gi250a");
1402+
}
1403+
1404+
int is_ntfs_dotgitattributes(const char *name)
1405+
{
1406+
return is_ntfs_dot_str(name, "gitattributes", "gi7d29");
1407+
}
1408+
13251409
int looks_like_command_line_option(const char *str)
13261410
{
13271411
return str && str[0] == '-';

0 commit comments

Comments
 (0)