Skip to content

OOB heap write in Array.prototype.fill #5315

Open

Description

@PwnToday

OOB heap write in Array.prototype.fill (fast-array path)

Revision: b7069350 (master)
Platform: macOS 26.5 (Darwin 25.5.0 arm64), Apple clang 21.0.0

Array.prototype.fill computes its bounds k/final from the array length captured before argument coercion. Normalizing start/end calls ToInteger → user valueOf, which can shrink the array and reallocate the fast-array backing store smaller. The write loop (ecma-builtin-array-prototype.c:2189-2194) then uses the stale bounds, never re-clamped to the current ext_obj_p->u.array.length — unlike indexOf/lastIndexOf/includes, which clamp. Result: OOB read/write (attacker-controlled fill value) past the buffer.

Build

python3 tools/build.py --clean --debug --jerry-cmdline=ON \
  --compile-flag=-fsanitize=address --linker-flag=-fsanitize=address

Test case

var arr = [];
for (var i = 0; i < 100; i++) { arr[i] = i; }
arr.fill(1, { valueOf: function () { arr.length = 1; return 0; } });

Run: build/bin/jerry testcase.js → deterministic SIGABRT (3/3).

Backtrace

ecma_free_value(value=4294967295)  at ecma-helpers-value.c:1122   (value-type assert)
ecma_builtin_array_prototype_fill  at ecma-builtin-array-prototype.c:2191   (k=8, final=100, len=100)

Array shrunk to length 1, but buffer_p[8] is read/written past the 1-element store.

Fix

Re-clamp before the write loop (as the sibling methods do):

final = JERRY_MIN (final, ext_obj_p->u.array.length);
k = JERRY_MIN (k, final);

Verified: removes the crash.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions