Heap buffer overflow in WebGL uniform upload leads to control-flow redirection in the GPU process (web-reachable via WebGL2)
Categories
(Core :: Graphics, defect)
Tracking
()
People
(Reporter: call_eax, Assigned: lsalzman)
Details
(6 keywords, Whiteboard: [client-bounty-form][adv-main154-][adv-esr153.1-][adv-esr115.41+][adv-esr140.16+])
Crash Data
Attachments
(5 files, 3 external files)
Summary
Ordinary WebGL2 content can overflow a roughly 64 KiB driver command buffer in the Firefox GPU process by calling any glUniform*v-family function with a typed array larger than the uniform array the linked program declares. WebGLContext::UniformData forwards the attacker-chosen element count straight to the GL driver without comparing it against the uniform's declared array size, and the driver copies the oversized payload without clamping. A corrupted 64-bit value at a fixed payload offset is later loaded into the program counter and tracks the planted value.
Steps to reproduce
./BrowserBuild/Release/dist/Nightly.app/Contents/MacOS/firefox minimal_poc.html
The page self-drives on load: it links a program declaring uniform vec4 u[2] (32 bytes of storage), confirms the sink runs cleanly on a correctly sized 8-float upload, then calls gl.uniform4fv with a 1 MiB Float32Array on that uniform.
Open pc_control_marker1_uiv_alternating.html and pc_control_marker2_uiv_varied.html the same way: each plants a distinct 64-bit marker via gl.uniform4uiv and crashes the GPU process with that marker in the program counter.
Observed result
ASan build, minimal_poc.html:
AddressSanitizer: heap-buffer-overflow on address 0x6310010c8818
WRITE of size 1048576 at 0x6310010c8818 thread T63
#1 mozilla::webgl::UniformAs4fv(...)
#2 mozilla::WebGLContext::UniformData(...) const
#5 mozilla::dom::WebGLParent::RecvDispatchCommands(...)
0x6310010c8818 is located 0 bytes after 65560-byte region
allocated by gleInitCommandBuffer (Apple GLEngine, inside the GL driver)
Boundary, bisected to 16-byte precision: last clean payload 65504 bytes, first fatal 65520. The threshold tracks payload bytes, not the declared uniform size (49152 bytes into a 32-byte declared array is clean), so this overflows the driver's command buffer. No legitimate call can exceed 16384 bytes on this host (MAX_*_UNIFORM_VECTORS is 1024), so every fatal payload is out-of-contract.
Control-flow redirection, sanitizer-disabled build, GPU process "Nightly GPU Helper", faulting thread "CanvasRenderer". Two runs, two different planted values, same payload offset and same call site:
marker1: pc = far = x8 = 0x4242424241414141 esr 0x8a000000 (PC alignment)
marker2: pc = far = x8 = 0x4142434445464748 esr 0x82000004 (Instruction Abort) Translation fault
both: lr = GLEngine+0xd0fd4 (gleCmdProcessor)
#5 mozilla::WebGLContext::Finish() #6 mozilla::dom::WebGLParent::RecvFinish()
Each pc holds exactly the value that run planted, so the branch target is attacker-chosen rather than merely disturbed, and both faults are instruction-side. The fault class tracks the planted value too: marker1 is not 4-byte aligned and faults on PC alignment, marker2 is aligned and reaches instruction fetch. Both .ips reports are included.
Impact
Web-reachable out-of-bounds write in the Firefox GPU process, from any origin, with a WebGL2 canvas and one uniform-upload call: no pref, no configuration, no user gesture beyond page load. The corrupted 64-bit slot is then fetched as the next instruction address.
Affected component
- WebGLContextGL.cpp:1391 - the sink,
WebGLContext::UniformData:(*pfn)(*gl, static_cast<GLint>(loc), elemCount, transpose, ptr) - WebGLContextGL.cpp:1361 - the only length guard, applied to non-array uniforms only, so any count is accepted for an array uniform
- WebGLTypes.h:844 -
ActiveInfo::elemCount, the declared array size, held at the sink asvalidationInfo.info.elemCountbut never compared - ClientWebGLContext.cpp:5099 - client-side mirror, same gap
Affected version: Nightly 155.0a1, on two local builds of that revision: one ASan, one sanitizer-disabled. default-on WebGL2, no pref or flag. The sink and its guard carry no preprocessor conditionals and no assert gating, so the missing validation is present on every channel rather than Nightly-only. The crash was tested on the GPU process on macOS / Apple Silicon (Apple's native GL driver).
| Reporter | ||
Comment 1•3 months ago
|
||
| Reporter | ||
Comment 2•3 months ago
|
||
| Reporter | ||
Comment 3•3 months ago
|
||
| Reporter | ||
Comment 4•3 months ago
|
||
Updated•3 months ago
|
| Assignee | ||
Comment 5•3 months ago
•
|
||
glUniform is supposed to signal an error of GL_INVALID_OPERATION if the size of the underlying type doesn't match the size passed in, rather than do an out-of-bounds copy. So even though we don't check that higher up, the driver itself is the source of the bug, not us - therefor sec-vector. As far as we know, this is only on macOS so far, and we can't verify what versions. Given that this is also only in the GPU process, this is priv escalation, but not a parent process sandbox escape.
| Assignee | ||
Comment 6•3 months ago
|
||
Updated•3 months ago
|
| Reporter | ||
Comment 7•3 months ago
|
||
Thanks for the follow up lsalzman. I figured similar but wasnt quite sure. And yes even in my testing, it currently only worked on macOS. For version reference, I tested on macOS Tahoe Beta 26.6. I could not get it to reproduce on Linux (Ubuntu 24.04.4 LTS) or Android, I also didnt do a deeper dive on either of those.
Comment 8•3 months ago
|
||
:lsalzman, please update all release status flags to reflect the affected/unaffected/disabled state correctly. Which branches (beta, release, and/or ESR) are affected by this flaw?
| Assignee | ||
Updated•3 months ago
|
Comment 10•3 months ago
|
||
Comment 11•3 months ago
|
||
TEST-UNEXPECTED-FAIL | dom/canvas/test/webgl-conf/generated/test_conformance__more__functions__uniformfArrayLen1.html | testUniformArray
followed by more failures
| Assignee | ||
Updated•3 months ago
|
Comment 12•3 months ago
|
||
Comment 13•3 months ago
|
||
Updated•3 months ago
|
Comment 14•3 months ago
|
||
The patch landed in nightly and beta is affected, along with ESR.
:lsalzman, is this bug important enough to require an uplift?
- If yes, please nominate the patch for beta and ESR approvals.
- See https://wiki.mozilla.org/Release_Management/Requesting_an_Uplift for documentation on how to request an uplift.
- If no, please set
status-firefox154and the ESR status flag(s) towontfix.
For more information, please visit BugBot documentation.
| Assignee | ||
Comment 15•3 months ago
|
||
Comment on attachment 9621063 [details]
(secure)
ESR Uplift Approval Request
- If this is not a sec:{high,crit} bug, please state case for ESR consideration: GL driver can cause out-of-bounds write
- User impact if declined: Priv escalation
- Fix Landed on Version: 155
- Risk to taking this patch: Low
- Why is the change risky/not risky? (and alternatives if risky): Avoids supplying tricky value to GL driver
Beta/Release Uplift Approval Request
- User impact if declined/Reason for urgency: GL driver out-of-bounds write
- Is this code covered by automated tests?: Yes
- Has the fix been verified in Nightly?: Yes
- Needs manual test from QE?: No
- If yes, steps to reproduce:
- List of other uplifts needed: None
- Risk to taking this patch: Low
- Why is the change risky/not risky? (and alternatives if risky): Avoids supply tricky value to GL driver
- String changes made/needed: None
- Is Android affected?: Unknown
Comment 16•3 months ago
|
||
Comment on attachment 9621063 [details]
(secure)
Approved for 154.0b8
Updated•3 months ago
|
Comment 17•3 months ago
|
||
| uplift | ||
Comment 18•2 months ago
|
||
Comment on attachment 9621063 [details]
(secure)
Approved for 153.1esr
Comment 19•2 months ago
|
||
| uplift | ||
Updated•2 months ago
|
Updated•2 months ago
|
Updated•2 months ago
|
Updated•2 months ago
|
Updated•2 months ago
|
Updated•2 months ago
|
Comment 20•2 months ago
|
||
(In reply to Lee Salzman [:lsalzman] from comment #5)
the driver itself is the source of the bug, not us - therefor sec-vector.
Although "sec-vector" conveys the blame for the bug, it doesn't usefully capture the risk to Firefox users on macOS who don't get a choice of what driver they use. For similar reasons we couldn't protect our users by blocklisting a buggy driver version like we can on other platforms, because that would kill major functionality for all Mac users. This bug should have been tagged sec-high in addition to the sec-vector.
Thankfully you were able to work around the driver's compliance issue. In cases where we can't we can add the blocked-thirdparty keyword so the nagbot and release managers know why there's a stale sec-high.
Comment 21•2 months ago
|
||
We should reconsider fixing this on ESR-140
bp-57e0adca-4cd1-4af0-9238-3e8100260902
Updated•2 months ago
|
Comment 22•2 months ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D315942
(cherry picked from commit 373c8e078e64ccc0ad9df7d4e4737fc71b916c55)
Updated•2 months ago
|
Comment 23•2 months ago
|
||
firefox-esr140 Uplift Approval Request
- User impact if declined/Reason for urgency: GL driver out-of-bounds write
- Code covered by automated testing?: yes
- Fix verified in Nightly?: yes
- Needs manual QE testing?: no
- Steps to reproduce for manual QE testing: n/a
- Risk associated with taking this patch: low
- Explanation of risk level: Avoids supply tricky value to GL driver. Already shipped to Release.
- String changes made/needed?: None
- Is Android affected?: unknown
Comment 24•2 months ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D315942
(cherry picked from commit 373c8e078e64ccc0ad9df7d4e4737fc71b916c55)
Updated•2 months ago
|
Comment 25•2 months ago
|
||
firefox-esr115 Uplift Approval Request
- User impact if declined/Reason for urgency: GL driver out-of-bounds write
- Code covered by automated testing?: yes
- Fix verified in Nightly?: yes
- Needs manual QE testing?: no
- Steps to reproduce for manual QE testing: n/a
- Risk associated with taking this patch: low
- Explanation of risk level: Avoids supply tricky value to GL driver. Already shipped to Release.
- String changes made/needed?: None
- Is Android affected?: unknown
| Reporter | ||
Comment 26•2 months ago
|
||
(In reply to Daniel Veditz [:dveditz] from comment #20)
(In reply to Lee Salzman [:lsalzman] from comment #5)
the driver itself is the source of the bug, not us - therefor sec-vector.
Although "sec-vector" conveys the blame for the bug, it doesn't usefully capture the risk to Firefox users on macOS who don't get a choice of what driver they use. For similar reasons we couldn't protect our users by blocklisting a buggy driver version like we can on other platforms, because that would kill major functionality for all Mac users. This bug should have been tagged sec-high in addition to the sec-vector.
Thankfully you were able to work around the driver's compliance issue. In cases where we can't we can add the
blocked-thirdpartykeyword so the nagbot and release managers know why there's a stale sec-high.
I'm a bit confused and hoping you can help me understand. It was tagged sec-vector (yes technically a driver bug reached through Firefox), but there was a fix on the Firefox side for the missing bounds check, and it's now rated sec-high as this is a path to privesc. Can you help me understand why this doesn't meet the bounty criteria?
Updated•2 months ago
|
Updated•2 months ago
|
Comment 27•2 months ago
|
||
| uplift | ||
Updated•2 months ago
|
Updated•2 months ago
|
Comment 28•2 months ago
|
||
| uplift | ||
Updated•1 month ago
|
Updated•1 month ago
|
Updated•8 days ago
|
Description
•