From f514ef9787f320287d7ba71f2965127b9d8b3832 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 19 Aug 2015 14:12:37 -0400 Subject: verify_absent: allow filenames longer than PATH_MAX When unpack-trees wants to know whether a path will overwrite anything in the working tree, we use lstat() to see if there is anything there. But if we are going to write "foo/bar", we can't just lstat("foo/bar"); we need to look for leading prefixes (e.g., "foo"). So we use the lstat cache to find the length of the leading prefix, and copy the filename up to that length into a temporary buffer (since the original name is const, we cannot just stick a NUL in it). The copy we make goes into a PATH_MAX-sized buffer, which will overflow if the prefix is longer than PATH_MAX. How this happens is a little tricky, since in theory PATH_MAX is the biggest path we will have read from the filesystem. But this can happen if: - the compiled-in PATH_MAX does not accurately reflect what the filesystem is capable of - the leading prefix is not _quite_ what is on disk; it contains the next element from the name we are checking. So if we want to write "aaa/bbb/ccc/ddd" and "aaa/bbb" exists, the prefix of interest is "aaa/bbb/ccc". If "aaa/bbb" approaches PATH_MAX, then "ccc" can overflow it. So this can be triggered, but it's hard to do. In particular, you cannot just "git clone" a bogus repo. The verify_absent checks happen before unpack-trees writes anything to the filesystem, so there are never any leading prefixes during the initial checkout, and the bug doesn't trigger. And by definition, these files are larger than PATH_MAX, so writing them will fail, and clone will complain (though it may write a partial path, which will cause a subsequent "git checkout" to hit the bug). We can fix it by creating the temporary path on the heap. The extra malloc overhead is not important, as we are already making at least one stat() call (and probably more for the prefix discovery). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- unpack-trees.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/unpack-trees.c b/unpack-trees.c index 256df47b35..4a6347899c 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -1432,15 +1432,18 @@ static int verify_absent_1(const struct cache_entry *ce, if (!len) return 0; else if (len > 0) { - char path[PATH_MAX + 1]; - memcpy(path, ce->name, len); - path[len] = 0; + char *path; + int ret; + + path = xmemdupz(ce->name, len); if (lstat(path, &st)) - return error("cannot stat '%s': %s", path, + ret = error("cannot stat '%s': %s", path, strerror(errno)); - - return check_ok_to_remove(path, len, DT_UNKNOWN, NULL, &st, - error_type, o); + else + ret = check_ok_to_remove(path, len, DT_UNKNOWN, NULL, + &st, error_type, o); + free(path); + return ret; } else if (lstat(ce->name, &st)) { if (errno != ENOENT) return error("cannot stat '%s': %s", ce->name, -- cgit v1.2.3 From c29edfefb6f6a3fef80172c16bcc34c826d417b0 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 19 Aug 2015 14:12:41 -0400 Subject: notes: use a strbuf in add_non_note When we are loading a notes tree into our internal hash table, we also collect any files that are clearly non-notes. We format the name of the file into a PATH_MAX buffer, but unlike true notes (which cannot be larger than a fanned-out sha1 hash), these tree entries can be arbitrarily long, overflowing our buffer. We can fix this by switching to a strbuf. It doesn't even cost us an extra allocation, as we can simply hand ownership of the buffer over to the non-note struct. This is of moderate security interest, as you might fetch notes trees from an untrusted remote. However, we do not do so by default, so you would have to manually fetch into the notes namespace. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- notes.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/notes.c b/notes.c index 5fe691dbcd..6a9cc6295b 100644 --- a/notes.c +++ b/notes.c @@ -362,13 +362,14 @@ static int non_note_cmp(const struct non_note *a, const struct non_note *b) return strcmp(a->path, b->path); } -static void add_non_note(struct notes_tree *t, const char *path, +/* note: takes ownership of path string */ +static void add_non_note(struct notes_tree *t, char *path, unsigned int mode, const unsigned char *sha1) { struct non_note *p = t->prev_non_note, *n; n = (struct non_note *) xmalloc(sizeof(struct non_note)); n->next = NULL; - n->path = xstrdup(path); + n->path = path; n->mode = mode; hashcpy(n->sha1, sha1); t->prev_non_note = n; @@ -482,17 +483,17 @@ handle_non_note: * component. */ { - char non_note_path[PATH_MAX]; - char *p = non_note_path; + struct strbuf non_note_path = STRBUF_INIT; const char *q = sha1_to_hex(subtree->key_sha1); int i; for (i = 0; i < prefix_len; i++) { - *p++ = *q++; - *p++ = *q++; - *p++ = '/'; + strbuf_addch(&non_note_path, *q++); + strbuf_addch(&non_note_path, *q++); + strbuf_addch(&non_note_path, '/'); } - strcpy(p, entry.path); - add_non_note(t, non_note_path, entry.mode, entry.sha1); + strbuf_addstr(&non_note_path, entry.path); + add_non_note(t, strbuf_detach(&non_note_path, NULL), + entry.mode, entry.sha1); } } free(buf); -- cgit v1.2.3 From 5015f01c12a45a1042c1aa6b6f7f6b62bfa00ade Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 19 Aug 2015 14:12:45 -0400 Subject: read_info_alternates: handle paths larger than PATH_MAX This function assumes that the relative_base path passed into it is no larger than PATH_MAX, and writes into a fixed-size buffer. However, this path may not have actually come from the filesystem; for example, add_submodule_odb generates a path using a strbuf and passes it in. This is hard to trigger in practice, though, because the long submodule directory would have to exist on disk before we would try to open its info/alternates file. We can easily avoid the bug, though, by simply creating the filename on the heap. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- sha1_file.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/sha1_file.c b/sha1_file.c index d7f1838c13..b231b62784 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -377,15 +377,12 @@ void read_info_alternates(const char * relative_base, int depth) char *map; size_t mapsz; struct stat st; - const char alt_file_name[] = "info/alternates"; - /* Given that relative_base is no longer than PATH_MAX, - ensure that "path" has enough space to append "/", the - file name, "info/alternates", and a trailing NUL. */ - char path[PATH_MAX + 1 + sizeof alt_file_name]; + char *path; int fd; - sprintf(path, "%s/%s", relative_base, alt_file_name); + path = xstrfmt("%s/info/alternates", relative_base); fd = git_open_noatime(path); + free(path); if (fd < 0) return; if (fstat(fd, &st) || (st.st_size == 0)) { -- cgit v1.2.3 From 78f23bdf68dae56d644892990484951583a64014 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 19 Aug 2015 14:12:48 -0400 Subject: show-branch: use a strbuf for reflog descriptions When we show "branch@{0}", we format into a fixed-size buffer using sprintf. This can overflow if you have long branch names. We can fix it by using a temporary strbuf. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/show-branch.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 270e39c6c1..9e60b12445 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -720,7 +720,6 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) if (reflog) { unsigned char sha1[20]; - char nth_desc[256]; char *ref; int base = 0; unsigned int flags = 0; @@ -759,6 +758,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) for (i = 0; i < reflog; i++) { char *logmsg; + char *nth_desc; const char *msg; unsigned long timestamp; int tz; @@ -777,8 +777,10 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) show_date(timestamp, tz, 1), msg); free(logmsg); - sprintf(nth_desc, "%s@{%d}", *av, base+i); + + nth_desc = xstrfmt("%s@{%d}", *av, base+i); append_ref(nth_desc, sha1, 1); + free(nth_desc); } free(ref); } -- cgit v1.2.3