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 <[email protected]> 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 <[email protected]> Reviewed-by: James Price <[email protected]> --- 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