From: Junio C Hamano Date: Thu, 22 Aug 2019 19:34:10 +0000 (-0700) Subject: Merge branch 'jk/tree-walk-overflow' X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/1b01cdbf2e65331879a4668880a191dfac953761?ds=inline;hp=-c Merge branch 'jk/tree-walk-overflow' Codepaths to walk tree objects have been audited for integer overflows and hardened. * jk/tree-walk-overflow: tree-walk: harden make_traverse_path() length computations tree-walk: add a strbuf wrapper for make_traverse_path() tree-walk: accept a raw length for traverse_path_len() tree-walk: use size_t consistently tree-walk: drop oid from traverse_info setup_traverse_info(): stop copying oid --- 1b01cdbf2e65331879a4668880a191dfac953761 diff --combined builtin/merge-tree.c index 97b54caeb9,87d949cf88..e72714a5a8 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@@ -180,8 -180,9 +180,9 @@@ static struct merge_list *create_entry( static char *traverse_path(const struct traverse_info *info, const struct name_entry *n) { - char *path = xmallocz(traverse_path_len(info, n) + the_hash_algo->rawsz); - return make_traverse_path(path, info, n); + struct strbuf buf = STRBUF_INIT; + strbuf_make_traverse_path(&buf, info, n->path, n->pathlen); + return strbuf_detach(&buf, NULL); } static void resolve(const struct traverse_info *info, struct name_entry *ours, struct name_entry *result) @@@ -205,7 -206,6 +206,7 @@@ static void unresolved_directory(const struct traverse_info *info, struct name_entry n[3]) { + struct repository *r = the_repository; char *newbase; struct name_entry *p; struct tree_desc t[3]; @@@ -221,9 -221,9 +222,9 @@@ newbase = traverse_path(info, p); #define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? &(e)->oid : NULL) - buf0 = fill_tree_descriptor(t + 0, ENTRY_OID(n + 0)); - buf1 = fill_tree_descriptor(t + 1, ENTRY_OID(n + 1)); - buf2 = fill_tree_descriptor(t + 2, ENTRY_OID(n + 2)); + buf0 = fill_tree_descriptor(r, t + 0, ENTRY_OID(n + 0)); + buf1 = fill_tree_descriptor(r, t + 1, ENTRY_OID(n + 1)); + buf2 = fill_tree_descriptor(r, t + 2, ENTRY_OID(n + 2)); #undef ENTRY_OID merge_trees(t, newbase); @@@ -352,16 -352,14 +353,16 @@@ static void merge_trees(struct tree_des traverse_trees(&the_index, 3, t, &info); } -static void *get_tree_descriptor(struct tree_desc *desc, const char *rev) +static void *get_tree_descriptor(struct repository *r, + struct tree_desc *desc, + const char *rev) { struct object_id oid; void *buf; - if (get_oid(rev, &oid)) + if (repo_get_oid(r, rev, &oid)) die("unknown rev %s", rev); - buf = fill_tree_descriptor(desc, &oid); + buf = fill_tree_descriptor(r, desc, &oid); if (!buf) die("%s is not a tree", rev); return buf; @@@ -369,16 -367,15 +370,16 @@@ int cmd_merge_tree(int argc, const char **argv, const char *prefix) { + struct repository *r = the_repository; struct tree_desc t[3]; void *buf1, *buf2, *buf3; if (argc != 4) usage(merge_tree_usage); - buf1 = get_tree_descriptor(t+0, argv[1]); - buf2 = get_tree_descriptor(t+1, argv[2]); - buf3 = get_tree_descriptor(t+2, argv[3]); + buf1 = get_tree_descriptor(r, t+0, argv[1]); + buf2 = get_tree_descriptor(r, t+1, argv[2]); + buf3 = get_tree_descriptor(r, t+2, argv[3]); merge_trees(t, ""); free(buf1); free(buf2); diff --combined cache-tree.c index 706ffcf188,badf5669f1..c22161f987 --- a/cache-tree.c +++ b/cache-tree.c @@@ -6,8 -6,8 +6,8 @@@ #include "object-store.h" #include "replace-object.h" -#ifndef DEBUG -#define DEBUG 0 +#ifndef DEBUG_CACHE_TREE +#define DEBUG_CACHE_TREE 0 #endif struct cache_tree *cache_tree(void) @@@ -111,7 -111,7 +111,7 @@@ static int do_invalidate_path(struct ca int namelen; struct cache_tree_sub *down; -#if DEBUG +#if DEBUG_CACHE_TREE fprintf(stderr, "cache-tree invalidate <%s>\n", path); #endif @@@ -398,7 -398,7 +398,7 @@@ static int update_one(struct cache_tre strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0'); strbuf_add(&buffer, oid->hash, the_hash_algo->rawsz); -#if DEBUG +#if DEBUG_CACHE_TREE fprintf(stderr, "cache-tree update-one %o %.*s\n", mode, entlen, path + baselen); #endif @@@ -421,7 -421,7 +421,7 @@@ strbuf_release(&buffer); it->entry_count = to_invalidate ? -1 : i - *skip_count; -#if DEBUG +#if DEBUG_CACHE_TREE fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n", it->entry_count, it->subtree_nr, oid_to_hex(&it->oid)); @@@ -462,7 -462,7 +462,7 @@@ static void write_one(struct strbuf *bu strbuf_add(buffer, path, pathlen); strbuf_addf(buffer, "%c%d %d\n", 0, it->entry_count, it->subtree_nr); -#if DEBUG +#if DEBUG_CACHE_TREE if (0 <= it->entry_count) fprintf(stderr, "cache-tree <%.*s> (%d ent, %d subtree) %s\n", pathlen, path, it->entry_count, it->subtree_nr, @@@ -536,7 -536,7 +536,7 @@@ static struct cache_tree *read_one(cons size -= rawsz; } -#if DEBUG +#if DEBUG_CACHE_TREE if (0 <= it->entry_count) fprintf(stderr, "cache-tree <%s> (%d ent, %d subtree) %s\n", *buffer, it->entry_count, subtree_nr, @@@ -713,7 -713,7 +713,7 @@@ static struct cache_tree *find_cache_tr if (!info->prev) return root; our_parent = find_cache_tree_from_traversal(root, info->prev); - return cache_tree_find(our_parent, info->name.path); + return cache_tree_find(our_parent, info->name); } int cache_tree_matches_traversal(struct cache_tree *root, diff --combined tree-walk.c index c20b62f49e,4f1e9d79ab..bea819d826 --- a/tree-walk.c +++ b/tree-walk.c @@@ -81,15 -81,13 +81,15 @@@ int init_tree_desc_gently(struct tree_d return result; } -void *fill_tree_descriptor(struct tree_desc *desc, const struct object_id *oid) +void *fill_tree_descriptor(struct repository *r, + struct tree_desc *desc, + const struct object_id *oid) { unsigned long size = 0; void *buf = NULL; if (oid) { - buf = read_object_with_reference(oid, tree_type, &size, NULL); + buf = read_object_with_reference(r, oid, tree_type, &size, NULL); if (!buf) die("unable to read tree %s", oid_to_hex(oid)); } @@@ -170,40 -168,61 +170,61 @@@ int tree_entry_gently(struct tree_desc void setup_traverse_info(struct traverse_info *info, const char *base) { - int pathlen = strlen(base); + size_t pathlen = strlen(base); static struct traverse_info dummy; memset(info, 0, sizeof(*info)); if (pathlen && base[pathlen-1] == '/') pathlen--; info->pathlen = pathlen ? pathlen + 1 : 0; - info->name.path = base; - info->name.pathlen = pathlen; - if (pathlen) { - hashcpy(info->name.oid.hash, (const unsigned char *)base + pathlen + 1); + info->name = base; + info->namelen = pathlen; + if (pathlen) info->prev = &dummy; - } } - char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n) + char *make_traverse_path(char *path, size_t pathlen, + const struct traverse_info *info, + const char *name, size_t namelen) { - int len = tree_entry_len(n); - int pathlen = info->pathlen; + /* Always points to the end of the name we're about to add */ + size_t pos = st_add(info->pathlen, namelen); + + if (pos >= pathlen) + BUG("too small buffer passed to make_traverse_path"); - path[pathlen + len] = 0; + path[pos] = 0; for (;;) { - memcpy(path + pathlen, n->path, len); - if (!pathlen) + if (pos < namelen) + BUG("traverse_info pathlen does not match strings"); + pos -= namelen; + memcpy(path + pos, name, namelen); + + if (!pos) break; - path[--pathlen] = '/'; - n = &info->name; - len = tree_entry_len(n); + path[--pos] = '/'; + + if (!info) + BUG("traverse_info ran out of list items"); + name = info->name; + namelen = info->namelen; info = info->prev; - pathlen -= len; } return path; } + void strbuf_make_traverse_path(struct strbuf *out, + const struct traverse_info *info, + const char *name, size_t namelen) + { + size_t len = traverse_path_len(info, namelen); + + strbuf_grow(out, len); + make_traverse_path(out->buf + out->len, out->alloc - out->len, + info, name, namelen); + strbuf_setlen(out, out->len + len); + } + struct tree_desc_skip { struct tree_desc_skip *prev; const void *ptr; @@@ -400,13 -419,12 +421,12 @@@ int traverse_trees(struct index_state * tx[i].d = t[i]; if (info->prev) { - strbuf_grow(&base, info->pathlen); - make_traverse_path(base.buf, info->prev, &info->name); - base.buf[info->pathlen-1] = '/'; - strbuf_setlen(&base, info->pathlen); - traverse_path = xstrndup(base.buf, info->pathlen); + strbuf_make_traverse_path(&base, info->prev, + info->name, info->namelen); + strbuf_addch(&base, '/'); + traverse_path = xstrndup(base.buf, base.len); } else { - traverse_path = xstrndup(info->name.path, info->pathlen); + traverse_path = xstrndup(info->name, info->pathlen); } info->traverse_path = traverse_path; for (;;) { @@@ -502,9 -520,7 +522,9 @@@ struct dir_state struct object_id oid; }; -static int find_tree_entry(struct tree_desc *t, const char *name, struct object_id *result, unsigned short *mode) +static int find_tree_entry(struct repository *r, struct tree_desc *t, + const char *name, struct object_id *result, + unsigned short *mode) { int namelen = strlen(name); while (t->size) { @@@ -534,23 -550,19 +554,23 @@@ oidcpy(result, &oid); return 0; } - return get_tree_entry(&oid, name + entrylen, result, mode); + return get_tree_entry(r, &oid, name + entrylen, result, mode); } return -1; } -int get_tree_entry(const struct object_id *tree_oid, const char *name, struct object_id *oid, unsigned short *mode) +int get_tree_entry(struct repository *r, + const struct object_id *tree_oid, + const char *name, + struct object_id *oid, + unsigned short *mode) { int retval; void *tree; unsigned long size; struct object_id root; - tree = read_object_with_reference(tree_oid, tree_type, &size, &root); + tree = read_object_with_reference(r, tree_oid, tree_type, &size, &root); if (!tree) return -1; @@@ -565,7 -577,7 +585,7 @@@ } else { struct tree_desc t; init_tree_desc(&t, tree, size); - retval = find_tree_entry(&t, name, oid, mode); + retval = find_tree_entry(r, &t, name, oid, mode); } free(tree); return retval; @@@ -593,10 -605,7 +613,10 @@@ * See the code for enum get_oid_result for a description of * the return values. */ -enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned short *mode) +enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r, + struct object_id *tree_oid, const char *name, + struct object_id *result, struct strbuf *result_path, + unsigned short *mode) { int retval = MISSING_OBJECT; struct dir_state *parents = NULL; @@@ -620,8 -629,7 +640,8 @@@ void *tree; struct object_id root; unsigned long size; - tree = read_object_with_reference(¤t_tree_oid, + tree = read_object_with_reference(r, + ¤t_tree_oid, tree_type, &size, &root); if (!tree) @@@ -690,7 -698,7 +710,7 @@@ } /* Look up the first (or only) path component in the tree. */ - find_result = find_tree_entry(&t, namebuf.buf, + find_result = find_tree_entry(r, &t, namebuf.buf, ¤t_tree_oid, mode); if (find_result) { goto done; @@@ -734,8 -742,7 +754,8 @@@ */ retval = DANGLING_SYMLINK; - contents = read_object_file(¤t_tree_oid, &type, + contents = repo_read_object_file(r, + ¤t_tree_oid, &type, &link_len); if (!contents) diff --combined tree-walk.h index 2a5db29e8f,a3ad54e6ce..abe2caf4e0 --- a/tree-walk.h +++ b/tree-walk.h @@@ -45,21 -45,22 +45,24 @@@ int init_tree_desc_gently(struct tree_d int tree_entry(struct tree_desc *, struct name_entry *); int tree_entry_gently(struct tree_desc *, struct name_entry *); -void *fill_tree_descriptor(struct tree_desc *desc, const struct object_id *oid); +void *fill_tree_descriptor(struct repository *r, + struct tree_desc *desc, + const struct object_id *oid); struct traverse_info; typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *); int traverse_trees(struct index_state *istate, int n, struct tree_desc *t, struct traverse_info *info); -enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned short *mode); +enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r, struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned short *mode); struct traverse_info { const char *traverse_path; struct traverse_info *prev; - struct name_entry name; - int pathlen; + const char *name; + size_t namelen; + unsigned mode; + + size_t pathlen; struct pathspec *pathspec; unsigned long df_conflicts; @@@ -68,13 -69,18 +71,18 @@@ int show_all_errors; }; -int get_tree_entry(const struct object_id *, const char *, struct object_id *, unsigned short *); +int get_tree_entry(struct repository *, const struct object_id *, const char *, struct object_id *, unsigned short *); - char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n); + char *make_traverse_path(char *path, size_t pathlen, const struct traverse_info *info, + const char *name, size_t namelen); + void strbuf_make_traverse_path(struct strbuf *out, + const struct traverse_info *info, + const char *name, size_t namelen); void setup_traverse_info(struct traverse_info *info, const char *base); - static inline int traverse_path_len(const struct traverse_info *info, const struct name_entry *n) + static inline size_t traverse_path_len(const struct traverse_info *info, + size_t namelen) { - return info->pathlen + tree_entry_len(n); + return st_add(info->pathlen, namelen); } /* in general, positive means "kind of interesting" */ diff --combined unpack-trees.c index 62276d4fef,65c4677578..50f257bd5c --- a/unpack-trees.c +++ b/unpack-trees.c @@@ -315,7 -315,7 +315,7 @@@ static struct progress *get_progress(st total++; } - return start_delayed_progress(_("Checking out files"), total); + return start_delayed_progress(_("Updating files"), total); } static void setup_collided_checkout_detection(struct checkout *state, @@@ -632,7 -632,7 +632,7 @@@ static int unpack_index_entry(struct ca return ret; } - static int find_cache_pos(struct traverse_info *, const struct name_entry *); + static int find_cache_pos(struct traverse_info *, const char *p, size_t len); static void restore_cache_bottom(struct traverse_info *info, int bottom) { @@@ -651,7 -651,7 +651,7 @@@ static int switch_cache_bottom(struct t if (o->diff_index_cached) return 0; ret = o->cache_bottom; - pos = find_cache_pos(info->prev, &info->name); + pos = find_cache_pos(info->prev, info->name, info->namelen); if (pos < -1) o->cache_bottom = -2 - pos; @@@ -686,21 -686,19 +686,19 @@@ static int index_pos_by_traverse_info(s struct traverse_info *info) { struct unpack_trees_options *o = info->data; - int len = traverse_path_len(info, names); - char *name = xmalloc(len + 1 /* slash */ + 1 /* NUL */); + struct strbuf name = STRBUF_INIT; int pos; - make_traverse_path(name, info, names); - name[len++] = '/'; - name[len] = '\0'; - pos = index_name_pos(o->src_index, name, len); + strbuf_make_traverse_path(&name, info, names->path, names->pathlen); + strbuf_addch(&name, '/'); + pos = index_name_pos(o->src_index, name.buf, name.len); if (pos >= 0) BUG("This is a directory and should not exist in index"); pos = -pos - 1; - if (!starts_with(o->src_index->cache[pos]->name, name) || - (pos > 0 && starts_with(o->src_index->cache[pos-1]->name, name))) + if (!starts_with(o->src_index->cache[pos]->name, name.buf) || + (pos > 0 && starts_with(o->src_index->cache[pos-1]->name, name.buf))) BUG("pos must point at the first entry in this directory"); - free(name); + strbuf_release(&name); return pos; } @@@ -811,8 -809,10 +809,10 @@@ static int traverse_trees_recursive(in newinfo = *info; newinfo.prev = info; newinfo.pathspec = info->pathspec; - newinfo.name = *p; - newinfo.pathlen += tree_entry_len(p) + 1; + newinfo.name = p->path; + newinfo.namelen = p->pathlen; + newinfo.mode = p->mode; + newinfo.pathlen = st_add3(newinfo.pathlen, tree_entry_len(p), 1); newinfo.df_conflicts |= df_conflicts; /* @@@ -840,7 -840,7 +840,7 @@@ const struct object_id *oid = NULL; if (dirmask & 1) oid = &names[i].oid; - buf[nr_buf++] = fill_tree_descriptor(t + i, oid); + buf[nr_buf++] = fill_tree_descriptor(the_repository, t + i, oid); } } @@@ -863,14 -863,18 +863,18 @@@ * itself - the caller needs to do the final check for the cache * entry having more data at the end! */ - static int do_compare_entry_piecewise(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n) + static int do_compare_entry_piecewise(const struct cache_entry *ce, + const struct traverse_info *info, + const char *name, size_t namelen, + unsigned mode) { - int len, pathlen, ce_len; + int pathlen, ce_len; const char *ce_name; if (info->prev) { int cmp = do_compare_entry_piecewise(ce, info->prev, - &info->name); + info->name, info->namelen, + info->mode); if (cmp) return cmp; } @@@ -884,15 -888,15 +888,15 @@@ ce_len -= pathlen; ce_name = ce->name + pathlen; - len = tree_entry_len(n); - return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode); + return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode); } static int do_compare_entry(const struct cache_entry *ce, const struct traverse_info *info, - const struct name_entry *n) + const char *name, size_t namelen, + unsigned mode) { - int len, pathlen, ce_len; + int pathlen, ce_len; const char *ce_name; int cmp; @@@ -902,7 -906,7 +906,7 @@@ * it is quicker to use the precomputed version. */ if (!info->traverse_path) - return do_compare_entry_piecewise(ce, info, n); + return do_compare_entry_piecewise(ce, info, name, namelen, mode); cmp = strncmp(ce->name, info->traverse_path, info->pathlen); if (cmp) @@@ -917,13 -921,12 +921,12 @@@ ce_len -= pathlen; ce_name = ce->name + pathlen; - len = tree_entry_len(n); - return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode); + return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode); } static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n) { - int cmp = do_compare_entry(ce, info, n); + int cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode); if (cmp) return cmp; @@@ -931,7 -934,7 +934,7 @@@ * Even if the beginning compared identically, the ce should * compare as bigger than a directory leading up to it! */ - return ce_namelen(ce) > traverse_path_len(info, n); + return ce_namelen(ce) > traverse_path_len(info, tree_entry_len(n)); } static int ce_in_traverse_path(const struct cache_entry *ce, @@@ -939,7 -942,8 +942,8 @@@ { if (!info->prev) return 1; - if (do_compare_entry(ce, info->prev, &info->name)) + if (do_compare_entry(ce, info->prev, + info->name, info->namelen, info->mode)) return 0; /* * If ce (blob) is the same name as the path (which is a tree @@@ -954,7 -958,7 +958,7 @@@ static struct cache_entry *create_ce_en struct index_state *istate, int is_transient) { - int len = traverse_path_len(info, n); + size_t len = traverse_path_len(info, tree_entry_len(n)); struct cache_entry *ce = is_transient ? make_empty_transient_cache_entry(len) : @@@ -964,7 -968,8 +968,8 @@@ ce->ce_flags = create_ce_flags(stage); ce->ce_namelen = len; oidcpy(&ce->oid, &n->oid); - make_traverse_path(ce->name, info, n); + /* len+1 because the cache_entry allocates space for NUL */ + make_traverse_path(ce->name, len + 1, info, n->path, n->pathlen); return ce; } @@@ -1057,13 -1062,12 +1062,12 @@@ static int unpack_failed(struct unpack_ * the directory. */ static int find_cache_pos(struct traverse_info *info, - const struct name_entry *p) + const char *p, size_t p_len) { int pos; struct unpack_trees_options *o = info->data; struct index_state *index = o->src_index; int pfxlen = info->pathlen; - int p_len = tree_entry_len(p); for (pos = o->cache_bottom; pos < index->cache_nr; pos++) { const struct cache_entry *ce = index->cache[pos]; @@@ -1099,7 -1103,7 +1103,7 @@@ ce_len = ce_slash - ce_name; else ce_len = ce_namelen(ce) - pfxlen; - cmp = name_compare(p->path, p_len, ce_name, ce_len); + cmp = name_compare(p, p_len, ce_name, ce_len); /* * Exact match; if we have a directory we need to * delay returning it. @@@ -1114,7 -1118,7 +1118,7 @@@ * E.g. ce_name == "t-i", and p->path == "t"; we may * have "t/a" in the index. */ - if (p_len < ce_len && !memcmp(ce_name, p->path, p_len) && + if (p_len < ce_len && !memcmp(ce_name, p, p_len) && ce_name[p_len] < '/') continue; /* keep looking */ break; @@@ -1125,7 -1129,7 +1129,7 @@@ static struct cache_entry *find_cache_entry(struct traverse_info *info, const struct name_entry *p) { - int pos = find_cache_pos(info, p); + int pos = find_cache_pos(info, p->path, p->pathlen); struct unpack_trees_options *o = info->data; if (0 <= pos) @@@ -1138,10 -1142,10 +1142,10 @@@ static void debug_path(struct traverse_ { if (info->prev) { debug_path(info->prev); - if (*info->prev->name.path) + if (*info->prev->name) putchar('/'); } - printf("%s", info->name.path); + printf("%s", info->name); } static void debug_name_entry(int i, struct name_entry *n)