High patch-now ~ plausible CVSS 9.6 chrome OOB 🔧 Commit mapped

Overview

High
Severity
9.6
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactBuffer overflow in Tint
DescriptionBuffer overflow in Tint
ComponentTint
Sub-componentTint
Bug ClassOOB
Action tierpatch-now — corruption primitive, web-reachable in default config (plausible) — 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 AIPlausible
Reviewer notes: Diff only adds an 8MB combined private-variable cap; commit says excessive size causes 'problems for some drivers'. Driver allocation/offset overflow is stated as fact but not shown; advisory places the overflow in Tint, not driver code.
A second, independent AI reviewer (not a human) re-derived this analysis from the fixing commit and the vendor advisory, looking for errors.
Tracker543141419
Fix commit023b7514b6a0 (dawn) +29/-0
CWECWE-122 (Heap buffer overflow)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-08-06 (per vendor advisory)
Fix landed2026-08-26 (public git)
Disclosed2026-09-22
Patch gap27 days — fix public in source before the advisory

Background

Tint
The WebGPU shader compiler inside Dawn that translates WGSL into an internal IR and lowers it to a backend shading language.
`Functional` validator
The Tint IR validation pass (src/tint/lang/core/ir/functional_validator.cc) that checks module-level invariants before code generation.
`AddressSpace::kPrivate`
The WGSL private address space, whose module-scope variables are per-invocation storage the driver must allocate for every shader invocation.
`internal_limits`
A header of compile-time constants (src/tint/utils/internal_limits.h) that bound resource sizes Tint will accept in a shader.

Root Cause Analysis

Verify before building a PoC

This analysis is consistent with the fixing commit but not fully provable from it: part of the vulnerable path lives in code the fix commit does not touch. Confirm the trigger and the sink against the source before relying on it for exploit development.
What the commit does not establish: Diff only adds an 8MB combined private-variable cap; commit says excessive size causes 'problems for some drivers'. Driver allocation/offset overflow is stated as fact but not shown; advisory places the overflow in Tint, not driver code.

Before the patch, the Functional validator checked individual module-scope variables but never accumulated the combined size of all variables declared in AddressSpace::kPrivate. An attacker-supplied WGSL shader could therefore declare many private variables (or private variables of very large aggregate types) whose combined StoreType()->Size() was effectively unbounded. Because private storage is materialized per shader invocation, some GPU drivers allocated or computed offsets against this excessive combined size, leading to a buffer overflow when the driver’s allocation or addressing assumptions were exceeded. The validator had no counterpart to a per-shader private budget, so the oversized layout passed validation and was handed to the backend and ultimately the driver.

The fix introduces a running counter total_private_bytes_ and rejects the module once the sum crosses kMaxCombinedPrivateVariableSize (8 MB). This shifts the failure from an out-of-bounds condition deep in a driver to a clean validation error in Tint.

Key insight
The vulnerability was an unbounded-accumulation flaw: each private variable individually looked reasonable, but nothing capped their sum, so a shader could inflate combined private storage past what drivers safely handle.

Attack Path

  1. Craft a malicious shader An attacker authors a WGSL shader containing many or very large module-scope variables in the private address space.
  2. Submit via WebGPU The shader is compiled through a GPUDevice.createShaderModule/pipeline call from web content or a compromised renderer.
  3. Pass validation The pre-patch Functional validator accepts the module because no combined private-size limit exists.
  4. Trigger driver allocation The oversized private layout is lowered to the backend and the GPU driver allocates or indexes per-invocation private storage.
  5. Overflow The driver’s size or offset assumptions are exceeded, causing an out-of-bounds write during allocation or addressing.

Impact Assessment

An attacker who can submit WebGPU shaders gains a heap/buffer overflow inside the GPU process (or the driver it drives), potentially corrupting memory and enabling further exploitation. The precondition is only that WebGPU is enabled and reachable, so hostile web content or a compromised renderer can trigger it without special privileges. The severity depends on the affected driver, since the actual overflow occurs in driver code operating on Tint’s accepted layout.

Changed Functions

FunctionChangeNotes
if
src/tint/lang/core/ir/functional_validator.cc
modified
TEST_F
src/tint/lang/core/ir/validator_value_test.cc
modified
for
src/tint/lang/core/ir/validator_value_test.cc
modified

Files Changed

  • src/tint/utils/internal_limits.h
  • src/tint/lang/core/ir/functional_validator.h
  • src/tint/lang/core/ir/validator_value_test.cc
  • src/tint/lang/core/ir/functional_validator.cc

Audit Directions

  • Unbounded accumulation
    Look for validators that check per-item limits but never sum sizes/counts across items in a module, buffer, or address space.
  • Size handed to drivers
    Audit any computed size or offset (StoreType()->Size() and similar) that flows into backend or driver allocation without an enforced upper bound.
  • Per-address-space budgets
    Verify that workgroup, storage, and other address spaces have combined-size checks analogous to the new private-space limit, since the same accumulation gap may exist elsewhere.
From 023b7514b6a0025a5218ed283f3c63a4a1ff082d Mon Sep 17 00:00:00 2001
From: James Price <[email protected]>
Date: Wed, 26 Aug 2026 10:54:37 -0700
Subject: [PATCH] [tint] Limit combined size of all private variables

Excessive combined private variable size is causing problems for some
drivers. Limit to 8MB for now, which is well above the minmax limit of
8KB that WGSL requires for a single declaration.

Fixed: 543141419
Change-Id: I152d8f504185417b0ae9a954e3be42b40b15763f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/336255
Reviewed-by: dan sinclair <[email protected]>
Auto-Submit: James Price <[email protected]>
Commit-Queue: James Price <[email protected]>
---
diff --git a/src/tint/lang/core/ir/functional_validator.cc b/src/tint/lang/core/ir/functional_validator.cc
index 2692da0..6d9863c 100644
--- a/src/tint/lang/core/ir/functional_validator.cc
+++ b/src/tint/lang/core/ir/functional_validator.cc
@@ -803,6 +803,15 @@
             }
         }
     }
+
+    if (mv->AddressSpace() == AddressSpace::kPrivate) {
+        total_private_bytes_ += mv->StoreType()->Size();
+        if (total_private_bytes_ > internal_limits::kMaxCombinedPrivateVariableSize) {
+            AddError(var) << "total size of private address-space variables exceeds "
+                          << internal_limits::kMaxCombinedPrivateVariableSize << " bytes";
+            return;
+        }
+    }
 }
 
 void Functional::CheckLet(const Let* l) {
diff --git a/src/tint/lang/core/ir/functional_validator.h b/src/tint/lang/core/ir/functional_validator.h
index 929e787..c5608eb 100644
--- a/src/tint/lang/core/ir/functional_validator.h
+++ b/src/tint/lang/core/ir/functional_validator.h
@@ -224,6 +224,7 @@
     Hashmap<const Loop*, const Continue*, 4> first_continues_;
     Hashset<std::string, 4> entry_point_names_;
     Hashmap<const core::type::Type*, uint64_t, 16> elements_counts_;
+    uint64_t total_private_bytes_ = 0;
 };
 
 }  // namespace tint::core::ir::validator
diff --git a/src/tint/utils/internal_limits.h b/src/tint/utils/internal_limits.h
index 56a1f9c..a66645c 100644
--- a/src/tint/utils/internal_limits.h
+++ b/src/tint/utils/internal_limits.h
@@ -62,6 +62,10 @@
 // expects to work.
 constexpr int64_t kMaxTemporaryStorageSize = 10LL * 1024 * 1024;
 
+// Limits the combined size of all private variables in the shader.
+// The WGSL spec has a min-max for a single variable of 8KB, so this 8MB limit is well above that.
+constexpr int64_t kMaxCombinedPrivateVariableSize = 8LL * 1024 * 1024;
+
 // The maximum number of locations allowed for shader IO.
 // This hard limit is derived from spirv-val (kMaxLocations in
 // https://github.com/KhronosGroup/SPIRV-Tools/blob/main/source/val/validate_interfaces.cpp).
diff --git a/src/tint/lang/core/ir/validator_value_test.cc b/src/tint/lang/core/ir/validator_value_test.cc
index 8c36322..25fb4c6 100644
--- a/src/tint/lang/core/ir/validator_value_test.cc
+++ b/src/tint/lang/core/ir/validator_value_test.cc
@@ -477,6 +477,21 @@
         << res.Failure();
 }
 
+TEST_F(IR_ValidatorTest, Var_CombinedPrivateSizeExceedsLimit) {
+    b.Append(mod.root_block, [&] {
+        for (uint32_t i = 0; i < 128; i++) {
+            b.Var(ty.ptr<private_>(ty.array<vec4u, 8000u>()));
+        }
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_THAT(
+        res.Failure().reason,
+        testing::HasSubstr("total size of private address-space variables exceeds 8388608 bytes"))
+        << res.Failure();
+}
+
 TEST_F(IR_ValidatorTest, Var_HandleMissingBindingPoint) {
     auto* v = b.Var(ty.ptr<handle, i32>());
     mod.root_block->Append(v);
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/src/tint/lang/core/ir/validator_value_test.cc b/src/tint/lang/core/ir/validator_value_test.cc
index 8c36322..25fb4c6 100644
--- a/src/tint/lang/core/ir/validator_value_test.cc
+++ b/src/tint/lang/core/ir/validator_value_test.cc
@@ -477,6 +477,21 @@
         << res.Failure();
 }
 
+TEST_F(IR_ValidatorTest, Var_CombinedPrivateSizeExceedsLimit) {
+    b.Append(mod.root_block, [&] {
+        for (uint32_t i = 0; i < 128; i++) {
+            b.Var(ty.ptr<private_>(ty.array<vec4u, 8000u>()));
+        }
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_THAT(
+        res.Failure().reason,
+        testing::HasSubstr("total size of private address-space variables exceeds 8388608 bytes"))
+        << res.Failure();
+}
+
 TEST_F(IR_ValidatorTest, Var_HandleMissingBindingPoint) {
     auto* v = b.Var(ty.ptr<handle, i32>());
     mod.root_block->Append(v);
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.