mirror of
https://github.com/openai/codex.git
synced 2026-10-19 16:17:49 +00:00
## Why Optimized `Array.prototype.sort` can write objects into incompatible array element storage when receiver element kinds differ and the comparator mutates the array. ## What changed - Upgrade `v8` to `152.2.0`, the Bazel V8 source to `15.2.124.1`, and ICU data and initialization to version 78. - Backport element-kind agreement checks for sort inlining in TurboFan and Maglev. Apply the patch to Bazel builds and Windows MSVC source builds, and include it in source-build cache keys and canary triggers. - Refresh pinned sandbox archives and bindings, extend Bazel prebuilt coverage to musl Linux, and use matching sandbox bindings on Windows MSVC. - Update Bazel integration patches and runtime dependencies for the new V8 release, and download the pinned Windows build tools in source-build workflows. ## Testing Include the upstream sort regression test covering mixed element kinds, comparator mutation, and continued optimization for matching kinds. Add a test requiring canary and Windows source builds when the backport changes, and update artifact-fetch tests for the new version. GitOrigin-RevId: 138b7821cddd25466c10aced1dd22be24837caf0
164 lines
6.3 KiB
Diff
164 lines
6.3 KiB
Diff
# What: backport CVE-2026-85046 to V8 15.2.124.1 by rejecting mixed
|
|
# elements kinds when inlining Array.prototype.sort.
|
|
# Scope: Maglev/TurboFan implementation guards and the upstream regression test.
|
|
# Upstream M152 fix: https://github.com/v8/v8/commit/94c34b200ae9caf83d06c167a070746a1c7e825c
|
|
# CVE advisory: https://chromereleases.googleblog.com/2026/09/stable-channel-update-for-desktop_01882797386.html
|
|
|
|
diff --git a/orig/v8-15.2.124.1/src/compiler/js-call-reducer.cc b/mod/v8-15.2.124.1/src/compiler/js-call-reducer.cc
|
|
index e8707261d3e2..0e2d9bfd0a74 100644
|
|
--- a/orig/v8-15.2.124.1/src/compiler/js-call-reducer.cc
|
|
+++ b/mod/v8-15.2.124.1/src/compiler/js-call-reducer.cc
|
|
@@ -4068,6 +4068,10 @@ class IteratingArrayBuiltinHelper {
|
|
return;
|
|
}
|
|
|
|
+ all_elements_kinds_equal_ = std::all_of(
|
|
+ receiver_maps.begin(), receiver_maps.end(),
|
|
+ [&](MapRef map) { return map.elements_kind() == elements_kind_; });
|
|
+
|
|
// TODO(jgruber): May only be needed for holey elements kinds.
|
|
if (!dependencies->DependOnNoElementsProtector()) return;
|
|
|
|
@@ -4083,10 +4087,13 @@ class IteratingArrayBuiltinHelper {
|
|
Control control() const { return control_; }
|
|
MapInference* inference() { return &inference_; }
|
|
ElementsKind elements_kind() const { return elements_kind_; }
|
|
+ // False when elements_kind() is a union over differing receiver kinds.
|
|
+ bool all_elements_kinds_equal() const { return all_elements_kinds_equal_; }
|
|
|
|
private:
|
|
bool can_reduce_ = false;
|
|
bool has_stability_dependency_ = false;
|
|
+ bool all_elements_kinds_equal_ = false;
|
|
Node* receiver_;
|
|
Effect effect_;
|
|
Control control_;
|
|
@@ -4126,6 +4133,11 @@ Reduction JSCallReducer::ReduceArraySort(Node* node,
|
|
IteratingArrayBuiltinHelper h(node, broker(), jsgraph(), dependencies());
|
|
if (!h.can_reduce()) return h.inference()->NoChange();
|
|
|
|
+ // The union kind is only an upper bound on each receiver's own kind. Loads
|
|
+ // through it widen and are sound; stores narrow and are not. Unlike the
|
|
+ // iterating builtins, sort writes its snapshot back into the receiver.
|
|
+ if (!h.all_elements_kinds_equal()) return h.inference()->NoChange();
|
|
+
|
|
// Only non-holey, non-double PACKED kinds are supported. Holey arrays need
|
|
// hole handling; double arrays need a FixedDoubleArray temp copy.
|
|
if (IsHoleyElementsKind(h.elements_kind())) return h.inference()->NoChange();
|
|
diff --git a/orig/v8-15.2.124.1/src/maglev/maglev-graph-builder.cc b/mod/v8-15.2.124.1/src/maglev/maglev-graph-builder.cc
|
|
index c40cadb32d92..c6ae7f791267 100644
|
|
--- a/orig/v8-15.2.124.1/src/maglev/maglev-graph-builder.cc
|
|
+++ b/mod/v8-15.2.124.1/src/maglev/maglev-graph-builder.cc
|
|
@@ -9797,6 +9797,16 @@ MaybeReduceResult MaglevGraphBuilder::TryReduceArrayPrototypeSort(
|
|
" array iteration");
|
|
}
|
|
|
|
+ // The union kind is only an upper bound on each receiver's own kind. Loads
|
|
+ // through it widen and are sound; stores narrow and are not. Unlike the
|
|
+ // iterating builtins, sort writes its snapshot back into the receiver.
|
|
+ if (std::any_of(possible_maps->begin(), possible_maps->end(),
|
|
+ [&](compiler::MapRef map) {
|
|
+ return map.elements_kind() != elements_kind;
|
|
+ })) {
|
|
+ FAIL(" to reduce Array.prototype.sort - receiver elements kinds disagree");
|
|
+ }
|
|
+
|
|
// Holey arrays require holes to be moved to the end of the sorted result
|
|
// (ECMA-262 23.1.3.30 step 5, skip-holes mode). The insertion sort does
|
|
// not implement this, so bail out for any holey kind.
|
|
diff --git a/orig/v8-15.2.124.1/test/mjsunit/regress/regress-crbug-542403045.js b/mod/v8-15.2.124.1/test/mjsunit/regress/regress-crbug-542403045.js
|
|
new file mode 100644
|
|
index 000000000000..e60a974f8784
|
|
--- /dev/null
|
|
+++ b/mod/v8-15.2.124.1/test/mjsunit/regress/regress-crbug-542403045.js
|
|
@@ -0,0 +1,89 @@
|
|
+// Copyright 2026 the V8 project authors. All rights reserved.
|
|
+// Use of this source code is governed by a BSD-style license that can be
|
|
+// found in the LICENSE file.
|
|
+
|
|
+// Flags: --allow-natives-syntax --maglev
|
|
+
|
|
+// The inlined sort snapshots the receiver's elements, runs comparefn, then
|
|
+// copies the snapshot back specialized on the receiver's elements kind. With
|
|
+// polymorphic {PACKED_SMI,PACKED} feedback that kind is the union
|
|
+// PACKED_ELEMENTS, which no longer describes a receiver that comparefn has
|
|
+// narrowed back to PACKED_SMI_ELEMENTS.
|
|
+
|
|
+function sort(a) {
|
|
+ function compare() {
|
|
+ a.fill(0);
|
|
+ return 0;
|
|
+ }
|
|
+ return a.sort(compare);
|
|
+}
|
|
+
|
|
+%PrepareFunctionForOptimization(sort);
|
|
+for (let i = 0; i < 100; ++i) {
|
|
+ sort([1, 2]);
|
|
+ sort([{}, {}]);
|
|
+}
|
|
+%OptimizeMaglevOnNextCall(sort);
|
|
+sort([1, 2]);
|
|
+
|
|
+const object = {};
|
|
+const bad = [object, {}];
|
|
+sort(bad);
|
|
+
|
|
+// A Smi-elements array must never hold a HeapObject.
|
|
+assertFalse(%HasSmiElements(bad) && bad[0] === object);
|
|
+
|
|
+// Mixed-kind feedback is not inlined, but still sorts correctly.
|
|
+function sortMixed(a) {
|
|
+ return a.sort((x, y) => x - y);
|
|
+}
|
|
+%PrepareFunctionForOptimization(sortMixed);
|
|
+for (let i = 0; i < 100; ++i) {
|
|
+ sortMixed([2, 1]);
|
|
+ sortMixed([{}, {}]);
|
|
+}
|
|
+%OptimizeMaglevOnNextCall(sortMixed);
|
|
+assertEquals([1, 2, 3], sortMixed([3, 1, 2]));
|
|
+
|
|
+// Same via a Smi/double mix, whose union kind PACKED_DOUBLE_ELEMENTS is
|
|
+// rejected by the double bailout rather than the kind-agreement one.
|
|
+function sortDouble(a) {
|
|
+ function compare(x, y) {
|
|
+ a.fill(0);
|
|
+ return x - y;
|
|
+ }
|
|
+ return a.sort(compare);
|
|
+}
|
|
+%PrepareFunctionForOptimization(sortDouble);
|
|
+for (let i = 0; i < 100; ++i) {
|
|
+ sortDouble([2, 1]);
|
|
+ sortDouble([2.5, 1.5]);
|
|
+}
|
|
+%OptimizeMaglevOnNextCall(sortDouble);
|
|
+sortDouble([2, 1]);
|
|
+// The sort writes its snapshot back, so comparefn's fill is not observable.
|
|
+assertEquals([1.5, 2.5], sortDouble([2.5, 1.5]));
|
|
+
|
|
+// Sites whose receiver kinds agree keep the inlined path, including when the
|
|
+// maps themselves differ.
|
|
+function withProperty() {
|
|
+ const a = [{}, {}];
|
|
+ a.foo = 1;
|
|
+ return a;
|
|
+}
|
|
+function sortSameKind(a) {
|
|
+ return a.sort((x, y) => 0);
|
|
+}
|
|
+%PrepareFunctionForOptimization(sortSameKind);
|
|
+for (let i = 0; i < 100; ++i) {
|
|
+ sortSameKind([{}, {}]);
|
|
+ sortSameKind(withProperty());
|
|
+}
|
|
+%OptimizeMaglevOnNextCall(sortSameKind);
|
|
+sortSameKind([{}, {}]);
|
|
+assertOptimized(sortSameKind);
|
|
+for (let i = 0; i < 100; ++i) {
|
|
+ sortSameKind([{}, {}]);
|
|
+ sortSameKind(withProperty());
|
|
+}
|
|
+assertOptimized(sortSameKind);
|