Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UAF in gin_helper::CallbackHolderBase::SecondWeakCallback (integration test crash) #192119

Closed
bpasero opened this issue Sep 4, 2023 · 12 comments · Fixed by #230184
Closed

UAF in gin_helper::CallbackHolderBase::SecondWeakCallback (integration test crash) #192119

bpasero opened this issue Sep 4, 2023 · 12 comments · Fixed by #230184
Assignees
Labels
bug Issue identified by VS Code Team member as probable bug electron Issues and items related to Electron freeze-slow-crash-leak VS Code crashing, performance, freeze and memory leak issues insiders-released Patch has been released in VS Code Insiders integration-test-failure linux Issues with VS Code on Linux macos Issues with VS Code on MAC/OS X upstream Issue identified as 'upstream' component related (exists outside of VS Code) verified Verification succeeded windows VS Code on Windows issues
Milestone

Comments

@bpasero
Copy link
Member

bpasero commented Sep 4, 2023

https://dev.azure.com/vscode/VSCode/_build/results?buildId=102349&view=results

crash-dump-linux-x64-integration-1.zip

@bpasero bpasero added the freeze-slow-crash-leak VS Code crashing, performance, freeze and memory leak issues label Sep 4, 2023
@deepak1556
Copy link
Collaborator

Operating system: Linux
                  5.15.0 -1042-azure #49~20.04.1-Ubuntu SMP Wed Jul 12 12:44:56 UTC 2023 x86_64
CPU: amd64
     family 6 model 106 stepping 6
     4 CPUs

GPU: UNKNOWN

Crash reason:  SIGSEGV /0x00000080
Crash address: 0x0
Process uptime: 10 seconds

Thread 0 (crashed)
 0  code-oss!gin_helper::CallbackHolderBase::SecondWeakCallback(v8::WeakCallbackInfo<gin_helper::CallbackHolderBase> const&) [function_template.cc : 33 + 0x1]
    rax = 0xf074a30178340000   rdx = 0x0000560587f54ca0
    rcx = 0x0000347800f7cae0   rbx = 0x00003478002abe20
    rsi = 0x0000347801a34540   rdi = 0x0000347801a34540
    rbp = 0x00007ffd31210f20   rsp = 0x00007ffd31210e18
     r8 = 0x00007ffd313f4080    r9 = 0x00000000000006e8
    r10 = 0x00007ffd313f4090   r11 = 0x000000000006dba6
    r12 = 0x0000347800329800   r13 = 0x0000000000000000
    r14 = 0x00007ffd31210ec0   r15 = 0x00007ffd31210ed8
    rip = 0x0000560587f54cb1
    Found by: given as instruction pointer in context
 1  code-oss!v8::internal::GlobalHandles::InvokeSecondPassPhantomCallbacks() [global-handles.cc : 865 + 0x5]
    rbx = 0x00003478002abe20   rbp = 0x00007ffd31210f20
    rsp = 0x00007ffd31210e20   r12 = 0x0000347800329800
    r13 = 0x0000000000000000   r14 = 0x00007ffd31210ec0
    r15 = 0x00007ffd31210ed8   rip = 0x00005605891ce7f8
    Found by: call frame info
 2  code-oss!node::PerIsolatePlatformData::RunForegroundTask(std::Cr::unique_ptr<v8::Task, std::Cr::default_delete<v8::Task>>) [node_platform.cc : 425 + 0x6]
    rbx = 0x00007ffd31210fe0   rbp = 0x00007ffd31210fa0
    rsp = 0x00007ffd31210f30   r12 = 0x000034780031e400
    r13 = 0x00007ffd31210f38   r14 = 0xaaaaaaaaaaaaaaaa
    r15 = 0x000034780038a318   rip = 0x000056058f08bffc
    Found by: call frame info
 3  code-oss!node::PerIsolatePlatformData::FlushForegroundTasksInternal() [node_platform.cc : 494 + 0xc]
    rbx = 0x000034780038a318   rbp = 0x00007ffd31211020
    rsp = 0x00007ffd31210fb0   r12 = 0x0000000000000000
    r13 = 0x0000000000000001   r14 = 0x0000000000000000
    r15 = 0x00003478021361e0   rip = 0x000056058f08af2f
    Found by: call frame info
 4  code-oss!uv__async_io [async.c : 162 + 0x5]
    rbx = 0x000056058f86f2e0   rbp = 0x00007ffd31211470
    rsp = 0x00007ffd31211030   r12 = 0x000034780024bc68
    r13 = 0x0000000000000000   r14 = 0x000034780024bc00
    r15 = 0x000056058f86f490   rip = 0x0000560587dddcb2
    Found by: call frame info
 5  code-oss!uv__io_poll [epoll.c : 374 + 0x7]
    rbx = 0x0000000000000002   rbp = 0x00007ffd31214580
    rsp = 0x00007ffd31211480   r12 = 0x0000000000000002
    r13 = 0x000056058f86f4a8   r14 = 0x0000000000000000
    r15 = 0x00007ffd31211480   rip = 0x0000560587dec793
    Found by: call frame info
 6  code-oss!uv_run [core.c : 406 + 0x8]
    rbx = 0x000056058f86f2e0   rbp = 0x00007ffd312145f0
    rsp = 0x00007ffd31214590   r12 = 0x000034780031ec01
    r13 = 0x000056058f86f328   r14 = 0x00007ffd312145b0
    r15 = 0x0000000000000001   rip = 0x0000560587dde0a6
    Found by: call frame info
 7  code-oss!node::Environment::CleanupHandles() [env.cc : 963 + 0xa]
    rbx = 0x000034780031e400   rbp = 0x00007ffd31214630
    rsp = 0x00007ffd31214600   r12 = 0x000034780031ec50
    r13 = 0x00007ffd312148c0   r14 = 0x000034780031ec30
    r15 = 0x000034780031ec50   rip = 0x000056058ef9fdcd
    Found by: call frame info
 8  code-oss!node::Environment::RunCleanup() [env.cc : 1016 + 0x8]
    rbx = 0x000034780031e400   rbp = 0x00007ffd31214880
    rsp = 0x00007ffd31214640   r12 = 0x00003478002eba00
    r13 = 0x00007ffd312148c0   r14 = 0x0000000000000000
    r15 = 0x0000000000000000   rip = 0x000056058efa0535
    Found by: call frame info
 9  code-oss!node::FreeEnvironment(node::Environment*) [environment.cc : 445 + 0x8]
    rbx = 0x000034780031e400   rbp = 0x00007ffd31214910
    rsp = 0x00007ffd31214890   r12 = 0x00003478002eba00
    r13 = 0x00007ffd312148c0   r14 = 0x00003478003e4000
    r15 = 0xaaaaaaaaaaaaaaaa   rip = 0x000056058ef5f1e9
    Found by: call frame info
10  code-oss!electron::NodeEnvironment::~NodeEnvironment() [javascript_environment.cc : 346 + 0x5]
    rbx = 0x00003478006ac000   rbp = 0x00007ffd31214930
    rsp = 0x00007ffd31214920   r12 = 0x0000347800290f01
    r13 = 0x0000000000000016   r14 = 0x000034780024dbc0
    r15 = 0x00007ffd31214940   rip = 0x0000560587edfda5
    Found by: call frame info
11  code-oss!electron::ElectronBrowserMainParts::PostMainMessageLoopRun() [unique_ptr.h : 65 + 0x8]
    rbx = 0x00003478003dacb0   rbp = 0x00007ffd312149b0
    rsp = 0x00007ffd31214940   r12 = 0x0000347800290f01
    r13 = 0x0000000000000016   r14 = 0x000034780024dbc0
    r15 = 0x00007ffd31214940   rip = 0x0000560587ec96c5
    Found by: call frame info
12  code-oss!content::BrowserMainLoop::ShutdownThreadsAndCleanUp() [browser_main_loop.cc : 1131 + 0x5]
    rbx = 0x0000000000000000   rbp = 0x00007ffd31214a50
    rsp = 0x00007ffd312149c0   r12 = 0x0000347800290f00
    r13 = 0xaaaaaaaaaaaaaaaa   r14 = 0x00007ffd31214a00
    r15 = 0xaaaaaaaaaaaaaaaa   rip = 0x000056058a2d9e85
    Found by: call frame info
13  code-oss!content::BrowserMainRunnerImpl::Shutdown() [browser_main_runner_impl.cc : 176 + 0x5]
    rbx = 0x0000347800377660   rbp = 0x00007ffd31214a90
    rsp = 0x00007ffd31214a60   r12 = 0xaaaaaaaaaaaaaaaa
    r13 = 0x00007ffd31214c00   r14 = 0x0000000000000000
    r15 = 0xaaaaaaaaaaaaaaaa   rip = 0x000056058a2dbade
    Found by: call frame info
14  code-oss!content::BrowserMain(content::MainFunctionParams) [browser_main.cc : 43 + 0x5]
    rbx = 0x0000000000000000   rbp = 0x00007ffd31214af0
    rsp = 0x00007ffd31214aa0   r12 = 0x00007ffd31214b08
    r13 = 0x00007ffd31214c30   r14 = 0x0000347800377660
    r15 = 0x00007ffd31214aa0   rip = 0x000056058a2d72f8
    Found by: call frame info
15  code-oss!content::RunBrowserProcessMain(content::MainFunctionParams, content::ContentMainDelegate*) [content_main_runner_impl.cc : 710 + 0x8]
    rbx = 0x00007ffd31215050   rbp = 0x00007ffd31214bc0
    rsp = 0x00007ffd31214b00   r12 = 0x00007ffd31214b08
    r13 = 0x00007ffd31214c30   r14 = 0x00007ffd31214b30
    r15 = 0x00007ffd31214b70   rip = 0x00005605880b2374
    Found by: call frame info
16  code-oss!content::ContentMainRunnerImpl::RunBrowser(content::MainFunctionParams, bool) [content_main_runner_impl.cc : 1280 + 0x8]
    rbx = 0x0000347800248200   rbp = 0x00007ffd31214c80
    rsp = 0x00007ffd31214bd0   r12 = 0x00000000ffffffff
    r13 = 0x00007ffd31214c30   r14 = 0x00007ffd31214c90
    r15 = 0x00007ffd31214bd0   rip = 0x00005605880b3bfe
    Found by: call frame info
17  code-oss!content::ContentMainRunnerImpl::Run() [content_main_runner_impl.cc : 1134 + 0xf]
    rbx = 0x0000347800248200   rbp = 0x00007ffd31214d70
    rsp = 0x00007ffd31214c90   r12 = 0x00007ffd31214d30
    r13 = 0xaaaaaaaaaaaaaaaa   r14 = 0x00007ffd31214c90
    r15 = 0x0000000000000000   rip = 0x00005605880b3a18
    Found by: call frame info
18  code-oss!content::RunContentProcess(content::ContentMainParams, content::ContentMainRunner*) [content_main.cc : 330 + 0x8]
    rbx = 0x0000347800248200   rbp = 0x00007ffd31214fd0
    rsp = 0x00007ffd31214d80   r12 = 0x0000000000000000
    r13 = 0x0000000000000000   r14 = 0x0000000000000000
    r15 = 0x00007ffd31214e61   rip = 0x00005605880b1355
    Found by: call frame info
19  code-oss!content::ContentMain(content::ContentMainParams) [content_main.cc : 347 + 0x5]
    rbx = 0x00007ffd31215010   rbp = 0x00007ffd31215040
    rsp = 0x00007ffd31214fe0   r12 = 0x00007ffd31215000
    r13 = 0x0000000000000000   r14 = 0x00007ffd31214ff8
    r15 = 0x00007ffd312150a0   rip = 0x00005605880b1445
    Found by: call frame info

@deepak1556 deepak1556 added bug Issue identified by VS Code Team member as probable bug upstream Issue identified as 'upstream' component related (exists outside of VS Code) electron Issues and items related to Electron linux Issues with VS Code on Linux labels Sep 5, 2023
@deepak1556 deepak1556 added the windows VS Code on Windows issues label Sep 14, 2023
@deepak1556 deepak1556 changed the title Crash in linux remote integration tests UAF in gin_helper::CallbackHolderBase::SecondWeakCallback Sep 14, 2023
@roblourens roblourens changed the title UAF in gin_helper::CallbackHolderBase::SecondWeakCallback UAF in gin_helper::CallbackHolderBase::SecondWeakCallback (integration test crash) Oct 9, 2023
@deepak1556 deepak1556 added the macos Issues with VS Code on MAC/OS X label Oct 10, 2023
@deepak1556 deepak1556 added the important Issue identified as high-priority label Nov 14, 2023
@deepak1556 deepak1556 added this to the November 2023 milestone Nov 14, 2023
@deepak1556
Copy link
Collaborator

Crash keys added to help narrow down the source of crash https://domoreexp.visualstudio.com/Teamspace/_git/electron-build/pullrequest/901475

@deepak1556 deepak1556 removed the important Issue identified as high-priority label Nov 15, 2023
@deepak1556 deepak1556 modified the milestones: November 2023, December 2023 Nov 27, 2023
@deepak1556
Copy link
Collaborator

Had a new failure with Electron 29 as well, refs https://dev.azure.com/monacotools/a6d41577-0fa3-498e-af22-257312ff0545/_build/results?buildId=277418

Sadly I missed porting the gin annotations to 29 branch

MDRawCrashpadInfo
  simple_annotations["_companyName"] = Microsoft
  simple_annotations["_productName"] = VSCode
  simple_annotations["_version"] = 1.91.0-insider
  simple_annotations["lsb-release"] = Ubuntu 20.04.6 LTS
  simple_annotations["plat"] = Linux
  simple_annotations["prod"] = Electron
  simple_annotations["ver"] = 29.4.0
  module_list[0].minidump_module_list_index = 0
  module_list[0].version = 1
  module_list[0].crashpad_annotations["osarch"] (type = 1) = x86_64
  module_list[0].crashpad_annotations["pid"] (type = 1) = 24875
  module_list[0].crashpad_annotations["ptype"] (type = 1) = browser
  module_list[0].crashpad_annotations["platform"] (type = 1) = linux
  module_list[0].crashpad_annotations["process_type"] (type = 1) = browser
  address_mask = 0

@deepak1556
Copy link
Collaborator

@rebornix
Copy link
Member

@deepak1556 deepak1556 added this to the August 2024 milestone Jul 25, 2024
@deepak1556
Copy link
Collaborator

@deepak1556
Copy link
Collaborator

Added crash keys and triggered test builds to debug further. Work can be tracked in https://github.com/microsoft/vscode/tree/robo/debug_integration_crash

@deepak1556
Copy link
Collaborator

I have now tracked down the source of crash, but before I get down into the final details lets walk through the nature of the crash and understand what is happening.

The crash started appearing a year ago in our CI mainly with integration tests across all 3 platforms inconsistently. Sometimes the crash would disappear after runtime updates but recently it has been pretty consistent in our Windows CI. Although I was unsuccessful to repro it locally but given its frequent presence in CI I decided to rely on Crash keys which are meant for cases like this.

First, where are these crashes happening ?

On shutdown of the main process, the runtime eventually has to gracefully cleanup the Node.js environment and its associated tasks from the event loop https://github.com/electron/electron/blob/15db63e26df3e3d59ce6281f030624f746518511/shell/browser/electron_browser_main_parts.cc#L595-L601 which in rare cases lead to UAF in certain SecondWeakCallback listeners.

The initial reports point to the resource cleanup in gin_helper::CallbackHolderBase::SecondWeakCallback

Newer reports point to the resource cleanup in gin::WrappableBase::SecondWeakCallback

So what are these affected classes and the weak callbacks ?

The runtime exposes JS api that are backed by C++ implementations through V8. gin::Wrappable is the base class that allows exposing C++ objects as corresponding V8 objects. For example to expose a following object in Javascript,

const app = {
  isHidden: false,
  hide: () => {
    isHidden = true;
  }
}

const a = Object.create(app);

This is how we would do the same in C++ and expose it through V8

class App : gin::Wrappable<App> {
  public:
    static gin::WrapperInfo kWrapperInfo; // important will be covered below
 
    gin::ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) override {
        return gin::Wrappable<App>::GetObjectTemplateBuilder(isolate) 
                     .setMethod("hide", &App::Hide)
                     .setProperty("isHidden", &App::isHidden);
    }
 
  private:
    bool isHidden() const;
    void Hide();
}

The work to construct an object instance from this C++ class will eventually be performed by gin::Wrappable::GetWrapperImpl.

gin::CallbackHolderBase works in a similar fashion but it is limited to holding the C++ function pointers that needs to be invoked from V8. In the above example, these pointers would be &App::Hide and &App::isHidden.

Now going back to the implementation of gin::Wrappable::GetWrapperImpl, you will see that a parameter called gin::WrapperInfo* is passed in to the function. This is the pointer to the static member kWrapperInfo from the above example which can store metadata about the object, in this case it contains infomation about the type of owner of the object in C++. The primary use case of this pointer in gin::Wrappable is to act as a cache key to store the object templates. The second point of interest in this implementation is the call to setWeak which acts the primary resource management hook between the C++ heap and Javascript heap. This works pretty much the same as how finalizationregistry. We request a callback to be invoked when the corresponding object is garbage collected. You can refer to v8::WeakCallbackInfo::SetSecondPassCallback for why there are two separate phases of callback registration in the C++ version.

What could cause these use-after-free ?

  • CallbackHolderBase class
  1. It could be deleted when the V8 isolate gets disposed https://source.chromium.org/chromium/chromium/src/+/main:v8/include/v8-weak-callback-info.h;l=39-45

  2. Or, it could be deleted when the second weak callback fires https://source.chromium.org/chromium/chromium/src/+/main:gin/function_template.cc

The order of destruction betweengin::PerIsolateData which is held by JavascriptEnvironment and node::Environment https://github.com/electron/electron/blob/15db63e26df3e3d59ce6281f030624f746518511/shell/browser/electron_browser_main_parts.h#L163-L172 guarantees that the CallbackHolderBase destruction entry points don't race against each other. I will leave the analysis of this destruction sequence to the interested readers.

The initial reports which don't appear now could be attributed to consuming the fix from https://chromium-review.googlesource.com/c/chromium/src/+/5104930 and electron/electron#41534. I can only speculate about this as there is not much we can understand from the old reports.

  • gin::Wrappable class
  1. It could be destroyed when the second weak callback fires https://source.chromium.org/chromium/chromium/src/+/main:gin/wrappable.cc;l=39

But there is another scenario of destruction happening during shutdown that is not captured. This is where crash keys come in handy. The keys I added are in https://gist.github.com/deepak1556/f9682fb1b94aa53d9cf37e83638c226d

  1. Extended the WrapperInfo to hold the name of the C++ class that is impacted
  2. Extended the WrapperInfo with a new boolean disposed that will be set to true when gin::Wrappable::~Wrappable is called. This will help in cases where either of the weak callbacks are called after the class is destroyed.
  3. I also want to see the other way, so added new boolean member second_weak_callback_called_ to gin::Wrappable which will be set to true when the second weak callback is called. We already have the member dead_ that tracks whether the first weak callback was called. Now in gin::Wrappable::~Wrappable we can have condition that basically checks dead_ && !second_weak_callback_called_ which can confidently pinpoint that someelse destroyed this class between the first and second callbacks.

Result

After a couple of retries in the CI, I was able to trigger the crash https://monacotools.visualstudio.com/Monaco/_build/results?buildId=295068&view=results. As expected I had a bunch of dumps from crash_reporter::DumpWithoutCrashing and one for the actual crash. This is how they looked, see the size of the crash dumps to know which one had the crash keys,

Screenshot 2024-09-26 at 12 54 17
  • Crash key values

Run minidump_dump <path-to.dmp> > result.txt

  module_list[0].crashpad_annotations["DumpWithoutCrashing-WrapperInfo"] (type = 1) = MessagePort
  module_list[0].crashpad_annotations["DumpWithoutCrashing-reason"] (type = 1) = gin::Wrappable incorrect dispose!!!
  module_list[0].crashpad_annotations["osarch"] (type = 1) = x86_64
  module_list[0].crashpad_annotations["pid"] (type = 1) = 7752
  module_list[0].crashpad_annotations["ptype"] (type = 1) = browser
  module_list[0].crashpad_annotations["platform"] (type = 1) = win32
  module_list[0].crashpad_annotations["process_type"] (type = 1) = browser
  • DumpWithoutCrashing stack
Crash reason:  Simulated Exception
Crash address: 0x7ff61618d7a7
Process uptime: 3 seconds

Thread 0 (crashed)
 0  Code - Insiders.exe!crash_reporter::DumpWithoutCrashing() [crashpad.cc : 268 + 0x18]
 1  Code - Insiders.exe!static void gin::Wrappable<electron::MessagePort>::~Wrappable() [wrappable.h : 119 + 0x5]
 2  Code - Insiders.exe!static void electron::MessagePort::~MessagePort() [message_port.cc : 59 + 0x10]
 3  Code - Insiders.exe![thunk]:electron::MessagePort::`vector deleting destructor'`adjustor{24}' (unsigned int) + 0x17
 4  Code - Insiders.exe!gin_helper::CleanedUpAtExit::DoCleanup() [cleaned_up_at_exit.cc : 36 + 0x16]
 5  Code - Insiders.exe!electron::JavascriptEnvironment::DestroyMicrotasksRunner() [javascript_environment.cc : 340 + 0x5]
 6  Code - Insiders.exe!electron::ElectronBrowserMainParts::PostMainMessageLoopRun() [electron_browser_main_parts.cc : 596 + 0x5]
 7  Code - Insiders.exe!content::BrowserMainLoop::ShutdownThreadsAndCleanUp() [browser_main_loop.cc : 1166 + 0x5]
 8  Code - Insiders.exe!content::BrowserMainRunnerImpl::Shutdown() [browser_main_runner_impl.cc : 180 + 0x5]
 9  Code - Insiders.exe!content::BrowserMain(content::MainFunctionParams) [browser_main.cc : 43 + 0x5]
10  Code - Insiders.exe!static int content::RunBrowserProcessMain(struct content::MainFunctionParams, class content::ContentMainDelegate *) [content_main_runner_impl.cc : 712 + 0x20]
11  Code - Insiders.exe!static int content::ContentMainRunnerImpl::RunBrowser(struct content::MainFunctionParams, bool) [content_main_runner_impl.cc : 1303 + 0x16]
  • Actual crash stack
Crash reason:  EXCEPTION_ACCESS_VIOLATION_READ
Crash address: 0xffffffffffffffff
Process uptime: 4 seconds

Thread 0 (crashed)
 0  Code - Insiders.exe!static void gin::WrappableBase::SecondWeakCallback(const class v8::WeakCallbackInfo<gin::WrappableBase> & const) [wrappable.cc : 61 + 0x8]
 1  Code - Insiders.exe!v8::internal::GlobalHandles::InvokeSecondPassPhantomCallbacks() [global-handles.cc : 767 + 0x33]
 2  Code - Insiders.exe!static void node::PerIsolatePlatformData::RunForegroundTask(class std::__Cr::unique_ptr<v8::Task,std::__Cr::default_delete<v8::Task> >) [node_platform.cc : 425 + 0xd]
 3  Code - Insiders.exe!static bool node::PerIsolatePlatformData::FlushForegroundTasksInternal() [node_platform.cc : 499 + 0xb]
 4  Code - Insiders.exe!static void uv__process_reqs(struct uv_loop_s *) [req-inl.h : 189 + 0xb]
 5  Code - Insiders.exe!uv_run [core.c : 668 + 0x8]
 6  Code - Insiders.exe!static void node::Environment::CleanupHandles() [env.cc : 1172 + 0xa]
 7  Code - Insiders.exe!node::Environment::RunCleanup() [env.cc : 1224 + 0x8]
 8  Code - Insiders.exe!node::FreeEnvironment(node::Environment *) [environment.cc : 522 + 0x8]
 9  Code - Insiders.exe!void std::__Cr::__shared_ptr_pointer<node::Environment *,`lambda at ..\..\electron\shell\common\node_bindings.cc:765:22',std::__Cr::allocator<node::Environment> >::__on_zero_shared() [shared_ptr.h : 228 + 0x6d]
10  Code - Insiders.exe!electron::ElectronBrowserMainParts::PostMainMessageLoopRun() [electron_browser_main_parts.cc : 598 + 0x2e]
11  Code - Insiders.exe!content::BrowserMainLoop::ShutdownThreadsAndCleanUp() [browser_main_loop.cc : 1166 + 0x5]
12  Code - Insiders.exe!content::BrowserMainRunnerImpl::Shutdown() [browser_main_runner_impl.cc : 180 + 0x5]
13  Code - Insiders.exe!content::BrowserMain(content::MainFunctionParams) [browser_main.cc : 43 + 0x5]
14  Code - Insiders.exe!static int content::RunBrowserProcessMain(struct content::MainFunctionParams, class content::ContentMainDelegate *) [content_main_runner_impl.cc : 712 + 0x20]
15  Code - Insiders.exe!static int content::ContentMainRunnerImpl::RunBrowser(struct content::MainFunctionParams, bool) [content_main_runner_impl.cc : 1303 + 0x16]

So we had a class in Electron that worked in theory similar to gin::CallbackHolderBase::DisposeObserver which destroys the C++ object on the disposal of isolates in concern that the finalization callbacks might have not run https://github.com/electron/electron/blob/main/shell/common/gin_helper/cleaned_up_at_exit.h#L10-L23. But the only issue is that DoCleanup function runs at the destruction of the microtasks runner which can race with the pending task flushing from the event loop on destruction of node::Environment as seen from the stack traces.

Proposed fix

  1. Convert CleanupAtExit into a gin::PerIsolateData::DisposeObserver that actually watches destruction of v8::Isolate
  2. Clear weakness for the wrapper inside gin::WrappableBase::~WrappableBase so that we can do the following in second weak callback
WrappableBase* wrappable = data.GetParameter() // can now be nullptr when weakness is cleared via https://source.chromium.org/chromium/chromium/src/+/main:v8/src/handles/global-handles.cc;l=521-527
if (wrappable) {
  delete wrappable;
}

What is the future ?

If you have made it this far, hope you enjoyed this debugging session! Now this disjoint between the C++ heap and Javascript heap leading to these uaf issues is not new. The V8 team had already worked on addressing it for the Blink rendering engine via the project called Oilpan and it is also available as a standalone library for embedding in any C++ project that uses V8 https://github.com/oilpan-gc/cppgc. Through this library, the trace based garbage collector can now view both the C++ heap and Javascript heap together to help make lifetime management simpler. IIUC, //gin could adopt cppgc when the heap setup mechanisms are moved from blink into V8 as part of https://issues.chromium.org/issues/42203693. We can then explore the adoption in Electron.

@vs-code-engineering vs-code-engineering bot added unreleased Patch has not yet been released in VS Code Insiders insiders-released Patch has been released in VS Code Insiders and removed unreleased Patch has not yet been released in VS Code Insiders labels Oct 1, 2024
@rzhao271 rzhao271 added the verified Verification succeeded label Oct 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issue identified by VS Code Team member as probable bug electron Issues and items related to Electron freeze-slow-crash-leak VS Code crashing, performance, freeze and memory leak issues insiders-released Patch has been released in VS Code Insiders integration-test-failure linux Issues with VS Code on Linux macos Issues with VS Code on MAC/OS X upstream Issue identified as 'upstream' component related (exists outside of VS Code) verified Verification succeeded windows VS Code on Windows issues
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants