Yaamlabs
Vulnerabilities

CoreGraphics zero-day: public PoC and a PDF font trail

Calif published a proof of concept for CVE-2026-86950: a crafted font in a PDF. How the bug works, why WhatsApp's checks point at PDFs, and how to scan files.

By Yaali. October 3, 2026, 6 min read, Vulnerabilities, Threat intel, Mobile.

Cover illustration of a smartphone with a document rising from its screen and an oversized curved glyph breaking through a pixel grid, with the Yaamlabs logo and the text: Booby-trapped PDF font behind Apple's zero-day, Sep 30, root cause and crash trigger made public

On September 30, researchers Dion Blazakis, Josh Maine and Anna Groza at the security firm Calif published a technical analysis and a working proof of concept (PoC) for CVE-2026-86950, the CoreGraphics zero-day Apple fixed on September 28. Their trigger is a PDF carrying a crafted TrueType font. Opened on an unpatched iPhone or Mac, it crashes the renderer with an out-of-bounds write, memory written past the end of the buffer the code was given. Calif also found that WhatsApp had quietly added checks for exactly this kind of font to its attachment scanner, which hints at how the real attack may have been delivered.

If your iPhones, iPads and Macs are already on iOS 26.7.1, iPadOS 26.7.1, macOS Tahoe 26.7.1, macOS Sequoia 15.8.1 or a 27 release, the device side is done; our earlier post on the patch covers versions, MDM rules and Lockdown Mode. What changed this week is that the bug is now documented in public, so the gap to a working exploit is shorter for anyone who wants one, and a defender can now check files for it. On October 2, Philippe Lagadec released a free scanner that looks for the malicious font inside PDFs.

How CVE-2026-86950 is reached: a PDF with an embedded TrueType font is drawn through CGContextDrawPDFPageWithOptions, op_Tj and ripc_DrawGlyphs into the glyph rasterizer, where coordinates are multiplied by 4096 and squeezed into a 32-bit integer, two functions handle the overflow differently, the buffer is sized too small and the drawing writes past its end

How it works

CoreGraphics draws text by turning each glyph, the outline of a letter in a font, into a pattern of pixels. To do that precisely it works in fixed-point numbers: each pixel is split into a 4096 by 4096 grid, so the inline function aa_double_to_fixed multiplies every floating-point coordinate by 4096 and stores the result in a signed 32-bit integer. A signed 32-bit integer tops out a little above 2.1 billion. Before the patch, nothing checked that the multiplied value fit.

Ordinary text never gets near that limit. Calif's font is built to. It uses the PDF text matrix, which scales and positions text on the page, together with composite glyphs, glyphs built from other glyphs that can each be scaled again, to push the final device coordinates far past the 32-bit range.

What turns an overflow into memory corruption is the compiler. Calif found two neighbouring functions in the rasterizer that handle the same out-of-range value differently. aa_moveto uses the ARM64 instruction FCVTZS on a 32-bit register, which saturates: a value that is too large is pinned to the maximum. aa_lineto converts to a 64-bit integer first and then narrows it with XTN, which simply drops the top bits, so a huge positive number can come out negative. The bounding box built from those mismatched points is far smaller than the shape really is, CoreGraphics allocates a coverage buffer to match the small box, and then writes outside it while filling in the glyph.

Calif describes the result as a controlled out-of-bounds 16-bit increment on two adjacent 16-bit values. The attacker also chooses the size of the allocation, and whether it lands on the heap or on the stack. Apple's fix adds bounds checks to aa_double_to_fixed, and because the function is inlined, the same pattern was patched more than 20 times across eight aa_* functions.

The path from a file to the bug runs through ordinary PDF drawing: CGContextDrawPDFPageWithOptions, the text-showing operator op_Tj, then ripc_DrawGlyphs, reaching the rasterizer through CGGlyphBitmapCreateWithPathAndDilation. Any app that renders a PDF page or its thumbnail with CoreGraphics walks that path. Calif says it used an AI agent to help build the crafted font, and the PoC repository includes the generation scripts.

What the WhatsApp changes suggest

Apple credited Meta Product Security with the report. Calif compared two WhatsApp builds, 26.37.73 and 26.38.74, and found that the newer one extends Kaleidoscope, WhatsApp's Rust-based set of checks that inspects attachments before the platform's own libraries touch them. Behind a new flag, ks_pdf_strict_validation_enabled, Kaleidoscope now parses the FontFile streams embedded in PDFs and can return three new defect tags: MalformedFontProgram, UndecodableFontProgram and UnverifiedFontProgram. Any of them gives the attachment a high risk score.

That is circumstantial. A messaging app adding font checks to its PDF scanner right as Meta reports a font bug in Apple's PDF renderer points at a booby-trapped PDF sent as an attachment, possibly needing no tap if a preview is drawn. Nobody has shown it. No in-the-wild sample has been published, WhatsApp has not tied its app to the attack, and whether other WhatsApp flaws were chained in is unknown.

The PoC stops at a crash. Turning a 16-bit increment into reliable code execution past Apple's memory protections is separate work that Calif did not publish. It does, however, give anyone attempting that work the trigger, the root cause and the affected functions.

What to do

Four steps after the CVE-2026-86950 PoC: confirm every Apple device is on a fixed build, scan stored and incoming PDFs with detect_cve_2026_86950.py, treat a hit on a file sent to a high-risk user as an incident, and keep WhatsApp updated, with the exit codes of the scanner

Close the remaining unpatched devices

Pull a list of Apple devices from your MDM and filter for anything below iOS or iPadOS 26.7.1, macOS Tahoe 26.7.1 or macOS Sequoia 15.8.1 that is not on a 27 release. Until September 30 the main risk was a private exploit used against chosen people. With the root cause public, chase the stragglers now rather than at the next monthly review, and remember an update only applies after the restart.

Scan PDFs for the malicious font

Lagadec's detect_cve_2026_86950.py is a static scanner. It never renders the file. It extracts every embedded font with pikepdf, parses it with fontTools, follows composite glyphs through their nested scaling, and works out whether the final coordinates, combined with the text sizes the PDF actually uses, would overflow the fixed-point conversion.

uv venv
uv pip install -r requirements.txt
python3 detect_cve_2026_86950.py -v suspicious.pdf
python3 detect_cve_2026_86950.py --json *.pdf

Exit code 0 means clean, 1 means a suspicious or exploit font, 2 means the file could not be analysed. That makes it easy to drop into a mail gateway hook, a sandbox step or a script that sweeps file shares. Lagadec reports no false positives on thousands of legitimate PDFs, but calls the tool new and heuristic, so a clean result only means no known pattern was found. He also notes that YARA rules are unlikely to catch these files, because there is no fixed byte pattern; the font has to be parsed.

Look back for signs of targeting

If you keep copies of attachments, from a secure email gateway or a mail archive, sweep PDFs received since early September by executives, legal, finance and anyone else your threat model singles out. A hit on a file that reached such a person is an incident: preserve the device before updating or wiping it, review that user's recent sign-ins and mailbox rules, and bring in a mobile forensics specialist. On the phone itself, keep WhatsApp current so its newer Kaleidoscope checks are in place.

The wider lesson

The fix for this bug is one bounds check, applied in more than 20 places because the compiler copied the function inline. That is why patch diffing worked so quickly here: two days after the update, an outside team had the root cause and a trigger. Expect the same for every exploited bug a vendor patches, and start the patch clock on the release date, because a public trigger can follow within days.

Our mobile penetration testing looks at how the apps your staff carry handle untrusted files, and our safeguarding and hardening work covers the MDM rules and mail filtering that close gaps like this one. Open the chat and Yaali, our AI agent, will pass your question to the engineer who would do the work.


Sources: Calif, The Great Glyph Grift, Calif PoC repository, The Hacker News, Security Affairs, Cyber Kendra, Privacy Needle, Philippe Lagadec, How to detect PDFs exploiting CVE-2026-86950, detect_CVE-2026-86950 on GitHub, Engineering at Meta, Rust at Scale, Help Net Security.

Read next

Back to the blog, or tell us about your system in the chat. Yaali, our AI agent, answers first and brings in an engineer.