Skip to content

Nested shm protections cause segfault #17246

Closed

Description

@YuanchengJiang

Description

The following code:

<?php
class Cycle
{
public function __construct()
{
$this->thing = function() use($obj) {};
}
public function __destruct()
{
}
}
for ($i = 0; $i < 10000; ++$i) {
$obj = new Cycle();
}
require __DIR__ . '/xpath_callables.inc';

Resulted in this output:

AddressSanitizer:DEADLYSIGNAL
=================================================================
==1400489==ERROR: AddressSanitizer: SEGV on unknown address 0x0000418f412c (pc 0x7fce9acccbfa bp 0x7fff6704fae0 sp 0x7fff6704f6a0 T0)
==1400489==The signal is caused by a WRITE memory access.
    #0 0x7fce9acccbfa in accel_new_interned_string /home/phpfuzz/WorkSpace/flowfusion/php-src/ext/opcache/ZendAccelerator.c:542:39
    #1 0x7fce9ac16cd0 in zend_accel_persist_class_table_calc /home/phpfuzz/WorkSpace/flowfusion/php-src/ext/opcache/zend_persist_calc.c:581:3
    #2 0x7fce9ac12ffe in zend_accel_script_persist_calc /home/phpfuzz/WorkSpace/flowfusion/php-src/ext/opcache/zend_persist_calc.c:632:2
    #3 0x7fce9acfe31f in cache_script_in_shared_memory /home/phpfuzz/WorkSpace/flowfusion/php-src/ext/opcache/ZendAccelerator.c:1614:16
    #4 0x7fce9aceab8a in persistent_compile_file /home/phpfuzz/WorkSpace/flowfusion/php-src/ext/opcache/ZendAccelerator.c:2168:24
    #5 0x49f74b9 in compile_filename /home/phpfuzz/WorkSpace/flowfusion/php-src/Zend/zend_language_scanner.l:704:11
    #6 0x46fa22b in zend_include_or_eval /home/phpfuzz/WorkSpace/flowfusion/php-src/Zend/zend_execute.c:5178:19
    #7 0x42d3093 in ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER /home/phpfuzz/WorkSpace/flowfusion/php-src/Zend/zend_vm_execute.h:5247:17
    #8 0x3f8fd47 in execute_ex /home/phpfuzz/WorkSpace/flowfusion/php-src/Zend/zend_vm_execute.h:58595:7
    #9 0x3f91fcc in zend_execute /home/phpfuzz/WorkSpace/flowfusion/php-src/Zend/zend_vm_execute.h:64247:2
    #10 0x4d2e029 in zend_execute_script /home/phpfuzz/WorkSpace/flowfusion/php-src/Zend/zend.c:1934:3
    #11 0x3534c0a in php_execute_script_ex /home/phpfuzz/WorkSpace/flowfusion/php-src/main/main.c:2577:13
    #12 0x3535d48 in php_execute_script /home/phpfuzz/WorkSpace/flowfusion/php-src/main/main.c:2617:9
    #13 0x4d4233a in do_cli /home/phpfuzz/WorkSpace/flowfusion/php-src/sapi/cli/php_cli.c:938:5
    #14 0x4d3c81f in main /home/phpfuzz/WorkSpace/flowfusion/php-src/sapi/cli/php_cli.c:1313:18
    #15 0x7fcea25a9d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #16 0x7fcea25a9e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #17 0x605934 in _start (/home/phpfuzz/WorkSpace/flowfusion/php-src/sapi/cli/php+0x605934)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/phpfuzz/WorkSpace/flowfusion/php-src/ext/opcache/ZendAccelerator.c:542:39 in accel_new_interned_string
==1400489==ABORTING

To reproduce:

-d "opcache.protect_memory=1" -d "zwfusion/php-src/modules/opcache.so" -d "opcache.enable_cli=1" -d "opcache.jit=1254"

PHP Version

nightly

Operating System

No response

Activity

  1. devnexen commented on Dec 23, 2024

    @devnexen
    Member

    can't reproduce this one.

  2. ndossche commented on Dec 23, 2024

    @ndossche
    Member

    It reproduces for me.

  3. ndossche commented on Dec 23, 2024

    @ndossche
    Member

    But only reproduces with protect_memory on. I'll try to take a look.

  4. ndossche commented on Dec 23, 2024

    @ndossche
    Member

    Got distracted.

    Anyway, this happens because of a nested SHM_UNPROTECT() sequence.
    In particular we have the following sequence. Pay attention to the comments marked with ^^^.

    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:

    php-src/Zend/Optimizer/sccp.c

    Lines 2387 to 2389 in 4e9cde7

    if (!Z_DELREF(ctx->values[i])) {
    zend_array_destroy(Z_ARR(ctx->values[i]));
    }

    In this case, this destruction invokes the tracing JIT, leading to the nested unprotects.

    I see two possible solutions:

    1. Keep a "counter" to check the nesting level of the unprotections. Only unprotect when the nesting level is 0, and only protect when the nesting level reaches 0 again.
    2. Do not JIT during SCCP.

    I think I prefer option 1 because it's more general and also not JITting would be a leaky abstraction.

  5. changed the title [-]SEGV ext/opcache/ZendAccelerator.c:542:39 in accel_new_interned_string[/-] [+]Nested shm protections cause segfault[/+] on Dec 23, 2024
  6. added 3 commits that reference this issue on Dec 24, 2024
    fa1cd7f
    df6db27
    e45fdd2
  7. added a commit that references this issue on Dec 24, 2024
    db54a83
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions