|
| 1 | +<?php |
| 2 | + |
| 3 | +/** |
| 4 | + * Minimal self-contained reproduction for PHP JIT tracing CV corruption bug. |
| 5 | + * https://github.com/php/php-src/issues/21746 |
| 6 | + * |
| 7 | + * Run: php -d opcache.enable_cli=1 -d opcache.jit=tracing tests/jit_type_error.php |
| 8 | + */ |
| 9 | + |
| 10 | +final class LR { |
| 11 | + public static function p(mixed $cx, string $name, mixed $context, array $hash, string $indent): string { |
| 12 | + try { $result = ''; } finally {} |
| 13 | + $lines = explode("\n", $result); |
| 14 | + foreach ($lines as $i => $_) { if ($i === 0) break; } |
| 15 | + return $result; |
| 16 | + } |
| 17 | + public static function hbbch(array $positional, ?\Closure $cb): string { |
| 18 | + if ($cb && is_array($positional[0] ?? null)) foreach ($positional[0] as $v) $cb($v); |
| 19 | + return ''; |
| 20 | + } |
| 21 | +} |
| 22 | + |
| 23 | +$renderCode = <<<'PHP' |
| 24 | +<?php |
| 25 | +return function (mixed $in = null, array $options = []) { |
| 26 | + $a = null; |
| 27 | + $b = ''; |
| 28 | + $c = ''; |
| 29 | + $d = fn() => ''; |
| 30 | + $e = fn() => ''; |
| 31 | + $f = fn() => ''; |
| 32 | + $g = fn() => ''; |
| 33 | + return '' |
| 34 | + .LR::p(null, '', null, [], '') |
| 35 | + .LR::hbbch([null], $d) |
| 36 | + .LR::hbbch([null], $e) |
| 37 | + .LR::hbbch([null], $f) |
| 38 | + .LR::hbbch([null], $g); |
| 39 | +}; |
| 40 | +PHP; |
| 41 | + |
| 42 | +// file_put_contents is required, not just require of an existing file: the fresh mtime |
| 43 | +// triggers opcache's file_update_protection, compiling the closure into a transient |
| 44 | +// (non-cached) opcode array. The JIT traces this differently, forming the cross-frame |
| 45 | +// STOP_LINK path that triggers the CV slot corruption. |
| 46 | +$renderFile = __DIR__ . '/jit_crash_code.php'; |
| 47 | +file_put_contents($renderFile, $renderCode); |
| 48 | +$renderer = require $renderFile; |
| 49 | + |
| 50 | +for ($r = 0; $r < 70; $r++) { |
| 51 | + echo "Render $r..."; |
| 52 | + $renderer(); |
| 53 | + echo "OK\n"; |
| 54 | +} |
| 55 | + |
| 56 | +echo "--- Completed without crash (JIT may not have been active) ---\n"; |
0 commit comments