Merge branch 'PHP-8.5' into PHP-8.6

* PHP-8.5:
  Fix GH-24438: Preserve indirect types in JIT side traces
  Fix GH-17733: System entropy does not take into account function order, resulting in wrong functions possibly called in different configurations
This commit is contained in:
ndossche committed 2026-10-26 18:44:01 +01:00
commit ca0030ba18
7 files changed
+171 -23

No files matched your search

+4
View File
@@ -32,6 +32,10 @@ PHP NEWS
assigning to a reference). (waroad)
. Fixed bug GH-24745 (File cache loses attributes on constants on 32-bit
builds). (Yannick Decat, Lazizbek Ergashev)
. Fixed bug GH-17733 (System entropy does not take into account function
order, resulting in wrong functions possibly called
in different configurations). (ndossche)
. Fixed bug GH-24438 (Preserve indirect types in JIT side traces). (aasul)
- OpenSSL:
. Fixed build with OpenSSL 4.1. (Weilin Du)
-2
View File
@@ -34,7 +34,6 @@
#include "zend_enum.h"
#include "zend_object_handlers.h"
#include "zend_observer.h"
#include "zend_system_id.h"
#include <stdarg.h>
@@ -3236,7 +3235,6 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
}
zend_flf_handlers[zend_flf_count] = flf_info->handler;
zend_flf_functions[zend_flf_count] = (zend_function *)reg_function;
zend_add_system_entropy("frameless", ZSTR_VAL(reg_function->function_name), &flf_info->num_args, sizeof(flf_info->num_args));
zend_flf_count++;
flf_info++;
}
+15
View File
@@ -87,6 +87,21 @@ void zend_finalize_system_id(void)
}
}
/* Cached scripts can refer to offsets of internal functions.
* Effects of disable_functions and frameless functions must be taken into account.
* Therefore, embed the function names in-order.
* Updating third party extensions may change function signatures, arginfo, offsets, ...
* Therefore, also embed the loaded modules and their versions. */
ZEND_HASH_MAP_FOREACH_STR_KEY(CG(function_table), zend_string *name) {
PHP_MD5Update(&context, ZSTR_VAL(name), ZSTR_LEN(name) + 1);
} ZEND_HASH_FOREACH_END();
ZEND_HASH_MAP_FOREACH_PTR(&module_registry, zend_module_entry *mod) {
PHP_MD5Update(&context, mod->name, strlen(mod->name) + 1);
if (EXPECTED(mod->version != NO_VERSION_YET)) {
PHP_MD5Update(&context, mod->version, strlen(mod->version) + 1);
}
} ZEND_HASH_FOREACH_END();
PHP_MD5Final(digest, &context);
zend_bin2hex(zend_system_id, digest, sizeof digest);
finalized = 1;
+1 -1
View File
@@ -1772,7 +1772,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
if (i < op_array->last_var) {
ssa_var_info[i].type = MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
} else {
ssa_var_info[i].type = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
ssa_var_info[i].type = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_INDIRECT | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
}
}
if (i < parent_vars_count) {
+110
View File
@@ -0,0 +1,110 @@
--TEST--
GH-17733 (System entropy does not take into account function order, resulting in wrong functions possibly called in different configurations)
--EXTENSIONS--
opcache
--FILE--
<?php
$cache = __DIR__ . '/' . 'gh17733';
$script = $cache . '/' . 'call.php';
mkdir($cache);
function write_script(string $script, int $version): void {
file_put_contents($script, <<<PHP
<?php
echo "v$version\n";
try {
var_dump(str_repeat("ab", 3));
} catch (\Throwable \$e) {
echo \$e::class, ": ", \$e->getMessage(), "\n";
}
PHP);
}
function run(string $cache, string $script, string $disable_functions): void {
$ext = ini_get('extension_dir');
$opcache = PHP_OS_FAMILY === 'Windows' ? 'php_opcache.dll' : 'opcache.so';
$cmd = [PHP_BINARY, '-n', '-d', 'extension_dir="' . $ext . '"'];
if (file_exists($ext . '/' . $opcache)) {
array_push($cmd, '-d', 'zend_extension=opcache');
}
array_push($cmd,
'-d', 'opcache.enable_cli=1',
'-d', 'opcache.file_cache="' . $cache . '"',
'-d', 'opcache.file_cache_only=1',
'-d', 'opcache.file_update_protection=0',
'-d', 'opcache.validate_timestamps=0', // Important to tell apart versions
'-d', 'disable_functions=' . $disable_functions,
$script,
);
$proc = proc_open($cmd, [1 => ['pipe', 'w'], 2 => ['redirect', 1]], $pipes);
echo stream_get_contents($pipes[1]);
proc_close($proc);
}
function system_ids(string $cache): int {
$pattern = PHP_OS_FAMILY === 'Windows' ? '/*/*' : '/*';
return count(glob($cache . $pattern, GLOB_ONLYDIR));
}
write_script($script, 1);
run($cache, $script, '');
var_dump(system_ids($cache));
write_script($script, 2);
echo "--- Same environment reuses the cache ---\n";
run($cache, $script, '');
var_dump(system_ids($cache));
echo "--- Shifted function table ---\n";
run($cache, $script, 'zend_version');
var_dump(system_ids($cache));
echo "--- Disabled function ---\n";
run($cache, $script, 'str_repeat');
var_dump(system_ids($cache));
write_script($script, 3);
echo "--- Each environment reuses its own cache ---\n";
run($cache, $script, 'zend_version');
run($cache, $script, 'str_repeat');
run($cache, $script, '');
var_dump(system_ids($cache));
?>
--CLEAN--
<?php
$cache = __DIR__ . '/' . 'gh17733';
if (is_dir($cache)) {
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($cache, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($it as $file) {
$file->isDir() ? rmdir($file->getPathname()) : unlink($file->getPathname());
}
rmdir($cache);
}
?>
--EXPECT--
v1
string(6) "ababab"
int(1)
--- Same environment reuses the cache ---
v1
string(6) "ababab"
int(1)
--- Shifted function table ---
v2
string(6) "ababab"
int(2)
--- Disabled function ---
v2
Error: Call to undefined function str_repeat()
int(3)
--- Each environment reuses its own cache ---
v2
string(6) "ababab"
v2
Error: Call to undefined function str_repeat()
v1
string(6) "ababab"
int(3)
+40
View File
@@ -0,0 +1,40 @@
--TEST--
GH-24438 (Tracing JIT preserves indirect arguments on side-trace entry)
--EXTENSIONS--
opcache
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.file_update_protection=0
opcache.jit_buffer_size=64M
opcache.jit=tracing
opcache.jit_hot_loop=1
opcache.jit_hot_func=1
opcache.jit_hot_return=1
opcache.jit_hot_side_exit=1
opcache.jit_max_polymorphic_calls=0
--FILE--
<?php
function byVal($value) {
$value = (int) $value + 1000;
return $value;
}
function byRef(&$value) {
$value = (int) $value + 1;
return $value;
}
$fns = ['byVal', 'byRef'];
$arr = [10, 20];
for ($i = 0; $i < 120; $i++) {
$f = $fns[$i % 2];
$f($arr[1]);
$f($arr[0]);
}
var_dump($arr[0], $arr[1]);
?>
--EXPECT--
int(70)
int(80)
+1 -20
View File
@@ -583,32 +583,13 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
}
if (opline->op2_type == IS_CONST) {
SERIALIZE_PTR(opline->op2.zv);
/* See GH-17733. Reset Z_EXTRA_P(op2) of ZEND_INIT_FCALL, which
* is an offset into the global function table, to avoid calling
* incorrect functions when environment changes. This, and the
* equivalent code below, can be removed once proper system ID
* validation is implemented. */
if (opline->opcode == ZEND_INIT_FCALL) {
zval *op2 = opline->op2.zv;
UNSERIALIZE_PTR(op2);
Z_EXTRA_P(op2) = 0;
ZEND_VM_SET_OPCODE_HANDLER(opline);
}
}
#else
if (opline->op1_type == IS_CONST) {
opline->op1.constant = RT_CONSTANT(opline, opline->op1) - literals;
}
if (opline->op2_type == IS_CONST) {
zval *op2 = RT_CONSTANT(opline, opline->op2);
opline->op2.constant = op2 - literals;
/* See GH-17733 and comment above. */
if (opline->opcode == ZEND_INIT_FCALL) {
Z_EXTRA_P(op2) = 0;
ZEND_VM_SET_OPCODE_HANDLER(opline);
}
opline->op2.constant = RT_CONSTANT(opline, opline->op2) - literals;
}
#endif
#if ZEND_USE_ABS_JMP_ADDR