Skip to content

Heap buffer overflow in JPEG Buffer decode due to unchecked image dimensions. #2566

@michaelknap

Description

@michaelknap

I am reporting a native memory corruption issue in node-canvas.

In node-canvas v3.2.3, loading a crafted JPEG from a Node.js Buffer can trigger a signed integer overflow in the JPEG decode path. The overflow causes node-canvas to allocate a buffer that is much smaller than the decoded image requires. The decoder then writes decoded pixels into that undersized buffer, causing a heap buffer overflow.

AddressSanitizer confirms a heap-buffer-overflow, and debugger testing confirms that the write continues into adjacent heap memory. In non-sanitized runs, the overwrite reaches adjacent heap chunk metadata and corrupts neighboring heap-resident libjpeg controller state, later used by an indirect call during normal JPEG decoding.

The issue is in the JPEG Buffer input path. The file input path has a size check that rejects oversized JPEG dimensions, but the Buffer path reaches the unsafe decode logic.

Code Locations

  • src/Image.cc, Image::loadJPEGFromBuffer()
  • src/Image.cc, Image::decodeJPEGIntoSurface()
  • src/Image.cc, Image::jpegToARGB()

Root Cause

The vulnerable allocation is:

uint8_t *data = new uint8_t[naturalWidth * naturalHeight * channels];

For a JPEG with decoded dimensions 32768 x 32768, the intended ARGB output size is 32768 * 32768 * 4, which is 0x100000000 bytes. In the current code path this calculation is performed with signed integer arithmetic and overflows. The pixel-copy loop then writes as if the full image-sized buffer had been allocated.

Relevant CWE IDs

  • CWE-122: Heap-based Buffer Overflow
  • CWE-787: Out-of-bounds Write
  • CWE-190: Integer Overflow or Wraparound
  • CWE-131: Incorrect Calculation of Buffer Size
  • CWE-680: Integer Overflow to Buffer Overflow, as a chain description

Impact

Applications that accept attacker-controlled image bytes and pass them to node-canvas as a Buffer, for example through loadImage(body) or Image.src = buffer, may be exposed to native process crashes and memory corruption. The current evidence supports treating this as more than denial of service, because corrupted heap state is later consumed by an indirect call.

Expected Behavior

node-canvas should reject JPEGs whose decoded dimensions or decoded byte size exceed safe limits before allocating or decoding. The Buffer path should enforce the same kind of bounds as the file path. Size calculations should use checked size_t arithmetic and fail safely on overflow.

Happy to send the PR with the patch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions