Medium watch ~ plausible CVSS 8.3 chrome Input Validation 🔧 Commit mapped

Overview

Medium
Severity
8.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactImproper input validation in Printing
DescriptionImproper input validation in Printing
ComponentPrinting
Bug ClassInput Validation
Action tierwatch — corruption primitive but gated (uncommon) — not default-reachable — factors set by independent review by AI
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Independent review by AIPlausible
The mapped commit may not contain the whole fix.
Reviewer notes: Mostly accurate. One function is misattributed to the paint_preview namespace. 2ded1aec alone sits behind an experimental flag. The fix spans 167ea6db, 2ded1aec and 619c0826 (the only commit with 'Fixed:'). The parser defect itself is not shown.
A second, independent AI reviewer (not a human) re-derived this analysis from the fixing commit and the vendor advisory, looking for errors.
Tracker497603247
Fix commit2ded1aec8f10 (chromium/src) +12/-2
Related commitse13ea4a61fc0 candidate · chromium/src
5139162fadb3 candidate · chromium/src
167ea6db8d29 previously mapped (not the fix) · chromium/src
619c08268865 candidate · chromium/src
CWECWE-20 (Improper input validation)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-03-30 (per vendor advisory)
Fix landed2026-09-11 (public git)
Disclosed2026-09-22
Patch gap11 days — fix public in source before the advisory

Background

`DeserializeOopTypeface`
the printing deserialization callback in printing/common/metafile_utils.cc that reconstructs an SkTypeface from an out-of-process serialized picture stream.
`SkTypeface::MakeDeserialize`
Skia’s legacy typeface deserializer that parses embedded font bytes from a stream using the platform font manager.
`skia::MakeTypefaceWithFontations`
a Skia-ext helper that deserializes an SkFontDescriptor and, when stream data is attached, instantiates the typeface strictly through the memory-safe Fontations engine.
`FontationsPrinting`
the Blink runtime-enabled feature that gates whether printing routes typeface instantiation through Fontations.

Root Cause Analysis

Verify before building a PoC

This analysis is consistent with the fixing commit but not fully provable from it: part of the vulnerable path lives in code the fix commit does not touch. Confirm the trigger and the sink against the source before relying on it for exploit development.
What the commit does not establish: Mostly accurate. One function is misattributed to the paint_preview namespace. 2ded1aec alone sits behind an experimental flag. The fix spans 167ea6db, 2ded1aec and 619c0826 (the only commit with 'Fixed:'). The parser defect itself is not shown.
Re-mapped on 2026-10-03: our pipeline had picked the earliest commit citing this bug (167ea6db8d29), which an audit found was a preparatory/helper change, not the fix. This page now points to the commit whose diff removes the bug.

In the pre-fix printing path, DeserializeOopTypeface reads a typeface id and a data_included flag from the serialized stream (metafile_utils.cc:504-511), and when data is present (metafile_utils.cc:521 DCHECK(data_included)) it calls SkTypeface::MakeDeserialize(&stream, skia::DefaultFontMgr()) at metafile_utils.cc:522-523 to parse the embedded font bytes. Those bytes are the typeface payload written by the serialize side (metafile_utils.cc:486-498), so DeserializeOopTypeface feeds an attacker-influenceable font stream directly into the legacy font parser, matching the advisory’s “improper input validation in Printing.”

The fix commit 2ded1aec replaces that single call with a branch: when blink::features::kFontationsPrinting is enabled it instantiates the typeface via skia::MakeTypefaceWithFontations(&stream), otherwise it falls back to SkTypeface::MakeDeserialize. MakeTypefaceWithFontations (added in skia/ext/font_utils.cc by 5139162/relanded by 167ea6db) deserializes an SkFontDescriptor and, if desc.hasStream(), parses the attached bytes only through SkTypeface_Make_Fontations(desc.detachStream(), desc.getFontArguments()), i.e. the memory-safe Fontations font engine rather than the platform parser. Because 2ded1aec keeps the SkTypeface::MakeDeserialize call in the else branch, its new code path runs only when FontationsPrinting is enabled, and at 2ded1aec that feature was still status: "experimental" (runtime_enabled_features.json5:3217-3218 at 619c08268865^), not on by default. Commit 619c08268865 (2026-09-15, “Enable the FontationsPrinting runtime enabled feature by default”) flips it to stable and carries the Fixed: 497603247 footer, so users were protected by default only from 619c08268865 onward. An earlier commit e13ea4a had instead wrapped MakeDeserialize with an OTS sanitizer argument &skia::SanitizeTypefaceStream, but the pre-fix source at 2ded1aec^ shows metafile_utils.cc:522-523 back to a plain MakeDeserialize call, so that OTS approach was not the surviving printing fix.

Key insight
The printing deserializer handed untrusted embedded font bytes straight to the legacy SkTypeface::MakeDeserialize parser; 2ded1aec reroutes that exact call through the memory-safe Fontations engine (skia::MakeTypefaceWithFontations) behind the FontationsPrinting flag, and the protection takes effect by default only when 619c08268865 enables that flag.

Attack Path

  1. Craft a malicious embedded font Produce a serialized OOP typeface stream whose data_included payload contains a malformed embedded font. (inferred)
  2. Reach the printing deserializer Have the stream deserialized by DeserializeOopTypeface, which at metafile_utils.cc:522-523 passes the bytes to SkTypeface::MakeDeserialize. (reachability of the untrusted producer is inferred)
  3. Mis-parse in the legacy font path The legacy deserializer parses the attacker-controlled font bytes with insufficient validation, the defect the advisory records. (inferred for the precise parser effect)
  4. Mitigation engaged Only with FontationsPrinting enabled (by default from 619c08268865; before that the else branch still calls SkTypeface::MakeDeserialize), the same bytes are instead parsed by the memory-safe Fontations engine via skia::MakeTypefaceWithFontations.

Impact Assessment

An attacker who controls the serialized typeface stream deserialized during printing can drive malformed embedded font bytes into the legacy font parser; the advisory classifies this as improper input validation in Printing at medium severity, not reported in the wild. The concrete memory-safety consequence of the mis-parse is not shown in the material and is (inferred). The remediation sends printing font parsing to the memory-safe Fontations engine only behind the FontationsPrinting flag, so builds between 2ded1aec and 619c08268865 (which turns the flag on by default) remained unprotected by default.

Files Changed

  • printing/DEPS
  • printing/common/BUILD.gn
  • printing/common/metafile_utils.cc
  • third_party/blink/public/common/BUILD.gn

Audit Directions

  • Untrusted font deserialization
    Flag any call to SkTypeface::MakeDeserialize on a stream that originates from another process and confirm it is routed through the memory-safe Fontations path.
  • Flag-gated security fixes
    When a hardening lands behind a runtime feature, verify the enabling commit (here 619c08268865, the one with the Fixed: footer) actually ships before treating the hole as closed, and date the fix from that commit.
  • Abandoned mitigations
    Check whether an earlier mitigation (e.g. the skia::SanitizeTypefaceStream OTS wrapper in e13ea4a) was reverted from the vulnerable path before relying on it.
diff --git a/printing/common/metafile_utils.cc b/printing/common/metafile_utils.cc
index 2921e53..bf618fa 100644
--- a/printing/common/metafile_utils.cc
+++ b/printing/common/metafile_utils.cc
@@ -12,6 +12,7 @@
 #include "base/compiler_specific.h"
 #include "base/containers/span.h"
 #include "base/containers/span_reader.h"
+#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/notreached.h"
 #include "base/strings/stringprintf.h"
@@ -22,6 +23,7 @@
 #include "skia/ext/codec_utils.h"
 #include "skia/ext/font_utils.h"
 #include "skia/ext/skia_utils_base.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkFontMgr.h"
 #include "third_party/skia/include/core/SkImage.h"
@@ -519,8 +521,13 @@
 
   // Typeface not encountered before, expect it to be present in the stream.
   DCHECK(data_included);
-  sk_sp<SkTypeface> typeface =
-      SkTypeface::MakeDeserialize(&stream, skia::DefaultFontMgr());
+  sk_sp<SkTypeface> typeface;
+  if (base::FeatureList::IsEnabled(blink::features::kFontationsPrinting)) {
+    typeface = skia::MakeTypefaceWithFontations(&stream);
+  } else {
+    typeface = SkTypeface::MakeDeserialize(&stream, skia::DefaultFontMgr());
+  }
+
   context->emplace(id, typeface);
   return typeface;
 }
diff --git a/printing/DEPS b/printing/DEPS
index a16613b..3b4727f6 100644
--- a/printing/DEPS
+++ b/printing/DEPS
@@ -5,6 +5,7 @@
   "+printing/printing_jni_headers",
   "+services/data_decoder/public/cpp",
   "+skia/ext",
+  "+third_party/blink/public/common/features.h",
   "+third_party/icu/source/common/unicode",
   "+third_party/icu/source/i18n/unicode",
   "+third_party/re2/src/re2",
diff --git a/printing/common/BUILD.gn b/printing/common/BUILD.gn
index f8f7793..3707ef02 100644
--- a/printing/common/BUILD.gn
+++ b/printing/common/BUILD.gn
@@ -19,5 +19,6 @@
   deps = [
     "//pdf:pdf_accessibility_constants",
     "//printing/buildflags",
+    "//third_party/blink/public/common",
   ]
 }
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index 9556300b..5a5d9a6 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -113,6 +113,7 @@
     "//content/*",
     "//fuchsia/*",
     "//pdf/*",
+    "//printing/*",
     "//services/*",
     "//storage/*",
     "//third_party/blink/*",
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.