1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-10 08:52:39 +01:00

When "mysqli->real_connect()" fails without setting an error code, recover more gracefully

Summary: Depends on D20779. Ref T13403. Bad parameters may cause this call to fail without setting an error code; if it does, catch the issue and go down the normal connection error pathway.

Test Plan:
  - With "mysql.port" set to "quack", ran `bin/storage probe`.
  - Before: wild mess of warnings as the code continued below and failed when trying to interact with the connection.
  - After: clean connection failure with a useful error message.

Maniphest Tasks: T13403

Differential Revision: https://secure.phabricator.com/D20780
This commit is contained in:
epriestley 2019-09-03 12:09:20 -07:00
parent d9badba147
commit f8eec38c94
2 changed files with 33 additions and 2 deletions

View file

@ -10,6 +10,9 @@ abstract class AphrontBaseMySQLDatabaseConnection
private $nextError; private $nextError;
const CALLERROR_QUERY = 777777;
const CALLERROR_CONNECT = 777778;
abstract protected function connect(); abstract protected function connect();
abstract protected function rawQuery($raw_query); abstract protected function rawQuery($raw_query);
abstract protected function rawQueries(array $raw_queries); abstract protected function rawQueries(array $raw_queries);

View file

@ -68,19 +68,47 @@ final class AphrontMySQLiDatabaseConnection
$host = 'p:'.$host; $host = 'p:'.$host;
} }
@$conn->real_connect( $trap = new PhutilErrorTrap();
$ok = @$conn->real_connect(
$host, $host,
$user, $user,
$pass, $pass,
$database, $database,
$port); $port);
$call_error = $trap->getErrorsAsString();
$trap->destroy();
$errno = $conn->connect_errno; $errno = $conn->connect_errno;
if ($errno) { if ($errno) {
$error = $conn->connect_error; $error = $conn->connect_error;
$this->throwConnectionException($errno, $error, $user, $host); $this->throwConnectionException($errno, $error, $user, $host);
} }
// See T13403. If the parameters to "real_connect()" are wrong, it may
// fail without setting an error code. In this case, raise a generic
// exception. (One way to reproduce this is to pass a string to the
// "port" parameter.)
if (!$ok) {
if (strlen($call_error)) {
$message = pht(
'mysqli->real_connect() failed: %s',
$call_error);
} else {
$message = pht(
'mysqli->real_connect() failed, but did not set an error code '.
'or emit a message.');
}
$this->throwConnectionException(
self::CALLERROR_CONNECT,
$message,
$user,
$host);
}
// See T13238. Attempt to prevent "LOAD DATA LOCAL INFILE", which allows a // See T13238. Attempt to prevent "LOAD DATA LOCAL INFILE", which allows a
// malicious server to ask the client for any file. At time of writing, // malicious server to ask the client for any file. At time of writing,
// this option MUST be set after "real_connect()" on all PHP versions. // this option MUST be set after "real_connect()" on all PHP versions.
@ -152,7 +180,7 @@ final class AphrontMySQLiDatabaseConnection
'Call to "mysqli->query()" failed, but did not set an error '. 'Call to "mysqli->query()" failed, but did not set an error '.
'code or emit an error message.'); 'code or emit an error message.');
} }
$this->throwQueryCodeException(777777, $message); $this->throwQueryCodeException(self::CALLERROR_QUERY, $message);
} }
} }