fix segfault for wikimedia
If you call a static method on a instance, we decRef the object before running the method. If that is the last reference to the object, then its destructor will be called right in the middle of setting up the ActRec for the static method. Currently, the top of the stack will be off by 2 Cells because we have already popped off one Cell (the object) and an ActRec takes up 3 Cells. Instead, we have to have the top of the stack to be after the ActRec that we are building for the next method call.
Esse commit está contido em:
@@ -1771,6 +1771,11 @@ void HhbcTranslator::emitFPushObjMethodD(int32_t numParams,
|
||||
nullptr);
|
||||
auto const actRec = spillStack();
|
||||
auto const objCls = gen(LdObjClass, obj);
|
||||
|
||||
// This is special. We need to move the stackpointer incase LdObjMethod
|
||||
// calls a destructor. Otherwise it would clobber the ActRec we just pushed.
|
||||
emitMarker();
|
||||
|
||||
gen(LdObjMethod,
|
||||
objCls,
|
||||
cns(methodName),
|
||||
|
||||
@@ -539,6 +539,14 @@ void methodCacheSlowPath(MethodCache::Pair* mce,
|
||||
ObjectData* arThis = ar->getThis();
|
||||
shouldBeObj->m_type = KindOfObject;
|
||||
shouldBeObj->m_data.pobj = arThis;
|
||||
|
||||
// There used to be a half-built ActRec on the stack that we need the
|
||||
// unwinder to ignore. We overwrote 1/3 of it with the code above, but
|
||||
// because of the emitMarker() in LdObjMethod we need the other two slots
|
||||
// to not have any TypedValues.
|
||||
tvWriteNull(shouldBeObj-1);
|
||||
tvWriteNull(shouldBeObj-2);
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
class A {
|
||||
|
||||
public static function factory() {
|
||||
return new A;
|
||||
}
|
||||
|
||||
public static function b() {
|
||||
return 'no segfault';
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
echo A::factory()->b();
|
||||
}
|
||||
main();
|
||||
@@ -0,0 +1 @@
|
||||
no segfault
|
||||
@@ -1,3 +1,24 @@
|
||||
<?php
|
||||
|
||||
function nop($en,$es){};set_error_handler('nop');class X { function bar() { var_dump($this); } }if (1) { class U { }} else { class U extends X { }}class V extends U {}function test() { $x = new X; $x->bar(); $x = new V; $x->bar();}test();
|
||||
function nop($en,$es){};
|
||||
set_error_handler('nop');
|
||||
class X {
|
||||
function bar() {
|
||||
var_dump($this);
|
||||
}
|
||||
}
|
||||
if (1) {
|
||||
class U { }
|
||||
} else {
|
||||
class U extends X { }
|
||||
}
|
||||
|
||||
class V extends U {}
|
||||
|
||||
function test() {
|
||||
$x = new X;
|
||||
$x->bar();
|
||||
$x = new V;
|
||||
$x->bar();
|
||||
}
|
||||
test();
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário