Medium watch CVSS 4.7 chrome Uninitialized Memory 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
4.7
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse of uninitialized variable in Tint
DescriptionUse of uninitialized variable in Tint
ComponentTint
Bug ClassUninitialized Memory
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.
Tracker546639650
Fix commit91d2ee608d31 (dawn) +3951/-2070
CWECWE-457 (Use of uninitialized)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-08-14 (per vendor advisory)
Fix landed2026-08-19 (public git)
Disclosed2026-09-22
Patch gap34 days — fix public in source before the advisory

Background

`subgroupBroadcast`
a WGSL builtin that returns the value held by the invocation whose subgroup-relative index equals the id argument, broadcasting it to all invocations in the subgroup.
Tint builtin polyfill
the pass in builtin_polyfill.cc that lowers WGSL builtins into their SPIR-V equivalents, here emitting OpGroupNonUniformBroadcast.
`tint_subgroup_last_id`
a kPrivate address-space u32 variable Tint materializes to hold the highest valid invocation index in the current subgroup, computed at runtime from OpGroupNonUniformIAdd.
Indeterminate value
the SPIR-V-defined result of OpGroupNonUniformBroadcast when id is out of range, i.e. an uninitialized/garbage value that leaks whatever the register or stack slot last held.

Root Cause Analysis

The SubgroupBroadcast polyfill in builtin_polyfill.cc emitted a bare OpGroupNonUniformBroadcast without validating that the caller-supplied id argument fell within the active subgroup’s invocation range. Under SPIR-V semantics, OpGroupNonUniformBroadcast yields an indeterminate value when id is greater than the last active invocation index, so a shader passing an out-of-range id received uninitialized data as the broadcast result. The invariant violated is that a builtin call must produce a well-defined value for all input the WGSL spec accepts, since WGSL requires an out-of-range id to behave safely rather than surface indeterminate memory.

The fix clamps by calling CreateSubgroupLastIdIfNeeded to ensure tint_subgroup_last_id exists, then wraps the broadcast in a kSelect guarded by b.LessThanEqual(id, b.Load(subgroup_last_id_)), returning the broadcast result when id <= last_id and a typed zero (b.Zero(ret_ty)) otherwise. This guarantees a deterministic, initialized result on every path and removes the leak of uninitialized shader state.

Key insight
The core mistake was trusting the raw id operand of OpGroupNonUniformBroadcast without a range check, letting SPIR-V’s indeterminate-value rule surface uninitialized memory; the fix neutralizes it by comparing id against tint_subgroup_last_id and selecting a zero fallback whenever the index is out of range.

Attack Path

  1. Craft the shader An attacker authors a WGSL shader (via WebGPU) that calls subgroupBroadcast(value, id) with an id larger than the last active subgroup invocation index.
  2. Reach the polyfill Tint’s SPIR-V writer lowers the call through SubgroupBroadcast in builtin_polyfill.cc, emitting an unguarded OpGroupNonUniformBroadcast with the attacker-controlled id.
  3. Trigger indeterminate result At execution the out-of-range id makes OpGroupNonUniformBroadcast return an indeterminate value, i.e. uninitialized register/stack contents rather than a broadcast lane value.
  4. Exfiltrate The shader stores that indeterminate result to a storage buffer (e.g. via OpAccessChain/OpStore), letting the attacker read back leaked uninitialized data through mapped GPU memory.

Impact Assessment

An attacker running an untrusted WebGPU shader gains the ability to read uninitialized GPU memory contents in the GPU/renderer process, potentially disclosing residual data from prior shader executions. The precondition is that the target supports subgroup operations and the attacker can submit a shader passing an out-of-range id to subgroupBroadcast; no memory corruption is required, only observation of the indeterminate result written to a readable buffer.

Changed Functions

FunctionChangeNotes
if
src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
modified

Files Changed

  • test/tint/builtins/gen/literal/subgroupBroadcast/f637f9.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/727609.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/3e6879.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/c36fe1.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/b7e93b.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/279027.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/1d79c7.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/0f44e2.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/279027.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/4a4334.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/727609.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/2b59c9.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/41e5d7.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/49de94.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/34ae44.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/07e2d8.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/3e6879.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/41e5d7.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/49de94.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/8855b2.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/f637f9.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/e4dd1a.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/719ad6.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/34fa3d.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/5196c8.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/912ff5.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/0f44e2.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/08beca.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/5196c8.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/867093.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/912ff5.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/08beca.wgsl.expected.spvasm
  • src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
  • test/tint/builtins/gen/var/subgroupBroadcast/e4dd1a.wgsl.expected.spvasm
  • src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc
  • test/tint/builtins/gen/literal/subgroupBroadcast/34fa3d.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/838c78.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/13f36c.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/07e2d8.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/cd7aa1.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/9ccdca.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/a279d7.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/867093.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/6290a2.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/02f329.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/2b59c9.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/a3b3e5.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/6290a2.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/719ad6.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/02f329.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/e275c8.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/1d79c7.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/8855b2.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/4a4334.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/e275c8.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/13f36c.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/c36fe1.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/9ccdca.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/cd7aa1.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/a279d7.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/b7e93b.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/34ae44.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/838c78.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/a3b3e5.wgsl.expected.spvasm
  • test/tint/builtins/gen/literal/subgroupBroadcast/fa6810.wgsl.expected.spvasm
  • test/tint/builtins/gen/var/subgroupBroadcast/fa6810.wgsl.expected.spvasm

Audit Directions

  • Unguarded subgroup index operands
    Review other subgroup builtins (subgroupShuffleXor, subgroupShuffleUp, subgroupShuffleDown, quadBroadcast) for lane/id arguments passed to SPIR-V group ops without a range clamp.
  • Indeterminate-value SPIR-V ops
    Audit every Tint lowering that emits a SPIR-V instruction whose spec permits an indeterminate result on out-of-range input, ensuring each guards with a kSelect or clamp to a defined value.
  • Cross-backend parity
    Compare the SPIR-V polyfill against the HLSL/MSL/GLSL backends for the same builtins, since a clamp added only in builtin_polyfill.cc may leave sibling writers exposed.
From 91d2ee608d3186b4d0003a8c5eaf9a4debb1a26b Mon Sep 17 00:00:00 2001
From: dan sinclair <dsinclair@chromium.org>
Date: Wed, 19 Aug 2026 09:08:44 -0700
Subject: [PATCH] Check SubgroupBroadcast ID in SPIR-V

When using a `subgroupBroadcast` if the provided `id` is out of range we
return an indeterminant value instead of the broadcast returned value.

Fixed: 546639650
Change-Id: I1678cf899977a377eb9fcd2c71e5a0a1b79dd8f7
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/333795
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
---
diff --git a/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc b/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
index 07524a8..c847a90 100644
--- a/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
+++ b/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
@@ -1320,6 +1320,15 @@
         ir.properties.Add(core::ir::Property::kAllowAnyInputAttachmentIndexType);
     }
 
+    void CreateSubgroupLastIdIfNeeded() {
+        if (subgroup_last_id_) {
+            return;
+        }
+        b.Append(ir.root_block, [&] {
+            subgroup_last_id_ = b.Var<core::AddressSpace::kPrivate, u32>("tint_subgroup_last_id");
+        });
+    }
+
     /// Handles SubgroupShuffle() builtin.
     /// @param builtin the builtin call instruction
     void SubgroupShuffle(core::ir::CoreBuiltinCall* builtin) {
@@ -1336,12 +1345,8 @@
 
         /// Polyfill a `subgroupShuffleX` builtin call with one that has clamped the arg2 param
         auto* shuffle_id = builtin->Args()[1];
-        if (!subgroup_last_id_) {
-            b.Append(ir.root_block, [&] {
-                subgroup_last_id_ =
-                    b.Var<core::AddressSpace::kPrivate, u32>("tint_subgroup_last_id");
-            });
-        }
+        CreateSubgroupLastIdIfNeeded();
+
         b.InsertBefore(builtin, [&] {
             auto* last_id = b.Load(subgroup_last_id_);
             auto* clamp_via_min = b.Min(shuffle_id, last_id);
@@ -1389,8 +1394,26 @@
 
         // For const signed int IDs, compile-time convert to u32 to maintain constness.
         if (id->Type()->IsSignedIntegerScalar()) {
-            builtin->SetArg(1, b.Constant(id->As<core::ir::Constant>()->Value()->ValueAs<u32>()));
+            id = b.Constant(id->As<core::ir::Constant>()->Value()->ValueAs<u32>());
         }
+        builtin->SetArg(1, id);
+
+        CreateSubgroupLastIdIfNeeded();
+
+        core::ir::CoreBuiltinCall* call = nullptr;
+        b.InsertAfter(builtin, [&] {
+            auto* ret_ty = builtin->Result()->Type();
+            auto* result = b.InstructionResult(ret_ty);
+
+            // Replace the uses before we create the new usage.
+            builtin->Result()->ReplaceAllUsesWith(result);
+
+            auto* cmp = b.LessThanEqual(id, b.Load(subgroup_last_id_));
+            call = b.CallWithResult(result, core::BuiltinFn::kSelect, b.Zero(ret_ty), builtin, cmp);
+        });
+
+        // Make sure the select gets converted to proper SPIR-V select
+        Select(call);
     }
 
     /// Handle a QuadBroadcast() builtin.
diff --git a/test/tint/builtins/gen/literal/subgroupBroadcast/02f329.wgsl.expected.spvasm b/test/tint/builtins/gen/literal/subgroupBroadcast/02f329.wgsl.expected.spvasm
index 2d1a399..5acedfd 100644
--- a/test/tint/builtins/gen/literal/subgroupBroadcast/02f329.wgsl.expected.spvasm
+++ b/test/tint/builtins/gen/literal/subgroupBroadcast/02f329.wgsl.expected.spvasm
@@ -4,17 +4,19 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 1
-; Bound: 26
+; Bound: 38
 ; Schema: 0
                OpCapability Shader
                OpCapability Float16
                OpCapability StorageBuffer16BitAccess
                OpCapability GroupNonUniformBallot
+               OpCapability GroupNonUniformArithmetic
                OpMemoryModel Logical GLSL450
                OpEntryPoint Fragment %fragment_main "fragment_main"
                OpExecutionMode %fragment_main OriginUpperLeft
                OpMemberName %prevent_dce_block 0 "inner"
                OpName %prevent_dce_block "prevent_dce_block"
+               OpName %tint_subgroup_last_id "tint_subgroup_last_id"
                OpName %subgroupBroadcast_02f329 "subgroupBroadcast_02f329"
                OpName %res "res"
                OpName %fragment_main "fragment_main"
@@ -28,30 +30,43 @@
 %prevent_dce_block = OpTypeStruct %v4half
 %_ptr_StorageBuffer_prevent_dce_block = OpTypePointer StorageBuffer %prevent_dce_block
           %1 = OpVariable %_ptr_StorageBuffer_prevent_dce_block StorageBuffer
-          %7 = OpTypeFunction %v4half
        %uint = OpTypeInt 32 0
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %9 = OpConstantNull %uint
+%tint_subgroup_last_id = OpVariable %_ptr_Private_uint Private %9
+         %11 = OpTypeFunction %v4half
      %uint_3 = OpConstant %uint 3
 %half_0x1p_0 = OpConstant %half 0x1p+0
-         %12 = OpConstantComposite %v4half %half_0x1p_0 %half_0x1p_0 %half_0x1p_0 %half_0x1p_0
+         %15 = OpConstantComposite %v4half %half_0x1p_0 %half_0x1p_0 %half_0x1p_0 %half_0x1p_0
      %uint_1 = OpConstant %uint 1
+       %bool = OpTypeBool
+     %v4bool = OpTypeVector %bool 4
+         %24 = OpConstantNull %v4half
 %_ptr_Function_v4half = OpTypePointer Function %v4half
        %void = OpTypeVoid
-         %20 = OpTypeFunction %void
+         %30 = OpTypeFunction %void
 %_ptr_StorageBuffer_v4half = OpTypePointer StorageBuffer %v4half
      %uint_0 = OpConstant %uint 0
-%subgroupBroadcast_02f329 = OpFunction %v4half None %7
-          %8 = OpLabel
+%subgroupBroadcast_02f329 = OpFunction %v4half None %11
+         %12 = OpLabel
         %res = OpVariable %_ptr_Function_v4half Function
-          %9 = OpGroupNonUniformBroadcast %v4half %uint_3 %12 %uint_1
-               OpStore %res %9
-         %17 = OpLoad %v4half %res None
-               OpReturnValue %17
+         %13 = OpGroupNonUniformBroadcast %v4half %uint_3 %15 %uint_1
+         %18 = OpLoad %uint %tint_subgroup_last_id None
+         %19 = OpULessThanEqual %bool %uint_1 %18
+         %22 = OpCompositeConstruct %v4bool %19 %19 %19 %19
+         %23 = OpSelect %v4half %22 %13 %24
+               OpStore %res %23
+         %27 = OpLoad %v4half %res None
+               OpReturnValue %27
                OpFunctionEnd
-%fragment_main = OpFunction %void None %20
-         %21 = OpLabel
-         %22 = OpFunctionCall %v4half %subgroupBroadcast_02f329
-         %23 = OpAccessChain %_ptr_StorageBuffer_v4half %1 %uint_0
-               OpStore %23 %22 None
+%fragment_main = OpFunction %void None %30
+         %31 = OpLabel
+         %32 = OpGroupNonUniformIAdd %uint %uint_3 Reduce %uint_1
+         %33 = OpISub %uint %32 %uint_1
+               OpStore %tint_subgroup_last_id %33 None
+         %34 = OpFunctionCall %v4half %subgroupBroadcast_02f329
+         %35 = OpAccessChain %_ptr_StorageBuffer_v4half %1 %uint_0
+               OpStore %35 %34 None
                OpReturn
                OpFunctionEnd
 ;
@@ -60,17 +75,19 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 1
-; Bound: 26
+; Bound: 38
 ; Schema: 0
                OpCapability Shader
                OpCapability Float16
                OpCapability StorageBuffer16BitAccess
                OpCapability GroupNonUniformBallot
+               OpCapability GroupNonUniformArithmetic
                OpMemoryModel Logical GLSL450
                OpEntryPoint GLCompute %compute_main "compute_main"
                OpExecutionMode %compute_main LocalSize 1 1 1
                OpMemberName %prevent_dce_block 0 "inner"
                OpName %prevent_dce_block "prevent_dce_block"
+               OpName %tint_subgroup_last_id "tint_subgroup_last_id"
                OpName %subgroupBroadcast_02f329 "subgroupBroadcast_02f329"
                OpName %res "res"
                OpName %compute_main "compute_main"
@@ -84,29 +101,42 @@
 %prevent_dce_block = OpTypeStruct %v4half
 %_ptr_StorageBuffer_prevent_dce_block = OpTypePointer StorageBuffer %prevent_dce_block
           %1 = OpVariable %_ptr_StorageBuffer_prevent_dce_block StorageBuffer
-          %7 = OpTypeFunction %v4half
        %uint = OpTypeInt 32 0
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %9 = OpConstantNull %uint
+%tint_subgroup_last_id = OpVariable %_ptr_Private_uint Private %9
+         %11 = OpTypeFunction %v4half
      %uint_3 = OpConstant %uint 3
 %half_0x1p_0 = OpConstant %half 0x1p+0
-         %12 = OpConstantComposite %v4half %half_0x1p_0 %half_0x1p_0 %half_0x1p_0 %half_0x1p_0
+         %15 = OpConstantComposite %v4half %half_0x1p_0 %half_0x1p_0 %half_0x1p_0 %half_0x1p_0
      %uint_1 = OpConstant %uint 1
+       %bool = OpTypeBool
+     %v4bool = OpTypeVector %bool 4
+         %24 = OpConstantNull %v4half
 %_ptr_Function_v4half = OpTypePointer Function %v4half
        %void = OpTypeVoid
-         %20 = OpTypeFunction %void
+         %30 = OpTypeFunction %void
 %_ptr_StorageBuffer_v4half = OpTypePointer StorageBuffer %v4half
      %uint_0 = OpConstant %uint 0
-%subgroupBroadcast_02f329 = OpFunction %v4half None %7
-          %8 = OpLabel
+%subgroupBroadcast_02f329 = OpFunction %v4half None %11
+         %12 = OpLabel
         %res = OpVariable %_ptr_Function_v4half Function
-          %9 = OpGroupNonUniformBroadcast %v4half %uint_3 %12 %uint_1
-               OpStore %res %9
-         %17 = OpLoad %v4half %res None
-               OpReturnValue %17
+         %13 = OpGroupNonUniformBroadcast %v4half %uint_3 %15 %uint_1
+         %18 = OpLoad %uint %tint_subgroup_last_id None
+         %19 = OpULessThanEqual %bool %uint_1 %18
+         %22 = OpCompositeConstruct %v4bool %19 %19 %19 %19
+         %23 = OpSelect %v4half %22 %13 %24
+               OpStore %res %23
+         %27 = OpLoad %v4half %res None
+               OpReturnValue %27
                OpFunctionEnd
-%compute_main = OpFunction %void None %20
-         %21 = OpLabel
-         %22 = OpFunctionCall %v4half %subgroupBroadcast_02f329
-         %23 = OpAccessChain %_ptr_StorageBuffer_v4half %1 %uint_0
-               OpStore %23 %22 None
+%compute_main = OpFunction %void None %30
+         %31 = OpLabel
+         %32 = OpGroupNonUniformIAdd %uint %uint_3 Reduce %uint_1
+         %33 = OpISub %uint %32 %uint_1
+               OpStore %tint_subgroup_last_id %33 None
+         %34 = OpFunctionCall %v4half %subgroupBroadcast_02f329
+         %35 = OpAccessChain %_ptr_StorageBuffer_v4half %1 %uint_0
+               OpStore %35 %34 None
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/builtins/gen/literal/subgroupBroadcast/07e2d8.wgsl.expected.spvasm b/test/tint/builtins/gen/literal/subgroupBroadcast/07e2d8.wgsl.expected.spvasm
index ca1b932..928f57f 100644
--- a/test/tint/builtins/gen/literal/subgroupBroadcast/07e2d8.wgsl.expected.spvasm
+++ b/test/tint/builtins/gen/literal/subgroupBroadcast/07e2d8.wgsl.expected.spvasm
@@ -4,17 +4,19 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 1
-; Bound: 24
+; Bound: 34
 ; Schema: 0
                OpCapability Shader
                OpCapability Float16
                OpCapability StorageBuffer16BitAccess
                OpCapability GroupNonUniformBallot
+               OpCapability GroupNonUniformArithmetic
                OpMemoryModel Logical GLSL450
                OpEntryPoint Fragment %fragment_main "fragment_main"
                OpExecutionMode %fragment_main OriginUpperLeft
                OpMemberName %prevent_dce_block 0 "inner"
                OpName %prevent_dce_block "prevent_dce_block"
+               OpName %tint_subgroup_last_id "tint_subgroup_last_id"
                OpName %subgroupBroadcast_07e2d8 "subgroupBroadcast_07e2d8"
                OpName %res "res"
                OpName %fragment_main "fragment_main"
@@ -27,29 +29,40 @@
 %prevent_dce_block = OpTypeStruct %half
 %_ptr_StorageBuffer_prevent_dce_block = OpTypePointer StorageBuffer %prevent_dce_block
           %1 = OpVariable %_ptr_StorageBuffer_prevent_dce_block StorageBuffer
-          %6 = OpTypeFunction %half
        %uint = OpTypeInt 32 0
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %8 = OpConstantNull %uint
+%tint_subgroup_last_id = OpVariable %_ptr_Private_uint Private %8
+         %10 = OpTypeFunction %half
      %uint_3 = OpConstant %uint 3
 %half_0x1p_0 = OpConstant %half 0x1p+0
      %uint_1 = OpConstant %uint 1
+       %bool = OpTypeBool
+%half_0x0p_0 = OpConstant %half 0x0p+0
 %_ptr_Function_half = OpTypePointer Function %half
        %void = OpTypeVoid
-         %18 = OpTypeFunction %void
+         %26 = OpTypeFunction %void
 %_ptr_StorageBuffer_half = OpTypePointer StorageBuffer %half
      %uint_0 = OpConstant %uint 0
-%subgroupBroadcast_07e2d8 = OpFunction %half None %6
-          %7 = OpLabel
+%subgroupBroadcast_07e2d8 = OpFunction %half None %10
+         %11 = OpLabel
         %res = OpVariable %_ptr_Function_half Function
-          %8 = OpGroupNonUniformBroadcast %half %uint_3 %half_0x1p_0 %uint_1
-               OpStore %res %8
-         %15 = OpLoad %half %res None
-               OpReturnValue %15
+         %12 = OpGroupNonUniformBroadcast %half %uint_3 %half_0x1p_0 %uint_1
+         %16 = OpLoad %uint %tint_subgroup_last_id None
+         %17 = OpULessThanEqual %bool %uint_1 %16
+         %19 = OpSelect %half %17 %12 %half_0x0p_0
+               OpStore %res %19
+         %23 = OpLoad %half %res None
+               OpReturnValue %23
                OpFunctionEnd
-%fragment_main = OpFunction %void None %18
-         %19 = OpLabel
-         %20 = OpFunctionCall %half %subgroupBroadcast_07e2d8
-         %21 = OpAccessChain %_ptr_StorageBuffer_half %1 %uint_0
-               OpStore %21 %20 None
+%fragment_main = OpFunction %void None %26
+         %27 = OpLabel
+         %28 = OpGroupNonUniformIAdd %uint %uint_3 Reduce %uint_1
+         %29 = OpISub %uint %28 %uint_1
+               OpStore %tint_subgroup_last_id %29 None
+         %30 = OpFunctionCall %half %subgroupBroadcast_07e2d8
+         %31 = OpAccessChain %_ptr_StorageBuffer_half %1 %uint_0
+               OpStore %31 %30 None
                OpReturn
                OpFunctionEnd
 ;
@@ -58,17 +71,19 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 1
-; Bound: 24
+; Bound: 34
 ; Schema: 0
                OpCapability Shader
                OpCapability Float16
                OpCapability StorageBuffer16BitAccess
                OpCapability GroupNonUniformBallot
+               OpCapability GroupNonUniformArithmetic
                OpMemoryModel Logical GLSL450
                OpEntryPoint GLCompute %compute_main "compute_main"
                OpExecutionMode %compute_main LocalSize 1 1 1
                OpMemberName %prevent_dce_block 0 "inner"
                OpName %prevent_dce_block "prevent_dce_block"
+               OpName %tint_subgroup_last_id "tint_subgroup_last_id"
                OpName %subgroupBroadcast_07e2d8 "subgroupBroadcast_07e2d8"
                OpName %res "res"
                OpName %compute_main "compute_main"
@@ -81,28 +96,39 @@
 %prevent_dce_block = OpTypeStruct %half
 %_ptr_StorageBuffer_prevent_dce_block = OpTypePointer StorageBuffer %prevent_dce_block
           %1 = OpVariable %_ptr_StorageBuffer_prevent_dce_block StorageBuffer
-          %6 = OpTypeFunction %half
        %uint = OpTypeInt 32 0
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %8 = OpConstantNull %uint
+%tint_subgroup_last_id = OpVariable %_ptr_Private_uint Private %8
+         %10 = OpTypeFunction %half
      %uint_3 = OpConstant %uint 3
 %half_0x1p_0 = OpConstant %half 0x1p+0
      %uint_1 = OpConstant %uint 1
+       %bool = OpTypeBool
+%half_0x0p_0 = OpConstant %half 0x0p+0
 %_ptr_Function_half = OpTypePointer Function %half
        %void = OpTypeVoid
-         %18 = OpTypeFunction %void
+         %26 = OpTypeFunction %void
 %_ptr_StorageBuffer_half = OpTypePointer StorageBuffer %half
      %uint_0 = OpConstant %uint 0
-%subgroupBroadcast_07e2d8 = OpFunction %half None %6
-          %7 = OpLabel
+%subgroupBroadcast_07e2d8 = OpFunction %half None %10
+         %11 = OpLabel
         %res = OpVariable %_ptr_Function_half Function
-          %8 = OpGroupNonUniformBroadcast %half %uint_3 %half_0x1p_0 %uint_1
-               OpStore %res %8
-         %15 = OpLoad %half %res None
-               OpReturnValue %15
+         %12 = OpGroupNonUniformBroadcast %half %uint_3 %half_0x1p_0 %uint_1
+         %16 = OpLoad %uint %tint_subgroup_last_id None
+         %17 = OpULessThanEqual %bool %uint_1 %16
+         %19 = OpSelect %half %17 %12 %half_0x0p_0
+               OpStore %res %19
+         %23 = OpLoad %half %res None
+               OpReturnValue %23
                OpFunctionEnd
-%compute_main = OpFunction %void None %18
-         %19 = OpLabel
-         %20 = OpFunctionCall %half %subgroupBroadcast_07e2d8
-         %21 = OpAccessChain %_ptr_StorageBuffer_half %1 %uint_0
-               OpStore %21 %20 None
+%compute_main = OpFunction %void None %26
+         %27 = OpLabel
+         %28 = OpGroupNonUniformIAdd %uint %uint_3 Reduce %uint_1
+         %29 = OpISub %uint %28 %uint_1
+               OpStore %tint_subgroup_last_id %29 None
+         %30 = OpFunctionCall %half %subgroupBroadcast_07e2d8
+         %31 = OpAccessChain %_ptr_StorageBuffer_half %1 %uint_0
+               OpStore %31 %30 None
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/builtins/gen/literal/subgroupBroadcast/08beca.wgsl.expected.spvasm b/test/tint/builtins/gen/literal/subgroupBroadcast/08beca.wgsl.expected.spvasm
index e8ea2ad..0fd2849 100644
--- a/test/tint/builtins/gen/literal/subgroupBroadcast/08beca.wgsl.expected.spvasm
+++ b/test/tint/builtins/gen/literal/subgroupBroadcast/08beca.wgsl.expected.spvasm
@@ -4,15 +4,17 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 1
-; Bound: 24
... (file diff truncated)
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc b/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc
index 59403b2..f889ff2 100644
--- a/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc
+++ b/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc
@@ -4346,10 +4346,20 @@
     EXPECT_EQ(src, str());
 
     auto* expect = R"(
+$B1: {  # root
+  %tint_subgroup_last_id:ptr<private, u32, read_write> = var undef
+}
+
 %foo = @fragment func():void {
-  $B1: {
-    %2:i32 = subgroupBroadcast 1i, 1u
-    %a:i32 = let %2
+  $B2: {
+    %3:u32 = subgroupAdd 1u
+    %4:u32 = sub %3, 1u
+    store %tint_subgroup_last_id, %4
+    %5:i32 = subgroupBroadcast 1i, 1u
+    %6:u32 = load %tint_subgroup_last_id
+    %7:bool = lte 1u, %6
+    %8:i32 = spirv.select %7, %5, 0i
+    %a:i32 = let %8
     ret
   }
 }
Loading diff…

Original Bug Report

reported by xi...@google.com

IMG: cross-origin information disclosure exists in the WebGPU shading pipeline due to uninitialization

  • Attack surface: WebGPU
  • Impact: Cross-origin disclosure of GPU shader contents to JavaScript from the unsandboxed Chrome GPU process. No memory corruption.

Android internal bug: b/538356700

View on issue tracker