CVE-2026-7333
Overview
Background
- `SurfaceManager`
- the viz-service component (in the GPU process) that tracks compositor
Surfaceobjects and groups them for lifetime management. - `SurfaceAllocationGroup`
- an object grouping surfaces that share a
FrameSinkIdand embed token; a client’sframe_sink_id_to_allocation_groups_entry is a vector of pointers to these. - `WillNotRegisterNewSurfaces()`
- a notification called on each allocation group when a frame sink is invalidated, which can activate a pending
CompositorFrameand, as a side effect, register brand-new allocation groups. - Iterator invalidation
- mutating a
std::vector(e.g. appending, which may reallocate its backing store) while a range-basedforloop holds iterators into it leaves those iterators dangling.
Root Cause Analysis
SurfaceManager::InvalidateFrameSinkId looked up the allocation-group vector for a FrameSinkId and ran a range-based for loop directly over the stored container it->second, calling group->WillNotRegisterNewSurfaces() on each element. That callback is reentrant: activating a pending compositor frame can cause additional SurfaceAllocationGroups (for referenced surfaces) to be added to the very same vector being iterated. Appending to the vector can reallocate its backing storage, so the loop’s begin/end iterators and the current element pointer are invalidated mid-iteration, producing a use-after-free read of freed vector memory.
The fix copies the vector (auto allocation_groups = it->second;) before the loop and iterates over the stable copy, so any groups added by the reentrant callback land in the original container without disturbing the iteration. The shipped regression test SurfaceTest.RentrantSurfaceActivationGroups reproduces this by submitting a frame whose activation adds 100 new groups during the InvalidateFrameSinkId walk.
WillNotRegisterNewSurfaces()) could grow that same container; the fix snapshots the vector into a local copy so iteration is over an immutable sequence.Attack Path
- Establish surfaces
A compromised or malicious renderer (via its
CompositorFrameSinkSupport) submits compositor frames under aFrameSinkIdso that at least oneSurfaceAllocationGroupis registered for that sink. - Arm a reentrant expansion
Submit a pending frame whose referenced surfaces map to many additional allocation groups that are only created upon activation, so one
WillNotRegisterNewSurfaces()call will append numerous entries to the sink’s vector. - Trigger invalidation
Cause
SurfaceManager::InvalidateFrameSinkIdto run for that sink; the loop callsWillNotRegisterNewSurfaces()on the first group, which activates the frame and reallocates the vector being iterated. - Use-after-free
The loop continues with now-dangling iterators/pointers into the freed vector buffer, dereferencing freed memory (
inferredexploitation beyond the UAF read is not shown by the diff).
Impact Assessment
SurfaceManager code inside the GPU process; the precondition is arranging surface references so activation adds allocation groups during InvalidateFrameSinkId. The vendor advisory rates this high severity with impact “Use after free in GPU” and reports no in-the-wild exploitation; memory-corruption escalation to code execution in the GPU process is (inferred) and not demonstrated by the patch.Changed Functions
| Function | Change | Notes |
|---|---|---|
forcomponents/viz/service/surfaces/surface_manager.cc |
modified | |
TEST_Fcomponents/viz/service/surfaces/surface_unittest.cc |
modified | |
forcomponents/viz/service/surfaces/surface_unittest.cc |
modified |
Files Changed
components/viz/service/surfaces/surface_manager.cccomponents/viz/service/surfaces/surface_unittest.cc
Audit Directions
- Reentrant container mutationSearch for range-based
forloops over a member container whose body invokes callbacks/notifications (WillNot*,OnActivated, observer dispatch) that can insert into the same container; prefer iterating a copy or an index-safe snapshot. - Activation side effectsAudit viz surface/allocation-group code paths where frame activation registers new groups or surfaces, since activation can be triggered synchronously from within other traversals.
- Stored-iterator lifetimeLook for code caching
begin()/end()or element pointers ofstd::vector/base::flat_mapacross any call that may append, which reallocates the backing store.
Patch
From 2332137922bb5abd4ffe7ea8ae125c5f0b1d400b Mon Sep 17 00:00:00 2001
From: kylechar <kylechar@chromium.org>
Date: Fri, 10 Apr 2026 09:33:51 -0700
Subject: [PATCH] Guard against reentrant vector modification
Copy active surface group vector during before iterating as
WillNotRegisterNewSurfaces() can cause new surface groups to be added to
vector, invalidating existing iterators.
Fixed: 493955227
Change-Id: Ia58a539523d7b5d700cbfd232f4d8bab597d20d4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7707244
Reviewed-by: Jonathan Ross <jonross@chromium.org>
Commit-Queue: Kyle Charbonneau <kylechar@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1612925}
---
diff --git a/components/viz/service/surfaces/surface_manager.cc b/components/viz/service/surfaces/surface_manager.cc
index 61538500..3dafe151 100644
--- a/components/viz/service/surfaces/surface_manager.cc
+++ b/components/viz/service/surfaces/surface_manager.cc
@@ -167,7 +167,9 @@
void SurfaceManager::InvalidateFrameSinkId(const FrameSinkId& frame_sink_id) {
auto it = frame_sink_id_to_allocation_groups_.find(frame_sink_id);
if (it != frame_sink_id_to_allocation_groups_.end()) {
- for (SurfaceAllocationGroup* group : it->second) {
+ // Copy allocation group vector since it can be modified while iterating.
+ auto allocation_groups = it->second;
+ for (SurfaceAllocationGroup* group : allocation_groups) {
group->WillNotRegisterNewSurfaces();
}
}
diff --git a/components/viz/service/surfaces/surface_unittest.cc b/components/viz/service/surfaces/surface_unittest.cc
index 0a5936c..aa6198f 100644
--- a/components/viz/service/surfaces/surface_unittest.cc
+++ b/components/viz/service/surfaces/surface_unittest.cc
@@ -2,12 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "components/viz/service/surfaces/surface.h"
+
#include <utility>
#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h"
+#include "base/unguessable_token.h"
#include "cc/test/scheduler_test_common.h"
#include "components/viz/common/features.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
@@ -16,7 +19,6 @@
#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
#include "components/viz/service/surfaces/pending_copy_output_request.h"
-#include "components/viz/service/surfaces/surface.h"
#include "components/viz/test/begin_frame_args_test.h"
#include "components/viz/test/compositor_frame_helpers.h"
#include "components/viz/test/fake_external_begin_frame_source.h"
@@ -398,5 +400,66 @@
EXPECT_TRUE(surface->activation_dependencies().empty());
}
+// Checks that modifying surface activation group vector while iterating through
+// the existing entries doesn't cause problems.
+TEST_F(SurfaceTest, RentrantSurfaceActivationGroups) {
+ SurfaceManager* surface_manager = frame_sink_manager_.surface_manager();
+
+ auto will_invalidate_support = std::make_unique<CompositorFrameSinkSupport>(
+ nullptr, &frame_sink_manager_, kArbitraryFrameSinkId, /*is_root=*/false);
+ auto y1_support = std::make_unique<CompositorFrameSinkSupport>(
+ nullptr, &frame_sink_manager_, FrameSinkId(3, 1), /*is_root=*/false);
+ auto y2_support = std::make_unique<CompositorFrameSinkSupport>(
+ nullptr, &frame_sink_manager_, FrameSinkId(4, 1), /*is_root=*/false);
+
+ // Builds a frame with dependencies and a long deadline for activation.
+ auto build_frame = [](std::vector<SurfaceId> deps,
+ std::vector<SurfaceRange> refs) {
+ return CompositorFrameBuilder()
+ .AddRenderPass(gfx::Rect(10, 10), gfx::Rect(10, 10))
+ .SetActivationDependencies(std::move(deps))
+ .SetReferencedSurfaces(std::move(refs))
+ .SetDeadline(FrameDeadline(base::TimeTicks::Now(), 10000u,
+ base::Milliseconds(16), false))
+ .Build();
+ };
+
+ // Each of these SurfaceIds are from the same FrameSinkId but have different
+ // embed_tokens therefore different SurfaceAllocationGroups.
+ std::vector<SurfaceRange> malicious_refs;
+ malicious_refs.reserve(100);
+ for (int i = 0; i < 100; i++) {
+ SurfaceId sid(kArbitraryFrameSinkId,
+ LocalSurfaceId(i, 1, base::UnguessableToken::Create()));
+ malicious_refs.emplace_back(sid);
+ }
+
+ // A SurfaceAllocationGroup for `dep1` is added immediately but groups for
+ // `malicious_refs` are only added once the CompositorFrame activates.
+ SurfaceId dep1(kArbitraryFrameSinkId,
+ LocalSurfaceId(1, 1, base::UnguessableToken::Create()));
+ LocalSurfaceId y1_lsid(1, 1, base::UnguessableToken::Create());
+ y1_support->SubmitCompositorFrame(y1_lsid,
+ build_frame({dep1}, malicious_refs));
+
+ SurfaceId dep2(kArbitraryFrameSinkId,
+ LocalSurfaceId(2, 1, base::UnguessableToken::Create()));
+ LocalSurfaceId y2_lsid(1, 1, base::UnguessableToken::Create());
+ y2_support->SubmitCompositorFrame(y2_lsid, build_frame({dep2}, {}));
+
+ // There will be two SurfaceAllocationGroups for `kArbitraryFrameSinkId` at
+ // this point. WillNotRegisterNewSurfaces() will be called on each allocation
+ // groups, first for `y1_lsid` group which activates the surface and add 100
+ // more SurfaceAllocationGroups to the vector. This tests that modifying
+ // the vector being iterated doesn't cause problems.
+ surface_manager->InvalidateFrameSinkId(kArbitraryFrameSinkId);
+
+ // Both y1 and y2 surfaces are now active.
+ EXPECT_TRUE(surface_manager->GetSurfaceForId(
+ SurfaceId(y1_support->frame_sink_id(), y1_lsid)));
+ EXPECT_TRUE(surface_manager->GetSurfaceForId(
+ SurfaceId(y2_support->frame_sink_id(), y2_lsid)));
+}
+
} // namespace
} // namespace viz
Regression Test / PoC
diff --git a/components/viz/service/surfaces/surface_unittest.cc b/components/viz/service/surfaces/surface_unittest.cc
index 0a5936c..aa6198f 100644
--- a/components/viz/service/surfaces/surface_unittest.cc
+++ b/components/viz/service/surfaces/surface_unittest.cc
@@ -2,12 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "components/viz/service/surfaces/surface.h"
+
#include <utility>
#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h"
+#include "base/unguessable_token.h"
#include "cc/test/scheduler_test_common.h"
#include "components/viz/common/features.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
@@ -16,7 +19,6 @@
#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
#include "components/viz/service/surfaces/pending_copy_output_request.h"
-#include "components/viz/service/surfaces/surface.h"
#include "components/viz/test/begin_frame_args_test.h"
#include "components/viz/test/compositor_frame_helpers.h"
#include "components/viz/test/fake_external_begin_frame_source.h"
@@ -398,5 +400,66 @@
EXPECT_TRUE(surface->activation_dependencies().empty());
}
+// Checks that modifying surface activation group vector while iterating through
+// the existing entries doesn't cause problems.
+TEST_F(SurfaceTest, RentrantSurfaceActivationGroups) {
+ SurfaceManager* surface_manager = frame_sink_manager_.surface_manager();
+
+ auto will_invalidate_support = std::make_unique<CompositorFrameSinkSupport>(
+ nullptr, &frame_sink_manager_, kArbitraryFrameSinkId, /*is_root=*/false);
+ auto y1_support = std::make_unique<CompositorFrameSinkSupport>(
+ nullptr, &frame_sink_manager_, FrameSinkId(3, 1), /*is_root=*/false);
+ auto y2_support = std::make_unique<CompositorFrameSinkSupport>(
+ nullptr, &frame_sink_manager_, FrameSinkId(4, 1), /*is_root=*/false);
+
+ // Builds a frame with dependencies and a long deadline for activation.
+ auto build_frame = [](std::vector<SurfaceId> deps,
+ std::vector<SurfaceRange> refs) {
+ return CompositorFrameBuilder()
+ .AddRenderPass(gfx::Rect(10, 10), gfx::Rect(10, 10))
+ .SetActivationDependencies(std::move(deps))
+ .SetReferencedSurfaces(std::move(refs))
+ .SetDeadline(FrameDeadline(base::TimeTicks::Now(), 10000u,
+ base::Milliseconds(16), false))
+ .Build();
+ };
+
+ // Each of these SurfaceIds are from the same FrameSinkId but have different
+ // embed_tokens therefore different SurfaceAllocationGroups.
+ std::vector<SurfaceRange> malicious_refs;
+ malicious_refs.reserve(100);
+ for (int i = 0; i < 100; i++) {
+ SurfaceId sid(kArbitraryFrameSinkId,
+ LocalSurfaceId(i, 1, base::UnguessableToken::Create()));
+ malicious_refs.emplace_back(sid);
+ }
+
+ // A SurfaceAllocationGroup for `dep1` is added immediately but groups for
+ // `malicious_refs` are only added once the CompositorFrame activates.
+ SurfaceId dep1(kArbitraryFrameSinkId,
+ LocalSurfaceId(1, 1, base::UnguessableToken::Create()));
+ LocalSurfaceId y1_lsid(1, 1, base::UnguessableToken::Create());
+ y1_support->SubmitCompositorFrame(y1_lsid,
+ build_frame({dep1}, malicious_refs));
+
+ SurfaceId dep2(kArbitraryFrameSinkId,
+ LocalSurfaceId(2, 1, base::UnguessableToken::Create()));
+ LocalSurfaceId y2_lsid(1, 1, base::UnguessableToken::Create());
+ y2_support->SubmitCompositorFrame(y2_lsid, build_frame({dep2}, {}));
+
+ // There will be two SurfaceAllocationGroups for `kArbitraryFrameSinkId` at
+ // this point. WillNotRegisterNewSurfaces() will be called on each allocation
+ // groups, first for `y1_lsid` group which activates the surface and add 100
+ // more SurfaceAllocationGroups to the vector. This tests that modifying
+ // the vector being iterated doesn't cause problems.
+ surface_manager->InvalidateFrameSinkId(kArbitraryFrameSinkId);
+
+ // Both y1 and y2 surfaces are now active.
+ EXPECT_TRUE(surface_manager->GetSurfaceForId(
+ SurfaceId(y1_support->frame_sink_id(), y1_lsid)));
+ EXPECT_TRUE(surface_manager->GetSurfaceForId(
+ SurfaceId(y2_support->frame_sink_id(), y2_lsid)));
+}
+
} // namespace
} // namespace viz
Original Bug Report
Reentrant vector mutation during FrameSink invalidation causes heap use-after-free in Viz process
Reentrant vector mutation during FrameSink invalidation causes heap use-after-free in Viz process
Summary
A heap use-after-free vulnerability exists in the Viz compositor process on all desktop platforms (Linux, macOS, Windows, ChromeOS). SurfaceManager::InvalidateFrameSinkId() iterates a vector of SurfaceAllocationGroup pointers using a range-for loop. During iteration, the synchronous callback chain through WillNotRegisterNewSurfaces can activate a pending surface whose referenced_surfaces metadata triggers creation of new allocation groups via push_back into the same vector. When the vector reallocates its internal buffer, the range-for loop’s captured iterators become dangling, and subsequent iterations read freed heap memory. A compromised renderer can construct the necessary state entirely through the EmbeddedFrameSinkProvider and CompositorFrameSink Mojo interfaces and trigger the invalidation by closing a connection. The crash occurs on the VizCompositorThread, which runs outside the renderer sandbox. ASAN confirms the region is not protected by MiraclePtr.
Bisect
Introducing Commit: 9ef8b6ebe397e7a492210006d949423ba27f5acd
- Date: 2019-04-05
- Author: Saman Sami <samans@chromium.org>
- Review: https://chromium-review.googlesource.com/c/chromium/src/+/1539860
Root Cause
SurfaceManager::InvalidateFrameSinkId looks up all SurfaceAllocationGroup entries associated with a given FrameSinkId and notifies each that no further surfaces will be registered:
// components/viz/service/surfaces/surface_manager.cc:166-174
void SurfaceManager::InvalidateFrameSinkId(const FrameSinkId& frame_sink_id) {
auto it = frame_sink_id_to_allocation_groups_.find(frame_sink_id);
if (it != frame_sink_id_to_allocation_groups_.end()) {
for (SurfaceAllocationGroup* group : it->second) {
group->WillNotRegisterNewSurfaces();
}
}
GarbageCollectSurfaces();
}
The range-for loop captures begin() and end() iterators of it->second, a std::vector<raw_ptr<SurfaceAllocationGroup, VectorExperimental>>. These iterators point into the vector’s internal heap buffer.
WillNotRegisterNewSurfaces moves out all blocked embedders and synchronously resolves their activation dependencies:
// components/viz/service/surfaces/surface_allocation_group.cc:176-182
void SurfaceAllocationGroup::WillNotRegisterNewSurfaces() {
base::flat_map<Surface*, SurfaceId> embedders = std::move(blocked_embedders_);
blocked_embedders_.clear();
for (const auto& entry : embedders) {
entry.first->OnActivationDependencyResolved(entry.second, this);
}
}
If the resolved dependency was the last outstanding one for a surface, OnActivationDependencyResolved calls ActivatePendingFrame, which calls ActivateFrame, which calls RecomputeActiveReferencedSurfaces. That function iterates the activated frame’s referenced_surfaces metadata and calls GetOrCreateAllocationGroupForSurfaceId for each entry:
// components/viz/service/surfaces/surface_manager.cc:656-661
if (!allocation_group) {
allocation_group = std::make_unique<SurfaceAllocationGroup>(
this, surface_id.frame_sink_id(),
surface_id.local_surface_id().embed_token());
frame_sink_id_to_allocation_groups_[surface_id.frame_sink_id()].push_back(
allocation_group.get());
}
When surface_id.frame_sink_id() matches the FrameSinkId currently being invalidated, push_back appends to the same vector that InvalidateFrameSinkId is iterating. If the vector’s capacity is exceeded, it reallocates its internal buffer, freeing the old one. The range-for loop then advances its stale iterator into the freed buffer, reading a dangling SurfaceAllocationGroup* pointer.
A compromised renderer can construct this state through normal Mojo interfaces. It creates a target FrameSink X and three attacker FrameSinks via blink.mojom.EmbeddedFrameSinkProvider.CreateSimpleCompositorFrameSink. Each attacker submits a CompositorFrame with one activation_dependency referencing X (using distinct embed tokens E1, E2, E3), which creates three allocation groups in the vector for X. The first attacker’s frame additionally carries 200 referenced_surfaces entries, each referencing X with a unique embed token. These entries are inert while the frame is pending. When the renderer closes X’s EmbeddedFrameSinkClient connection, the browser forwards an InvalidateFrameSinkId(X) call to the Viz process. The loop processes E1’s group first, resolving the first attacker surface’s sole dependency, activating its pending frame, and processing 200 referenced_surfaces entries. Each entry calls GetOrCreateAllocationGroupForSurfaceId, which push_backs a new group into the vector for X. The vector grows from 3 elements to over 200, reallocating its buffer. The next loop iteration dereferences the stale iterator into the freed 3-element buffer.
No mitigations block this path. There are no CHECK guards on the iteration, no reentrancy protection, and no copy of the vector before iteration. The raw_ptr<VectorExperimental> wrapper on the vector elements protects the pointed-to SurfaceAllocationGroup objects but does not protect the vector’s own internal buffer from iterator invalidation. The mojom deserialization in compositor_frame_metadata_mojom_traits.cc imposes no restrictions on the number or content of referenced_surfaces or activation_dependencies. ASAN confirms “MiraclePtr Status: NOT PROTECTED” for this access.
Reproduce
Tested at commit 7c89d33808e551aed6122c1f324864784011c158.
Apply the attached patch.diff to the renderer source, then build:
git apply issue_framesink_invalidation_uaf/patch.diff
autoninja -C ~/chromium/src/out/asan-release chrome
Launch and open poc.html:
ASAN_OPTIONS=detect_odr_violation=0 xvfb-run -a \
~/chromium/src/out/asan-release/chrome \
--user-data-dir=/tmp/poc-$(date +%s) \
issue_framesink_invalidation_uaf/poc.html
The GPU/Viz process crashes with a heap-use-after-free within approximately one second. The crash occurs on the VizCompositorThread inside SurfaceManager::InvalidateFrameSinkId.
==2458553==ERROR: AddressSanitizer: heap-use-after-free on address 0x7b2e9da4f4c8 at pc 0x7efea76d1825 bp 0x7afe82a9eb30 sp 0x7afe82a9eb28
READ of size 8 at 0x7b2e9da4f4c8 thread T15 (VizCompositorTh)
#0 0x7efea76d1824 in viz::SurfaceManager::InvalidateFrameSinkId(viz::FrameSinkId const&) base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h:1018:47
#1 0x7efea75774b5 in viz::FrameSinkManagerImpl::InvalidateFrameSinkId(viz::FrameSinkId const&, base::OnceCallback<void ()>) components/viz/service/frame_sinks/frame_sink_manager_impl.cc:223:20
#2 0x7efea77696ae in viz::mojom::FrameSinkManagerStubDispatch::AcceptWithResponder(viz::mojom::FrameSinkManager*, mojo::Message*, std::__Cr::unique_ptr<mojo::MessageReceiverWithStatus, std::__Cr::default_delete<mojo::MessageReceiverWithStatus>>) gen/services/viz/privileged/mojom/compositing/frame_sink_manager.mojom.cc:3271:13
0x7b2e9da4f4c8 is located 8 bytes inside of 32-byte region [0x7b2e9da4f4c0,0x7b2e9da4f4e0)
freed by thread T15 (VizCompositorTh) here:
#0 0x5596ce120b02 in operator delete(void*, unsigned long)
#1 0x7efea76db70d in std::__Cr::vector<...>::__emplace_back_slow_path gen/third_party/libc++/src/include/__new/allocate.h:63:10
#2 0x7efea76cfed8 in viz::SurfaceManager::GetOrCreateAllocationGroupForSurfaceId(viz::SurfaceId const&) gen/third_party/libc++/src/include/__vector/vector.h:1148:21
#3 0x7efea76a888f in viz::Surface::RecomputeActiveReferencedSurfaces() components/viz/service/surfaces/surface.cc:634:27
#4 0x7efea76ad04e in viz::Surface::ActivateFrame(viz::Surface::FrameData) components/viz/service/surfaces/surface.cc:697:3
#5 0x7efea76a7df9 in viz::Surface::ActivatePendingFrame() components/viz/service/surfaces/surface.cc:520:3
#6 0x7efea76af659 in viz::Surface::OnActivationDependencyResolved(viz::SurfaceId const&, viz::SurfaceAllocationGroup*) components/viz/service/surfaces/surface.cc:459:3
#7 0x7efea76cb544 in viz::SurfaceAllocationGroup::WillNotRegisterNewSurfaces() components/viz/service/surfaces/surface_allocation_group.cc:180:18
#8 0x7efea76d1781 in viz::SurfaceManager::InvalidateFrameSinkId(viz::FrameSinkId const&) components/viz/service/surfaces/surface_manager.cc:170:14
previously allocated by thread T15 (VizCompositorTh) here:
#0 0x5596ce11fefd in operator new(unsigned long)
#1 0x7efea76db5e4 in std::__Cr::vector<...>::__emplace_back_slow_path gen/third_party/libc++/src/include/__new/allocate.h:43:28
#2 0x7efea76cfed8 in viz::SurfaceManager::GetOrCreateAllocationGroupForSurfaceId(viz::SurfaceId const&) gen/third_party/libc++/src/include/__vector/vector.h:1148:21
#3 0x7efea76ac1d6 in viz::Surface::UpdateActivationDependencies(viz::CompositorFrame const&) components/viz/service/surfaces/surface.cc:802:27
#4 0x7efea76aab53 in viz::Surface::CommitFrame(viz::Surface::FrameData) components/viz/service/surfaces/surface.cc:343:3
#5 0x7efea76a9e4a in viz::Surface::QueueFrame(viz::CompositorFrame, unsigned int, base::ScopedClosureRunner) components/viz/service/surfaces/surface.cc:270:14
#6 0x7efea7541176 in viz::CompositorFrameSinkSupport::MaybeSubmitCompositorFrame components/viz/service/frame_sinks/compositor_frame_sink_support.cc:1014:55
MiraclePtr Status: NOT PROTECTED
No raw_ptr<T> access to this region was detected prior to this crash.
This crash is still exploitable with MiraclePtr.
The complete ASAN log is in asan.log.
Credit
Please use c6eed09fc8b174b0f3eebedcceb1e792 as the credit for this vulnerability. Thank you.