Medium watch CVSS 4.3 chrome OOB 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
4.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactOut of bounds read in Dawn
DescriptionOut of bounds read in Dawn
ComponentDawn
Sub-componentTint
Bug ClassOOB
Action tierwatch — information disclosure — security-relevant, below the RCE-class bar
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.
Tracker497975608
Fix commit53ab80b61447 (dawn) +3780/-2840
CWECWE-125 (Out-of-bounds read)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-03-31 (per vendor advisory)
Fix landed2026-04-02 (public git)
Disclosed2026-05-05
Patch gap33 days — fix public in source before the advisory

Background

Dawn / Tint
Dawn is Chromium’s WebGPU implementation; Tint is its WGSL shader compiler, which includes an IR robustness transform that inserts bounds clamps into shaders.
Robustness transform (`robustness.cc`)
a Tint IR pass that clamps out-of-range indices (coordinates, mip levels, array indices) on texture operations so GPU accesses stay in bounds.
`textureLoad` on multisampled textures
a WGSL builtin that reads a texel from a texture_multisampled_2d / texture_depth_multisampled_2d using coordinates plus a sample index selecting one of the texture’s MSAA samples.
`textureNumSamples`
the WGSL builtin returning a multisampled texture’s sample count; the inspector reports which texture bindings need this value so the backend can supply it.

Root Cause Analysis

For textureLoad on multisampled and depth-multisampled textures, the robustness transform clamped the coordinate argument (via clamp_coords) but never clamped the sample index argument, so an attacker-controlled out-of-range sample index reached the texture load unclamped. As the commit notes, an out-of-bounds sample index “returns an undefined value in some APIs, which could potentially produce uninitialized values,” i.e. an out-of-bounds/undefined read.

The fix adds a clamp_sample_index lambda that computes textureNumSamples(args[0]) - 1 as the limit and rewrites the sample-index operand to min(CastToU32(args[idx]), limit), and invokes it in the kTextureLoad handling when the texture IsAnyOf<type::MultisampledTexture, type::DepthMultisampledTexture>(). The regression IR in robustness_test.cc confirms the new lowering now emits textureNumSamples/sub/min for the sample index before textureLoad. Because clamping the sample index requires the shader to query textureNumSamples, the inspector (inspector.cc) is updated to mark such texture bindings in metadata.textures_with_num_samples (via a new uses_num_samples flag), so the GLSL backend binds the sample count.

Key insight
The robustness pass clamped coordinates and mip levels but omitted the multisample sample index, leaving textureLoad on multisampled textures able to read an out-of-bounds/undefined sample; the fix adds clamp_sample_index to min() it against textureNumSamples - 1.

Attack Path

  1. Author a malicious WGSL shader An attacker supplies a WebGPU shader that calls textureLoad on a texture_multisampled_2d (or depth-multisampled) texture with an attacker-chosen sample index.
  2. Compile through Tint The pre-patch robustness transform clamps the coordinates but leaves the sample index unclamped, so the shader’s textureLoad carries an unbounded sample index into the generated backend shader.
  3. Dispatch with an out-of-range sample index At draw/dispatch time the shader passes a sample index >= the texture’s sample count.
  4. Out-of-bounds / undefined read The GPU texture load returns an undefined value, which per the commit may be uninitialized memory, surfacing out-of-bounds data to the shader (the concrete backend read sink is not shown in this diff).

Impact Assessment

A page using WebGPU can run a crafted shader that reads out-of-bounds/undefined multisample data via textureLoad, potentially exposing uninitialized GPU memory to the shader (and thus to the renderer) across the WebGPU attack surface. The precondition is only the ability to submit a WGSL shader and a multisampled texture binding. The vendor advisory rates this medium severity (“Out of bounds read in Dawn”) and does not report in-the-wild exploitation; any use of the leaked values beyond information disclosure is (inferred).

Changed Functions

FunctionChangeNotes
for
src/tint/lang/wgsl/inspector/inspector.cc
modified
if
src/tint/lang/wgsl/inspector/inspector.cc
modified
TEST_F
src/tint/lang/wgsl/inspector/inspector_test.cc
modified

Files Changed

  • src/tint/lang/core/ir/transform/robustness.cc
  • src/tint/lang/core/ir/transform/robustness_test.cc
  • src/tint/lang/wgsl/inspector/inspector.cc
  • src/tint/lang/wgsl/inspector/inspector_test.cc

Audit Directions

  • Per-argument clamp coverage
    In robustness/bounds-insertion passes, enumerate every index-like argument of each texture/buffer builtin (coords, level, array layer, sample index) and verify each has a clamp; omissions of newer argument kinds are the recurring bug.
  • Overload-specific index arguments
    Audit builtins whose argument set varies by texture type (multisampled vs sampled vs depth), since clamps written for the common overload often miss arguments that exist only on specialized overloads.
  • Metadata driving a security clamp
    Where a bounds clamp depends on a runtime value (like textureNumSamples), confirm the inspector/metadata path actually reports and binds that value for every relevant texture type, or the inserted clamp cannot function.
From 53ab80b614475f245e7dc6dbf882ce7b24247c98 Mon Sep 17 00:00:00 2001
From: James Price <jrprice@google.com>
Date: Thu, 02 Apr 2026 16:22:10 -0700
Subject: [PATCH] [tint] Clamp sample index for textureLoad on multisampled textures

An out-of-bounds sample index returns an undefined value in some APIs,
which could potentially produce uninitialized values.

Update the inspector to reflect the new usage of textureNumSamples to
Dawn for the GLSL backend.

Fixed: 497975608
Change-Id: I6f3dfc073d317516e6cdc94918182e1fd7a6cab9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/300995
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: James Price <jrprice@google.com>
---

diff --git a/src/tint/lang/core/ir/transform/robustness.cc b/src/tint/lang/core/ir/transform/robustness.cc
index 82cc820..545fe2d 100644
--- a/src/tint/lang/core/ir/transform/robustness.cc
+++ b/src/tint/lang/core/ir/transform/robustness.cc
@@ -377,6 +377,14 @@
                              b.Min(CastToU32(args[idx]), limit)->Result());
         };
 
+        // Helper for clamping the sample index.
+        auto clamp_sample_index = [&](uint32_t idx) {
+            auto* num_samples = b.Call(ty.u32(), core::BuiltinFn::kTextureNumSamples, args[0]);
+            auto* limit = b.Subtract(num_samples, 1_u);
+            call->SetOperand(CoreBuiltinCall::kArgsOperandOffset + idx,
+                             b.Min(CastToU32(args[idx]), limit)->Result());
+        };
+
         // Select which arguments to clamp based on the function overload.
         switch (call->Func()) {
             case core::BuiltinFn::kTextureDimensions: {
@@ -393,6 +401,9 @@
                 if (texture->IsAnyOf<type::SampledTexture, type::DepthTexture>()) {
                     clamp_level(next_arg++);
                 }
+                if (texture->IsAnyOf<type::MultisampledTexture, type::DepthMultisampledTexture>()) {
+                    clamp_sample_index(next_arg++);
+                }
                 clamp_coords(1u);  // Must run after clamp_level
                 break;
             }
diff --git a/src/tint/lang/core/ir/transform/robustness_test.cc b/src/tint/lang/core/ir/transform/robustness_test.cc
index 4af13d0..01f13ed 100644
--- a/src/tint/lang/core/ir/transform/robustness_test.cc
+++ b/src/tint/lang/core/ir/transform/robustness_test.cc
@@ -2339,22 +2339,29 @@
 %load_signed = func(%coords:vec2<i32>, %level:i32):vec4<f32> {
   $B2: {
     %5:texture_multisampled_2d<f32> = load %texture
-    %6:vec2<u32> = textureDimensions %5
-    %7:vec2<u32> = sub %6, vec2<u32>(1u)
-    %8:vec2<u32> = convert %coords
-    %9:vec2<u32> = min %8, %7
-    %10:vec4<f32> = textureLoad %5, %9, %level
-    ret %10
+    %6:u32 = textureNumSamples %5
+    %7:u32 = sub %6, 1u
+    %8:u32 = convert %level
+    %9:u32 = min %8, %7
+    %10:vec2<u32> = textureDimensions %5
+    %11:vec2<u32> = sub %10, vec2<u32>(1u)
+    %12:vec2<u32> = convert %coords
+    %13:vec2<u32> = min %12, %11
+    %14:vec4<f32> = textureLoad %5, %13, %9
+    ret %14
   }
 }
 %load_unsigned = func(%coords_1:vec2<u32>, %level_1:u32):vec4<f32> {  # %coords_1: 'coords', %level_1: 'level'
   $B3: {
-    %14:texture_multisampled_2d<f32> = load %texture
-    %15:vec2<u32> = textureDimensions %14
-    %16:vec2<u32> = sub %15, vec2<u32>(1u)
-    %17:vec2<u32> = min %coords_1, %16
-    %18:vec4<f32> = textureLoad %14, %17, %level_1
-    ret %18
+    %18:texture_multisampled_2d<f32> = load %texture
+    %19:u32 = textureNumSamples %18
+    %20:u32 = sub %19, 1u
+    %21:u32 = min %level_1, %20
+    %22:vec2<u32> = textureDimensions %18
+    %23:vec2<u32> = sub %22, vec2<u32>(1u)
+    %24:vec2<u32> = min %coords_1, %23
+    %25:vec4<f32> = textureLoad %18, %24, %21
+    ret %25
   }
 }
 )";
@@ -2626,22 +2633,29 @@
 %load_signed = func(%coords:vec2<i32>, %index:i32):f32 {
   $B2: {
     %5:texture_depth_multisampled_2d = load %texture
-    %6:vec2<u32> = textureDimensions %5
-    %7:vec2<u32> = sub %6, vec2<u32>(1u)
-    %8:vec2<u32> = convert %coords
-    %9:vec2<u32> = min %8, %7
-    %10:f32 = textureLoad %5, %9, %index
-    ret %10
+    %6:u32 = textureNumSamples %5
+    %7:u32 = sub %6, 1u
+    %8:u32 = convert %index
+    %9:u32 = min %8, %7
+    %10:vec2<u32> = textureDimensions %5
+    %11:vec2<u32> = sub %10, vec2<u32>(1u)
+    %12:vec2<u32> = convert %coords
+    %13:vec2<u32> = min %12, %11
+    %14:f32 = textureLoad %5, %13, %9
+    ret %14
   }
 }
 %load_unsigned = func(%coords_1:vec2<u32>, %index_1:u32):f32 {  # %coords_1: 'coords', %index_1: 'index'
   $B3: {
-    %14:texture_depth_multisampled_2d = load %texture
-    %15:vec2<u32> = textureDimensions %14
-    %16:vec2<u32> = sub %15, vec2<u32>(1u)
-    %17:vec2<u32> = min %coords_1, %16
-    %18:f32 = textureLoad %14, %17, %index_1
-    ret %18
+    %18:texture_depth_multisampled_2d = load %texture
+    %19:u32 = textureNumSamples %18
+    %20:u32 = sub %19, 1u
+    %21:u32 = min %index_1, %20
+    %22:vec2<u32> = textureDimensions %18
+    %23:vec2<u32> = sub %22, vec2<u32>(1u)
+    %24:vec2<u32> = min %coords_1, %23
+    %25:f32 = textureLoad %18, %24, %21
+    ret %25
   }
 }
 )";
diff --git a/src/tint/lang/wgsl/inspector/inspector.cc b/src/tint/lang/wgsl/inspector/inspector.cc
index b8b0480..f9ecbad 100644
--- a/src/tint/lang/wgsl/inspector/inspector.cc
+++ b/src/tint/lang/wgsl/inspector/inspector.cc
@@ -639,6 +639,7 @@
         }
 
         bool uses_num_levels = false;
+        bool uses_num_samples = false;
         switch (builtin->Fn()) {
             case wgsl::BuiltinFn::kTextureNumLevels:
                 uses_num_levels = true;
@@ -656,6 +657,8 @@
                 uses_num_levels = !texture_type->IsAnyOf<core::type::MultisampledTexture,
                                                          core::type::DepthMultisampledTexture,
                                                          core::type::ExternalTexture>();
+                uses_num_samples = texture_type->IsAnyOf<core::type::MultisampledTexture,
+                                                         core::type::DepthMultisampledTexture>();
                 metadata.has_texture_load_with_depth_texture |=
                     texture_type
                         ->IsAnyOf<core::type::DepthTexture, core::type::DepthMultisampledTexture>();
@@ -670,10 +673,7 @@
                 break;
 
             case wgsl::BuiltinFn::kTextureNumSamples:
-                for (const auto* texture : textures) {
-                    auto texture_binding_point = texture->Attributes().binding_point.value();
-                    metadata.textures_with_num_samples.insert(texture_binding_point);
-                }
+                uses_num_samples = true;
                 break;
 
             default:
@@ -686,6 +686,12 @@
                 metadata.textures_with_num_levels.insert(texture_binding_point);
             }
         }
+        if (uses_num_samples) {
+            for (const auto* texture : textures) {
+                auto texture_binding_point = texture->Attributes().binding_point.value();
+                metadata.textures_with_num_samples.insert(texture_binding_point);
+            }
+        }
     };
 
     // Iterate the call graph in reverse topological order such that function callers come before
diff --git a/src/tint/lang/wgsl/inspector/inspector_test.cc b/src/tint/lang/wgsl/inspector/inspector_test.cc
index 09215dd..6c97476 100644
--- a/src/tint/lang/wgsl/inspector/inspector_test.cc
+++ b/src/tint/lang/wgsl/inspector/inspector_test.cc
@@ -4285,7 +4285,11 @@
     Inspector& inspector = Initialize(shader);
     auto info = inspector.GetTextureQueries("main");
 
-    ASSERT_EQ(0u, info.size());
+    ASSERT_EQ(1u, info.size());
+
+    EXPECT_EQ(Inspector::TextureQueryType::kTextureNumSamples, info[0].type);
+    EXPECT_EQ(2u, info[0].group);
+    EXPECT_EQ(3u, info[0].binding);
 }
 
 TEST_F(InspectorTextureTest, TextureLoadMultipleInEP) {
@@ -4306,7 +4310,7 @@
     Inspector& inspector = Initialize(shader);
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/src/tint/lang/core/ir/transform/robustness_test.cc b/src/tint/lang/core/ir/transform/robustness_test.cc
index 4af13d0..01f13ed 100644
--- a/src/tint/lang/core/ir/transform/robustness_test.cc
+++ b/src/tint/lang/core/ir/transform/robustness_test.cc
@@ -2339,22 +2339,29 @@
 %load_signed = func(%coords:vec2<i32>, %level:i32):vec4<f32> {
   $B2: {
     %5:texture_multisampled_2d<f32> = load %texture
-    %6:vec2<u32> = textureDimensions %5
-    %7:vec2<u32> = sub %6, vec2<u32>(1u)
-    %8:vec2<u32> = convert %coords
-    %9:vec2<u32> = min %8, %7
-    %10:vec4<f32> = textureLoad %5, %9, %level
-    ret %10
+    %6:u32 = textureNumSamples %5
+    %7:u32 = sub %6, 1u
+    %8:u32 = convert %level
+    %9:u32 = min %8, %7
+    %10:vec2<u32> = textureDimensions %5
+    %11:vec2<u32> = sub %10, vec2<u32>(1u)
+    %12:vec2<u32> = convert %coords
+    %13:vec2<u32> = min %12, %11
+    %14:vec4<f32> = textureLoad %5, %13, %9
+    ret %14
   }
 }
 %load_unsigned = func(%coords_1:vec2<u32>, %level_1:u32):vec4<f32> {  # %coords_1: 'coords', %level_1: 'level'
   $B3: {
-    %14:texture_multisampled_2d<f32> = load %texture
-    %15:vec2<u32> = textureDimensions %14
-    %16:vec2<u32> = sub %15, vec2<u32>(1u)
-    %17:vec2<u32> = min %coords_1, %16
-    %18:vec4<f32> = textureLoad %14, %17, %level_1
-    ret %18
+    %18:texture_multisampled_2d<f32> = load %texture
+    %19:u32 = textureNumSamples %18
+    %20:u32 = sub %19, 1u
+    %21:u32 = min %level_1, %20
+    %22:vec2<u32> = textureDimensions %18
+    %23:vec2<u32> = sub %22, vec2<u32>(1u)
+    %24:vec2<u32> = min %coords_1, %23
+    %25:vec4<f32> = textureLoad %18, %24, %21
+    ret %25
   }
 }
 )";
@@ -2626,22 +2633,29 @@
 %load_signed = func(%coords:vec2<i32>, %index:i32):f32 {
   $B2: {
     %5:texture_depth_multisampled_2d = load %texture
-    %6:vec2<u32> = textureDimensions %5
-    %7:vec2<u32> = sub %6, vec2<u32>(1u)
-    %8:vec2<u32> = convert %coords
-    %9:vec2<u32> = min %8, %7
-    %10:f32 = textureLoad %5, %9, %index
-    ret %10
+    %6:u32 = textureNumSamples %5
+    %7:u32 = sub %6, 1u
+    %8:u32 = convert %index
+    %9:u32 = min %8, %7
+    %10:vec2<u32> = textureDimensions %5
+    %11:vec2<u32> = sub %10, vec2<u32>(1u)
+    %12:vec2<u32> = convert %coords
+    %13:vec2<u32> = min %12, %11
+    %14:f32 = textureLoad %5, %13, %9
+    ret %14
   }
 }
 %load_unsigned = func(%coords_1:vec2<u32>, %index_1:u32):f32 {  # %coords_1: 'coords', %index_1: 'index'
   $B3: {
-    %14:texture_depth_multisampled_2d = load %texture
-    %15:vec2<u32> = textureDimensions %14
-    %16:vec2<u32> = sub %15, vec2<u32>(1u)
-    %17:vec2<u32> = min %coords_1, %16
-    %18:f32 = textureLoad %14, %17, %index_1
-    ret %18
+    %18:texture_depth_multisampled_2d = load %texture
+    %19:u32 = textureNumSamples %18
+    %20:u32 = sub %19, 1u
+    %21:u32 = min %index_1, %20
+    %22:vec2<u32> = textureDimensions %18
+    %23:vec2<u32> = sub %22, vec2<u32>(1u)
+    %24:vec2<u32> = min %coords_1, %23
+    %25:f32 = textureLoad %18, %24, %21
+    ret %25
   }
 }
 )";
diff --git a/src/tint/lang/wgsl/inspector/inspector_test.cc b/src/tint/lang/wgsl/inspector/inspector_test.cc
index 09215dd..6c97476 100644
--- a/src/tint/lang/wgsl/inspector/inspector_test.cc
+++ b/src/tint/lang/wgsl/inspector/inspector_test.cc
@@ -4285,7 +4285,11 @@
     Inspector& inspector = Initialize(shader);
     auto info = inspector.GetTextureQueries("main");
 
-    ASSERT_EQ(0u, info.size());
+    ASSERT_EQ(1u, info.size());
+
+    EXPECT_EQ(Inspector::TextureQueryType::kTextureNumSamples, info[0].type);
+    EXPECT_EQ(2u, info[0].group);
+    EXPECT_EQ(3u, info[0].binding);
 }
 
 TEST_F(InspectorTextureTest, TextureLoadMultipleInEP) {
@@ -4306,7 +4310,7 @@
     Inspector& inspector = Initialize(shader);
     auto info = inspector.GetTextureQueries("main");
 
-    ASSERT_EQ(2u, info.size());
+    ASSERT_EQ(3u, info.size());
 
     Inspector::LevelSampleInfo info1 = {
         /*type */ Inspector::TextureQueryType::kTextureNumLevels,
@@ -4318,7 +4322,12 @@
         /*group*/ 2,
         /*binding*/ 3,
     };
-    EXPECT_THAT(info, testing::UnorderedElementsAre(info1, info2));
+    Inspector::LevelSampleInfo info3 = {
+        /*type */ Inspector::TextureQueryType::kTextureNumSamples,
+        /*group*/ 1,
+        /*binding*/ 4,
+    };
+    EXPECT_THAT(info, testing::UnorderedElementsAre(info1, info2, info3));
 }
 
 TEST_F(InspectorTextureTest, TextureInSubfunction) {
Loading diff…

Original Bug Report

reported by vm...@google.com

OOB Read via Unclamped sample_index in Multisampled textureLoad

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

Overview: Tint’s Robustness transform fails to clamp the sample_index argument for textureLoad operations on multisampled textures. This allows an attacker to pass an out-of-bounds sample index to the Metal and OpenGL backends, potentially leading to an out-of-bounds read of adjacent or stale GPU memory and a cross-origin information leak.

Affected files:

  • third_party/dawn/src/tint/lang/core/ir/transform/robustness.cc

Estimated timestamp from git blame: 2024-11-12

Summary

Tint’s IR Robustness transform (third_party/dawn/src/tint/lang/core/ir/transform/robustness.cc) incorrectly skips clamping the sample_index argument for textureLoad operations on multisampled textures (specifically texture_multisampled_2d<T> and texture_depth_multisampled_2d). This omission results in a potential out-of-bounds (OOB) read vulnerability on the GPU when using WebGPU on platforms where Tint is the primary robustness mechanism, such as macOS/iOS (Metal) and OpenGL.

Technical Details

In third_party/dawn/src/tint/lang/core/ir/transform/robustness.cc, the function ClampTextureCallArgs contains logic for handling textureLoad built-ins. For multisampled textures, the sample_index is the third argument (args[2]). The current clamping logic is as follows:

case core::BuiltinFn::kTextureLoad: {
    uint32_t next_arg = 2u;
    if (type::IsTextureArray(texture->Dim())) {
        clamp_array_index(next_arg++);
    }
    if (texture->IsAnyOf<type::SampledTexture, type::DepthTexture>()) {
        clamp_level(next_arg++);
    }
    clamp_coords(1u);  // Must run after clamp_level
    break;
}

The texture->IsAnyOf<type::SampledTexture, type::DepthTexture>() check incorrectly excludes multisampled texture types. In the Tint type system, MultisampledTexture and DepthMultisampledTexture are final classes that derive directly from the base Texture class and are not instances of SampledTexture or DepthTexture.

Consequently, for multisampled textures, the transform evaluates this condition as false and entirely skips clamping the argument at next_arg (index 2).

Since no clamp_sample_index helper is implemented in the transform and kTextureNumSamples is never queried to determine the valid range, the sample_index value remains completely unmodified in the generated Tint IR. This behavior is explicitly reflected in Tint’s golden tests (e.g., TextureLoad_Multisampled2D in robustness_test.cc), which show coordinates being safely clamped while the sample index remains raw.

Impact

When lowered to Metal Shading Language (MSL) or GLSL, the unclamped sample_index is passed directly to the underlying driver functions (e.g., texture.read(coords, sample_index) in MSL or texelFetch() in GLSL). Both the MSL and GLSL specifications define out-of-bounds sample indices as undefined behavior.

On many GPUs, particularly those with Tile Based Deferred Rendering (TBDR) architectures like Apple Silicon, an out-of-bounds sample read causes the hardware sampling unit to calculate an offset far outside the memory region allocated for that pixel or tile. This returns stale data from adjacent tile memory or other GPU device memory allocations. This allows a malicious WebGPU-enabled website to perform an information leak of highly sensitive, cross-origin texture data or other GPU-resident information without triggering crashes or driver errors.

Potential Reproduction Steps

(Note: These are potential steps as our tooling agent cannot execute code to verify the exploit end-to-end.)

  1. On a system using Metal (macOS) or OpenGL, open a browser with WebGPU enabled.
  2. Create a multisampled texture (texture_multisampled_2d<f32>).
  3. Create a compute shader that calls textureLoad(tex, vec2<i32>(0,0), sample_index). Source the sample_index from an attacker-controlled storage buffer.
  4. Populate the storage buffer with a massively out-of-bounds value (e.g., 0x7FFFFFFF).
  5. Execute the compute pass and write the result of the textureLoad call to an output storage buffer.
  6. Map the output buffer and read it back to the CPU via JavaScript. Observe that the returned values contain undefined data from adjacent GPU memory, violating the WebGPU specification’s out-of-bounds safety guarantees.

Suggested Fix

Update ClampTextureCallArgs in third_party/dawn/src/tint/lang/core/ir/transform/robustness.cc to explicitly handle multisampled textures.

  1. Implement a clamp_sample_index helper lambda that queries core::BuiltinFn::kTextureNumSamples to determine the maximum valid sample index for the given texture (num_samples - 1).
  2. Add a check if (texture->IsAnyOf<type::MultisampledTexture, type::DepthMultisampledTexture>()) within the kTextureLoad case block to call this new clamp_sample_index helper on the appropriate argument index (next_arg).

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