From 4ba7ed665026ef9b3ecd42aa47611a6c2cf8724c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Fl=C3=BCckiger?= Date: Wed, 19 Aug 2026 08:51:38 +0000 Subject: [PATCH] deps: V8: cherry-pick c795f5948568 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original commit message: [immutable-array-buffer] Fix check order in TypedArray.prototype.set According to the spec, TypedArray.prototype.set checks IsImmutableBuffer(target.[[ViewedArrayBuffer]]) before converting the offset argument to integer and before reading from the source object. Additionally, when setting from a TypedArray source, reading from an immutable source TypedArray is permitted, so the source array should be validated using TypedArrayAccessMode::kRead rather than kWrite. Drive-By: Add a fast case for Smi indices where the steps are not observable and we can fold all checks. TAG=agy CONV=94aa3be8-9990-41fe-a565-c62e3daa9a42 Bug: 450237486 Change-Id: I21790be90cde9a96ba7c1f573f034016d9c29850 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/8252528 Reviewed-by: Igor Sheludko Commit-Queue: Igor Sheludko Auto-Submit: Olivier Flückiger Cr-Commit-Position: refs/heads/main@{#109366} Refs: https://github.com/v8/v8/commit/c795f5948568dc7c5cce585928b9a6acb307ca82 --- common.gypi | 2 +- deps/v8/src/builtins/typed-array-set.tq | 75 +++++++++++++------ deps/v8/test/mjsunit/immutable-arraybuffer.js | 44 +++++++++++ 3 files changed, 97 insertions(+), 24 deletions(-) diff --git a/common.gypi b/common.gypi index ce50adc016ea..841dc0288cd8 100644 --- a/common.gypi +++ b/common.gypi @@ -44,7 +44,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.36', + 'v8_embedder_string': '-node.37', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/src/builtins/typed-array-set.tq b/deps/v8/src/builtins/typed-array-set.tq index 6fe170ede868..51562299edc9 100644 --- a/deps/v8/src/builtins/typed-array-set.tq +++ b/deps/v8/src/builtins/typed-array-set.tq @@ -46,32 +46,61 @@ transitioning javascript builtin TypedArrayPrototypeSet( } try { - // 5. Let targetOffset be ? ToInteger(offset). - // 6. If targetOffset < 0, throw a RangeError exception. - let targetOffsetOverflowed: bool = false; + let attachedTargetAndLength: ValidJSTypedArrayAndLength; let targetOffset: uintptr = 0; - if (arguments.length > 1) { - const offsetArg = arguments[1]; - try { - targetOffset = ToUintPtr(offsetArg) - // On values less than zero throw RangeError immediately. - otherwise OffsetOutOfBounds, - // On UintPtr or SafeInteger range overflow throw RangeError after - // performing observable steps to follow the spec. - OffsetOverflow, OffsetOverflow; - } label OffsetOverflow { - targetOffsetOverflowed = true; + let targetOffsetOverflowed: bool = false; + + // If the offset argument is not provided then the targetOffset is 0. + const offsetArg: JSAny = + arguments.length > 1 ? arguments[1] : SmiConstant(0); + typeswitch (offsetArg) { + case (offsetSmi: Smi): { + // 5. Let targetOffset be ? ToInteger(offset). + // 6. If targetOffset < 0, throw a RangeError exception. + if (offsetSmi < 0) goto OffsetOutOfBounds; + targetOffset = Unsigned(SmiUntag(offsetSmi)); + + // For Smi offsets, integer conversion has no side effects, so step 4 + // (IsImmutableBuffer check) can be deferred and combined with steps 7-9 + // in EnsureValidAndReadLength without observable differences. + // 4. If IsImmutableBuffer(target.[[ViewedArrayBuffer]]) is true, throw + // a TypeError exception. + // 7. Let targetBuffer be target.[[ViewedArrayBuffer]]. + // 8. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError + // exception. + // 9. Let targetLength be target.[[ArrayLength]]. + attachedTargetAndLength = EnsureValidAndReadLength( + target, TypedArrayAccessMode::kWrite) otherwise Fail; + } + case (offsetOther: JSAny): { + // 4. If IsImmutableBuffer(target.[[ViewedArrayBuffer]]) is true, throw + // a TypeError exception. + if (IsImmutableArrayBuffer(target.buffer)) deferred { + goto Fail; + } + + // 5. Let targetOffset be ? ToInteger(offset). + // 6. If targetOffset < 0, throw a RangeError exception. + try { + targetOffset = ToUintPtr(offsetOther) + // On values less than zero throw RangeError immediately. + otherwise OffsetOutOfBounds, + // On UintPtr or SafeInteger range overflow throw RangeError after + // performing observable steps to follow the spec. + OffsetOverflow, OffsetOverflow; + } label OffsetOverflow { + targetOffsetOverflowed = true; + } + + // 7. Let targetBuffer be target.[[ViewedArrayBuffer]]. + // 8. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError + // exception. + // 9. Let targetLength be target.[[ArrayLength]]. + attachedTargetAndLength = EnsureValidAndReadLength( + target, TypedArrayAccessMode::kWrite) otherwise Fail; } - } else { - // If the offset argument is not provided then the targetOffset is 0. } - // 7. Let targetBuffer be target.[[ViewedArrayBuffer]]. - // 8. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError - // exception. - const attachedTargetAndLength = EnsureValidAndReadLength( - target, TypedArrayAccessMode::kWrite) otherwise Fail; - const overloadedArg = arguments[0]; try { // 1. Choose SetTypedArrayFromTypedArray or SetTypedArrayFromArrayLike @@ -86,7 +115,7 @@ transitioning javascript builtin TypedArrayPrototypeSet( // 5. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError // exception. const attachedSourceAndLength = - EnsureValidAndReadLength(typedArray, TypedArrayAccessMode::kWrite) + EnsureValidAndReadLength(typedArray, TypedArrayAccessMode::kRead) otherwise Fail; TypedArrayPrototypeSetTypedArray( attachedTargetAndLength, attachedSourceAndLength, targetOffset, diff --git a/deps/v8/test/mjsunit/immutable-arraybuffer.js b/deps/v8/test/mjsunit/immutable-arraybuffer.js index 1a4619d53c09..a76ce4f7e5b4 100644 --- a/deps/v8/test/mjsunit/immutable-arraybuffer.js +++ b/deps/v8/test/mjsunit/immutable-arraybuffer.js @@ -564,3 +564,47 @@ if (this.Worker) { assertEquals('OK', w.getMessage()); w.terminate(); } + +(function testTypedArraySetOrder() { + const ab = new ArrayBuffer(8); + const imm = ab.transferToImmutable(); + const immTA = new Uint8Array(imm); + + let offsetEvaluated = false; + const offset = { + valueOf() { + offsetEvaluated = true; + return 0; + } + }; + + let sourceRead = false; + const source = { + get length() { + sourceRead = true; + return 1; + }, + get 0() { + sourceRead = true; + return 42; + } + }; + + // 1. Immutable target throws before offset conversion or source reading + assertThrows(() => immTA.set(source, offset), TypeError); + assertFalse(offsetEvaluated, "offset should not be evaluated for immutable target"); + assertFalse(sourceRead, "source should not be read for immutable target"); + + // 2. Mutable target can read from immutable TypedArray source + const mutableTA = new Uint8Array(8); + assertDoesNotThrow(() => mutableTA.set(immTA)); + + // 3. Detached target evaluates offset before throwing TypeError + const detachedAb = new ArrayBuffer(8); + const detachedTA = new Uint8Array(detachedAb); + %ArrayBufferDetach(detachedAb); + + offsetEvaluated = false; + assertThrows(() => detachedTA.set([1], offset), TypeError); + assertTrue(offsetEvaluated, "offset should be evaluated for detached target"); +})();