phpv8 / v8js

V8 Javascript Engine for PHP — This PHP extension embeds the Google V8 Javascript Engine
http://pecl.php.net/package/v8js
MIT License
1.83k stars 200 forks source link

Fatal error inside PHP callback from JS results in segfault on cleanup #87

Closed rosmo closed 10 years ago

rosmo commented 10 years ago

A fatal error in PHP called from JS results in a segmentation fault while cleaning up.

$v8 = new V8Js();
$v8->phpFatalError = function () {
  $foo = NULL;
  $foo->bar();
};
$v8->executeString('PHP.phpFatalError();', 'crash.js');

Output:

PHP Fatal error:  Call to a member function bar() on a non-object in fatal-error.phpt on line 5

Fatal error: Call to a member function bar() on a non-object in fatal-error.phpt on line 5
Segmentation fault (core dumped)

Stack trace:

(gdb) bt
#0  0x00007fffeeb80ae1 in v8::internal::ExitFrame::Iterate(v8::internal::ObjectVisitor*) const () from /var/v8/lib/libv8.so
#1  0x00007fffeec605b9 in v8::internal::Isolate::Iterate(v8::internal::ObjectVisitor*, v8::internal::ThreadLocalTop*) () from /var/v8/lib/libv8.so
#2  0x00007fffeebadec9 in v8::internal::Heap::IterateStrongRoots(v8::internal::ObjectVisitor*, v8::internal::VisitMode) () from /var/v8/lib/libv8.so
#3  0x00007fffeec46528 in v8::internal::IncrementalMarking::StartMarking(v8::internal::IncrementalMarking::CompactionFlag) () from /var/v8/lib/libv8.so
#4  0x00007fffeec46831 in v8::internal::IncrementalMarking::Start(v8::internal::IncrementalMarking::CompactionFlag) () from /var/v8/lib/libv8.so
#5  0x00007fffeebbb25e in v8::internal::Heap::IdleNotification(int) () from /var/v8/lib/libv8.so
#6  0x00007fffef291462 in php_v8js_free_storage (object=0x7ffff7fc7680) at /usr/share/crasman/v8js/v8js.cc:624
#7  0x0000000000800f08 in zend_objects_store_free_object_storage (objects=0x10407e0) at /usr/src/debug/php-5.4.26/Zend/zend_objects_API.c:97
#8  0x00000000007ccfb3 in shutdown_executor () at /usr/src/debug/php-5.4.26/Zend/zend_execute_API.c:295
#9  0x00000000007d9e72 in zend_deactivate () at /usr/src/debug/php-5.4.26/Zend/zend.c:934
#10 0x000000000077ce2c in php_request_shutdown (dummy=<value optimized out>) at /usr/src/debug/php-5.4.26/main/main.c:1808
#11 0x0000000000883737 in do_cli (argc=2, argv=0x7fffffffe588) at /usr/src/debug/php-5.4.26/sapi/cli/php_cli.c:1172
#12 0x0000000000884af8 in main (argc=2, argv=0x7fffffffe588) at /usr/src/debug/php-5.4.26/sapi/cli/php_cli.c:1365

I tried some workarounds to this, like removing the locking before calling IdleNotification in php_v8js_free_storage but that results in memory leaks. Adding v8::TerminateExecution before IdleNotification doesn't help either. I also tried moving the whole script->Run() into a new thread, but basically the code never returns from it.

stesie commented 10 years ago

hey there,

I've had a successful go at that one; ... after some failures :)

The problem is, that php uses a longjmp to leave the whole execution stack, which effectively simply jumps out of v8's executeString and all its isolation fluff ... leading to a crash on next garbage collection.

The hack from above, yet just kind of a "proof of concept", overwrites zend_error_cb callback to catch fatal error handling, then first leaves all stack frames down to the JS -> PHP transition, re-enters the isolate there, calls v8::TerminateExecution and returns back into JS context. After termination of v8 execution the PHP fatal error is "re-thrown"

The current implementation has a few flaws:

... which should be easy going to implement, so I'll follow up, ... just to let you know for the moment :)

... but the example from above at least doesn't crash the engine anymore :D

cheers ~stesie