CVE-2025-5280
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
forsrc/compiler/turboshaft/late-load-elimination-reducer.cc |
modified |
Files Changed
src/compiler/turboshaft/late-load-elimination-reducer.ccsrc/compiler/turboshaft/snapshot-table-opindex.hsrc/compiler/turboshaft/store-store-elimination-phase.ccsrc/flags/flag-definitions.htest/mjsunit/turboshaft/regress-417169470-1.jstest/mjsunit/turboshaft/regress-417169470-2.jstest/mjsunit/turboshaft/regress-417169470-3.jstest/mjsunit/turboshaft/regress-417169470-4.js
Patch
From 37d6fa3f39e17bf46d1cdf340e666cbd3ff976b3 Mon Sep 17 00:00:00 2001 From: Darius Mercadier <[email protected]> Date: Mon, 19 May 2025 14:48:48 +0200 Subject: [PATCH] [turboshaft] Fix map-based alias analysis in Late Load Elimination Fixed: 417169470 Change-Id: I589f6667ce3b26b07a4dffa707ee78f9d642d409 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6564404 Reviewed-by: Marja Hölttä <[email protected]> Commit-Queue: Darius Mercadier <[email protected]> Cr-Commit-Position: refs/heads/main@{#100349} --- diff --git a/src/compiler/turboshaft/late-load-elimination-reducer.cc b/src/compiler/turboshaft/late-load-elimination-reducer.cc index 7b21b84..e70e556 100644 --- a/src/compiler/turboshaft/late-load-elimination-reducer.cc +++ b/src/compiler/turboshaft/late-load-elimination-reducer.cc @@ -409,11 +409,17 @@ non_aliasing_objects_.Set(value, false); } - // If we just stored a map, invalidate the maps for this base. + // If we just stored a map, invalidate all object_maps_. if (store.offset == HeapObject::kMapOffset && !store.index().valid()) { - if (object_maps_.HasKeyFor(store.base())) { - TRACE(">> Wiping map\n"); - object_maps_.Set(store.base(), MapMaskAndOr{}); + // TODO(dmercadier): can we only do this for objects that are potentially + // aliasing with the `base` (based on their maps and the maps of `base`)? + // Also, it might be worth to record a new map if this is actually a map + // store. + // TODO(dmercadier): do this only if `value` is a Constant with kind + // kHeapObject, since all map stores should store a known constant maps. + TRACE(">> Wiping all maps\n"); + for (auto it : object_maps_) { + object_maps_.Set(it.second, MapMaskAndOr{}); } } } diff --git a/src/compiler/turboshaft/snapshot-table-opindex.h b/src/compiler/turboshaft/snapshot-table-opindex.h index 733ef19..496065d 100644 --- a/src/compiler/turboshaft/snapshot-table-opindex.h +++ b/src/compiler/turboshaft/snapshot-table-opindex.h @@ -60,6 +60,9 @@ return std::nullopt; } + auto begin() { return indices_to_keys_.begin(); } + auto end() { return indices_to_keys_.end(); } + private: Key GetOrCreateKey(OpIndex idx) { auto it = indices_to_keys_.find(idx); diff --git a/src/compiler/turboshaft/store-store-elimination-phase.cc b/src/compiler/turboshaft/store-store-elimination-phase.cc index 1204683..ec00650 100644 --- a/src/compiler/turboshaft/store-store-elimination-phase.cc +++ b/src/compiler/turboshaft/store-store-elimination-phase.cc @@ -18,6 +18,9 @@ namespace v8::internal::compiler::turboshaft { void StoreStoreEliminationPhase::Run(PipelineData* data, Zone* temp_zone) { + UnparkedScopeIfNeeded unparked_scope( + data->broker(), v8_flags.turboshaft_trace_load_elimination); + turboshaft::CopyingPhase< LoopStackCheckElisionReducer, StoreStoreEliminationReducer, LateLoadEliminationReducer, MachineOptimizationReducer, diff --git a/src/flags/flag-definitions.h b/src/flags/flag-definitions.h index cf1e168..cdf3b2b 100644 --- a/src/flags/flag-definitions.h +++ b/src/flags/flag-definitions.h @@ -1644,6 +1644,8 @@ "trace emitted Turboshaft instructions") DEFINE_BOOL_READONLY(turboshaft_trace_intermediate_reductions, false, "trace intermediate Turboshaft reduction steps") +DEFINE_BOOL_READONLY(turboshaft_trace_load_elimination, false, + "trace Turboshaft's late load elimination") #endif // DEBUG DEFINE_BOOL(profile_guided_optimization, true, "profile guided optimization") diff --git a/test/mjsunit/turboshaft/regress-417169470-1.js b/test/mjsunit/turboshaft/regress-417169470-1.js new file mode 100644 index 0000000..ad0dd39 --- /dev/null +++ b/test/mjsunit/turboshaft/regress-417169470-1.js @@ -0,0 +1,24 @@ +// Copyright 2025 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 --turbofan + +function foo(a, b, c) { + a.x = 42; + b.y = 99; // Transitioning store + c.x = 17; + return a.x; +} + +// Using a constructor so that we don't go out of object when creating the .y +// property in foo. +function MyObject() { this.x = 27; } +let o1 = new MyObject(); +let o2 = new MyObject(); + +%PrepareFunctionForOptimization(foo); +assertEquals(17, foo(o1, o1, o1)); + +%OptimizeFunctionOnNextCall(foo); +assertEquals(17, foo(o2, o2, o2)); diff --git a/test/mjsunit/turboshaft/regress-417169470-2.js b/test/mjsunit/turboshaft/regress-417169470-2.js new file mode 100644 index 0000000..f5ec903 --- /dev/null +++ b/test/mjsunit/turboshaft/regress-417169470-2.js @@ -0,0 +1,21 @@ +// Copyright 2025 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 --turbofan + +function foo(a, b, c) { + a.x = 42; + b.y = 99; // Transitioning store + c.x = 17; + return a.x; +} + +let o1 = { x : 27 }; +let o2 = { x : 27 }; + +%PrepareFunctionForOptimization(foo); +assertEquals(17, foo(o1, o1, o1)); + +%OptimizeFunctionOnNextCall(foo); +assertEquals(17, foo(o2, o2, o2)); diff --git a/test/mjsunit/turboshaft/regress-417169470-3.js b/test/mjsunit/turboshaft/regress-417169470-3.js new file mode 100644 index 0000000..064946c --- /dev/null +++ b/test/mjsunit/turboshaft/regress-417169470-3.js @@ -0,0 +1,31 @@ +// Copyright 2025 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 --turbofan + +function foo(a, b, c) { + a.x = 42; + b.y = 99; // Transitioning store + c.x = 17; + return a.x; +} + +let o1 = { x : 27 }; +// If we add additional properties, we need to add at least 2 so that `b.y = 99` +// doesn't end up at offset 12, because that's also the offset of .x (which is +// in-object rather than out-of-object), and because we don't have map +// information for backing stores in Late load elimination, we'll assume that +// the store at b.y can alias with the a.x that was previously loaded. +o1.unused1 = 12; +o1.unused2 = 12; + +let o2 = { x : 27 }; +o2.unused1 = 12; +o2.unused2 = 12; + +%PrepareFunctionForOptimization(foo); +assertEquals(17, foo(o1, o1, o1)); + +%OptimizeFunctionOnNextCall(foo); +assertEquals(17, foo(o2, o2, o2)); diff --git a/test/mjsunit/turboshaft/regress-417169470-4.js b/test/mjsunit/turboshaft/regress-417169470-4.js new file mode 100644 index 0000000..d632a8f --- /dev/null +++ b/test/mjsunit/turboshaft/regress-417169470-4.js @@ -0,0 +1,31 @@ +// Copyright 2025 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 --turbofan + +function opt() { + const nop = 0; + const empty = {}; + const a = {p1: 42.42}; + + function foo(b) { + nop; + + a.p4 = 42; + b.p2 = 42; + b.p5 = empty; + a.p6 = 41.414; + } +
Regression Test / PoC
diff --git a/test/mjsunit/turboshaft/regress-417169470-1.js b/test/mjsunit/turboshaft/regress-417169470-1.js
new file mode 100644
index 0000000..ad0dd39
--- /dev/null
+++ b/test/mjsunit/turboshaft/regress-417169470-1.js
@@ -0,0 +1,24 @@
+// Copyright 2025 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 --turbofan
+
+function foo(a, b, c) {
+ a.x = 42;
+ b.y = 99; // Transitioning store
+ c.x = 17;
+ return a.x;
+}
+
+// Using a constructor so that we don't go out of object when creating the .y
+// property in foo.
+function MyObject() { this.x = 27; }
+let o1 = new MyObject();
+let o2 = new MyObject();
+
+%PrepareFunctionForOptimization(foo);
+assertEquals(17, foo(o1, o1, o1));
+
+%OptimizeFunctionOnNextCall(foo);
+assertEquals(17, foo(o2, o2, o2));
diff --git a/test/mjsunit/turboshaft/regress-417169470-2.js b/test/mjsunit/turboshaft/regress-417169470-2.js
new file mode 100644
index 0000000..f5ec903
--- /dev/null
+++ b/test/mjsunit/turboshaft/regress-417169470-2.js
@@ -0,0 +1,21 @@
+// Copyright 2025 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 --turbofan
+
+function foo(a, b, c) {
+ a.x = 42;
+ b.y = 99; // Transitioning store
+ c.x = 17;
+ return a.x;
+}
+
+let o1 = { x : 27 };
+let o2 = { x : 27 };
+
+%PrepareFunctionForOptimization(foo);
+assertEquals(17, foo(o1, o1, o1));
+
+%OptimizeFunctionOnNextCall(foo);
+assertEquals(17, foo(o2, o2, o2));
diff --git a/test/mjsunit/turboshaft/regress-417169470-3.js b/test/mjsunit/turboshaft/regress-417169470-3.js
new file mode 100644
index 0000000..064946c
--- /dev/null
+++ b/test/mjsunit/turboshaft/regress-417169470-3.js
@@ -0,0 +1,31 @@
+// Copyright 2025 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 --turbofan
+
+function foo(a, b, c) {
+ a.x = 42;
+ b.y = 99; // Transitioning store
+ c.x = 17;
+ return a.x;
+}
+
+let o1 = { x : 27 };
+// If we add additional properties, we need to add at least 2 so that `b.y = 99`
+// doesn't end up at offset 12, because that's also the offset of .x (which is
+// in-object rather than out-of-object), and because we don't have map
+// information for backing stores in Late load elimination, we'll assume that
+// the store at b.y can alias with the a.x that was previously loaded.
+o1.unused1 = 12;
+o1.unused2 = 12;
+
+let o2 = { x : 27 };
+o2.unused1 = 12;
+o2.unused2 = 12;
+
+%PrepareFunctionForOptimization(foo);
+assertEquals(17, foo(o1, o1, o1));
+
+%OptimizeFunctionOnNextCall(foo);
+assertEquals(17, foo(o2, o2, o2));
diff --git a/test/mjsunit/turboshaft/regress-417169470-4.js b/test/mjsunit/turboshaft/regress-417169470-4.js
new file mode 100644
index 0000000..d632a8f
--- /dev/null
+++ b/test/mjsunit/turboshaft/regress-417169470-4.js
@@ -0,0 +1,31 @@
+// Copyright 2025 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 --turbofan
+
+function opt() {
+ const nop = 0;
+ const empty = {};
+ const a = {p1: 42.42};
+
+ function foo(b) {
+ nop;
+
+ a.p4 = 42;
+ b.p2 = 42;
+ b.p5 = empty;
+ a.p6 = 41.414;
+ }
+
+ a.p3 = 42;
+ a.p4 = 42;
+
+ for (let i = 0; i < 300; i++) {
+ foo(a);
+ }
+}
+
+opt();
+opt();
+opt();
Original Bug Report
V8 Turboshaft Late Load Elimination Aliasing bug leads to Memory Corruption
Steps to reproduce the problem
- ./d8 –expose-externalize-string –omit-quit –allow-natives-syntax –fuzzing –jit-fuzzing –future –harmony –js-staging –wasm-staging –wasm-fast-api –expose-fast-api –turbolev –shared-string-table poc.js
Problem Description
I’ll analyze it and upload it soon.
poc.js
function opt(){
function v3() {
const v4 = {"B":42};
}
const v2 = {};
const v6 = {"C":42.42};
function v10(v11) {
function v13() {
v6.e = 41.414;
for (let v19 = 0; v19 < 100; v19++) {
//const v4 = {"B":42};
v3();
}
}
v6.d = 42;
v11.b = 42;
v11.f = v2;
for (let v26 = 0; v26 < 100; v26++) {
v13();
}
}
v6.c = 42;
v6.d = v2;
for (let v36 = 0; v36 < 1000; v36++) {
v10(v6);
}
}
opt();
opt();
opt();
Summary
V8 Turbolev Memory Corruption
Custom Questions
Crash state:
./d8 –expose-externalize-string –omit-quit –allow-natives-syntax –fuzzing –jit-fuzzing –future –harmony –js-staging –wasm-staging –wasm-fast-api –expose-fast-api –turbolev –shared-string-table test3.js [COV] no shared memory bitmap available, skipping [COV] edge counters initialized. Shared memory: (null) with 1520960 edges V8 is running with experimental features enabled. Stability and security will suffer. Received signal 11 SEGV_ACCERR 7e8a00000014
==== C stack trace ===============================
./d8(___interceptor_backtrace+0x46)[0x5a98942fd856] ./d8(+0x26e5959)[0x5a98947a5959] /lib/x86_64-linux-gnu/libc.so.6(+0x42520)[0x78df3e842520] [0x5a98e0002849] [end of stack trace] Segmentation fault
Reporter credit:
un3xploitable
Additional Data
Category: Security
Chrome Channel: Not sure
Regression: N/A