Skip to content
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
6 changes: 5 additions & 1 deletion Zend/Optimizer/zend_cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -335,9 +335,13 @@ ZEND_API void zend_build_cfg(zend_arena **arena, const zend_op_array *op_array,
flags |= ZEND_FUNC_HAS_CALLS;
break;
case ZEND_INIT_FCALL:
case ZEND_INIT_FCALL_BY_NAME:
case ZEND_INIT_NS_FCALL_BY_NAME:
zv = CRT_CONSTANT(opline->op2);
if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
if (opline->opcode == ZEND_INIT_FCALL_BY_NAME) {
/* The second literal is the lowercased name */
zv += 1;
} else if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
/* The third literal is the lowercased unqualified name */
zv += 2;
}
Expand Down
2 changes: 1 addition & 1 deletion Zend/Optimizer/zend_optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -817,7 +817,7 @@ static bool zend_optimizer_ignore_function(zval *fbc_zv, zend_string *filename)
zend_function *fbc = Z_PTR_P(fbc_zv);

if (fbc->type == ZEND_INTERNAL_FUNCTION) {
return false;
return fbc->internal_function.module->type == MODULE_TEMPORARY;
} else if (fbc->type == ZEND_USER_FUNCTION) {
if (fbc->op_array.fn_flags & ZEND_ACC_PRELOADED) {
if (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE) {
Expand Down
4 changes: 3 additions & 1 deletion Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -4234,7 +4234,9 @@ static bool zend_compile_ignore_class(zend_class_entry *ce, zend_string *filenam
static bool zend_compile_ignore_function(zend_function *fbc, zend_string *filename)
{
if (fbc->type == ZEND_INTERNAL_FUNCTION) {
return CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS;
return (CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS)
|| ((CG(compiler_options) & ZEND_COMPILE_IGNORE_OTHER_FILES)
&& fbc->internal_function.module->type == MODULE_TEMPORARY);
} else {
return (CG(compiler_options) & ZEND_COMPILE_IGNORE_USER_FUNCTIONS)
|| ((CG(compiler_options) & ZEND_COMPILE_IGNORE_OTHER_FILES)
Expand Down
2 changes: 1 addition & 1 deletion ext/dl_test/tests/frameless_temporary_opcache.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ dl(PHP_OS_FAMILY === 'Windows' ? 'php_dl_test.dll' : 'dl_test.so');
include __DIR__ . '/frameless_temporary.inc';
?>
--EXPECTF--
%A0001 INIT_FCALL 1 %d string("dl_test_frameless")
%A0001 INIT_FCALL_BY_NAME 1 string("dl_test_frameless")
%Aint(7)
dl_test_frameless(): Argument #1 ($value) must be of type int, string given
105 changes: 105 additions & 0 deletions ext/opcache/tests/gh25296.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
--TEST--
GH-25296 (Segfault or wrong function called with dl() and opcache.file_cache)
--EXTENSIONS--
opcache
--SKIPIF--
<?php
$dl_test = PHP_OS_FAMILY === 'Windows' ? 'php_dl_test.dll' : 'dl_test.so';
if (!file_exists(ini_get('extension_dir') . '/' . $dl_test)) {
die('skip dl_test extension is not built');
}
?>
--FILE--
<?php
$cache = __DIR__ . '/' . 'gh25296';
$main = $cache . '/' . 'main.php';
$call = $cache . '/' . 'call.php';
mkdir($cache);

file_put_contents($main, <<<'PHP'
<?php
set_exception_handler(function (Throwable $e) {
echo $e::class, ": ", $e->getMessage(), "\n";
});
if ($argv[1] === 'shifted') {
function a() {}
function b() {}
}
if ($argv[1] === 'user') {
function dl_test_test2(string $str): string {
return "User $str";
}
} elseif ($argv[1] !== 'missing') {
dl('dl_test');
}
include __DIR__ . '/call.php';
PHP);

function write_script(string $script, int $version): void {
file_put_contents($script, <<<PHP
<?php
echo "v$version\n";
echo dl_test_test2("hi"), "\n";
PHP);
}

function run(string $cache, string $script, string $mode): 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
$script,
$mode,
);
$proc = proc_open($cmd, [1 => ['pipe', 'w'], 2 => ['redirect', 1]], $pipes);
echo stream_get_contents($pipes[1]);
proc_close($proc);
}

write_script($call, 1);
run($cache, $main, 'shifted');

write_script($call, 2);
echo "--- Function at another position ---\n";
run($cache, $main, 'dl');

echo "--- Function not loaded ---\n";
run($cache, $main, 'missing');

echo "--- User function of the same name ---\n";
run($cache, $main, 'user');
?>
--CLEAN--
<?php
$cache = __DIR__ . '/' . 'gh25296';
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
Hello hi
--- Function at another position ---
v1
Hello hi
--- Function not loaded ---
v1
Error: Call to undefined function dl_test_test2()
--- User function of the same name ---
v1
User hi
Loading