CVE-2026-7342
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
JsBindingcomponents/js_injection/renderer/js_binding.h |
modified |
Files Changed
components/js_injection/renderer/js_binding.cccomponents/js_injection/renderer/js_binding.h
Patch
From a677b82cf06e1243940d0ac4c8d5b6c97d1a0ff1 Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner <[email protected]> Date: Tue, 21 Apr 2026 09:10:02 -0700 Subject: [PATCH] js_injection: Fix Use-After-Free in JsBinding JsBinding is an Oilpan-managed object but used a raw Mojo receiver. This allowed IPC messages to be dispatched to a logically dead object during the lazy sweeping phase. Synchronous JS execution during the IPC handler could then trigger a nested GC that frees the object while it is still on the stack. This CL fixes the issue by adding a pre-finalizer to JsBinding that explicitly resets the Mojo receiver during the GC's atomic pause. Fixed: 503889643 Change-Id: I3468fce74c5c8c3fa24d33ee1b35398df2e08b2f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7780011 Commit-Queue: Andrew Paseltiner <[email protected]> Reviewed-by: Bo Liu <[email protected]> Cr-Commit-Position: refs/heads/main@{#1618238} --- diff --git a/components/js_injection/renderer/js_binding.cc b/components/js_injection/renderer/js_binding.cc index 835c772..d93325c2 100644 --- a/components/js_injection/renderer/js_binding.cc +++ b/components/js_injection/renderer/js_binding.cc @@ -285,6 +285,13 @@ return receiver_.Bind(std::move(receiver)); } +void JsBinding::Dispose() { + // Explicitly reset the receiver to prevent IPC messages from being dispatched + // to this object while it is awaiting lazy sweeping. This prevents a UAF if + // synchronous JS execution triggers a nested GC. See crbug.com/503889643. + receiver_.reset(); +} + gin::ObjectTemplateBuilder JsBinding::GetObjectTemplateBuilder( v8::Isolate* isolate) { return gin::Wrappable<JsBinding>::GetObjectTemplateBuilder(isolate) diff --git a/components/js_injection/renderer/js_binding.h b/components/js_injection/renderer/js_binding.h index 9ed2d45..5c4eade 100644 --- a/components/js_injection/renderer/js_binding.h +++ b/components/js_injection/renderer/js_binding.h @@ -18,6 +18,7 @@ #include "mojo/public/cpp/bindings/associated_receiver.h" #include "third_party/blink/public/common/messaging/string_message_codec.h" #include "v8/include/cppgc/persistent.h" +#include "v8/include/cppgc/prefinalizer.h" #include "v8/include/v8.h" namespace v8 { @@ -38,6 +39,8 @@ // to the page. JsBinding is owned by v8. class JsBinding final : public gin::Wrappable<JsBinding>, public mojom::BrowserToJsMessaging { + CPPGC_USING_PRE_FINALIZER(JsBinding, Dispose); + public: static constexpr gin::WrapperInfo kWrapperInfo = {{gin::kEmbedderNativeGin}, gin::kJsBinding}; @@ -73,6 +76,8 @@ mojo::PendingAssociatedReceiver<mojom::BrowserToJsMessaging> receiver); private: + void Dispose(); + // gin::WrappableBase implementation. gin::ObjectTemplateBuilder GetObjectTemplateBuilder( v8::Isolate* isolate) override;
Original Bug Report
Potential Use-After-Free in js_injection::JsBinding due to lazy sweeping of Mojo receiver
Flapjack, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the Chrome Security team. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: js_injection::JsBinding is an Oilpan-managed object that uses a raw Mojo receiver, allowing IPC messages to be dispatched to a logically dead object. Synchronous JavaScript execution during the IPC handler can force a nested garbage collection cycle that destroys the object while its member function is active, leading to a highly exploitable Use-After-Free.
Affected files:
components/js_injection/renderer/js_binding.cccomponents/js_injection/renderer/js_binding.h
Estimated timestamp from git blame: 2023-10-25
Summary
A potential Use-After-Free (UAF) vulnerability exists in the js_injection::JsBinding class in the renderer process. The class is managed by Oilpan (cppgc) but uses a raw mojo::AssociatedReceiver, which lacks awareness of Oilpan’s lifecycle. This allows Mojo messages to be dispatched to a logically dead object. An attacker can exploit this by triggering synchronous JavaScript execution that forces the object to be swept while it is executing on the call stack.
Vulnerability Details
The js_injection::JsBinding class inherits from gin::Wrappable<JsBinding>, making its lifetime managed by Chromium’s Oilpan garbage collector (cppgc). It hosts a mojo::AssociatedReceiver<mojom::BrowserToJsMessaging> member (receiver_) which is bound to this to handle IPC messages from the browser.
In Chromium’s Oilpan system, garbage collection uses “lazy sweeping”. When an object becomes unreachable, it is marked as dead during the atomic pause, but its C++ destructor is deferred until the sweeping phase reclaims the memory later. Because JsBinding uses a raw Mojo receiver and does not have a pre-finalizer to unbind it during the atomic pause, a race condition exists: the receiver remains active and bound to the IPC pipe even after the object is marked dead.
Exploitation Steps (Theoretical)
An attacker could potentially trigger this vulnerability using the following steps:
- Setup: The attacker uses JavaScript to register an
onmessagecallback on the injected JS object, and adds a dummy event listener to populate thelisteners_vector. - Orphan the Object: The attacker drops all JavaScript references to the object and forces a garbage collection cycle.
- Marking & Handle Clearing:
cppgcidentifies theJsBindingobject as unreachable. V8’sTracedHandles::ResetDeadNodesclears thewrapper_reference. The object is logically dead but itsmojo::AssociatedReceiverremains active pending lazy sweeping. - IPC Dispatch: The Browser process sends an
OnPostMessageIPC. Mojo dispatches this to the active receiver, callingJsBinding::OnPostMessagewith thethispointer referencing the logically dead memory. - Synchronous JS Execution: Inside
OnPostMessage, the code safely generates a new wrapper (aswrapper_.IsEmpty()is true) and synchronously executes the attacker’son_message_callback viaPausableScriptExecutor::CreateAndRun(..., EvaluationTiming::kSynchronous). - Nested GC & Synchronous Sweeping: Inside the JS callback, the attacker forces massive allocations to trigger a new GC cycle. In
cppgc, starting a new GC cycle explicitly callssweeper_.FinishIfRunning(). This forces the synchronous execution of the previous cycle’s destructors on the main thread, including~JsBinding(). The memory forthisis freed. - Heap Spraying: The attacker immediately allocates a new
cppgcobject of the same size, overlapping the newly freedJsBindingmemory and forging the internal pointers of thelisteners_vector. - Use-After-Free: The JS callback returns.
OnPostMessageresumes and attempts to accesslisteners_.size()and iterate over the vector (listeners_copy.reserve(listeners_.size());). This reads from the attacker-controlled overlapped memory.
Because MiraclePtr (BackupRefPtr) protects PartitionAlloc but does not protect the cppgc managed heap, this UAF can be reliably transformed into an arbitrary memory read/write primitive, leading to Remote Code Execution (RCE) in the renderer process.
Fix Recommendations
- Primary Fix: Replace the raw
mojo::AssociatedReceiverwithHeapMojoAssociatedReceiver(orHeapMojoReceiverif the interface is updated). Heap Mojo wrappers arecppgc-aware and automatically disconnect when the owner object is marked dead, preventing dispatch to a dying object. - Alternative Fix: Add the
USING_PRE_FINALIZERmacro toJsBindingto explicitly callreceiver_.reset()during the pre-finalization phase (the atomic pause), before lazy sweeping begins.
Evaluated with Chrome root at commit: c0eb5541aebfa4ea08806eaf6e94bcc69f87ab2f
Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.