Page MenuHomePhabricator

LuaSandbox segmentation fault on PHP 8 during request shutdown
Closed, ResolvedPublicBUG REPORT

Description

We observed a segmentation fault with LuaSandbox 4.1.0 on PHP 8.0.25, after upgrading from PHP 7.3.33. This occurs at a low rate (single digit frequence / day):

Core was generated by `php-fpm:'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x000055d84eb04838 in zend_objects_store_free_object_storage (objects=objects@entry=0x55d84f7830a8 <executor_globals+840>, fast_shutdown=fast_shutdown@entry=true)
    at /usr/src/php/Zend/zend_objects_API.c:102
102						if (obj->handlers->free_obj != zend_object_std_dtor) {
[Current thread is 1 (Thread 0x7f58e6e83980 (LWP 768814))]
(gdb) bt
#0  0x000055d84eb04838 in zend_objects_store_free_object_storage (objects=objects@entry=0x55d84f7830a8 <executor_globals+840>, fast_shutdown=fast_shutdown@entry=true)
    at /usr/src/php/Zend/zend_objects_API.c:102
#1  0x000055d84ea6e5df in shutdown_executor () at /usr/src/php/Zend/zend_execute_API.c:339
#2  0x000055d84ea7db29 in zend_deactivate () at /usr/src/php/Zend/zend.c:1239
#3  0x000055d84ea18b49 in php_request_shutdown (dummy=dummy@entry=0x0) at /usr/src/php/main/main.c:1853
#4  0x000055d84e7a3ada in main (argc=<optimized out>, argv=<optimized out>) at /usr/src/php/sapi/fpm/fpm/fpm_main.c:1942
(gdb) printf "%s\n", obj->ce->name->val
LuaSandbox
(gdb) print obj->handlers
$1 = (const zend_object_handlers *) 0x0

Somehow obj->handlers is an apparent null pointer. I don't know if this is a PHP bug or a LuaSandbox bug and unfortunately have not yet managed to create a consistent reproducer.
https://github.com/php/php-src/commit/71d6899e535d960d9b43d75ed2b7073288c13b59 seems like a suspicious related commit that is only in PHP >= 7.4...

Event Timeline

One thing you can try, to find a reproduction procedure from a core dump, is to dump the request data:

Core was generated by `php-fpm: pool www'.
Program terminated with signal SIGTRAP, Trace/breakpoint trap.
#0  php_request_shutdown (dummy=dummy@entry=0x0) at ./main/main.c:1784
1784    ./main/main.c: No such file or directory.
[Current thread is 1 (Thread 0x7f5c84e5aa00 (LWP 281))]
(gdb) source /srv/php/core/.gdbinit
(gdb) printzv &core_globals.http_globals[3]
[0x55b69526d010] (refcount=2) array:     Hash(51)[0x7f5c820581c0]: {
      [0] "USER" => [0x7f5c82062c00] (refcount=2) string: www-data
      [1] "HOME" => [0x7f5c82062c20] (refcount=2) string: /var/www
...

http_globals[3] is TRACK_VARS_SERVER, i.e. $_SERVER.

Then you can run the request again by feeding that back into curl. If that's not enough, maybe run it 1000 times.

If the request calls the parser, you may have to suppress the parser cache to reproduce the bug. Maybe get the cache key out of the debug log, and delete it before each request.

Thanks a lot, Tim :)- using your advice, I inspected a more recent dump and found that it was a request where PHP was exceeding its configured memory limit while it was parsing a rather complex page. Going by that, I managed to narrow it down to a consistent reproducer:

--TEST--
Memory limit exceeded during sandbox init
--INI--
memory_limit=2M
--FILE--
<?php
$buf = str_repeat('a', 1000000);
$sandboxes = [];
for ($i = 0; $i < 100; $i++) {
    $sandboxes[] = new LuaSandbox();
}
?>
--EXPECTF--
Fatal error: Allowed memory size of 2097152 bytes exhausted%s(tried to allocate %d bytes) in %s on line %d

This seems to reliably segfault on PHP 8.2. It seems that this is because the assignment of standard handlers sandbox->std.handlers = &luasandbox_object_handlers; occurs only after the sandbox state is allocated, which uses the Zend allocator and so may trigger PHP's memory limiter. Then the process dumps core during the shutdown phase.

I'll make a patch.

I think this is not a PHP 8.0 specific issue, it'd likely happen on earlier versions as well. We just happened to notice it because we were on the lookout for core dumps while investigating unrelated JIT issues.

Change 938925 had a related patch set uploaded (by TK-999; author: TK-999):

[mediawiki/php/luasandbox@master] Fix segmentation fault when memory limit is exceeded in LuaSandbox init

https://gerrit.wikimedia.org/r/938925

Change 938925 merged by jenkins-bot:

[mediawiki/php/luasandbox@master] Fix segmentation fault when memory limit is exceeded in LuaSandbox init

https://gerrit.wikimedia.org/r/938925

Thanks @TK-999!

It would seem that there's a critical section between zend_object_std_init() and the assignment of handlers, during which time it's unsafe to call emalloc().

I grepped the PHP in-tree extensions, and it looks like there are a few that have the same problem. I'll probably file something upstream about this.

tstarling claimed this task.

This is fixed and released in LuaSandbox 4.1.1.