diff -Nru git-2.17.0/apply.c git-2.17.1/apply.c --- git-2.17.0/apply.c 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/apply.c 2018-05-29 08:14:06.000000000 +0000 @@ -3860,9 +3860,9 @@ if (!patch->is_delete) new_name = patch->new_name; - if (old_name && !verify_path(old_name)) + if (old_name && !verify_path(old_name, patch->old_mode)) return error(_("invalid path '%s'"), old_name); - if (new_name && !verify_path(new_name)) + if (new_name && !verify_path(new_name, patch->new_mode)) return error(_("invalid path '%s'"), new_name); return 0; } diff -Nru git-2.17.0/builtin/fsck.c git-2.17.1/builtin/fsck.c --- git-2.17.0/builtin/fsck.c 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/builtin/fsck.c 2018-05-29 08:14:06.000000000 +0000 @@ -337,7 +337,7 @@ } } -static int fsck_obj(struct object *obj) +static int fsck_obj(struct object *obj, void *buffer, unsigned long size) { int err; @@ -351,7 +351,7 @@ if (fsck_walk(obj, NULL, &fsck_obj_options)) objerror(obj, "broken links"); - err = fsck_object(obj, NULL, 0, &fsck_obj_options); + err = fsck_object(obj, buffer, size, &fsck_obj_options); if (err) goto out; @@ -396,7 +396,7 @@ } obj->flags &= ~(REACHABLE | SEEN); obj->flags |= HAS_OBJ; - return fsck_obj(obj); + return fsck_obj(obj, buffer, size); } static int default_refs; @@ -504,44 +504,42 @@ } } -static struct object *parse_loose_object(const struct object_id *oid, - const char *path) +static int fsck_loose(const struct object_id *oid, const char *path, void *data) { struct object *obj; - void *contents; enum object_type type; unsigned long size; + void *contents; int eaten; - if (read_loose_object(path, oid->hash, &type, &size, &contents) < 0) - return NULL; + if (read_loose_object(path, oid->hash, &type, &size, &contents) < 0) { + errors_found |= ERROR_OBJECT; + error("%s: object corrupt or missing: %s", + oid_to_hex(oid), path); + return 0; /* keep checking other objects */ + } if (!contents && type != OBJ_BLOB) - die("BUG: read_loose_object streamed a non-blob"); + BUG("read_loose_object streamed a non-blob"); obj = parse_object_buffer(oid, type, size, contents, &eaten); - - if (!eaten) - free(contents); - return obj; -} - -static int fsck_loose(const struct object_id *oid, const char *path, void *data) -{ - struct object *obj = parse_loose_object(oid, path); - if (!obj) { errors_found |= ERROR_OBJECT; - error("%s: object corrupt or missing: %s", + error("%s: object could not be parsed: %s", oid_to_hex(oid), path); + if (!eaten) + free(contents); return 0; /* keep checking other objects */ } obj->flags &= ~(REACHABLE | SEEN); obj->flags |= HAS_OBJ; - if (fsck_obj(obj)) + if (fsck_obj(obj, contents, size)) errors_found |= ERROR_OBJECT; - return 0; + + if (!eaten) + free(contents); + return 0; /* keep checking other objects, even if we saw an error */ } static int fsck_cruft(const char *basename, const char *path, void *data) @@ -750,6 +748,9 @@ } stop_progress(&progress); } + + if (fsck_finish(&fsck_obj_options)) + errors_found |= ERROR_OBJECT; } for (i = 0; i < argc; i++) { diff -Nru git-2.17.0/builtin/index-pack.c git-2.17.1/builtin/index-pack.c --- git-2.17.0/builtin/index-pack.c 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/builtin/index-pack.c 2018-05-29 08:14:06.000000000 +0000 @@ -836,6 +836,9 @@ blob->object.flags |= FLAG_CHECKED; else die(_("invalid blob object %s"), oid_to_hex(oid)); + if (do_fsck_object && + fsck_object(&blob->object, (void *)data, size, &fsck_options)) + die(_("fsck error in packed object")); } else { struct object *obj; int eaten; @@ -853,7 +856,7 @@ die(_("invalid %s"), type_name(type)); if (do_fsck_object && fsck_object(obj, buf, size, &fsck_options)) - die(_("Error in object")); + die(_("fsck error in packed object")); if (strict && fsck_walk(obj, NULL, &fsck_options)) die(_("Not all child objects of %s are reachable"), oid_to_hex(&obj->oid)); @@ -1477,6 +1480,9 @@ } else chmod(final_index_name, 0444); + if (do_fsck_object) + add_packed_git(final_index_name, strlen(final_index_name), 0); + if (!from_stdin) { printf("%s\n", sha1_to_hex(hash)); } else { @@ -1818,6 +1824,10 @@ pack_hash); else close(input_fd); + + if (do_fsck_object && fsck_finish(&fsck_options)) + die(_("fsck error in pack objects")); + free(objects); strbuf_release(&index_name_buf); if (pack_name == NULL) diff -Nru git-2.17.0/builtin/submodule--helper.c git-2.17.1/builtin/submodule--helper.c --- git-2.17.0/builtin/submodule--helper.c 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/builtin/submodule--helper.c 2018-05-29 08:14:06.000000000 +0000 @@ -1817,6 +1817,29 @@ return !is_submodule_active(the_repository, argv[1]); } +/* + * Exit non-zero if any of the submodule names given on the command line is + * invalid. If no names are given, filter stdin to print only valid names + * (which is primarily intended for testing). + */ +static int check_name(int argc, const char **argv, const char *prefix) +{ + if (argc > 1) { + while (*++argv) { + if (check_submodule_name(*argv) < 0) + return 1; + } + } else { + struct strbuf buf = STRBUF_INIT; + while (strbuf_getline(&buf, stdin) != EOF) { + if (!check_submodule_name(buf.buf)) + printf("%s\n", buf.buf); + } + strbuf_release(&buf); + } + return 0; +} + #define SUPPORT_SUPER_PREFIX (1<<0) struct cmd_struct { @@ -1842,6 +1865,7 @@ {"push-check", push_check, 0}, {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX}, {"is-active", is_active, 0}, + {"check-name", check_name, 0}, }; int cmd_submodule__helper(int argc, const char **argv, const char *prefix) diff -Nru git-2.17.0/builtin/unpack-objects.c git-2.17.1/builtin/unpack-objects.c --- git-2.17.0/builtin/unpack-objects.c 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/builtin/unpack-objects.c 2018-05-29 08:14:06.000000000 +0000 @@ -210,7 +210,7 @@ if (!obj_buf) die("Whoops! Cannot find object '%s'", oid_to_hex(&obj->oid)); if (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options)) - die("Error in object"); + die("fsck error in packed object"); fsck_options.walk = check_object; if (fsck_walk(obj, NULL, &fsck_options)) die("Error on reachable objects of %s", oid_to_hex(&obj->oid)); @@ -572,8 +572,11 @@ unpack_all(); the_hash_algo->update_fn(&ctx, buffer, offset); the_hash_algo->final_fn(oid.hash, &ctx); - if (strict) + if (strict) { write_rest(); + if (fsck_finish(&fsck_options)) + die(_("fsck error in pack objects")); + } if (hashcmp(fill(the_hash_algo->rawsz), oid.hash)) die("final sha1 did not match"); use(the_hash_algo->rawsz); diff -Nru git-2.17.0/builtin/update-index.c git-2.17.1/builtin/update-index.c --- git-2.17.0/builtin/update-index.c 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/builtin/update-index.c 2018-05-29 08:14:06.000000000 +0000 @@ -364,10 +364,9 @@ return error("%s: is a directory - add files inside instead", path); } -static int process_path(const char *path) +static int process_path(const char *path, struct stat *st, int stat_errno) { int pos, len; - struct stat st; const struct cache_entry *ce; len = strlen(path); @@ -391,13 +390,13 @@ * First things first: get the stat information, to decide * what to do about the pathname! */ - if (lstat(path, &st) < 0) - return process_lstat_error(path, errno); + if (stat_errno) + return process_lstat_error(path, stat_errno); - if (S_ISDIR(st.st_mode)) - return process_directory(path, len, &st); + if (S_ISDIR(st->st_mode)) + return process_directory(path, len, st); - return add_one_path(ce, path, len, &st); + return add_one_path(ce, path, len, st); } static int add_cacheinfo(unsigned int mode, const struct object_id *oid, @@ -406,7 +405,7 @@ int size, len, option; struct cache_entry *ce; - if (!verify_path(path)) + if (!verify_path(path, mode)) return error("Invalid path '%s'", path); len = strlen(path); @@ -449,7 +448,18 @@ static void update_one(const char *path) { - if (!verify_path(path)) { + int stat_errno = 0; + struct stat st; + + if (mark_valid_only || mark_skip_worktree_only || force_remove || + mark_fsmonitor_only) + st.st_mode = 0; + else if (lstat(path, &st) < 0) { + st.st_mode = 0; + stat_errno = errno; + } /* else stat is valid */ + + if (!verify_path(path, st.st_mode)) { fprintf(stderr, "Ignoring path %s\n", path); return; } @@ -475,7 +485,7 @@ report("remove '%s'", path); return; } - if (process_path(path)) + if (process_path(path, &st, stat_errno)) die("Unable to process path %s", path); report("add '%s'", path); } @@ -545,7 +555,7 @@ path_name = uq.buf; } - if (!verify_path(path_name)) { + if (!verify_path(path_name, mode)) { fprintf(stderr, "Ignoring path %s\n", path_name); continue; } diff -Nru git-2.17.0/cache.h git-2.17.1/cache.h --- git-2.17.0/cache.h 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/cache.h 2018-05-29 08:14:06.000000000 +0000 @@ -634,7 +634,7 @@ */ extern int index_has_changes(struct strbuf *sb); -extern int verify_path(const char *path); +extern int verify_path(const char *path, unsigned mode); extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change); extern int index_dir_exists(struct index_state *istate, const char *name, int namelen); extern void adjust_dirname_case(struct index_state *istate, char *name); @@ -1165,7 +1165,15 @@ int longest_ancestor_length(const char *path, struct string_list *prefixes); char *strip_path_suffix(const char *path, const char *suffix); int daemon_avoid_alias(const char *path); -extern int is_ntfs_dotgit(const char *name); + +/* + * These functions match their is_hfs_dotgit() counterparts; see utf8.h for + * details. + */ +int is_ntfs_dotgit(const char *name); +int is_ntfs_dotgitmodules(const char *name); +int is_ntfs_dotgitignore(const char *name); +int is_ntfs_dotgitattributes(const char *name); /* * Returns true iff "str" could be confused as a command-line option when diff -Nru git-2.17.0/configure git-2.17.1/configure --- git-2.17.0/configure 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/configure 2018-05-29 08:14:06.000000000 +0000 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for git 2.17.0. +# Generated by GNU Autoconf 2.69 for git 2.17.1. # # Report bugs to . # @@ -580,8 +580,8 @@ # Identity of this package. PACKAGE_NAME='git' PACKAGE_TARNAME='git' -PACKAGE_VERSION='2.17.0' -PACKAGE_STRING='git 2.17.0' +PACKAGE_VERSION='2.17.1' +PACKAGE_STRING='git 2.17.1' PACKAGE_BUGREPORT='git@vger.kernel.org' PACKAGE_URL='' @@ -1265,7 +1265,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures git 2.17.0 to adapt to many kinds of systems. +\`configure' configures git 2.17.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1327,7 +1327,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of git 2.17.0:";; + short | recursive ) echo "Configuration of git 2.17.1:";; esac cat <<\_ACEOF @@ -1472,7 +1472,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -git configure 2.17.0 +git configure 2.17.1 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1952,7 +1952,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by git $as_me 2.17.0, which was +It was created by git $as_me 2.17.1, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -8247,7 +8247,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by git $as_me 2.17.0, which was +This file was extended by git $as_me 2.17.1, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -8304,7 +8304,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -git config.status 2.17.0 +git config.status 2.17.1 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff -Nru git-2.17.0/debian/changelog git-2.17.1/debian/changelog --- git-2.17.0/debian/changelog 2018-04-06 14:38:28.000000000 +0000 +++ git-2.17.1/debian/changelog 2018-09-20 09:34:49.000000000 +0000 @@ -1,3 +1,31 @@ +git (1:2.17.1-1ubuntu0.2) bionic; urgency=medium + + * Build diff-highlight in the contrib dir (closes: #868871, LP: #1713690) + + -- Adam Conrad Thu, 20 Sep 2018 03:34:49 -0600 + +git (1:2.17.1-1ubuntu0.1) bionic-security; urgency=low + + * SECURITY UPDATE: arbitrary code execution via submodule names + in .gitsubmodules. + - CVE-2018-11235 + * SECURITY UPDATE: out-of-bounds memory when sanity-checking + pathnames on NTFS + - CVE-2018-11233 + * Merge from Debian (LP: #1774061). Remaining changes: + - debian/control: build against pcre v3 only + - debian/rules: s390x libpcre3 library has JIT disabled, set + NO_LIBPCRE1_JIT on that arch to stop the build from failing. + + -- Steve Beattie Thu, 31 May 2018 10:50:28 -0700 + +git (1:2.17.1-1) unstable; urgency=high + + * new upstream point release to fix CVE-2018-11235, arbitary code + execution via submodule names in .gitmodules (see RelNotes/2.17.1.txt). + + -- Jonathan Nieder Mon, 28 May 2018 17:37:54 -0700 + git (1:2.17.0-1ubuntu1) bionic; urgency=medium * Merge with Debian; remaining changes: diff -Nru git-2.17.0/debian/changelog.upstream git-2.17.1/debian/changelog.upstream --- git-2.17.0/debian/changelog.upstream 2018-04-02 19:58:05.000000000 +0000 +++ git-2.17.1/debian/changelog.upstream 2018-05-29 22:02:58.000000000 +0000 @@ -1,3 +1,38 @@ +Version v2.17.1; changes since v2.17.0: +--------------------------------------- + +Jeff King (18): + submodule-config: verify submodule names as paths + is_ntfs_dotgit: use a size_t for traversing string + is_hfs_dotgit: match other .git files + skip_prefix: add case-insensitive variant + verify_path: drop clever fallthrough + verify_dotfile: mention case-insensitivity in comment + update-index: stat updated files earlier + verify_path: disallow symlinks in .gitmodules + index-pack: make fsck error message more specific + fsck: simplify ".git" check + fsck: actually fsck blob data + fsck: detect gitmodules files + fsck: handle promisor objects in .gitmodules check + fsck: check .gitmodules content + fsck: call fsck_finish() after fscking objects + unpack-objects: call fsck_finish() after fscking objects + index-pack: check .gitmodules files with --strict + fsck: complain when .gitmodules is a symlink + +Johannes Schindelin (2): + is_ntfs_dotgit: match other .git files + is_{hfs,ntfs}_dotgitmodules: add tests + +Junio C Hamano (5): + Git 2.13.7 + Git 2.14.4 + Git 2.15.2 + Git 2.16.4 + Git 2.17.1 + + Version v2.17.0; changes since v2.17.0-rc2: ------------------------------------------- @@ -110,7 +145,7 @@ completion: complete tags with git tag --delete/--verify -Version v2.17.0-rc0; changes since v2.16.3: +Version v2.17.0-rc0; changes since v2.16.4: ------------------------------------------- Adam Borowski (1): @@ -588,6 +623,30 @@ git manpage: note git-security@googlegroups.com +Version v2.16.4; changes since v2.16.3: +--------------------------------------- + +Jeff King (8): + submodule-config: verify submodule names as paths + is_ntfs_dotgit: use a size_t for traversing string + is_hfs_dotgit: match other .git files + skip_prefix: add case-insensitive variant + verify_path: drop clever fallthrough + verify_dotfile: mention case-insensitivity in comment + update-index: stat updated files earlier + verify_path: disallow symlinks in .gitmodules + +Johannes Schindelin (2): + is_ntfs_dotgit: match other .git files + is_{hfs,ntfs}_dotgitmodules: add tests + +Junio C Hamano (4): + Git 2.13.7 + Git 2.14.4 + Git 2.15.2 + Git 2.16.4 + + Version v2.16.3; changes since v2.16.2: --------------------------------------- @@ -929,7 +988,7 @@ check-non-portable-shell.pl: `wc -l` may have leading WS -Version v2.16.0-rc0; changes since v2.15.1: +Version v2.16.0-rc0; changes since v2.15.2: ------------------------------------------- Albert Astals Cid (1): @@ -1052,9 +1111,8 @@ sha1_name: minimize OID comparisons during disambiguation sha1_file: use strbuf_add() instead of strbuf_addf() -Elijah Newren (7): +Elijah Newren (6): sequencer: warn when internal merge may be suboptimal due to renameLimit - merge-recursive: handle addition of submodule on our side of history progress: fix progress meters when dealing with lots of work diff: remove silent clamp of renameLimit sequencer: show rename progress during cherry picks @@ -1069,8 +1127,7 @@ clone: support 'clone --shared' from a worktree version --build-options: also report host CPU -Eric Wong (2): - rebase: use mboxrd format to avoid split errors +Eric Wong (1): git-svn: convert CRLF to LF in commit message to SVN Florian Klink (1): @@ -1167,7 +1224,7 @@ clone, fetch: remove redundant transport check transport: make transport vtable more private -Junio C Hamano (28): +Junio C Hamano (26): t0027: do not use an empty string as a pathspec element describe: do not use cmd_*() as a subroutine merge-ours: do not use cmd_*() as a subroutine @@ -1177,7 +1234,6 @@ The first batch for 2.16 RelNotes: the second batch post 2.15 comes diff: --ignore-cr-at-eol - merge-base --fork-point doc: clarify the example and failure modes RelNotes: the third batch for 2.16 branch: correctly reject refs/heads/{-dash,HEAD} RelNotes: the fourth batch for 2.16 @@ -1186,7 +1242,6 @@ RelNotes: the sixth batch for 2.16 RelNotes: the seventh batch t2020: test variations that matter - Prepare for 2.15.2 RelNotes: the eighth batch diff: use skip_to_optional_arg_default() in parsing --relative t4045: reindent to make helpers readable @@ -1249,9 +1304,7 @@ branch: respect `pager.branch` in list-mode only branch: change default of `pager.branch` to "on" -Michael Haggerty (12): - t1409: check that `packed-refs` is not rewritten unnecessarily - files-backend: don't rewrite the `packed-refs` file unnecessarily +Michael Haggerty (10): t0000: check whether the shell supports the "local" keyword files_transaction_prepare(): don't leak flags to packed transaction prune_ref(): call `ref_transaction_add_update()` directly @@ -1274,10 +1327,6 @@ doc: prefer 'stash push' over 'stash save' stash: learn to parse -m/--message like commit does -Phillip Wood (2): - config: avoid "write_in_full(fd, buf, len) != len" pattern - sequencer: reschedule pick if index can't be locked - Pranit Bauva (6): bisect--helper: use OPT_CMDMODE instead of OPT_BOOL bisect--helper: rewrite `check_term_format` shell function in C @@ -1304,11 +1353,10 @@ Documentation/config: add sendemail.tocmd to list preceding "See git-send-email(1)" completion: add git config sendemail.tocmd -René Scharfe (15): +René Scharfe (14): notes: move hex_to_bytes() to hex.c and export it http-push: use hex_to_bytes() sha1_file: use hex_to_bytes() - apply: update line lengths for --inaccurate-eof t4051: add test for comments preceding function lines xdiff: factor out is_func_rec() xdiff: show non-empty lines before functions with -W @@ -1338,18 +1386,14 @@ travis-ci: set GIT_TEST_HTTPD in 'ci/lib-travisci.sh' travis-ci: use 'set -x' in 'ci/*' scripts for extra tracing output -Shuyu Wei (1): - pull: pass -4/-6 option to 'git fetch' - Simon Ruderich (1): config: document default value of http.sslVerify -Stefan Beller (12): +Stefan Beller (11): recursive submodules: detach HEAD from new state Documentation/checkout: clarify submodule HEADs to be detached t6120: fix typo in test name list-objects.c: factor out traverse_trees_and_blobs - t/3512: demonstrate unrelated submodule/file conflict as cherry-pick failure revision.h: introduce blob/tree walking in order of the commits builtin/describe.c: rename `oid` to avoid variable shadowing builtin/describe.c: print debug statements earlier @@ -1377,12 +1421,8 @@ worktree: add --guess-remote flag to add subcommand add worktree.guessRemote config option -Todd Zullinger (8): - t/lib-gpg: fix gpgconf stderr redirect to /dev/null - rebase: fix stderr redirect in apply_autostash() - notes: send "Automatic notes merge failed" messages to stderr +Todd Zullinger (4): branch doc: remove --set-upstream from synopsis - completion: add '--copy' option to 'git branch' t/lib-git-svn: cleanup inconsistent tab/space usage t/lib-git-svn.sh: improve svnserve tests with parallel make test RelNotes: minor typo fixes in 2.16.0 draft @@ -1398,7 +1438,7 @@ Wei Shuyu (1): http: support CURLPROXY_HTTPS -brian m. carlson (31): +brian m. carlson (29): walker: convert to struct object_id refs/files-backend: convert struct ref_to_prune to object_id refs: convert delete_ref and refs_delete_ref to struct object_id @@ -1424,12 +1464,10 @@ refs: convert peel_object to struct object_id refs: convert read_raw_ref backends to struct object_id refs/files-backend: convert static functions to object_id - Documentation: enable compat-mode for Asciidoctor setup: expose enumerated repo info Add structure representing hash algorithm Integrate hash algorithm support with repo setup Switch empty tree and blob lookups to use hash abstraction - Documentation: convert SubmittingPatches to AsciiDoc Ævar Arnfjörð Bjarmason (2): test-lib: add LIBPCRE1 & LIBPCRE2 prerequisites @@ -1439,6 +1477,64 @@ git-gui: prevent double UTF-8 conversion +Version v2.15.2; changes since v2.15.1: +--------------------------------------- + +Elijah Newren (1): + merge-recursive: handle addition of submodule on our side of history + +Eric Wong (1): + rebase: use mboxrd format to avoid split errors + +Jeff King (8): + submodule-config: verify submodule names as paths + is_ntfs_dotgit: use a size_t for traversing string + is_hfs_dotgit: match other .git files + skip_prefix: add case-insensitive variant + verify_path: drop clever fallthrough + verify_dotfile: mention case-insensitivity in comment + update-index: stat updated files earlier + verify_path: disallow symlinks in .gitmodules + +Johannes Schindelin (2): + is_ntfs_dotgit: match other .git files + is_{hfs,ntfs}_dotgitmodules: add tests + +Junio C Hamano (5): + merge-base --fork-point doc: clarify the example and failure modes + Prepare for 2.15.2 + Git 2.13.7 + Git 2.14.4 + Git 2.15.2 + +Michael Haggerty (2): + t1409: check that `packed-refs` is not rewritten unnecessarily + files-backend: don't rewrite the `packed-refs` file unnecessarily + +Phillip Wood (2): + config: avoid "write_in_full(fd, buf, len) != len" pattern + sequencer: reschedule pick if index can't be locked + +René Scharfe (1): + apply: update line lengths for --inaccurate-eof + +Shuyu Wei (1): + pull: pass -4/-6 option to 'git fetch' + +Stefan Beller (1): + t/3512: demonstrate unrelated submodule/file conflict as cherry-pick failure + +Todd Zullinger (4): + t/lib-gpg: fix gpgconf stderr redirect to /dev/null + rebase: fix stderr redirect in apply_autostash() + notes: send "Automatic notes merge failed" messages to stderr + completion: add '--copy' option to 'git branch' + +brian m. carlson (2): + Documentation: enable compat-mode for Asciidoctor + Documentation: convert SubmittingPatches to AsciiDoc + + Version v2.15.1; changes since v2.15.0: --------------------------------------- @@ -1743,7 +1839,7 @@ api-argv-array.txt: remove broken link to string-list API -Version v2.15.0-rc0; changes since v2.14.3: +Version v2.15.0-rc0; changes since v2.14.4: ------------------------------------------- Brandon Casey (7): @@ -2285,6 +2381,28 @@ merge: add a --signoff flag +Version v2.14.4; changes since v2.14.3: +--------------------------------------- + +Jeff King (8): + submodule-config: verify submodule names as paths + is_ntfs_dotgit: use a size_t for traversing string + is_hfs_dotgit: match other .git files + skip_prefix: add case-insensitive variant + verify_path: drop clever fallthrough + verify_dotfile: mention case-insensitivity in comment + update-index: stat updated files earlier + verify_path: disallow symlinks in .gitmodules + +Johannes Schindelin (2): + is_ntfs_dotgit: match other .git files + is_{hfs,ntfs}_dotgitmodules: add tests + +Junio C Hamano (2): + Git 2.13.7 + Git 2.14.4 + + Version v2.14.3; changes since v2.14.2: --------------------------------------- @@ -2799,7 +2917,7 @@ RelNotes: mention "sha1dc: optionally use sha1collisiondetection as a submodule" -Version v2.14.0-rc0; changes since v2.13.6: +Version v2.14.0-rc0; changes since v2.13.7: ------------------------------------------- Andreas Heiduk (4): @@ -3312,6 +3430,27 @@ sha1dc: optionally use sha1collisiondetection as a submodule +Version v2.13.7; changes since v2.13.6: +--------------------------------------- + +Jeff King (8): + submodule-config: verify submodule names as paths + is_ntfs_dotgit: use a size_t for traversing string + is_hfs_dotgit: match other .git files + skip_prefix: add case-insensitive variant + verify_path: drop clever fallthrough + verify_dotfile: mention case-insensitivity in comment + update-index: stat updated files earlier + verify_path: disallow symlinks in .gitmodules + +Johannes Schindelin (2): + is_ntfs_dotgit: match other .git files + is_{hfs,ntfs}_dotgitmodules: add tests + +Junio C Hamano (1): + Git 2.13.7 + + Version v2.13.6; changes since v2.13.5: --------------------------------------- diff -Nru git-2.17.0/debian/rules git-2.17.1/debian/rules --- git-2.17.0/debian/rules 2018-04-06 14:38:28.000000000 +0000 +++ git-2.17.1/debian/rules 2018-09-20 09:34:49.000000000 +0000 @@ -62,6 +62,7 @@ override_dh_auto_build-arch: build-stamp $(MAKE) -C contrib/subtree all $(OPTS) ln -s contrib/subtree/git-subtree + $(MAKE) -C contrib/diff-highlight $(OPTS) override_dh_auto_test-arch: test -z '$(TEST)' || \ diff -Nru git-2.17.0/debian/versions.upstream git-2.17.1/debian/versions.upstream --- git-2.17.0/debian/versions.upstream 2018-04-02 19:57:40.000000000 +0000 +++ git-2.17.1/debian/versions.upstream 2018-05-29 22:02:58.000000000 +0000 @@ -588,17 +588,20 @@ v2.13.4 v2.13.5 v2.13.6 +v2.13.7 v2.14.0-rc0 v2.14.0-rc1 v2.14.0 v2.14.1 v2.14.2 v2.14.3 +v2.14.4 v2.15.0-rc0 v2.15.0-rc1 v2.15.0-rc2 v2.15.0 v2.15.1 +v2.15.2 v2.16.0-rc0 v2.16.0-rc1 v2.16.0-rc2 @@ -606,7 +609,9 @@ v2.16.1 v2.16.2 v2.16.3 +v2.16.4 v2.17.0-rc0 v2.17.0-rc1 v2.17.0-rc2 v2.17.0 +v2.17.1 diff -Nru git-2.17.0/dir.c git-2.17.1/dir.c --- git-2.17.0/dir.c 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/dir.c 2018-05-29 08:14:06.000000000 +0000 @@ -2992,7 +2992,7 @@ { if (!istate->untracked || !istate->untracked->root) return; - if (!safe_path && !verify_path(path)) + if (!safe_path && !verify_path(path, 0)) return; invalidate_one_component(istate->untracked, istate->untracked->root, path, strlen(path)); diff -Nru git-2.17.0/Documentation/RelNotes/2.13.7.txt git-2.17.1/Documentation/RelNotes/2.13.7.txt --- git-2.17.0/Documentation/RelNotes/2.13.7.txt 1970-01-01 00:00:00.000000000 +0000 +++ git-2.17.1/Documentation/RelNotes/2.13.7.txt 2018-05-29 08:14:06.000000000 +0000 @@ -0,0 +1,20 @@ +Git v2.13.7 Release Notes +========================= + +Fixes since v2.13.6 +------------------- + + * Submodule "names" come from the untrusted .gitmodules file, but we + blindly append them to $GIT_DIR/modules to create our on-disk repo + paths. This means you can do bad things by putting "../" into the + name. We now enforce some rules for submodule names which will cause + Git to ignore these malicious names (CVE-2018-11235). + + Credit for finding this vulnerability and the proof of concept from + which the test script was adapted goes to Etienne Stalmans. + + * It was possible to trick the code that sanity-checks paths on NTFS + into reading random piece of memory (CVE-2018-11233). + +Credit for fixing for these bugs goes to Jeff King, Johannes +Schindelin and others. diff -Nru git-2.17.0/Documentation/RelNotes/2.14.4.txt git-2.17.1/Documentation/RelNotes/2.14.4.txt --- git-2.17.0/Documentation/RelNotes/2.14.4.txt 1970-01-01 00:00:00.000000000 +0000 +++ git-2.17.1/Documentation/RelNotes/2.14.4.txt 2018-05-29 08:14:06.000000000 +0000 @@ -0,0 +1,5 @@ +Git v2.14.4 Release Notes +========================= + +This release is to forward-port the fixes made in the v2.13.7 version +of Git. See its release notes for details. diff -Nru git-2.17.0/Documentation/RelNotes/2.15.2.txt git-2.17.1/Documentation/RelNotes/2.15.2.txt --- git-2.17.0/Documentation/RelNotes/2.15.2.txt 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/Documentation/RelNotes/2.15.2.txt 2018-05-29 08:14:06.000000000 +0000 @@ -43,5 +43,8 @@ * Clarify and enhance documentation for "merge-base --fork-point", as it was clear what it computed but not why/what for. + * This release also contains the fixes made in the v2.13.7 version of + Git. See its release notes for details. + Also contains various documentation updates and code clean-ups. diff -Nru git-2.17.0/Documentation/RelNotes/2.16.4.txt git-2.17.1/Documentation/RelNotes/2.16.4.txt --- git-2.17.0/Documentation/RelNotes/2.16.4.txt 1970-01-01 00:00:00.000000000 +0000 +++ git-2.17.1/Documentation/RelNotes/2.16.4.txt 2018-05-29 08:14:06.000000000 +0000 @@ -0,0 +1,5 @@ +Git v2.16.4 Release Notes +========================= + +This release is to forward-port the fixes made in the v2.13.7 version +of Git. See its release notes for details. diff -Nru git-2.17.0/Documentation/RelNotes/2.17.1.txt git-2.17.1/Documentation/RelNotes/2.17.1.txt --- git-2.17.0/Documentation/RelNotes/2.17.1.txt 1970-01-01 00:00:00.000000000 +0000 +++ git-2.17.1/Documentation/RelNotes/2.17.1.txt 2018-05-29 08:14:06.000000000 +0000 @@ -0,0 +1,16 @@ +Git v2.17.1 Release Notes +========================= + +Fixes since v2.17 +----------------- + + * This release contains the same fixes made in the v2.13.7 version of + Git, covering CVE-2018-11233 and 11235, and forward-ported to + v2.14.4, v2.15.2 and v2.16.4 releases. See release notes to + v2.13.7 for details. + + * In addition to the above fixes, this release has support on the + server side to reject pushes to repositories that attempt to create + such problematic .gitmodules file etc. as tracked contents, to help + hosting sites protect their customers by preventing malicious + contents from spreading. diff -Nru git-2.17.0/fsck.c git-2.17.1/fsck.c --- git-2.17.0/fsck.c 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/fsck.c 2018-05-29 08:14:06.000000000 +0000 @@ -10,6 +10,13 @@ #include "utf8.h" #include "sha1-array.h" #include "decorate.h" +#include "oidset.h" +#include "packfile.h" +#include "submodule-config.h" +#include "config.h" + +static struct oidset gitmodules_found = OIDSET_INIT; +static struct oidset gitmodules_done = OIDSET_INIT; #define FSCK_FATAL -1 #define FSCK_INFO -2 @@ -44,6 +51,7 @@ FUNC(MISSING_TAG_ENTRY, ERROR) \ FUNC(MISSING_TAG_OBJECT, ERROR) \ FUNC(MISSING_TREE, ERROR) \ + FUNC(MISSING_TREE_OBJECT, ERROR) \ FUNC(MISSING_TYPE, ERROR) \ FUNC(MISSING_TYPE_ENTRY, ERROR) \ FUNC(MULTIPLE_AUTHORS, ERROR) \ @@ -51,6 +59,11 @@ FUNC(TREE_NOT_SORTED, ERROR) \ FUNC(UNKNOWN_TYPE, ERROR) \ FUNC(ZERO_PADDED_DATE, ERROR) \ + FUNC(GITMODULES_MISSING, ERROR) \ + FUNC(GITMODULES_BLOB, ERROR) \ + FUNC(GITMODULES_PARSE, ERROR) \ + FUNC(GITMODULES_NAME, ERROR) \ + FUNC(GITMODULES_SYMLINK, ERROR) \ /* warnings */ \ FUNC(BAD_FILEMODE, WARN) \ FUNC(EMPTY_NAME, WARN) \ @@ -561,10 +574,18 @@ has_empty_name |= !*name; has_dot |= !strcmp(name, "."); has_dotdot |= !strcmp(name, ".."); - has_dotgit |= (!strcmp(name, ".git") || - is_hfs_dotgit(name) || - is_ntfs_dotgit(name)); + has_dotgit |= is_hfs_dotgit(name) || is_ntfs_dotgit(name); has_zero_pad |= *(char *)desc.buffer == '0'; + + if (is_hfs_dotgitmodules(name) || is_ntfs_dotgitmodules(name)) { + if (!S_ISLNK(mode)) + oidset_insert(&gitmodules_found, oid); + else + retval += report(options, &item->object, + FSCK_MSG_GITMODULES_SYMLINK, + ".gitmodules is a symbolic link"); + } + if (update_tree_entry_gently(&desc)) { retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree"); break; @@ -901,6 +922,66 @@ return fsck_tag_buffer(tag, data, size, options); } +struct fsck_gitmodules_data { + struct object *obj; + struct fsck_options *options; + int ret; +}; + +static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata) +{ + struct fsck_gitmodules_data *data = vdata; + const char *subsection, *key; + int subsection_len; + char *name; + + if (parse_config_key(var, "submodule", &subsection, &subsection_len, &key) < 0 || + !subsection) + return 0; + + name = xmemdupz(subsection, subsection_len); + if (check_submodule_name(name) < 0) + data->ret |= report(data->options, data->obj, + FSCK_MSG_GITMODULES_NAME, + "disallowed submodule name: %s", + name); + free(name); + + return 0; +} + +static int fsck_blob(struct blob *blob, const char *buf, + unsigned long size, struct fsck_options *options) +{ + struct fsck_gitmodules_data data; + + if (!oidset_contains(&gitmodules_found, &blob->object.oid)) + return 0; + oidset_insert(&gitmodules_done, &blob->object.oid); + + if (!buf) { + /* + * A missing buffer here is a sign that the caller found the + * blob too gigantic to load into memory. Let's just consider + * that an error. + */ + return report(options, &blob->object, + FSCK_MSG_GITMODULES_PARSE, + ".gitmodules too large to parse"); + } + + data.obj = &blob->object; + data.options = options; + data.ret = 0; + if (git_config_from_mem(fsck_gitmodules_fn, CONFIG_ORIGIN_BLOB, + ".gitmodules", buf, size, &data)) + data.ret |= report(options, &blob->object, + FSCK_MSG_GITMODULES_PARSE, + "could not parse gitmodules blob"); + + return data.ret; +} + int fsck_object(struct object *obj, void *data, unsigned long size, struct fsck_options *options) { @@ -908,7 +989,7 @@ return report(options, obj, FSCK_MSG_BAD_OBJECT_SHA1, "no valid object to fsck"); if (obj->type == OBJ_BLOB) - return 0; + return fsck_blob((struct blob *)obj, data, size, options); if (obj->type == OBJ_TREE) return fsck_tree((struct tree *) obj, options); if (obj->type == OBJ_COMMIT) @@ -932,3 +1013,52 @@ error("object %s: %s", describe_object(o, obj), message); return 1; } + +int fsck_finish(struct fsck_options *options) +{ + int ret = 0; + struct oidset_iter iter; + const struct object_id *oid; + + oidset_iter_init(&gitmodules_found, &iter); + while ((oid = oidset_iter_next(&iter))) { + struct blob *blob; + enum object_type type; + unsigned long size; + char *buf; + + if (oidset_contains(&gitmodules_done, oid)) + continue; + + blob = lookup_blob(oid); + if (!blob) { + ret |= report(options, &blob->object, + FSCK_MSG_GITMODULES_BLOB, + "non-blob found at .gitmodules"); + continue; + } + + buf = read_sha1_file(oid->hash, &type, &size); + if (!buf) { + if (is_promisor_object(&blob->object.oid)) + continue; + ret |= report(options, &blob->object, + FSCK_MSG_GITMODULES_MISSING, + "unable to read .gitmodules blob"); + continue; + } + + if (type == OBJ_BLOB) + ret |= fsck_blob(blob, buf, size, options); + else + ret |= report(options, &blob->object, + FSCK_MSG_GITMODULES_BLOB, + "non-blob found at .gitmodules"); + free(buf); + } + + + oidset_clear(&gitmodules_found); + oidset_clear(&gitmodules_done); + return ret; +} diff -Nru git-2.17.0/fsck.h git-2.17.1/fsck.h --- git-2.17.0/fsck.h 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/fsck.h 2018-05-29 08:14:06.000000000 +0000 @@ -53,4 +53,11 @@ int fsck_object(struct object *obj, void *data, unsigned long size, struct fsck_options *options); +/* + * Some fsck checks are context-dependent, and may end up queued; run this + * after completing all fsck_object() calls in order to resolve any remaining + * checks. + */ +int fsck_finish(struct fsck_options *options); + #endif diff -Nru git-2.17.0/git-compat-util.h git-2.17.1/git-compat-util.h --- git-2.17.0/git-compat-util.h 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/git-compat-util.h 2018-05-29 08:14:06.000000000 +0000 @@ -1001,6 +1001,23 @@ return (x & 0x20) == 0; } +/* + * Like skip_prefix, but compare case-insensitively. Note that the comparison + * is done via tolower(), so it is strictly ASCII (no multi-byte characters or + * locale-specific conversions). + */ +static inline int skip_iprefix(const char *str, const char *prefix, + const char **out) +{ + do { + if (!*prefix) { + *out = str; + return 1; + } + } while (tolower(*str++) == tolower(*prefix++)); + return 0; +} + static inline int strtoul_ui(char const *s, int base, unsigned int *result) { unsigned long ul; diff -Nru git-2.17.0/git-submodule.sh git-2.17.1/git-submodule.sh --- git-2.17.0/git-submodule.sh 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/git-submodule.sh 2018-05-29 08:14:06.000000000 +0000 @@ -229,6 +229,11 @@ sm_name="$sm_path" fi + if ! git submodule--helper check-name "$sm_name" + then + die "$(eval_gettext "'$sm_name' is not a valid submodule name")" + fi + # perhaps the path exists and is already a git repo, else clone it if test -e "$sm_path" then diff -Nru git-2.17.0/GIT-VERSION-GEN git-2.17.1/GIT-VERSION-GEN --- git-2.17.0/GIT-VERSION-GEN 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/GIT-VERSION-GEN 2018-05-29 08:14:06.000000000 +0000 @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.17.0 +DEF_VER=v2.17.1 LF=' ' diff -Nru git-2.17.0/path.c git-2.17.1/path.c --- git-2.17.0/path.c 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/path.c 2018-05-29 08:14:06.000000000 +0000 @@ -1305,7 +1305,7 @@ int is_ntfs_dotgit(const char *name) { - int len; + size_t len; for (len = 0; ; len++) if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) { @@ -1322,6 +1322,90 @@ } } +static int is_ntfs_dot_generic(const char *name, + const char *dotgit_name, + size_t len, + const char *dotgit_ntfs_shortname_prefix) +{ + int saw_tilde; + size_t i; + + if ((name[0] == '.' && !strncasecmp(name + 1, dotgit_name, len))) { + i = len + 1; +only_spaces_and_periods: + for (;;) { + char c = name[i++]; + if (!c) + return 1; + if (c != ' ' && c != '.') + return 0; + } + } + + /* + * Is it a regular NTFS short name, i.e. shortened to 6 characters, + * followed by ~1, ... ~4? + */ + if (!strncasecmp(name, dotgit_name, 6) && name[6] == '~' && + name[7] >= '1' && name[7] <= '4') { + i = 8; + goto only_spaces_and_periods; + } + + /* + * Is it a fall-back NTFS short name (for details, see + * https://en.wikipedia.org/wiki/8.3_filename? + */ + for (i = 0, saw_tilde = 0; i < 8; i++) + if (name[i] == '\0') + return 0; + else if (saw_tilde) { + if (name[i] < '0' || name[i] > '9') + return 0; + } else if (name[i] == '~') { + if (name[++i] < '1' || name[i] > '9') + return 0; + saw_tilde = 1; + } else if (i >= 6) + return 0; + else if (name[i] < 0) { + /* + * We know our needles contain only ASCII, so we clamp + * here to make the results of tolower() sane. + */ + return 0; + } else if (tolower(name[i]) != dotgit_ntfs_shortname_prefix[i]) + return 0; + + goto only_spaces_and_periods; +} + +/* + * Inline helper to make sure compiler resolves strlen() on literals at + * compile time. + */ +static inline int is_ntfs_dot_str(const char *name, const char *dotgit_name, + const char *dotgit_ntfs_shortname_prefix) +{ + return is_ntfs_dot_generic(name, dotgit_name, strlen(dotgit_name), + dotgit_ntfs_shortname_prefix); +} + +int is_ntfs_dotgitmodules(const char *name) +{ + return is_ntfs_dot_str(name, "gitmodules", "gi7eba"); +} + +int is_ntfs_dotgitignore(const char *name) +{ + return is_ntfs_dot_str(name, "gitignore", "gi250a"); +} + +int is_ntfs_dotgitattributes(const char *name) +{ + return is_ntfs_dot_str(name, "gitattributes", "gi7d29"); +} + int looks_like_command_line_option(const char *str) { return str && str[0] == '-'; diff -Nru git-2.17.0/read-cache.c git-2.17.1/read-cache.c --- git-2.17.0/read-cache.c 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/read-cache.c 2018-05-29 08:14:06.000000000 +0000 @@ -752,7 +752,7 @@ int size, len; struct cache_entry *ce, *ret; - if (!verify_path(path)) { + if (!verify_path(path, mode)) { error("Invalid path '%s'", path); return NULL; } @@ -817,7 +817,7 @@ * Also, we don't want double slashes or slashes at the * end that can make pathnames ambiguous. */ -static int verify_dotfile(const char *rest) +static int verify_dotfile(const char *rest, unsigned mode) { /* * The first character was '.', but that @@ -831,8 +831,13 @@ switch (*rest) { /* - * ".git" followed by NUL or slash is bad. This - * shares the path end test with the ".." case. + * ".git" followed by NUL or slash is bad. Note that we match + * case-insensitively here, even if ignore_case is not set. + * This outlaws ".GIT" everywhere out of an abundance of caution, + * since there's really no good reason to allow it. + * + * Once we've seen ".git", we can also find ".gitmodules", etc (also + * case-insensitively). */ case 'g': case 'G': @@ -840,8 +845,15 @@ break; if (rest[2] != 't' && rest[2] != 'T') break; - rest += 2; - /* fallthrough */ + if (rest[3] == '\0' || is_dir_sep(rest[3])) + return 0; + if (S_ISLNK(mode)) { + rest += 3; + if (skip_iprefix(rest, "modules", &rest) && + (*rest == '\0' || is_dir_sep(*rest))) + return 0; + } + break; case '.': if (rest[1] == '\0' || is_dir_sep(rest[1])) return 0; @@ -849,7 +861,7 @@ return 1; } -int verify_path(const char *path) +int verify_path(const char *path, unsigned mode) { char c; @@ -862,12 +874,25 @@ return 1; if (is_dir_sep(c)) { inside: - if (protect_hfs && is_hfs_dotgit(path)) - return 0; - if (protect_ntfs && is_ntfs_dotgit(path)) - return 0; + if (protect_hfs) { + if (is_hfs_dotgit(path)) + return 0; + if (S_ISLNK(mode)) { + if (is_hfs_dotgitmodules(path)) + return 0; + } + } + if (protect_ntfs) { + if (is_ntfs_dotgit(path)) + return 0; + if (S_ISLNK(mode)) { + if (is_ntfs_dotgitmodules(path)) + return 0; + } + } + c = *path++; - if ((c == '.' && !verify_dotfile(path)) || + if ((c == '.' && !verify_dotfile(path, mode)) || is_dir_sep(c) || c == '\0') return 0; } @@ -1184,7 +1209,7 @@ if (!ok_to_add) return -1; - if (!verify_path(ce->name)) + if (!verify_path(ce->name, ce->ce_mode)) return error("Invalid path '%s'", ce->name); if (!skip_df_check && diff -Nru git-2.17.0/RelNotes git-2.17.1/RelNotes --- git-2.17.0/RelNotes 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/RelNotes 2018-05-29 08:14:06.000000000 +0000 @@ -1,398 +1,16 @@ -Git 2.17 Release Notes -====================== +Git v2.17.1 Release Notes +========================= -Updates since v2.16 -------------------- - -UI, Workflows & Features - - * "diff" family of commands learned "--find-object=" option - to limit the findings to changes that involve the named object. - - * "git format-patch" learned to give 72-cols to diffstat, which is - consistent with other line length limits the subcommand uses for - its output meant for e-mails. - - * The log from "git daemon" can be redirected with a new option; one - relevant use case is to send the log to standard error (instead of - syslog) when running it from inetd. - - * "git rebase" learned to take "--allow-empty-message" option. - - * "git am" has learned the "--quit" option, in addition to the - existing "--abort" option; having the pair mirrors a few other - commands like "rebase" and "cherry-pick". - - * "git worktree add" learned to run the post-checkout hook, just like - "git clone" runs it upon the initial checkout. - - * "git tag" learned an explicit "--edit" option that allows the - message given via "-m" and "-F" to be further edited. - - * "git fetch --prune-tags" may be used as a handy short-hand for - getting rid of stale tags that are locally held. - - * The new "--show-current-patch" option gives an end-user facing way - to get the diff being applied when "git rebase" (and "git am") - stops with a conflict. - - * "git add -p" used to offer "/" (look for a matching hunk) as a - choice, even there was only one hunk, which has been corrected. - Also the single-key help is now given only for keys that are - enabled (e.g. help for '/' won't be shown when there is only one - hunk). - - * Since Git 1.7.9, "git merge" defaulted to --no-ff (i.e. even when - the side branch being merged is a descendant of the current commit, - create a merge commit instead of fast-forwarding) when merging a - tag object. This was appropriate default for integrators who pull - signed tags from their downstream contributors, but caused an - unnecessary merges when used by downstream contributors who - habitually "catch up" their topic branches with tagged releases - from the upstream. Update "git merge" to default to --no-ff only - when merging a tag object that does *not* sit at its usual place in - refs/tags/ hierarchy, and allow fast-forwarding otherwise, to - mitigate the problem. - - * "git status" can spend a lot of cycles to compute the relation - between the current branch and its upstream, which can now be - disabled with "--no-ahead-behind" option. - - * "git diff" and friends learned funcname patterns for Go language - source files. - - * "git send-email" learned "--reply-to=
" option. - - * Funcname pattern used for C# now recognizes "async" keyword. - - * In a way similar to how "git tag" learned to honor the pager - setting only in the list mode, "git config" learned to ignore the - pager setting when it is used for setting values (i.e. when the - purpose of the operation is not to "show"). - - -Performance, Internal Implementation, Development Support etc. - - * More perf tests for threaded grep - - * "perf" test output can be sent to codespeed server. - - * The build procedure for perl/ part has been greatly simplified by - weaning ourselves off of MakeMaker. - - * Perl 5.8 or greater has been required since Git 1.7.4 released in - 2010, but we continued to assume some core modules may not exist and - used a conditional "eval { require <> }"; we no longer do - this. Some platforms (Fedora/RedHat/CentOS, for example) ship Perl - without all core modules by default (e.g. Digest::MD5, File::Temp, - File::Spec, Net::Domain, Net::SMTP). Users on such platforms may - need to install these additional modules. - - * As a convenience, we install copies of Perl modules we require which - are not part of the core Perl distribution (e.g. Error and - Mail::Address). Users and packagers whose operating system provides - these modules can set NO_PERL_CPAN_FALLBACKS to avoid installing the - bundled modules. - - * In preparation for implementing narrow/partial clone, the machinery - for checking object connectivity used by gc and fsck has been - taught that a missing object is OK when it is referenced by a - packfile specially marked as coming from trusted repository that - promises to make them available on-demand and lazily. - - * The machinery to clone & fetch, which in turn involves packing and - unpacking objects, has been told how to omit certain objects using - the filtering mechanism introduced by another topic. It now knows - to mark the resulting pack as a promisor pack to tolerate missing - objects, laying foundation for "narrow" clones. - - * The first step to getting rid of mru API and using the - doubly-linked list API directly instead. - - * Retire mru API as it does not give enough abstraction over - underlying list API to be worth it. - - * Rewrite two more "git submodule" subcommands in C. - - * The tracing machinery learned to report tweaking of environment - variables as well. - - * Update Coccinelle rules to catch and optimize strbuf_addf(&buf, "%s", str) - - * Prevent "clang-format" from breaking line after function return type. - - * The sequencer infrastructure is shared across "git cherry-pick", - "git rebase -i", etc., and has always spawned "git commit" when it - needs to create a commit. It has been taught to do so internally, - when able, by reusing the codepath "git commit" itself uses, which - gives performance boost for a few tens of percents in some sample - scenarios. - - * Push the submodule version of collision-detecting SHA-1 hash - implementation a bit harder on builders. - - * Avoid mmapping small files while using packed refs (especially ones - with zero size, which would cause later munmap() to fail). - - * Conversion from uchar[20] to struct object_id continues. - - * More tests for wildmatch functions. - - * The code to binary search starting from a fan-out table (which is - how the packfile is indexed with object names) has been refactored - into a reusable helper. - - * We now avoid using identifiers that clash with C++ keywords. Even - though it is not a goal to compile Git with C++ compilers, changes - like this help use of code analysis tools that targets C++ on our - codebase. - - * The executable is now built in 'script' phase in Travis CI integration, - to follow the established practice, rather than during 'before_script' - phase. This allows the CI categorize the failures better ('failed' - is project's fault, 'errored' is build environment's). - (merge 3c93b82920 sg/travis-build-during-script-phase later to maint). - - * Writing out the index file when the only thing that changed in it - is the untracked cache information is often wasteful, and this has - been optimized out. - - * Various pieces of Perl code we have have been cleaned up. - - * Internal API clean-up to allow write_locked_index() optionally skip - writing the in-core index when it is not modified. - - -Also contains various documentation updates and code clean-ups. - - -Fixes since v2.16 +Fixes since v2.17 ----------------- - * An old regression in "git describe --all $annotated_tag^0" has been - fixed. - - * "git status" after moving a path in the working tree (hence making - it appear "removed") and then adding with the -N option (hence - making that appear "added") detected it as a rename, but did not - report the old and new pathnames correctly. - - * "git svn dcommit" did not take into account the fact that a - svn+ssh:// URL with a username@ (typically used for pushing) refers - to the same SVN repository without the username@ and failed when - svn.pushmergeinfo option is set. - - * API clean-up around revision traversal. - - * "git merge -Xours/-Xtheirs" learned to use our/their version when - resolving a conflicting updates to a symbolic link. - - * "git clone $there $here" is allowed even when here directory exists - as long as it is an empty directory, but the command incorrectly - removed it upon a failure of the operation. - - * "git commit --fixup" did not allow "-m" option to be used - at the same time; allow it to annotate resulting commit with more - text. - - * When resetting the working tree files recursively, the working tree - of submodules are now also reset to match. - - * "git stash -- " incorrectly blew away untracked files in - the directory that matched the pathspec, which has been corrected. - - * Instead of maintaining home-grown email address parsing code, ship - a copy of reasonably recent Mail::Address to be used as a fallback - in 'git send-email' when the platform lacks it. - (merge d60be8acab mm/send-email-fallback-to-local-mail-address later to maint). - - * "git add -p" was taught to ignore local changes to submodules as - they do not interfere with the partial addition of regular changes - anyway. - - * Avoid showing a warning message in the middle of a line of "git - diff" output. - (merge 4e056c989f nd/diff-flush-before-warning later to maint). - - * The http tracing code, often used to debug connection issues, - learned to redact potentially sensitive information from its output - so that it can be more safely sharable. - (merge 8ba18e6fa4 jt/http-redact-cookies later to maint). - - * Crash fix for a corner case where an error codepath tried to unlock - what it did not acquire lock on. - (merge 81fcb698e0 mr/packed-ref-store-fix later to maint). - - * The split-index mode had a few corner case bugs fixed. - (merge ae59a4e44f tg/split-index-fixes later to maint). - - * Assorted fixes to "git daemon". - (merge ed15e58efe jk/daemon-fixes later to maint). - - * Completion of "git merge -s" (in contrib/) did not work - well in non-C locale. - (merge 7cc763aaa3 nd/list-merge-strategy later to maint). - - * Workaround for segfault with more recent versions of SVN. - (merge 7f6f75e97a ew/svn-branch-segfault-fix later to maint). - - * Plug recently introduced leaks in fsck. - (merge ba3a08ca0e jt/fsck-code-cleanup later to maint). - - * "git pull --rebase" did not pass verbosity setting down when - recursing into a submodule. - (merge a56771a668 sb/pull-rebase-submodule later to maint). - - * The way "git reset --hard" reports the commit the updated HEAD - points at is made consistent with the way how the commit title is - generated by the other parts of the system. This matters when the - title is spread across physically multiple lines. - (merge 1cf823fb68 tg/reset-hard-show-head-with-pretty later to maint). - - * Test fixes. - (merge 63b1a175ee sg/test-i18ngrep later to maint). - - * Some bugs around "untracked cache" feature have been fixed. This - will notice corrupt data in the untracked cache left by old and - buggy code and issue a warning---the index can be fixed by clearing - the untracked cache from it. - (merge 0cacebf099 nd/fix-untracked-cache-invalidation later to maint). - (merge 7bf0be7501 ab/untracked-cache-invalidation-docs later to maint). - - * "git blame HEAD COPYING" in a bare repository failed to run, while - "git blame HEAD -- COPYING" run just fine. This has been corrected. - - * "git add" files in the same directory, but spelling the directory - path in different cases on case insensitive filesystem, corrupted - the name hash data structure and led to unexpected results. This - has been corrected. - (merge c95525e90d bp/name-hash-dirname-fix later to maint). - - * "git rebase -p" mangled log messages of a merge commit, which is - now fixed. - (merge ed5144d7eb js/fix-merge-arg-quoting-in-rebase-p later to maint). - - * Some low level protocol codepath could crash when they get an - unexpected flush packet, which is now fixed. - (merge bb1356dc64 js/packet-read-line-check-null later to maint). - - * "git check-ignore" with multiple paths got confused when one is a - file and the other is a directory, which has been fixed. - (merge d60771e930 rs/check-ignore-multi later to maint). - - * "git describe $garbage" stopped giving any errors when the garbage - happens to be a string with 40 hexadecimal letters. - (merge a8e7a2bf0f sb/describe-blob later to maint). - - * Code to unquote single-quoted string (used in the parser for - configuration files, etc.) did not diagnose bogus input correctly - and produced bogus results instead. - (merge ddbbf8eb25 jk/sq-dequote-on-bogus-input later to maint). - - * Many places in "git apply" knew that "/dev/null" that signals - "there is no such file on this side of the diff" can be followed by - whitespace and garbage when parsing a patch, except for one, which - made an otherwise valid patch (e.g. ones from subversion) rejected. - (merge e454ad4bec tk/apply-dev-null-verify-name-fix later to maint). - - * We no longer create any *.spec file, so "make clean" should not - remove it. - (merge 4321bdcabb tz/do-not-clean-spec-file later to maint). - - * "git push" over http transport did not unquote the push-options - correctly. - (merge 90dce21eb0 jk/push-options-via-transport-fix later to maint). - - * "git send-email" learned to complain when the batch-size option is - not defined when the relogin-delay option is, since these two are - mutually required. - (merge 9caa70697b xz/send-email-batch-size later to maint). - - * Y2k20 fix ;-) for our perl scripts. - (merge a40e06ee33 bw/perl-timegm-timelocal-fix later to maint). - - * Threaded "git grep" has been optimized to avoid allocation in code - section that is covered under a mutex. - (merge 38ef24dccf rv/grep-cleanup later to maint). - - * "git subtree" script (in contrib/) scripted around "git log", whose - output got affected by end-user configuration like log.showsignature - (merge 8841b5222c sg/subtree-signed-commits later to maint). - - * While finding unique object name abbreviation, the code may - accidentally have read beyond the end of the array of object names - in a pack. - (merge 21abed500c ds/find-unique-abbrev-optim later to maint). - - * Micro optimization in revision traversal code. - (merge ebbed3ba04 ds/mark-parents-uninteresting-optim later to maint). - - * "git commit" used to run "gc --auto" near the end, which was lost - when the command was reimplemented in C by mistake. - (merge 095c741edd ab/gc-auto-in-commit later to maint). - - * Allow running a couple of tests with "sh -x". - (merge c20bf94abc sg/cvs-tests-with-x later to maint). - - * The codepath to replace an existing entry in the index had a bug in - updating the name hash structure, which has been fixed. - (merge 0e267b7a24 bp/refresh-cache-ent-rehash-fix later to maint). - - * The transfer.fsckobjects configuration tells "git fetch" to - validate the data and connected-ness of objects in the received - pack; the code to perform this check has been taught about the - narrow clone's convention that missing objects that are reachable - from objects in a pack that came from a promissor remote is OK. - - * There was an unused file-scope static variable left in http.c when - building for versions of libCURL that is older than 7.19.4, which - has been fixed. - (merge b8fd6008ec rj/http-code-cleanup later to maint). - - * Shell script portability fix. - (merge 206a6ae013 ml/filter-branch-portability-fix later to maint). - - * Other minor doc, test and build updates and code cleanups. - (merge e2a5a028c7 bw/oidmap-autoinit later to maint). - (merge ec3b4b06f8 cl/t9001-cleanup later to maint). - (merge e1b3f3dd38 ks/submodule-doc-updates later to maint). - (merge fbac558a9b rs/describe-unique-abbrev later to maint). - (merge 8462ff43e4 tb/crlf-conv-flags later to maint). - (merge 7d68bb0766 rb/hashmap-h-compilation-fix later to maint). - (merge 3449847168 cc/sha1-file-name later to maint). - (merge ad622a256f ds/use-get-be64 later to maint). - (merge f919ffebed sg/cocci-move-array later to maint). - (merge 4e801463c7 jc/mailinfo-cleanup-fix later to maint). - (merge ef5b3a6c5e nd/shared-index-fix later to maint). - (merge 9f5258cbb8 tz/doc-show-defaults-to-head later to maint). - (merge b780e4407d jc/worktree-add-short-help later to maint). - (merge ae239fc8e5 rs/cocci-strbuf-addf-to-addstr later to maint). - (merge 2e22a85e5c nd/ignore-glob-doc-update later to maint). - (merge 3738031581 jk/gettext-poison later to maint). - (merge 54360a1956 rj/sparse-updates later to maint). - (merge 12e31a6b12 sg/doc-test-must-fail-args later to maint). - (merge 760f1ad101 bc/doc-interpret-trailers-grammofix later to maint). - (merge 4ccf461f56 bp/fsmonitor later to maint). - (merge a6119f82b1 jk/test-hashmap-updates later to maint). - (merge 5aea9fe6cc rd/typofix later to maint). - (merge e4e5da2796 sb/status-doc-fix later to maint). - (merge 7976e901c8 gs/test-unset-xdg-cache-home later to maint). - (merge d023df1ee6 tg/worktree-create-tracking later to maint). - (merge 4cbe92fd41 sm/mv-dry-run-update later to maint). - (merge 75e5e9c3f7 sb/color-h-cleanup later to maint). - (merge 2708ef4af6 sg/t6300-modernize later to maint). - (merge d88e92d4e0 bw/doc-submodule-recurse-config-with-clone later to maint). - (merge f74bbc8dd2 jk/cached-commit-buffer later to maint). - (merge 1316416903 ms/non-ascii-ticks later to maint). - (merge 878056005e rs/strbuf-read-file-or-whine later to maint). - (merge 79f0ba1547 jk/strbuf-read-file-close-error later to maint). - (merge edfb8ba068 ot/ref-filter-cleanup later to maint). - (merge 11395a3b4b jc/test-must-be-empty later to maint). - (merge 768b9d6db7 mk/doc-pretty-fill later to maint). - (merge 2caa7b8d27 ab/man-sec-list later to maint). - (merge 40c17eb184 ks/t3200-typofix later to maint). - (merge bd9958c358 dp/merge-strategy-doc-fix later to maint). - (merge 9ee0540a40 js/ming-strftime later to maint). - (merge 1775e990f7 tz/complete-tag-delete-tagname later to maint). - (merge 00a4b03501 rj/warning-uninitialized-fix later to maint). - (merge b635ed97a0 jk/attributes-path-doc later to maint). + * This release contains the same fixes made in the v2.13.7 version of + Git, covering CVE-2018-11233 and 11235, and forward-ported to + v2.14.4, v2.15.2 and v2.16.4 releases. See release notes to + v2.13.7 for details. + + * In addition to the above fixes, this release has support on the + server side to reject pushes to repositories that attempt to create + such problematic .gitmodules file etc. as tracked contents, to help + hosting sites protect their customers by preventing malicious + contents from spreading. diff -Nru git-2.17.0/sha1_file.c git-2.17.1/sha1_file.c --- git-2.17.0/sha1_file.c 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/sha1_file.c 2018-05-29 08:14:06.000000000 +0000 @@ -2209,7 +2209,7 @@ goto out; } - if (*type == OBJ_BLOB) { + if (*type == OBJ_BLOB && *size > big_file_threshold) { if (check_stream_sha1(&stream, hdr, *size, path, expected_sha1) < 0) goto out; } else { diff -Nru git-2.17.0/submodule-config.c git-2.17.1/submodule-config.c --- git-2.17.0/submodule-config.c 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/submodule-config.c 2018-05-29 08:14:06.000000000 +0000 @@ -190,6 +190,31 @@ return NULL; } +int check_submodule_name(const char *name) +{ + /* Disallow empty names */ + if (!*name) + return -1; + + /* + * Look for '..' as a path component. Check both '/' and '\\' as + * separators rather than is_dir_sep(), because we want the name rules + * to be consistent across platforms. + */ + goto in_component; /* always start inside component */ + while (*name) { + char c = *name++; + if (c == '/' || c == '\\') { +in_component: + if (name[0] == '.' && name[1] == '.' && + (!name[2] || name[2] == '/' || name[2] == '\\')) + return -1; + } + } + + return 0; +} + static int name_and_item_from_var(const char *var, struct strbuf *name, struct strbuf *item) { @@ -201,6 +226,12 @@ return 0; strbuf_add(name, subsection, subsection_len); + if (check_submodule_name(name->buf) < 0) { + warning(_("ignoring suspicious submodule name: %s"), name->buf); + strbuf_release(name); + return 0; + } + strbuf_addstr(item, key); return 1; diff -Nru git-2.17.0/submodule-config.h git-2.17.1/submodule-config.h --- git-2.17.0/submodule-config.h 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/submodule-config.h 2018-05-29 08:14:06.000000000 +0000 @@ -48,4 +48,11 @@ const char *key); extern void submodule_free(void); +/* + * Returns 0 if the name is syntactically acceptable as a submodule "name" + * (e.g., that may be found in the subsection of a .gitmodules file) and -1 + * otherwise. + */ +int check_submodule_name(const char *name); + #endif /* SUBMODULE_CONFIG_H */ diff -Nru git-2.17.0/t/helper/test-path-utils.c git-2.17.1/t/helper/test-path-utils.c --- git-2.17.0/t/helper/test-path-utils.c 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/t/helper/test-path-utils.c 2018-05-29 08:14:06.000000000 +0000 @@ -1,5 +1,6 @@ #include "cache.h" #include "string-list.h" +#include "utf8.h" /* * A "string_list_each_func_t" function that normalizes an entry from @@ -170,6 +171,11 @@ { NULL, NULL } }; +static int is_dotgitmodules(const char *path) +{ + return is_hfs_dotgitmodules(path) || is_ntfs_dotgitmodules(path); +} + int cmd_main(int argc, const char **argv) { if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) { @@ -270,6 +276,20 @@ if (argc == 2 && !strcmp(argv[1], "dirname")) return test_function(dirname_data, posix_dirname, argv[1]); + if (argc > 2 && !strcmp(argv[1], "is_dotgitmodules")) { + int res = 0, expect = 1, i; + for (i = 2; i < argc; i++) + if (!strcmp("--not", argv[i])) + expect = !expect; + else if (expect != is_dotgitmodules(argv[i])) + res = error("'%s' is %s.gitmodules", argv[i], + expect ? "not " : ""); + else + fprintf(stderr, "ok: '%s' is %s.gitmodules\n", + argv[i], expect ? "" : "not "); + return !!res; + } + fprintf(stderr, "%s: unknown function name: %s\n", argv[0], argv[1] ? argv[1] : "(there was none)"); return 1; diff -Nru git-2.17.0/t/lib-pack.sh git-2.17.1/t/lib-pack.sh --- git-2.17.0/t/lib-pack.sh 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/t/lib-pack.sh 2018-05-29 08:14:06.000000000 +0000 @@ -79,6 +79,18 @@ ;; esac + # If it's not a delta, we can convince pack-objects to generate a pack + # with just our entry, and then strip off the header (12 bytes) and + # trailer (20 bytes). + if test -z "$2" + then + echo "$1" | git pack-objects --stdout >pack_obj.tmp && + size=$(wc -c &2 "BUG: don't know how to print $1${2:+ (from $2)}" return 1 } diff -Nru git-2.17.0/t/t0060-path-utils.sh git-2.17.1/t/t0060-path-utils.sh --- git-2.17.0/t/t0060-path-utils.sh 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/t/t0060-path-utils.sh 2018-05-29 08:14:06.000000000 +0000 @@ -349,4 +349,90 @@ test_submodule_relative_url "(null)" "user@host:path/to/repo" "../subrepo" "user@host:path/to/subrepo" test_submodule_relative_url "(null)" "user@host:repo" "../subrepo" "user@host:subrepo" +test_expect_success 'match .gitmodules' ' + test-path-utils is_dotgitmodules \ + .gitmodules \ + \ + .git${u200c}modules \ + \ + .Gitmodules \ + .gitmoduleS \ + \ + ".gitmodules " \ + ".gitmodules." \ + ".gitmodules " \ + ".gitmodules. " \ + ".gitmodules ." \ + ".gitmodules.." \ + ".gitmodules " \ + ".gitmodules. " \ + ".gitmodules . " \ + ".gitmodules ." \ + \ + ".Gitmodules " \ + ".Gitmodules." \ + ".Gitmodules " \ + ".Gitmodules. " \ + ".Gitmodules ." \ + ".Gitmodules.." \ + ".Gitmodules " \ + ".Gitmodules. " \ + ".Gitmodules . " \ + ".Gitmodules ." \ + \ + GITMOD~1 \ + gitmod~1 \ + GITMOD~2 \ + gitmod~3 \ + GITMOD~4 \ + \ + "GITMOD~1 " \ + "gitmod~2." \ + "GITMOD~3 " \ + "gitmod~4. " \ + "GITMOD~1 ." \ + "gitmod~2 " \ + "GITMOD~3. " \ + "gitmod~4 . " \ + \ + GI7EBA~1 \ + gi7eba~9 \ + \ + GI7EB~10 \ + GI7EB~11 \ + GI7EB~99 \ + GI7EB~10 \ + GI7E~100 \ + GI7E~101 \ + GI7E~999 \ + ~1000000 \ + ~9999999 \ + \ + --not \ + ".gitmodules x" \ + ".gitmodules .x" \ + \ + " .gitmodules" \ + \ + ..gitmodules \ + \ + gitmodules \ + \ + .gitmodule \ + \ + ".gitmodules x " \ + ".gitmodules .x" \ + \ + GI7EBA~ \ + GI7EBA~0 \ + GI7EBA~~1 \ + GI7EBA~X \ + Gx7EBA~1 \ + GI7EBX~1 \ + \ + GI7EB~1 \ + GI7EB~01 \ + GI7EB~1X +' + test_done diff -Nru git-2.17.0/t/t7415-submodule-names.sh git-2.17.1/t/t7415-submodule-names.sh --- git-2.17.0/t/t7415-submodule-names.sh 1970-01-01 00:00:00.000000000 +0000 +++ git-2.17.1/t/t7415-submodule-names.sh 2018-05-29 08:14:06.000000000 +0000 @@ -0,0 +1,154 @@ +#!/bin/sh + +test_description='check handling of .. in submodule names + +Exercise the name-checking function on a variety of names, and then give a +real-world setup that confirms we catch this in practice. +' +. ./test-lib.sh +. "$TEST_DIRECTORY"/lib-pack.sh + +test_expect_success 'check names' ' + cat >expect <<-\EOF && + valid + valid/with/paths + EOF + + git submodule--helper check-name >actual <<-\EOF && + valid + valid/with/paths + + ../foo + /../foo + ..\foo + \..\foo + foo/.. + foo/../ + foo\.. + foo\..\ + foo/../bar + EOF + + test_cmp expect actual +' + +test_expect_success 'create innocent subrepo' ' + git init innocent && + git -C innocent commit --allow-empty -m foo +' + +test_expect_success 'submodule add refuses invalid names' ' + test_must_fail \ + git submodule add --name ../../modules/evil "$PWD/innocent" evil +' + +test_expect_success 'add evil submodule' ' + git submodule add "$PWD/innocent" evil && + + mkdir modules && + cp -r .git/modules/evil modules && + write_script modules/evil/hooks/post-checkout <<-\EOF && + echo >&2 "RUNNING POST CHECKOUT" + EOF + + git config -f .gitmodules submodule.evil.update checkout && + git config -f .gitmodules --rename-section \ + submodule.evil submodule.../../modules/evil && + git add modules && + git commit -am evil +' + +# This step seems like it shouldn't be necessary, since the payload is +# contained entirely in the evil submodule. But due to the vagaries of the +# submodule code, checking out the evil module will fail unless ".git/modules" +# exists. Adding another submodule (with a name that sorts before "evil") is an +# easy way to make sure this is the case in the victim clone. +test_expect_success 'add other submodule' ' + git submodule add "$PWD/innocent" another-module && + git add another-module && + git commit -am another +' + +test_expect_success 'clone evil superproject' ' + git clone --recurse-submodules . victim >output 2>&1 && + ! grep "RUNNING POST CHECKOUT" output +' + +test_expect_success 'fsck detects evil superproject' ' + test_must_fail git fsck +' + +test_expect_success 'transfer.fsckObjects detects evil superproject (unpack)' ' + rm -rf dst.git && + git init --bare dst.git && + git -C dst.git config transfer.fsckObjects true && + test_must_fail git push dst.git HEAD +' + +test_expect_success 'transfer.fsckObjects detects evil superproject (index)' ' + rm -rf dst.git && + git init --bare dst.git && + git -C dst.git config transfer.fsckObjects true && + git -C dst.git config transfer.unpackLimit 1 && + test_must_fail git push dst.git HEAD +' + +# Normally our packs contain commits followed by trees followed by blobs. This +# reverses the order, which requires backtracking to find the context of a +# blob. We'll start with a fresh gitmodules-only tree to make it simpler. +test_expect_success 'create oddly ordered pack' ' + git checkout --orphan odd && + git rm -rf --cached . && + git add .gitmodules && + git commit -m odd && + { + pack_header 3 && + pack_obj $(git rev-parse HEAD:.gitmodules) && + pack_obj $(git rev-parse HEAD^{tree}) && + pack_obj $(git rev-parse HEAD) + } >odd.pack && + pack_trailer odd.pack +' + +test_expect_success 'transfer.fsckObjects handles odd pack (unpack)' ' + rm -rf dst.git && + git init --bare dst.git && + test_must_fail git -C dst.git unpack-objects --strict output && + grep gitmodulesSymlink output + ) +' + +test_done diff -Nru git-2.17.0/utf8.c git-2.17.1/utf8.c --- git-2.17.0/utf8.c 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/utf8.c 2018-05-29 08:14:06.000000000 +0000 @@ -620,28 +620,33 @@ } } -int is_hfs_dotgit(const char *path) +static int is_hfs_dot_generic(const char *path, + const char *needle, size_t needle_len) { ucs_char_t c; c = next_hfs_char(&path); if (c != '.') return 0; - c = next_hfs_char(&path); /* * there's a great deal of other case-folding that occurs - * in HFS+, but this is enough to catch anything that will - * convert to ".git" + * in HFS+, but this is enough to catch our fairly vanilla + * hard-coded needles. */ - if (c != 'g' && c != 'G') - return 0; - c = next_hfs_char(&path); - if (c != 'i' && c != 'I') - return 0; - c = next_hfs_char(&path); - if (c != 't' && c != 'T') - return 0; + for (; needle_len > 0; needle++, needle_len--) { + c = next_hfs_char(&path); + + /* + * We know our needles contain only ASCII, so we clamp here to + * make the results of tolower() sane. + */ + if (c > 127) + return 0; + if (tolower(c) != *needle) + return 0; + } + c = next_hfs_char(&path); if (c && !is_dir_sep(c)) return 0; @@ -649,6 +654,35 @@ return 1; } +/* + * Inline wrapper to make sure the compiler resolves strlen() on literals at + * compile time. + */ +static inline int is_hfs_dot_str(const char *path, const char *needle) +{ + return is_hfs_dot_generic(path, needle, strlen(needle)); +} + +int is_hfs_dotgit(const char *path) +{ + return is_hfs_dot_str(path, "git"); +} + +int is_hfs_dotgitmodules(const char *path) +{ + return is_hfs_dot_str(path, "gitmodules"); +} + +int is_hfs_dotgitignore(const char *path) +{ + return is_hfs_dot_str(path, "gitignore"); +} + +int is_hfs_dotgitattributes(const char *path) +{ + return is_hfs_dot_str(path, "gitattributes"); +} + const char utf8_bom[] = "\357\273\277"; int skip_utf8_bom(char **text, size_t len) diff -Nru git-2.17.0/utf8.h git-2.17.1/utf8.h --- git-2.17.0/utf8.h 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/utf8.h 2018-05-29 08:14:06.000000000 +0000 @@ -52,8 +52,13 @@ * The path should be NUL-terminated, but we will match variants of both ".git\0" * and ".git/..." (but _not_ ".../.git"). This makes it suitable for both fsck * and verify_path(). + * + * Likewise, the is_hfs_dotgitfoo() variants look for ".gitfoo". */ int is_hfs_dotgit(const char *path); +int is_hfs_dotgitmodules(const char *path); +int is_hfs_dotgitignore(const char *path); +int is_hfs_dotgitattributes(const char *path); typedef enum { ALIGN_LEFT, diff -Nru git-2.17.0/version git-2.17.1/version --- git-2.17.0/version 2018-04-02 17:44:04.000000000 +0000 +++ git-2.17.1/version 2018-05-29 08:14:06.000000000 +0000 @@ -1 +1 @@ -2.17.0 +2.17.1