]> git.ipfire.org Git - thirdparty/git.git/blobdiff - cache-tree.c
sparse index: fix use-after-free bug in cache_tree_verify()
[thirdparty/git.git] / cache-tree.c
index 90919f9e3454791e2132236a2319b5f51c9c1d41..8044e21bcf312a73d6404ee7706ebc141b75dffa 100644 (file)
@@ -826,10 +826,17 @@ static void verify_one_sparse(struct repository *r,
                    path->buf);
 }
 
-static void verify_one(struct repository *r,
-                      struct index_state *istate,
-                      struct cache_tree *it,
-                      struct strbuf *path)
+/*
+ * Returns:
+ *  0 - Verification completed.
+ *  1 - Restart verification - a call to ensure_full_index() freed the cache
+ *      tree that is being verified and verification needs to be restarted from
+ *      the new toplevel cache tree.
+ */
+static int verify_one(struct repository *r,
+                     struct index_state *istate,
+                     struct cache_tree *it,
+                     struct strbuf *path)
 {
        int i, pos, len = path->len;
        struct strbuf tree_buf = STRBUF_INIT;
@@ -837,21 +844,30 @@ static void verify_one(struct repository *r,
 
        for (i = 0; i < it->subtree_nr; i++) {
                strbuf_addf(path, "%s/", it->down[i]->name);
-               verify_one(r, istate, it->down[i]->cache_tree, path);
+               if (verify_one(r, istate, it->down[i]->cache_tree, path))
+                       return 1;
                strbuf_setlen(path, len);
        }
 
        if (it->entry_count < 0 ||
            /* no verification on tests (t7003) that replace trees */
            lookup_replace_object(r, &it->oid) != &it->oid)
-               return;
+               return 0;
 
        if (path->len) {
+               /*
+                * If the index is sparse and the cache tree is not
+                * index_name_pos() may trigger ensure_full_index() which will
+                * free the tree that is being verified.
+                */
+               int is_sparse = istate->sparse_index;
                pos = index_name_pos(istate, path->buf, path->len);
+               if (is_sparse && !istate->sparse_index)
+                       return 1;
 
                if (pos >= 0) {
                        verify_one_sparse(r, istate, it, path, pos);
-                       return;
+                       return 0;
                }
 
                pos = -pos - 1;
@@ -899,6 +915,7 @@ static void verify_one(struct repository *r,
                    oid_to_hex(&new_oid), oid_to_hex(&it->oid));
        strbuf_setlen(path, len);
        strbuf_release(&tree_buf);
+       return 0;
 }
 
 void cache_tree_verify(struct repository *r, struct index_state *istate)
@@ -907,6 +924,10 @@ void cache_tree_verify(struct repository *r, struct index_state *istate)
 
        if (!istate->cache_tree)
                return;
-       verify_one(r, istate, istate->cache_tree, &path);
+       if (verify_one(r, istate, istate->cache_tree, &path)) {
+               strbuf_reset(&path);
+               if (verify_one(r, istate, istate->cache_tree, &path))
+                       BUG("ensure_full_index() called twice while verifying cache tree");
+       }
        strbuf_release(&path);
 }