High chrome UAF ⚠️ Exploited in the wild 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
CVSS
Yes
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Dawn
DescriptionUse after free in Dawn
ComponentDawn
Bug ClassUAF
Tracker491518608
Fix commit3c890398bda4 (dawn) +44/-121
CISA KEVNot listed
Credited86ac1f1587b71893ed2ad792cd7dde32
Disclosed2026-03-31

Background

Dawn
Chromium’s implementation of the WebGPU API that translates GPU commands onto native backends such as Vulkan, Metal, and D3D12.
Dawn wire
A client/server serialization protocol that lets a sandboxed renderer process (client) drive GPU objects that actually live in the GPU process (server).
`ObjectData`
The wire server’s bookkeeping record for one native object, holding its handle, AllocationState, and the callback context the server registered for that object.
Spontaneous callbacks
Device-scoped callbacks (uncaptured-error, logging, device-lost) that the native backend can invoke at any time, closing over server-owned state to forward events back to the client.

Root Cause Analysis

When the wire server tore down a device’s ObjectData in the generated ServerDoers.cpp, the AllocationState::Allocated branch called ClearDeviceCallbacks(data.handle) and then Release(data.handle). ClearDeviceCallbacks only deregistered the uncaptured-error and logging callbacks; it never fired the device-lost callback, and it left the native device free to invoke callbacks that still closed over the soon-to-be-freed server context. The invariant that must hold is that no callback closing over server-owned ObjectData can run after that data is deallocated, but Release merely dropped the wire’s reference without guaranteeing the native Device was gone, so an outstanding reference to the backing native device could later trigger a callback into freed memory.

The fix replaces ClearDeviceCallbacks with mProcs->deviceDestroy(data.handle), which both clears the spontaneous callbacks and synchronously fires (and thereby flushes) the device-lost callback before the ObjectData is deallocated. This closes the window because after deviceDestroy the device has no registered callbacks and its terminal callback has already run, so a lingering native reference can no longer reach freed server state.

Key insight
The core mistake was assuming that deregistering a device’s spontaneous callbacks was sufficient teardown, when in fact a pending device-lost callback still needed to fire against valid server memory; the fix calls deviceDestroy to force all callbacks to flush before the ObjectData is freed.

Attack Path

  1. Create a device over the wire From the renderer, request a WebGPU device so the wire server allocates an ObjectData and registers spontaneous callbacks bound to server-owned context.
  2. Retain a native reference Arrange for an outstanding reference to the backing native Device to persist beyond the wire object (for example via an in-flight operation or backend-held reference).
  3. Trigger server teardown Cause the server to free the device’s ObjectData, which under the old code ran ClearDeviceCallbacks and Release without firing the device-lost callback.
  4. Fire a callback into freed memory Provoke the native device to invoke a device-lost or other spontaneous callback, which dereferences the now-freed server context, yielding a use-after-free.

Impact Assessment

An attacker who controls a compromised or scripted renderer gains a use-after-free in the GPU process, where the Dawn wire server runs, by freeing a device’s ObjectData while a native device reference outlives it and later fires a callback. Exploitation requires creating a device over the wire and keeping a backing native reference alive across server teardown so the stale callback references freed memory, which can lead to memory corruption and potential code execution in the GPU process.

Changed Functions

FunctionChangeNotes
if
generator/templates/dawn/wire/server/ServerDoers.cpp
modified
TEST_P
src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
modified
TestEarlyMapCancelled
src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
modified

Files Changed

  • generator/templates/dawn/wire/server/ServerDoers.cpp
  • generator/templates/mock_api.cpp
  • generator/templates/mock_api.h
  • src/dawn/tests/unittests/wire/WireAdapterTests.cpp
  • src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
  • src/dawn/tests/unittests/wire/WireDisconnectTests.cpp
  • src/dawn/tests/unittests/wire/WireQueueTests.cpp

Audit Directions

  • Deregister-vs-destroy teardown
    Flag any cleanup path that only removes or clears callbacks before freeing their captured context; verify a terminal callback (e.g. device-lost) is actually fired or that outstanding references are provably gone.
  • Callback lifetime vs object lifetime
    Audit wire-server ObjectData release paths for cases where a native object can outlive the server record and still invoke callbacks closing over freed server state.
  • Reference release ordering
    Review sites that call Release/Destroy on wire handles to confirm all spontaneous callbacks are flushed before deallocation, not merely detached.
From 3c890398bda440703c55f25cdaf1f800a700970d Mon Sep 17 00:00:00 2001
From: Lokbondo Kung <[email protected]>
Date: Mon, 16 Mar 2026 12:47:12 -0700
Subject: [PATCH] [dawn][wire] Ensure that Devices on the Server always call Destroy.

- Because the wire server creates and manages callback information
  for all Devices, we need to ensure that all Devices' callbacks
  are fired before the server goes away. Otherwise, if the server
  is deleted, and somehow there is an outstanding reference to the
  backing native Device, the callbacks can happen and reference
  freed memory.
- Updates Wire testing infrastructure to:
  1) Use a NiceMock for the ProcTable to avoid overly strict tests
     that end up adding a lot implementation detail expectations.
  2) Add expecatations that successfully created Devices on the
     server should call Destroy to ensure that their callbacks are
     all flushed.
  3) Remove some tech-debt/unused members and functions after the
     change.

Bug: 491518608
Change-Id: I136f7c94ee7e2d79b5b04796bf850a990300aba4
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/297136
Reviewed-by: Kai Ninomiya <[email protected]>
Commit-Queue: Loko Kung <[email protected]>
Reviewed-by: Corentin Wallez <[email protected]>
---

diff --git a/generator/templates/dawn/wire/server/ServerDoers.cpp b/generator/templates/dawn/wire/server/ServerDoers.cpp
index 26cb32f..ebf8cad 100644
--- a/generator/templates/dawn/wire/server/ServerDoers.cpp
+++ b/generator/templates/dawn/wire/server/ServerDoers.cpp
@@ -101,9 +101,11 @@
                     if (data.state == AllocationState::Allocated) {
                         DAWN_ASSERT(data.handle != nullptr);
                         {% if type.name.get() == "device" %}
-                            //* Deregisters uncaptured error and device lost callbacks since
-                            //* they should not be forwarded if the device no longer exists on the wire.
-                            ClearDeviceCallbacks(data.handle);
+                            //* Destroy the device to ensure that the spontaneous callbacks, i.e.
+                            //* the uncaptured error and logging callbacks, are cleared, and the
+                            //* device lost callback is fired. This is important because once we
+                            //* deallocate the ObjectData, those callbacks reference freed memory.
+                            mProcs->deviceDestroy(data.handle);
                         {% endif %}
                         Release(data.handle);
                     }
diff --git a/generator/templates/mock_api.cpp b/generator/templates/mock_api.cpp
index 37e4527..3d8ef48 100644
--- a/generator/templates/mock_api.cpp
+++ b/generator/templates/mock_api.cpp
@@ -141,9 +141,3 @@
 MockProcTable::MockProcTable() = default;
 
 MockProcTable::~MockProcTable() = default;
-
-void MockProcTable::IgnoreAllReleaseCalls() {
-    {% for type in by_category["object"] %}
-        EXPECT_CALL(*this, {{as_CppMethodSuffix(type.name, Name("release"))}}(_)).Times(AnyNumber());
-    {% endfor %}
-}
diff --git a/generator/templates/mock_api.h b/generator/templates/mock_api.h
index d286dbf..0c669ae 100644
--- a/generator/templates/mock_api.h
+++ b/generator/templates/mock_api.h
@@ -140,8 +140,6 @@
         MockProcTable();
         ~MockProcTable() override;
 
-        void IgnoreAllReleaseCalls();
-
         {%- for type in by_category["object"] %}
 
             MOCK_METHOD(void, {{as_MethodSuffix(type.name, Name("add ref"))}}, ({{as_cType(type.name)}} self), (override));
diff --git a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp b/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
index 84d66b0..6052aae 100644
--- a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
@@ -162,7 +162,7 @@
     RequestDevice(&desc);
 
     // Expect the server to receive the message. Then, mock a fake reply.
-    WGPUDevice apiDevice = api.GetNewDevice();
+    WGPUDevice apiDevice = GetNewDevice();
     // The backend device should not be known by the wire server.
     EXPECT_FALSE(GetWireServer()->IsDeviceKnown(apiDevice));
 
@@ -230,7 +230,6 @@
 
     device = nullptr;
     // Cleared when the device is destroyed.
-    EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, _)).Times(1);
     EXPECT_CALL(api, DeviceRelease(apiDevice));
 
     // Server has not recevied the release yet, so the device should be known.
@@ -315,7 +314,7 @@
     adapter = nullptr;
 
     // Mock a reply from the server.
-    WGPUDevice apiDevice = api.GetNewDevice();
+    WGPUDevice apiDevice = GetNewDevice();
     EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), _))
         .WillOnce(InvokeWithoutArgs([&] {
             // Set on device creation to forward callbacks to the client.
@@ -336,12 +335,6 @@
             .WillOnce(WithArg<1>([&](wgpu::Device result) { device = std::move(result); }));
         FlushCallbacks();
     });
-
-    device = nullptr;
-    // Cleared when the device is destroyed.
-    EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, _)).Times(1);
-    EXPECT_CALL(api, DeviceRelease(apiDevice));
-    FlushClient();
 }
 
 // Test that RequestDevice receives unknown status if the wire is disconnected
@@ -364,7 +357,7 @@
     RequestDevice(nullptr);
 
     // Expect the server to receive the message. Then, mock a fake reply.
-    WGPUDevice apiDevice = api.GetNewDevice();
+    WGPUDevice apiDevice = GetNewDevice();
     EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), _))
         .WillOnce(InvokeWithoutArgs([&] {
             // Set on device creation to forward callbacks to the client.
@@ -394,7 +387,6 @@
 
     device = nullptr;
     // Cleared when the device is destroyed.
-    EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, _)).Times(1);
     EXPECT_CALL(api, DeviceRelease(apiDevice));
     FlushClient();
 }
diff --git a/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp b/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
index f607fc2..0346239 100644
--- a/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
@@ -330,12 +330,10 @@
 TEST_P(WireBufferMappingTests, DeviceReleasedTooEarly) {
     TestEarlyMapCancelled([&]() { device = nullptr; },
                           [&]() {
-                              EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, _)).Times(1);
                               EXPECT_CALL(api, DeviceRelease(apiDevice));
                           },
                           wgpu::MapAsyncStatus::Aborted,
                           "The Device was lost before mapping was resolved.", false);
-    DefaultApiDeviceWasReleased();
 }
 
 // Check that if device is released early client-side, we disregard server-side validation errors.
@@ -343,11 +341,9 @@
     TestEarlyMapErrorCancelled(
         [&]() { device = nullptr; },
         [&]() {
-            EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, _)).Times(1);
             EXPECT_CALL(api, DeviceRelease(apiDevice));
         },
         wgpu::MapAsyncStatus::Aborted, "The Device was lost before mapping was resolved.", false);
-    DefaultApiDeviceWasReleased();
 }
 
 // Check the map callback when the map request would have worked, but the device was destroyed.
diff --git a/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp b/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp
index 389a468..291c759 100644
--- a/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp
@@ -147,11 +147,9 @@
     EXPECT_CALL(api, DeviceCreateSampler(apiDevice, _)).WillOnce(Return(apiSampler));
 
     FlushClient();
-
     DeleteClient();
 
     // Expect release on all objects created by the client.
-    EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, _)).Times(1);
     EXPECT_CALL(api, DeviceRelease(apiDevice)).Times(1);
     EXPECT_CALL(api, QueueRelease(apiQueue)).Times(1);
     EXPECT_CALL(api, CommandEncoderRelease(apiCommandEncoder)).Times(1);
@@ -159,10 +157,6 @@
     EXPECT_CALL(api, AdapterRelease(apiAdapter)).Times(1);
     EXPECT_CALL(api, InstanceRelease(apiInstance)).Times(1);
     FlushClient();
-
-    // Signal that we already released and cleared callbacks for |apiDevice|
-    DefaultApiDeviceWasReleased();
-    DefaultApiAdapterWasReleased();
 }
 
 }  // anonymous namespace
diff --git a/src/dawn/tests/unittests/wire/WireQueueTests.cpp b/src/dawn/tests/unittests/wire/WireQueueTests.cpp
index 4ecfe81..18813bf 100644
--- a/src/dawn/tests/unittests/wire/WireQueueTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireQueueTests.cpp
@@ -230,12 +230,7 @@
 
     EXPECT_CALL(api, QueueRelease(apiQueue));
     EXPECT_CALL(api, DeviceRelease(apiDevice));
-    // These set X callback methods are called before the device is released.
-    EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, _)).Times(1);
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp b/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
index 84d66b0..6052aae 100644
--- a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
@@ -162,7 +162,7 @@
     RequestDevice(&desc);
 
     // Expect the server to receive the message. Then, mock a fake reply.
-    WGPUDevice apiDevice = api.GetNewDevice();
+    WGPUDevice apiDevice = GetNewDevice();
     // The backend device should not be known by the wire server.
     EXPECT_FALSE(GetWireServer()->IsDeviceKnown(apiDevice));
 
@@ -230,7 +230,6 @@
 
     device = nullptr;
     // Cleared when the device is destroyed.
-    EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, _)).Times(1);
     EXPECT_CALL(api, DeviceRelease(apiDevice));
 
     // Server has not recevied the release yet, so the device should be known.
@@ -315,7 +314,7 @@
     adapter = nullptr;
 
     // Mock a reply from the server.
-    WGPUDevice apiDevice = api.GetNewDevice();
+    WGPUDevice apiDevice = GetNewDevice();
     EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), _))
         .WillOnce(InvokeWithoutArgs([&] {
             // Set on device creation to forward callbacks to the client.
@@ -336,12 +335,6 @@
             .WillOnce(WithArg<1>([&](wgpu::Device result) { device = std::move(result); }));
         FlushCallbacks();
     });
-
-    device = nullptr;
-    // Cleared when the device is destroyed.
-    EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, _)).Times(1);
-    EXPECT_CALL(api, DeviceRelease(apiDevice));
-    FlushClient();
 }
 
 // Test that RequestDevice receives unknown status if the wire is disconnected
@@ -364,7 +357,7 @@
     RequestDevice(nullptr);
 
     // Expect the server to receive the message. Then, mock a fake reply.
-    WGPUDevice apiDevice = api.GetNewDevice();
+    WGPUDevice apiDevice = GetNewDevice();
     EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), _))
         .WillOnce(InvokeWithoutArgs([&] {
             // Set on device creation to forward callbacks to the client.
@@ -394,7 +387,6 @@
 
     device = nullptr;
     // Cleared when the device is destroyed.
-    EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, _)).Times(1);
     EXPECT_CALL(api, DeviceRelease(apiDevice));
     FlushClient();
 }
diff --git a/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp b/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
index f607fc2..0346239 100644
--- a/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
@@ -330,12 +330,10 @@
 TEST_P(WireBufferMappingTests, DeviceReleasedTooEarly) {
     TestEarlyMapCancelled([&]() { device = nullptr; },
                           [&]() {
-                              EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, _)).Times(1);
                               EXPECT_CALL(api, DeviceRelease(apiDevice));
                           },
                           wgpu::MapAsyncStatus::Aborted,
                           "The Device was lost before mapping was resolved.", false);
-    DefaultApiDeviceWasReleased();
 }
 
 // Check that if device is released early client-side, we disregard server-side validation errors.
@@ -343,11 +341,9 @@
     TestEarlyMapErrorCancelled(
         [&]() { device = nullptr; },
         [&]() {
-            EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, _)).Times(1);
             EXPECT_CALL(api, DeviceRelease(apiDevice));
         },
         wgpu::MapAsyncStatus::Aborted, "The Device was lost before mapping was resolved.", false);
-    DefaultApiDeviceWasReleased();
 }
 
 // Check the map callback when the map request would have worked, but the device was destroyed.
diff --git a/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp b/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp
index 389a468..291c759 100644
--- a/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp
@@ -147,11 +147,9 @@
     EXPECT_CALL(api, DeviceCreateSampler(apiDevice, _)).WillOnce(Return(apiSampler));
 
     FlushClient();
-
     DeleteClient();
 
     // Expect release on all objects created by the client.
-    EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, _)).Times(1);
     EXPECT_CALL(api, DeviceRelease(apiDevice)).Times(1);
     EXPECT_CALL(api, QueueRelease(apiQueue)).Times(1);
     EXPECT_CALL(api, CommandEncoderRelease(apiCommandEncoder)).Times(1);
@@ -159,10 +157,6 @@
     EXPECT_CALL(api, AdapterRelease(apiAdapter)).Times(1);
     EXPECT_CALL(api, InstanceRelease(apiInstance)).Times(1);
     FlushClient();
-
-    // Signal that we already released and cleared callbacks for |apiDevice|
-    DefaultApiDeviceWasReleased();
-    DefaultApiAdapterWasReleased();
 }
 
 }  // anonymous namespace
diff --git a/src/dawn/tests/unittests/wire/WireQueueTests.cpp b/src/dawn/tests/unittests/wire/WireQueueTests.cpp
index 4ecfe81..18813bf 100644
--- a/src/dawn/tests/unittests/wire/WireQueueTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireQueueTests.cpp
@@ -230,12 +230,7 @@
 
     EXPECT_CALL(api, QueueRelease(apiQueue));
     EXPECT_CALL(api, DeviceRelease(apiDevice));
-    // These set X callback methods are called before the device is released.
-    EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, _)).Times(1);
     FlushClient();
-
-    // Indicate to the fixture that the device was already released.
-    DefaultApiDeviceWasReleased();
 }
 
 // Test the device, then its default queue. The default queue should be released when its external
@@ -250,17 +245,12 @@
     device = nullptr;
 
     EXPECT_CALL(api, DeviceRelease(apiDevice));
-    // These set X callback methods are called before the device is released.
-    EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, _)).Times(1);
     FlushClient();
 
     // Release the external queue reference. The queue should be released.
     queue = nullptr;
     EXPECT_CALL(api, QueueRelease(apiQueue));
     FlushClient();
-
-    // Indicate to the fixture that the device was already released.
-    DefaultApiDeviceWasReleased();
 }
 
 // Test that QueueSubmit does an implicit call to OnSubmittedWorkDone for its own tracking, and that
diff --git a/src/dawn/tests/unittests/wire/WireTest.cpp b/src/dawn/tests/unittests/wire/WireTest.cpp
index 41ef354..f22285e 100644
--- a/src/dawn/tests/unittests/wire/WireTest.cpp
+++ b/src/dawn/tests/unittests/wire/WireTest.cpp
@@ -36,6 +36,7 @@
 
 using testing::_;
 using testing::AnyNumber;
+using testing::AtLeast;
 using testing::AtMost;
 using testing::Exactly;
 using testing::Mock;
@@ -56,9 +57,20 @@
 uint32_t sWireProcTableRefCount = 0;
 }  // namespace
 
-WireTest::WireTest() {}
+WireTest::WireTest() {
+    // Set up default expectation for Device.Destroy to ensure we can track that every device on the
+    // server has Destroy called.
+    ON_CALL(api, DeviceDestroy).WillByDefault([this](WGPUDevice device) {
+        mDeviceDestroyed[device] = true;
+    });
+}
 
-WireTest::~WireTest() {}
+WireTest::~WireTest() {
+    // Verify that all devices had Destroy called on them.
+    for (auto& [_, destroyed] : mDeviceDestroyed) {
+        EXPECT_TRUE(destroyed);
+    }
+}
 
 wire::client::MemoryTransferService* WireTest::GetClientMemoryTransferService() {
     return nullptr;
@@ -71,7 +83,6 @@
 void WireTest::SetUp() {
     DawnProcTable mockProcs;
     api.GetProcTable(&mockProcs);
-    SetupIgnoredCallExpectations();
 
     mS2cBuf = std::make_unique<utils::TerribleCommandBuffer>();
     mC2sBuf = std::make_unique<utils::TerribleCommandBuffer>(mWireServer.get());
@@ -141,7 +152,7 @@
     EXPECT_NE(adapter, nullptr);
 
     // Create the device for testing.
-    apiDevice = api.GetNewDevice();
+    apiDevice = GetNewDevice();
     wgpu::DeviceDescriptor deviceDesc = {};
     deviceDesc.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
                                      deviceLostCallback.Callback());
@@ -192,9 +203,6 @@
     apiQueue = api.GetNewQueue();
     EXPECT_CALL(api, DeviceGetQueue(apiDevice)).WillOnce(Return(apiQueue));
     FlushClient();
-
-    cDevice = device.Get();
-    cQueue = queue.Get();
 }
 
 void WireTest::TearDown() {
@@ -210,42 +218,20 @@
 
     // Derived classes should call the base TearDown() first. The client must
     // be reset before any mocks are deleted.
-    // Incomplete client callbacks will be called on deletion, so the mocks
-    // cannot be null.
-    api.IgnoreAllReleaseCalls();
-    mS2cBuf->SetHandler(nullptr);
-    mWireClient = nullptr;
-
-    if (mWireServer && apiDevice) {
-        // These are called on server destruction to clear the callbacks. They must not be
-        // called after the server is destroyed.
-        EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, _))
-            .Times(Exactly(1))
-            .WillOnce(WithArg<1>([](const WGPULoggingCallbackInfo& callbackInfo) {
-                EXPECT_EQ(callbackInfo.callback, nullptr);
-            }));
-    }
-    mC2sBuf->SetHandler(nullptr);
-    mWireServer = nullptr;
+    DeleteClient();
+    DeleteServer();
 }
 
-// This should be called if |apiDevice| no longer exists on the wire.
-// This signals that expectations in |TearDown| shouldn't be added.
-void WireTest::DefaultApiDeviceWasReleased() {
-    apiDevice = nullptr;
-}
-
-// This should be called if |apiAdapter| no longer exists on the wire.
-// This signals that expectations in |TearDown| shouldn't be added.
-void WireTest::DefaultApiAdapterWasReleased() {
-    apiAdapter = nullptr;
+WGPUDevice WireTest::GetNewDevice() {
+    auto device = api.GetNewDevice();
+    mDeviceDestroyed[device] = false;
+    return device;
 }
 
 void WireTest::FlushClient(bool success) {
     ASSERT_EQ(mC2sBuf->Flush(), success);
 
     Mock::VerifyAndClearExpectations(&api);
-    SetupIgnoredCallExpectations();
 }
 
 void WireTest::FlushServer(bool success) {
@@ -265,20 +251,6 @@
 }
 
 void WireTest::DeleteServer() {
-    EXPECT_CALL(api, QueueRelease(apiQueue)).Times(1);
-    EXPECT_CALL(api, DeviceRelease(apiDevice)).Times(1);
-    EXPECT_CALL(api, AdapterRelease(apiAdapter)).Times(1);
-    EXPECT_CALL(api, InstanceRelease(apiInstance)).Times(1);
-
-    if (mWireServer) {
-        // These are called on server destruction to clear the callbacks. They must not be
-        // called after the server is destroyed.
-        EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, _))
-            .Times(Exactly(1))
-            .WillOnce(WithArg<1>([](const WGPULoggingCallbackInfo& callbackInfo) {
-                EXPECT_EQ(callbackInfo.callback, nullptr);
-            }));
-    }
     mC2sBuf->SetHandler(nullptr);
     mWireServer = nullptr;
 }
@@ -288,9 +260,4 @@
     mWireClient = nullptr;
 }
 
-void WireTest::SetupIgnoredCallExpectations() {
-    EXPECT_CALL(api, InstanceProcessEvents(_)).Times(AnyNumber());
-    EXPECT_CALL(api, DeviceTick(_)).Times(AnyNumber());
-}
-
 }  // namespace dawn
diff --git a/src/dawn/tests/unittests/wire/WireTest.h b/src/dawn/tests/unittests/wire/WireTest.h
index b8d61a4..41e8917 100644
--- a/src/dawn/tests/unittests/wire/WireTest.h
+++ b/src/dawn/tests/unittests/wire/WireTest.h
@@ -30,6 +30,7 @@
 
 #include <memory>
... (truncated)
Loading diff…

Original Bug Report

reported by [email protected]

Use-After-Free in Dawn Wire Server Uncaptured-Error Callback via Incomplete Device Unregistration Cleanup

Summary

The Dawn wire server’s device cleanup function fails to clear the uncaptured-error callback when a device is unregistered, leaving a dangling pointer to a freed DeviceInfo structure as the callback’s userdata. A compromised renderer can exploit this by sending an UnregisterObject command for a device followed by a QueueWriteBuffer command with invalid parameters in the same wire command batch. The first command frees the DeviceInfo structure while the native device remains alive due to an extra reference held by Chromium’s WebGPUDecoderImpl. The second command triggers a validation error on the still-alive native device, which fires the uncaptured-error callback and dereferences the freed DeviceInfo pointer, producing a use-after-free in the GPU process. ASAN confirms this as NOT PROTECTED by MiraclePtr. The vulnerability affects all platforms that support WebGPU. No special GPU hardware is required.

Bisect

The vulnerability was introduced when the uncaptured-error callback was migrated from a dynamic deviceSetUncapturedErrorCallback call in Server.cpp to a descriptor-based setup in ServerAdapter.cpp. Before this change, ClearDeviceCallbacks cleared both the uncaptured-error and logging callbacks. The commit removed the uncaptured-error cleanup from ClearDeviceCallbacks but did not add any replacement, leaving the callback with a dangling DeviceInfo* userdata after device unregistration.

Introducing Commit (Dawn): f2a5e573d7022cdc709824d3cc6a1195ef0ba690

Chromium Roll: 5812b73728a49e078d6e3db94389a25de4fe0dfb

  • Date: Fri Jun 21 19:16:00 2024
  • Description: Roll Dawn from b58a264fc729 to 52baba00758d (19 revisions)

Root Cause

When the Dawn wire server creates a device in response to an AdapterRequestDevice command, it registers an uncaptured-error callback with a raw pointer to a heap-allocated DeviceInfo structure as userdata:

// third_party/dawn/src/dawn/wire/server/ServerAdapter.cpp:63-73
desc.uncapturedErrorCallbackInfo = {
    nullptr,
    [](WGPUDevice const*, WGPUErrorType type, WGPUStringView message, void*, void* userdata) {
        DeviceInfo* info = static_cast<DeviceInfo*>(userdata);
        {
            auto serverGuard = info->server->GetGuard();
            info->server->OnUncapturedError(info->self, type, message);
        }
        info->server->Flush();
    },
    nullptr, device->info.get()};

The DeviceInfo is owned by ObjectData<WGPUDevice> as a std::unique_ptr<DeviceInfo>:

// third_party/dawn/src/dawn/wire/server/ObjectStorage.h:78-88
struct DeviceInfo {
    raw_ptr<Server> server;
    ObjectHandle self;
};

template <>
struct ObjectData<WGPUDevice> : public ObjectDataBase<WGPUDevice> {
    // Store |info| as a separate allocation so that its address does not move.
    // The pointer to |info| is used as the userdata to device callback.
    std::unique_ptr<DeviceInfo> info = std::make_unique<DeviceInfo>();
};

The wire server provides a ClearDeviceCallbacks function that is called during device unregistration. This function clears only the logging callback, not the uncaptured-error callback:

// third_party/dawn/src/dawn/wire/server/Server.cpp:200-203
void Server::ClearDeviceCallbacks(WGPUDevice device) {
    // Un-set the logging callback since we cannot forward them after the server has been destroyed.
    mProcs->deviceSetLoggingCallback(device, kEmptyLoggingCallbackInfo);
}

The device unregistration path in the auto-generated doers calls Free<WGPUDevice> to move the ObjectData out of the server’s object table, then calls ClearDeviceCallbacks and Release, and finally lets the local ObjectData go out of scope, which destroys the unique_ptr and frees the DeviceInfo:

// out/asan/gen/third_party/dawn/src/dawn/wire/server/ServerDoers_autogen.cpp:1292-1302
case ObjectType::Device: {
    ObjectData<WGPUDevice> data;
    WIRE_TRY(Free<WGPUDevice>(objectId, &data));
    if (data.state == AllocationState::Allocated) {
        DAWN_ASSERT(data.handle != nullptr);
        ClearDeviceCallbacks(data.handle);
        Release(data.handle);
    }
    return WireResult::Success;
} // data destructor frees data.info (unique_ptr<DeviceInfo>)

The Release(data.handle) call decrements the native device’s external reference count. Under normal circumstances this would trigger WillDropLastExternalRef, which resets the uncaptured-error callback at the native level. However, Chromium’s WebGPUDecoderImpl holds an additional reference to every created device in its known_device_metadata_ map:

// gpu/command_buffer/service/webgpu_decoder_impl.cc:1619-1634
wgpu::Device device_copy = device;
// ...
if (device_copy) {
    known_device_metadata_.emplace(
        std::move(device_copy),
        DeviceMetadata{info.adapterType, info.backendType});
}

This extra reference prevents the external reference count from reaching zero after the wire server’s Release call. The native device remains alive with mState == State::Alive, and its mUncapturedErrorCallbackInfo still holds the raw pointer to the now-freed DeviceInfo structure.

The known_device_metadata_ entries are cleaned up asynchronously in PerformPollingWork, which checks wire_server_->IsDeviceKnown(device.Get()) and erases entries for devices no longer known to the wire server. This cleanup does not run during HandleCommands, so a command batch that unregisters a device and then triggers a validation error on the same device will reliably produce the use-after-free.

The native device’s HandleError method invokes the uncaptured-error callback synchronously when the device is in the Alive state and no error scope captures the error:

// third_party/dawn/src/dawn/native/Device.cpp:827-834
if (!captured && mUncapturedErrorCallbackInfo.callback != nullptr && mState == State::Alive) {
    auto device = ToAPI(this);
    mUncapturedErrorCallbackInfo.callback(
        &device, ToAPI(ToWGPUErrorType(type)), ToOutputStringView(messageStr),
        mUncapturedErrorCallbackInfo.userdata1, mUncapturedErrorCallbackInfo.userdata2);
}

The callback lambda in ServerAdapter.cpp then dereferences the freed DeviceInfo pointer via info->server->GetGuard() and info->server->OnUncapturedError(...), producing the use-after-free.

Reproduce

Tested on Chromium commit 1cf03136f094a16c5d029554426290ad46e58374 on macOS. The ASAN build directory should be configured with the following args.gn:

is_asan = true
is_debug = false
dcheck_always_on = false

This vulnerability is not suitable for a MojoJS-based PoC because the exploit operates through the Dawn wire protocol embedded inside the GPU command buffer’s shared memory ring, not through direct Mojo IPC messages. The patch modifies two renderer-side files to simulate a compromised renderer. Both modification sites include a cmdline->HasSwitch("type") && cmdline->GetSwitchValueASCII("type") == "renderer" process guard to ensure all changes execute exclusively in the renderer process. The ASAN crash is captured in the GPU process.

git apply patch.diff
autoninja -C out/asan chrome

Launch Chrome:

./out/asan/Chromium.app/Contents/MacOS/Chromium --user-data-dir=./userdata poc.html

The PoC page creates a WebGPU device, obtains its queue, and creates a small buffer with COPY_DST usage. It then calls device.destroy(), which the patch redirects to send an UnregisterObject command for the device. Immediately afterwards, it calls queue.writeBuffer with a buffer offset far exceeding the buffer’s size. Both commands are flushed together. The GPU process wire server processes the UnregisterObject first, freeing the DeviceInfo structure, then processes the QueueWriteBuffer, which triggers a validation error on the native device. The uncaptured-error callback fires with the freed DeviceInfo pointer as userdata, and ASAN reports a heap-use-after-free when the callback dereferences it.

ASAN output:

=================================================================
==44414==ERROR: AddressSanitizer: heap-use-after-free on address 0x60200007df70 at pc 0x0003621ac1d0 bp 0x00016af743b0 sp 0x00016af743a8
READ of size 8 at 0x60200007df70 thread T0
==44414==WARNING: invalid path to external symbolizer!
==44414==WARNING: Failed to use and restart external symbolizer!
    #0 0x0003621ac1cc in dawn::wire::server::Server::DoAdapterRequestDevice(dawn::wire::server::Known<WGPUAdapterImpl*>, dawn::wire::ObjectHandle, WGPUFuture, dawn::wire::ObjectHandle, WGPUFuture, WGPUDeviceDescriptor const*)::$_0::__invoke(WGPUDeviceImpl* const*, WGPUErrorType, WGPUStringView, void*, void*)+0x278 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x186e01cc)
    #1 0x00034adbb080 in dawn::native::DeviceBase::HandleError(std::__Cr::unique_ptr<dawn::native::ErrorData, std::__Cr::default_delete<dawn::native::ErrorData>>, dawn::native::InternalErrorType, wgpu::DeviceLostReason, dawn::native::DeviceBase::ForwardToErrorScope)+0x560 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x12ef080)
    #2 0x00034aea2158 in dawn::native::QueueBase::APIWriteBuffer(dawn::native::BufferBase*, unsigned long long, void const*, unsigned long)+0x220 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x13d6158)
    #3 0x00034ac92f38 in dawn::native::NativeQueueWriteBuffer(WGPUQueueImpl*, WGPUBufferImpl*, unsigned long long, void const*, unsigned long)+0xf4 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x11c6f38)
    #4 0x0003621a64f4 in dawn::wire::server::Server::DoQueueWriteBuffer(dawn::wire::server::Known<WGPUQueueImpl*>, dawn::wire::server::Known<WGPUBufferImpl*>, unsigned long long, unsigned char const*, unsigned long long)+0xd0 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x186da4f4)
    #5 0x0003621b74b8 in dawn::wire::server::Server::HandleQueueWriteBuffer(dawn::wire::DeserializeBuffer*)+0x2c8 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x186eb4b8)
    #6 0x0003621ba660 in dawn::wire::server::Server::HandleCommands(char const volatile*, unsigned long)+0xe00 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x186ee660)
    #7 0x0003621ef38c in gpu::webgpu::(anonymous namespace)::DawnWireServer::HandleCommands(char const volatile*, unsigned long)+0x154 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1872338c)
    #8 0x0003621ef790 in gpu::webgpu::(anonymous namespace)::WebGPUDecoderImpl::HandleDawnCommands(unsigned int, void const volatile*)+0x2e8 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x18723790)
    #9 0x0003621e5714 in gpu::webgpu::(anonymous namespace)::WebGPUDecoderImpl::DoCommands(unsigned int, void const volatile*, int, int*)+0x200 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x18719714)
    #10 0x00035189af08 in gpu::CommandBufferService::Flush(int, gpu::AsyncAPIInterface*)+0x4bc (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x7dcef08)
    #11 0x0003620fb380 in gpu::CommandBufferStub::OnAsyncFlush(int, unsigned int, std::__Cr::vector<gpu::SyncToken, std::__Cr::allocator<gpu::SyncToken>> const&)+0x450 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1862f380)
    #12 0x0003620fa4e8 in gpu::CommandBufferStub::ExecuteDeferredRequest(gpu::mojom::DeferredCommandBufferRequestParams&, gpu::FenceSyncReleaseDelegate*)+0x468 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1862e4e8)
    #13 0x000362118dcc in gpu::GpuChannel::ExecuteDeferredRequest(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*)+0x290 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1864cdcc)
    #14 0x000362124a84 in void base::internal::DecayedFunctorTraits<void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel>&&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>&&>::Invoke<void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel> const&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*>(void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel> const&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>&&, gpu::FenceSyncReleaseDelegate*&&)+0x144 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x18658a84)
    #15 0x00036212489c in base::internal::Invoker<base::internal::FunctorTraits<void (gpu::GpuChannel::*&&)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel>&&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>&&>, base::internal::BindState<true, true, false, void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel>, mojo::StructPtr<gpu::mojom::DeferredRequestParams>>, void (gpu::FenceSyncReleaseDelegate*)>::RunOnce(base::internal::BindStateBase*, gpu::FenceSyncReleaseDelegate*)+0x118 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1865889c)
    #16 0x0003518d3d58 in void base::internal::Invoker<base::internal::FunctorTraits<base::OnceCallback<void (gpu::FenceSyncReleaseDelegate*)>&&, gpu::FenceSyncReleaseDelegate*>, base::internal::BindState<false, true, true, base::OnceCallback<void (gpu::FenceSyncReleaseDelegate*)>, base::internal::UnretainedWrapper<gpu::FenceSyncReleaseDelegate, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>>, void ()>::RunImpl<base::OnceCallback<void (gpu::FenceSyncReleaseDelegate*)>, std::__Cr::tuple<base::internal::UnretainedWrapper<gpu::FenceSyncReleaseDelegate, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>>, 0ul>(base::OnceCallback<void (gpu::FenceSyncReleaseDelegate*)>&&, std::__Cr::tuple<base::internal::UnretainedWrapper<gpu::FenceSyncReleaseDelegate, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>>&&, std::__Cr::integer_sequence<unsigned long, 0ul>)+0x1c8 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x7e07d58)
    #17 0x0003518ae838 in gpu::Scheduler::ExecuteSequence(base::IdType<gpu::SyncPointOrderData, unsigned int, 0u, 1u>)+0x634 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x7de2838)
    #18 0x0003518aced0 in gpu::Scheduler::RunNextTask()+0x27c (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x7de0ed0)
    #19 0x0003518b026c in base::internal::Invoker<base::internal::FunctorTraits<void (gpu::Scheduler::*&&)(), gpu::Scheduler*>, base::internal::BindState<true, true, false, void (gpu::Scheduler::*)(), base::internal::UnretainedWrapper<gpu::Scheduler, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>>, void ()>::RunOnce(base::internal::BindStateBase*)+0x184 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x7de426c)
    #20 0x00035beea8a4 in base::TaskAnnotator::RunTaskImpl(base::PendingTask&)+0x348 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1241e8a4)
    #21 0x00035bf5290c in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow*)+0x88c (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1248690c)
    #22 0x00035bf51cc4 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork()+0x138 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x12485cc4)
    #23 0x00035c073330 in base::MessagePumpCFRunLoopBase::RunWork()+0x1c8 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x125a7330)
    #24 0x00035c0649e0 in base::apple::CallWithEHFrame(void () block_pointer)+0xc (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x125989e0)
    #25 0x00035c071798 in base::MessagePumpCFRunLoopBase::RunWorkSource(void*)+0xe4 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x125a5798)
    #26 0x00019b3549f4 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__+0x18 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation:arm64e+0x5f9f4)
    #27 0x00019b354988 in __CFRunLoopDoSource0+0xa8 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation:arm64e+0x5f988)
    #28 0x00019b3546f4 in __CFRunLoopDoSources0+0xe4 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation:arm64e+0x5f6f4)
    #29 0x00019b353384 in __CFRunLoopRun+0x330 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation:arm64e+0x5e384)
    #30 0x00019b40de30 in _CFRunLoopRunSpecificWithOptions+0x210 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation:arm64e+0x118e30)
    #31 0x00019d5a2960 in -[NSRunLoop(NSRunLoop) runMode:beforeDate:]+0xd0 (/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation:arm64e+0xa5b960)
    #32 0x00035c07448c in base::MessagePumpNSRunLoop::DoRun(base::MessagePump::Delegate*)+0xc8 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x125a848c)
    #33 0x00035c070500 in base::MessagePumpCFRunLoopBase::Run(base::MessagePump::Delegate*)+0x290 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x125a4500)
    #34 0x00035bf53c6c in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::Run(bool, base::TimeDelta)+0x32c (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x12487c6c)
    #35 0x00035be7885c in base::RunLoop::Run(base::Location const&)+0x430 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x123ac85c)
    #36 0x00036523bea8 in content::GpuMain(content::MainFunctionParams)+0x8b4 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1b76fea8)
    #37 0x0003585cbdd0 in content::RunOtherNamedProcessTypeMain(std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char>> const&, content::MainFunctionParams, content::ContentMainDelegate*)+0x420 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0xeaffdd0)
    #38 0x0003585cdf50 in content::ContentMainRunnerImpl::Run()+0x53c (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0xeb01f50)
    #39 0x0003585c9ac0 in content::RunContentProcess(content::ContentMainParams, content::ContentMainRunner*)+0x858 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0xeafdac0)
    #40 0x0003585c9fb0 in content::ContentMain(content::ContentMainParams)+0x190 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0xeafdfb0)
    #41 0x000349ad1cb4 in ChromeMain+0x490 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x5cb4)
    #42 0x000104e88c94 in main+0x254 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Helpers/Chromium Helper.app/Contents/MacOS/Chromium Helper:arm64+0x100000c94)
    #43 0x00019aeedd50 in start+0x1c0c (/usr/lib/dyld:arm64e+0x8d50)

0x60200007df70 is located 0 bytes inside of 16-byte region [0x60200007df70,0x60200007df80)
freed by thread T0 here:
    #0 0x00010542d074 in __asan_memmove+0x308c (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Helpers/Chromium Helper.app/Contents/MacOS/libclang_rt.asan_osx_dynamic.dylib:arm64+0x55074)
    #1 0x00036219e574 in dawn::wire::server::Server::DoUnregisterObject(dawn::wire::ObjectType, unsigned int)+0x578 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x186d2574)
    #2 0x0003621bac44 in dawn::wire::server::Server::HandleCommands(char const volatile*, unsigned long)+0x13e4 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x186eec44)
    #3 0x0003621ef38c in gpu::webgpu::(anonymous namespace)::DawnWireServer::HandleCommands(char const volatile*, unsigned long)+0x154 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1872338c)
    #4 0x0003621ef790 in gpu::webgpu::(anonymous namespace)::WebGPUDecoderImpl::HandleDawnCommands(unsigned int, void const volatile*)+0x2e8 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x18723790)
    #5 0x0003621e5714 in gpu::webgpu::(anonymous namespace)::WebGPUDecoderImpl::DoCommands(unsigned int, void const volatile*, int, int*)+0x200 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x18719714)
    #6 0x00035189af08 in gpu::CommandBufferService::Flush(int, gpu::AsyncAPIInterface*)+0x4bc (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x7dcef08)
    #7 0x0003620fb380 in gpu::CommandBufferStub::OnAsyncFlush(int, unsigned int, std::__Cr::vector<gpu::SyncToken, std::__Cr::allocator<gpu::SyncToken>> const&)+0x450 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1862f380)
    #8 0x0003620fa4e8 in gpu::CommandBufferStub::ExecuteDeferredRequest(gpu::mojom::DeferredCommandBufferRequestParams&, gpu::FenceSyncReleaseDelegate*)+0x468 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1862e4e8)
    #9 0x000362118dcc in gpu::GpuChannel::ExecuteDeferredRequest(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*)+0x290 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1864cdcc)
    #10 0x000362124a84 in void base::internal::DecayedFunctorTraits<void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel>&&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>&&>::Invoke<void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel> const&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*>(void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel> const&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>&&, gpu::FenceSyncReleaseDelegate*&&)+0x144 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x18658a84)
    #11 0x00036212489c in base::internal::Invoker<base::internal::FunctorTraits<void (gpu::GpuChannel::*&&)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel>&&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>&&>, base::internal::BindState<true, true, false, void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel>, mojo::StructPtr<gpu::mojom::DeferredRequestParams>>, void (gpu::FenceSyncReleaseDelegate*)>::RunOnce(base::internal::BindStateBase*, gpu::FenceSyncReleaseDelegate*)+0x118 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1865889c)
    #12 0x0003518d3d58 in void base::internal::Invoker<base::internal::FunctorTraits<base::OnceCallback<void (gpu::FenceSyncReleaseDelegate*)>&&, gpu::FenceSyncReleaseDelegate*>, base::internal::BindState<false, true, true, base::OnceCallback<void (gpu::FenceSyncReleaseDelegate*)>, base::internal::UnretainedWrapper<gpu::FenceSyncReleaseDelegate, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>>, void ()>::RunImpl<base::OnceCallback<void (gpu::FenceSyncReleaseDelegate*)>, std::__Cr::tuple<base::internal::UnretainedWrapper<gpu::FenceSyncReleaseDelegate, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>>, 0ul>(base::OnceCallback<void (gpu::FenceSyncReleaseDelegate*)>&&, std::__Cr::tuple<base::internal::UnretainedWrapper<gpu::FenceSyncReleaseDelegate, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>>&&, std::__Cr::integer_sequence<unsigned long, 0ul>)+0x1c8 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x7e07d58)
    #13 0x0003518ae838 in gpu::Scheduler::ExecuteSequence(base::IdType<gpu::SyncPointOrderData, unsigned int, 0u, 1u>)+0x634 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x7de2838)
    #14 0x0003518aced0 in gpu::Scheduler::RunNextTask()+0x27c (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x7de0ed0)
    #15 0x0003518b026c in base::internal::Invoker<base::internal::FunctorTraits<void (gpu::Scheduler::*&&)(), gpu::Scheduler*>, base::internal::BindState<true, true, false, void (gpu::Scheduler::*)(), base::internal::UnretainedWrapper<gpu::Scheduler, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>>, void ()>::RunOnce(base::internal::BindStateBase*)+0x184 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x7de426c)
    #16 0x00035beea8a4 in base::TaskAnnotator::RunTaskImpl(base::PendingTask&)+0x348 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1241e8a4)
    #17 0x00035bf5290c in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow*)+0x88c (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1248690c)
    #18 0x00035bf51cc4 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork()+0x138 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x12485cc4)
    #19 0x00035c073330 in base::MessagePumpCFRunLoopBase::RunWork()+0x1c8 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x125a7330)
    #20 0x00035c0649e0 in base::apple::CallWithEHFrame(void () block_pointer)+0xc (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x125989e0)
    #21 0x00035c071798 in base::MessagePumpCFRunLoopBase::RunWorkSource(void*)+0xe4 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x125a5798)
    #22 0x00019b3549f4 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__+0x18 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation:arm64e+0x5f9f4)
    #23 0x00019b354988 in __CFRunLoopDoSource0+0xa8 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation:arm64e+0x5f988)
    #24 0x00019b3546f4 in __CFRunLoopDoSources0+0xe4 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation:arm64e+0x5f6f4)
    #25 0x00019b353384 in __CFRunLoopRun+0x330 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation:arm64e+0x5e384)
    #26 0x00019b40de30 in _CFRunLoopRunSpecificWithOptions+0x210 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation:arm64e+0x118e30)
    #27 0x00019d5a2960 in -[NSRunLoop(NSRunLoop) runMode:beforeDate:]+0xd0 (/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation:arm64e+0xa5b960)
    #28 0x00035c07448c in base::MessagePumpNSRunLoop::DoRun(base::MessagePump::Delegate*)+0xc8 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x125a848c)
    #29 0x00035c070500 in base::MessagePumpCFRunLoopBase::Run(base::MessagePump::Delegate*)+0x290 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x125a4500)

previously allocated by thread T0 here:
    #0 0x00010542cf84 in __asan_memmove+0x2f9c (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Helpers/Chromium Helper.app/Contents/MacOS/libclang_rt.asan_osx_dynamic.dylib:arm64+0x54f84)
    #1 0x0003724bf314 in operator new(unsigned long)+0x18 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x289f3314)
    #2 0x0003621ac2b8 in dawn::wire::server::KnownObjectsBase<WGPUDeviceImpl*>::Allocate(dawn::wire::server::Reserved<WGPUDeviceImpl*>*, dawn::wire::ObjectHandle, dawn::wire::server::AllocationState)+0xac (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x186e02b8)
    #3 0x0003621ab384 in dawn::wire::server::Server::DoAdapterRequestDevice(dawn::wire::server::Known<WGPUAdapterImpl*>, dawn::wire::ObjectHandle, WGPUFuture, dawn::wire::ObjectHandle, WGPUFuture, WGPUDeviceDescriptor const*)+0x150 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x186df384)
    #4 0x0003621b1660 in dawn::wire::server::Server::HandleAdapterRequestDevice(dawn::wire::DeserializeBuffer*)+0x224 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x186e5660)
    #5 0x0003621bab20 in dawn::wire::server::Server::HandleCommands(char const volatile*, unsigned long)+0x12c0 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x186eeb20)
    #6 0x0003621ef38c in gpu::webgpu::(anonymous namespace)::DawnWireServer::HandleCommands(char const volatile*, unsigned long)+0x154 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1872338c)
    #7 0x0003621ef790 in gpu::webgpu::(anonymous namespace)::WebGPUDecoderImpl::HandleDawnCommands(unsigned int, void const volatile*)+0x2e8 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x18723790)
    #8 0x0003621e5714 in gpu::webgpu::(anonymous namespace)::WebGPUDecoderImpl::DoCommands(unsigned int, void const volatile*, int, int*)+0x200 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x18719714)
    #9 0x00035189af08 in gpu::CommandBufferService::Flush(int, gpu::AsyncAPIInterface*)+0x4bc (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x7dcef08)
    #10 0x0003620fb380 in gpu::CommandBufferStub::OnAsyncFlush(int, unsigned int, std::__Cr::vector<gpu::SyncToken, std::__Cr::allocator<gpu::SyncToken>> const&)+0x450 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1862f380)
    #11 0x0003620fa4e8 in gpu::CommandBufferStub::ExecuteDeferredRequest(gpu::mojom::DeferredCommandBufferRequestParams&, gpu::FenceSyncReleaseDelegate*)+0x468 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1862e4e8)
    #12 0x000362118dcc in gpu::GpuChannel::ExecuteDeferredRequest(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*)+0x290 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1864cdcc)
    #13 0x000362124a84 in void base::internal::DecayedFunctorTraits<void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel>&&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>&&>::Invoke<void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel> const&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*>(void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel> const&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>&&, gpu::FenceSyncReleaseDelegate*&&)+0x144 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x18658a84)
    #14 0x00036212489c in base::internal::Invoker<base::internal::FunctorTraits<void (gpu::GpuChannel::*&&)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel>&&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>&&>, base::internal::BindState<true, true, false, void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel>, mojo::StructPtr<gpu::mojom::DeferredRequestParams>>, void (gpu::FenceSyncReleaseDelegate*)>::RunOnce(base::internal::BindStateBase*, gpu::FenceSyncReleaseDelegate*)+0x118 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1865889c)
    #15 0x0003518d3d58 in void base::internal::Invoker<base::internal::FunctorTraits<base::OnceCallback<void (gpu::FenceSyncReleaseDelegate*)>&&, gpu::FenceSyncReleaseDelegate*>, base::internal::BindState<false, true, true, base::OnceCallback<void (gpu::FenceSyncReleaseDelegate*)>, base::internal::UnretainedWrapper<gpu::FenceSyncReleaseDelegate, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>>, void ()>::RunImpl<base::OnceCallback<void (gpu::FenceSyncReleaseDelegate*)>, std::__Cr::tuple<base::internal::UnretainedWrapper<gpu::FenceSyncReleaseDelegate, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>>, 0ul>(base::OnceCallback<void (gpu::FenceSyncReleaseDelegate*)>&&, std::__Cr::tuple<base::internal::UnretainedWrapper<gpu::FenceSyncReleaseDelegate, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>>&&, std::__Cr::integer_sequence<unsigned long, 0ul>)+0x1c8 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x7e07d58)
    #16 0x0003518ae838 in gpu::Scheduler::ExecuteSequence(base::IdType<gpu::SyncPointOrderData, unsigned int, 0u, 1u>)+0x634 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x7de2838)
    #17 0x0003518aced0 in gpu::Scheduler::RunNextTask()+0x27c (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x7de0ed0)
    #18 0x0003518b026c in base::internal::Invoker<base::internal::FunctorTraits<void (gpu::Scheduler::*&&)(), gpu::Scheduler*>, base::internal::BindState<true, true, false, void (gpu::Scheduler::*)(), base::internal::UnretainedWrapper<gpu::Scheduler, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>>, void ()>::RunOnce(base::internal::BindStateBase*)+0x184 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x7de426c)
    #19 0x00035beea8a4 in base::TaskAnnotator::RunTaskImpl(base::PendingTask&)+0x348 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1241e8a4)
    #20 0x00035bf5290c in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow*)+0x88c (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x1248690c)
    #21 0x00035bf51cc4 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork()+0x138 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x12485cc4)
    #22 0x00035c073330 in base::MessagePumpCFRunLoopBase::RunWork()+0x1c8 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x125a7330)
    #23 0x00035c0649e0 in base::apple::CallWithEHFrame(void () block_pointer)+0xc (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x125989e0)
    #24 0x00035c071798 in base::MessagePumpCFRunLoopBase::RunWorkSource(void*)+0xe4 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x125a5798)
    #25 0x00019b3549f4 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__+0x18 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation:arm64e+0x5f9f4)
    #26 0x00019b354988 in __CFRunLoopDoSource0+0xa8 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation:arm64e+0x5f988)
    #27 0x00019b3546f4 in __CFRunLoopDoSources0+0xe4 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation:arm64e+0x5f6f4)
    #28 0x00019b353384 in __CFRunLoopRun+0x330 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation:arm64e+0x5e384)
    #29 0x00019b40de30 in _CFRunLoopRunSpecificWithOptions+0x210 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation:arm64e+0x118e30)

SUMMARY: AddressSanitizer: heap-use-after-free (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x186e01cc) in dawn::wire::server::Server::DoAdapterRequestDevice(dawn::wire::server::Known<WGPUAdapterImpl*>, dawn::wire::ObjectHandle, WGPUFuture, dawn::wire::ObjectHandle, WGPUFuture, WGPUDeviceDescriptor const*)::$_0::__invoke(WGPUDeviceImpl* const*, WGPUErrorType, WGPUStringView, void*, void*)+0x278
Shadow bytes around the buggy address:
  0x60200007dc80: f7 fa fd fa f7 fa fd fa f7 fa fc fa f7 fa fd fa
  0x60200007dd00: f7 fa 00 00 f7 fa fd fd f7 fa fd fa f7 fa fd fa
  0x60200007dd80: f7 fa fd fd f7 fa 00 00 f7 fa 00 00 f7 fa 00 00
  0x60200007de00: f7 fa fd fd f7 fa 00 00 f7 fa 00 00 f7 fa 00 00
  0x60200007de80: f7 fa fd fd f7 fa 00 00 f7 fa fd fa f7 fa fd fa
=>0x60200007df00: f7 fa 00 00 f7 fa fd fd f7 fa 00 00 f7 fa[fd]fd
  0x60200007df80: f7 fa fd fa f7 fa fd fa f7 fa fd fd f7 fa fd fa
  0x60200007e000: f7 fa 00 00 f7 fa fd fd f7 fa fd fd f7 fa fd fd
  0x60200007e080: f7 fa fd fd f7 fa fd fd f7 fa fd fd f7 fa fd fd
  0x60200007e100: f7 fa fd fd f7 fa fd fd f7 fa fd fd f7 fa 00 00
  0x60200007e180: f7 fa 00 00 f7 fa 00 fa f7 fa 00 fa f7 fa fd fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb

==44414==ADDITIONAL INFO

==44414==Note: Please include this section with the ASan report.
Task trace:
    #0 0x0003518a8afc in gpu::Scheduler::TryScheduleSequence(gpu::Scheduler::Sequence*)+0x48c (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/147.0.7725.0/Chromium Framework:arm64+0x7ddcafc)

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.
Refer to https://chromium.googlesource.com/chromium/src/+/main/base/memory/raw_ptr.md for details.

==44414==END OF ADDITIONAL INFO

==44414==ABORTING
[44366:69197717:0309/192738.378215:ERROR:content/browser/gpu/gpu_process_host.cc:999] GPU process exited unexpectedly: exit_code=256

References

Credit

Please use 86ac1f1587b71893ed2ad792cd7dde32 as the credit for this vulnerability. Thank you.

View on issue tracker
Links in the report