Skip to content

Commit 0230204

Browse files
committed
Sync with Git 2.15.2
* maint-2.15: Git 2.15.2 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 d32eb83 + d33c875 commit 0230204

18 files changed

+500
-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.

Documentation/RelNotes/2.15.2.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,8 @@ Fixes since v2.15.1
4343
* Clarify and enhance documentation for "merge-base --fork-point", as
4444
it was clear what it computed but not why/what for.
4545

46+
* This release also contains the fixes made in the v2.13.7 version of
47+
Git. See its release notes for details.
48+
4649

4750
Also contains various documentation updates and code clean-ups.

apply.c

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

3863-
if (old_name && !verify_path(old_name))
3863+
if (old_name && !verify_path(old_name, patch->old_mode))
38643864
return error(_("invalid path '%s'"), old_name);
3865-
if (new_name && !verify_path(new_name))
3865+
if (new_name && !verify_path(new_name, patch->new_mode))
38663866
return error(_("invalid path '%s'"), new_name);
38673867
return 0;
38683868
}

builtin/submodule--helper.c

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

1483+
/*
1484+
* Exit non-zero if any of the submodule names given on the command line is
1485+
* invalid. If no names are given, filter stdin to print only valid names
1486+
* (which is primarily intended for testing).
1487+
*/
1488+
static int check_name(int argc, const char **argv, const char *prefix)
1489+
{
1490+
if (argc > 1) {
1491+
while (*++argv) {
1492+
if (check_submodule_name(*argv) < 0)
1493+
return 1;
1494+
}
1495+
} else {
1496+
struct strbuf buf = STRBUF_INIT;
1497+
while (strbuf_getline(&buf, stdin) != EOF) {
1498+
if (!check_submodule_name(buf.buf))
1499+
printf("%s\n", buf.buf);
1500+
}
1501+
strbuf_release(&buf);
1502+
}
1503+
return 0;
1504+
}
1505+
14831506
#define SUPPORT_SUPER_PREFIX (1<<0)
14841507

14851508
struct cmd_struct {
@@ -1502,6 +1525,7 @@ static struct cmd_struct commands[] = {
15021525
{"push-check", push_check, 0},
15031526
{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
15041527
{"is-active", is_active, 0},
1528+
{"check-name", check_name, 0},
15051529
};
15061530

15071531
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
@@ -364,10 +364,9 @@ static int process_directory(const char *path, int len, struct stat *st)
364364
return error("%s: is a directory - add files inside instead", path);
365365
}
366366

367-
static int process_path(const char *path)
367+
static int process_path(const char *path, struct stat *st, int stat_errno)
368368
{
369369
int pos, len;
370-
struct stat st;
371370
const struct cache_entry *ce;
372371

373372
len = strlen(path);
@@ -391,13 +390,13 @@ static int process_path(const char *path)
391390
* First things first: get the stat information, to decide
392391
* what to do about the pathname!
393392
*/
394-
if (lstat(path, &st) < 0)
395-
return process_lstat_error(path, errno);
393+
if (stat_errno)
394+
return process_lstat_error(path, stat_errno);
396395

397-
if (S_ISDIR(st.st_mode))
398-
return process_directory(path, len, &st);
396+
if (S_ISDIR(st->st_mode))
397+
return process_directory(path, len, st);
399398

400-
return add_one_path(ce, path, len, &st);
399+
return add_one_path(ce, path, len, st);
401400
}
402401

403402
static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
@@ -406,7 +405,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
406405
int size, len, option;
407406
struct cache_entry *ce;
408407

409-
if (!verify_path(path))
408+
if (!verify_path(path, mode))
410409
return error("Invalid path '%s'", path);
411410

412411
len = strlen(path);
@@ -449,7 +448,17 @@ static void chmod_path(char flip, const char *path)
449448

450449
static void update_one(const char *path)
451450
{
452-
if (!verify_path(path)) {
451+
int stat_errno = 0;
452+
struct stat st;
453+
454+
if (mark_valid_only || mark_skip_worktree_only || force_remove)
455+
st.st_mode = 0;
456+
else if (lstat(path, &st) < 0) {
457+
st.st_mode = 0;
458+
stat_errno = errno;
459+
} /* else stat is valid */
460+
461+
if (!verify_path(path, st.st_mode)) {
453462
fprintf(stderr, "Ignoring path %s\n", path);
454463
return;
455464
}
@@ -475,7 +484,7 @@ static void update_one(const char *path)
475484
report("remove '%s'", path);
476485
return;
477486
}
478-
if (process_path(path))
487+
if (process_path(path, &st, stat_errno))
479488
die("Unable to process path %s", path);
480489
report("add '%s'", path);
481490
}
@@ -545,7 +554,7 @@ static void read_index_info(int nul_term_line)
545554
path_name = uq.buf;
546555
}
547556

548-
if (!verify_path(path_name)) {
557+
if (!verify_path(path_name, mode)) {
549558
fprintf(stderr, "Ignoring path %s\n", path_name);
550559
continue;
551560
}

cache.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ extern int unmerged_index(const struct index_state *);
655655
*/
656656
extern int index_has_changes(struct strbuf *sb);
657657

658-
extern int verify_path(const char *path);
658+
extern int verify_path(const char *path, unsigned mode);
659659
extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
660660
extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
661661
extern void adjust_dirname_case(struct index_state *istate, char *name);
@@ -1187,7 +1187,15 @@ int normalize_path_copy(char *dst, const char *src);
11871187
int longest_ancestor_length(const char *path, struct string_list *prefixes);
11881188
char *strip_path_suffix(const char *path, const char *suffix);
11891189
int daemon_avoid_alias(const char *path);
1190-
extern int is_ntfs_dotgit(const char *name);
1190+
1191+
/*
1192+
* These functions match their is_hfs_dotgit() counterparts; see utf8.h for
1193+
* details.
1194+
*/
1195+
int is_ntfs_dotgit(const char *name);
1196+
int is_ntfs_dotgitmodules(const char *name);
1197+
int is_ntfs_dotgitignore(const char *name);
1198+
int is_ntfs_dotgitattributes(const char *name);
11911199

11921200
/*
11931201
* 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
@@ -1001,6 +1001,23 @@ static inline int sane_iscase(int x, int is_lower)
10011001
return (x & 0x20) == 0;
10021002
}
10031003

1004+
/*
1005+
* Like skip_prefix, but compare case-insensitively. Note that the comparison
1006+
* is done via tolower(), so it is strictly ASCII (no multi-byte characters or
1007+
* locale-specific conversions).
1008+
*/
1009+
static inline int skip_iprefix(const char *str, const char *prefix,
1010+
const char **out)
1011+
{
1012+
do {
1013+
if (!*prefix) {
1014+
*out = str;
1015+
return 1;
1016+
}
1017+
} while (tolower(*str++) == tolower(*prefix++));
1018+
return 0;
1019+
}
1020+
10041021
static inline int strtoul_ui(char const *s, int base, unsigned int *result)
10051022
{
10061023
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)