深入解析Chrome浏览器V8引擎优化与反优化漏洞

本文详细分析了Chrome V8引擎中因BigInt处理不当导致的优化与反优化漏洞。通过研究Ignition解释器、TurboFan简化降低阶段及反优化机制,揭示了如何利用该漏洞实现任意堆对象读取和引用伪造。文章包含完整的技术架构分析和实验验证。

现代Chrome浏览器攻击:优化与反优化

引言

2019年底,我在Azimuth Security内部会议上介绍了通过JavaScript引擎攻击Chrome的研究工作。当时我重点研究了反优化机制,特别是反优化器中的漏洞。本文基于一年前为Azimuth Security撰写的内部技术报告,经授权后公开发布。

动机

相关提交

要理解这个安全漏洞,需要深入V8内部机制。首先分析修复提交:

1
2
3
4
Fixes word64-lowered BigInt in FrameState accumulator

Bug: chromium:1016450
Change-Id: I4801b5ffb0ebea92067aa5de37e11a4e75dcd3c0

该提交修复了src/compiler/simplified-lowering.cc中的VisitFrameStateVisitStateValues函数。

漏洞概述

这是TurboFan简化降低阶段处理FrameState和StateValues节点时的bug。这些节点与反优化相关。在代码生成阶段,TurboFan使用这些节点构建反优化输入数据。当运行时跳转到反优化器时,反优化器利用这些数据重建正确的执行帧。

通过此漏洞,可以使代码生成错误构建反优化输入数据,导致反优化器实例化伪造对象,并将执行重定向到使用任意对象指针的Ignition字节码处理器。

内部机制

Ignition解释器

概述

V8使用名为Ignition的解释器,它是基于寄存器的虚拟机。许多操作码使用累加器作为隐式操作数。每个操作码都有对应的处理程序,执行字节码主要是获取当前操作码并分派到正确的处理程序。

处理程序示例

1
2
3
IGNITION_HANDLER(AddSmi, InterpreterBinaryOpAssembler) {
  BinaryOpSmiWithFeedback(&BinaryOpAssembler::Generate_AddWithFeedback);
}

调试技巧

启用ignition调试功能:

  • --trace-ignition:跟踪字节码执行
  • --trace_feedback_updates:跟踪反馈向量更新

简化降低阶段

三个阶段

  1. 截断传播阶段:向后传播截断信息
  2. 类型传播阶段:向前传播类型反馈信息
  3. 降低阶段:实际降低节点并插入转换

节点处理示例

SpeculativeNumberModulus节点为例,当两个输入都是无符号32位类型时,会调用VisitWord32TruncatingBinop,指示对前两个输入进行word32截断。

反优化机制

高级概述

反优化涉及多个V8组件:

  • 指令选择:构建FrameState和StateValues节点描述符
  • 代码生成:构建反优化输入数据(包括Translation)
  • 反优化器:运行时将优化代码帧转换为解释器帧

反优化跟踪

使用--trace-deopt可以跟踪实际的反优化过程:

1
[deoptimizing (DEOPT eager): begin ... wrong name]

案例研究:错误的BigInt重新实例化

回到简化降低

FrameState节点期望6个输入,其中累加器输入的使用信息为UseInfo::Any()。对于BigIntAsUintN节点,输出表示为kWord64,类型为BigInt

在简化降低阶段,累加器输入被替换为TypedStateValues节点,其MachineType计算为MachineType::AnyTagged()。这导致代码生成时将BigInt值当作指针处理。

实验1:读取任意堆数字

通过反优化到加法操作处理程序,可以实现任意堆数字读取:

1
2
3
4
5
6
7
8
9
let addr = BigInt(0x11111111);

function f(x) {
  let y = BigInt.asUintN(49, addr);
  try {
    var res = 1.1 + y; // 触发反优化
    return res;
  } catch(_){ return y}
}

实验2:获取任意对象引用

通过反优化到属性存储处理程序,可以实现任意对象引用:

1
2
3
4
5
6
7
8
function f(x) {
  let y = BigInt.asUintN(49, addr);
  try {
    var obj = {};
    obj[x] = y; // 违反推测触发反优化
    return obj;
  } catch(_){ return y}
}

变体分析

后续提交修复了类似问题:

  • ObjectState节点的相同bug
  • 表示转换器中的rematerialization问题
  • 内联bug(缺少参数检查)

指针压缩的影响

V8 8.0引入指针压缩,Smi和压缩指针存储为32位值。这影响了利用策略,需要调整指针处理方式。

结论

本文深入分析了V8的多个组件:Ignition解释器、TurboFan简化降低阶段和反优化机制。虽然该漏洞提供了强大的原语,但缺乏信息泄露能力,需要与其他漏洞结合使用。

特别感谢V8团队构建了如此出色的JavaScript引擎!如需反馈或问题,欢迎通过Twitter联系。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计