summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2013-06-19 15:54:19 -0700
committerRussell Belfer <rb@github.com>2013-06-19 15:54:19 -0700
commit7863523a1be51981bafee9d13b3344fb4ff47347 (patch)
treeff0f851b89ad1e0a72a980f402d0cac0c4d028a6
parentf30fff45a752cb0781067ad48c283e49345a5813 (diff)
downloadlibgit2-7863523a1be51981bafee9d13b3344fb4ff47347.tar.gz
Add tests and fix use of freed memory
This adds some tests for updating the index and having it remove items to make sure that the iteration over the index still works even as earlier items are removed. In testing with valgrind, this found a path that would use the path string from the index entry after it had been freed. The bug fix is simply to copy the path of the index entry before doing any actual index manipulation.
-rw-r--r--src/index.c12
-rw-r--r--tests-clar/index/addall.c20
2 files changed, 29 insertions, 3 deletions
diff --git a/src/index.c b/src/index.c
index e65dc052c..d5568528b 100644
--- a/src/index.c
+++ b/src/index.c
@@ -2176,6 +2176,7 @@ static int index_apply_to_all(
size_t i;
git_pathspec_context ps;
const char *match;
+ git_buf path = GIT_BUF_INIT;
assert(index);
@@ -2205,23 +2206,27 @@ static int index_apply_to_all(
}
}
+ /* index manipulation may alter entry, so don't depend on it */
+ if ((error = git_buf_sets(&path, entry->path)) < 0)
+ break;
+
switch (action) {
case INDEX_ACTION_NONE:
break;
case INDEX_ACTION_UPDATE:
- error = git_index_add_bypath(index, entry->path);
+ error = git_index_add_bypath(index, path.ptr);
if (error == GIT_ENOTFOUND) {
giterr_clear();
- error = git_index_remove_bypath(index, entry->path);
+ error = git_index_remove_bypath(index, path.ptr);
if (!error) /* back up foreach if we removed this */
i--;
}
break;
case INDEX_ACTION_REMOVE:
- if (!(error = git_index_remove_bypath(index, entry->path)))
+ if (!(error = git_index_remove_bypath(index, path.ptr)))
i--; /* back up foreach if we removed this */
break;
default:
@@ -2231,6 +2236,7 @@ static int index_apply_to_all(
}
}
+ git_buf_free(&path);
git_pathspec_context_free(&ps);
return error;
diff --git a/tests-clar/index/addall.c b/tests-clar/index/addall.c
index 33873cb7a..fca6e77fa 100644
--- a/tests-clar/index/addall.c
+++ b/tests-clar/index/addall.c
@@ -132,6 +132,7 @@ static void commit_index_to_head(
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_write_tree(&tree_id, index));
+ cl_git_pass(git_index_write(index)); /* not needed, but might as well */
git_index_free(index);
cl_git_pass(git_tree_lookup(&tree, repo, &tree_id));
@@ -250,5 +251,24 @@ void test_index_addall__repo_lifecycle(void)
cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
check_status(g_repo, 0, 1, 0, 3, 0, 0, 0);
+ strs[0] = "*";
+ cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
+ check_status(g_repo, 3, 1, 0, 0, 0, 0, 0);
+
+ /* must be able to remove at any position while still updating other files */
+ cl_must_pass(p_unlink("addall/.gitignore"));
+ cl_git_rewritefile("addall/file.zzz", "reconstructed file");
+ cl_git_rewritefile("addall/more.zzz", "altered file reality");
+ check_status(g_repo, 3, 1, 0, 1, 1, 1, 0);
+
+ cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
+ check_status(g_repo, 2, 1, 0, 1, 0, 0, 0);
+ /* this behavior actually matches 'git add -u' where "file.zzz" has
+ * been removed from the index, so when you go to update, even though
+ * it exists in the HEAD, it is not re-added to the index, leaving it
+ * as a DELETE when comparing HEAD to index and as an ADD comparing
+ * index to worktree
+ */
+
git_index_free(index);
}