@
最近在学习 Maglev
相关知识,然后看了一些与其相关的 CVE
,感觉该漏洞比较容易复现,所以先打算复现一下,本文还是主要分析漏洞产生的原因,基础知识笔者会简单说一说,更多的还是需要读者自己去学习
这里说一下为什么笔者不愿意在漏洞分析中写过多的前置知识,因为笔者认为读者都已经开始复现漏洞了,那么对基础知识应当是有一定的了解了,并且笔者的基础也比较差,所以不希望误人子弟,网上的资料很多,自己学学就 OK 啦
new
关键字: new func()
效果为:
这里给一个示例代码:
new.target
这里自行看资料
Reflect.construct(target, argument, new_target)
函数以 target
为构造函数创建对象,这里 new_target
提供原型,然后行为跟 new func()
类似
上面的知识都比较简单,所以也不想细说了,如果读者不是很清楚的话,请自行查阅下相关资料吧,这里主要关注 JS
引擎层面的实现
对于默认对象,其是通过 FastNewObject
函数创建的,其调用链如下:
先来看看 TF_BUILTIN(FastNewObject, ConstructorBuiltinsAssembler)
:
该函数比较简单,其主要就是调用了 ConstructorBuiltinsAssembler::FastNewObject
函数:
可以看到 ConstructorBuiltinsAssembler::FastNewObject
分为快速路径和慢速路径:
这里的快速路径可能有点奇怪?因为这里 target
才是构造函数,所以默认对象的 map
再怎么说也不应该与 new_target
的 initial_map
相同,但这其实是一个优化,其会将 target
的 initial_map
和 new_target
的 prototype
缓存在 new_target
的 initial_map
域,这个后面再说
然后可以看到走快速路径是存在两个条件的,不满足则会走慢速路径 Runtime::kNewObjec
:
可以看到其直接调用了 JSObject::New
函数:
在 【1】
处会调用 JSFunction::GetDerivedMap
函数,这里的 constructor
传入的是 target
:
可以看到其会调用 FastInitializeDerivedMap
为 new_target
创建 initial_map
:
可以看到在 【2】
处设置了 new_target
的 initial_map
为 map
,但是修改了 prototype
为 new_target
的 prototype
, constructor
为 target
。而该 map
在 【1】
处是通过复制 constructor_initial_map
来的,看到这里可能就明白了之前快速路径的逻辑
所以在快速路径中,当 new_target.initial_map.constructor = target
时,则说明 new_target.initial_map
与 target.initial_map
是相同的,所以这里就会直接使用 new_target.initial_map
OK,以上就是后面漏洞分析需要的一些基础知识
还是先从 patch 入手:
从补丁打的位置可以知道该漏洞应该发生在 Maglev
的图构建阶段,并且其主要打在了 MaglevGraphBuilder::VisitFindNonDefaultConstructorOrConstruct
函数中,根据函数名大概知道其主要就是处理 FindNonDefaultConstructorOrConstruct
字节码的,而该操作的功能为“寻找非默认构造函数”,这里结合 chatGPT
食用:
在 V8 引擎中,FindNonDefaultConstructorOrConstruct 字节码指令用于查找非默认构造函数或构造器函数。这个字节码指令在 JavaScript 代码中的类构造过程中使用。
当在 JavaScript 中创建一个类并调用 new 关键字来实例化对象时,V8 引擎会执行相应的字节码指令序列。其中,FindNonDefaultConstructorOrConstruct 字节码指令用于查找适当的构造函数或构造器函数。
具体而言,该指令会检查类的原型链以查找适合的构造函数。它首先尝试查找类自身的 constructor 属性,如果找到则使用该构造函数。否则,它会沿着原型链向上查找,直到找到一个非默认构造函数或构造器函数。
这个过程是为了确保在类继承链中正确地选择构造函数,以便在实例化对象时执行相应的初始化逻辑。
所以可以写出如下代码去生成目标字节码:
来看下 B
产生的字节码:
可以看到这里确实生成了 FindNonDefaultConstructorOrConstruct
字节码,其实可以把它看成一种优化,其会遍历 B
的原型链,尽量的忽略哪些默认构造函数,如果最后到达原型链顶层,则调用 FastNewObject
创建默认对象
现在我们回到主要的补丁代码:
可以看到这里的补丁仅仅对 new_target
的 initial_map
进行了检查,而之前的漏洞代码并没有对 new_target
的 initial_map
进行合法性检查,我们看下这个函数的关键逻辑:
可以看到如果最后到达顶层构造函数,并且 new_target
是一个 JSFunction
对象,则会调用 BuildAllocateFastObject
进行默认对象的创建,而不是调用之前分析的 TF_BUILTIN(FastNewObject, ConstructorBuiltinsAssembler)
函数进行创建,但是这里调用 BuildAllocateFastObject
时,没用对 new_target
的 initial_map
进行合法性检查,然后这里第一个参数是通过 FastObject
构造函数创建的,跟进看看:
注意我们传入的参数是 new_target_function
,所以可以看到这里的 map
就是 new_target_function.initial_map
然后可以跟进 BuildAllocateFastObject
函数看看:
该函数有多个实现,可以根据参数类型判断具体调用了那个函数
所以这里的关键点就是其把默认对象的 map
设置为了 new_target.initial_map
,这便是漏洞之处,通过之前的分析我们知道,调用 BuildAllocateFastObject
函数之前没有对 new_target.initial_map
进行合法性检查,所以最后可以导致的效果为:
那么这时如果 new_target
与 target
的 initial_map
不相同,则可能导致属性初始化错误,比如 new_target
的 initial_map
为 JSArray
,那么此时就会导致 target
忽略对默认对象 length
属性的初始化
漏洞触发
想要到达漏洞代码逻辑,得使以下关键判断成立:
先来看下 TryGetConstant
函数:
注:这里 TryGetConstant
存在多个实现,但没办法用参数进行判断调用了哪一个,所以这里参考参考文章的分析
由于还没开始审计 Maglev
源码,所以这里笔者不是很懂,简单来说就是这里有两个路径可以进行判断 node
是否是 constant
:
这里 【1】
路径行不通,所以这里利用【2】
路径进行绕过,其主要就是插入一个 CheckValue
节点,而该节点会标记该 node
为一个 constant
:
这里直接看 poc
:
看下 Maglev IR
:
可以看到这里确实产生了 CheckValue
节点,并且这里可以看 Map
的值:
可以看到其直接赋值的 new_target.initial_map
,而 new_target.initial_map
的类型为 JSArray
:
来看下初始化过程:
这里的 4/8
明显是 properties/element
的偏移,但是其却没有对 length
进行赋值,可以看到print(arr instanceof Array)
输出的是 true
,即 arr
是一个数组:
这里主要利用的是 JSObject
与 JSArray
的类型混淆,JSObject
是不存在 length
属性的,而 JSArray
存在 length
属性,所以如果 target
为 JSObject
,而 new_target
为 JSArray
,那么触发漏洞后,target
就不会初始化创建对象的 length
属性,所以这里的 length
就是一个未初始化的值,如果这个未初始化 length
值比较大,就可以实现越界读写
有了越界读写,后面写利用就比较简单了,很多利用手法都大同小异,所以就不细说了,主要说下关键点:
虽然没有对 length
进行初始化,但是一般(没有 gc
)时,length
就为 0,所以这里先提前触发 gc
去移动对象,这样有概率存在残留数据覆盖 length
,如果 length
比较大,就可以实现越界读写
由于 JSObect
对象不使用 element
属性,所以这里 element
指向 FixedArray[0]
,其值在笔者机器上固定为 0x219
,其属于的页权限为只读权限,但是这里问题不大,因为 0x219
基本就在堆的最低地址处了,只要被覆盖的 length
比较大,就可以实现越界读写
exploit
如下:
效果如下:
通过对该 CVE
的分析利用,对 Maglev
有了基本的了解,但是还有一些细节上的东西没有搞清楚,这个只能后面对 Mgalev
逐渐熟悉后再看看了
然后看了下腾讯玄武去年的 talk
,发现 Maglev
是一个很不错的攻击面(玄武的大佬好像直接挖了7个洞),其与 trubofan
有部分相同的性质,而其又具有独特的攻击面,所以笔者感觉可以将 turbofan
的一些历史漏洞去套下 Maglev
。当然了,随着 chrome
对 turbofan
保护强度的逐步上升,目前想在 trubofan
中出一个洞可以说是非常难了,而 Maglev
应该是目前最能够出 JIT
洞的了,当然自己目前太菜了,也希望早日能够挖到自己的漏洞
说点别的,瞎扯一下:目前其实也复现了很多漏洞,但是对挖漏洞其实还是比较迷茫的,自己也花了一些时间总结了下,发现自己虽然在复现一些漏洞,但是很多漏洞都比较老,并且在复现漏洞的时候没有去总结可能的攻击面,看了很多大佬的 talk
,发现选取攻击面是非常重要的,就 chrome
而言,其有很多组件,每个组件又有不同的功能分支,如何针对性的进行 fuzz
是非常重要的。也希望后面可以跟踪一些比较前沿的攻击手法和比较新的攻击面。目前在笔者看来,复现漏洞就两个目的,第一就是熟悉某个知识点,就比如笔者之所以复现该漏洞其实就是为了巩固下 Maglev
中的一些东西;第二就是总结攻击面,这个目前笔者做的比较失败。然后单纯的为了写利用去复现漏洞是没有意义的,漏洞那么多,是不能全部复现的。所以后面笔者复现漏洞也会有针对性的复现,会尽量选取一些比较新的漏洞进行复现
人一定要学会反思,不然就如同行尸走肉一般
Getting RCE in Chrome with incomplete object initialization in the Maglev compiler
git checkout 7f22404388ef0eb9383f189c1b0a85b5ea93b079
gclient
sync
-D
git checkout 7f22404388ef0eb9383f189c1b0a85b5ea93b079
gclient
sync
-D
class A {
constructor() {
this
.x = 1;
}
}
class B {
constructor() {
this
.x = 1;
return
[1.1, 2.2];
}
}
var
a =
new
A();
var
b =
new
B();
print(a);
print(b);
class A {
constructor() {
this
.x = 1;
}
}
class B {
constructor() {
this
.x = 1;
return
[1.1, 2.2];
}
}
var
a =
new
A();
var
b =
new
B();
print(a);
print(b);
TF_BUILTIN(FastNewObject, ConstructorBuiltinsAssembler)
TNode<JSObject> ConstructorBuiltinsAssembler::FastNewObject(
TNode<Context> context,
TNode<JSFunction> target,
TNode<JSReceiver> new_target)
⇒ TNode<JSObject> ConstructorBuiltinsAssembler::FastNewObject(
TNode<Context> context,
TNode<JSFunction> target,
TNode<JSReceiver> new_target,
Label* call_runtime)
TF_BUILTIN(FastNewObject, ConstructorBuiltinsAssembler)
TNode<JSObject> ConstructorBuiltinsAssembler::FastNewObject(
TNode<Context> context,
TNode<JSFunction> target,
TNode<JSReceiver> new_target)
⇒ TNode<JSObject> ConstructorBuiltinsAssembler::FastNewObject(
TNode<Context> context,
TNode<JSFunction> target,
TNode<JSReceiver> new_target,
Label* call_runtime)
TF_BUILTIN(FastNewObject, ConstructorBuiltinsAssembler) {
auto
context = Parameter<Context>(Descriptor::kContext);
auto
target = Parameter<JSFunction>(Descriptor::kTarget);
auto
new_target = Parameter<JSReceiver>(Descriptor::kNewTarget);
Label call_runtime(
this
);
TNode<JSObject> result = FastNewObject(context, target, new_target, &call_runtime);
Return(result);
BIND(&call_runtime);
TailCallRuntime(Runtime::kNewObject, context, target, new_target);
}
TF_BUILTIN(FastNewObject, ConstructorBuiltinsAssembler) {
auto
context = Parameter<Context>(Descriptor::kContext);
auto
target = Parameter<JSFunction>(Descriptor::kTarget);
auto
new_target = Parameter<JSReceiver>(Descriptor::kNewTarget);
Label call_runtime(
this
);
TNode<JSObject> result = FastNewObject(context, target, new_target, &call_runtime);
Return(result);
BIND(&call_runtime);
TailCallRuntime(Runtime::kNewObject, context, target, new_target);
}
TNode<JSObject> ConstructorBuiltinsAssembler::FastNewObject(
TNode<Context> context, TNode<JSFunction> target,
TNode<JSReceiver> new_target, Label* call_runtime) {
Label end(
this
);
TNode<JSFunction> new_target_func = HeapObjectToJSFunctionWithPrototypeSlot(new_target, call_runtime);
TNode<Object> initial_map_or_proto = LoadJSFunctionPrototypeOrInitialMap(new_target_func);
GotoIf(TaggedIsSmi(initial_map_or_proto), call_runtime);
GotoIf(DoesntHaveInstanceType(CAST(initial_map_or_proto), MAP_TYPE), call_runtime);
TNode<Map> initial_map = CAST(initial_map_or_proto);
TNode<Object> new_target_constructor = LoadObjectField(initial_map, Map::kConstructorOrBackPointerOrNativeContextOffset);
GotoIf(TaggedNotEqual(target, new_target_constructor), call_runtime);
TVARIABLE(HeapObject, properties);
Label instantiate_map(
this
), allocate_properties(
this
);
GotoIf(IsDictionaryMap(initial_map), &allocate_properties);
{
properties = EmptyFixedArrayConstant();
Goto(&instantiate_map);
}
BIND(&allocate_properties);
{
if
(V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
properties = AllocateSwissNameDictionary(SwissNameDictionary::kInitialCapacity);
}
else
{
properties = AllocateNameDictionary(NameDictionary::kInitialCapacity);
}
Goto(&instantiate_map);
}
BIND(&instantiate_map);
return
AllocateJSObjectFromMap(initial_map, properties.value(), base::nullopt,
AllocationFlag::kNone, kWithSlackTracking);
}
TNode<JSObject> ConstructorBuiltinsAssembler::FastNewObject(
TNode<Context> context, TNode<JSFunction> target,
TNode<JSReceiver> new_target, Label* call_runtime) {
Label end(
this
);
TNode<JSFunction> new_target_func = HeapObjectToJSFunctionWithPrototypeSlot(new_target, call_runtime);
TNode<Object> initial_map_or_proto = LoadJSFunctionPrototypeOrInitialMap(new_target_func);
GotoIf(TaggedIsSmi(initial_map_or_proto), call_runtime);
GotoIf(DoesntHaveInstanceType(CAST(initial_map_or_proto), MAP_TYPE), call_runtime);
TNode<Map> initial_map = CAST(initial_map_or_proto);
TNode<Object> new_target_constructor = LoadObjectField(initial_map, Map::kConstructorOrBackPointerOrNativeContextOffset);
GotoIf(TaggedNotEqual(target, new_target_constructor), call_runtime);
TVARIABLE(HeapObject, properties);
Label instantiate_map(
this
), allocate_properties(
this
);
GotoIf(IsDictionaryMap(initial_map), &allocate_properties);
{
properties = EmptyFixedArrayConstant();
Goto(&instantiate_map);
}
BIND(&allocate_properties);
{
if
(V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
properties = AllocateSwissNameDictionary(SwissNameDictionary::kInitialCapacity);
}
else
{
properties = AllocateNameDictionary(NameDictionary::kInitialCapacity);
}
Goto(&instantiate_map);
}
BIND(&instantiate_map);
return
AllocateJSObjectFromMap(initial_map, properties.value(), base::nullopt,
AllocationFlag::kNone, kWithSlackTracking);
}
RUNTIME_FUNCTION(Runtime_NewObject) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
Handle<JSFunction> target = args.at<JSFunction>(0);
Handle<JSReceiver> new_target = args.at<JSReceiver>(1);
RETURN_RESULT_OR_FAILURE(
isolate,
JSObject::New(target, new_target, Handle<AllocationSite>::null()));
}
RUNTIME_FUNCTION(Runtime_NewObject) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
Handle<JSFunction> target = args.at<JSFunction>(0);
Handle<JSReceiver> new_target = args.at<JSReceiver>(1);
RETURN_RESULT_OR_FAILURE(
isolate,
JSObject::New(target, new_target, Handle<AllocationSite>::null()));
}
MaybeHandle<JSObject> JSObject::New(Handle<JSFunction> constructor,
Handle<JSReceiver> new_target,
Handle<AllocationSite> site) {
Isolate*
const
isolate = constructor->GetIsolate();
DCHECK(constructor->IsConstructor());
DCHECK(new_target->IsConstructor());
DCHECK(!constructor->has_initial_map() ||
!InstanceTypeChecker::IsJSFunction(constructor->initial_map().instance_type()));
Handle<Map> initial_map;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, initial_map,
JSFunction::GetDerivedMap(isolate, constructor, new_target), JSObject);
constexpr
int
initial_capacity = V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL
? SwissNameDictionary::kInitialCapacity
: NameDictionary::kInitialCapacity;
Handle<JSObject> result = isolate->factory()->NewFastOrSlowJSObjectFromMap(
initial_map, initial_capacity, AllocationType::kYoung, site);
return
result;
}
MaybeHandle<JSObject> JSObject::New(Handle<JSFunction> constructor,
Handle<JSReceiver> new_target,
Handle<AllocationSite> site) {
Isolate*
const
isolate = constructor->GetIsolate();
DCHECK(constructor->IsConstructor());
DCHECK(new_target->IsConstructor());
DCHECK(!constructor->has_initial_map() ||
!InstanceTypeChecker::IsJSFunction(constructor->initial_map().instance_type()));
Handle<Map> initial_map;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, initial_map,
JSFunction::GetDerivedMap(isolate, constructor, new_target), JSObject);
constexpr
int
initial_capacity = V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL
? SwissNameDictionary::kInitialCapacity
: NameDictionary::kInitialCapacity;
Handle<JSObject> result = isolate->factory()->NewFastOrSlowJSObjectFromMap(
initial_map, initial_capacity, AllocationType::kYoung, site);
return
result;
}
MaybeHandle<Map> JSFunction::GetDerivedMap(Isolate* isolate,
Handle<JSFunction> constructor,
Handle<JSReceiver> new_target) {
EnsureHasInitialMap(constructor);
Handle<Map> constructor_initial_map(constructor->initial_map(), isolate);
if
(*new_target == *constructor)
return
constructor_initial_map;
Handle<Map> result_map;
if
(new_target->IsJSFunction()) {
Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
if
(FastInitializeDerivedMap(isolate, function, constructor, constructor_initial_map)) {
return
handle(function->initial_map(), isolate);
}
}
MaybeHandle<Map> JSFunction::GetDerivedMap(Isolate* isolate,
Handle<JSFunction> constructor,
Handle<JSReceiver> new_target) {
EnsureHasInitialMap(constructor);
Handle<Map> constructor_initial_map(constructor->initial_map(), isolate);
if
(*new_target == *constructor)
return
constructor_initial_map;
Handle<Map> result_map;
if
(new_target->IsJSFunction()) {
Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
if
(FastInitializeDerivedMap(isolate, function, constructor, constructor_initial_map)) {
return
handle(function->initial_map(), isolate);
}
}
bool
FastInitializeDerivedMap(Isolate* isolate, Handle<JSFunction> new_target,
Handle<JSFunction> constructor,
Handle<Map> constructor_initial_map) {
if
(!new_target->has_prototype_slot())
return
false
;
if
(new_target->has_initial_map() &&
new_target->initial_map().GetConstructor() == *constructor) {
DCHECK(new_target->instance_prototype().IsJSReceiver());
return
true
;
}
......
Handle<Map> map =
Map::CopyInitialMap(isolate, constructor_initial_map, instance_size, in_object_properties, unused_property_fields);
map->set_new_target_is_base(
false
);
Handle<HeapObject> prototype(new_target->instance_prototype(), isolate);
JSFunction::SetInitialMap(isolate, new_target, map, prototype, constructor);
DCHECK(new_target->instance_prototype().IsJSReceiver());
map->set_construction_counter(Map::kNoSlackTracking);
map->StartInobjectSlackTracking();
return
true
;
}
bool
FastInitializeDerivedMap(Isolate* isolate, Handle<JSFunction> new_target,
Handle<JSFunction> constructor,
Handle<Map> constructor_initial_map) {
if
(!new_target->has_prototype_slot())
return
false
;
if
(new_target->has_initial_map() &&
new_target->initial_map().GetConstructor() == *constructor) {
DCHECK(new_target->instance_prototype().IsJSReceiver());
return
true
;
}
......
Handle<Map> map =
Map::CopyInitialMap(isolate, constructor_initial_map, instance_size, in_object_properties, unused_property_fields);
map->set_new_target_is_base(
false
);
Handle<HeapObject> prototype(new_target->instance_prototype(), isolate);
JSFunction::SetInitialMap(isolate, new_target, map, prototype, constructor);
DCHECK(new_target->instance_prototype().IsJSReceiver());
map->set_construction_counter(Map::kNoSlackTracking);
map->StartInobjectSlackTracking();
return
true
;
}
diff --git a/src/maglev/maglev-graph-builder.cc b/src/maglev/maglev-graph-builder.cc
index d5f6128..2c5227e 100644
--- a/src/maglev/maglev-graph-builder.cc
+++ b/src/maglev/maglev-graph-builder.cc
@@ -5347,6 +5347,14 @@
StoreRegister(iterator_.GetRegisterOperand(0), map_proto);
}
+
bool
MaglevGraphBuilder::HasValidInitialMap(
+ compiler::JSFunctionRef new_target, compiler::JSFunctionRef constructor) {
+
if
(!new_target.map(broker()).has_prototype_slot())
return
false
;
+
if
(!new_target.has_initial_map(broker()))
return
false
;
+ compiler::MapRef initial_map = new_target.initial_map(broker());
+
return
initial_map.GetConstructor(broker()).equals(constructor);
+}
+
void
MaglevGraphBuilder::VisitFindNonDefaultConstructorOrConstruct() {
ValueNode* this_function = LoadRegisterTagged(0);
ValueNode* new_target = LoadRegisterTagged(1);
@@ -5380,7 +5388,9 @@
TryGetConstant(new_target);
if
(kind == FunctionKind::kDefaultBaseConstructor) {
ValueNode* object;
-
if
(new_target_function && new_target_function->IsJSFunction()) {
+
if
(new_target_function && new_target_function->IsJSFunction() &&
+ HasValidInitialMap(new_target_function->AsJSFunction(),
+ current_function)) {
object = BuildAllocateFastObject(
FastObject(new_target_function->AsJSFunction(), zone(),
broker()),
diff --git a/src/maglev/maglev-graph-builder.h b/src/maglev/maglev-graph-builder.h
index 0abb4a8..d92354c 100644
--- a/src/maglev/maglev-graph-builder.h
+++ b/src/maglev/maglev-graph-builder.h
@@ -1884,6 +1884,9 @@
void
MergeDeadLoopIntoFrameState(
int
target);
void
MergeIntoInlinedReturnFrameState(BasicBlock* block);
+
bool
HasValidInitialMap(compiler::JSFunctionRef new_target,
+ compiler::JSFunctionRef constructor);
+
enum
JumpType { kJumpIfTrue, kJumpIfFalse };
enum
class
BranchSpecializationMode { kDefault, kAlwaysBoolean };
JumpType NegateJumpType(JumpType jump_type);
diff --git a/src/maglev/maglev-graph-builder.cc b/src/maglev/maglev-graph-builder.cc
index d5f6128..2c5227e 100644
--- a/src/maglev/maglev-graph-builder.cc
+++ b/src/maglev/maglev-graph-builder.cc
@@ -5347,6 +5347,14 @@
StoreRegister(iterator_.GetRegisterOperand(0), map_proto);
}
+
bool
MaglevGraphBuilder::HasValidInitialMap(
+ compiler::JSFunctionRef new_target, compiler::JSFunctionRef constructor) {
+
if
(!new_target.map(broker()).has_prototype_slot())
return
false
;
+
if
(!new_target.has_initial_map(broker()))
return
false
;
+ compiler::MapRef initial_map = new_target.initial_map(broker());
+
return
initial_map.GetConstructor(broker()).equals(constructor);
+}
+
void
MaglevGraphBuilder::VisitFindNonDefaultConstructorOrConstruct() {
ValueNode* this_function = LoadRegisterTagged(0);
ValueNode* new_target = LoadRegisterTagged(1);
@@ -5380,7 +5388,9 @@
TryGetConstant(new_target);
if
(kind == FunctionKind::kDefaultBaseConstructor) {
ValueNode* object;
-
if
(new_target_function && new_target_function->IsJSFunction()) {
+
if
(new_target_function && new_target_function->IsJSFunction() &&
+ HasValidInitialMap(new_target_function->AsJSFunction(),
+ current_function)) {
object = BuildAllocateFastObject(
FastObject(new_target_function->AsJSFunction(), zone(),
broker()),
diff --git a/src/maglev/maglev-graph-builder.h b/src/maglev/maglev-graph-builder.h
index 0abb4a8..d92354c 100644
--- a/src/maglev/maglev-graph-builder.h
+++ b/src/maglev/maglev-graph-builder.h
@@ -1884,6 +1884,9 @@
void
MergeDeadLoopIntoFrameState(
int
target);
void
MergeIntoInlinedReturnFrameState(BasicBlock* block);
+
bool
HasValidInitialMap(compiler::JSFunctionRef new_target,
+ compiler::JSFunctionRef constructor);
+
enum
JumpType { kJumpIfTrue, kJumpIfFalse };
enum
class
BranchSpecializationMode { kDefault, kAlwaysBoolean };
JumpType NegateJumpType(JumpType jump_type);
class A {}
class B extends A {}
var
b =
new
B();
class A {}
class B extends A {}
var
b =
new
B();
CreateRestParameter
Star2
Mov <closure>, r1
FindNonDefaultConstructorOrConstruct r1, r0, r7-r8
Mov r2, r5
Ldar r7
Mov r1, r3
Mov r0, r6
Mov r8, r4
JumpIfTrue [12] (0x352f0019b2df @ 35) ----> |
ThrowIfNotSuperConstructor r4 | t
Ldar r6 | r
ConstructWithSpread r4, r5-r5, [0] | u
Star4 | e
Ldar r4 <--------
Return
CreateRestParameter
Star2
Mov <closure>, r1
FindNonDefaultConstructorOrConstruct r1, r0, r7-r8
Mov r2, r5
Ldar r7
Mov r1, r3
Mov r0, r6
Mov r8, r4
JumpIfTrue [12] (0x352f0019b2df @ 35) ----> |
ThrowIfNotSuperConstructor r4 | t
Ldar r6 | r
ConstructWithSpread r4, r5-r5, [0] | u
Star4 | e
Ldar r4 <--------
Return
void
MaglevGraphBuilder::VisitFindNonDefaultConstructorOrConstruct() {
ValueNode* this_function = LoadRegisterTagged(0);
ValueNode* new_target = LoadRegisterTagged(1);
@@ -5380,7 +5388,9 @@
TryGetConstant(new_target);
if
(kind == FunctionKind::kDefaultBaseConstructor) {
ValueNode* object;
-
if
(new_target_function && new_target_function->IsJSFunction()) {
+
if
(new_target_function && new_target_function->IsJSFunction() &&
+ HasValidInitialMap(new_target_function->AsJSFunction(),
+ current_function)) {
object = BuildAllocateFastObject(
FastObject(new_target_function->AsJSFunction(), zone(),
broker()),
void
MaglevGraphBuilder::VisitFindNonDefaultConstructorOrConstruct() {
ValueNode* this_function = LoadRegisterTagged(0);
ValueNode* new_target = LoadRegisterTagged(1);
@@ -5380,7 +5388,9 @@
TryGetConstant(new_target);
if
(kind == FunctionKind::kDefaultBaseConstructor) {
ValueNode* object;
-
if
(new_target_function && new_target_function->IsJSFunction()) {
+
if
(new_target_function && new_target_function->IsJSFunction() &&
+ HasValidInitialMap(new_target_function->AsJSFunction(),
+ current_function)) {
object = BuildAllocateFastObject(
FastObject(new_target_function->AsJSFunction(), zone(),
broker()),
void
MaglevGraphBuilder::VisitFindNonDefaultConstructorOrConstruct() {
ValueNode* this_function = LoadRegisterTagged(0);
ValueNode* new_target = LoadRegisterTagged(1);
auto
register_pair = iterator_.GetRegisterPairOperand(2);
if
(compiler::OptionalHeapObjectRef constant = TryGetConstant(this_function)) {
compiler::MapRef function_map = constant->map(broker());
compiler::HeapObjectRef current = function_map.prototype(broker());
if
(broker()->dependencies()->DependOnArrayIteratorProtector()) {
while
(
true
) {
if
(!current.IsJSFunction())
break
;
compiler::JSFunctionRef current_function = current.AsJSFunction();
......
FunctionKind kind = current_function.shared(broker()).kind();
if
(kind != FunctionKind::kDefaultDerivedConstructor) {
broker()->dependencies()->DependOnStablePrototypeChain(
function_map, WhereToStart::kStartAtReceiver, current_function);
compiler::OptionalHeapObjectRef new_target_function = TryGetConstant(new_target);
if
(kind == FunctionKind::kDefaultBaseConstructor) {
ValueNode* object;
if
(new_target_function && new_target_function->IsJSFunction()) {
object = BuildAllocateFastObject(
FastObject(new_target_function->AsJSFunction(), zone(), broker()),
AllocationType::kYoung);
}
else
{
object = BuildCallBuiltin<Builtin::kFastNewObject>(
{GetConstant(current_function), new_target});
}
StoreRegister(register_pair.first, GetBooleanConstant(
true
));
StoreRegister(register_pair.second, object);
return
;
}
break
;
}
current = current_function.map(broker()).prototype(broker());
}
}
StoreRegister(register_pair.first, GetBooleanConstant(
false
));
StoreRegister(register_pair.second, GetConstant(current));
return
;
}
......
}
void
MaglevGraphBuilder::VisitFindNonDefaultConstructorOrConstruct() {
ValueNode* this_function = LoadRegisterTagged(0);
ValueNode* new_target = LoadRegisterTagged(1);
auto
register_pair = iterator_.GetRegisterPairOperand(2);
if
(compiler::OptionalHeapObjectRef constant = TryGetConstant(this_function)) {
compiler::MapRef function_map = constant->map(broker());
compiler::HeapObjectRef current = function_map.prototype(broker());
if
(broker()->dependencies()->DependOnArrayIteratorProtector()) {
while
(
true
) {
if
(!current.IsJSFunction())
break
;
compiler::JSFunctionRef current_function = current.AsJSFunction();
......
FunctionKind kind = current_function.shared(broker()).kind();
if
(kind != FunctionKind::kDefaultDerivedConstructor) {
broker()->dependencies()->DependOnStablePrototypeChain(
function_map, WhereToStart::kStartAtReceiver, current_function);
compiler::OptionalHeapObjectRef new_target_function = TryGetConstant(new_target);
if
(kind == FunctionKind::kDefaultBaseConstructor) {
ValueNode* object;
if
(new_target_function && new_target_function->IsJSFunction()) {
object = BuildAllocateFastObject(
FastObject(new_target_function->AsJSFunction(), zone(), broker()),
AllocationType::kYoung);
}
else
{
object = BuildCallBuiltin<Builtin::kFastNewObject>(
{GetConstant(current_function), new_target});
}
StoreRegister(register_pair.first, GetBooleanConstant(
true
));
StoreRegister(register_pair.second, object);
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2024-4-13 21:22
被XiaozaYa编辑
,原因: