Skip to content

Fix mysqlnd protocol errors #16421

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions ext/mysqli/tests/fake_server.inc
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,13 @@ class my_mysqli_fake_server_conn
$packets = $this->packet_generator->server_query_execute_data_response($field_name);
$this->send($this->packets_to_bytes($packets), "Query execute data $field_name");
}

public function send_too_short_server_ok()
{
$packet = $this->packet_generator->server_ok();
$packet->packet_length = "060000";
$this->send($packet->to_bytes(), "Server OK");
}
}

class my_mysqli_fake_server_process
Expand Down Expand Up @@ -807,6 +814,18 @@ function my_mysqli_test_query_response_row_read_two_fields(my_mysqli_fake_server
}
}

function my_mysqli_test_invalid_greeting_packet(my_mysqli_fake_server_conn $conn): void
{
// Send nothing at all to the client
}

function my_mysqli_test_ok_packet_too_short(my_mysqli_fake_server_conn $conn): void
{
$conn->send_server_greetings();
$conn->read();
$conn->send_too_short_server_ok();
}

function run_fake_server(string $test_function, $port = 33305): void
{
$address = '127.0.0.1';
Expand Down
6 changes: 2 additions & 4 deletions ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ try {
$info = mysqli_info($conn);
var_dump($info);
} catch (Exception $e) {
echo $e->getMessage() . PHP_EOL;
echo 'ERROR: ' . $e->getMessage() . PHP_EOL;
}

$process->terminate();
Expand All @@ -32,7 +32,5 @@ print "done!";
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
[*] Sending - Malicious OK Auth Response [Extract heap through buffer over-read]: 0900000200000002000000fcff

Warning: mysqli::__construct(): OK packet message length is past the packet size in %s on line %d
Unknown error while trying to connect via tcp://127.0.0.1:50001
ERROR: OK packet message length is past the packet size
done!
13 changes: 5 additions & 8 deletions ext/mysqli/tests/ghsa-h35g-vwh6-m678-def.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@ $conn = new mysqli($servername, $username, $password, "", $port);

echo "[*] Running query on the fake server...\n";

$result = $conn->query("SELECT * from users");

if ($result) {
$all_fields = $result->fetch_fields();
var_dump($result->fetch_all(MYSQLI_ASSOC));
var_dump(get_object_vars($all_fields[0])["def"]);
try {
$result = $conn->query("SELECT * from users");
} catch (mysqli_sql_exception $exception) {
echo 'ERROR: ' . $exception->getMessage() . PHP_EOL;
}

$conn->close();
Expand All @@ -42,6 +40,5 @@ print "done!";
[*] Running query on the fake server...
[*] Received: 140000000353454c454354202a2066726f6d207573657273
[*] Sending - Malicious Tabular Response [Extract heap through buffer over-read]: 01000001011e0000020164016401640164016401640c3f000b000000030350000000fd000001aa05000003fe00002200040000040135017405000005fe00002200

Warning: mysqli::query(): Protocol error. Server sent default for unsupported field list (mysqlnd_wireprotocol.c:%d) in %s on line %d
ERROR: Server sent default for unsupported field list
done!
12 changes: 7 additions & 5 deletions ext/mysqli/tests/ghsa-h35g-vwh6-m678-filename.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ $process->wait();
$conn = new mysqli($servername, $username, $password, "", $port);
echo "[*] Running query on the fake server...\n";

$result = $conn->query("SELECT * from users");
try {
$result = $conn->query("SELECT * from users");
} catch (mysqli_sql_exception $exception) {
echo 'ERROR: ' . $exception->getMessage() . PHP_EOL;
}

$info = mysqli_info($conn);

var_dump($info);
Expand All @@ -35,9 +40,6 @@ print "done!";
[*] Running query on the fake server...
[*] Received: 140000000353454c454354202a2066726f6d207573657273
[*] Sending - Malicious Tabular Response [Extract heap through buffer over-read]: 0900000100000000000000fa65

Warning: mysqli::query(): RSET_HEADER packet additional data length is past 249 bytes the packet size in %s on line %d

Warning: mysqli::query(): Error reading result set's header in %s on line %d
ERROR: RSET_HEADER packet additional data length is past the packet size
NULL
done!
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
AUTH_RESPONSE packet is shorter than expected
--EXTENSIONS--
mysqli
--FILE--
<?php
require_once __DIR__ . '/../fake_server.inc';

$port = 50001;
$servername = "127.0.0.1";
$username = "root";
$password = "";

$process = run_fake_server_in_background('ok_packet_too_short', $port);
$process->wait();

try {
$conn = new mysqli( $servername, $username, $password, "", $port );
} catch (Exception $e) {
echo 'ERROR: ' . $e->getMessage() . PHP_EOL;
}

$process->terminate();

print "done!";
?>
--EXPECTF--
[*] Server started
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
[*] Sending - Server OK: 0600000200000002000000

Warning: mysqli::__construct(): Premature end of data (mysqlnd_wireprotocol.c:732) in %s
ERROR: AUTH_RESPONSE packet is shorter than expected
done!
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
--TEST--
Error while reading greeting packet
--EXTENSIONS--
mysqli
--FILE--
<?php
require_once __DIR__ . '/../fake_server.inc';

$port = 50001;
$servername = "127.0.0.1";
$username = "root";
$password = "";

$process = run_fake_server_in_background('invalid_greeting_packet', $port);
$process->wait();

try {
$conn = new mysqli( $servername, $username, $password, "", $port );
} catch (Exception $e) {
echo 'ERROR: ' . $e->getMessage() . PHP_EOL;
}

$process->terminate();

print "done!";
?>
--EXPECTF--
[*] Server started
[*] Connection established
ERROR: Error while reading greeting packet
done!
3 changes: 1 addition & 2 deletions ext/mysqlnd/mysqlnd_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -594,8 +594,7 @@ MYSQLND_METHOD(mysqlnd_command, handshake)(MYSQLND_CONN_DATA * const conn, const
conn->payload_decoder_factory->m.init_greet_packet(&greet_packet);

if (FAIL == PACKET_READ(conn, &greet_packet)) {
DBG_ERR("Error while reading greeting packet");
php_error_docref(NULL, E_WARNING, "Error while reading greeting packet. PID=%d", getpid());
SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Error while reading greeting packet");
goto err;
} else if (greet_packet.error_no) {
DBG_ERR_FMT("errorno=%u error=%s", greet_packet.error_no, greet_packet.error);
Expand Down
2 changes: 2 additions & 0 deletions ext/mysqlnd/mysqlnd_ps.c
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ mysqlnd_stmt_read_prepare_response(MYSQLND_STMT * s)
conn->payload_decoder_factory->m.init_prepare_response_packet(&prepare_resp);

if (FAIL == PACKET_READ(conn, &prepare_resp)) {
// There might have been a connection specific error which we must report as a statement error
COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
goto done;
}

Expand Down
3 changes: 0 additions & 3 deletions ext/mysqlnd/mysqlnd_result.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,6 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);

if (FAIL == (ret = PACKET_READ(conn, &rset_header))) {
if (conn->error_info->error_no != CR_SERVER_GONE_ERROR && conn->error_info->error_no != CR_CLIENT_INTERACTION_TIMEOUT) {
php_error_docref(NULL, E_WARNING, "Error reading result set's header");
}
break;
}

Expand Down
Loading