High chrome Integer Overflow 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInteger overflow in Dawn
DescriptionInteger overflow in Dawn
ComponentDawn
Bug ClassInteger Overflow
Tracker499159695
Fix commita9185ea4ddcc (dawn) +5/-5
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Files Changed

  • src/dawn/native/Commands.cpp
  • src/dawn/native/Commands.h
From a9185ea4ddcc586e2e0ed10fda490f5eb0c79561 Mon Sep 17 00:00:00 2001
From: Corentin Wallez <[email protected]>
Date: Thu, 23 Apr 2026 10:46:32 -0700
Subject: [PATCH] [dawn][native] Use size_t to store string command lengths.

Fixed: 499159695
Change-Id: Ia811965d2d9813eaf73341f9238e816663e5dd21
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/304695
Commit-Queue: Corentin Wallez <[email protected]>
Reviewed-by: Loko Kung <[email protected]>
---

diff --git a/src/dawn/native/Commands.cpp b/src/dawn/native/Commands.cpp
index 2e538b1..05d9afa 100644
--- a/src/dawn/native/Commands.cpp
+++ b/src/dawn/native/Commands.cpp
@@ -435,9 +435,9 @@
     }
 }
 
-const char* AddNullTerminatedString(CommandAllocator* allocator, StringView s, uint32_t* length) {
+const char* AddNullTerminatedString(CommandAllocator* allocator, StringView s, size_t* length) {
     std::string_view view = s;
-    *length = static_cast<uint32_t>(view.length());
+    *length = view.length();
 
     // Include extra null-terminator character. The string_view may not be null-terminated. It also
     // may already have a null-terminator inside of it, in which case adding the null-terminator is
diff --git a/src/dawn/native/Commands.h b/src/dawn/native/Commands.h
index c975fba..a0f9af0 100644
--- a/src/dawn/native/Commands.h
+++ b/src/dawn/native/Commands.h
@@ -342,7 +342,7 @@
 };
 
 struct InsertDebugMarkerCmd {
-    uint32_t length;
+    size_t length;
 };
 
 struct PixelLocalStorageBarrierCmd {};
@@ -350,7 +350,7 @@
 struct PopDebugGroupCmd {};
 
 struct PushDebugGroupCmd {
-    uint32_t length;
+    size_t length;
 };
 
 struct ResolveQuerySetCmd {
@@ -465,7 +465,7 @@
 void SkipCommand(CommandIterator* commands, Command type);
 
 // Helper function to copy a wgpu::StringView into a safely null-terminated C-string in commands.
-const char* AddNullTerminatedString(CommandAllocator* allocator, StringView s, uint32_t* length);
+const char* AddNullTerminatedString(CommandAllocator* allocator, StringView s, size_t* length);
 
 }  // namespace dawn::native
 
Loading diff…

Original Bug Report

reported by [email protected]

GPU Process RCE via Integer Overflow in Dawn Command Iterator

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the security team.

Overview: An integer overflow exists in Dawn’s command processing when handling large debug marker strings. By supplying a string of length 0xFFFFFFFF, an attacker can desynchronize the command iterator. This causes the execution loop to interpret attacker-controlled string data as command IDs, triggering an unreachable code path that can be leveraged for Remote Code Execution.

Affected files:

  • third_party/dawn/src/dawn/native/Commands.cpp
  • third_party/dawn/src/dawn/native/d3d12/CommandBufferD3D12.cpp
  • third_party/dawn/src/dawn/native/vulkan/CommandBufferVk.cpp
  • third_party/dawn/src/dawn/native/CommandAllocator.h
  • third_party/dawn/src/dawn/native/Commands.h

Estimated timestamp from git blame: 2025-11-17

Description

A potential integer overflow vulnerability exists in Dawn’s handling of debug marker and group wire messages (e.g., CommandEncoderInsertDebugMarker). This vulnerability can be triggered to corrupt the command stream iterator and execute attacker-controlled data as a function pointer jump, leading to potential Remote Code Execution (RCE) in the GPU process.

When a large string is provided to AddNullTerminatedString (in third_party/dawn/src/dawn/native/Commands.cpp), the string’s length is explicitly cast and stored in a 32-bit integer: cmd->length = static_cast<uint32_t>(view.length());. However, the memory allocation uses 64-bit arithmetic: allocator->AllocateData<char>(view.length() + 1);.

If an attacker supplies a string of exactly 0xFFFFFFFF (4GB) non-NUL bytes, the 4GB block is successfully allocated and populated. Later, when the backend command execution loops (e.g., in CommandBufferD3D12::RecordComputePass or FreeCommands) attempt to skip this data, they call:

mCommands.NextData<char>(cmd->length + 1);

Since cmd->length is a uint32_t (0xFFFFFFFF), the addition of 1 causes a 32-bit integer overflow, resulting in 0. Consequently, NextData<char>(0) is called. The command iterator consumes the internal kAdditionalData marker but advances its internal pointer by 0 bytes, failing to skip the 4GB string data.

On the next iteration, the command loop reads the next 4 bytes to determine the next Command ID. Because the pointer was not advanced, it reads directly from the attacker-controlled string data. The resulting invalid command ID falls through to the default: case in the execution loop’s switch statement, which calls DAWN_UNREACHABLE().

In standard Chromium release builds, DAWN_UNREACHABLE() expands to __builtin_unreachable(). Compilers (like Clang) use this as a hint to elide bounds checks on switch jump tables. As a result, the attacker-controlled command ID is used as an out-of-bounds index for the jump table, enabling control-flow hijacking.

Potential Attacker Steps

Note: Our tooling does not yet have the ability to run code, so these are theoretical steps an attacker might follow based on code analysis.

  1. Bypass Client Constraints: From a compromised renderer, manually construct Dawn chunked wire commands to bypass client-side buffer serializers, sending an InsertDebugMarker command with a total size of 0xFFFFFFFF bytes of non-NUL data.
  2. Server Assembly: The GPU process’s ChunkedCommandHandler allocates a 4GB buffer (which succeeds on 64-bit systems with sufficient memory) and reconstructs the chunked payload.
  3. Command Deserialization: The Dawn wire server deserializes the WGPUStringView, preserving the 0xFFFFFFFF length (as it is bounded by WGPU_STRLEN, which is SIZE_MAX).
  4. Trigger Overflow: The command is recorded into the native command buffer. The string is stored, but the length field in the command struct truncates.
  5. Execution Desync: Upon submission, the GPU process attempts to execute the command buffer. The cmd->length + 1 overflow causes the iterator to stall exactly at the attacker’s payload.
  6. Control Flow Hijack: The switch statement reads the attacker’s payload as a command ID, falling through to DAWN_UNREACHABLE(). The compiler-optimized jump table reads an out-of-bounds function pointer dictated by the attacker’s fake ID, granting RCE.

Suggested Fix

  1. Validate String Lengths: Reject any StringView with a length exceeding a reasonable upper bound (e.g., a few megabytes) early in NormalizeMessageString or during Wire deserialization.
  2. Use Checked Arithmetic: Modify CommandAllocator::NextData and iterator offset calculations to use base::CheckedNumeric to prevent overflow wrap-around.
  3. Fix Struct Types: Change the length field in structures like InsertDebugMarkerCmd and PushDebugGroupCmd to size_t rather than uint32_t.

Evaluated with Chrome root at commit: ff3d2b74fa39431785bd60e51463b08fcc71ee33


Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker