WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
323560
[WebGPU][WGSL] Three or more inlined calls to one function over a 256-byte struct silently miscompile on iOS
https://bugs.webkit.org/show_bug.cgi?id=323560
Summary
[WebGPU][WGSL] Three or more inlined calls to one function over a 256-byte st...
sohamzemse143
Reported
2026-09-06 13:20:33 PDT
Created
attachment 481310
[details]
Self-contained repro: two entry points, same function, one call site vs three SUMMARY On iOS, a WGSL compute shader that inlines three or more calls to the same function over a 256-byte struct silently produces wrong results. No validation error, no compilation message, no device loss. Two entry points in one module compute the same value from the same read-only buffer. The one written with three inlined call sites writes all zeros; the one written with a single call site writes the correct data. Chrome 152 on macOS (Dawn/Metal) makes the two byte-identical. Reduced from a BN254 Groth16 prover, where it made every G2 multi-scalar-multiplication result a point that is not on the curve, so the proofs it produced were rejected by an independent verifier. Everything else in that prover, including four structurally identical MSMs over 128-byte G1 points, was bit-exact on the same device. ENVIRONMENT iPhone 15 Pro (iPhone16,1), iOS 26.6, Safari. Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Mobile/15E148 Safari/604.1 Adapter: vendor apple, architecture apple. maxComputeWorkgroupStorageSize 16384, maxComputeInvocationsPerWorkgroup 256, maxStorageBufferBindingSize 1073741824. Not reproducible in Chrome 152 on macOS (Apple silicon, Dawn/Metal). STEPS TO REPRODUCE Open the attached page on the device and tap "run" (or append ?auto=1). It is self-contained: no network, no build step, no dependencies. Expected, and what Chrome on macOS prints: repro_folded: 468 ms repro_direct: 461 ms outputs MATCH device: alive Actual on the iPhone: repro_folded: 158 ms repro_direct: 519 ms all-zero output: direct true, folded false outputs DIFFER from word 0 (window 0) device: alive THE TWO KERNELS Both read the same storage buffer of PtG2, a 256-byte struct of four Fq2, each two 8-word BN254 base field elements. pt_add_g2 is unified projective XYZZ point addition: roughly 200 lines of 256-bit modular arithmetic, no loops, no memory access of its own. @compute @workgroup_size(1) fn repro_direct(@builtin(workgroup_id) wid: vec3<u32>) { let w = wid.x; var run = pt_zero_g2(); var tot = pt_zero_g2(); for (var j = P.n_buckets; j > 0u; j = j - 1u) { run = pt_add_g2(run, BUCKETS[w * P.n_buckets + (j - 1u)]); tot = pt_add_g2(tot, run); } WSUMS[w] = pt_add_g2(tot, run); } @compute @workgroup_size(1) fn repro_folded(@builtin(workgroup_id) wid: vec3<u32>) { let w = wid.x; var pts: array<PtG2, 2>; pts[0] = pt_zero_g2(); pts[1] = pt_zero_g2(); for (var step = 0u; step < 2u * P.n_buckets + 1u; step = step + 1u) { var dst = 1u; var src = pts[0]; if (step < 2u * P.n_buckets && (step & 1u) == 0u) { dst = 0u; src = BUCKETS[w * P.n_buckets + (P.n_buckets - 1u - (step >> 1u))]; } pts[dst] = pt_add_g2(pts[dst], src); } WSUMS[w] = pts[1]; } repro_direct has three inlined pt_add_g2 call sites. repro_folded has one, reached through a two-element array and a step counter: even steps fold a bucket into pts[0], odd steps fold pts[0] into pts[1], and the final step is the closing addition. The two are mathematically identical for any input, and only repro_folded returns data. WHAT THIS IS NOT Not a race. @workgroup_size(1), no workgroupBarrier, no var<workgroup>, no atomics, one dispatch per pass and one pass per submit. Not out of bounds. Both kernels index the same buffer over the same range, and WGSL clamps or discards out-of-range accesses rather than leaving them undefined. Not input-dependent branching. The buffer holds a fixed xorshift pattern, identical for both entry points, and pt_add_g2 branches only on its own intermediates. Not a shader-creation error. getCompilationInfo() returns no messages. Not the identity fast path. The buffer is filled with nonzero words precisely so the short-circuit on a zero z-coordinate cannot skip the arithmetic under test. WHERE THE EDGE IS Bisected in the original kernel by holding the phone against a laptop one construct at a time, with the count of inlined point-operation call sites as the only variable: 2 call sites bit-exact 3 call sites every output is the all-zero identity value 4 or more call sites outputs wrong, and different on every run of the same page one 4-site spelling the device is lost, reason "unknown", empty message, and nothing in the device system log The identical algorithm over 128-byte G1 points (Fq rather than Fq2, half the struct) never failed at any call-site count on the same device in the same submit. That is why this reads as a code-size or inlining-budget cliff in the WGSL-to-Metal translation rather than a logic error in one routine. The "different on every run" behaviour above is worth flagging because it cost time to interpret: the miscompiled code is deterministic, but it was being fed projective coordinates whose encoding legitimately varies between runs, so the corruption looked like a race for a while. The reduced case here removes that and fails the same way every time. WORKAROUND Route every point operation in the affected kernels through a single inlined call site, as repro_folded does. It costs roughly twice the loop trips and recovers correct results. POSSIBLY RELATED
Bug 323543
, "[WebGPU][WGSL] Array value constructor named through a type alias emits invalid MSL, silently producing wrong compute results". Different root cause, same failure mode of compiling clean and computing wrong. The project this was reduced from already carries a workaround for that one. SOURCE The generator that produces the WGSL, and the same probe wired into the project's own build, are at
https://github.com/zemse/gpu-snark
(web/static/dbg/reduce.html and crates/g16-wgpu/src/gen/points.rs).
Attachments
Self-contained repro: two entry points, same function, one call site vs three
(56.34 KB, text/html)
2026-09-06 13:20 PDT
,
sohamzemse143
no flags
Details
View All
Add attachment
proposed patch, testcase, etc.
Radar WebKit Bug Importer
Comment 1
2026-09-06 13:36:11 PDT
<
rdar://problem/186804865
>
Mike Wyrzykowski
Comment 2
2026-09-07 11:33:36 PDT
Running this on my iPhone Air results in a firmware lockup
Mike Wyrzykowski
Comment 3
2026-09-08 18:04:56 PDT
Pull request:
https://github.com/WebKit/WebKit/pull/73518
EWS
Comment 4
2026-09-08 18:55:57 PDT
Committed
320697@main
(6d963be7219a): <
https://commits.webkit.org/320697@main
> Reviewed commits have been landed. Closing PR #73518 and removing active labels.
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug