Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in PDFium
DescriptionUse after free in PDFium
ComponentPDFium
Bug ClassUAF
Tracker504418475
Fix commite14569d5800f (pdfium) +4/-9
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Changed Functions

FunctionChangeNotes
if
fpdfsdk/formfiller/cffl_checkbox.cpp
modified

Files Changed

  • fpdfsdk/formfiller/cffl_checkbox.cpp
From e14569d5800ff6728c21a48c87271fb69dce8278 Mon Sep 17 00:00:00 2001
From: Tom Sepez <[email protected]>
Date: Fri, 24 Apr 2026 12:01:59 -0700
Subject: [PATCH] Fix potential UAF in CFFL_CheckBox::OnChar().

AI suggested fix for AI detected issue.

Chrome is not affected, nor it it likely that other embedders are
affected, either.

Take the patch anyways as it makes the logic more correct.

Bug: 504418475
Change-Id: I3a1936f7965ca128701f55ddff445c063df4d3c7
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/146650
Reviewed-by: Lei Zhang <[email protected]>
Commit-Queue: Tom Sepez <[email protected]>
---

diff --git a/fpdfsdk/formfiller/cffl_checkbox.cpp b/fpdfsdk/formfiller/cffl_checkbox.cpp
index 03453a3..0fd3857 100644
--- a/fpdfsdk/formfiller/cffl_checkbox.cpp
+++ b/fpdfsdk/formfiller/cffl_checkbox.cpp
@@ -51,15 +51,10 @@
       CPDFSDK_PageView* pPageView = pWidget->GetPageView();
       DCHECK(pPageView);
 
-      ObservedPtr<CPDFSDK_Widget> pObserved(widget_);
-      if (form_filler_->OnButtonUp(pObserved, pPageView, nFlags)) {
-        if (!pObserved) {
-          widget_ = nullptr;
-        }
-        return true;
-      }
-      if (!pObserved) {
-        widget_ = nullptr;
+      // If OnButtonUp() destroys `observed` (the owning CPDFSDK_Widget), its
+      // destructor has already destroyed `this`.
+      ObservedPtr<CPDFSDK_Widget> observed(widget_);
+      if (form_filler_->OnButtonUp(observed, pPageView, nFlags) || !observed) {
         return true;
       }
 
Loading diff…

Original Bug Report

reported by [email protected]

Potential Use-After-Free write in CFFL_CheckBox::OnChar due to synchronous widget destruction

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the Chrome Security team. Please see go/chrome-ai-generated-security-bugs-faq for more information.

Overview: If XFA support is enabled, JavaScript execution during a CheckBox’s OnChar event can synchronously destroy the widget. This leads to the CFFL_CheckBox object being freed while still executing its method, resulting in a Use-After-Free (UAF) write when it attempts to set a raw_ptr member to nullptr.

Affected files:

  • third_party/pdfium/fpdfsdk/formfiller/cffl_checkbox.cpp

Estimated timestamp from git blame: 2025-04-09

Technical Details

A potential Use-After-Free (UAF) vulnerability exists in PDFium’s CFFL_CheckBox::OnChar event handler. When a user interacts with a CheckBox (e.g., by pressing Return or Space), the OnChar event is triggered. This handler temporarily wraps the CPDFSDK_Widget in an ObservedPtr and calls form_filler_->OnButtonUp, which can execute associated JavaScript actions.

In configurations where XFA is supported and enabled (via the PdfXfaSupport feature flag), the OnButtonUp event can trigger scripts that synchronously modify the document’s structure (e.g., xfa.form.remerge() or instanceManager.removeInstance()). These XFA actions can cause layout updates that remove the current page, leading to the synchronous destruction of the CPDFSDK_Widget object.

The destruction of the CPDFSDK_Widget unregisters it from the CFFL_InteractiveFormFiller, which subsequently destroys the CFFL_FormField (the CFFL_CheckBox instance) that is currently processing the OnChar event.

When execution returns from the JavaScript callback to CFFL_CheckBox::OnChar, the this pointer is stale. The code checks the ObservedPtr and, noticing the widget was destroyed, attempts to clear its internal widget_ member:

// third_party/pdfium/fpdfsdk/formfiller/cffl_checkbox.cpp
bool CFFL_CheckBox::OnChar(CPDFSDK_Widget* pWidget, ...) {
  // ...
  ObservedPtr<CPDFSDK_Widget> pObserved(widget_);
  if (form_filler_->OnButtonUp(pObserved, pPageView, nFlags)) {
    if (!pObserved) {
      widget_ = nullptr;   // UAF write on |this|
    }
    return true;
  }
  // ...
}

Because CFFL_CheckBox itself has already been freed and is not protected by MiraclePtr (the caller holds a raw CFFL_FormField*, not a raw_ptr), this assignment results in a UAF write on this.

Impact

This is a heap-use-after-free write in the sandboxed PDF renderer process. While the write value is fixed (nullptr), widget_ is typed as an UnownedPtr (which is an alias for base::raw_ptr in PartitionAlloc builds).

If an attacker performs a heap spray during the JavaScript execution, they can reclaim the freed CFFL_CheckBox memory and place a controlled pointer value at the widget_ offset. When widget_ = nullptr executes, the raw_ptr assignment operator reads the attacker-controlled “old pointer” value from memory and decrements its BackupRefPtr (BRP) reference count. This provides the attacker with a powerful arbitrary BRP-decrement primitive, which can be leveraged to defeat MiraclePtr protections and exploit other UAF vulnerabilities for Remote Code Execution (RCE).

Note: This specific exploit path requires PdfXfaSupport to be enabled, which is a non-default feature in Chrome.

Suggested Steps to Reproduce

Note: These are potential steps as our tooling does not run live code.

  1. Enable the PdfXfaSupport feature flag.
  2. Craft a malicious PDF containing a CheckBox widget and XFA elements.
  3. Embed XFA JavaScript in the CheckBox’s OnButtonUp event that calls xfa.form.remerge() or a similar function to synchronously destroy the widget.
  4. Follow the destruction with a heap spray to allocate controlled objects over the freed CFFL_CheckBox memory.
  5. Trigger the OnChar event by pressing Space or Return while focused on the CheckBox.

Suggested Fix

To prevent the UAF, ensure that the caller holding the CFFL_FormField does so safely. Using an ObservedPtr or raw_ptr for the CFFL_FormField object itself (not just the widget_) in CFFL_InteractiveFormFiller::OnChar before invoking the virtual call would either prevent the UAF or allow BRP to quarantine the allocation safely.

Evaluated with Chrome root at commit: 7353d249d9cacf9c7218e1d7b8a39cf39c72d646


Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker