CVE-2019-11929 (published 2019-10-02T19:15Z)

Insufficient boundary checks when formatting numbers in number_format allows read/write access to out-of-bounds memory, potentially leading to remote code execution. This issue affects HHVM versions prior to 3.30.10, all versions between 4.0.0 and 4.8.5, all versions between 4.9.0 and 4.18.2, and versions 4.19.0, 4.19.1, 4.20.0, 4.20.1, 4.20.2, 4.21.0, 4.22.0, 4.23.0.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-119
cwe_name: Improper Restriction of Operations within the Bounds of a Memory Buffer
description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
extended_description: 
            Certain languages allow direct addressing of memory locations and do not automatically ensure that these locations are valid for the memory buffer that is being referenced. This can cause read or write operations to be performed on memory locations that may be associated with other variables, data structures, or internal program data.
         
         

Fix information:
repo_url: https://github.com/facebook/hhvm
commit_details: repo_url: https://github.com/facebook/hhvm
author: Dhaval Kapil
author_date: 2019-09-18 01:44:34-07:00
msg: string_number_format: Correctly handles return value of snprintf

Summary: `snprintf` can return a value greater than the number of bytes copied. In case the first byte of the string is not a digit (could be '-'), size of `tmpstr` was being updated without checking `tmplen`. This resulted in either an assertion error or a heap overflow depending on whether the assertion is compiled or not.

Reviewed By: mofarrell, qianxuweiren

Differential Revision: D17327899

fbshipit-source-id: ee53875d21e02608c6d870388eecf1464de24ff1
repository: repo_url: https://github.com/facebook/hhvm
repo_name: facebook/hhvm
description: A virtual machine for executing programs written in Hack.
repo_language: Hack
owner: facebook
file_changes: filename: zend-string.cpp
change_type: ModificationType.MODIFY
diff: @@ -1618,11 +1618,15 @@ String string_number_format(double d, int dec,
   d = php_math_round(d, dec);
 
   // departure from PHP: we got rid of dependencies on spprintf() here.
+  // This actually means 63 bytes for characters + 1 byte for '\0'
   String tmpstr(63, ReserveString);
   tmpbuf = tmpstr.mutableData();
   tmplen = snprintf(tmpbuf, 64, "%.*F", dec, d);
+  // From the man page of snprintf, the return value is:
+  // The number of characters that would have been written if n had been
+  // sufficiently large, not counting the terminating null character.
   if (tmplen < 0) return empty_string();
-  if (tmpbuf == nullptr || !isdigit((int)tmpbuf[0])) {
+  if (tmplen < 64 && (tmpbuf == nullptr || !isdigit((int)tmpbuf[0]))) {
     tmpstr.setSize(tmplen);
     return tmpstr;
   }

programming_language: C++
filename: number_format_t53795309.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,9 @@
+<?hh
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+<<__EntryPoint>>
+function main() {
+  $bin_repr = "\x00\x00\x00\x00\x00\x00\x00\x80";
+  $double_num = unpack("dnum", $bin_repr)['num'];
+  var_dump(number_format($double_num, 100));
+}

programming_language: PHP
filename: number_format_t53795309.php.expect
change_type: ModificationType.ADD
diff: @@ -0,0 +1 @@
+string(103) "-0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"

programming_language: Batchfile