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:
		| @@ -10,6 +10,9 @@ abstract class AphrontBaseMySQLDatabaseConnection | ||||
|  | ||||
|   private $nextError; | ||||
|  | ||||
|   const CALLERROR_QUERY = 777777; | ||||
|   const CALLERROR_CONNECT = 777778; | ||||
|  | ||||
|   abstract protected function connect(); | ||||
|   abstract protected function rawQuery($raw_query); | ||||
|   abstract protected function rawQueries(array $raw_queries); | ||||
|   | ||||
| @@ -68,19 +68,47 @@ final class AphrontMySQLiDatabaseConnection | ||||
|       $host = 'p:'.$host; | ||||
|     } | ||||
|  | ||||
|     @$conn->real_connect( | ||||
|     $trap = new PhutilErrorTrap(); | ||||
|  | ||||
|     $ok = @$conn->real_connect( | ||||
|       $host, | ||||
|       $user, | ||||
|       $pass, | ||||
|       $database, | ||||
|       $port); | ||||
|  | ||||
|     $call_error = $trap->getErrorsAsString(); | ||||
|     $trap->destroy(); | ||||
|  | ||||
|     $errno = $conn->connect_errno; | ||||
|     if ($errno) { | ||||
|       $error = $conn->connect_error; | ||||
|       $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 | ||||
|     // 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. | ||||
| @@ -152,7 +180,7 @@ final class AphrontMySQLiDatabaseConnection | ||||
|             'Call to "mysqli->query()" failed, but did not set an error '. | ||||
|             'code or emit an error message.'); | ||||
|         } | ||||
|         $this->throwQueryCodeException(777777, $message); | ||||
|         $this->throwQueryCodeException(self::CALLERROR_QUERY, $message); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 epriestley
					epriestley