CVE-2019-11925: Out-of-bounds Read (CWE-125)
value: Insufficient boundary checks when processing the JPEG APP12 block marker in the GD extension could allow access to out-of-bounds memory via a maliciously constructed invalid JPEG input. This issue affects HHVM versions prior to 3.30.9, all versions between 4.0.0 and 4.8.3, all versions between 4.9.0 and 4.15.2, and versions 4.16.0 to 4.16.3, 4.17.0 to 4.17.2, 4.18.0 to 4.18.1, 4.19.0, 4.20.0 to 4.20.1.
CWE description: The product reads data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 9.8
Published: 2019-09-06T19:15Z
Repository: https://github.com/facebook/hhvm
Language: PHP
Files changed: hphp/runtime/ext/gd/ext_gd.cpp, hphp/test/slow/ext_gd/exifreaddata_APP12.php, hphp/test/slow/ext_gd/exifreaddata_APP12.php.expectf
Commit date: 2019-09-03 17:20:18 -0700
Commit message: exif_process_APP12: Fix length calculation for second tag to prevent information leak

Summary:
buffer + 2+l1+1 points to the second tag. The maximum length of the buffer is length. Hence, the max length of the tag is length - (2+l1+1). Instead, the length passed is length - 2 - l1 + 1 which leads to a 2 byte overflow. If the last character of buffer is not null, this would lead to an out of bounds read of size 2.

Fixes CVE-2019-11925

Reviewed By: fredemmott

Differential Revision: D16930333

fbshipit-source-id: aad9cccaef3c678abc53bfd549aff6b582082a90

Diff: diff --git a/hphp/runtime/ext/gd/ext_gd.cpp b/hphp/runtime/ext/gd/ext_gd.cpp
index 96db385c0a4..df336ea9af0 100644
--- a/hphp/runtime/ext/gd/ext_gd.cpp
+++ b/hphp/runtime/ext/gd/ext_gd.cpp
@@ -6962,11 +6962,11 @@ static void exif_process_APP12(image_info_type *ImageInfo,
   size_t l1, l2=0;
   if ((l1 = php_strnlen(buffer+2, length-2)) > 0) {
     exif_iif_add_tag(ImageInfo, SECTION_APP12, "Company",
                      TAG_NONE, TAG_FMT_STRING, l1, buffer+2);
     if (length > 2+l1+1) {
-      l2 = php_strnlen(buffer+2+l1+1, length-2-l1+1);
+      l2 = php_strnlen(buffer+2+l1+1, length-2-l1-1);
       exif_iif_add_tag(ImageInfo, SECTION_APP12, "Info",
                        TAG_NONE, TAG_FMT_STRING, l2, buffer+2+l1+1);
     }
   }
 }
diff --git a/hphp/test/slow/ext_gd/exifreaddata_APP12.php b/hphp/test/slow/ext_gd/exifreaddata_APP12.php
new file mode 100644
index 00000000000..d563a481468
--- /dev/null
+++ b/hphp/test/slow/ext_gd/exifreaddata_APP12.php
@@ -0,0 +1,8 @@
+<?hh
+
+<<__EntryPoint>>
+function main_exifreaddata() {
+  $jpeg = "\xff\xd8\xec\x00\x05\x58\x00\x17";
+  $data_stream = "data://text/plain;base64," . base64_encode($jpeg);
+  var_dump(exif_read_data($data_stream));
+}
diff --git a/hphp/test/slow/ext_gd/exifreaddata_APP12.php.expectf b/hphp/test/slow/ext_gd/exifreaddata_APP12.php.expectf
new file mode 100644
index 00000000000..84a1dbec7b6
--- /dev/null
+++ b/hphp/test/slow/ext_gd/exifreaddata_APP12.php.expectf
@@ -0,0 +1,4 @@
+Warning: File structure corrupted in %s/ext_gd/exifreaddata_APP12.php on line 7
+
+Warning: Invalid JPEG file in %s/ext_gd/exifreaddata_APP12.php on line 7
+bool(false)

Vulnerable code:       l2 = php_strnlen(buffer+2+l1+1, length-2-l1+1);
Fixed code:       l2 = php_strnlen(buffer+2+l1+1, length-2-l1-1);
<?hh

<<__EntryPoint>>
function main_exifreaddata() {
  $jpeg = "\xff\xd8\xec\x00\x05\x58\x00\x17";
  $data_stream = "data://text/plain;base64," . base64_encode($jpeg);
  var_dump(exif_read_data($data_stream));
}
Warning: File structure corrupted in %s/ext_gd/exifreaddata_APP12.php on line 7

Warning: Invalid JPEG file in %s/ext_gd/exifreaddata_APP12.php on line 7
bool(false)