Vulnerability: The Memcache::getextendedstats function can be used to trigger an out-of-bounds read. Exploiting this issue requires control over memcached server hostnames and/or ports. This affects all supported versions of HHVM (3.30 and 3.27.4 and below).
Vulnerability ID: CVE-2018-6340
CWE: CWE-125
Severity score: 6.4
Project: https://github.com/facebook/hhvm
Published: 2018-12-31
File: hphp/runtime/ext/memcache/ext_memcache.cpp
Commit message: CVE-2018-6340 Fix buffer overread

Summary:
snprintf() returns the number of would be written bytes if the buffer had
enough size, letting the original code to construct output string from
arbitrary data on the stack.

Fix this by properly assembling the String without trying to arbitrarily
limiting the length to 30 bytes.

Reviewed By: ottoni

Differential Revision: D13509547

fbshipit-source-id: aa401b83d356f5b261433c4b16d777948ba7f9b4
Commit author: Jan Oravec

Vulnerable version (before fix):
 static Array HHVM_METHOD(Memcache, getextendedstats,    for (int server_id = 0; server_id < server_count; server_id++) {     memcached_stat_st *stat;     char stats_key[30] = {0};     size_t key_len;      LMCD_SERVER_POSITION_INSTANCE_TYPE instance =       memcached_server_instance_by_position(&data->m_memcache, server_id);     const char *hostname = LMCD_SERVER_HOSTNAME(instance); static Array HHVM_METHOD(Memcache, getextendedstats,       continue;     }      key_len = snprintf(stats_key, sizeof(stats_key), "%s:%d", hostname, port);      return_val.set(String(stats_key, key_len, CopyString), server_stats);   }    free(stats);

Fixed version (after fix):
 static Array HHVM_METHOD(Memcache, getextendedstats,    for (int server_id = 0; server_id < server_count; server_id++) {     memcached_stat_st *stat;     LMCD_SERVER_POSITION_INSTANCE_TYPE instance =       memcached_server_instance_by_position(&data->m_memcache, server_id);     const char *hostname = LMCD_SERVER_HOSTNAME(instance); static Array HHVM_METHOD(Memcache, getextendedstats,       continue;     }      auto const port_str = folly::to<std::string>(port);     auto const key_len = strlen(hostname) + 1 + port_str.length();     auto key = String(key_len, ReserveString);     key += hostname;     key += ":";     key += port_str;     return_val.set(key, server_stats);   }    free(stats);

Patch:
patch: @@ -700,9 +700,6 @@ static Array HHVM_METHOD(Memcache, getextendedstats,
 
   for (int server_id = 0; server_id < server_count; server_id++) {
     memcached_stat_st *stat;
-    char stats_key[30] = {0};
-    size_t key_len;
-
     LMCD_SERVER_POSITION_INSTANCE_TYPE instance =
       memcached_server_instance_by_position(&data->m_memcache, server_id);
     const char *hostname = LMCD_SERVER_HOSTNAME(instance);
@@ -715,9 +712,13 @@ static Array HHVM_METHOD(Memcache, getextendedstats,
       continue;
     }
 
-    key_len = snprintf(stats_key, sizeof(stats_key), "%s:%d", hostname, port);
-
-    return_val.set(String(stats_key, key_len, CopyString), server_stats);
+    auto const port_str = folly::to<std::string>(port);
+    auto const key_len = strlen(hostname) + 1 + port_str.length();
+    auto key = String(key_len, ReserveString);
+    key += hostname;
+    key += ":";
+    key += port_str;
+    return_val.set(key, server_stats);
   }
 
   free(stats);
path: hphp/runtime/ext/memcache/ext_memcache.cpp