Repository navigation
GlyphTypeface.ComputeSubset rejects stock Windows fonts (Calibri, Cambria, Constantia, Corbel) after TtfDelta bounds-check hardening #11834
Description
Description
Since the August 2026 servicing update, GlyphTypeface.ComputeSubset throws FileFormatException for several stock, unmodified Windows system fonts. This breaks all XPS export, XPS-based PDF export and printing for documents that use them.
The fonts are structurally valid — every table offset is in bounds and all checksums are correct. They render perfectly on screen. Only subsetting fails, which makes the failure easy to misdiagnose, since the exception message names the font file and implies it is corrupt.
The trigger is the legacy kern table: in these fonts the subtable's uint16 length field has wrapped, and the new bounds validation rejects the entire font rather than skipping the offending optional table.
Affected: Calibri, Cambria, Constantia and Corbel — 15 faces in total. Calibri is a Microsoft default font, so a stock Windows install plus FontFamily="Calibri" is sufficient to hit this.
Reproduction Steps
Console app, <UseWPF>true</UseWPF>, x64, on Windows with stock Calibri installed:
using System.Reflection;
using System.Windows.Media;
var m = typeof(GlyphTypeface).GetMethod("ComputeSubset",
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
var gt = new GlyphTypeface(new Uri("file:///C:/Windows/Fonts/calibrib.ttf"));
var glyphs = new List<ushort>();
foreach (var c in "Hello world 0123456789")
if (gt.CharacterToGlyphMap.TryGetValue(c, out var g) && !glyphs.Contains(g))
glyphs.Add(g);
var bytes = (byte[])m.Invoke(gt, new object[] { glyphs }); // throwsSwap calibrib.ttf for calibril.ttf (Calibri Light) and it succeeds — same machine, same process, same code.
The public path reaches the same code: build a FlowDocument with FontFamily="Calibri", then XpsDocument.CreateXpsDocumentWriter(doc).Write(paginator).
Expected behavior
Stock Windows system fonts subset successfully, producing a valid XPS/PDF document, as they did before the update.
Actual behavior
System.IO.FileFormatException: 'file:///C:/WINDOWS/FONTS/CALIBRIB.TTF' file does not conform to the expected file format specification.
No document is produced — the whole serialization call aborts, with no partial output and no font fallback.
at MS.Internal.TrueTypeSubsetter.ComputeSubset(Void* fontData, Int32 fileSize, Uri sourceUri, Int32 directoryOffset, UInt16[] glyphArray)
at MS.Internal.FontFace.TrueTypeFontDriver.ComputeFontSubset(ICollection`1 glyphs)
at System.Windows.Media.GlyphTypeface.ComputeSubset(ICollection`1 glyphs)
at System.Windows.Xps.Serialization.FEMCacheItem.SubSetFont(ICollection`1 glyphs, Stream stream)
at System.Windows.Xps.Serialization.FEMCacheItem.Commit()
at System.Windows.Xps.Serialization.XpsFontSubsetter.CommitFontSubsetsSignal(FontSubsetterCommitPolicies signal)
at System.Windows.Xps.Serialization.XpsSerializationManager.ReleaseXmlWriter(Type writerType)
at System.Windows.Xps.Serialization.ReachHierarchySimulator.SimulateEndFixedDocumentSequence(XmlWriter xmlWriter)
at System.Windows.Xps.Serialization.XpsSerializationManager.SaveAsXaml(Object serializedObject)
On .NET 8/9/10 the message wording differs slightly: File 'file:///C:/Windows/Fonts/calibrib.ttf' has an invalid file format.
Regression?
Yes. Verified by A/B on one machine — same font, same test code, swapping only PresentationCore.dll via DEVPATH:
PresentationCore.dll |
Origin | Result |
|---|---|---|
| 4.8.9032.0 (built 2024-04-01) | pre-update, from WinSxS | OK — 499,120-byte subset |
| 4.8.9344.0 (built 2026-07-08) | KB5120708, installed 2026-08-11 | FAIL |
Our production failure occurred 2026-08-12, the day after KB5120708 installed.
The affected fonts have shipped with an identically sized kern table across Calibri 6.24, 6.26 and 6.27 — all three fail now and all worked before — so the fonts did not change. Only the subsetter's tolerance did.
I do not have an unpatched .NET Core runtime installed to bisect that side, but given the shared TtfDelta sources I would expect 8.0.29 / 9.0.18 / 10.0.10 to succeed.
Known Workarounds
None that are both correct and cost-free.
- Strip the
kerntable from the font and load the repaired copy from a private font folder. Verified to fix all 15 faces with no visual or metric change. Requires redistributing a modified Microsoft font binary. - Patch the fonts in
C:\Windows\Fontsin place. Needstakeown/icacls, a FontCache service stop and a reboot, and is undone by any future font-carrying Windows Update,sfc /scannowor DISM repair. - Use an unaffected font — Calibri Light, Candara, Arial, Georgia and Consolas all subset fine. Changes document appearance.
- Disable subsetting via
XpsSerializationManager.SetFontSubsettingPolicy(FontSubsetterCommitPolicies.None), embedding the whole font instead. Avoids the crash but replaces a ~500 KB subset with ~1.6 MB of font data per document, which was unacceptable for us.
The intended opt-out switch does not work — details under Other information.
Rolling back the update is not a workaround, as it re-opens CVE-2026-62886 / CVE-2026-62897.
Impact
Radius — any WPF app that subsets fonts: XPS export, XPS-based PDF export, and printing. Triggered by 15 stock faces: Calibri, Cambria, Constantia and Corbel (Regular/Bold/Italic/BoldItalic each, except Cambria which has no affected Regular). No third-party or exotic font is needed.
Intensity — total functional failure rather than degradation. Screen rendering is unaffected, so the app looks healthy right up until export.
Reach — every machine that has taken the August 2026 security update. Because it is security servicing, it deploys broadly and automatically and cannot reasonably be declined.
Concrete example — our production system issues official education diplomas. Diploma generation stopped completely on 2026-08-12, mid-season, with no way for affected students to obtain their documents until we ship a workaround.
Configuration
Reproduces on:
- .NET Framework 4.8.1 —
PresentationCore.dll4.8.9344.0 (NET481REL1LAST_25H2_B), installed via KB5120708 on 2026-08-11 - .NET 8.0.30, 9.0.19, 10.0.11 (
Microsoft.WindowsDesktop.App)
Does not reproduce on: .NET Framework 4.8.1 with PresentationCore.dll 4.8.9032.0.
OS: Windows 11 Pro 25H2, build 10.0.26200.9168
Architecture: x64. The 32-bit PresentationCore.dll contains the same code; I tested x64 only.
Specific to this configuration? No — it depends on the font file, not the machine, OS version or architecture. Font under test: Calibri Bold 6.27, C:\Windows\Fonts\calibrib.ttf, 1,615,616 bytes.
Other information
Where the problem is
src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ — the TTF_SAFE_CHECKS_ENABLED() validation added by #11642 ("Fix potential overflow issues in WPF native code", merged 2026-05-18), which rewrote 13 files in that directory. Most likely ttfacc.cpp, ttftable.cpp or modtable.cpp, where table lengths are read and bounds-checked.
Why these fonts trip it
The legacy kern subtable header stores its own length in a uint16 (max 65,535), while the table directory stores the real length in a uint32. For calibrib.ttf:
| Field | Value |
|---|---|
kern length from table directory (uint32) |
160,164 |
Subtable header length at kern+6 (uint16) |
29,088 |
160,164 mod 65,536 = 29,088 — the field has wrapped. This is a decades-old limitation of the kern format, and these fonts have always exceeded it. DirectWrite ignores the table entirely and applies kerning from GPOS, so nobody noticed until the subsetter started validating the field.
Measured across 25 fonts, the correlation is perfect — 15/15 fonts whose kern table exceeds 65,535 bytes fail; 10/10 that don't, succeed:
| Font | kern bytes |
uint16 field | Subsets? |
|---|---|---|---|
| calibri / calibrib / calibrii / calibriz | 149,796–160,254 | wrapped | fail |
| cambriab / cambriai / cambriaz | 190,000–218,000 | wrapped | fail |
| constan / constanb / constani / constanz | 219,000–232,000 | wrapped | fail |
| corbel / corbelb / corbeli / corbelz | 80,000–100,000 | wrapped | fail |
| calibril / calibrili | 12,288 / 12,300 | valid | ok |
| corbell / corbelli | ~41,000 | valid | ok |
| Candara | 48,792 | valid | ok |
| arial / times / verdana | 3,000–5,000 | valid | ok |
| consola / georgia | no kern |
— | ok |
Suggested fix
An inconsistent legacy kern subtable should cause that table to be skipped or truncated, not the whole font to be rejected. It is optional and unused by the WPF/DirectWrite text stack. I verified that stripping only kern restores subsetting for every affected face with no visual or metric change and zero missing characters (including Danish æøåÆØÅ): calibri 520,552 B, calibrib 513,800 B, calibrii 256,064 B, calibriz 255,208 B, corbel 81,332 B, constan 118,996 B, cambriab 241,084 B.
Second bug: the TtfDelta AppContext switch is not wired up
Switch.MS.Internal.TtfDelta.DisableDirectWriteForwarderBoundsCheckProtection is present as a UTF-16 literal in the shipped binary, and MS.Internal.CoreAppContextSwitches.DisableDirectWriteForwarderBoundsCheckProtection honours it — but enabling it does not change behaviour. Tested on .NET Framework 4.8.9344.0 and .NET 8.0.30:
| Configuration | AppContext.TryGetSwitch |
CoreAppContextSwitches |
Result |
|---|---|---|---|
AppContext.SetSwitch(name, true) |
True | — | FAIL |
app.config AppContextSwitchOverrides |
True | — | FAIL |
app.config + MediaSystem booted via RenderTargetBitmap |
True | True | FAIL |
| All three bounds-check switches enabled | True | True | FAIL |
Reflecting over PresentationCore shows managed setters exist for the sibling mitigations but not for this one:
MS.Win32.Penimc.UnsafeNativeMethods.SetDisablePenImcBoundsCheckProtection(Boolean)— presentMS.Win32.PresentationCore.UnsafeNativeMethods+MilCoreApi.WpfGfx_SetDisableBoundsCheckProtection(Boolean)— present- TtfDelta equivalent — no managed method, though the native symbol
SetDisableDirectWriteForwarderBoundsCheckProtectionand the globalg_fDWFBoundsCheckEnabledare both present in the binary.
In #11642 the .NET Core truetype.cpp reads the switch itself via System::AppContext::TryGetSwitch in a static-once block at the top of ComputeSubset; that path appears not to take effect in the shipped builds. Please wire this up — it is the documented escape hatch, and currently the only mitigation that would not require modifying Microsoft's font binaries.
Related
- Fix potential overflow issues in WPF native code #11642 — "Fix potential overflow issues in WPF native code"
- Microsoft Security Advisory CVE-2026-62886 | .NET Elevation of Privilege Vulnerability #11826 (CVE-2026-62886) and Microsoft Security Advisory CVE-2026-62897 | .NET Remote Code Execution Vulnerability #11830 (CVE-2026-62897) — the CWE-190 advisories shipped with this change
Activity
JeppeJepsen commentedon Aug 12, 2026
Also filed against .NET Framework 4.8.1, since this repo does not service it and a fix shipped in
Microsoft.WindowsDesktop.Appwill not reach .NET Framework applications: https://developercommunity.visualstudio.com/t/KB5120708:-GlyphTypefaceComputeSubset-r/11134941The .NET Framework build is affected identically.
PresentationCore.dll4.8.9344.0, shipped in KB5120708 on 2026-08-11, fails.PresentationCore.dll4.8.9032.0 succeeds on the same machine with the same font and the same code (DEVPATH A/B).Since the two implementations are forks of the same TtfDelta sources, both will need the fix.
dfergcsd commentedon Aug 13, 2026
Independent reproduction, and two data points that may help — one correcting the affected-font list, one filling in the .NET Core bisect you weren't able to run.
Environment: Windows 11 Pro 26200, .NET Framework 4.8.1
PresentationCore.dll4.8.9344.0 (KB5120708, installed 2026-08-12), plusMicrosoft.WindowsDesktop.App8.0.30 / 9.0.19 / 10.0.11. Hit in production the following day — PDF export and printing from a structural engineering app, sameSaveAsXamlstack.Scanning all 193 installed font families, exactly five fail: Calibri, Cambria, Cambria Math, Constantia, Corbel. Your
kerncorrelation holds on every one I measured.Cambria Regular and Cambria Math are affected
The table lists
cambriab/cambriai/cambriazand notes Cambria has no affected Regular, but Cambria Regular does not ship ascambria.ttfon this machine — it iscambria.ttc, and the collection carries a wrappedkernshared by both faces:kern(uint32)cambria.ttcface 0 — Cambriacambria.ttcface 1 — Cambria MathBoth fail
SaveAsXamlat regular weight, so the affected count is higher than 15 once the collection is counted. Worth checking whether.ttchandling needs its own coverage in the fix..NET Core bisect
You expected 8.0.29 / 9.0.18 / 10.0.10 to succeed but had no unpatched runtime to test. I can't confirm those exact patches, but the major-version split is unambiguous — same binary, same fonts, same machine, only
Microsoft.WindowsDesktop.Approlled viaruntimeconfig.json:wpfgfx_cor3.dllbuilt6 and 7 are out of support and never received the change, which matches your
PresentationCoreA/B on the .NET Framework side and confirms both forks are affected.On workaround 4
Disabling subsetting was unusable for you at ~1.6 MB per document, but it becomes practical when applied only on failure rather than globally: catch the
FileFormatException, checkSourceUrinames a font, and re-serialize that one document withFontSubsetterCommitPolicies.None. Unaffected documents keep their normal subset output and pay nothing, and it self-heals once this is fixed — no font list to maintain.One trap worth flagging for anyone taking that route to a printer rather than a file:
PrintQueue.AddJob(..., fastCopy: false)re-validates the package and fails again on the embedded font, now reported against the obfuscated resource rather than the original:fastCopy: truestreams the package to the spooler untouched and succeeds. Same root cause, second hop.Also confirming your finding that the
AppContextswitch has no effect — no managed setter for the TtfDelta equivalent, so there is no configuration-only mitigation.ToreDemant commentedon Aug 18, 2026
Could this have been introduced with #11835
miloush commentedon Aug 23, 2026
To be fair the fonts should be fixed. EDIT: since Windows supports only one subtable of format 0, there isn't anything the fonts can do.
himgoyalmicro commentedon Aug 26, 2026
We are investigating the issue and working on a fix.