Oct 23, 2026·5 min read·3 visits
pypdf prior to version 6.21.0 suffers from quadratic O(N^2) complexity in form field cycle detection and lacks traversal depth limits, enabling denial-of-service via malformed PDFs.
An uncontrolled resource consumption and inefficient algorithmic complexity vulnerability in pypdf prior to version 6.21.0 allows remote attackers to trigger application-level denial-of-service (DoS) via crafted PDF form field hierarchies.
The Python library pypdf (formerly PyPDF2) is an open-source tool widely utilized for extracting text, merging pages, and parsing document structures from PDF files. Within its interactive form field processing module, pypdf provides methods such as get_fields(), _build_field(), and _check_kids() to traverse hierarchical field structures and extract form field metadata.
When processing untrusted PDF documents containing complex or synthetic interactive form fields (/AcroForm and /Fields trees), versions of pypdf prior to 6.21.0 exhibit critical efficiency bottlenecks. Specifically, cycle detection during recursive tree traversal was implemented using sequential array lookups over a growing list of traversed nodes, leading to quadratic time complexity ($O(N^2)$).
In addition to the time complexity flaw, the parser lacked upper boundaries for recursion depth and total processed form field entries. Consequently, an unauthenticated attacker capable of submitting a tailored PDF file to a service using pypdf can cause severe CPU starvation and memory exhaustion, resulting in thread blockages or process failure.
The root cause of CVE-2026-113789 consists of two interrelated defects located in pypdf/_doc_common.py: linear cycle detection lookup (CWE-407) and missing input structural boundaries (CWE-400).
During recursive traversal of field hierarchies, child objects referenced in /Kids arrays are checked for cyclical references to prevent infinite recursive loops. In affected versions, this check relied on a Python list passed via the stack parameter:
if tree in stack:
return
stack.append(tree)In Python, evaluating tree in stack on a list object forces a linear scan ($O(N)$) through every previously visited node. As the total number of field nodes $N$ increases (for instance, in PDFs containing 20,000 or more widget annotations), each node lookup takes time proportional to $N$. The cumulative complexity of traversing the tree scales quadratically ($O(N^2)$), consuming CPU time exponentially relative to element count.
Furthermore, the parser contained no guardrails regarding maximum traversal depth or total total node counts. Because PDF object dictionaries can contain deeply nested child references, an attacker can construct a tree structure that consumes hundreds of megabytes of memory and holds CPU execution in tight loop iterations for tens of seconds per document.
The vulnerability was remediated in commit 5ef728f0e16d225c853e89f861ba0a67402fe3f2 (PR #4219) and released in pypdf version 6.21.0.
The fix introduces two major modifications: converting the identity tracking structure to an $O(1)$ hash set and establishing runtime execution limits in Configuration (pypdf/_configuration.py).
# File: pypdf/_configuration.py
@@ -106,6 +106,11 @@ class Configuration:
page_tree_maximum_depth: int = 100
"""The maximum allowed depth of the page tree."""
+ form_fields_maximum_entries: int = 100_000
+ """The maximum allowed number of form fields."""
+ form_fields_maximum_depth: int = 100
+ """The maximum allowed depth for form fields."""
+
xform_maximum_invocations_per_extraction: int = 5_000In pypdf/_doc_common.py, the cycle detection algorithm was rewritten to use Python object memory identifiers (id(tree)) stored within a hash set visited: set[int]:
# File: pypdf/_doc_common.py
@@ -789,17 +828,26 @@ def _check_kids(
visited: set[int],
depth: int,
traversal_state: _TraversalState,
) -> None:
- if tree in stack:
+ tree_id = id(tree)
+ if tree_id in visited:
logger_warning(
"%(field_name)s already parsed",
source=__name__,
field_name=self._get_qualified_field_name(parent=tree),
)
return
- stack.append(tree)
+ visited.add(tree_id)By replacing tree in stack (list membership, $O(N)$) with tree_id in visited (set hash lookup, $O(1)$), average iteration complexity drops from $O(N^2)$ to linear $O(N)$. Furthermore, traversal functions now evaluate _depth and _traversal_state.entry_count against configurable limits, throwing LimitReachedError when boundaries are breached.
Exploitation of CVE-2026-113789 relies on delivery of a specially crafted PDF document to any application endpoint that automatically calls reader.get_fields() or writer.get_fields() on uploaded or ingested files.
The attack vector requires no authentication or user privilege on the target platform. The payload structure consists of a standard PDF document header containing a valid interactive form dictionary (/AcroForm) with an extensive /Fields array or deeply chained child dictionaries (/Kids).
When get_fields() processes a synthetic PDF file containing 30,000 dummy form fields, an unpatched system spends seconds in loop operations performing identity comparisons. Multiple concurrent uploads of such files will rapidly deplete worker process pools in web applications (such as Gunicorn or Uvicorn workers), causing full HTTP service unresponsiveness.
The primary impact of CVE-2026-113789 is high availability impairment (Denial of Service). The vulnerability has no impact on system confidentiality or data integrity.
According to CVSS v4.0 metrics, the issue receives a base score of 6.0 (CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N). The requirement metric AT:P reflects the necessity of triggering form field extraction methods specifically within downstream application logic.
Systems most exposed to this vulnerability include centralized document processing platforms, automated invoice parser services, cloud storage indexing agents, and compliance screening gateways that automatically analyze incoming PDF uploads. Because processing occurs synchronously on service worker threads, triggering the bug can easily result in complete service outage across multi-tenant environments.
To fully remediate CVE-2026-113789, organizations must update pypdf to version 6.21.0 or later.
pip install --upgrade "pypdf>=6.21.0"In environments handling high-risk untrusted documents where immediate package upgrades are delayed, applications can enforce strict runtime limits using the newly exposed apply_configuration context manager:
from pypdf import PdfReader, apply_configuration
from pypdf.errors import LimitReachedError
reader = PdfReader("untrusted_input.pdf")
try:
with apply_configuration(form_fields_maximum_entries=5000, form_fields_maximum_depth=20):
fields = reader.get_fields()
except LimitReachedError as err:
# Gracefully reject malicious or over-complex PDF files
logger.warning(f"Rejected complex PDF form structure: {err}")Additionally, applications parsing user-provided PDF files should wrap PDF extraction tasks in isolated worker subprocesses with strict timeout mechanisms and memory ceilings using system-level tools (e.g., cgroups or systemd resource limits).
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N| Attribute | Detail |
|---|---|
| CWE ID | CWE-400 / CWE-407 |
| Attack Vector | Network (Unauthenticated PDF Submission) |
| CVSS v4.0 Score | 6.0 (Medium) |
| EPSS Score | 0.00317 (23.44th percentile) |
| Impact | Denial of Service (CPU & Memory Exhaustion) |
| Exploit Status | No public weaponized exploit detected |
| CISA KEV Status | Not listed |
Nuxt versions 3.11.0 through 3.21.3 and 4.0.0 through 4.4.3 contain an interpretation conflict between the route rules matching engine and the core page routing mechanism. By submitting percent-encoded request path segments, unauthenticated attackers can bypass route rules, including appMiddleware authorization guards, and access rendered server-side page payloads.
A Cross-Site Scripting (XSS) vulnerability exists in Nuxt when configured with pages disabled (pages: false). ASCII C0 control characters prefixing javascript: URIs bypass internal protocol classification in the fallback router, allowing unauthenticated attackers to execute arbitrary script in the victim's origin context upon user interaction.
A Stored Cross-Site Scripting (XSS) vulnerability exists in Nuxt Server Island rendering logic when utilizing the useHeadSafe composable. Raw head attributes bypass sanitization during island serialization.
Nuxt versions prior to 4.6.2 suffer from a route rule bypass vulnerability caused by an architectural mismatch in case-sensitivity handling between the Nitro server runtime and the Vue page router. An unauthenticated remote attacker can bypass server-enforced route directives—such as redirects, security headers, and SSR controls—by altering character casing in HTTP request paths.
A heap-based buffer overflow vulnerability in Pillow PyPI binary wheels (versions 11.1.0 to 12.x) allows remote code execution or application crash via crafted JPEG 2000 files processed by the bundled libopenjp2 library.
CVE-2026-109618 (dubbed PHP2Shell) is a critical vulnerability in the PHP engine core resulting from improper out-of-memory exception handling via longjmp(). When request allocation fails mid-processing, non-local control flow bypasses cleanup procedures, leaving memory structures uninitialized or in a use-after-free state during request shutdown.