diff --git a/src/aphront/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php b/src/aphront/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php --- a/src/aphront/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php +++ b/src/aphront/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php @@ -64,6 +64,18 @@ $conn->options(MYSQLI_OPT_CONNECT_TIMEOUT, $timeout); } + // See T13238. Attempt to prevent "LOAD DATA LOCAL INFILE", which allows a + // malicious server to ask the client for any file. + + // NOTE: See T13238. This option does not appear to ever have any effect. + // Only the PHP level configuration of "mysqli.allow_local_infile" is + // effective in preventing "LOAD DATA LOCAL INFILE". It appears that the + // configuration option may overwrite the local option? Set the local + // option to the desired (safe) value anyway in case this starts working + // properly in some future version of PHP/MySQLi. + + $conn->options(MYSQLI_OPT_LOCAL_INFILE, 0); + if ($this->getPersistent()) { $host = 'p:'.$host; } @@ -122,7 +134,36 @@ return @$conn->reap_async_query(); } - return @$conn->query($raw_query); + $trap = new PhutilErrorTrap(); + + $result = @$conn->query($raw_query); + + $err = $trap->getErrorsAsString(); + $trap->destroy(); + + // See T13238 and PHI1014. Sometimes, the call to "$conn->query()" may fail + // without setting an error code on the connection. One way to reproduce + // this is to use "LOAD DATA LOCAL INFILE" with "mysqli.allow_local_infile" + // disabled. + + // If we have no result and no error code, raise a synthetic query error + // with whatever error message was raised as a local PHP warning. + + if (!$result) { + $error_code = $this->getErrorCode($conn); + if (!$error_code) { + if (strlen($err)) { + $message = $err; + } else { + $message = pht( + 'Call to "mysqli->query()" failed, but did not set an error '. + 'code or emit an error message.'); + } + $this->throwQueryCodeException(777777, $message); + } + } + + return $result; } protected function rawQueries(array $raw_queries) {