mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Fix GH-17246: GC during SCCP causes segfault
This bug happens because of a nested `SHM_UNPROTECT()` sequence.
In particular:
```
unprotect memory at ext/opcache/ZendAccelerator.c:2127
protect memory at ext/opcache/ZendAccelerator.c:2160
unprotect memory at ext/opcache/ZendAccelerator.c:2164
unprotect memory at ext/opcache/jit/zend_jit_trace.c:7464
^^^ Nested
protect memory at ext/opcache/jit/zend_jit_trace.c:7591
^^^ Problem is here: it should not protect again due to the nested unprotect
protect memory at ext/opcache/ZendAccelerator.c:2191
^^^ This one should actually protect, not the previous one
```
The reason this nesting happen is because:
1. We try to include the script, this eventually calls `cache_script_in_shared_memory`
2. `zend_optimize_script` will eventually run SCCP as part of the DFA pass.
3. SCCP will try to replace constants, but can also run destructors when a partial array is destructed here:
4e9cde758e/Zend/Optimizer/sccp.c (L2387-L2389)
In this case, this destruction invokes the GC which invokes the tracing JIT,
leading to the nested unprotects.
This patch disables the GC to prevent invoking user code, as user code
is not supposed to run during the optimizer pipeline.
Closes GH-17249.
Co-authored-by: Dmitry Stogov <dmitry@zend.com>
This commit is contained in:
parent
a7f7e169d6
commit
df6db27580
4 changed files with 51 additions and 0 deletions
1
NEWS
1
NEWS
|
@ -51,6 +51,7 @@ PHP NEWS
|
|||
|
||||
- Opcache:
|
||||
. opcache_get_configuration() properly reports jit_prof_threshold. (cmb)
|
||||
. Fixed bug GH-17246 (GC during SCCP causes segfault). (Dmitry)
|
||||
|
||||
- PCNTL:
|
||||
. Fix memory leak in cleanup code of pcntl_exec() when a non stringable
|
||||
|
|
|
@ -2153,7 +2153,10 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
|
|||
*/
|
||||
from_shared_memory = false;
|
||||
if (persistent_script) {
|
||||
/* See GH-17246: we disable GC so that user code cannot be executed during the optimizer run. */
|
||||
bool orig_gc_state = gc_enable(false);
|
||||
persistent_script = cache_script_in_shared_memory(persistent_script, key, &from_shared_memory);
|
||||
gc_enable(orig_gc_state);
|
||||
}
|
||||
|
||||
/* Caching is disabled, returning op_array;
|
||||
|
|
8
ext/opcache/tests/jit/gh17246.inc
Normal file
8
ext/opcache/tests/jit/gh17246.inc
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
// Need to cause a trace exit, so extend non existent class
|
||||
class MyXSLTProcessor extends NonExistentClass {
|
||||
public function registerCycle() {
|
||||
[[$this]]; // Non trivial array
|
||||
}
|
||||
}
|
39
ext/opcache/tests/jit/gh17246.phpt
Executable file
39
ext/opcache/tests/jit/gh17246.phpt
Executable file
|
@ -0,0 +1,39 @@
|
|||
--TEST--
|
||||
GH-17246 (Nested shm protections cause segfault)
|
||||
--EXTENSIONS--
|
||||
opcache
|
||||
--INI--
|
||||
opcache.protect_memory=1
|
||||
opcache.jit_buffer_size=32M
|
||||
opcache.jit=1254
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Test
|
||||
{
|
||||
private $field;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->field = function() {};
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
// Necessary because we need to invoke tracing JIT during destruction
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = 0; $i < 10000; ++$i) {
|
||||
$obj = new Test();
|
||||
}
|
||||
|
||||
require __DIR__.'/gh17246.inc';
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Uncaught Error: Class "NonExistentClass" not found in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): require()
|
||||
#1 {main}
|
||||
thrown in %s on line %d
|
Loading…
Add table
Add a link
Reference in a new issue