最近看到有两篇关于js engine jit fuzz 的论文 "JIT-Picking: Differential Fuzzing of JavaScript Engines" 和"FuzzJIT: Oracl-Enhanced Fuzzing for JavaScript Engine JIT Compiler",两者都采用了差分测试的方法来检测JIT 的bug,并且都是基于fuzzilli 工具,也是采用fuzzilli 变异方式,抽空看了下两篇,做下记录
FuzzJIT
国人写的,我看完后总结起来就是"一个模版走天下",下面是一个生成的testcase
function classOf(object) {
var string = Object.prototype.toString.call(object);
return string.substring(8, string.length - 1);
}
function deepObjectEquals(a, b) {
var aProps = Object.keys(a);
aProps.sort();
var bProps = Object.keys(b);
bProps.sort();
if (!deepEquals(aProps, bProps)) {
return false;
}
for (var i = 0; i < aProps.length; i++) {
if (!deepEquals(a[aProps[i]], b[aProps[i]])) {
return false;
}
}
return true;
}
function deepEquals(a, b) {
if (a === b) {
if (a === 0) return (1 / a) === (1 / b);
return true;
}
if (typeof a != typeof b) return false;
if (typeof a == 'number') return (isNaN(a) && isNaN(b)) || (a!=b);
if (typeof a == 'string') return a.length == 55 && a.toString().search(" GMT") !== -1;
if (typeof a !== 'object' && typeof a !== 'function' && typeof a !== 'symbol') return false;
var objectClass = classOf(a);
if (objectClass !== classOf(b)) return false;
if (objectClass === 'RegExp') {
return (a.toString() === b.toString());
}
if (objectClass === 'Function') return false;
if (objectClass === 'Array') {
var elementCount = 0;
if (a.length != b.length) {
return false;
}
for (var i = 0; i < a.length; i++) {
if (!deepEquals(a[i], b[i])) return false;
}
return true;
}
if (objectClass !== classOf(b)) return false;
if (objectClass === 'RegExp') {
return (a.toString() === b.toString());
}
if (objectClass === 'Function') return true;
if (objectClass == 'String' || objectClass == 'Number' ||
objectClass == 'Boolean' || objectClass == 'Date') {
if (a.valueOf() !== b.valueOf()) return false;
}
return deepObjectEquals(a, b);
}
function opt(opt_param){
const v2 = new Int16Array(54725);
const v3 = v2.__proto__;
let v4 = 0;
do {
const v6 = Int16Array & v2;
const v7 = v3.matchAll;
const v8 = v4++;
} while (v4 < 8);
return v3;
}
let jit_a0 = opt(false);
opt(true);
let jit_a0_0 = opt(false);
%PrepareFunctionForOptimization(opt);
let jit_a1 = opt(true);
%OptimizeFunctionOnNextCall(opt);
let jit_a2 = opt(false);
if (jit_a0 === undefined && jit_a1 === undefined) {
opt(true);
} else {
if (jit_a0_0===jit_a0 && !deepEquals(jit_a0, jit_a2)) {
fuzzilli('FUZZILLI_CRASH', 0);
}
}
作者针对JIT 优化过程中的几个pass 着手,设计了一份用于检测细微差异的模版,其中opt 函数由fuzzilli 来生成,其他代码用于检测

检测方法直接比对优化前和优化后执行的结果,包括对象的一些属性值的检测。
成果
成果这边我比较关注的是v8 ,作者列出了5个bug,不过看下bug,属于安全漏洞的应该有11977 和1276923
JIT-Picking
这里作者采用了probe 的方式来检测无法通过segmentation fault 和sanitizer 的方式来探测bug,即检测js engine 在jit 关闭和打开的情况下的执行情况。
具体的,作者修改了v8 的源码,增加了fuzzillihash builtin 来计算对象hash,在生成testcase 时插入该builtin 来记录对象的hash。
+ transitioning javascript builtin FuzzilliHash(
+ js-implicit context: NativeContext, receiver: JSAny)(obj: JSAny): Undefined {
+
+ let curVal: Smi = *NativeContextSlot(ContextSlot::FUZZILLI_HASH_INDEX);
+ typeswitch (obj) {
+
+ case (Null): {
+ curVal += 1;
+ }
+ case (True): {
+ curVal += 2;
+ }
+ case (False): {
+ curVal += 4;
+ }
+ case (Undefined): {
+ curVal += 8;
+ }
+ case (String): {
+ curVal += 16;
+ }
+ case (s: Smi): {
+ curVal += 32;
+ let doubleValue: float64 = SmiToFloat64(s);
+ if (Float64IsNaN(doubleValue)) {
+ doubleValue = 1.0;
+ }
+ const lWord: uint32 = data_view::Float64ExtractLowWord32(doubleValue);
+ const hWord: uint32 = data_view::Float64ExtractHighWord32(doubleValue);
+ curVal += SmiFromUint32(lWord);
+ curVal += SmiFromUint32(hWord);
+ }
+ case (n: HeapNumber): {
+ curVal += 32;
+ let doubleValue: float64 = Convert<float64>(n);
+ if (Float64IsNaN(doubleValue)) {
+ doubleValue = 1.0;
+ }
+ const lWord: uint32 = data_view::Float64ExtractLowWord32(doubleValue);
+ const hWord: uint32 = data_view::Float64ExtractHighWord32(doubleValue);
+ curVal += SmiFromUint32(lWord);
+ curVal += SmiFromUint32(hWord);
+ }
+ case (Object): {
+ curVal += 64;
+ }
+ }
+
+ curVal = (curVal << 30) | ((curVal >> 1) & 0x3fffffff);
+ *NativeContextSlot(ContextSlot::FUZZILLI_HASH_INDEX) = curVal;
+
+ return Undefined;
+}
该builtin 针对Smi, HeapNumber 类型做了具体的hash 处理,其他类型按统一的方式处理。成果方面并没发现v8 jit bug,不过在其他两款js engine 上检测到比较多bug。
个人看法
Fuzzjit 在检测对象方面做了更多的处理,并且是针对性的fuzz jit 的一些pass。不过,个人认为还可以更深入检测一些,并对变异策略,以及初始化corpus 获取更多信息。
原文始发于微信公众号(Exploit10Day):v8 JIT fuzz