Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in PDFium
DescriptionInappropriate implementation in PDFium
ComponentPDFium
Bug ClassLogic Error
Tracker513337989
Fix commit159b66453572 (pdfium) +27/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Changed Functions

FunctionChangeNotes
TEST_F
core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
modified

Files Changed

  • core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
  • core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
From 159b664535720a9c466425c3a01de3947b154456 Mon Sep 17 00:00:00 2001
From: Tom Sepez <tsepez@google.com>
Date: Fri, 15 May 2026 12:11:16 -0700
Subject: [PATCH] Encode property names when generating a page.

Otherwise, the resulting document may not parse properly.

-- Gemini suggested patch and generated test.

Fixed: 513337989
Change-Id: Ieef3174f9a0cd53bb0953d431d19a209aa70cad2
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/147690
Reviewed-by: Lei Zhang <thestig@chromium.org>
Auto-Submit: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
---

diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
index aa3fede..cff86c7 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -621,7 +621,7 @@
         break;
       }
       case CPDF_ContentMarkItem::kPropertiesDict: {
-        *buf << "/" << item->GetPropertyName() << " ";
+        *buf << "/" << PDF_NameEncode(item->GetPropertyName()) << " ";
         break;
       }
       case CPDF_ContentMarkItem::kNone:
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
index c498bf5..6f4abd5 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
@@ -448,3 +448,29 @@
       "99999 4.6500001 2.98 3.4560001 .23999999 c 3.102 4.6700001 l h f Q\n",
       ByteString(process_buf));
 }
+
+TEST_F(CPDFPageContentGeneratorTest, ProcessContentMarksWithProperties) {
+  auto doc = std::make_unique<CPDF_TestDocument>();
+  doc->CreateNewDoc();
+
+  RetainPtr<CPDF_Dictionary> page_dict(doc->CreateNewPage(0));
+  auto test_page = pdfium::MakeRetain<CPDF_Page>(doc.get(), page_dict);
+
+  auto path_obj = std::make_unique<CPDF_PathObject>();
+  path_obj->set_filltype(CFX_FillRenderOptions::FillType::kWinding);
+  path_obj->path().AppendRect(0, 0, 10, 10);
+  path_obj->SetDirty(true);
+
+  auto marks_dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  path_obj->GetContentMarks()->AddMarkWithPropertiesHolder(
+      "M1", marks_dict, "Property Name With Space");
+
+  test_page->AppendPageObject(std::move(path_obj));
+
+  CPDF_PageContentGenerator generator(test_page.Get());
+  fxcrt::ostringstream buf;
+  EXPECT_TRUE(generator.ProcessPageObjects(&buf));
+  ByteString content(buf);
+
+  EXPECT_TRUE(content.Contains("/M1 /Property#20Name#20With#20Space BDC"));
+}
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
index c498bf5..6f4abd5 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
@@ -448,3 +448,29 @@
       "99999 4.6500001 2.98 3.4560001 .23999999 c 3.102 4.6700001 l h f Q\n",
       ByteString(process_buf));
 }
+
+TEST_F(CPDFPageContentGeneratorTest, ProcessContentMarksWithProperties) {
+  auto doc = std::make_unique<CPDF_TestDocument>();
+  doc->CreateNewDoc();
+
+  RetainPtr<CPDF_Dictionary> page_dict(doc->CreateNewPage(0));
+  auto test_page = pdfium::MakeRetain<CPDF_Page>(doc.get(), page_dict);
+
+  auto path_obj = std::make_unique<CPDF_PathObject>();
+  path_obj->set_filltype(CFX_FillRenderOptions::FillType::kWinding);
+  path_obj->path().AppendRect(0, 0, 10, 10);
+  path_obj->SetDirty(true);
+
+  auto marks_dict = pdfium::MakeRetain<CPDF_Dictionary>();
+  path_obj->GetContentMarks()->AddMarkWithPropertiesHolder(
+      "M1", marks_dict, "Property Name With Space");
+
+  test_page->AppendPageObject(std::move(path_obj));
+
+  CPDF_PageContentGenerator generator(test_page.Get());
+  fxcrt::ostringstream buf;
+  EXPECT_TRUE(generator.ProcessPageObjects(&buf));
+  ByteString content(buf);
+
+  EXPECT_TRUE(content.Contains("/M1 /Property#20Name#20With#20Space BDC"));
+}
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential PDF Operator Injection in CPDF_PageContentGenerator via Missing Name Encoding

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 https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: A vulnerability in PDFium’s content regeneration logic allows for potential arbitrary PDF operator injection into saved or printed documents. This occurs because property names in content marks are serialized without proper PDF Name encoding, failing to escape delimiter characters like spaces.

Affected files:

  • third_party/pdfium/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
  • third_party/pdfium/core/fpdfapi/page/cpdf_contentmarkitem.h
  • third_party/pdfium/core/fpdfapi/page/cpdf_streamcontentparser.cpp

Estimated timestamp from git blame: 2018-07-11

Description

A potential vulnerability exists in PDFium’s CPDF_PageContentGenerator::ProcessContentMarks where property names for kPropertiesDict marks are emitted into the regenerated content stream without being passed through PDF_NameEncode(). Because these names are decoded when the PDF is first parsed, characters that act as delimiters in PDF content streams (such as spaces) are written back in their raw form, allowing an attacker to ‘break out’ of a Name token and inject arbitrary PDF operators.

Root Cause Analysis

In third_party/pdfium/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp, the ProcessContentMarks function handles the serialization of content marks. For kPropertiesDict marks, the property name is written directly to the output buffer:

// third_party/pdfium/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp:623-625
case CPDF_ContentMarkItem::kPropertiesDict: {
  *buf << "/" << item->GetPropertyName() << " ";        // Missing encoding
  break;
}

item->GetPropertyName() returns a string that was decoded during initial parsing by PDF_NameDecode() in CPDF_StreamContentParser. For example, a Name token like /Tag#20Q#20EMC is stored internally as the string "Tag Q EMC". When serialized back without encoding, it produces /Tag Q EMC BDC, which a PDF parser interprets as a Name /Tag followed by the operators Q (Restore Graphics State) and EMC (End Marked Content).

This contrasts with other Name emissions in the same file (e.g., line 607) which correctly use PDF_NameEncode().

Potential Trigger Path

This issue is reachable through the Ink2 annotation feature in Chrome. Suggested steps to trigger the vulnerability include:

  1. Crafting the PDF: Create a PDF containing a path object marked with the GOOG:INKIsInker tag (identifying it as an Ink2 stroke) and a content mark using a hex-encoded property name containing spaces (e.g., /P#20Q#20EMC#20q).
  2. User Interaction: The user opens the PDF in Chrome and interacts with the Ink2 annotation tools, such as using the eraser to remove the marked path. This marks the associated content stream as ‘dirty’.
  3. Regeneration: Upon saving or printing the document, PDFiumEngine::RegenerateContents is called, which invokes CPDF_PageContentGenerator::GenerateContent to re-serialize the page’s objects.
  4. Injection: The serialized output contains the raw decoded characters from the property name, resulting in operator injection in the saved file.

While this does not immediately cause memory corruption in the renderer, it allows for document integrity attacks where the appearance and structure of the saved document are maliciously altered when viewed in any PDF reader.

Suggested Fix

Apply PDF_NameEncode() to the property name in CPDF_PageContentGenerator::ProcessContentMarks before writing it to the buffer:

case CPDF_ContentMarkItem::kPropertiesDict: {
  *buf << "/" << PDF_NameEncode(item->GetPropertyName()) << " ";
  break;
}

Evaluated with Chrome root at commit: b3153093eb3c78c3e88ccf562bcbc20437a04b0e


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

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