Skip to content
Merged
127 changes: 61 additions & 66 deletions ext/ffi/ffi.c
Original file line number Diff line number Diff line change
Expand Up @@ -4995,107 +4995,102 @@ ZEND_METHOD(FFI_CType, getFuncParameterType) /* {{{ */
}
/* }}} */

static char *zend_ffi_skip_ws_and_comments(char *p, bool allow_standalone_newline)
{
while (true) {
if (*p == ' ' || *p == '\t') {
p++;
} else if (allow_standalone_newline && (*p == '\r' || *p == '\n' || *p == '\f' || *p == '\v')) {
p++;
} else if (allow_standalone_newline && *p == '/' && p[1] == '/') {
p += 2;
while (*p && *p != '\r' && *p != '\n') {
p++;
}
} else if (*p == '/' && p[1] == '*') {
p += 2;
while (*p && (*p != '*' || p[1] != '/')) {
p++;
}
if (*p == '*') {
p++;
if (*p == '/') {
p++;
}
}
} else {
break;
}
}

return p;
}

static char *zend_ffi_parse_directives(const char *filename, char *code_pos, char **scope_name, char **lib, bool preload) /* {{{ */
{
char *p;

code_pos = zend_ffi_skip_ws_and_comments(code_pos, true);

*scope_name = NULL;
*lib = NULL;
while (*code_pos == '#') {
if (strncmp(code_pos, "#define FFI_SCOPE", sizeof("#define FFI_SCOPE") - 1) == 0
&& (code_pos[sizeof("#define FFI_SCOPE") - 1] == ' '
|| code_pos[sizeof("#define FFI_SCOPE") - 1] == '\t')) {
p = code_pos + sizeof("#define FFI_SCOPE");
while (*p == ' ' || *p == '\t') {
p++;
}
if (*p != '"') {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_SCOPE define", filename);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_SCOPE define", filename);
}
return NULL;
}
p++;
if (*scope_name) {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', FFI_SCOPE defined twice", filename);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', FFI_SCOPE defined twice", filename);
}
return NULL;
}
*scope_name = p;
while (1) {
if (*p == '\"') {
*p = 0;
if (strncmp(code_pos, ZEND_STRL("#define")) == 0) {
p = zend_ffi_skip_ws_and_comments(code_pos + sizeof("#define") - 1, false);

char **target = NULL;
const char *target_name = NULL;
if (strncmp(p, ZEND_STRL("FFI_SCOPE")) == 0) {
p = zend_ffi_skip_ws_and_comments(p + sizeof("FFI_SCOPE") - 1, false);
target = scope_name;
target_name = "FFI_SCOPE";
} else if (strncmp(p, ZEND_STRL("FFI_LIB")) == 0) {
p = zend_ffi_skip_ws_and_comments(p + sizeof("FFI_LIB") - 1, false);
target = lib;
target_name = "FFI_LIB";
} else {
while (*p && *p != '\n' && *p != '\r') {
p++;
break;
} else if (*p <= ' ') {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_SCOPE define", filename);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_SCOPE define", filename);
}
return NULL;
}
p++;
}
while (*p == ' ' || *p == '\t') {
p++;
}
while (*p == '\r' || *p == '\n') {
p++;
}
code_pos = p;
} else if (strncmp(code_pos, "#define FFI_LIB", sizeof("#define FFI_LIB") - 1) == 0
&& (code_pos[sizeof("#define FFI_LIB") - 1] == ' '
|| code_pos[sizeof("#define FFI_LIB") - 1] == '\t')) {
p = code_pos + sizeof("#define FFI_LIB");
while (*p == ' ' || *p == '\t') {
p++;
code_pos = zend_ffi_skip_ws_and_comments(p, true);
continue;
}

if (*p != '"') {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_LIB define", filename);
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad %s define", filename, target_name);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_LIB define", filename);
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad %s define", filename, target_name);
}
return NULL;
}
p++;
if (*lib) {
if (*target) {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', FFI_LIB defined twice", filename);
zend_error(E_WARNING, "FFI: failed pre-loading '%s', %s defined twice", filename, target_name);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', FFI_LIB defined twice", filename);
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', %s defined twice", filename, target_name);
}
return NULL;
}
*lib = p;
*target = p;
while (1) {
if (*p == '\"') {
*p = 0;
p++;
break;
} else if (*p <= ' ') {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_LIB define", filename);
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad %s define", filename, target_name);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_LIB define", filename);
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad %s define", filename, target_name);
}
return NULL;
}
p++;
}
while (*p == ' ' || *p == '\t') {
p++;
}
while (*p == '\r' || *p == '\n') {
p++;
}
code_pos = p;

code_pos = zend_ffi_skip_ws_and_comments(p, true);
} else {
break;
}
Expand Down
12 changes: 12 additions & 0 deletions ext/ffi/tests/bug79075.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Multiline comment
*/
// whitespace line

#define ignore_this_line 1
//
#define/* inline */FFI_SCOPE /* multi-
line */ "bug79075" /* end
*/

int printf(const char *format, ...);
3 changes: 3 additions & 0 deletions ext/ffi/tests/bug79075.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

FFI::load(__DIR__ . "/bug79075.h");
25 changes: 25 additions & 0 deletions ext/ffi/tests/bug79075.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
Bug #79075 (FFI header parser chokes on comments)
--EXTENSIONS--
ffi
opcache
posix
--SKIPIF--
<?php
if (substr(PHP_OS, 0, 3) == 'WIN') die('skip not for Windows');
if (posix_geteuid() == 0) die('skip Cannot run test as root.');
?>
--INI--
ffi.enable=1
opcache.enable=1
opcache.enable_cli=1
opcache.optimization_level=-1
opcache.preload={PWD}/bug79075.inc
opcache.file_cache_only=0
--FILE--
<?php
$ffi = FFI::scope("bug79075");
$ffi->printf("Hello World from %s!\n", "PHP");
?>
--EXPECT--
Hello World from PHP!
34 changes: 25 additions & 9 deletions ext/mysqli/tests/fake_server.inc
Original file line number Diff line number Diff line change
Expand Up @@ -615,19 +615,30 @@ class my_mysqli_fake_server_conn

class my_mysqli_fake_server_process
{
private int $port;

public function __construct(private $process, private array $pipes) {}

public function terminate(bool $wait = false)
public function terminate(bool $wait = false): void
{
if ($wait) {
$this->wait();
}
proc_terminate($this->process);
}

public function wait()
public function wait(): void
{
echo fgets($this->pipes[1]);
$line = fgets($this->pipes[1]);
if (preg_match('/\[\*\] Server started on \d+\.\d+\.\d+\.\d+:(\d+)/', $line, $matches)) {
$this->port = (int)$matches[1];
}
echo $line;
}

public function getPort(): int
{
return $this->port ?? throw new RuntimeException("Port not set");
}
}

Expand Down Expand Up @@ -807,15 +818,20 @@ function my_mysqli_test_query_response_row_read_two_fields(my_mysqli_fake_server
}
}

function run_fake_server(string $test_function, $port = 33305): void
function run_fake_server(string $test_function, int|string $port = 0): int
{
$address = '127.0.0.1';
$host = '127.0.0.1';

$socket = @stream_socket_server("tcp://$address:$port", $errno, $errstr);
$socket = @stream_socket_server("tcp://$host:$port", $errno, $errstr);
if (!$socket) {
die("Failed to create socket: $errstr ($errno)\n");
}
echo "[*] Server started\n";
if (intval($port) === 0) {
$address = stream_socket_get_name($socket, false);
list($host, $port) = explode(":", $address);
}

echo "[*] Server started on $host:$port\n";

try {
$conn = new my_mysqli_fake_server_conn($socket);
Expand All @@ -832,7 +848,7 @@ function run_fake_server(string $test_function, $port = 33305): void
}


function run_fake_server_in_background($test_function, $port = 33305): my_mysqli_fake_server_process
function run_fake_server_in_background($test_function, $port = 0): my_mysqli_fake_server_process
{
$command = [PHP_BINARY, '-n', __FILE__, 'mysqli_fake_server', $test_function, $port];

Expand All @@ -852,5 +868,5 @@ function run_fake_server_in_background($test_function, $port = 33305): my_mysqli
}

if (isset($argv) && $argc > 2 && $argv[1] == 'mysqli_fake_server') {
run_fake_server($argv[2], $argv[3] ?? '33305');
run_fake_server($argv[2], $argv[3] ?? 0);
}
9 changes: 4 additions & 5 deletions ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@ mysqli
<?php
require_once 'fake_server.inc';

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

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

try {
$conn = new mysqli( $servername, $username, $password, "", $port );
$conn = new mysqli( $servername, $username, $password, "", $process->getPort());
$info = mysqli_info($conn);
var_dump($info);
} catch (Exception $e) {
Expand All @@ -27,12 +26,12 @@ $process->terminate();
print "done!";
?>
--EXPECTF--
[*] Server started
[*] Server started on 127.0.0.1:%d
[*] Connection established
[*] 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:33305
Unknown error while trying to connect via tcp://127.0.0.1:%d
done!
8 changes: 3 additions & 5 deletions ext/mysqli/tests/ghsa-h35g-vwh6-m678-def.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@ mysqli
<?php
require_once 'fake_server.inc';


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

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

$conn = new mysqli($servername, $username, $password, "", $port);
$conn = new mysqli($servername, $username, $password, "", $process->getPort());

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

Expand All @@ -34,7 +32,7 @@ $process->terminate();
print "done!";
?>
--EXPECTF--
[*] Server started
[*] Server started on 127.0.0.1:%d
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
Expand Down
7 changes: 3 additions & 4 deletions ext/mysqli/tests/ghsa-h35g-vwh6-m678-filename.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ mysqli
<?php
require_once 'fake_server.inc';

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

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

$conn = new mysqli($servername, $username, $password, "", $port);
$conn = new mysqli($servername, $username, $password, "", $process->getPort());
echo "[*] Running query on the fake server...\n";

$result = $conn->query("SELECT * from users");
Expand All @@ -27,7 +26,7 @@ $process->terminate();
print "done!";
?>
--EXPECTF--
[*] Server started
[*] Server started on 127.0.0.1:%d
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
Expand Down
Loading
Loading