Chrome · V8
CVE-2025-0434
OOB in V8
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifsrc/interpreter/bytecode-generator.cc |
modified | |
Btest/mjsunit/regress/regress-374627491.js |
modified | |
Ctest/mjsunit/regress/regress-374627491.js |
modified | |
constructortest/mjsunit/regress/regress-374627491.js |
modified | |
switchtest/mjsunit/regress/regress-374627491.js |
modified |
Files Changed
src/interpreter/bytecode-generator.cctest/mjsunit/regress/regress-374627491.js
Patch
From 5c3b50c26c50e68dbedf8ff991249e75e46ef06e Mon Sep 17 00:00:00 2001 From: Shu-yu Guo <[email protected]> Date: Mon, 18 Nov 2024 16:02:28 -0800 Subject: [PATCH] [interpreter] Fix hole elision scope for switch jump tables Fixed: 374627491 Change-Id: I7b6142b45295ba795a8ae8a90692fc09e704b65d Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6032225 Reviewed-by: Leszek Swirski <[email protected]> Commit-Queue: Shu-yu Guo <[email protected]> Cr-Commit-Position: refs/heads/main@{#97274} --- diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc index 7912eba..9e22967 100644 --- a/src/interpreter/bytecode-generator.cc +++ b/src/interpreter/bytecode-generator.cc @@ -2526,6 +2526,9 @@ // Are we still using any if-else bytecodes to evaluate the switch? bool use_jumps = n_comp_cases != 0; + // Does the comparison for non-jump table jumps need an elision scope? + bool jump_comparison_needs_hole_check_elision_scope = false; + SwitchBuilder switch_builder(builder(), block_coverage_builder_, stmt, n_comp_cases, jump_table); ControlScopeForBreakable scope(this, stmt, &switch_builder); @@ -2583,6 +2586,10 @@ info.covered_cases); if (use_jumps) { + // When using a jump table, the first jump comparison is conditionally + // executed if the discriminant wasn't matched by anything in the jump + // table, and so needs its own elision scope. + jump_comparison_needs_hole_check_elision_scope = true; builder()->LoadAccumulatorWithRegister(r1); } } @@ -2603,16 +2610,14 @@ // The comparisons linearly dominate, so no need to open a new elision // scope for each one. std::optional<HoleCheckElisionScope> elider; - bool first_jump_emitted = false; for (int i = 0; i < clauses->length(); ++i) { CaseClause* clause = clauses->at(i); if (clause->is_default()) { info.default_case = i; } else if (!info.CaseExists(clause->label())) { - // The first non-default label is - // unconditionally executed, so we only need to emplace it before - // visiting the second non-default label. - if (first_jump_emitted) elider.emplace(this); + if (jump_comparison_needs_hole_check_elision_scope && !elider) { + elider.emplace(this); + } // Perform label comparison as if via '===' with tag. VisitForAccumulatorValue(clause->label()); @@ -2623,7 +2628,9 @@ #endif switch_builder.JumpToCaseIfTrue(ToBooleanMode::kAlreadyBoolean, case_compare_ctr++); - first_jump_emitted = true; + // The second and subsequent non-default comparisons are always + // conditionally executed, and need an elision scope. + jump_comparison_needs_hole_check_elision_scope = true; } } } diff --git a/test/mjsunit/regress/regress-374627491.js b/test/mjsunit/regress/regress-374627491.js new file mode 100644 index 0000000..ebb7e1d --- /dev/null +++ b/test/mjsunit/regress/regress-374627491.js @@ -0,0 +1,26 @@ +// Copyright 2024 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. + +class B { } +class C extends B { + constructor() { + let x = 0; + switch (0) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + x += this; + break; + case this: + } + } +} +assertThrows(() => { new C(); }, ReferenceError);
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/test/mjsunit/regress/regress-374627491.js b/test/mjsunit/regress/regress-374627491.js
new file mode 100644
index 0000000..ebb7e1d
--- /dev/null
+++ b/test/mjsunit/regress/regress-374627491.js
@@ -0,0 +1,26 @@
+// Copyright 2024 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.
+
+class B { }
+class C extends B {
+ constructor() {
+ let x = 0;
+ switch (0) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ x += this;
+ break;
+ case this:
+ }
+ }
+}
+assertThrows(() => { new C(); }, ReferenceError);
Loading diff…
Original Bug Report
reported by [email protected]
DCHECK failure in Handle<To> v8::internal::Cast(Handle<From>, const v8::SourceLocation &) [To = v8
Steps to reproduce the problem
- download d8-asan-linux-debug-v8-component-96641
- unzip that
- run d8 ./poc.js
Problem Description
#
# Fatal error in ../../src/objects/objects.cc, line 353
# Debug check failed: Handle<To> v8::internal::Cast(Handle<From>, const v8::SourceLocation &) [To = v8::internal::JSReceiver, From = v8::internal::Object].
#
#
#
#FailureMessage Object: 0x7bc62e2f9c60
==== C stack trace ===============================
./google-cloud-sdk/d8-asan-linux-debug-v8-component-96641/d8(__interceptor_backtrace+0x46) [0x55cbd91b17a6]
/root/google-cloud-sdk/d8-asan-linux-debug-v8-component-96641/libv8_libbase.so(v8::base::debug::StackTrace::StackTrace()+0x13) [0x7fc6317c8573]
/root/google-cloud-sdk/d8-asan-linux-debug-v8-component-96641/libv8_libplatform.so(+0x3687a) [0x7fc63171c87a]
/root/google-cloud-sdk/d8-asan-linux-debug-v8-component-96641/libv8_libbase.so(V8_Fatal(char const*, int, char const*, ...)+0x2a0) [0x7fc631792c80]
/root/google-cloud-sdk/d8-asan-linux-debug-v8-component-96641/libv8_libbase.so(+0x56d3f) [0x7fc631791d3f]
/root/google-cloud-sdk/d8-asan-linux-debug-v8-component-96641/libv8.so(v8::internal::Handle<v8::internal::JSReceiver> v8::internal::Cast<v8::internal::JSReceiver, v8::internal::Object>(v8::internal::Handle<v8::internal::Object>, v8::SourceLocation const&)+0x1b2) [0x7fc635ba53f2]
/root/google-cloud-sdk/d8-asan-linux-debug-v8-component-96641/libv8.so(v8::internal::Object::ConvertToNumeric(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>)+0x1ba) [0x7fc637bd9dba]
/root/google-cloud-sdk/d8-asan-linux-debug-v8-component-96641/libv8.so(v8::internal::Object::ToNumeric(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>)+0x3d1) [0x7fc635aacda1]
/root/google-cloud-sdk/d8-asan-linux-debug-v8-component-96641/libv8.so(+0x6acfa0d) [0x7fc6382b3a0d]
/root/google-cloud-sdk/d8-asan-linux-debug-v8-component-96641/libv8.so(v8::internal::Runtime_ToNumeric(int, unsigned long*, v8::internal::Isolate*)+0x1da) [0x7fc6382b315a]
/root/google-cloud-sdk/d8-asan-linux-debug-v8-component-96641/libv8.so(+0x3a58afd) [0x7fc63523cafd]
AddressSanitizer:DEADLYSIGNAL
=================================================================
==1914346==ERROR: AddressSanitizer: TRAP on unknown address 0x000000000000 (pc 0x7fc6317c3690 bp 0x7ffe27e76810 sp 0x7ffe27e76810 T0)
SCARINESS: 10 (signal)
#0 0x7fc6317c3690 in v8::base::OS::Abort() src/base/platform/platform-posix.cc:730:7
#1 0x7fc631792c9b in V8_Fatal(char const*, int, char const*, ...) src/base/logging.cc:215:3
#2 0x7fc631791d3e in v8::base::(anonymous namespace)::DefaultDcheckHandler(char const*, int, char const*) src/base/logging.cc:59:3
#3 0x7fc635ba53f1 in v8::internal::Handle<v8::internal::JSReceiver> v8::internal::Cast<v8::internal::JSReceiver, v8::internal::Object>(v8::internal::Handle<v8::internal::Object>, v8::SourceLocation const&) src/handles/handles-inl.h:50:3
#4 0x7fc637bd9db9 in v8::internal::Object::ConvertToNumeric(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>) src/objects/objects.cc:350:5
#5 0x7fc635aacda0 in v8::internal::Object::ToNumeric(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>) src/objects/objects-inl.h:816:10
#6 0x7fc6382b3a0c in v8::internal::__RT_impl_Runtime_ToNumeric(v8::internal::Arguments<(v8::internal::ArgumentsType)0>, v8::internal::Isolate*) src/runtime/runtime-object.cc:1231:3
#7 0x7fc6382b3159 in v8::internal::Runtime_ToNumeric(int, unsigned long*, v8::internal::Isolate*) src/runtime/runtime-object.cc:1227:1
#8 0x7fc63523cafc in Builtins_CEntry_Return1_ArgvOnStack_NoBuiltinExit setup-isolate-deserialize.cc
#9 0x7fc635454701 in Builtins_NonNumberToNumeric setup-isolate-deserialize.cc
#10 0x7fc6358490d4 in Builtins_ShiftRightLogicalHandler setup-isolate-deserialize.cc
#11 0x7fc634e5754e in Builtins_InterpreterEntryTrampoline setup-isolate-deserialize.cc
#12 0x7fc634e58162 in Builtins_InterpreterPushArgsThenFastConstructFunction setup-isolate-deserialize.cc
#13 0x7fc63587725c in Builtins_ConstructHandler setup-isolate-deserialize.cc
#14 0x7fc634e5754e in Builtins_InterpreterEntryTrampoline setup-isolate-deserialize.cc
#15 0x7fc634e4e5db in Builtins_JSEntryTrampoline setup-isolate-deserialize.cc
#16 0x7fc634e4e31e in Builtins_JSEntry setup-isolate-deserialize.cc
#17 0x7fc63659536a in v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) src/execution/simulator.h:191:12
#18 0x7fc63659903c in v8::internal::Execution::CallScript(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>) src/execution/execution.cc:517:10
#19 0x7fc635a5b263 in v8::Script::Run(v8::Local<v8::Context>, v8::Local<v8::Data>) src/api/api.cc:2140:7
#20 0x55cbd927e335 in v8::Shell::ExecuteString(v8::Isolate*, v8::Local<v8::String>, v8::Local<v8::String>, v8::Shell::ReportExceptions, v8::Global<v8::Value>*)
Summary
Type Confusion in ConvertToNumeric
Custom Questions
Type of crash:
tab
Additional Data
Category: Security
Chrome Channel: Not sure
Regression: N/A
References
On This Page