CVE-2016-8689: Out-of-bounds Read (CWE-125)
value: The read_Header function in archive_read_support_format_7zip.c in libarchive 3.2.1 allows remote attackers to cause a denial of service (out-of-bounds read) via multiple EmptyStream attributes in a header in a 7zip archive.
CWE description: The product reads data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 7.5
Published: 2017-02-15T19:59Z
Repository: https://github.com/libarchive/libarchive
Language: C
Files changed: libarchive/archive_read_support_format_7zip.c
Commit date: 2016-09-18 18:14:58 -0700
Commit message: Issue 761:  Heap overflow reading corrupted 7Zip files

The sample file that demonstrated this had multiple 'EmptyStream'
attributes.  The first one ended up being used to calculate
certain statistics, then was overwritten by the second which
was incompatible with those statistics.

The fix here is to reject any header with multiple EmptyStream
attributes.  While here, also reject headers with multiple
EmptyFile, AntiFile, Name, or Attributes markers.

Diff: diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c
index 1dfe52b85..c0a536c4a 100644
--- a/libarchive/archive_read_support_format_7zip.c
+++ b/libarchive/archive_read_support_format_7zip.c
@@ -2429,10 +2429,12 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
 			return (-1);
 		ll = (size_t)size;
 
 		switch (type) {
 		case kEmptyStream:
+			if (h->emptyStreamBools != NULL)
+				return (-1);
 			h->emptyStreamBools = calloc((size_t)zip->numFiles,
 			    sizeof(*h->emptyStreamBools));
 			if (h->emptyStreamBools == NULL)
 				return (-1);
 			if (read_Bools(
@@ -2449,10 +2451,12 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
 				/* Unexcepted sequence. Skip this. */
 				if (header_bytes(a, ll) == NULL)
 					return (-1);
 				break;
 			}
+			if (h->emptyFileBools != NULL)
+				return (-1);
 			h->emptyFileBools = calloc(empty_streams,
 			    sizeof(*h->emptyFileBools));
 			if (h->emptyFileBools == NULL)
 				return (-1);
 			if (read_Bools(a, h->emptyFileBools, empty_streams) < 0)
@@ -2463,10 +2467,12 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
 				/* Unexcepted sequence. Skip this. */
 				if (header_bytes(a, ll) == NULL)
 					return (-1);
 				break;
 			}
+			if (h->antiBools != NULL)
+				return (-1);
 			h->antiBools = calloc(empty_streams,
 			    sizeof(*h->antiBools));
 			if (h->antiBools == NULL)
 				return (-1);
 			if (read_Bools(a, h->antiBools, empty_streams) < 0)
@@ -2489,10 +2495,12 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
 			ll--;
 
 			if ((ll & 1) || ll < zip->numFiles * 4)
 				return (-1);
 
+			if (zip->entry_names != NULL)
+				return (-1);
 			zip->entry_names = malloc(ll);
 			if (zip->entry_names == NULL)
 				return (-1);
 			np = zip->entry_names;
 			nb = ll;
@@ -2541,10 +2549,12 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
 			int allAreDefined;
 
 			if ((p = header_bytes(a, 2)) == NULL)
 				return (-1);
 			allAreDefined = *p;
+			if (h->attrBools != NULL)
+				return (-1);
 			h->attrBools = calloc((size_t)zip->numFiles,
 			    sizeof(*h->attrBools));
 			if (h->attrBools == NULL)
 				return (-1);
 			if (allAreDefined)

Fixed code: 			if (h->emptyStreamBools != NULL)
				return (-1);
			if (h->emptyFileBools != NULL)
				return (-1);
			if (h->antiBools != NULL)
				return (-1);
			if (zip->entry_names != NULL)
				return (-1);
			if (h->attrBools != NULL)
				return (-1);