tree-walk: harden make_traverse_path() length computations
[gitweb.git] / tree-walk.c
index 70f9eb5f1b449cfa59a871caa22010a7d9eae0a3..4f1e9d79ab9aa2a13a4dc63edbaf2b4c613105d3 100644 (file)
@@ -181,25 +181,48 @@ void setup_traverse_info(struct traverse_info *info, const char *base)
                info->prev = &dummy;
 }
 
-char *make_traverse_path(char *path, const struct traverse_info *info,
+char *make_traverse_path(char *path, size_t pathlen,
+                        const struct traverse_info *info,
                         const char *name, size_t namelen)
 {
-       size_t pathlen = info->pathlen;
+       /* Always points to the end of the name we're about to add */
+       size_t pos = st_add(info->pathlen, namelen);
 
-       path[pathlen + namelen] = 0;
+       if (pos >= pathlen)
+               BUG("too small buffer passed to make_traverse_path");
+
+       path[pos] = 0;
        for (;;) {
-               memcpy(path + pathlen, name, namelen);
-               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] = '/';
+               path[--pos] = '/';
+
+               if (!info)
+                       BUG("traverse_info ran out of list items");
                name = info->name;
                namelen = info->namelen;
                info = info->prev;
-               pathlen -= namelen;
        }
        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;
@@ -396,12 +419,10 @@ int traverse_trees(struct index_state *istate,
                tx[i].d = t[i];
 
        if (info->prev) {
-               strbuf_grow(&base, info->pathlen);
-               make_traverse_path(base.buf, info->prev, info->name,
-                                  info->namelen);
-               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, info->pathlen);
        }