WebKit · WebGPU
CVE-2026-43794
UAF in WebGPU
Overview
Medium
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
Background
- newBufferWithBytesNoCopy
- A Metal API that wraps existing memory as an MTLBuffer without copying; the backing storage must outlive the GPU’s use of the buffer.
- _ArrayLength bindings
- Auto-generated bindings placed above user bindings (offset by maxBindingsPerBindGroup) to hold array lengths; their index must not collide with user bindings.
- checkedSum / overflow
- Overflow-checked addition; without it the binding-index bump can wrap and alias a user binding.
Root Cause Analysis
This fixes two memory-safety bugs in the WebGPU (Metal) backend: a use-after-free of buffer storage in Queue::writeTexture’s no-copy path, and an integer-overflow in auto-generated pipeline-layout binding indices.
- writeTexture no-copy: for a large upload it creates an MTLBuffer with newBufferWithBytesNoCopy that ALIASES the caller’s newData storage rather than copying it; the GPU reads that storage asynchronously. Pre-patch, newData was allowed to be destroyed when writeTexture returned, so the aliased storage could be freed while the GPU was still consuming it — a use-after-free of GPU-visible memory. The fix moves newData into a __block Vector<uint8_t> and releases it only in the command buffer’s addCompletedHandler, keeping the storage alive until the GPU has finished.
- addPipelineLayouts array-length bindings: auto-generated ‘_ArrayLength’ bindings are placed at webBinding + limits().maxBindingsPerBindGroup to sit in a reserved range above user bindings; pre-patch this uint32 addition was unchecked, so a large webBinding could overflow and wrap the array-length binding index back down to ALIAS a real user binding, defeating the bounds check that separates array-length metadata from user bindings. The fix computes the bumped index with checkedSum<uint32_t> and rejects the layout (‘Binding index overflow in auto-generated layouts’) on overflow. The restored invariants are that no-copy GPU buffers keep their backing storage alive until the GPU is done, and that array-length binding indices cannot wrap to collide with user bindings.
Key insight
A no-copy WebGPU upload aliased caller storage the GPU read asynchronously without keeping it alive, and an array-length binding index was bumped by an unchecked uint32 add that could wrap onto a user binding — retaining the storage until GPU completion and using checkedSum close both.
Attack Path
- Trigger a no-copy texture upload Use WebGPU writeTexture with data large enough to take the newBufferWithBytesNoCopy path that aliases the caller’s storage.
- Free the aliased storage Let the source Vector be destroyed while the GPU still reads the aliased buffer — a use-after-free of GPU-visible memory.
- (Or) overflow a binding index Alternatively, craft a pipeline layout whose ‘_ArrayLength’ binding index plus maxBindingsPerBindGroup overflows uint32 and wraps onto a user binding.
- Corrupt GPU-process memory Either the freed-buffer read or the aliased binding defeats a safety invariant, corrupting memory in the WebGPU/GPU path.
Impact Assessment
Two memory-corruption bugs in the WebGPU (GPU-process) path reachable from WebGPU content: a use-after-free of GPU-visible buffer storage, and an integer-overflow that aliases an array-length binding onto a user binding. The advisory rates it memory corruption; both are controllable primitives in the graphics path.
Changed Functions
| Function | Change | Notes |
|---|---|---|
Queue::writeTexture (noCopy path)Source/WebGPU/WebGPU/Queue.mm |
modified | Moves newData into a __block Vector retained until the command buffer's completion handler, keeping the newBufferWithBytesNoCopy-aliased storage alive until the GPU consumes it (fixing a UAF). |
Device::addPipelineLayouts (_ArrayLength binding)Source/WebGPU/WebGPU/RenderPipeline.mm |
modified | Computes webBinding + maxBindingsPerBindGroup with checkedSum<uint32_t> and rejects on overflow, so an array-length binding index cannot wrap to alias a user binding. |
Files Changed
Source/WebGPU/WebGPU/Queue.mmSource/WebGPU/WebGPU/Queue.swiftSource/WebGPU/WebGPU/RenderPipeline.mmSource/WebGPU/WebGPU/WebGPUExt.hSource/WebKit/GPUProcess/graphics/WebGPU/RemoteQueue.cpp
Audit Directions
- noCopy buffer lifetimesAudit WebGPU Queue/Buffer for newBufferWithBytesNoCopy (and other no-copy/aliasing) uses that don’t retain the backing storage until a command-buffer completion handler.
- Binding-index arithmeticGrep the WebGPU layout code for binding/index computations (offsets by maxBindingsPerBindGroup, strides) using unchecked integer arithmetic that could wrap and alias.
Original Bug Report
The reporter's bug is still restricted on the tracker.
References
On This Page