CVE-2019-9278: Out-of-bounds Write (CWE-787)
value: In libexif, there is a possible out of bounds write due to an integer overflow. This could lead to remote escalation of privilege in the media content provider with no additional execution privileges needed. User interaction is needed for exploitation. Product: AndroidVersions: Android-10Android ID: A-112537774
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 8.8
Published: 2019-09-27T19:15Z
Repository: https://github.com/libexif/libexif
Language: C
Files changed: libexif/exif-data.c
Commit date: 2020-01-18 09:31:35 +0100
Commit message: fix CVE-2019-9278

avoid the use of unsafe integer overflow checking constructs (unsigned integer operations cannot overflow, so "u1 + u2 > u1" can be optimized away)

check for the actual sizes, which should also handle the overflows
document other places google patched, but do not seem relevant due to other restrictions

fixes https://github.com/libexif/libexif/issues/26

Diff: diff --git a/libexif/exif-data.c b/libexif/exif-data.c
index a6f9c94..6332cd1 100644
--- a/libexif/exif-data.c
+++ b/libexif/exif-data.c
@@ -190,13 +190,19 @@ exif_data_load_data_entry (ExifData *data, ExifEntry *entry,
 		doff = exif_get_long (d + offset + 8, data->priv->order);
 	else
 		doff = offset + 8;
 
 	/* Sanity checks */
-	if ((doff + s < doff) || (doff + s < s) || (doff + s > size)) {
+	if (doff >= size) {
 		exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData",
-				  "Tag data past end of buffer (%u > %u)", doff+s, size);	
+				  "Tag starts past end of buffer (%u > %u)", doff, size);
+		return 0;
+	}
+
+	if (s > size - doff) {
+		exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData",
+				  "Tag data goes past end of buffer (%u > %u)", doff+s, size);
 		return 0;
 	}
 
 	entry->data = exif_data_alloc (data, s);
 	if (entry->data) {
@@ -313,17 +319,18 @@ exif_data_save_data_entry (ExifData *data, ExifEntry *e,
 static void
 exif_data_load_data_thumbnail (ExifData *data, const unsigned char *d,
 			       unsigned int ds, ExifLong o, ExifLong s)
 {
 	/* Sanity checks */
-	if ((o + s < o) || (o + s < s) || (o + s > ds) || (o > ds)) {
-		exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData",
-			  "Bogus thumbnail offset (%u) or size (%u).",
-			  o, s);
+	if (o >= ds) {
+		exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "Bogus thumbnail offset (%u).", o);
+		return;
+	}
+	if (s > ds - o) {
+		exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "Bogus thumbnail size (%u), max would be %u.", s, ds-o);
 		return;
 	}
-
 	if (data->data) 
 		exif_mem_free (data->priv->mem, data->data);
 	if (!(data->data = exif_data_alloc (data, s))) {
 		EXIF_LOG_NO_MEMORY (data->priv->log, "ExifData", s);
 		data->size = 0;
@@ -945,29 +952,30 @@ exif_data_load_data (ExifData *data, const unsigned char *d_orig,
 	/* IFD 0 offset */
 	offset = exif_get_long (d + 10, data->priv->order);
 	exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", 
 		  "IFD 0 at %i.", (int) offset);
 
-	/* Sanity check the offset, being careful about overflow */
+	/* ds is restricted to 16 bit above, so offset is restricted too, and offset+8 should not overflow. */
 	if (offset > ds || offset + 6 + 2 > ds)
 		return;
 
 	/* Parse the actual exif data (usually offset 14 from start) */
 	exif_data_load_data_content (data, EXIF_IFD_0, d + 6, ds - 6, offset, 0);
 
 	/* IFD 1 offset */
 	n = exif_get_short (d + 6 + offset, data->priv->order);
+	/* offset < 2<<16, n is 16 bit at most, so this op will not overflow */
 	if (offset + 6 + 2 + 12 * n + 4 > ds)
 		return;
 
 	offset = exif_get_long (d + 6 + offset + 2 + 12 * n, data->priv->order);
 	if (offset) {
 		exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData",
 			  "IFD 1 at %i.", (int) offset);
 
-		/* Sanity check. */
-		if (offset > ds || offset + 6 > ds) {
+		/* Sanity check. ds is ensured to be above 6 above, offset is 16bit */
+		if (offset > ds - 6) {
 			exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA,
 				  "ExifData", "Bogus offset of IFD1.");
 		} else {
 		   exif_data_load_data_content (data, EXIF_IFD_1, d + 6, ds - 6, offset, 0);
 		}

Vulnerable code: 	if ((doff + s < doff) || (doff + s < s) || (doff + s > size)) {
				  "Tag data past end of buffer (%u > %u)", doff+s, size);	
	if ((o + s < o) || (o + s < s) || (o + s > ds) || (o > ds)) {
		exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData",
			  "Bogus thumbnail offset (%u) or size (%u).",
			  o, s);

	/* Sanity check the offset, being careful about overflow */
		/* Sanity check. */
		if (offset > ds || offset + 6 > ds) {
Fixed code: 	if (doff >= size) {
				  "Tag starts past end of buffer (%u > %u)", doff, size);
		return 0;
	}

	if (s > size - doff) {
		exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData",
				  "Tag data goes past end of buffer (%u > %u)", doff+s, size);
	if (o >= ds) {
		exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "Bogus thumbnail offset (%u).", o);
		return;
	}
	if (s > ds - o) {
		exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "Bogus thumbnail size (%u), max would be %u.", s, ds-o);
	/* ds is restricted to 16 bit above, so offset is restricted too, and offset+8 should not overflow. */
	/* offset < 2<<16, n is 16 bit at most, so this op will not overflow */
		/* Sanity check. ds is ensured to be above 6 above, offset is 16bit */
		if (offset > ds - 6) {