High patch-now ✔︎ verified CVSS 8.8 chrome UAF 🔧 Commit mapped 🧬 A nested callout frees the object, then it is used

Overview

High
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in PDF
DescriptionUse after free in PDF
ComponentPDF
Sub-componentPDF
Bug ClassUAF
Action tierpatch-now — corruption primitive, web-reachable in default config (confirmed) — upheld 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 AIConfirmed
Reviewer notes: Claim sets reachability=file, but Chrome renders PDFs inline from http(s) URLs, so the crafted-PDF path is web-reachable, not only via local file open.
A second, independent AI reviewer (not a human) re-derived this analysis from the fixing commit and the vendor advisory, looking for errors.
Tracker558036280
Fix commit8ff0627e42eb (chromium/src) +2/-0
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedSeungMyung Lee (@sm1ee), Siung kim (@ksw9722)
Reported2026-09-06 (per vendor advisory)
Fix landed2026-09-11 (public git)
Disclosed2026-09-15
Patch gap4 days — fix public in source before the advisory

Background

`PDFiumPage::PopulateAnnotationLinks()`
A method in Chrome’s PDF plugin that walks a page’s link annotations to build the plugin’s list of clickable link targets.
`FPDF_PAGE` / `GetPage()`
FPDF_PAGE is PDFium’s opaque handle to a loaded page; GetPage() returns the currently loaded handle, which becomes dangling once the page is unloaded.
`ScopedPageUnloadPreventer`
An RAII guard that, while alive, pins a PDFiumPage as loaded so PDFium cannot unload (and free) the underlying page object.
Embedded-PDF JavaScript
Script embedded in a PDF (via actions/annotations) that PDFium can execute during document processing and that can re-enter the plugin and request a page unload.

Root Cause Analysis

PDFiumPage::PopulateAnnotationLinks() caches a raw FPDF_PAGE page = GetPage() and then iterates over link annotations with FPDFLink_Enumerate(page, &start_pos, &link_annot), resolving each via GetLinkTarget(). Per the commit, processing links during this loop can trigger JavaScript embedded in the PDF, which can re-enter the plugin and cause the page to be unloaded — freeing the object that page still points at. Because the cached page handle is used across every loop iteration, the next FPDFLink_Enumerate() call (and its downstream use) dereferences a freed page, a use-after-free. The invariant violated is that page must remain a valid, loaded handle for the entire duration of the enumeration loop.

The fix constructs a ScopedPageUnloadPreventer scoped_unload_preventer(this) immediately after obtaining page, so any unload attempt triggered by embedded JS is deferred until after the loop (and the guard’s scope) ends, keeping page valid throughout.

Key insight
The core mistake is holding a raw FPDF_PAGE handle across a loop whose body can synchronously run embedded JavaScript that unloads and frees that very page; the fix pins the page alive for the loop’s lifetime with a ScopedPageUnloadPreventer so re-entrant unloads cannot free the handle mid-iteration.

Attack Path

  1. Craft a malicious PDF Build a PDF with link annotations plus embedded JavaScript wired to run during annotation/link processing.
  2. Trigger annotation-link population Cause the PDF plugin to call PDFiumPage::PopulateAnnotationLinks(), which caches page = GetPage() and begins the FPDFLink_Enumerate() loop.
  3. Force a page unload from within the loop During link processing the embedded JS re-enters the plugin and requests unloading of the page, freeing the object that the cached page handle references.
  4. Reuse the freed handle The loop’s next FPDFLink_Enumerate() / GetLinkTarget() use dereferences the now-dangling page, producing the use-after-free.
  5. Exploit the freed allocation With control over reallocation of the freed page memory, an attacker may groom the heap to corrupt state and gain a read/write primitive or code execution in the PDF/renderer process (inferred).

Impact Assessment

An attacker who convinces a user to open a crafted PDF gains a use-after-free of a PDFium page object within the renderer/PDF process context, with the only precondition being that the PDF is loaded and its links populated. Successful heap grooming could turn the dangling handle into memory corruption and potentially remote code execution in the sandboxed renderer (inferred). The vendor advisory rates this high severity and does not report in-the-wild exploitation; NVD scores it CVSS 8.8.

Files Changed

  • pdf/pdfium/pdfium_page.cc

Audit Directions

  • Cached page/handle across re-entrant loops
    Audit any PDFium code that stores an FPDF_PAGE (or similar raw handle) from GetPage() and then loops calling PDFium APIs, since intervening embedded-JS execution can unload the page; require a ScopedPageUnloadPreventer (or equivalent) for the handle’s lifetime.
  • Annotation/link/form processing entry points
    Review sibling methods that iterate annotations, links, or form fields (e.g. other Populate*/Enumerate-driven loops) for the same missing unload guard.
  • JS-reentrancy boundaries
    Trace every plugin operation that can synchronously invoke embedded PDF JavaScript and verify no raw page/object pointer captured before the call is used after it without a lifetime guard.
From 8ff0627e42eba19d9b72207b0b22dcfc60f3860c Mon Sep 17 00:00:00 2001
From: Lei Zhang <thestig@chromium.org>
Date: Fri, 11 Sep 2026 10:06:41 -0700
Subject: [PATCH] [PDF] Prevent page unloading in PDFiumPage::PopulateAnnotationLinks()

PDFiumPage::PopulateAnnotationLinks() can trigger JS embedded in PDFs,
which can in turn trigger page unloads. Avoid this with a
ScopedPageUnloadPreventer.

Bug: 558036280
Change-Id: I8944a1ea5879029be30051edb1a80d1d557de3a1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8393364
Reviewed-by: Andy Phan <andyphan@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1696369}
---
diff --git a/pdf/pdfium/pdfium_page.cc b/pdf/pdfium/pdfium_page.cc
index 9543e49..7d8fd62 100644
--- a/pdf/pdfium/pdfium_page.cc
+++ b/pdf/pdfium/pdfium_page.cc
@@ -1544,6 +1544,8 @@
   int start_pos = 0;
   FPDF_LINK link_annot;
   FPDF_PAGE page = GetPage();
+  // Make sure `page` stays valid for the duration of the loop.
+  ScopedPageUnloadPreventer scoped_unload_preventer(this);
   while (FPDFLink_Enumerate(page, &start_pos, &link_annot)) {
     Link link;
     Area area = GetLinkTarget(link_annot, &link.target);
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.