Patch to Root CausePart 1 of 2
CVE-2025-24201 in 60 seconds
Safari runs website code inside a restricted Web Content process. CVE-2025-24201 was an
out-of-bounds write that Apple says could let malicious web content break out of that sandbox. The
public WebKit patch removes one unnecessary capability from that process: a page-controlled WebGL
context could ask the graphics layer to enable or disable PRIMITIVE_RESTART_FIXED_INDEX and WebKit
forwarded the request to ANGLE.
The public path is:
malicious web content
-> issue a WebGL state-change request
-> GraphicsContextGLANGLE forwards primitive-restart enable/disable
-> ANGLE / graphics state is changed from the Web Content process
-> Apple associates the bug with an out-of-bounds write and sandbox escape
The final arrow is an important evidence boundary. Apple’s advisory confirms the vulnerability class and impact, while the public patch confirms the capability that was removed. Neither public source reveals the complete memory-corruption primitive or the original exploit chain. This article does not pretend that twelve changed lines alone reconstruct those private details.
What are Web Content, WebGL, ANGLE, and primitive restart?
The Web Content process parses and executes untrusted page content under sandbox restrictions.
WebGL gives that content a browser-mediated graphics API. WebKit’s GraphicsContextGLANGLE is part
of the bridge to ANGLE, a graphics translation layer that ultimately manages lower-level GPU state.
Primitive restart is a rendering feature that lets a special index value end one geometry primitive and begin another without a separate draw call. WebGL 2 enables fixed-index primitive restart by default; WebGL 1 leaves it disabled. The WebKit commit says there was no use case for the Web Content process to toggle that state. A page could request a transition that the browser already knew should remain fixed for the context type.
What happened?
Apple’s Safari 18.3.1 advisory describes an out-of-bounds write fixed with improved checks and says malicious web content could break out of the Web Content sandbox. It calls the release a supplementary fix for an attack blocked in iOS 17.2 and notes a report of exploitation in an extremely sophisticated attack against specific targeted individuals on earlier iOS versions.
WebKit commit 7d784721e4 changes GraphicsContextGLANGLE::enable() and disable(). Both now
intercept PRIMITIVE_RESTART_FIXED_INDEX and return before calling ANGLE; invalid state changes also
record InvalidOperation. The patch removes the unnecessary state-changing route rather than trying
to sanitize a collection of known inputs after they reach the graphics layer.
Who was actually affected?
Safari 18.3.1 fixed the issue for Safari on macOS Ventura and Sonoma. Apple’s exploitation note refers specifically to targeted individuals on iOS versions before 17.2 and describes Safari 18.3.1 as a supplementary fix. Those statements should not be collapsed into “every vulnerable Safari user was attacked.” They establish a serious web-content entry point, a sandbox-escape impact, and evidence of targeted use; device-specific exposure still depends on the platform release and update history.
What I verified
- The commit changes one file and the two methods
GraphicsContextGLANGLE::enable()andGraphicsContextGLANGLE::disable(). - Both methods intercept
PRIMITIVE_RESTART_FIXED_INDEXbefore forwarding the request to ANGLE. - The patched branch returns immediately. On an invalid WebGL context it records
InvalidOperationinstead of changing graphics state. - I matched the WebKit commit and Bugzilla identifier to Apple’s Safari 18.3.1 advisory.
- This is a static patch reproduction. I verified the changed control flow, not the out-of-bounds write, the original exploit, or the reported targeted attacks.
Deep dive: the public trail is unusually complete
Apple’s advisory names WebKit Bugzilla issue 285858. The corresponding WebKit commit is
292004@main, represented in Git as 7d784721e4. Its subject is direct: a WebGL context
could toggle primitive-restart state from the WebContent process.
The change touches one file and two methods:
GraphicsContextGLANGLE::enableGraphicsContextGLANGLE::disable
Ten lines are added and two blank lines are removed. No allocator is replaced. No large parser is rewritten. The fix stops one capability from being passed to the graphics layer.
Primitive restart is not supposed to be a page-controlled switch
Primitive restart is a graphics feature used while drawing indexed geometry. A special index value ends the current primitive and starts another one without a separate draw call.
The important part of the commit message is not the rendering detail. It is the ownership of the state:
- WebGL 2 expects primitive restart to be enabled.
- WebGL 1 expects it to be disabled.
- Web content has no legitimate reason to flip that setting.
Before the patch, the general enable and disable paths could still forward this capability
to ANGLE. After the patch, WebKit handles the capability locally and returns before calling
the underlying graphics function.
In simplified pseudocode, the new behavior is:
when enable or disable receives PRIMITIVE_RESTART_FIXED_INDEX:
report an invalid operation when the requested state conflicts with the WebGL version
return without forwarding the request to ANGLE
otherwise:
continue with the normal graphics call
That is a small code change with a large architectural meaning: a component processing untrusted web content loses the ability to send a state transition that the graphics layer never needed to accept from it.
Reading the fix backwards
A patch is strongest when it lets us state a precise before-and-after condition.
Before: enable and disable were generic pass-through methods after making the graphics
context current. The primitive-restart capability could travel through the same path as
ordinary capabilities.
After: both methods recognize the primitive-restart capability, enforce the WebGL-version rule, and stop processing it locally.
The vulnerability-research lesson is not “one missing if caused a sandbox escape.” The
public evidence does not prove that. The stronger and more accurate lesson is:
A state-changing command controlled by web content crossed into a graphics subsystem even though the web-facing API had no valid reason to control that state.
That is the condition worth hunting elsewhere. Generic forwarding functions accumulate exceptions over time. Every exception is a clue that the public API and the lower-level API do not have identical authority.
What the patch proves — and what it does not
The public sources support four conclusions:
- Apple associated CVE-2025-24201 with an out-of-bounds write and possible Web Content sandbox escape.
- Apple linked the CVE to WebKit Bugzilla issue
285858. - The public fix for that issue blocks WebContent-controlled toggling of primitive restart.
- The affected code sits in WebKit’s ANGLE-backed graphics context.
They do not publish the original exploit, the corrupt object, the exact out-of-bounds write primitive, or the rest of the reported attack chain. The patch is a root-cause clue, not a complete exploit narrative.
This distinction matters. It would be easy to turn a twelve-line patch into an impressive but unsupported story about GPU compromise. The responsible analysis stops at the edge of the evidence.
A safe way to reproduce the analysis
This research can be repeated without running exploit code or targeting a device:
- Read Apple’s Safari 18.3.1 advisory and record the CVE, impact, affected version, and WebKit Bugzilla number.
- Resolve Bugzilla
285858to WebKit commit292004@main. - Inspect the commit and confirm that only
GraphicsContextGLANGLE.cppchanged. - Compare the old and new
enableanddisablepaths. - Confirm that the patched paths return before the underlying graphics call for
PRIMITIVE_RESTART_FIXED_INDEX. - Write down separately what the advisory proves, what the patch proves, and what neither source reveals.
This is patch analysis, not exploit validation. Reproducing the original vulnerability would require an authorized lab, affected builds, reliable crash evidence, and an independently verified impact demonstration.
The next variant-hunting question
The most useful follow-up is not to imitate the unavailable exploit. It is to search WebKit
for other generic enable, disable, or state-forwarding paths where:
- web content can select a low-level capability;
- the higher-level web standard fixes that capability to one state;
- the graphics API exposes more authority than the web API requires; and
- the message crosses into a more privileged or separately isolated component.
The candidate pattern is simple: a public API can ask for more state change than its own contract permits. Candidates found by that search remain hypotheses until tested on an affected build with a safe negative control.
Evidence matrix
| Signal | What it proves | What it does not prove | My check |
|---|---|---|---|
| Apple advisory links CVE-2025-24201 to Bugzilla 285858 | The vendor associates the public issue and fix record | The exact exploit path | I matched the identifiers across Apple’s advisory and WebKit history |
| Commit changes only the ANGLE graphics context | The public fix is narrowly scoped | That no private fixes existed elsewhere | I inspected the complete public commit and its parent |
| New branches return before the graphics call | Primitive-restart toggles stop at WebKit | The exact memory corruption mechanism | I traced both patched control-flow paths |
| Apple describes a supplementary fix | The update strengthened an earlier blocked attack path | Which earlier mitigation blocked it | I kept the earlier attack chain explicitly unknown |
My conclusion
For users and defenders, the action is straightforward: run a Safari, iOS, iPadOS, macOS, visionOS, or watchOS version containing Apple’s fix. Patch analysis does not replace the vendor update.
My conclusion is that the important change is not its line count but the authority it removes. The patch deletes no capability a WebGL page needs; it prevents untrusted content from toggling a lower-level graphics state that the web-facing contract already fixes to one value.
Public sources
Evidence used for this analysis
Version and exploitation status can change; follow the linked vendor records.
The author completed a technical review. This does not, by itself, claim lab reproduction.
