#49090: fix: crash when attempting to resolve modules during process exit
Description of Change
_Analysis based on crash dumps_
Only listing the interesting threads below
Crashed Thread: 12
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Termination Reason: Namespace SIGNAL, Code 6 Abort trap: 6
Terminating Process: Electron [56936]
Application Specific Information:
abort() called
Thread 0:: Dispatch queue: com.apple.main-thread
0 libsystem_kernel.dylib 0x190fe39b8 __ulock_wait + 8
1 libsystem_pthread.dylib 0x19102606c _pthread_join + 608
2 Electron Framework 0x119600a5c uv_thread_join + 20
3 Electron Framework 0x119a1b2ac node::worker::Worker::JoinThread() + 60
4 Electron Framework 0x11988d5b4 node::Environment::stop_sub_worker_contexts() + 216
5 Electron Framework 0x119827460 node::DefaultProcessExitHandler(node::Environment*, int) + 44
6 Electron Framework 0x11988d4c0 node::Environment::Exit(node::ExitCode) + 412
7 ??? 0x138d90278 ???
8 ??? 0x138d8e270 ???
9 ??? 0x138d8e270 ???
10 ??? 0x1310e7734 ???
11 ??? 0x1310f8b7c ???
12 ??? 0x138d8b228 ???
13 ??? 0x138d8ae74 ???
14 Electron Framework 0x1172db5cc v8::Function::Call(v8::Isolate*, v8::Local<v8::Context>, v8::Local<v8::Value>, int, v8::Local<v8::Value>*) + 3576
15 Electron Framework 0x1172da968 v8::Function::Call(v8::Isolate*, v8::Local<v8::Context>, v8::Local<v8::Value>, int, v8::Local<v8::Value>*) + 404
16 Electron Framework 0x11988bf20 node::Environment::RunTimers(uv_timer_s*) + 356
17 Electron Framework 0x1195f0ea0 uv__run_timers + 144
18 Electron Framework 0x1195f4320 uv_run + 600
19 Electron Framework 0x119821b64 node::SpinEventLoopInternal(node::Environment*) + 360
20 Electron Framework 0x119822a60 node::SpinEventLoop(node::Environment*) + 12
21 Electron Framework 0x11960ad38 ElectronInitializeICUandStartNode + 17784
22 Electron Framework 0x1196068cc ElectronInitializeICUandStartNode + 268
23 dyld 0x190c82b98 start + 6076
...
Thread 12 Crashed:
0 libsystem_kernel.dylib 0x190fea388 __pthread_kill + 8
1 libsystem_pthread.dylib 0x191023848 pthread_kill + 296
2 libsystem_c.dylib 0x190f2c9e4 abort + 124
3 Electron Framework 0x11990195c node::OnFatalError(char const*, char const*) + 252
4 Electron Framework 0x11a5f9318 v8::PropertyDescriptor::set() const + 4540844
5 Electron Framework 0x11995ec20 node::modules::BindingData::GetPackageJSON(node::Realm*, std::__Cr::basic_string_view<char, std::__Cr::char_traits<char>>, node::modules::BindingData::ErrorContext*) + 5712
6 Electron Framework 0x119961b54 void node::modules::BindingData::GetPackageScopeConfig<false>(v8::FunctionCallbackInfo<v8::Value> const&) + 1604
7 ??? 0x138d90278 ???
8 ??? 0x138d8e270 ???
9 ??? 0x138d8e270 ???
10 ??? 0x131054f88 ???
11 ??? 0x138d8e270 ???
12 ??? 0x138d8e270 ???
13 ??? 0x138d8e270 ???
14 ??? 0x131054cb4 ???
15 ??? 0x131053dfc ???
16 ??? 0x13104ee74 ???
17 ??? 0x131059d64 ???
18 ??? 0x13104ee74 ???
19 ??? 0x131052d74 ???
20 ??? 0x131051b54 ???
21 ??? 0x131048f1c ???
22 ??? 0x138d8e270 ???
23 ??? 0x138d8b228 ???
24 ??? 0x138d8ae74 ???
25 Electron Framework 0x1172db5cc v8::Function::Call(v8::Isolate*, v8::Local<v8::Context>, v8::Local<v8::Value>, int, v8::Local<v8::Value>*) + 3576
26 Electron Framework 0x1172da968 v8::Function::Call(v8::Isolate*, v8::Local<v8::Context>, v8::Local<v8::Value>, int, v8::Local<v8::Value>*) + 404
27 Electron Framework 0x11982115c node::InternalMakeCallback(node::Environment*, v8::Local<v8::Object>, v8::Local<v8::Object>, v8::Local<v8::Function>, int, v8::Local<v8::Value>*, node::async_context, v8::Local<v8::Value>) + 504
28 Electron Framework 0x11983002c node::AsyncWrap::MakeCallback(v8::Local<v8::Function>, int, v8::Local<v8::Value>*) + 240
29 Electron Framework 0x1199550ec node::worker::MessagePort::OnMessage(node::worker::MessagePort::MessageProcessingMode) + 800
30 Electron Framework 0x1195f3d74 uv__async_fork + 772
31 Electron Framework 0x119606030 uv__io_poll + 1336
32 Electron Framework 0x1195f4240 uv_run + 376
33 Electron Framework 0x119821b64 node::SpinEventLoopInternal(node::Environment*) + 360
34 Electron Framework 0x119a1afb0 node::worker::Worker::Run() + 2008
35 Electron Framework 0x119a1fa14 _register_external_reference_worker(node::ExternalReferenceRegistry*) + 4712
36 libsystem_pthread.dylib 0x191023bc8 _pthread_start + 136
37 libsystem_pthread.dylib 0x19101eb80 thread_start + 8
(lldb) frame select 5
frame #5: 0x000000010d377c80 Electron Framework`node::modules::BindingData::GetPackageJSON(node::Realm*, std::__Cr::basic_string_view<char, std::__Cr::char_traits<char>>, node::modules::BindingData::ErrorContext*) at v8-local-handle.h:770
General Purpose Registers:
x8 = 0x0000000000020019
x19 = 0x0000011800091598
x20 = 0x0000011800091560
x21 = 0x0000011800a7dff0
x22 = 0x0000011800a7dff0
x23 = 0x0000011800a7c060
x24 = 0x0000011800ae0000
x25 = 0x0000011800121f80
x26 = 0x0000000173bd87e0
x27 = 0x0000000173bd8858
x28 = 0x0000000000000064
fp = 0x0000000173bd8d10
lr = 0x000000010d377c80 Electron Framework`node::modules::BindingData::GetPackageJSON(node::Realm*, std::__Cr::basic_string_view<char, std::__Cr::char_traits<char>>, node::modules::BindingData::ErrorContext*) + 5852 at v8-local-handle.h
sp = 0x0000000173bd8720
pc = 0x000000010d377c80 Electron Framework`node::modules::BindingData::GetPackageJSON(node::Realm*, std::__Cr::basic_string_view<char, std::__Cr::char_traits<char>>, node::modules::BindingData::ErrorContext*) + 5852 at v8-local-handle.h
(lldb) disassemble
...
0x10d37683c <+664>: bl 0x10b07ad08 ; v8::Function::Call(v8::Local<v8::Context>, v8::Local<v8::Value>, int, v8::Local<v8::Value>*) at api.cc:5384
0x10d376840 <+668>: mov x22, x0
0x10d376844 <+672>: cbz x0, 0x10d377c7c ; <+5848> at v8-local-handle.h:770
...
0x10d377c7c <+5848>: bl 0x10dede2c4 ; v8::api_internal::ToLocalEmpty() at api.h:217
-> 0x10d377c80 <+5852>: b 0x10d376848 ; <+676> at v8-internal.h:1787
We are attempting to resolve an empty handle returned by modules_read_file_sync->Call that leads to the Api failure check. As to why this happens, with a try catch scope added
(v8::TryCatch) try_catch = {
i_isolate_ = 0x0000011800ac0000
next_ = nullptr
exception_ = 0x000009e000020019
message_obj_ = 0x000009e000020001
js_stack_comparable_address_ = 6236767984
is_verbose_ = false
can_continue_ = false
capture_message_ = true
rethrow_ = false
}
(lldb) command script import src/v8/tools/lldb_commands.py
(lldb) job 0x000009e000020001
0x09e000020001 <the_hole_value>
(lldb) job 0x000009e000020019
0x09e000020019 <termination_exception>
We got a termination exception from V8 when the function call was executed that results in the empty handle for result, thread 0 shows we are in the shutdown phase which calls isolate->TerminateExecution when the worker threads are stopped setting thread local interrupt flags. The interrupt handler from the worker thread raises the exception when attempting to enter JS.
The change in this PR makes the JS re-entrancy resilient to this exception. cc @indutny-signal
However, the stop call from the Worker::Exit is ignoring the flags we would ideally be calling for main thread of all our processes, ex:
electron/shell/app/node_main.cc
Line 316 in 1ff8e80
Release Notes
Notes: fix crash when attempting to resolve modules during process exit
Backports
Semver Impact
Semantic Versioning helps users understand the impact of updates:
- Major (X.y.z): Breaking changes that may require code modifications
- Minor (x.Y.z): New features that maintain backward compatibility
- Patch (x.y.Z): Bug fixes that don't change the API
- None: Changes that don't affect using facing parts of Electron