Skip to content

Commit

Permalink
Sync with Git 2.17.1
Browse files Browse the repository at this point in the history
* maint: (25 commits)
  Git 2.17.1
  Git 2.16.4
  Git 2.15.2
  Git 2.14.4
  Git 2.13.7
  fsck: complain when .gitmodules is a symlink
  index-pack: check .gitmodules files with --strict
  unpack-objects: call fsck_finish() after fscking objects
  fsck: call fsck_finish() after fscking objects
  fsck: check .gitmodules content
  fsck: handle promisor objects in .gitmodules check
  fsck: detect gitmodules files
  fsck: actually fsck blob data
  fsck: simplify ".git" check
  index-pack: make fsck error message more specific
  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
  ...
  • Loading branch information
gitster committed May 29, 2018
2 parents e144d12 + fc54c1a commit 7913f53
Show file tree
Hide file tree
Showing 28 changed files with 794 additions and 72 deletions.
20 changes: 20 additions & 0 deletions Documentation/RelNotes/2.13.7.txt
@@ -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.
5 changes: 5 additions & 0 deletions Documentation/RelNotes/2.14.4.txt
@@ -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.
3 changes: 3 additions & 0 deletions Documentation/RelNotes/2.15.2.txt
Expand Up @@ -43,5 +43,8 @@ Fixes since v2.15.1
* 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.
5 changes: 5 additions & 0 deletions Documentation/RelNotes/2.16.4.txt
@@ -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.
16 changes: 16 additions & 0 deletions Documentation/RelNotes/2.17.1.txt
@@ -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.
4 changes: 2 additions & 2 deletions apply.c
Expand Up @@ -3860,9 +3860,9 @@ static int check_unsafe_path(struct patch *patch)
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;
}
Expand Down
45 changes: 23 additions & 22 deletions builtin/fsck.c
Expand Up @@ -340,7 +340,7 @@ static void check_connectivity(void)
}
}

static int fsck_obj(struct object *obj)
static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
{
int err;

Expand All @@ -354,7 +354,7 @@ static int fsck_obj(struct object *obj)

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;

Expand Down Expand Up @@ -399,7 +399,7 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
}
obj->flags &= ~(REACHABLE | SEEN);
obj->flags |= HAS_OBJ;
return fsck_obj(obj);
return fsck_obj(obj, buffer, size);
}

static int default_refs;
Expand Down Expand Up @@ -507,44 +507,42 @@ static void get_default_heads(void)
}
}

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, &type, &size, &contents) < 0)
return NULL;
if (read_loose_object(path, oid, &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)
Expand Down Expand Up @@ -756,6 +754,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
}
stop_progress(&progress);
}

if (fsck_finish(&fsck_obj_options))
errors_found |= ERROR_OBJECT;
}

for (i = 0; i < argc; i++) {
Expand Down
12 changes: 11 additions & 1 deletion builtin/index-pack.c
Expand Up @@ -837,6 +837,9 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
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;
Expand All @@ -854,7 +857,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
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));

Expand Down Expand Up @@ -1479,6 +1482,9 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
} 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 {
Expand Down Expand Up @@ -1820,6 +1826,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
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)
Expand Down
24 changes: 24 additions & 0 deletions builtin/submodule--helper.c
Expand Up @@ -1825,6 +1825,29 @@ static int is_active(int argc, const char **argv, const char *prefix)
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 {
Expand All @@ -1850,6 +1873,7 @@ static struct cmd_struct commands[] = {
{"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)
Expand Down
7 changes: 5 additions & 2 deletions builtin/unpack-objects.c
Expand Up @@ -210,7 +210,7 @@ static int check_object(struct object *obj, int type, void *data, struct fsck_op
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));
Expand Down Expand Up @@ -572,8 +572,11 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
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);
Expand Down
32 changes: 21 additions & 11 deletions builtin/update-index.c
Expand Up @@ -364,10 +364,9 @@ static int process_directory(const char *path, int len, struct stat *st)
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);
Expand All @@ -391,13 +390,13 @@ static int process_path(const char *path)
* 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,
Expand All @@ -406,7 +405,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
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);
Expand Down Expand Up @@ -449,7 +448,18 @@ static void chmod_path(char flip, const char *path)

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;
}
Expand All @@ -475,7 +485,7 @@ static void update_one(const char *path)
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);
}
Expand Down Expand Up @@ -545,7 +555,7 @@ static void read_index_info(int nul_term_line)
path_name = uq.buf;
}

if (!verify_path(path_name)) {
if (!verify_path(path_name, mode)) {
fprintf(stderr, "Ignoring path %s\n", path_name);
continue;
}
Expand Down
12 changes: 10 additions & 2 deletions cache.h
Expand Up @@ -642,7 +642,7 @@ extern int unmerged_index(const struct index_state *);
*/
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);
Expand Down Expand Up @@ -1168,7 +1168,15 @@ int normalize_path_copy(char *dst, const char *src);
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
Expand Down
2 changes: 1 addition & 1 deletion dir.c
Expand Up @@ -2993,7 +2993,7 @@ void untracked_cache_invalidate_path(struct index_state *istate,
{
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));
Expand Down

0 comments on commit 7913f53

Please sign in to comment.