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;
 | 
					  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);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user