CVE-2025-7656
先找到漏洞修复前的v8版本,e01c1a9108edddfcda2627a2d93565ee89867ff3
,然后开始学习尝试复现。
漏洞发生在 V8 的 ARM64 架构后端代码生成阶段 ,特别是在处理宏指令(macro-assembler)时,没有正确计算指令数量 ,导致宏汇编器在某些情况下可能使用了错误的寄存器或跳转指令偏移,从而引发潜在的 内存破坏或控制流劫持 。
🧩 一、背景:V8 中的宏汇编器(MacroAssembler)
V8 在生成机器码时,使用了一个叫做 MacroAssembler 的抽象层,用于编写更高级的汇编指令(宏指令),这些宏指令最终会被展开为具体的机器指令。
在 ARM64 架构中,有些指令需要多个微指令(micro-ops)来实现,比如加载大立即数(ldr x0, =0x12345678
),这在底层可能需要多个指令来完成。
为了确保宏指令不会意外使用"临时寄存器"或生成的指令数量不一致,V8 引入了一个叫做:
InstructionAccurateScope
的机制,它用于限制宏汇编器在某个代码块内只能使用指定数量的指令空间。
🧱 二、问题核心:InstructionAccurateScope
的不准确使用
在漏洞发生前,InstructionAccurateScope
的构造函数是这样定义的:
arduino
explicit InstructionAccurateScope(MacroAssembler* masm, size_t count = 0)
也就是说,count
是可选参数,默认为 0。当 count == 0
时,它表示"不确定需要多少指令空间",但此时汇编器仍然会继续生成代码。
这就带来一个问题:
当宏汇编器在生成代码时,没有准确知道将要生成多少条指令,就无法正确预留空间,导致跳转偏移、寄存器使用等操作出错。
特别是在以下函数中:
PushCalleeSavedRegisters()
PopCalleeSavedRegisters()
MacroAssembler::LoadLiteral()
这些函数在生成代码时,没有正确指定将要生成多少条指令,导致:
- 汇编器无法正确预留跳转空间
- 指令偏移计算错误
- 可能导致越界写入或控制流劫持(尤其是启用 CFI 的情况下)
🧪 三、PoC 如何触发漏洞?
PoC 通过以下方式触发:
ini
var num_args = 40000;
var argsList = "";
for (var i = 0; i < num_args; i++) argsList += "a" + i + ",";
argsList = argsList.slice(0, -1);
var bigArgFunc = new Function(argsList, body);
for (var i = 0; i < 2; i++) {
bigArgFunc(0);
%OptimizeOsr();
}
关键点:
-
创建一个带有 40000 个参数 的函数。
-
这样会触发 V8 的 JIT 编译器(Maglev) 和 OSR(On-Stack Replacement) 。
-
在编译过程中,V8 需要处理大量参数的函数调用栈帧,涉及寄存器保存、跳转、栈操作等。
-
这些操作会调用到
PushCalleeSavedRegisters()
、PopCalleeSavedRegisters()
等函数。 -
如果这些函数中使用的
InstructionAccurateScope
没有正确指定count
,就会导致:- 指令偏移错误
- 寄存器使用冲突
- 可能导致跳转到错误地址,从而执行任意代码
🔧 四、修复详解:为什么这样修复可以解决问题?
✅ 1. 强制传入 count
参数
修复前:
arduino
explicit InstructionAccurateScope(MacroAssembler* masm, size_t count = 0)
修复后:
arduino
explicit InstructionAccurateScope(MacroAssembler* masm, size_t count)
✅ 强制要求开发者在使用
InstructionAccurateScope
时,必须显式传入将要生成的指令数量。
这样做的好处:
- 确保编译器知道要预留多少指令空间
- 防止因指令数量不一致导致跳转偏移错误
- 避免在启用 CFI 的情况下,跳转目标偏移不正确,从而被攻击者利用
✅ 2. 为 PushCalleeSavedRegisters()
等函数指定 kInstrCount
修复前:
arduino
InstructionAccurateScope scope(this);
修复后:
arduino
constexpr int kInstrCount = 10; // 或 11,取决于是否启用 CFI
InstructionAccurateScope scope(this, kInstrCount);
✅ 显式告诉汇编器,这个函数将生成 多少条指令
为什么这很重要?
-
在启用 CFI 的情况下,某些函数(如
PushCalleeSavedRegisters()
)会多出一条额外的指令(用于完整性检查)。 -
如果没有预留足够的空间,就会导致:
- 跳转地址偏移错误
- 寄存器保存/恢复错位
- 最终可能导致任意代码执行
✅ 3. 删除 count == 0
的判断,确保每次绑定地址
修复前:
scss
if (count != 0) {
masm_->bind(&start_);
}
修复后:
scss
masm_->bind(&start_);
✅ 即使
count == 0
,也绑定地址
这样做的目的是:
- 确保宏汇编器始终知道当前代码位置
- 避免因地址绑定不一致导致的跳转错误
- 防止在启用 CFI 的情况下,跳转目标偏移不一致
🧠 五、总结:漏洞本质与修复逻辑
问题 | 原因 | 修复方式 | 修复效果 |
---|---|---|---|
指令计数不准确 | 使用 InstructionAccurateScope 时未指定 count |
强制必须传入 count |
确保预留正确指令空间 |
指令偏移错误 | 某些函数未根据 CFI 启用情况预留足够指令 | 根据 CFI 启用定义 kInstrCount |
防止跳转目标偏移错误 |
地址绑定不一致 | 只有 count != 0 才绑定地址 |
总是绑定地址 | 防止地址偏移不一致 |
🧨 六、攻击者如何利用?
如果攻击者能控制函数参数数量(如 PoC 中的 40000 个参数),并触发 JIT 编译和 OSR 优化,就可能:
- 触发宏汇编器在生成代码时使用错误的寄存器
- 生成错误的跳转指令偏移
- 在启用 CFI 的情况下,绕过完整性检查
- 最终实现 控制流劫持 或 任意代码执行
下周末如果有时间复现一下,上个漏洞CVE-2025-6554复现不是鸽了,可能是我环境问题,复现总有点问题之后再研究一下。