High patch-now ✔︎ verified CVSS 7.5 chrome UAF 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
7.5
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Dawn
DescriptionUse after free in Dawn
ComponentDawn
Sub-componentDawn
Bug ClassUAF
Action tierpatch-now — corruption primitive, web-reachable in default config (confirmed) — upheld by independent review by AI
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Independent review by AIConfirmed
Reviewer notes: none -- diff deletes BufferMapWriteState/cached writeState, re-queries bufferGetMappedRange at update time and returns Success on nullptr (destroyed/unmapped), matching the claimed stale-pointer UAF-write mechanism in the GPU wire server.
A second, independent AI reviewer (not a human) re-derived this analysis from the fixing commit and the vendor advisory, looking for errors.
Tracker492139412
Fix commit4ba836a41006 (dawn) +251/-110
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
Credited86ac1f1587b71893ed2ad792cd7dde32
Reported2026-03-12 (per vendor advisory)
Fix landed2026-03-24 (public git)
Disclosed2026-03-31
Patch gap7 days — fix public in source before the advisory

Background

Dawn wire
Dawn’s client/server serialization layer that lets a sandboxed renderer (the WireClient) drive WebGPU in the GPU process (the wire Server) by passing commands over an IPC channel.
Buffer mapping
the WebGPU mechanism that exposes a GPU buffer’s memory to the client for CPU reads/writes; the wire server tracks per-buffer mapping information in the BufferMapState struct in ObjectStorage.h.
`MemoryTransferService::WriteHandle`
a server-side object that holds a target pointer (set via SetTarget) into mapped buffer memory and applies client-supplied bytes to it through DeserializeDataUpdate.
`bufferGetMappedRange`
the backend proc returning the buffer’s current mapped-memory pointer, or nullptr when the buffer is no longer mapped (for example after an implicit unmap caused by device.Destroy()).

Root Cause Analysis

The wire server’s buffer-update path cached a pointer to the mapped memory at map time and reused it later without re-validating that the buffer was still mapped. On a successful write-map (and on mappedAtCreation), the server called writeHandle->SetTarget(mProcs->bufferGetMappedRange(...) - offset) and recorded mapState->writeState = BufferMapWriteState::Mapped. When the client subsequently sent an update-mapped-data command, the handler switched on the cached writeState; in the Mapped case it proceeded to writeHandle->DeserializeDataUpdate(...), writing client-controlled bytes through the previously stored target pointer. The violated invariant is that the cached target pointer stays valid for the buffer’s whole mapped lifetime — but a buffer can be implicitly unmapped (notably by device.Destroy()), freeing or invalidating that memory while writeState still reads Mapped, so the deserialize wrote into a stale (freed) pointer: a use-after-free write in the GPU process.

The fix deletes the cached writeState flag and the BufferMapWriteState enum entirely and instead re-queries bufferGetMappedRange(buffer->handle, offset, size) at update time; a nullptr result (unmapped/destroyed/error buffer) short-circuits with WireResult::Success and no write, and otherwise the handle’s length and target are set fresh from the live pointer before any deserialize occurs.

Key insight
The single mistake was caching a mapped-memory pointer at map time and reusing it at update time behind a writeState flag that could silently go stale after an implicit unmap; the fix stops caching and re-fetches the live mapped range on every update, treating a nullptr as “no longer mapped” and skipping the write.

Attack Path

  1. Map a buffer for writing The client issues a write MapAsync (or mappedAtCreation), and the server succeeds, caches the mapped-range pointer in the WriteHandle target, and marks mapState->writeState = BufferMapWriteState::Mapped.
  2. Trigger an implicit unmap The client calls device.Destroy() (or otherwise causes the buffer’s backing memory to be freed/unmapped) while the server still records the buffer as Mapped; the patch’s new comment explicitly names device.Destroy() as a reason the mapped range would now be nullptr.
  3. Send an update-mapped-data command Pre-patch, the server switched on the stale Mapped state and called writeHandle->DeserializeDataUpdate(...) through the dangling cached target pointer.
  4. Use-after-free write The client-controlled deserialized bytes are written into freed/invalid memory in the GPU process. Any further read/write primitive or escalation beyond this UAF write is (inferred) and not shown by the diff.

Impact Assessment

An attacker controlling a malicious or compromised renderer (the wire client) can drive the GPU-process wire server into writing client-supplied data through a dangling pointer to freed buffer memory — a use-after-free write in the GPU process — with preconditions limited to issuing ordinary WebGPU wire commands (map a buffer for write, destroy the device, then update mapped data). The vendor advisory rates this High severity and describes it only as “Use after free in Dawn,” reporting no in-the-wild exploitation. Escalation from this UAF write to broader memory corruption or code execution in the GPU process is (inferred) and not demonstrated by the patch itself.

Changed Functions

FunctionChangeNotes
if
src/dawn/wire/server/ServerBuffer.cpp
modified
switch
src/dawn/wire/server/ServerBuffer.cpp
modified
if
src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
modified
TEST_P
src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
modified
TestCancelInCallback
src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
modified

Files Changed

  • include/dawn/wire/WireClient.h
  • src/dawn/tests/BUILD.gn
  • src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
  • src/dawn/tests/unittests/wire/WireMemoryTransferServiceTests.cpp
  • src/dawn/tests/unittests/wire/WireSpecificCommandTests.cpp
  • src/dawn/tests/unittests/wire/WireTest.cpp
  • src/dawn/tests/unittests/wire/WireTest.h
  • src/dawn/wire/WireClient.cpp
  • src/dawn/wire/server/ObjectStorage.h
  • src/dawn/wire/server/ServerBuffer.cpp

Audit Directions

  • Cached pointers to externally-owned memory
    review every wire-server site that stores a raw pointer into backend-owned memory (e.g., SetTarget, cached mapped ranges) and reuses it across separate commands; such pointers should be re-validated at use time, not trusted via a cached flag.
  • State flags invalidated out of band
    hunt for state machines like writeState that can be made stale by out-of-band operations such as device.Destroy() or implicit unmap without the tracker being notified, so a later handler acts on an obsolete state.
  • Sibling handle types
    the fix touched only the write path; audit ReadHandle and other MemoryTransferService handles for the same cache-and-reuse-the-mapped-pointer pattern.
diff --git a/include/dawn/wire/WireClient.h b/include/dawn/wire/WireClient.h
index 3b935ce..6472e40 100644
--- a/include/dawn/wire/WireClient.h
+++ b/include/dawn/wire/WireClient.h
@@ -96,6 +96,8 @@
     // Commands allocated after this point will not be sent.
     void Disconnect();
 
+    client::Client* GetImplForTesting();
+
   private:
     std::unique_ptr<client::Client> mImpl;
 };
diff --git a/src/dawn/wire/WireClient.cpp b/src/dawn/wire/WireClient.cpp
index 978c475..2d4eaa3 100644
--- a/src/dawn/wire/WireClient.cpp
+++ b/src/dawn/wire/WireClient.cpp
@@ -91,6 +91,10 @@
     return wireDevice->GetWireHandle(mImpl.get());
 }
 
+client::Client* WireClient::GetImplForTesting() {
+    return mImpl.get();
+}
+
 namespace client {
 MemoryTransferService::MemoryTransferService() = default;
 
diff --git a/src/dawn/wire/server/ObjectStorage.h b/src/dawn/wire/server/ObjectStorage.h
index c9ecb25..b6ee390 100644
--- a/src/dawn/wire/server/ObjectStorage.h
+++ b/src/dawn/wire/server/ObjectStorage.h
@@ -63,12 +63,9 @@
 template <typename T>
 struct ObjectData : public ObjectDataBase<T> {};
 
-enum class BufferMapWriteState { Unmapped, Mapped, MapError };
-
 struct BufferMapState {
     std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = nullptr;
     std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
-    BufferMapWriteState writeState = BufferMapWriteState::Unmapped;
 };
 
 template <>
diff --git a/src/dawn/wire/server/ServerBuffer.cpp b/src/dawn/wire/server/ServerBuffer.cpp
index 059243b..3b927e7 100644
--- a/src/dawn/wire/server/ServerBuffer.cpp
+++ b/src/dawn/wire/server/ServerBuffer.cpp
@@ -32,6 +32,7 @@
 
 #include <limits>
 #include <memory>
+#include <span>
 
 #include "dawn/common/Assert.h"
 #include "dawn/common/StringViewUtils.h"
@@ -63,7 +64,6 @@
             // don't assert it's non-null
             mapState->writeHandle = nullptr;
         }
-        mapState->writeState = BufferMapWriteState::Unmapped;
     });
 
     return WireResult::Success;
@@ -78,7 +78,6 @@
     buffer->mapState.Use([](auto mapState) {
         mapState->readHandle = nullptr;
         mapState->writeHandle = nullptr;
-        mapState->writeState = BufferMapWriteState::Unmapped;
     });
 
     return WireResult::Success;
@@ -163,10 +162,8 @@
             if (buffer->handle == nullptr) {
                 DAWN_ASSERT(descriptor->mappedAtCreation);
                 // A null buffer indicates that mapping-at-creation failed inside createBuffer.
-                // - Unmark the buffer as allocated so we will skip freeing it.
+                // Unmark the buffer as allocated so we will skip freeing it.
                 buffer->state = AllocationState::Reserved;
-                // - Remember the buffer is an error so we will skip subsequent mapping operations.
-                mapState->writeState = BufferMapWriteState::MapError;
                 return WireResult::Success;
             }
 
@@ -179,23 +176,6 @@
             }
             DAWN_ASSERT(writeHandle != nullptr);
             mapState->writeHandle.reset(writeHandle);
-            writeHandle->SetDataLength(descriptor->size);
-
-            if (descriptor->mappedAtCreation) {
-                void* mapping = mProcs->bufferGetMappedRange(buffer->handle, 0, descriptor->size);
-                if (mapping == nullptr) {
-                    DAWN_ASSERT(descriptor->size % 4 != 0);
-                    // GetMappedRange can still fail if the buffer's size isn't aligned.
-                    // - Remember the buffer is an error so we will skip subsequent mapping
-                    // operations.
-                    mapState->writeState = BufferMapWriteState::MapError;
-                    return WireResult::Success;
-                }
-                DAWN_ASSERT(mapping != nullptr);
-                writeHandle->SetTarget(mapping);
-
-                mapState->writeState = BufferMapWriteState::Mapped;
-            }
         }
 
         if (isReadMode) {
@@ -207,7 +187,6 @@
                 return WireResult::FatalError;
             }
             DAWN_ASSERT(readHandle != nullptr);
-
             mapState->readHandle.reset(readHandle);
         }
 
@@ -226,25 +205,40 @@
     }
 
     return buffer->mapState.Use([&](auto mapState) {
-        switch (mapState->writeState) {
-            case BufferMapWriteState::Unmapped:
-                return WireResult::FatalError;
-            case BufferMapWriteState::MapError:
-                // The buffer is mapped but there was an error allocating mapped data.
-                // Do not perform the memcpy.
-                return WireResult::Success;
-            case BufferMapWriteState::Mapped:
-                break;
+        uint8_t* mappedData =
+            static_cast<uint8_t*>(mProcs->bufferGetMappedRange(buffer->handle, offset, size));
+
+        // There are a few valid reasons why getting the mapped range would fail here:
+        //  - The buffer was implicitly unmapped because of a device.Destroy() call.
+        //  - The buffer was an error buffer created just to replace an OOM mappedAtCreation buffer.
+        // Unfortunately validating exactly that the failure is due to a valid reason and not
+        // another is difficult, so we return WireResult::Success even for misuses of the wire
+        // protocol (like a size being larger than the buffer's size, etc).
+        if (mappedData == nullptr) {
+            return WireResult::Success;
         }
 
+        // TODO(https://issues.chromium.org/492456046): We would like to map only the `offset` and
+        // `size` here but the Chromium implementation of DeserializeDataUpdate uses `offset` to
+        // offset both the target data and it's shmem pointer. So the pointer passed in SetTarget
+        // must be for the start of the buffer. Fix this somehow when spanifying the interfaces but
+        // for now we need to duplicate the overflow check that's done in GetMappedRange.
+        mappedData -= offset;
+
+        // Note that offset + size was checked to not overflow in GetMappedRange above.
+        std::span<uint8_t> mappedRange = {mappedData, static_cast<size_t>(offset + size)};
+
+        // However it is easy to check for misuses of the wire protocol to UpdateMappedData without
+        // a WriteHandle.
         if (!mapState->writeHandle) {
-            // This check is performed after the check for the MapError state. It is permissible
-            // to Unmap and attempt to update mapped data of an error buffer.
             return WireResult::FatalError;
         }
 
         // Deserialize the flush info and flush updated data from the handle into the target
-        // of the handle. The target is set via WriteHandle::SetTarget.
+        // of the handle. The target is set via WriteHandle::SetTarget/SetDataLength.
+        mapState->writeHandle->SetDataLength(mappedRange.size());
+        mapState->writeHandle->SetTarget(mappedRange.data());
+
         if (!mapState->writeHandle->DeserializeDataUpdate(
                 writeDataUpdateInfo, static_cast<size_t>(writeDataUpdateInfoLength),
                 static_cast<size_t>(offset), static_cast<size_t>(size))) {
@@ -301,18 +295,6 @@
             break;
         }
         case WGPUMapMode_Write: {
-            buffer->mapState.Use([&](auto mapState) {
-                // The in-flight map request returned successfully.
-                mapState->writeState = BufferMapWriteState::Mapped;
-                // Set the target of the WriteHandle to the mapped buffer data.
-                // Note that writeHandle's target always refers to the buffer base address, but we
-                // call getMappedRange exactly with the range of data that is potentially modified
-                // (i.e. we don't want getMappedRange(0, wholeBufferSize) if only a subset of the
-                // buffer is actually mapped) in case the implementation does some range tracking.
-                mapState->writeHandle->SetTarget(static_cast<uint8_t*>(mProcs->bufferGetMappedRange(
-                                                     data->bufferObj, data->offset, data->size)) -
-                                                 data->offset);
-            });
             SerializeCommand(cmd);
             break;
         }
diff --git a/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp b/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
index 0346239..5fbfa97 100644
--- a/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
@@ -123,14 +123,14 @@
     }
 
     // Sets up the correct mapped range call expectations given the map mode.
-    void ExpectMappedRangeCall(uint64_t bufferSize, void* bufferContent) {
+    void ExpectMappedRangeCall() {
         wgpu::MapMode mapMode = GetMapMode();
         if (mapMode == wgpu::MapMode::Read) {
-            EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, bufferSize))
-                .WillOnce(Return(bufferContent));
+            EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize))
+                .WillOnce(Return(&mappedBufferContents));
         } else if (mapMode == wgpu::MapMode::Write) {
-            EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, bufferSize))
-                .WillOnce(Return(bufferContent));
+            EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
+                .WillOnce(Return(&mappedBufferContents));
         }
     }
 
@@ -144,14 +144,15 @@
         wgpu::MapMode mapMode = GetMapMode();
         MapAsync(mapMode, 0, kBufferSize);
 
-        uint32_t bufferContent = 31337;
         EXPECT_CALL(
             api, OnBufferMapAsync(apiBuffer, static_cast<WGPUMapMode>(mapMode), 0, kBufferSize, _))
             .WillOnce(InvokeWithoutArgs([&] {
                 api.CallBufferMapAsyncCallback(apiBuffer, WGPUMapAsyncStatus_Success,
                                                kEmptyOutputStringView);
             }));
-        ExpectMappedRangeCall(kBufferSize, &bufferContent);
+        if (mapMode & wgpu::MapMode::Read) {
+            ExpectMappedRangeCall();
+        }
         addExpectations();
 
         // The callback should get called with the expected status, regardless if the server has
@@ -230,14 +231,15 @@
         wgpu::MapMode mapMode = GetMapMode();
         MapAsync(mapMode, 0, kBufferSize);
 
-        uint32_t bufferContent = 31337;
         EXPECT_CALL(
             api, OnBufferMapAsync(apiBuffer, static_cast<WGPUMapMode>(mapMode), 0, kBufferSize, _))
             .WillOnce(InvokeWithoutArgs([&] {
                 api.CallBufferMapAsyncCallback(apiBuffer, WGPUMapAsyncStatus_Success,
                                                kEmptyOutputStringView);
             }));
-        ExpectMappedRangeCall(kBufferSize, &bufferContent);
+        if (mapMode & wgpu::MapMode::Read) {
+            ExpectMappedRangeCall();
+        }
 
         // Ensure that the server had a chance to respond if relevant.
         FlushClient();
@@ -260,7 +262,11 @@
         FlushCallbacks();
     }
 
+    // The buffer contents is in a member to ensure it outlives all test bodies (it is passed by
+    // pointer as the mocked result of GetMappedRange and can be derefenced anywhere in the test).
+    uint32_t mappedBufferContents = 31337;
     static constexpr uint64_t kBufferSize = sizeof(uint32_t);
+
     // A successfully created buffer
     wgpu::Buffer buffer;
     WGPUBuffer apiBuffer;
@@ -363,7 +369,12 @@
 // Test that the callback isn't fired twice when Unmap() is called inside the callback.
 TEST_P(WireBufferMappingTests, UnmapInsideMapCallback) {
     TestCancelInCallback([&]() { buffer.Unmap(); },
-                         [&]() { EXPECT_CALL(api, BufferUnmap(apiBuffer)); });
+                         [&]() {
+                             if (GetMapMode() & wgpu::MapMode::Write) {
+                                 ExpectMappedRangeCall();
+                             }
+                             EXPECT_CALL(api, BufferUnmap(apiBuffer));
+                         });
 }
 
 // Test that the callback isn't fired twice when Destroy() is called inside the callback.
@@ -394,14 +405,13 @@
 TEST_P(WireBufferMappingReadTests, MappingSuccess) {
     MapAsync(wgpu::MapMode::Read, 0, kBufferSize);
 
-    uint32_t bufferContent = 31337;
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _))
         .WillOnce(InvokeWithoutArgs([&] {
             api.CallBufferMapAsyncCallback(apiBuffer, WGPUMapAsyncStatus_Success,
                                            kEmptyOutputStringView);
         }));
     EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize))
-        .WillOnce(Return(&bufferContent));
+        .WillOnce(Return(&mappedBufferContents));
 
     FlushClient();
     FlushFutures();
@@ -411,7 +421,7 @@
         FlushCallbacks();
     });
 
-    EXPECT_EQ(bufferContent,
+    EXPECT_EQ(mappedBufferContents,
               *static_cast<const uint32_t*>(buffer.GetConstMappedRange(0, kBufferSize)));
     EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
     buffer.Unmap();
@@ -424,14 +434,13 @@
     // Successful map
     MapAsync(wgpu::MapMode::Read, 0, kBufferSize);
 
-    uint32_t bufferContent = 31337;
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _))
         .WillOnce(InvokeWithoutArgs([&] {
             api.CallBufferMapAsyncCallback(apiBuffer, WGPUMapAsyncStatus_Success,
                                            kEmptyOutputStringView);
         }));
     EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize))
-        .WillOnce(Return(&bufferContent));
+        .WillOnce(Return(&mappedBufferContents));
 
     FlushClient();
     FlushFutures();
@@ -459,7 +468,7 @@
         FlushCallbacks();
     });
 
-    EXPECT_EQ(bufferContent,
+    EXPECT_EQ(mappedBufferContents,
               *static_cast<const uint32_t*>(buffer.GetConstMappedRange(0, kBufferSize)));
 }
 
@@ -478,7 +487,6 @@
 TEST_P(WireBufferMappingWriteTests, MappingSuccess) {
     MapAsync(wgpu::MapMode::Write, 0, kBufferSize);
 
-    uint32_t serverBufferContent = 31337;
     uint32_t updatedContent = 4242;
 
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _))
@@ -486,8 +494,6 @@
             api.CallBufferMapAsyncCallback(apiBuffer, WGPUMapAsyncStatus_Success,
                                            kEmptyOutputStringView);
         }));
-    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
-        .WillOnce(Return(&serverBufferContent));
 
     // The map write callback always gets a buffer full of zeroes.
     FlushClient();
@@ -504,13 +510,15 @@
     // Write something to the mapped pointer
     *lastMapWritePointer = updatedContent;
 
+    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
+        .WillOnce(Return(&mappedBufferContents));
     EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
     buffer.Unmap();
 
     FlushClient();
 
     // After the buffer is unmapped, the content of the buffer is updated on the server
-    ASSERT_EQ(serverBufferContent, updatedContent);
+    ASSERT_EQ(mappedBufferContents, updatedContent);
 }
 
 // Check that an error map write while a buffer is already mapped.
@@ -518,14 +526,11 @@
     // Successful map
     MapAsync(wgpu::MapMode::Write, 0, kBufferSize);
 
-    uint32_t bufferContent = 31337;
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _))
         .WillOnce(InvokeWithoutArgs([&] {
             api.CallBufferMapAsyncCallback(apiBuffer, WGPUMapAsyncStatus_Success,
                                            kEmptyOutputStringView);
         }));
-    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
-        .WillOnce(Return(&bufferContent));
 
     FlushClient();
     FlushFutures();
@@ -574,11 +579,10 @@
 
     uint32_t apiBufferData = 1234;
     EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer));
-    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, 4)).WillOnce(Return(&apiBufferData));
-
     buffer = device.CreateBuffer(&descriptor);
     FlushClient();
 
+    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, 4)).WillOnce(Return(&apiBufferData));
     EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
     buffer.Unmap();
     FlushClient();
@@ -590,10 +594,7 @@
     descriptor.size = kBufferSize;
     descriptor.mappedAtCreation = true;
 
-    uint32_t apiBufferData = 1234;
     EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer));
-    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, 4)).WillOnce(Return(&apiBufferData));
-
     buffer = device.CreateBuffer(&descriptor);
     FlushClient();
 
@@ -611,11 +612,10 @@
 
... (file diff truncated)
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/src/dawn/tests/BUILD.gn b/src/dawn/tests/BUILD.gn
index 04900b6..ed0fc2e 100644
--- a/src/dawn/tests/BUILD.gn
+++ b/src/dawn/tests/BUILD.gn
@@ -468,6 +468,7 @@
     "unittests/wire/WireOptionalTests.cpp",
     "unittests/wire/WireQueueTests.cpp",
     "unittests/wire/WireShaderModuleTests.cpp",
+    "unittests/wire/WireSpecificCommandTests.cpp",
     "unittests/wire/WireTest.cpp",
     "unittests/wire/WireTest.h",
   ]
diff --git a/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp b/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
index 0346239..5fbfa97 100644
--- a/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
@@ -123,14 +123,14 @@
     }
 
     // Sets up the correct mapped range call expectations given the map mode.
-    void ExpectMappedRangeCall(uint64_t bufferSize, void* bufferContent) {
+    void ExpectMappedRangeCall() {
         wgpu::MapMode mapMode = GetMapMode();
         if (mapMode == wgpu::MapMode::Read) {
-            EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, bufferSize))
-                .WillOnce(Return(bufferContent));
+            EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize))
+                .WillOnce(Return(&mappedBufferContents));
         } else if (mapMode == wgpu::MapMode::Write) {
-            EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, bufferSize))
-                .WillOnce(Return(bufferContent));
+            EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
+                .WillOnce(Return(&mappedBufferContents));
         }
     }
 
@@ -144,14 +144,15 @@
         wgpu::MapMode mapMode = GetMapMode();
         MapAsync(mapMode, 0, kBufferSize);
 
-        uint32_t bufferContent = 31337;
         EXPECT_CALL(
             api, OnBufferMapAsync(apiBuffer, static_cast<WGPUMapMode>(mapMode), 0, kBufferSize, _))
             .WillOnce(InvokeWithoutArgs([&] {
                 api.CallBufferMapAsyncCallback(apiBuffer, WGPUMapAsyncStatus_Success,
                                                kEmptyOutputStringView);
             }));
-        ExpectMappedRangeCall(kBufferSize, &bufferContent);
+        if (mapMode & wgpu::MapMode::Read) {
+            ExpectMappedRangeCall();
+        }
         addExpectations();
 
         // The callback should get called with the expected status, regardless if the server has
@@ -230,14 +231,15 @@
         wgpu::MapMode mapMode = GetMapMode();
         MapAsync(mapMode, 0, kBufferSize);
 
-        uint32_t bufferContent = 31337;
         EXPECT_CALL(
             api, OnBufferMapAsync(apiBuffer, static_cast<WGPUMapMode>(mapMode), 0, kBufferSize, _))
             .WillOnce(InvokeWithoutArgs([&] {
                 api.CallBufferMapAsyncCallback(apiBuffer, WGPUMapAsyncStatus_Success,
                                                kEmptyOutputStringView);
             }));
-        ExpectMappedRangeCall(kBufferSize, &bufferContent);
+        if (mapMode & wgpu::MapMode::Read) {
+            ExpectMappedRangeCall();
+        }
 
         // Ensure that the server had a chance to respond if relevant.
         FlushClient();
@@ -260,7 +262,11 @@
         FlushCallbacks();
     }
 
+    // The buffer contents is in a member to ensure it outlives all test bodies (it is passed by
+    // pointer as the mocked result of GetMappedRange and can be derefenced anywhere in the test).
+    uint32_t mappedBufferContents = 31337;
     static constexpr uint64_t kBufferSize = sizeof(uint32_t);
+
     // A successfully created buffer
     wgpu::Buffer buffer;
     WGPUBuffer apiBuffer;
@@ -363,7 +369,12 @@
 // Test that the callback isn't fired twice when Unmap() is called inside the callback.
 TEST_P(WireBufferMappingTests, UnmapInsideMapCallback) {
     TestCancelInCallback([&]() { buffer.Unmap(); },
-                         [&]() { EXPECT_CALL(api, BufferUnmap(apiBuffer)); });
+                         [&]() {
+                             if (GetMapMode() & wgpu::MapMode::Write) {
+                                 ExpectMappedRangeCall();
+                             }
+                             EXPECT_CALL(api, BufferUnmap(apiBuffer));
+                         });
 }
 
 // Test that the callback isn't fired twice when Destroy() is called inside the callback.
@@ -394,14 +405,13 @@
 TEST_P(WireBufferMappingReadTests, MappingSuccess) {
     MapAsync(wgpu::MapMode::Read, 0, kBufferSize);
 
-    uint32_t bufferContent = 31337;
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _))
         .WillOnce(InvokeWithoutArgs([&] {
             api.CallBufferMapAsyncCallback(apiBuffer, WGPUMapAsyncStatus_Success,
                                            kEmptyOutputStringView);
         }));
     EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize))
-        .WillOnce(Return(&bufferContent));
+        .WillOnce(Return(&mappedBufferContents));
 
     FlushClient();
     FlushFutures();
@@ -411,7 +421,7 @@
         FlushCallbacks();
     });
 
-    EXPECT_EQ(bufferContent,
+    EXPECT_EQ(mappedBufferContents,
               *static_cast<const uint32_t*>(buffer.GetConstMappedRange(0, kBufferSize)));
     EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
     buffer.Unmap();
@@ -424,14 +434,13 @@
     // Successful map
     MapAsync(wgpu::MapMode::Read, 0, kBufferSize);
 
-    uint32_t bufferContent = 31337;
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _))
         .WillOnce(InvokeWithoutArgs([&] {
             api.CallBufferMapAsyncCallback(apiBuffer, WGPUMapAsyncStatus_Success,
                                            kEmptyOutputStringView);
         }));
     EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize))
-        .WillOnce(Return(&bufferContent));
+        .WillOnce(Return(&mappedBufferContents));
 
     FlushClient();
     FlushFutures();
@@ -459,7 +468,7 @@
         FlushCallbacks();
     });
 
-    EXPECT_EQ(bufferContent,
+    EXPECT_EQ(mappedBufferContents,
               *static_cast<const uint32_t*>(buffer.GetConstMappedRange(0, kBufferSize)));
 }
 
@@ -478,7 +487,6 @@
 TEST_P(WireBufferMappingWriteTests, MappingSuccess) {
     MapAsync(wgpu::MapMode::Write, 0, kBufferSize);
 
-    uint32_t serverBufferContent = 31337;
     uint32_t updatedContent = 4242;
 
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _))
@@ -486,8 +494,6 @@
             api.CallBufferMapAsyncCallback(apiBuffer, WGPUMapAsyncStatus_Success,
                                            kEmptyOutputStringView);
         }));
-    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
-        .WillOnce(Return(&serverBufferContent));
 
     // The map write callback always gets a buffer full of zeroes.
     FlushClient();
@@ -504,13 +510,15 @@
     // Write something to the mapped pointer
     *lastMapWritePointer = updatedContent;
 
+    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
+        .WillOnce(Return(&mappedBufferContents));
     EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
     buffer.Unmap();
 
     FlushClient();
 
     // After the buffer is unmapped, the content of the buffer is updated on the server
-    ASSERT_EQ(serverBufferContent, updatedContent);
+    ASSERT_EQ(mappedBufferContents, updatedContent);
 }
 
 // Check that an error map write while a buffer is already mapped.
@@ -518,14 +526,11 @@
     // Successful map
     MapAsync(wgpu::MapMode::Write, 0, kBufferSize);
 
-    uint32_t bufferContent = 31337;
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _))
         .WillOnce(InvokeWithoutArgs([&] {
             api.CallBufferMapAsyncCallback(apiBuffer, WGPUMapAsyncStatus_Success,
                                            kEmptyOutputStringView);
         }));
-    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
-        .WillOnce(Return(&bufferContent));
 
     FlushClient();
     FlushFutures();
@@ -574,11 +579,10 @@
 
     uint32_t apiBufferData = 1234;
     EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer));
-    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, 4)).WillOnce(Return(&apiBufferData));
-
     buffer = device.CreateBuffer(&descriptor);
     FlushClient();
 
+    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, 4)).WillOnce(Return(&apiBufferData));
     EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
     buffer.Unmap();
     FlushClient();
@@ -590,10 +594,7 @@
     descriptor.size = kBufferSize;
     descriptor.mappedAtCreation = true;
 
-    uint32_t apiBufferData = 1234;
     EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer));
-    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, 4)).WillOnce(Return(&apiBufferData));
-
     buffer = device.CreateBuffer(&descriptor);
     FlushClient();
 
@@ -611,11 +612,10 @@
 
     uint32_t apiBufferData = 1234;
     EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer));
-    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, 4)).WillOnce(Return(&apiBufferData));
-
     buffer = device.CreateBuffer(&descriptor);
     FlushClient();
 
+    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, 4)).WillOnce(Return(&apiBufferData));
     EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
     buffer.Unmap();
     FlushClient();
@@ -627,8 +627,6 @@
             api.CallBufferMapAsyncCallback(apiBuffer, WGPUMapAsyncStatus_Success,
                                            kEmptyOutputStringView);
         }));
-    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
-        .WillOnce(Return(&apiBufferData));
     FlushClient();
     FlushFutures();
 
@@ -647,8 +645,6 @@
 
     uint32_t apiBufferData = 1234;
     EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer));
-    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, 4)).WillOnce(Return(&apiBufferData));
-
     buffer = device.CreateBuffer(&descriptor);
     FlushClient();
 
@@ -673,6 +669,7 @@
 
     EXPECT_NE(nullptr, static_cast<const uint32_t*>(buffer.GetConstMappedRange(0, kBufferSize)));
 
+    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, 4)).WillOnce(Return(&apiBufferData));
     EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
     buffer.Unmap();
 
@@ -767,14 +764,15 @@
     wgpu::MapMode mapMode = GetMapMode();
     MapAsync(mapMode, 0, kBufferSize);
 
-    uint32_t bufferContent = 0;
     EXPECT_CALL(api,
                 OnBufferMapAsync(apiBuffer, static_cast<WGPUMapMode>(mapMode), 0, kBufferSize, _))
         .WillOnce(InvokeWithoutArgs([&] {
             api.CallBufferMapAsyncCallback(apiBuffer, WGPUMapAsyncStatus_Success,
                                            kEmptyOutputStringView);
         }));
-    ExpectMappedRangeCall(kBufferSize, &bufferContent);
+    if (mapMode & wgpu::MapMode::Read) {
+        ExpectMappedRangeCall();
+    }
 
     FlushClient();
     ExpectWireCallbacksWhen([&](auto& mockCb) {
@@ -803,14 +801,15 @@
     MapAsync(mapMode, 0, kBufferSize);
 
     // Calls for the first successful map.
-    uint32_t bufferContent = 0;
     EXPECT_CALL(api,
                 OnBufferMapAsync(apiBuffer, static_cast<WGPUMapMode>(mapMode), 0, kBufferSize, _))
         .WillOnce(InvokeWithoutArgs([&] {
             api.CallBufferMapAsyncCallback(apiBuffer, WGPUMapAsyncStatus_Success,
                                            kEmptyOutputStringView);
         }));
-    ExpectMappedRangeCall(kBufferSize, &bufferContent);
+    if (mapMode & wgpu::MapMode::Read) {
+        ExpectMappedRangeCall();
+    }
 
     if (IsSpontaneous()) {
         // In spontaneous mode, the second map on the pending immediately calls the callback.
@@ -849,7 +848,6 @@
 // Test that GetMapState() returns map state as expected
 TEST_P(WireBufferMappingTests, GetMapState) {
     wgpu::MapMode mapMode = GetMapMode();
... (truncated)
Loading diff…

Original Bug Report

reported by [email protected]

UAF write in Dawn wire server: BufferUpdateMappedData writes to freed GPU memory after DeviceDestroy

Summary

A use-after-free write exists in the Dawn wire server running in the GPU process. When a compromised renderer sends a DeviceDestroy wire command followed by BufferUpdateMappedData for a buffer that was created with mappedAtCreation, the server performs a memcpy into GPU buffer memory that has already been freed during device destruction. The dangling pointer is a raw uint8_t* stored in the wire server’s WriteHandle, which is not protected by raw_ptr, MiraclePtr, or any other mitigation. The attacker controls the write content (sourced from shared memory), offset, and size. This affects all platforms with WebGPU support.

Bisect

Introducing Commit: f93fa6acd96d77df9ef48f8f5ff78ea72cebf5ae (Dawn)

This commit implemented mappedAtCreation in the Dawn wire server by calling writeHandle->SetTarget(mapping, descriptor->size) inside DoDeviceCreateBuffer. The raw pointer stored via SetTarget was never invalidated on the DeviceDestroy path, creating the dangling pointer condition. A later commit (6e680fc56f, 2021-07-08) extended WriteHandle lifetime to persist across map/unmap cycles, widening the exploitable window, but the root gap has existed since this original commit.

Root Cause

Chromium’s WebGPU implementation uses the Dawn wire protocol to relay GPU commands from the renderer process to the GPU process. When a renderer creates a buffer with mappedAtCreation=true, the wire server obtains a pointer to the native mapped memory and stores it in a WriteHandle via SetTarget:

// third_party/dawn/src/dawn/wire/server/ServerBuffer.cpp
writeHandle->SetTarget(mapping);
buffer->mapWriteState = BufferMapWriteState::Mapped;

SetTarget stores this as a bare uint8_t*:

// third_party/dawn/src/dawn/wire/WireServer.cpp
void MemoryTransferService::WriteHandle::SetTarget(void* data) {
    mTargetData = static_cast<uint8_t*>(data);
}

The field declaration confirms it is not wrapped in raw_ptr or any other safety abstraction:

// third_party/dawn/include/dawn/wire/WireServer.h
uint8_t* mTargetData = nullptr;
size_t mDataLength = 0;

When the renderer later sends a DeviceDestroy command, the wire server delegates directly to the native layer without cleaning up any per-buffer state:

// (auto-generated) third_party/dawn/src/dawn/wire/server/ServerDoers_autogen.cpp
WireResult Server::DoDeviceDestroy(WGPUDevice self) {
    mProcs->deviceDestroy(self);
    return WireResult::Success;
}

The native layer destroys all device-owned objects, including buffers. On Vulkan, this frees the underlying memory through the FencedDeleter:

// third_party/dawn/src/dawn/native/vulkan/BufferVk.cpp
void Buffer::DestroyImpl(DestroyReason reason) {
    BufferBase::DestroyImpl(reason);
    ToBackend(GetDevice())->GetResourceMemoryAllocator()->Deallocate(&mMemoryAllocation);
    if (mHandle != VK_NULL_HANDLE) {
        ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mHandle);
        mHandle = VK_NULL_HANDLE;
    }
}

The FencedDeleter flushes all pending deletions during device teardown, calling vkFreeMemory which ultimately calls free() on the underlying heap allocation (in SwiftShader’s case via sw::freeMemory).

After the memory is freed, the wire server’s buffer->writeHandle still holds the stale mTargetData pointer, and buffer->mapWriteState is still BufferMapWriteState::Mapped. A subsequent BufferUpdateMappedData command passes all validation checks and reaches the memcpy:

// gpu/command_buffer/service/dawn_service_memory_transfer_service.cc
bool WriteHandleImpl::DeserializeDataUpdate(const void* deserialize_pointer,
                                            size_t deserialize_size,
                                            size_t offset,
                                            size_t size) {
    // ...bounds checks against targetData and buffer_data_view_...
    UNSAFE_TODO(memcpy(static_cast<uint8_t*>(targetData.data()) + offset,
                       buffer_data_view_.data() + offset, size));
    return true;
}

The targetData comes from GetTarget(), which returns a span over mTargetData, the now-dangling pointer. The source data (buffer_data_view_) points into renderer-controlled shared memory.

The gap exists because PreHandleBufferDestroy does clean up the WriteHandle on individual buffer destruction, but DoDeviceDestroy does not invoke equivalent cleanup for each buffer owned by the device. There is no iteration over associated buffers, no clearing of writeHandle, and no resetting of mapWriteState.

A compromised renderer exploits this by reordering the wire commands. In normal Blink code, GPUDevice::destroy() calls UnmapAllMappableBuffers() (which sends BufferUpdateMappedData) before GetHandle().Destroy() (which sends DeviceDestroy). By reversing this order, the renderer ensures DeviceDestroy frees the memory before BufferUpdateMappedData writes to it.

Reproduce

Tested on commit 3484f09b1620f6b7198fda97caf21b822d3df8ff on macOS and Ubuntu 22.04. This vulnerability exists on all platforms that support WebGPU (Windows, macOS, Linux, ChromeOS, Android).

Configure an ASAN build. A minimal args.gn for out/asan:

is_asan = true
is_debug = false
is_component_build = false

Build Chrome:

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

Run Chrome:

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

# Linux
out/asan/chrome --enable-unsafe-webgpu --user-data-dir=./userdata poc.html

On macOS, the native Metal backend allocates GPU buffer memory through vm_allocate/IOKit mapped memory, which lives outside the heap regions that ASAN instruments. When the buffer is destroyed and the VM pages are reclaimed by the OS, the subsequent memcpy hits unmapped virtual memory and produces a SEGV rather than an ASAN report. On Linux, --enable-unsafe-webgpu selects SwiftShader as the Vulkan backend. SwiftShader allocates GPU buffer memory via malloc (through sw::allocateZeroOrPoison), which ASAN fully instruments, so the dangling write produces a proper heap-use-after-free report. To obtain an ASAN report on macOS, pass --use-webgpu-adapter=swiftshader --enable-unsafe-webgpu to force the SwiftShader backend.

SEGV on macOS (native Metal backend):

Received signal 11 SEGV_ACCERR 0001341f4000
 [0x00035c2633c8]
 [0x00035c237198]
 [0x00035c2631fc]
 [0x000180743744]
 [0x000104a41994]
 [0x0003623fe9dc]
 [0x0003623c0ee0]
 [0x0003623cd95c]
 [0x0003623d712c]
 [0x00036240ada4]
 [0x00036240b1a8]
 [0x000362401144]
 [0x000351a5af0c]
 [0x000362316cc0]
 [0x000362315e10]
 [0x00036233470c]
 [0x0003623403ac]
 [0x0003623401c4]
 [0x000351a93d5c]
 [0x000351a6e83c]
 [0x000351a6ced4]
 [0x000351a70270]
 [0x00035c0fb574]
 [0x00035c163318]
 [0x00035c1626d0]
 [0x00035c284a8c]
 [0x00035c2761d8]
 [0x00035c282edc]
 [0x0001807d89f8]
 [0x0001807d898c]
 [0x0001807d86f8]
 [0x0001807d7388]
 [0x000180891e34]
 [0x000182a26964]
 [0x00035c285bdc]
 [0x00035c281c44]
 [0x00035c164678]
 [0x00035c089820]
 [0x00036546ae9c]
 [0x0003587c9b64]
 [0x0003587cbce4]
 [0x0003587c7854]
 [0x0003587c7d44]
 [0x000349c4dcb8]
 [0x000104640c98]
 [0x000180371d54]
[end of stack trace]

ASAN output on Linux (SwiftShader backend via --enable-unsafe-webgpu):

=================================================================
==154737==ERROR: AddressSanitizer: heap-use-after-free on address 0x71787d175900 at pc 0x5eec4ca260ae bp 0x7ffe523cc350 sp 0x7ffe523cbb10
WRITE of size 16777216 at 0x71787d175900 thread T0 (chrome)
    #0 0x5eec4ca260ad in __asan_memcpy (/home/test/Desktop/chromium/src/out/asan/chrome+0x10e810ad) (BuildId: 285b4624b4b2a3a5)
    #1 0x5eec6e07c0a4 in gpu::webgpu::(anonymous namespace)::WriteHandleImpl::DeserializeDataUpdate(void const*, unsigned long, unsigned long, unsigned long) gpu/command_buffer/service/dawn_service_memory_transfer_service.cc:123:17
    #2 0x5eec6e0c9709 in dawn::wire::server::Server::DoBufferUpdateMappedData(dawn::wire::server::Known<WGPUBufferImpl*>, unsigned long, unsigned char const*, unsigned long, unsigned long) third_party/dawn/src/dawn/wire/server/ServerBuffer.cpp:248:37
    #3 0x5eec6e0a68eb in dawn::wire::server::Server::HandleBufferUpdateMappedData(dawn::wire::DeserializeBuffer*) gen/third_party/dawn/src/dawn/wire/server/ServerHandlers_autogen.cpp:101:18
    #4 0x5eec6e0b1c8a in dawn::wire::server::Server::HandleCommands(char const volatile*, unsigned long) gen/third_party/dawn/src/dawn/wire/server/ServerHandlers_autogen.cpp:1676:30
    #5 0x5eec6e06c317 in gpu::webgpu::(anonymous namespace)::DawnWireServer::HandleCommands(char const volatile*, unsigned long) gpu/command_buffer/service/webgpu_decoder_impl.cc:155:33
    #6 0x5eec6e06c78d in gpu::webgpu::(anonymous namespace)::WebGPUDecoderImpl::HandleDawnCommands(unsigned int, void const volatile*) gpu/command_buffer/service/webgpu_decoder_impl.cc:1988:22
    #7 0x5eec6e060682 in gpu::webgpu::(anonymous namespace)::WebGPUDecoderImpl::DoCommands(unsigned int, void const volatile*, int, int*) gpu/command_buffer/service/webgpu_decoder_impl.cc:1933:18
    #8 0x5eec5763b814 in gpu::CommandBufferService::Flush(int, gpu::AsyncAPIInterface*) gpu/command_buffer/service/command_buffer_service.cc:267:35
    #9 0x5eec6d79a62b in gpu::CommandBufferStub::OnAsyncFlush(int, unsigned int, std::__Cr::vector<gpu::SyncToken, std::__Cr::allocator<gpu::SyncToken>> const&) gpu/ipc/service/command_buffer_stub.cc:504:22
    #10 0x5eec6d799891 in gpu::CommandBufferStub::ExecuteDeferredRequest(gpu::mojom::DeferredCommandBufferRequestParams&, gpu::FenceSyncReleaseDelegate*) gpu/ipc/service/command_buffer_stub.cc:173:7
    #11 0x5eec6d7bc97c in gpu::GpuChannel::ExecuteDeferredRequest(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*) gpu/ipc/service/gpu_channel.cc:833:13
    #12 0x5eec6d7ca967 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*&&) base/functional/bind_internal.h:740:12
    #13 0x5eec6d7ca749 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*) base/functional/bind_internal.h:956:5
    #14 0x5eec5767e211 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>) base/functional/callback.h:155:12
    #15 0x5eec57652957 in gpu::Scheduler::ExecuteSequence(base::IdType<gpu::SyncPointOrderData, unsigned int, 0u, 1u>) base/functional/callback.h:155:12
    #16 0x5eec57650988 in gpu::Scheduler::RunNextTask() gpu/command_buffer/service/scheduler.cc:625:3
    #17 0x5eec57654571 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*) base/functional/bind_internal.h:740:12
    #18 0x5eec64210c76 in base::TaskAnnotator::RunTaskImpl(base::PendingTask&) base/functional/callback.h:155:12
    #19 0x5eec64288459 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow*) base/task/common/task_annotator.h:112:5
    #20 0x5eec642872ca in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork() base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:346:40
    #21 0x5eec64435e74 in base::MessagePumpGlib::Run(base::MessagePump::Delegate*) base/message_loop/message_pump_glib.cc:782:48
    #22 0x5eec64289b67 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::Run(bool, base::TimeDelta) base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:650:12
    #23 0x5eec6418c210 in base::RunLoop::Run(base::Location const&) base/run_loop.cc:135:14
    #24 0x5eec6fb5379c in content::GpuMain(content::MainFunctionParams) content/gpu/gpu_main.cc:479:14
    #25 0x5eec5fe67e3f in content::RunZygote(content::ContentMainDelegate*) content/app/content_main_runner_impl.cc:664:14
    #26 0x5eec5fe6916f in content::RunOtherNamedProcessTypeMain(std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char>> const&, content::MainFunctionParams, content::ContentMainDelegate*) content/app/content_main_runner_impl.cc:771:12
    #27 0x5eec5fe6be78 in content::ContentMainRunnerImpl::Run() content/app/content_main_runner_impl.cc:1152:10
    #28 0x5eec5fe65851 in content::RunContentProcess(content::ContentMainParams, content::ContentMainRunner*) content/app/content_main.cc:358:36
    #29 0x5eec5fe65e4c in content::ContentMain(content::ContentMainParams) content/app/content_main.cc:371:10
    #30 0x5eec4ca62b39 in ChromeMain chrome/app/chrome_main.cc:191:12
    #31 0x757a1a229d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

0x71787d175900 is located 256 bytes inside of 16777495-byte region [0x71787d175800,0x71787e175917)
freed by thread T0 (chrome) here:
    #0 0x5eec4ca28086 in free (/home/test/Desktop/chromium/src/out/asan/chrome+0x10e83086) (BuildId: 285b4624b4b2a3a5)
    #1 0x71789987f42c in vk::DeviceMemory::freeBuffer() third_party/swiftshader/src/Vulkan/VkDeviceMemory.cpp:354:2
    #2 0x71789987f1f8 in vk::DeviceMemory::destroy(VkAllocationCallbacks const*) third_party/swiftshader/src/Vulkan/VkDeviceMemory.cpp:162:3
    #3 0x7178998d37af in vkFreeMemory third_party/swiftshader/src/Vulkan/VkDestroy.hpp:61:11
    #4 0x5eec4ec44fcf in dawn::native::vulkan::FencedDeleter::UpdateCompletedSerialTo(unsigned long) third_party/dawn/src/dawn/native/vulkan/FencedDeleter.cpp:88:30
    #5 0x5eec4e8fd68f in dawn::native::ExecutionQueueBase::WaitForIdleForDestruction() third_party/dawn/src/dawn/native/ExecutionQueue.cpp:184:24
    #6 0x5eec4e883c05 in dawn::native::DeviceBase::Destroy(dawn::native::DestroyReason) third_party/dawn/src/dawn/native/Device.cpp:681:34
    #7 0x5eec4e71e8a2 in dawn::native::NativeDeviceDestroy(WGPUDeviceImpl*) gen/third_party/dawn/src/dawn/native/ProcTable.cpp:946:15
    #8 0x5eec6e0be92d in dawn::wire::server::Server::DoDeviceDestroy(WGPUDeviceImpl*) gen/third_party/dawn/src/dawn/wire/server/ServerDoers_autogen.cpp:494:9
    #9 0x5eec6e0b323b in dawn::wire::server::Server::HandleCommands(char const volatile*, unsigned long) gen/third_party/dawn/src/dawn/wire/server/ServerHandlers_autogen.cpp:692:18
    #10 0x5eec6e06c317 in gpu::webgpu::(anonymous namespace)::DawnWireServer::HandleCommands(char const volatile*, unsigned long) gpu/command_buffer/service/webgpu_decoder_impl.cc:155:33
    #11 0x5eec6e06c78d in gpu::webgpu::(anonymous namespace)::WebGPUDecoderImpl::HandleDawnCommands(unsigned int, void const volatile*) gpu/command_buffer/service/webgpu_decoder_impl.cc:1988:22
    #12 0x5eec6e060682 in gpu::webgpu::(anonymous namespace)::WebGPUDecoderImpl::DoCommands(unsigned int, void const volatile*, int, int*) gpu/command_buffer/service/webgpu_decoder_impl.cc:1933:18
    #13 0x5eec5763b814 in gpu::CommandBufferService::Flush(int, gpu::AsyncAPIInterface*) gpu/command_buffer/service/command_buffer_service.cc:267:35
    #14 0x5eec6d79a62b in gpu::CommandBufferStub::OnAsyncFlush(int, unsigned int, std::__Cr::vector<gpu::SyncToken, std::__Cr::allocator<gpu::SyncToken>> const&) gpu/ipc/service/command_buffer_stub.cc:504:22
    #15 0x5eec6d799891 in gpu::CommandBufferStub::ExecuteDeferredRequest(gpu::mojom::DeferredCommandBufferRequestParams&, gpu::FenceSyncReleaseDelegate*) gpu/ipc/service/command_buffer_stub.cc:173:7
    #16 0x5eec6d7bc97c in gpu::GpuChannel::ExecuteDeferredRequest(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*) gpu/ipc/service/gpu_channel.cc:833:13
    #17 0x5eec6d7ca967 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*&&) base/functional/bind_internal.h:740:12
    #18 0x5eec6d7ca749 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*) base/functional/bind_internal.h:956:5
    #19 0x5eec5767e211 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>) base/functional/callback.h:155:12
    #20 0x5eec57652957 in gpu::Scheduler::ExecuteSequence(base::IdType<gpu::SyncPointOrderData, unsigned int, 0u, 1u>) base/functional/callback.h:155:12
    #21 0x5eec57650988 in gpu::Scheduler::RunNextTask() gpu/command_buffer/service/scheduler.cc:625:3
    #22 0x5eec57654571 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*) base/functional/bind_internal.h:740:12
    #23 0x5eec64210c76 in base::TaskAnnotator::RunTaskImpl(base::PendingTask&) base/functional/callback.h:155:12
    #24 0x5eec64288459 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow*) base/task/common/task_annotator.h:112:5
    #25 0x5eec642872ca in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork() base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:346:40
    #26 0x5eec64435538 in base::MessagePumpGlib::HandleDispatch() base/message_loop/message_pump_glib.cc:736:46
    #27 0x5eec64438af8 in base::(anonymous namespace)::WorkSourceDispatch(_GSource*, int (*)(void*), void*) base/message_loop/message_pump_glib.cc:355:43
    #28 0x757a1b72dd3a in g_main_context_dispatch (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x55d3a) (BuildId: 6b4f160dbc5397c2f502dc4f08a8cff259917926)

previously allocated by thread T0 (chrome) here:
    #0 0x5eec4ca28324 in malloc (/home/test/Desktop/chromium/src/out/asan/chrome+0x10e83324) (BuildId: 285b4624b4b2a3a5)
    #1 0x717899d6bf1d in sw::allocateZeroOrPoison(unsigned long, unsigned long) third_party/swiftshader/src/System/Memory.cpp:81:42
    #2 0x71789987f3c4 in vk::DeviceMemory::allocateBuffer() third_party/swiftshader/src/Vulkan/VkDeviceMemory.cpp:342:11
    #3 0x71789987e024 in vk::DeviceMemory::Allocate(VkAllocationCallbacks const*, VkMemoryAllocateInfo const*, VkNonDispatchableHandle<VkDeviceMemory_T*>*, vk::Device*) third_party/swiftshader/src/Vulkan/VkDeviceMemory.cpp:275:12
    #4 0x7178998d36a7 in vkAllocateMemory third_party/swiftshader/src/Vulkan/libVulkan.cpp:1421:20
    #5 0x5eec4eca20a9 in dawn::native::vulkan::ResourceMemoryAllocator::SingleTypeAllocator::AllocateResourceHeap(unsigned long) third_party/dawn/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.cpp:113:35
    #6 0x5eec4ec9ebfa in dawn::native::vulkan::ResourceMemoryAllocator::Allocate(VkMemoryRequirements const&, dawn::native::vulkan::MemoryKind, bool) third_party/dawn/src/dawn/native/vulkan/ResourceMemoryAllocatorVk.cpp:241:67
    #7 0x5eec4ec03eb2 in dawn::native::vulkan::Buffer::Initialize(bool) third_party/dawn/src/dawn/native/vulkan/BufferVk.cpp:293:59
    #8 0x5eec4ec02ca6 in dawn::native::vulkan::Buffer::Create(dawn::native::vulkan::Device*, dawn::native::UnpackedPtr<dawn::native::BufferDescriptor> const&) third_party/dawn/src/dawn/native/vulkan/BufferVk.cpp:219:26
    #9 0x5eec4ec316b7 in dawn::native::vulkan::Device::CreateBufferImpl(dawn::native::UnpackedPtr<dawn::native::BufferDescriptor> const&) third_party/dawn/src/dawn/native/vulkan/DeviceVk.cpp:213:12
    #10 0x5eec4e88e50b in dawn::native::DeviceBase::APICreateBuffer(dawn::native::BufferDescriptor const*) third_party/dawn/src/dawn/native/Device.cpp:1339:20
    #11 0x5eec6e0c8c2c in dawn::wire::server::Server::DoDeviceCreateBuffer(dawn::wire::server::Known<WGPUDeviceImpl*>, WGPUBufferDescriptor const*, dawn::wire::ObjectHandle, unsigned long, unsigned char const*, unsigned long, unsigned char const*) third_party/dawn/src/dawn/wire/server/ServerBuffer.cpp:142:22
    #12 0x5eec6e0a831c in dawn::wire::server::Server::HandleDeviceCreateBuffer(dawn::wire::DeserializeBuffer*) gen/third_party/dawn/src/dawn/wire/server/ServerHandlers_autogen.cpp:463:18
    #13 0x5eec6e0b0648 in dawn::wire::server::Server::HandleCommands(char const volatile*, unsigned long) gen/third_party/dawn/src/dawn/wire/server/ServerHandlers_autogen.cpp:1778:30
    #14 0x5eec6e06c317 in gpu::webgpu::(anonymous namespace)::DawnWireServer::HandleCommands(char const volatile*, unsigned long) gpu/command_buffer/service/webgpu_decoder_impl.cc:155:33
    #15 0x5eec6e06c78d in gpu::webgpu::(anonymous namespace)::WebGPUDecoderImpl::HandleDawnCommands(unsigned int, void const volatile*) gpu/command_buffer/service/webgpu_decoder_impl.cc:1988:22
    #16 0x5eec6e060682 in gpu::webgpu::(anonymous namespace)::WebGPUDecoderImpl::DoCommands(unsigned int, void const volatile*, int, int*) gpu/command_buffer/service/webgpu_decoder_impl.cc:1933:18
    #17 0x5eec5763b814 in gpu::CommandBufferService::Flush(int, gpu::AsyncAPIInterface*) gpu/command_buffer/service/command_buffer_service.cc:267:35
    #18 0x5eec6d79a62b in gpu::CommandBufferStub::OnAsyncFlush(int, unsigned int, std::__Cr::vector<gpu::SyncToken, std::__Cr::allocator<gpu::SyncToken>> const&) gpu/ipc/service/command_buffer_stub.cc:504:22
    #19 0x5eec6d799891 in gpu::CommandBufferStub::ExecuteDeferredRequest(gpu::mojom::DeferredCommandBufferRequestParams&, gpu::FenceSyncReleaseDelegate*) gpu/ipc/service/command_buffer_stub.cc:173:7
    #20 0x5eec6d7bc97c in gpu::GpuChannel::ExecuteDeferredRequest(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*) gpu/ipc/service/gpu_channel.cc:833:13
    #21 0x5eec6d7ca967 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*&&) base/functional/bind_internal.h:740:12
    #22 0x5eec6d7ca749 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*) base/functional/bind_internal.h:956:5
    #23 0x5eec5767e211 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>) base/functional/callback.h:155:12
    #24 0x5eec57652957 in gpu::Scheduler::ExecuteSequence(base::IdType<gpu::SyncPointOrderData, unsigned int, 0u, 1u>) base/functional/callback.h:155:12
    #25 0x5eec57650988 in gpu::Scheduler::RunNextTask() gpu/command_buffer/service/scheduler.cc:625:3
    #26 0x5eec57654571 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*) base/functional/bind_internal.h:740:12
    #27 0x5eec64210c76 in base::TaskAnnotator::RunTaskImpl(base::PendingTask&) base/functional/callback.h:155:12
    #28 0x5eec64288459 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow*) base/task/common/task_annotator.h:112:5
    #29 0x5eec642872ca in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork() base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:346:40

SUMMARY: AddressSanitizer: heap-use-after-free (/home/test/Desktop/chromium/src/out/asan/chrome+0x10e810ad) (BuildId: 285b4624b4b2a3a5) in __asan_memcpy
Shadow bytes around the buggy address:
  0x71787d175680: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x71787d175700: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x71787d175780: fa fa fa fa fa fa fa fa fa fa fa fa fa fa f7 fa
  0x71787d175800: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x71787d175880: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x71787d175900:[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x71787d175980: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x71787d175a00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x71787d175a80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x71787d175b00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x71787d175b80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
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

==154737==ADDITIONAL INFO

==154737==Note: Please include this section with the ASan report.
Task trace:
    #0 0x5eec57650e62 in gpu::Scheduler::RunNextTask() gpu/command_buffer/service/scheduler.cc:647:27
    #1 0x5eec57650e62 in gpu::Scheduler::RunNextTask() gpu/command_buffer/service/scheduler.cc:647:27
    #2 0x5eec57650e62 in gpu::Scheduler::RunNextTask() gpu/command_buffer/service/scheduler.cc:647:27
    #3 0x5eec57650e62 in gpu::Scheduler::RunNextTask() gpu/command_buffer/service/scheduler.cc:647:27

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.

==154737==END OF ADDITIONAL INFO

==154737==ABORTING
[154700:154700:0312/225514.129998:ERROR:content/browser/gpu/gpu_process_host.cc:999] GPU process exited unexpectedly: exit_code=256

Credit

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

View on issue tracker