Skip to content

Commit 7e17ee3

Browse files
Merge pull request #439: sparse index: fix use-after-free bug in cache_tree_verify()
This is a copy of [the v2 patch sent by Philip Wood](https://lore.kernel.org/git/pull.1053.v2.git.1633600244854.gitgitgadget@gmail.com/T/#u). This fixes a possible segfault when a `git rebase --apply` creates a full cache tree in a sparse index. There is a slight change from Philip's patch that makes the test more robust to verbose output.
2 parents 6df9339 + e9317e5 commit 7e17ee3

File tree

2 files changed

+30
-9
lines changed

2 files changed

+30
-9
lines changed

cache-tree.c

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -923,32 +923,48 @@ static void verify_one_sparse(struct repository *r,
923923
path->buf);
924924
}
925925

926-
static void verify_one(struct repository *r,
927-
struct index_state *istate,
928-
struct cache_tree *it,
929-
struct strbuf *path)
926+
/*
927+
* Returns:
928+
* 0 - Verification completed.
929+
* 1 - Restart verification - a call to ensure_full_index() freed the cache
930+
* tree that is being verified and verification needs to be restarted from
931+
* the new toplevel cache tree.
932+
*/
933+
static int verify_one(struct repository *r,
934+
struct index_state *istate,
935+
struct cache_tree *it,
936+
struct strbuf *path)
930937
{
931938
int i, pos, len = path->len;
932939
struct strbuf tree_buf = STRBUF_INIT;
933940
struct object_id new_oid;
934941

935942
for (i = 0; i < it->subtree_nr; i++) {
936943
strbuf_addf(path, "%s/", it->down[i]->name);
937-
verify_one(r, istate, it->down[i]->cache_tree, path);
944+
if (verify_one(r, istate, it->down[i]->cache_tree, path))
945+
return 1;
938946
strbuf_setlen(path, len);
939947
}
940948

941949
if (it->entry_count < 0 ||
942950
/* no verification on tests (t7003) that replace trees */
943951
lookup_replace_object(r, &it->oid) != &it->oid)
944-
return;
952+
return 0;
945953

946954
if (path->len) {
955+
/*
956+
* If the index is sparse and the cache tree is not
957+
* index_name_pos() may trigger ensure_full_index() which will
958+
* free the tree that is being verified.
959+
*/
960+
int is_sparse = istate->sparse_index;
947961
pos = index_name_pos(istate, path->buf, path->len);
962+
if (is_sparse && !istate->sparse_index)
963+
return 1;
948964

949965
if (pos >= 0) {
950966
verify_one_sparse(r, istate, it, path, pos);
951-
return;
967+
return 0;
952968
}
953969

954970
pos = -pos - 1;
@@ -996,6 +1012,7 @@ static void verify_one(struct repository *r,
9961012
oid_to_hex(&new_oid), oid_to_hex(&it->oid));
9971013
strbuf_setlen(path, len);
9981014
strbuf_release(&tree_buf);
1015+
return 0;
9991016
}
10001017

10011018
void cache_tree_verify(struct repository *r, struct index_state *istate)
@@ -1004,6 +1021,10 @@ void cache_tree_verify(struct repository *r, struct index_state *istate)
10041021

10051022
if (!istate->cache_tree)
10061023
return;
1007-
verify_one(r, istate, istate->cache_tree, &path);
1024+
if (verify_one(r, istate, istate->cache_tree, &path)) {
1025+
strbuf_reset(&path);
1026+
if (verify_one(r, istate, istate->cache_tree, &path))
1027+
BUG("ensure_full_index() called twice while verifying cache tree");
1028+
}
10081029
strbuf_release(&path);
10091030
}

t/t1092-sparse-checkout-compatibility.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -890,7 +890,7 @@ test_expect_success 'read-tree --merge with directory-file conflicts' '
890890
test_expect_success 'merge, cherry-pick, and rebase' '
891891
init_repos &&
892892
893-
for OPERATION in "merge -m merge" cherry-pick rebase
893+
for OPERATION in "merge -m merge" cherry-pick "rebase -q --apply" "rebase --merge"
894894
do
895895
test_all_match git checkout -B temp update-deep &&
896896
test_all_match git $OPERATION update-folder1 &&

0 commit comments

Comments
 (0)