Automattic / Automattic/node-canvas
Heap buffer overflow in JPEG Buffer decode due to unchecked image dimensions.
- Dominant language
- JavaScript
- Stars
- 10.7k
- Forks
- 1.2k
- Avg merge
- 4d 8h
- Merged PRs (30d)
- 1
Description
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:
```cpp
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.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.