021769f1006aad305fac2a3ad96c3a607e5d3051 [M144-LTS] InspectorIndexedDBAgent must not use v8_session_ after disposal
The bug at a glance
A genuine use-after-free of the DanglingUntriaged raw_ptr v8_session_ in a renderer-side DevTools agent, confirmed by a test that repros 100% under ASAN; severity is capped at medium because reaching it requires an attached DevTools/Inspector IndexedDB session rather than plain web content.
An attacker (or malicious automation/extension) driving the DevTools IndexedDB protocol can start an async requestData() read and then navigate the inspected frame so the agent is disposed before the callback fires, leaving v8_session_ dangling when the callback dereferences it.
Root cause
InspectorIndexedDBAgent holds v8_session_ as a raw_ptr<…,DanglingUntriaged>. Before this fix, Dispose() did not null the pointer and async callbacks (e.g. OpenCursorCallback::Invoke) and lifecycle hooks (DidCommitLoadForLocalFrame, disable) called agent_->v8_session()->releaseObjectGroup()/used the session unconditionally. If the agent is disposed while a requestData() callback is still pending during navigation, the V8InspectorSession has been torn down and the dangling v8_session_ is used, producing a UAF. The fix nulls v8_session_ in Dispose(), routes session use through a ReleaseObjectGroup() helper that null-checks, and adds an explicit null check in the callback that fails the request with kSessionDetachedDuringOperation.
Key code
Dispose() nulls v8_session_ and callbacks now null-check before use
void InspectorIndexedDBAgent::Dispose() {
ReleaseObjectGroup();
v8_session_ = nullptr;
InspectorBaseAgent<protocol::IndexedDB::Metainfo>::Dispose();
}
...
v8_inspector::V8InspectorSession* v8_session = agent->v8_session();
if (!v8_session) {
request_callback_->sendFailure(
protocol::Response::ServerError(kSessionDetachedDuringOperation));
return;
}
Patch walkthrough
third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc— Adds Dispose() which calls ReleaseObjectGroup() then sets v8_session_ = nullptr; introduces ReleaseObjectGroup() guarded by a null check; and inserts a null check on the session inside the async callback before dereferencing, closing the window where a disposed agent’s dangling session pointer could be used.third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.h— Declares the Dispose() override and private ReleaseObjectGroup() helper and documents that v8_session_ is null after disposal, making the lifecycle contract explicit for the DanglingUntriaged raw_ptr.third_party/blink/web_tests/http/tests/inspector-protocol/storage/indexed-db-request-data-race-with-navigation-large-db.js— Regression test that issues IndexedDB.requestData() against a large store and immediately navigates, racing teardown against the pending callback to exercise the UAF window.
Background
InspectorIndexedDBAgent — Blink renderer agent implementing the DevTools IndexedDB protocol domain for an inspected frame.
V8InspectorSession — V8 inspector object owning inspector-created JS wrappers/object groups; destroyed when the DevTools session detaches.
raw_ptr DanglingUntriaged — A MiraclePtr raw_ptr annotation marking a pointer known to possibly dangle, signaling an unresolved lifetime hazard.
Oilpan Member vs raw_ptr — v8_session_ is a non-traced raw_ptr, so it is not kept alive or cleared automatically when the session dies.
Vulnerability window
- Reachability — A DevTools client enables the IndexedDB domain and issues an async requestData() on a frame with a sizeable store.
- Trigger — The client navigates the inspected frame so the agent is disposed and the V8InspectorSession torn down while the requestData() callback is still pending.
- Primitive — The pending callback dereferences the dangling v8_session_ raw_ptr, a use-after-free (inferred that the freed session memory may be reclaimed/controlled).
- Escalation — Controlling the reclaimed V8InspectorSession allocation could turn the UAF into type confusion or control-flow hijack (inferred).
Proof of concept
Conceptually: with the DevTools IndexedDB domain enabled, create a large object store, call IndexedDB.requestData() and immediately navigate the inspected frame; the agent disposes while the read callback is in flight, so the callback touches the freed session. The committed regression test demonstrates the race shape without weaponization.
Exploitation
- Setup — Attach a DevTools/Inspector session and enable the IndexedDB domain on a target frame (inferred to require inspector access).
- Race — Launch a long-running requestData() and navigate to force agent disposal mid-callback.
- Reclaim — Spray allocations to reoccupy the freed V8InspectorSession before the dangling use (inferred).
- Exploit — Leverage the controlled dereference for info leak or corruption (inferred).
Detection & hunting
For defenders and detection engineers:
- grep for raw_ptr members used in async DevTools callbacks without null checks after Dispose() — Audit Inspector*Agent classes where a session/pointer member is used in Event/callback Invoke paths but not cleared in Dispose().
- ASAN heap-use-after-free in InspectorIndexedDBAgent / V8InspectorSession during navigation — Run inspector-protocol web tests under ASAN and watch for UAF stacks referencing releaseObjectGroup/requestData callbacks.
Audit directions
- Other InspectorBaseAgent subclasses holding raw_ptr — Check each for a Dispose() that nulls the session and for null checks in all async callbacks, mirroring the InspectorDOMAgent pattern this CL follows.
- DanglingUntriaged raw_ptr members in blink/renderer/modules — Treat each as an unresolved lifetime hazard and verify clearing on owner teardown plus guarded use.