Medium chrome Integer Overflow 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInteger overflow in Dawn
DescriptionInteger overflow in Dawn
ComponentDawn
Bug ClassInteger Overflow
Tracker497565944
Fix commitcacb2e67d9cd (dawn) +47/-5
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-05

Changed Functions

FunctionChangeNotes
TextureCorruptionTests_WidthAndHeight_IntelGen12
src/dawn/tests/end2end/TextureCorruptionTests.cpp
modified
TEST_P
src/dawn/tests/end2end/TextureCorruptionTests.cpp
modified
TextureCorruptionTests_ArrayLayer
src/dawn/tests/end2end/TextureCorruptionTests.cpp
modified

Files Changed

  • src/dawn/native/d3d12/ResourceAllocatorManagerD3D12.cpp
  • src/dawn/tests/end2end/TextureCorruptionTests.cpp
From cacb2e67d9cd469074052e537a7e217ac9d6f0c0 Mon Sep 17 00:00:00 2001
From: Antonio Maiorano <amaiorano@google.com>
Date: Wed, 01 Apr 2026 12:32:09 -0700
Subject: [PATCH] [d3d12][native] Fix potential uint16 overflow during texture allocation on Intel Gen12

When "d3d12_allocate_extra_memory_for_2d_array_color_texture" is
enabled, it's possible to create a texture that results in a uint16
overflow. Detect this and return an OOM.

Bug: 497565944
Change-Id: Ifeb1b9818db676ac3a990f3f965346f8201fd17a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/300576
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
---

diff --git a/src/dawn/native/d3d12/ResourceAllocatorManagerD3D12.cpp b/src/dawn/native/d3d12/ResourceAllocatorManagerD3D12.cpp
index 7b52c67..7b61a99 100644
--- a/src/dawn/native/d3d12/ResourceAllocatorManagerD3D12.cpp
+++ b/src/dawn/native/d3d12/ResourceAllocatorManagerD3D12.cpp
@@ -191,7 +191,7 @@
     return Align(columnPitch, 4);
 }
 
-uint32_t ComputeExtraArraySizeForIntelGen12(uint32_t width,
+uint64_t ComputeExtraArraySizeForIntelGen12(uint32_t width,
                                             uint32_t height,
                                             uint32_t arrayLayerCount,
                                             uint32_t mipLevelCount,
@@ -355,10 +355,18 @@
         // Multisample textures have one layer at most. Only non-multisample textures need the
         // workaround.
         DAWN_ASSERT(revisedDescriptor.SampleDesc.Count <= 1);
-        revisedDescriptor.DepthOrArraySize += ComputeExtraArraySizeForIntelGen12(
-            resourceDescriptor.Width, resourceDescriptor.Height,
-            resourceDescriptor.DepthOrArraySize, resourceDescriptor.MipLevels,
-            resourceDescriptor.SampleDesc.Count, colorFormatBytesPerBlock);
+        // Make sure the result fits in DepthOrArraySize which is a UINT16
+        uint64_t depthOrArraySize =
+            revisedDescriptor.DepthOrArraySize +
+            ComputeExtraArraySizeForIntelGen12(
+                resourceDescriptor.Width, resourceDescriptor.Height,
+                resourceDescriptor.DepthOrArraySize, resourceDescriptor.MipLevels,
+                resourceDescriptor.SampleDesc.Count, colorFormatBytesPerBlock);
+        if (depthOrArraySize >= std::numeric_limits<UINT16>::max()) {
+            return DAWN_OUT_OF_MEMORY_ERROR(
+                "Texture array size with Intel Gen12 workaround exceeds UINT16");
+        }
+        revisedDescriptor.DepthOrArraySize = depthOrArraySize;
     }
 
     // TODO(crbug.com/dawn/849): Conditionally disable sub-allocation.
diff --git a/src/dawn/tests/end2end/TextureCorruptionTests.cpp b/src/dawn/tests/end2end/TextureCorruptionTests.cpp
index e172910..e19de3e 100644
--- a/src/dawn/tests/end2end/TextureCorruptionTests.cpp
+++ b/src/dawn/tests/end2end/TextureCorruptionTests.cpp
@@ -399,6 +399,40 @@
                         {kDefaultSampleCount},
                         {kDefaultWriteType});
 
+// Test for UINT16 overflow when calling ComputeExtraArraySizeForIntelGen12 as per
+// crbug.com/497565944.
+class TextureCorruptionTests_WidthAndHeight_IntelGen12 : public TextureCorruptionTests {
+    void GetRequiredLimits(const dawn::utils::ComboLimits& supported,
+                           dawn::utils::ComboLimits& required) override {
+        required.maxTextureArrayLayers = supported.maxTextureArrayLayers;
+    }
+};
+TEST_P(TextureCorruptionTests_WidthAndHeight_IntelGen12, Tests) {
+    wgpu::Limits limits;
+    device.GetLimits(&limits);
+    DAWN_SUPPRESS_TEST_IF(limits.maxTextureArrayLayers < 2048);
+
+    uint32_t width = GetParam().mTextureWidth;
+    uint32_t height = GetParam().mTextureHeight;
+    uint32_t depthOrArrayLayerCount = GetParam().mArrayLayerCount;
+    uint32_t mipLevelCount = GetParam().mMipLevelCount;
+    uint32_t sampleCount = GetParam().mSampleCount;
+    wgpu::Extent3D textureSize = {width, height, depthOrArrayLayerCount};
+    wgpu::TextureFormat format = GetParam().mTextureFormat;
+    // This should fail due to an OOM error.
+    ASSERT_DEVICE_ERROR(wgpu::Texture texture =
+                            Create2DTexture(textureSize, format, mipLevelCount, sampleCount));
+}
+DAWN_INSTANTIATE_TEST_P(TextureCorruptionTests_WidthAndHeight_IntelGen12,
+                        {D3D12Backend({"d3d12_allocate_extra_memory_for_2d_array_color_texture"})},
+                        {wgpu::TextureFormat::R16Uint},
+                        {1u},
+                        {8192u},
+                        {1793u},
+                        {kDefaultMipLevelCount},
+                        {kDefaultSampleCount},
+                        {WriteType::WriteTexture});
+
 class TextureCorruptionTests_ArrayLayer : public TextureCorruptionTests {};
 
 TEST_P(TextureCorruptionTests_ArrayLayer, Tests) {
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/src/dawn/tests/end2end/TextureCorruptionTests.cpp b/src/dawn/tests/end2end/TextureCorruptionTests.cpp
index e172910..e19de3e 100644
--- a/src/dawn/tests/end2end/TextureCorruptionTests.cpp
+++ b/src/dawn/tests/end2end/TextureCorruptionTests.cpp
@@ -399,6 +399,40 @@
                         {kDefaultSampleCount},
                         {kDefaultWriteType});
 
+// Test for UINT16 overflow when calling ComputeExtraArraySizeForIntelGen12 as per
+// crbug.com/497565944.
+class TextureCorruptionTests_WidthAndHeight_IntelGen12 : public TextureCorruptionTests {
+    void GetRequiredLimits(const dawn::utils::ComboLimits& supported,
+                           dawn::utils::ComboLimits& required) override {
+        required.maxTextureArrayLayers = supported.maxTextureArrayLayers;
+    }
+};
+TEST_P(TextureCorruptionTests_WidthAndHeight_IntelGen12, Tests) {
+    wgpu::Limits limits;
+    device.GetLimits(&limits);
+    DAWN_SUPPRESS_TEST_IF(limits.maxTextureArrayLayers < 2048);
+
+    uint32_t width = GetParam().mTextureWidth;
+    uint32_t height = GetParam().mTextureHeight;
+    uint32_t depthOrArrayLayerCount = GetParam().mArrayLayerCount;
+    uint32_t mipLevelCount = GetParam().mMipLevelCount;
+    uint32_t sampleCount = GetParam().mSampleCount;
+    wgpu::Extent3D textureSize = {width, height, depthOrArrayLayerCount};
+    wgpu::TextureFormat format = GetParam().mTextureFormat;
+    // This should fail due to an OOM error.
+    ASSERT_DEVICE_ERROR(wgpu::Texture texture =
+                            Create2DTexture(textureSize, format, mipLevelCount, sampleCount));
+}
+DAWN_INSTANTIATE_TEST_P(TextureCorruptionTests_WidthAndHeight_IntelGen12,
+                        {D3D12Backend({"d3d12_allocate_extra_memory_for_2d_array_color_texture"})},
+                        {wgpu::TextureFormat::R16Uint},
+                        {1u},
+                        {8192u},
+                        {1793u},
+                        {kDefaultMipLevelCount},
+                        {kDefaultSampleCount},
+                        {WriteType::WriteTexture});
+
 class TextureCorruptionTests_ArrayLayer : public TextureCorruptionTests {};
 
 TEST_P(TextureCorruptionTests_ArrayLayer, Tests) {
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential GPU OOB Access via Integer Overflow in Dawn D3D12 Texture Allocation Workaround

Project Fortify, an experimental security project, has identified the following potential security issue.

Overview: An integer overflow exists in Dawn’s D3D12 backend when applying a memory allocation workaround for Intel Gen12 GPUs. A 32-bit padding size is added to a 16-bit texture dimension field without bounds checking, causing the allocation of an undersized GPU buffer. Dawn’s validation relies on the original requested size, allowing WebGPU commands to perform out-of-bounds reads and writes in GPU memory.

Affected files:

  • third_party/dawn/src/dawn/native/d3d12/ResourceAllocatorManagerD3D12.cpp
  • third_party/dawn/src/dawn/native/d3d12/TextureD3D12.cpp
  • third_party/dawn/src/dawn/native/Texture.cpp
  • third_party/dawn/src/dawn/native/d3d12/UtilsD3D12.cpp

Estimated timestamp from git blame: 2022-12-14

Description

A potential vulnerability exists in Dawn’s D3D12 backend where an integer overflow can lead to the allocation of undersized GPU textures on systems with Intel Gen12 GPUs (e.g., Tiger Lake) running older drivers.

In third_party/dawn/src/dawn/native/d3d12/ResourceAllocatorManagerD3D12.cpp, the AllocateMemory function implements a workaround for a legacy bug in Intel Gen12 GPU drivers (Toggle::D3D12AllocateExtraMemoryFor2DArrayColorTexture). This workaround increases the number of layers in a 2D array texture to avoid corruption:

// ResourceAllocatorManagerD3D12.cpp
revisedDescriptor.DepthOrArraySize += ComputeExtraArraySizeForIntelGen12(
    resourceDescriptor.Width, resourceDescriptor.Height,
    resourceDescriptor.DepthOrArraySize, resourceDescriptor.MipLevels,
    resourceDescriptor.SampleDesc.Count, colorFormatBytesPerBlock);

The vulnerability occurs because ComputeExtraArraySizeForIntelGen12 returns a uint32_t, but revisedDescriptor.DepthOrArraySize is a UINT16 field (as defined in the D3D12_RESOURCE_DESC struct). There are no bounds or overflow checks on this addition.

An attacker can request a texture with dimensions that cause ComputeExtraArraySizeForIntelGen12 to return a very large value. When added to the requested DepthOrArraySize, the resulting sum overflows the 16-bit integer and is truncated. Consequently, D3D12 allocates a resource with a much smaller capacity than requested. However, Dawn’s internal representation (TextureBase::mBaseSize) retains the original, larger size. When the attacker accesses layers beyond the allocated capacity, Dawn’s frontend validation permits the access, passing the out-of-bounds subresource index to the D3D12 runtime, which performs an out-of-bounds memory access in the GPU heap.

Potential Attacker Steps

Note: These steps are theoretical as our tooling agent does not run exploit code directly.

  1. Meet Preconditions: The victim must be using an Intel Gen12 GPU with a Windows driver older than 30.0.101.1692, triggering the workaround toggle.
  2. Request Malicious Texture: The attacker requests a 2D array texture via WebGPU. For example, a texture of size 1x8192 with 1793 layers and format r16uint.
    • Dawn validates this against the device’s maxTextureArrayLayers limit (usually 2048), which passes.
  3. Trigger Overflow: Inside AllocateMemory, ComputeExtraArraySizeForIntelGen12 calculates the required padding. For the above dimensions, it returns 457,215.
    • The addition 1793 + 457215 = 459008 overflows the UINT16 type.
    • The value wraps around modulo 65536: 459008 % 65536 = 256.
  4. Undersized Allocation: D3D12 creates a physical GPU resource with only 256 layers, but Dawn’s internal state continues to track it as having 1793 layers.
  5. Out-of-Bounds Access: The attacker invokes a WebGPU copy command (e.g., copyTextureToBuffer) targeting layer 1700. Dawn’s validation permits this (1700 < 1793).
  6. Memory Corruption: Dawn issues an ID3D12GraphicsCommandList::CopyTextureRegion command for subresource 1700. The GPU driver computes the memory address, pointing far past the allocated 256 layers, resulting in an out-of-bounds read/write in GPU memory (a GPU process sandbox escape primitive).

Suggested Fix

Check for integer overflow before assigning the value to the UINT16 field. If the required size exceeds the capacity of a UINT16, the allocation should fail gracefully (return an out-of-memory error).

uint32_t extraSize = ComputeExtraArraySizeForIntelGen12(...);
uint32_t newSize = resourceDescriptor.DepthOrArraySize + extraSize;
if (newSize > std::numeric_limits<UINT16>::max()) {
    return DAWN_OUT_OF_MEMORY_ERROR("Texture array size with Intel Gen12 workaround exceeds UINT16 max");
}
revisedDescriptor.DepthOrArraySize = static_cast<UINT16>(newSize);

Evaluated with Chrome root at commit: a9cbf6e8b275fe4147435aa905f3b7f5a656f5f0


Results from 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