Page MenuHomePhabricator

PCRE segfaults after "fork()" on PHP 7.3+ on MacOS with "pcre.jit" enabled
Open, WishlistPublic

Description

See PHI1277. Originally, see T13232#243866.

On MacOS with PHP 7.3.1 (and apparently other versions of 7.3.x), this script segfaults reliably:

<?php

// Call "preg_match()" before we fork.
preg_match('//', '');

// Now, fork.
$pid = pcntl_fork();
if ($pid === -1) {
  die('Unable to fork.');
}

// We're the parent, so wait for the child to exit.
if ($pid) {
  $status = 0;
  pcntl_waitpid($pid, $status);
  printf("Child exited with status %d.\n", $status);
  exit(0);
}

// We're the child. Do nothing, we'll segfault on exit.

The backtrace looks like this on my system:

(lldb) bt
* thread #1, stop reason = signal SIGSTOP
  * frame #0: 0x00000001074db459 php`sljit_free_code [inlined] sljit_free_exec(ptr=0x00000001087c3010) at sljitExecAllocator.c:260:28 [opt]
    frame #1: 0x00000001074db449 php`sljit_free_code(code=0x00000001087c3010) at sljitLir.c:501 [opt]
    frame #2: 0x00000001074db39d php`_pcre2_jit_free_8(executable_jit=0x00007ff127e8c640, memctl=0x00007ff127e8c500) at pcre2_jit_misc.c:92:5 [opt]
    frame #3: 0x00000001074b373f php`php_pcre2_code_free(code=0x00007ff127e8c500) at pcre2_compile.c:1189:5 [opt]
    frame #4: 0x000000010753f726 php`php_free_pcre_cache(data=<unavailable>) at php_pcre.c:148:2 [opt]
    frame #5: 0x00000001078e9129 php`zend_hash_destroy(ht=0x000000010819d560) at zend_hash.c:1428:6 [opt]
    frame #6: 0x000000010753b4b9 php`zm_globals_dtor_pcre(pcre_globals=<unavailable>) at php_pcre.c:275:2 [opt]
    frame #7: 0x00000001078ded76 php`module_destructor(module=0x00007ff127d0a420) at zend_API.c:2586:4 [opt]
    frame #8: 0x00000001078d63a1 php`module_destructor_zval(zv=<unavailable>) at zend.c:745:2 [opt]
    frame #9: 0x00000001078e9d38 php`zend_hash_graceful_reverse_destroy at zend_hash.c:1181:3 [opt]
    frame #10: 0x00000001078e9bb5 php`zend_hash_graceful_reverse_destroy [inlined] _zend_hash_del_el(ht=<unavailable>, idx=<unavailable>, p=<unavailable>) at zend_hash.c:1204 [opt]
    frame #11: 0x00000001078e9b72 php`zend_hash_graceful_reverse_destroy(ht=0x00000001081a2a98) at zend_hash.c:1658 [opt]
    frame #12: 0x00000001078d6484 php`zend_shutdown at zend.c:976:2 [opt]
    frame #13: 0x000000010786e03b php`php_module_shutdown at main.c:2489:2 [opt]
    frame #14: 0x00000001079986f6 php`main(argc=<unavailable>, argv=0x00007ffee8791a88) at php_cli.c:1404:3 [opt]
    frame #15: 0x00007fff6e41c3d5 libdyld.dylib`start + 1
    frame #16: 0x00007fff6e41c3d5 libdyld.dylib`start + 1

Various tales from around the internet:

Setting pcre.jit = 0 works around this, and the setting can be changed at runtime, so that seems like a clear-cut way forward.

Event Timeline

epriestley created this task.

I built my PHP from source in T13232 so this is presumably not a Homebrew issue.

Although there are a lot of GitHub issues, I can't actually find a bugs.php.net issue which looks applicable.

Ideally, the next step is probably to write a C program in that links PCRE directly and see if this can be reproduced to a bug in PCRE, or if the bug is in the PHP/PCRE bindings. I'm in pretty far over my head at this point, though, being a simple man who programs in easy-to-use, mostly-memory-safe languages.

epriestley lowered the priority of this task from Normal to Low.May 23 2019, 11:54 PM

The changelog for PHP 7.3.6 mentions "Fixed possible crashes, because of inconsistent PCRE cache and opcache SHM reset." which might be relevant, but no bug link so who knows.

epriestley lowered the priority of this task from Low to Wishlist.Jun 17 2019, 6:02 PM

The change above seems to have worked around things, I'll leave this open until we can confirm that PHP 7.3.6 or some later version fixes the underlying issue.