CVE-2026-95332
Overview
Background
- `subgroupBroadcast`
- a WGSL builtin that returns the value held by the invocation whose subgroup-relative index equals the
idargument, broadcasting it to all invocations in the subgroup. - Tint builtin polyfill
- the pass in
builtin_polyfill.ccthat lowers WGSL builtins into their SPIR-V equivalents, here emittingOpGroupNonUniformBroadcast. - `tint_subgroup_last_id`
- a
kPrivateaddress-spaceu32variable Tint materializes to hold the highest valid invocation index in the current subgroup, computed at runtime fromOpGroupNonUniformIAdd. - Indeterminate value
- the SPIR-V-defined result of
OpGroupNonUniformBroadcastwhenidis 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.
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
- Craft the shader
An attacker authors a WGSL shader (via WebGPU) that calls
subgroupBroadcast(value, id)with anidlarger than the last active subgroup invocation index. - Reach the polyfill
Tint’s SPIR-V writer lowers the call through
SubgroupBroadcastinbuiltin_polyfill.cc, emitting an unguardedOpGroupNonUniformBroadcastwith the attacker-controlledid. - Trigger indeterminate result
At execution the out-of-range
idmakesOpGroupNonUniformBroadcastreturn an indeterminate value, i.e. uninitialized register/stack contents rather than a broadcast lane value. - 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
id to subgroupBroadcast; no memory corruption is required, only observation of the indeterminate result written to a readable buffer.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifsrc/tint/lang/spirv/writer/raise/builtin_polyfill.cc |
modified |
Files Changed
test/tint/builtins/gen/literal/subgroupBroadcast/f637f9.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/727609.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/3e6879.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/c36fe1.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/b7e93b.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/279027.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/1d79c7.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/0f44e2.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/279027.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/4a4334.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/727609.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/2b59c9.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/41e5d7.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/49de94.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/34ae44.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/07e2d8.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/3e6879.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/41e5d7.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/49de94.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/8855b2.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/f637f9.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/e4dd1a.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/719ad6.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/34fa3d.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/5196c8.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/912ff5.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/0f44e2.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/08beca.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/5196c8.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/867093.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/912ff5.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/08beca.wgsl.expected.spvasmsrc/tint/lang/spirv/writer/raise/builtin_polyfill.cctest/tint/builtins/gen/var/subgroupBroadcast/e4dd1a.wgsl.expected.spvasmsrc/tint/lang/spirv/writer/raise/builtin_polyfill_test.cctest/tint/builtins/gen/literal/subgroupBroadcast/34fa3d.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/838c78.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/13f36c.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/07e2d8.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/cd7aa1.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/9ccdca.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/a279d7.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/867093.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/6290a2.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/02f329.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/2b59c9.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/a3b3e5.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/6290a2.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/719ad6.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/02f329.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/e275c8.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/1d79c7.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/8855b2.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/4a4334.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/e275c8.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/13f36c.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/c36fe1.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/9ccdca.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/cd7aa1.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/a279d7.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/b7e93b.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/34ae44.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/838c78.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/a3b3e5.wgsl.expected.spvasmtest/tint/builtins/gen/literal/subgroupBroadcast/fa6810.wgsl.expected.spvasmtest/tint/builtins/gen/var/subgroupBroadcast/fa6810.wgsl.expected.spvasm
Audit Directions
- Unguarded subgroup index operandsReview other subgroup builtins (
subgroupShuffleXor,subgroupShuffleUp,subgroupShuffleDown,quadBroadcast) for lane/idarguments passed to SPIR-V group ops without a range clamp. - Indeterminate-value SPIR-V opsAudit 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
kSelector clamp to a defined value. - Cross-backend parityCompare the SPIR-V polyfill against the HLSL/MSL/GLSL backends for the same builtins, since a clamp added only in
builtin_polyfill.ccmay leave sibling writers exposed.
Patch
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)
Regression Test / PoC
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
}
}
Original Bug Report
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