|
| 1 | +--TEST-- |
| 2 | +GH-17733 (System entropy does not take into account function order, resulting in wrong functions possibly called in different configurations) |
| 3 | +--EXTENSIONS-- |
| 4 | +opcache |
| 5 | +--FILE-- |
| 6 | +<?php |
| 7 | +$cache = __DIR__ . '/' . 'gh17733'; |
| 8 | +$script = $cache . '/' . 'call.php'; |
| 9 | +mkdir($cache); |
| 10 | + |
| 11 | +function write_script(string $script, int $version): void { |
| 12 | + file_put_contents($script, <<<PHP |
| 13 | + <?php |
| 14 | + echo "v$version\n"; |
| 15 | + try { |
| 16 | + var_dump(str_repeat("ab", 3)); |
| 17 | + } catch (\Throwable \$e) { |
| 18 | + echo \$e::class, ": ", \$e->getMessage(), "\n"; |
| 19 | + } |
| 20 | + PHP); |
| 21 | +} |
| 22 | + |
| 23 | +function run(string $cache, string $script, string $disable_functions): void { |
| 24 | + $ext = ini_get('extension_dir'); |
| 25 | + $opcache = PHP_OS_FAMILY === 'Windows' ? 'php_opcache.dll' : 'opcache.so'; |
| 26 | + $cmd = [PHP_BINARY, '-n', '-d', 'extension_dir="' . $ext . '"']; |
| 27 | + if (file_exists($ext . '/' . $opcache)) { |
| 28 | + array_push($cmd, '-d', 'zend_extension=opcache'); |
| 29 | + } |
| 30 | + array_push($cmd, |
| 31 | + '-d', 'opcache.enable_cli=1', |
| 32 | + '-d', 'opcache.file_cache="' . $cache . '"', |
| 33 | + '-d', 'opcache.file_cache_only=1', |
| 34 | + '-d', 'opcache.file_update_protection=0', |
| 35 | + '-d', 'opcache.validate_timestamps=0', // Important to tell apart versions |
| 36 | + '-d', 'disable_functions=' . $disable_functions, |
| 37 | + $script, |
| 38 | + ); |
| 39 | + $proc = proc_open($cmd, [1 => ['pipe', 'w'], 2 => ['redirect', 1]], $pipes); |
| 40 | + echo stream_get_contents($pipes[1]); |
| 41 | + proc_close($proc); |
| 42 | +} |
| 43 | + |
| 44 | +function system_ids(string $cache): int { |
| 45 | + $pattern = PHP_OS_FAMILY === 'Windows' ? '/*/*' : '/*'; |
| 46 | + return count(glob($cache . $pattern, GLOB_ONLYDIR)); |
| 47 | +} |
| 48 | + |
| 49 | +write_script($script, 1); |
| 50 | +run($cache, $script, ''); |
| 51 | +var_dump(system_ids($cache)); |
| 52 | + |
| 53 | +write_script($script, 2); |
| 54 | +echo "--- Same environment reuses the cache ---\n"; |
| 55 | +run($cache, $script, ''); |
| 56 | +var_dump(system_ids($cache)); |
| 57 | + |
| 58 | +echo "--- Shifted function table ---\n"; |
| 59 | +run($cache, $script, 'zend_version'); |
| 60 | +var_dump(system_ids($cache)); |
| 61 | + |
| 62 | +echo "--- Disabled function ---\n"; |
| 63 | +run($cache, $script, 'str_repeat'); |
| 64 | +var_dump(system_ids($cache)); |
| 65 | + |
| 66 | +write_script($script, 3); |
| 67 | +echo "--- Each environment reuses its own cache ---\n"; |
| 68 | +run($cache, $script, 'zend_version'); |
| 69 | +run($cache, $script, 'str_repeat'); |
| 70 | +run($cache, $script, ''); |
| 71 | +var_dump(system_ids($cache)); |
| 72 | +?> |
| 73 | +--CLEAN-- |
| 74 | +<?php |
| 75 | +$cache = __DIR__ . '/' . 'gh17733'; |
| 76 | +if (is_dir($cache)) { |
| 77 | + $it = new RecursiveIteratorIterator( |
| 78 | + new RecursiveDirectoryIterator($cache, FilesystemIterator::SKIP_DOTS), |
| 79 | + RecursiveIteratorIterator::CHILD_FIRST |
| 80 | + ); |
| 81 | + foreach ($it as $file) { |
| 82 | + $file->isDir() ? rmdir($file->getPathname()) : unlink($file->getPathname()); |
| 83 | + } |
| 84 | + rmdir($cache); |
| 85 | +} |
| 86 | +?> |
| 87 | +--EXPECT-- |
| 88 | +v1 |
| 89 | +string(6) "ababab" |
| 90 | +int(1) |
| 91 | +--- Same environment reuses the cache --- |
| 92 | +v1 |
| 93 | +string(6) "ababab" |
| 94 | +int(1) |
| 95 | +--- Shifted function table --- |
| 96 | +v2 |
| 97 | +string(6) "ababab" |
| 98 | +int(2) |
| 99 | +--- Disabled function --- |
| 100 | +v2 |
| 101 | +Error: Call to undefined function str_repeat() |
| 102 | +int(3) |
| 103 | +--- Each environment reuses its own cache --- |
| 104 | +v2 |
| 105 | +string(6) "ababab" |
| 106 | +v2 |
| 107 | +Error: Call to undefined function str_repeat() |
| 108 | +v1 |
| 109 | +string(6) "ababab" |
| 110 | +int(3) |
0 commit comments