Chrome · V8
CVE-2025-7656
Integer Overflow in V8
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
V8_NODISCARDsrc/codegen/arm64/macro-assembler-arm64.h |
modified | |
ifsrc/codegen/arm64/macro-assembler-arm64.h |
modified | |
fortest/mjsunit/regress/regress-425583995.js |
modified |
Files Changed
src/codegen/arm64/macro-assembler-arm64.ccsrc/codegen/arm64/macro-assembler-arm64.htest/mjsunit/regress/regress-425583995.js
Patch
From c58fda1f0ec46429dd66c2cacf6a98fac001e4fd Mon Sep 17 00:00:00 2001
From: Victor Gomes <victorgomes@chromium.org>
Date: Fri, 27 Jun 2025 12:40:10 +0200
Subject: [PATCH] [arm64] Ensure InstructionAccurateScope is called with correct count
The scope prevents veneer pool generation. We need to pass the
correct count of instructions to CheckVeneerPool inside the scope
constructor, otherwise we might overflow the veneer distance
margin in the next check (after the scope has ended).
Fixed: 425583995
Change-Id: Iebb81898c4f7999137fc784ce6704773614c2bb5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6683635
Auto-Submit: Victor Gomes <victorgomes@chromium.org>
Commit-Queue: Victor Gomes <victorgomes@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/main@{#101089}
---
diff --git a/src/codegen/arm64/macro-assembler-arm64.cc b/src/codegen/arm64/macro-assembler-arm64.cc
index 74b995e..59c9aa6 100644
--- a/src/codegen/arm64/macro-assembler-arm64.cc
+++ b/src/codegen/arm64/macro-assembler-arm64.cc
@@ -1301,7 +1301,7 @@
const CPURegister& src2,
const CPURegister& src3) {
// Ensure that we don't unintentially modify scratch or debug registers.
- InstructionAccurateScope scope(this);
+ InstructionAccurateScope scope(this, count <= 2 ? 1 : 2);
DCHECK(AreSameSizeAndType(src0, src1, src2, src3));
DCHECK(size == src0.SizeInBytes());
@@ -1338,7 +1338,7 @@
const CPURegister& dst1, const CPURegister& dst2,
const CPURegister& dst3) {
// Ensure that we don't unintentially modify scratch or debug registers.
- InstructionAccurateScope scope(this);
+ InstructionAccurateScope scope(this, count <= 2 ? 1 : 2);
DCHECK(AreSameSizeAndType(dst0, dst1, dst2, dst3));
DCHECK(size == dst0.SizeInBytes());
@@ -1388,8 +1388,14 @@
void MacroAssembler::PushCalleeSavedRegisters() {
ASM_CODE_COMMENT(this);
+#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
+ constexpr int kInstrCount = 11;
+#else
+ constexpr int kInstrCount = 10;
+#endif
+
// Ensure that the macro-assembler doesn't use any scratch registers.
- InstructionAccurateScope scope(this);
+ InstructionAccurateScope scope(this, kInstrCount);
MemOperand tos(sp, -2 * static_cast<int>(kXRegSize), PreIndex);
@@ -1422,8 +1428,14 @@
void MacroAssembler::PopCalleeSavedRegisters() {
ASM_CODE_COMMENT(this);
+#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
+ constexpr int kInstrCount = 11;
+#else
+ constexpr int kInstrCount = 10;
+#endif
+
// Ensure that the macro-assembler doesn't use any scratch registers.
- InstructionAccurateScope scope(this);
+ InstructionAccurateScope scope(this, kInstrCount);
MemOperand tos(sp, 2 * kXRegSize, PostIndex);
diff --git a/src/codegen/arm64/macro-assembler-arm64.h b/src/codegen/arm64/macro-assembler-arm64.h
index b9ca871..84cc32e 100644
--- a/src/codegen/arm64/macro-assembler-arm64.h
+++ b/src/codegen/arm64/macro-assembler-arm64.h
@@ -2494,7 +2494,7 @@
// emitted is what you specified when creating the scope.
class V8_NODISCARD InstructionAccurateScope {
public:
- explicit InstructionAccurateScope(MacroAssembler* masm, size_t count = 0)
+ explicit InstructionAccurateScope(MacroAssembler* masm, size_t count)
: masm_(masm),
block_pool_(masm, count * kInstrSize)
#ifdef DEBUG
@@ -2502,12 +2502,11 @@
size_(count * kInstrSize)
#endif
{
+ DCHECK_GT(count, 0);
masm_->CheckVeneerPool(false, true, count * kInstrSize);
masm_->StartBlockVeneerPool();
#ifdef DEBUG
- if (count != 0) {
- masm_->bind(&start_);
- }
+ masm_->bind(&start_);
previous_allow_macro_instructions_ = masm_->allow_macro_instructions();
masm_->set_allow_macro_instructions(false);
#endif
@@ -2516,9 +2515,7 @@
~InstructionAccurateScope() {
masm_->EndBlockVeneerPool();
#ifdef DEBUG
- if (start_.is_bound()) {
- DCHECK(masm_->SizeOfCodeGeneratedSince(&start_) == size_);
- }
+ DCHECK(masm_->SizeOfCodeGeneratedSince(&start_) == size_);
masm_->set_allow_macro_instructions(previous_allow_macro_instructions_);
#endif
}
diff --git a/test/mjsunit/regress/regress-425583995.js b/test/mjsunit/regress/regress-425583995.js
new file mode 100644
index 0000000..eaed312c
--- /dev/null
+++ b/test/mjsunit/regress/regress-425583995.js
@@ -0,0 +1,33 @@
+// 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
+
+const topLevel = %GetFunctionForCurrentFrame();
+%PrepareFunctionForOptimization(topLevel);
+
+function g(
+ // Too many arguments to fit here manually and overflow int32 calculation
+) {
+ return arguments.length + 1;
+}
+
+var num_args = 40000; // large number to cause overflow
+
+// Construct argument list string
+var argsList = "";
+for (var i = 0; i < num_args; i++) argsList += "a" + i + ",";
+argsList = argsList.slice(0, -1);
+
+// Construct function source to return sum of all args
+var body = "return arguments.length + 1;";
+
+// Create large argument function dynamically
+var bigArgFunc = new Function(argsList, body);
+
+// Call many times to trigger OSR in v8 maglev
+for (var i = 0; i < 2; i++) {
+ bigArgFunc(0);
+ %OptimizeOsr();
+}
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/test/mjsunit/regress/regress-425583995.js b/test/mjsunit/regress/regress-425583995.js
new file mode 100644
index 0000000..eaed312c
--- /dev/null
+++ b/test/mjsunit/regress/regress-425583995.js
@@ -0,0 +1,33 @@
+// 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
+
+const topLevel = %GetFunctionForCurrentFrame();
+%PrepareFunctionForOptimization(topLevel);
+
+function g(
+ // Too many arguments to fit here manually and overflow int32 calculation
+) {
+ return arguments.length + 1;
+}
+
+var num_args = 40000; // large number to cause overflow
+
+// Construct argument list string
+var argsList = "";
+for (var i = 0; i < num_args; i++) argsList += "a" + i + ",";
+argsList = argsList.slice(0, -1);
+
+// Construct function source to return sum of all args
+var body = "return arguments.length + 1;";
+
+// Create large argument function dynamically
+var bigArgFunc = new Function(argsList, body);
+
+// Call many times to trigger OSR in v8 maglev
+for (var i = 0; i < 2; i++) {
+ bigArgFunc(0);
+ %OptimizeOsr();
+}
Loading diff…
Original Bug Report
reported by fa...@gmail.com
Debug check failed: pc_offset() < unresolved_branches_first_limit()
#
# Fatal error in ../../src/codegen/arm64/assembler-arm64.cc, line 4917
# Debug check failed: pc_offset() < unresolved_branches_first_limit().
#
#
#
#FailureMessage Object: 0x16d305468
==== C stack trace ===============================
0 libv8_libbase.dylib 0x0000000104389ee8 v8::base::debug::StackTrace::StackTrace() + 24
1 libv8_libplatform.dylib 0x00000001043c67c8 v8::platform::(anonymous namespace)::PrintStackTrace() + 116
2 libv8_libbase.dylib 0x000000010436d5cc V8_Fatal(char const*, int, char const*, ...) + 352
3 libv8_libbase.dylib 0x000000010436ced0 v8::base::SetFatalFunction(void (*)(char const*, int, char const*)) + 0
4 libv8.dylib 0x000000010c96b6dc v8::internal::Assembler::CheckVeneerPool(bool, bool, unsigned long) + 232
5 libv8.dylib 0x000000010aeb3208 v8::internal::Assembler::Emit(unsigned int) + 132
6 libv8.dylib 0x000000010c96bc8c v8::internal::Assembler::EmitVeneers(bool, bool, unsigned long) + 908
7 libv8.dylib 0x000000010c977f14 v8::internal::MacroAssembler::PushHelper(int, int, v8::internal::CPURegister const&, v8::internal::CPURegister const&, v8::internal::CPURegister const&, v8::internal::CPURegister const&) + 136
8 libv8.dylib 0x000000010c3a8d48 void v8::internal::maglev::detail::PushIteratorReverse<v8::internal::maglev::RepeatIterator<v8::internal::Register>>(v8::internal::maglev::MaglevAssembler*, v8::base::iterator_range<v8::internal::maglev::RepeatIterator<v8::internal::Register>>) + 324
9 libv8.dylib 0x000000010c3a8aec void v8::internal::maglev::detail::PushIteratorReverse<std::__Cr::reverse_iterator<v8::internal::maglev::Input*>, v8::base::iterator_range<v8::internal::maglev::RepeatIterator<v8::internal::Register>>>(v8::internal::maglev::MaglevAssembler*, v8::base::iterator_range<std::__Cr::reverse_iterator<v8::internal::maglev::Input*>>, v8::base::iterator_range<v8::internal::maglev::RepeatIterator<v8::internal::Register>>) + 108
10 libv8.dylib 0x000000010c35ff7c v8::internal::maglev::CallKnownJSFunction::GenerateCode(v8::internal::maglev::MaglevAssembler*, v8::internal::maglev::ProcessingState const&) + 356
11 libv8.dylib 0x000000010bf32110 v8::internal::maglev::ProcessResult v8::internal::maglev::NodeMultiProcessor<v8::internal::maglev::(anonymous namespace)::SafepointingNodeProcessor, v8::internal::maglev::(anonymous namespace)::MaglevCodeGeneratingNodeProcessor>::Process<v8::internal::maglev::CallKnownJSFunction>(v8::internal::maglev::CallKnownJSFunction*, v8::internal::maglev::ProcessingState const&) + 1160
12 libv8.dylib 0x000000010beef718 v8::internal::maglev::MaglevCodeGenerator::EmitCode() + 7092
13 libv8.dylib 0x000000010beed8d4 v8::internal::maglev::MaglevCodeGenerator::Assemble() + 48
14 libv8.dylib 0x000000010bfc82f0 v8::internal::maglev::MaglevCompiler::Compile(v8::internal::LocalIsolate*, v8::internal::maglev::MaglevCompilationInfo*) + 5532
15 libv8.dylib 0x000000010c0e514c v8::internal::maglev::MaglevCompilationJob::ExecuteJobImpl(v8::internal::RuntimeCallStats*, v8::internal::LocalIsolate*) + 96
16 libv8.dylib 0x000000010af9587c v8::internal::OptimizedCompilationJob::ExecuteJob(v8::internal::RuntimeCallStats*, v8::internal::LocalIsolate*) + 160
17 libv8.dylib 0x000000010c0e718c v8::internal::maglev::MaglevConcurrentDispatcher::JobTask::Run(v8::JobDelegate*) + 856
18 libv8_libplatform.dylib 0x00000001043c52cc v8::platform::DefaultJobWorker::Run() + 292
19 libv8_libplatform.dylib 0x00000001043c7d0c v8::platform::DefaultWorkerThreadsTaskRunner::WorkerThread::Run() + 276
20 libv8_libbase.dylib 0x00000001043882ec v8::base::ThreadEntry(void*) + 180
21 libsystem_pthread.dylib 0x00000001911fac0c _pthread_start + 136
22 libsystem_pthread.dylib 0x00000001911f5b80 thread_start + 8
zsh: trace trap ./v8/v8/out/debug/d8 poc.js
VERSION
V8 version 13.9.0 (candidate)
REPRODUCTION CASE
Build: v8 Debug build, MacOS arm64
Run: ./d8 poc.js
Reporter credit: Shaheen Fazim
References
On This Page