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·3 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 minute ago•CVE-2026-53467
5.3

CVE-2026-53467: Heap Information Disclosure via Uninitialized Pixel Cache in ImageMagick MNG Decoder

CVE-2026-53467 is a heap information disclosure vulnerability in the Multiple-image Network Graphics (MNG) decoder of ImageMagick. The vulnerability arises from a failure to zero-initialize newly allocated pixel cache memory buffers. A remote attacker can exploit this by submitting a crafted sparse MNG image file to trigger uninitialized memory preservation. The resulting output contains residual heap bytes, potentially leaking sensitive process memory or assisting in ASLR bypass.

Amit Schendel
Amit Schendel
0 views•6 min read
•about 1 hour ago•CVE-2026-55223
6.3

CVE-2026-55223: Remote Code Execution via Deserialization Gadget Chain in c3p0 Connection Pooling Library

An untrusted deserialization vulnerability exists in the c3p0 JDBC connection pooling library before version 0.14.0. Standard JDBC getter methods conform to the JavaBean property getter pattern, allowing introspection libraries like Apache Commons BeanUtils to evaluate connection properties dynamically during deserialization, leading to arbitrary code execution when chained with a vulnerable database driver or JNDI sink.

Alon Barad
Alon Barad
3 views•6 min read
•about 2 hours ago•CVE-2026-54696
3.7

CVE-2026-54696: Heap-based Buffer Overflow in Ruby json Gem Native C Extension

A heap-based buffer overflow vulnerability exists in the native C extension of the Ruby json gem (versions 2.9.0 through 2.19.8) during IO-based streaming serialization. An incorrect buffer size calculation can lead to memory corruption and process termination when processing large strings.

Alon Barad
Alon Barad
5 views•6 min read
•about 4 hours ago•CVE-2026-59936
8.7

CVE-2026-59936: Infinite Loop in pypdf Inline Image Parser Leads to Denial of Service

An infinite loop vulnerability exists in the pure-python PDF library pypdf prior to version 6.14.1. This vulnerability occurs during the processing of malformed or truncated page content streams containing inline images. Specifically, the parser fails to validate the End-of-Stream condition during inline image dictionary parsing. This results in continuous backward stream-seeking and an infinite loop that consumes 100% CPU on the processing thread.

Alon Barad
Alon Barad
5 views•6 min read
•about 5 hours ago•CVE-2026-59935
8.7

CVE-2026-59935: Infinite Loop Denial of Service in pypdf via Malformed Inline Images

An infinite loop vulnerability exists in the pypdf library prior to version 6.14.2 when processing malformed PDF inline images that utilize ASCII85 or ASCIIHex decoders. Attackers can exploit this vulnerability by submitting crafted PDF files containing incomplete streams, causing the parsing thread to consume 100% CPU resource indefinitely and leading to Denial of Service.

Amit Schendel
Amit Schendel
5 views•7 min read
•about 6 hours ago•CVE-2026-59937
7.5

CVE-2026-59937: CPU Exhaustion via Quadratic-Time Cross-Reference Recovery Loop in pypdf

A critical CPU exhaustion vulnerability exists in the pypdf library before version 6.14.0. When parsing PDF files with malformed or corrupt standard cross-reference (xref) table entries, the library falls back to sequentially scanning the entire file buffer via regular expression search. An attacker can exploit this algorithmic bottleneck by supplying a crafted PDF with numerous malformed xref entries, leading to denial of service.

Alon Barad
Alon Barad
5 views•4 min read