CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-59938

CVE-2026-59938: Uncontrolled Memory Allocation (DoS) in pypdf Image Parsing

Amit Schendel
Amit Schendel
Senior Security Researcher

Jul 23, 2026·8 min read·41 visits

Executive Summary (TL;DR)

Uncontrolled memory allocation in pypdf < 6.14.0 allows remote attackers to cause a Denial of Service (OOM crash) by submitting a small PDF with artificially large image dimensions (/Width and /Height) in XObject metadata.

An uncontrolled memory allocation vulnerability (CWE-789) exists in pypdf prior to version 6.14.0. The library blindly trusted user-controlled image dimensions (/Width and /Height) from PDF metadata, allowing attackers to trigger physical memory exhaustion and an Out-of-Memory crash via tiny, malformed files.

Vulnerability Overview

The open-source Python library pypdf is widely utilized for reading, manipulating, and writing PDF files across various server-side applications. Within this library, the image extraction and decoding workflows parse specialized image dictionary structures, such as Image XObjects and inline images. The processing of these image objects relies on extracting metadata descriptors from the PDF file to construct native python representations of image formats.

Prior to version 6.14.0, the library did not validate the sanity of declared dimensions prior to initiating fallback processing for mismatched data. When parsing a PDF, an application resolving images relies on the /Width and /Height keys specified within the image object's dictionary to allocate buffers and read the physical stream data. This setup exposes an attack surface where maliciously crafted, structurally abnormal PDF files are processed directly on host systems.

This flaw is classified under CWE-789 (Memory Allocation with Excessive Size Value). When a host application processes an untrusted PDF, a mismatched length configuration triggers a byte-expansion routine that attempts to construct an excessively large string in system memory. This uncontrolled allocation triggers immediate physical memory depletion and process termination.

Root Cause Analysis

The root cause of CVE-2026-59938 resides in the _image_from_bytes function in pypdf/generic/_image_xobject.py. This helper function accepts physical stream data alongside untrusted dimensions derived from the PDF's /Width and /Height dictionary entries. The function's main execution flow attempts to leverage the Python Imaging Library (Pillow) wrapper Image.frombytes(mode, size, data) to map raw bytes into an image object.

When a discrepancy exists between the expected dimensions and the actual uncompressed length of the data stream, Pillow raises a ValueError. The library handles this mismatch by entering a fallback recovery block. In this block, pypdf attempts to dynamically reconstruct the buffer to align with the metadata. It calculates a scaling factor k based on the formula:

k = (pixel_count * bytes_per_pixel) / data_length

Here, pixel_count is the product of /Width and /Height, and bytes_per_pixel is determined by the length of the string representation of the color mode (e.g., 3 bytes for 'RGB'). The code then uses a list comprehension coupled with b"".join() to pad the mismatched stream to the expected size:

data = b"".join(bytes((x,) * int(k)) for x in data)

Because the factor k is derived from metadata that is completely controlled by the user, an attacker can define extremely large dimensions (e.g., 100,000 by 80,000) while providing only a single byte of actual physical payload data. This mismatch forces k to scale into tens of billions, causing the subsequent join() execution to attempt an unconstrained allocation of gigabytes of RAM. The Python interpreter terminates abruptly due to memory exhaustion.

Code and Patch Analysis

To fully understand the changes implemented to address CVE-2026-59938, we examine the differences between the vulnerable code path and the secure implementation introduced in version 6.14.0.

The vulnerable execution flow directly passed the untrusted size and mode attributes into the error handler where allocation occurred. Below is the vulnerable implementation of the parsing function:

# Vulnerable implementation in pypdf < 6.14.0
def _image_from_bytes(
    mode: str, size: tuple[int, int], data: bytes
) -> Image.Image:
    try:
        img = Image.frombytes(mode, size, data)
    except ValueError as exc:
        nb_pix = size[0] * size[1]
        data_length = len(data)
        if data_length == 0:
            raise EmptyImageDataError(
                "Data is 0 bytes, cannot process an image from empty data."
            ) from exc
        if data_length % nb_pix != 0:
            raise
        k = nb_pix * len(mode) / data_length
        # Unbounded multiplication and buffer creation occurs here
        data = b"".join(bytes((x,) * int(k)) for x in data)
        img = Image.frombytes(mode, size, data)
    return img

The patch merged in commit c64583be16b8e8763d8777075f8ecbf382014b7a introduces a strict safety check before executing Pillow commands or evaluating fallback code. It calculates the memory footprint mathematically and halts execution if the size exceeds a predefined threshold:

# Patched implementation in pypdf >= 6.14.0
def _image_from_bytes(
    mode: str, size: tuple[int, int], data: bytes
) -> Image.Image:
    # Mathematically calculate the total pixel count and expected buffer size
    pixel_count = size[0] * size[1]
    bytes_per_pixel = len(mode)
    required_byte_count = pixel_count * bytes_per_pixel
 
    # Enforce upper bound limits before performing operations
    from pypdf.filters import FLATE_MAX_BUFFER_SIZE
    if required_byte_count > FLATE_MAX_BUFFER_SIZE:
        raise LimitReachedError(
            f"Requested image buffer size {required_byte_count} exceeds limit {FLATE_MAX_BUFFER_SIZE}."
        )
 
    try:
        img = Image.frombytes(mode, size, data)
    except ValueError as exc:
        data_length = len(data)
        if data_length == 0:
            raise EmptyImageDataError(
                "Data is 0 bytes, cannot process an image from empty data."
            ) from exc
        if data_length % pixel_count != 0:
            raise
        k = required_byte_count / data_length
        data = b"".join(bytes((x,) * int(k)) for x in data)
        img = Image.frombytes(mode, size, data)
    return img

This defensive design is highly effective because it computes the total buffer requirement first, using Python's native arbitrary-precision integers which are immune to traditional overflow exploits. The security threshold FLATE_MAX_BUFFER_SIZE defaults to 75 MB, which provides a sensible default upper bound for typical image operations. However, systems processing multiple pages concurrently must ensure that cumulative concurrent allocations do not bypass host-level memory constraints.

Exploitation Methodology

An attack targeting CVE-2026-59938 requires transmitting a malformed PDF document to a server running a vulnerable version of pypdf. The exploitation does not require authentication or specific configurations beyond the target application processing user-supplied PDFs and requesting image resolution or content extraction.

The vector relies on defining an Image XObject dictionary containing structural metadata that triggers the library's fallback path. A conceptual representation of this XObject inside the PDF content stream is organized as follows:

5 0 obj
<<
  /Type /XObject
  /Subtype /Image
  /Width 100000
  /Height 80000
  /ColorSpace /DeviceRGB
  /BitsPerComponent 8
  /Length 1
>>
stream
\x00
endstream
endobj

This small block defines an image footprint demanding 24 GB of uncompressed RAM (100,000 * 80,000 * 3 bytes). The underlying stream contains only a single byte. When a script iterates over the page's resources or attempts to convert pages using page.images, pypdf parses this stream and activates the byte-expansion loop.

The execution flow of this vulnerability is visualized in the following system flow diagram:

To demonstrate this behavior, a test runner executing list(page.images) on the malformed PDF payload immediately triggers high CPU thrashing and spikes system memory utilization to maximum capacity before causing the operating system kernel to issue a SIGKILL (exit code 137).

Impact Assessment

The primary impact of CVE-2026-59938 is local and remote Denial of Service (DoS). When exploited, the vulnerability leads to resource exhaustion and terminates the affected process. This behavior represents a significant threat to backend architectures that process document uploads automatically, such as resume screening tools, financial auditing systems, or cloud storage viewers.

The CVSS v3.1 base score is 5.3, with an availability impact score of 'Low' under standard mapping because the crash typically terminates only the parsing thread or container, which can be automatically restarted. However, in environments lacking container orchestration or process monitoring, this crash results in permanent application downtime until manual administrator intervention occurs.

From a data security perspective, the vulnerability does not present a mechanism for remote code execution, file modification, or unauthorized data access. The attack complexity is low, and no specialized preconditions or user interaction are required, making it highly attractive for adversaries seeking to degrade application availability with minimal payload sizes.

Remediation and Configuration Guidance

The standard remediation for this vulnerability is upgrading pypdf to version 6.14.0 or later. This release addresses the uncontrolled buffer allocation by introducing bounded limits. Upgrades can be performed natively using python package managers.

For installations where immediate package upgrades are not feasible, temporary workarounds can mitigate the risk. Administrators should deploy sandbox environments utilizing operating system controls to constrain resource limits. For example, configuring limits via the Linux resource API or setting container memory boundaries ensures that an OOM crash inside pypdf is isolated and does not disrupt neighboring system processes.

# Restricting process virtual memory limit using Python's resource module
import resource
 
def limit_memory(max_memory_bytes):
    resource.setrlimit(resource.RLIMIT_AS, (max_memory_bytes, max_memory_bytes))

In addition, modern Web Application Firewalls (WAF) or pre-processing pipeline checks can analyze incoming PDF byte patterns. Filtering for incoming documents that describe a massive disparity between declared pixel footprints and actual stream lengths provides a robust layer of defense before the PDF reaches the processing library.

Official Patches

py-pdfOfficial Security Patch Commit

Technical Appendix

CVSS Score
5.3/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L
EPSS Probability
0.30%
Top 78% most exploited

Affected Systems

pypdf Python Library

Affected Versions Detail

Product
Affected Versions
Fixed Version
pypdf
py-pdf
< 6.14.06.14.0
AttributeDetail
CWE IDCWE-789
Attack VectorNetwork
CVSS Score5.3 (CVSS v3.1)
EPSS Score0.00303 (Percentile: 22.45%)
ImpactDenial of Service (OOM Application Crash)
Exploit StatusProof-of-Concept (PoC) available
KEV StatusNot Listed

MITRE ATT&CK Mapping

T1499.004Endpoint Denial of Service: System Resource Exhaustion
Denial of Service
CWE-789
Memory Allocation with Excessive Size Value

The product allocates memory based on an untrusted, user-controlled size value without adequately validating that the size is within reasonable limits, making it susceptible to resource exhaustion.

Known Exploits & Detection

GitHub Security AdvisoryDetails regarding the uncontrolled memory usage for wrong image dimensions.

Vulnerability Timeline

Release of pypdf version 6.13.3 (vulnerable)
2026-06-17
Security patch merged and version 6.14.0 released
2026-06-22
GitHub Security Advisory and CVE-2026-59938 published
2026-07-08

References & Sources

  • [1]GitHub Security Advisory (GHSA-5qjq-93h5-hrgp)
  • [2]Fix Commit in pypdf
  • [3]Pull Request #3888 - Fix potential large memory usage
  • [4]pypdf v6.14.0 Release Notes
  • [5]CVE-2026-59938 Record Page

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•1 day ago•CVE-2026-107844
5.3

CVE-2026-107844: Path Traversal Vulnerability in Contao ImagesController

A path traversal vulnerability (CWE-22) in Contao CMS allows unauthenticated remote attackers to bypass directory boundary restrictions in ImagesController and access files within the project directory.

Amit Schendel
Amit Schendel
8 views•5 min read
•1 day ago•CVE-2026-107848
3.5

CVE-2026-107848: Cross-Site Request Forgery (CSRF) in Contao Backend Actions via GET Requests

Contao Open Source CMS versions 4.0.0 through 5.3.49 and 5.4.0-RC1 through 5.7.11 contain a Cross-Site Request Forgery (CSRF) vulnerability in backend parameter handling. The `RequestTokenListener` component validates anti-CSRF tokens solely for HTTP POST requests, while GET-based declarative guards run only when an `act` parameter is present in the query string. Consequently, custom backend actions dispatched via alternative parameters such as `key=` can execute without CSRF token verification when triggered by an authenticated user.

Amit Schendel
Amit Schendel
8 views•5 min read
•2 days ago•CVE-2026-107843
5.3

CVE-2026-107843: Unthrottled Activation Email Resend and Account Enumeration in Contao CMS

Contao CMS versions 4.1.0 through 5.3.49 and 5.4.0-RC1 through 5.7.11 fail to validate form submission tokens and enforce rate limiting when processing activation email resend requests via HTTP POST, enabling resource exhaustion and account state enumeration.

Amit Schendel
Amit Schendel
8 views•4 min read
•2 days ago•CVE-2026-107842
5.3

CVE-2026-107842: Information Disclosure via Stale Indexing in Contao Search Module

An information disclosure vulnerability in Contao CMS allows unauthenticated site visitors to view protected page titles, URLs, and text excerpts through search queries when protected page indexing is disabled after previously being enabled.

Amit Schendel
Amit Schendel
10 views•4 min read
•2 days ago•GHSA-G38J-7V97-X298
6.5

GHSA-G38J-7V97-X298: Missing Authorization Check in Vikunja CalDAV Task Relation Creation

In Vikunja prior to version 2.6.0, relation creation via the CalDAV endpoint fails to invoke the TaskRelation.CanCreate authorization check. This missing access control allows an authenticated user to establish unauthorized relationships and perform write operations against any task, provided its unique identifier (UID) is known.

Alon Barad
Alon Barad
12 views•5 min read
•2 days ago•GHSA-3HC7-R24J-RPWC
6.8

GHSA-3hc7-r24j-rpwc: Cross-Project Task Disclosure via Subtask Expansion in Vikunja

A cross-project information disclosure vulnerability in Vikunja allows authenticated users with read access to one project to view private task details from unauthorized projects via subtask expansion parameters.

Alon Barad
Alon Barad
9 views•5 min read