Skip to content

Commit

Permalink
Fix phpGH-17246: GC during SCCP causes segfault
Browse files Browse the repository at this point in the history
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:

https://github.com/php/php-src/blob/4e9cde758eadf30cc4d596d6398c2c34c64197b4/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 phpGH-17249.

Co-authored-by: Dmitry Stogov <[email protected]>
  • Loading branch information
nielsdos and dstogov committed Dec 24, 2024
1 parent a7f7e16 commit df6db27
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 0 deletions.
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions ext/opcache/ZendAccelerator.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions ext/opcache/tests/jit/gh17246.inc
Original file line number Diff line number Diff line change
@@ -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 changes: 39 additions & 0 deletions ext/opcache/tests/jit/gh17246.phpt
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit df6db27

Please sign in to comment.