CVE-2018-8099 (published 2018-03-14T00:29Z)

Incorrect returning of an error code in the index.c:read_entry() function leads to a double free in libgit2 before v0.26.2, which allows an attacker to cause a denial of service via a crafted repository index file.

Severity: MEDIUM
CVSS v2 base score: 4.3 (AV:N/AC:M/Au:N/C:N/I:N/A:P)
CVSS v3 base score: 6.5, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-415
cwe_name: Double Free
description: The product calls free() twice on the same memory address, potentially leading to modification of unexpected memory locations.
extended_description: When a program calls free() twice with the same argument, the program's memory management data structures become corrupted. This corruption can cause the program to crash or, in some circumstances, cause two later calls to malloc() to return the same pointer. If malloc() returns the same value twice and the program later gives the attacker control over the data that is written into this doubly-allocated memory, the program becomes vulnerable to a buffer overflow attack.
         

Fix information:
repo_url: https://github.com/libgit2/libgit2
commit_details: repo_url: https://github.com/libgit2/libgit2
author: Patrick Steinhardt
author_date: 2018-03-08 11:49:19+00:00
msg: index: convert `read_entry` to return entry size via an out-param

The function `read_entry` does not conform to our usual coding style of
returning stuff via the out parameter and to use the return value for
reporting errors. Due to most of our code conforming to that pattern, it
has become quite natural for us to actually return `-1` in case there is
any error, which has also slipped in with commit 5625d86b9 (index:
support index v4, 2016-05-17). As the function returns an `size_t` only,
though, the return value is wrapped around, causing the caller of
`read_tree` to continue with an invalid index entry. Ultimately, this
can lead to a double-free.

Improve code and fix the bug by converting the function to return the
index entry size via an out parameter and only using the return value to
indicate errors.

Reported-by: Krishna Ram Prakash R <krp@gtux.in>
Reported-by: Vivek Parikh <viv0411.parikh@gmail.com>
repository: repo_url: https://github.com/libgit2/libgit2
repo_name: libgit2/libgit2
description: A cross-platform, linkable library implementation of Git that you can use in your application.
repo_language: C
owner: libgit2
file_changes: filename: index.c
change_type: ModificationType.MODIFY
diff: @@ -2299,8 +2299,9 @@ static size_t index_entry_size(size_t path_len, size_t varint_len, uint32_t flag
 	}
 }
 
-static size_t read_entry(
+static int read_entry(
 	git_index_entry **out,
+	size_t *out_size,
 	git_index *index,
 	const void *buffer,
 	size_t buffer_size,
@@ -2314,7 +2315,7 @@ static size_t read_entry(
 	char *tmp_path = NULL;
 
 	if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size)
-		return 0;
+		return -1;
 
 	/* buffer is not guaranteed to be aligned */
 	memcpy(&source, buffer, sizeof(struct entry_short));
@@ -2356,7 +2357,7 @@ static size_t read_entry(
 
 			path_end = memchr(path_ptr, '\0', buffer_size);
 			if (path_end == NULL)
-				return 0;
+				return -1;
 
 			path_length = path_end - path_ptr;
 		}
@@ -2386,16 +2387,20 @@ static size_t read_entry(
 		entry.path = tmp_path;
 	}
 
+	if (entry_size == 0)
+		return -1;
+
 	if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
-		return 0;
+		return -1;
 
 	if (index_entry_dup(out, index, &entry) < 0) {
 		git__free(tmp_path);
-		return 0;
+		return -1;
 	}
 
 	git__free(tmp_path);
-	return entry_size;
+	*out_size = entry_size;
+	return 0;
 }
 
 static int read_header(struct index_header *dest, const void *buffer)
@@ -2499,10 +2504,9 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
 	/* Parse all the entries */
 	for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
 		git_index_entry *entry = NULL;
-		size_t entry_size = read_entry(&entry, index, buffer, buffer_size, last);
+		size_t entry_size;
 
-		/* 0 bytes read means an object corruption */
-		if (entry_size == 0) {
+		if ((error = read_entry(&entry, &entry_size, index, buffer, buffer_size, last)) < 0) {
 			error = index_error_invalid("invalid entry");
 			goto done;
 		}

programming_language: C