Commit 76182e84531d144aa0edf27012b69cebde2b69f3

Patrick Steinhardt 2019-07-24T18:04:38

config_entries: fix possible segfault when duplicating entries When duplicating a configuration entry, we allocate a new entry but do not verify that we get a valid pointer back. As we're dereferencing the pointer afterwards, we might thus run into a segfault in out-of-memory situations. Extract a new function `git_config_entries_dup_entry` that handles the complete entry duplication. Fix the error by using `GIT_ERROR_CHECK_ALLOC`.

diff --git a/src/config_entries.c b/src/config_entries.c
index 18f8b21..2eb9e66 100644
--- a/src/config_entries.c
+++ b/src/config_entries.c
@@ -67,6 +67,36 @@ int git_config_entries_new(git_config_entries **out)
 	return error;
 }
 
+int git_config_entries_dup_entry(git_config_entries *entries, const git_config_entry *entry)
+{
+	git_config_entry *duplicated;
+	int error;
+
+	duplicated = git__calloc(1, sizeof(git_config_entry));
+	GIT_ERROR_CHECK_ALLOC(duplicated);
+
+	duplicated->name = git__strdup(entry->name);
+	GIT_ERROR_CHECK_ALLOC(duplicated->name);
+
+	if (entry->value) {
+		duplicated->value = git__strdup(entry->value);
+		GIT_ERROR_CHECK_ALLOC(duplicated->value);
+	}
+	duplicated->level = entry->level;
+	duplicated->include_depth = entry->include_depth;
+
+	if ((error = git_config_entries_append(entries, duplicated)) < 0)
+		goto out;
+
+out:
+	if (error && duplicated) {
+		git__free((char *) duplicated->name);
+		git__free((char *) duplicated->value);
+		git__free(duplicated);
+	}
+	return error;
+}
+
 int git_config_entries_dup(git_config_entries **out, git_config_entries *entries)
 {
 	git_config_entries *result = NULL;
@@ -76,22 +106,9 @@ int git_config_entries_dup(git_config_entries **out, git_config_entries *entries
 	if ((error = git_config_entries_new(&result)) < 0)
 		goto out;
 
-	for (head = entries->list; head; head = head->next) {
-		git_config_entry *dup;
-
-		dup = git__calloc(1, sizeof(git_config_entry));
-		dup->name = git__strdup(head->entry->name);
-		GIT_ERROR_CHECK_ALLOC(dup->name);
-		if (head->entry->value) {
-			dup->value = git__strdup(head->entry->value);
-			GIT_ERROR_CHECK_ALLOC(dup->value);
-		}
-		dup->level = head->entry->level;
-		dup->include_depth = head->entry->include_depth;
-
-		if ((error = git_config_entries_append(result, dup)) < 0)
+	for (head = entries->list; head; head = head->next)
+		if ((git_config_entries_dup_entry(result, head->entry)) < 0)
 			goto out;
-	}
 
 	*out = result;
 	result = NULL;
diff --git a/src/config_entries.h b/src/config_entries.h
index 6fdbc41..832379e 100644
--- a/src/config_entries.h
+++ b/src/config_entries.h
@@ -14,6 +14,7 @@ typedef struct git_config_entries git_config_entries;
 
 int git_config_entries_new(git_config_entries **out);
 int git_config_entries_dup(git_config_entries **out, git_config_entries *entries);
+int git_config_entries_dup_entry(git_config_entries *entries, const git_config_entry *entry);
 void git_config_entries_incref(git_config_entries *entries);
 void git_config_entries_free(git_config_entries *entries);
 /* Add or append the new config option */