Commit e4def81aab5df3ce1bd8622f3c89224d5910a97b

Vicent Marti 2010-10-08T13:52:17

Fix issue 3 (memory corruption resize_tree_array) The tree array wasn't being initialized when instantiating a tree object in memory instead of loading it from disk. New unit tests added to check for the problem. Signed-off-by: Vicent Marti <tanoku@gmail.com>

diff --git a/src/git/tree.h b/src/git/tree.h
index bec49f2..9e2d077 100644
--- a/src/git/tree.h
+++ b/src/git/tree.h
@@ -113,8 +113,9 @@ GIT_EXTERN(git_object *) git_tree_entry_2object(git_tree_entry *entry);
  * @iparam id OID for the tree entry
  * @param filename Filename for the tree entry
  * @param attributes UNIX file attributes for the entry
+ * @return 0 on success; otherwise error code
  */
-GIT_EXTERN(void) git_tree_add_entry(git_tree *tree, const git_oid *id, const char *filename, int attributes);
+GIT_EXTERN(int) git_tree_add_entry(git_tree *tree, const git_oid *id, const char *filename, int attributes);
 
 /**
  * Remove an entry by its index.
diff --git a/src/tree.c b/src/tree.c
index b809290..28bc035 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -29,17 +29,24 @@
 #include "tree.h"
 #include "git/repository.h"
 
-static void resize_tree_array(git_tree *tree)
+static int resize_tree_array(git_tree *tree)
 {
 	git_tree_entry **new_entries;
 
-	tree->array_size = tree->array_size * 2;
+	tree->array_size *= 2; 
+	if (tree->array_size == 0)
+		tree->array_size = 8;
 
 	new_entries = git__malloc(tree->array_size * sizeof(git_tree_entry *));
+	if (new_entries == NULL)
+		return GIT_ENOMEM;
+
 	memcpy(new_entries, tree->entries, tree->entry_count * sizeof(git_tree_entry *));
 
 	free(tree->entries);
 	tree->entries = new_entries;
+
+	return GIT_SUCCESS;
 }
 
 int entry_cmp(const void *key, const void *array_member)
@@ -166,17 +173,18 @@ size_t git_tree_entrycount(git_tree *tree)
 	return tree->entry_count;
 }
 
-void git_tree_add_entry(git_tree *tree, const git_oid *id, const char *filename, int attributes)
+int git_tree_add_entry(git_tree *tree, const git_oid *id, const char *filename, int attributes)
 {
 	git_tree_entry *entry;
 
 	assert(tree && id && filename);
 
 	if (tree->entry_count >= tree->array_size)
-		resize_tree_array(tree);
+		if (resize_tree_array(tree) < 0)
+			return GIT_ENOMEM;
 
 	if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL)
-		return;
+		return GIT_ENOMEM;
 
 	memset(entry, 0x0, sizeof(git_tree_entry));
 
@@ -189,6 +197,7 @@ void git_tree_add_entry(git_tree *tree, const git_oid *id, const char *filename,
 	entry_resort(tree);
 
 	tree->object.modified = 1;
+	return GIT_SUCCESS;
 }
 
 int git_tree_remove_entry_byindex(git_tree *tree, int idx)
@@ -277,11 +286,15 @@ int git_tree__parse(git_tree *tree)
 	tree->array_size = (tree->object.source.raw.len / avg_entry_size) + 1;
 	tree->entries = git__malloc(tree->array_size * sizeof(git_tree_entry *));
 
+	if (tree->entries == NULL)
+		return GIT_ENOMEM;
+
 	while (buffer < buffer_end) {
 		git_tree_entry *entry;
 
 		if (tree->entry_count >= tree->array_size)
-			resize_tree_array(tree);
+			if (resize_tree_array(tree) < 0)
+				return GIT_ENOMEM;
 
 		entry = git__malloc(sizeof(git_tree_entry));
 		if (entry == NULL) {
diff --git a/tests/t0902-modify.c b/tests/t0902-modify.c
index a321165..60a5cd7 100644
--- a/tests/t0902-modify.c
+++ b/tests/t0902-modify.c
@@ -9,6 +9,39 @@
 static const char *odb_dir = "../resources/sample-odb";
 static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";
 
+BEGIN_TEST(tree_in_memory_add_test)
+	const unsigned int entry_count = 128;
+
+
+	git_odb *db;
+	git_repository *repo;
+	git_tree *tree;
+	unsigned int i;
+	git_oid entry_id;
+
+	must_pass(git_odb_open(&db, odb_dir));
+
+	repo = git_repository_alloc(db);
+	must_be_true(repo != NULL);
+
+	tree = git_tree_new(repo);
+	must_be_true(tree != NULL);
+
+	git_oid_mkstr(&entry_id, tree_oid);
+	for (i = 0; i < entry_count; ++i) {
+		char filename[32];
+		sprintf(filename, "file%d.txt", i);
+		must_pass(git_tree_add_entry(tree, &entry_id, filename, 040000));
+	}
+
+	must_be_true(git_tree_entrycount(tree) == entry_count);
+	must_pass(git_object_write((git_object *)tree));
+	must_pass(remove_loose_object(odb_dir, (git_object *)tree));
+
+	git_repository_free(repo);
+	git_odb_close(db);
+END_TEST
+
 BEGIN_TEST(tree_add_entry_test)
 	git_odb *db;
 	git_oid id;