Fix crashes/assertions related to fb_intercept and reentry
If a function was called via (re)enterVM, and it didn't yet have a prolog, and it was unable to get the write lease, and it was intercepted, and the intercept handler asked for the original function to be skipped, fcallHelper didn't return correctly, typically resulting in the original function being re-executed, and then re-intercepted - except that it was one level deeper in the vm-nesting hierarchy. Eventually, it crashed.
Esse commit está contido em:
@@ -669,7 +669,7 @@ public:
|
||||
HPHP::PCFilter* m_lastLocFilter;
|
||||
bool m_interpreting;
|
||||
bool m_dbgNoBreak;
|
||||
void doFCall(HPHP::ActRec* ar, PC& pc);
|
||||
bool doFCall(HPHP::ActRec* ar, PC& pc);
|
||||
bool doFCallArray(PC& pc);
|
||||
CVarRef getEvaledArg(const StringData* val);
|
||||
private:
|
||||
|
||||
@@ -5784,7 +5784,7 @@ void VMExecutionContext::iopFPassM(PC& pc) {
|
||||
}
|
||||
}
|
||||
|
||||
void VMExecutionContext::doFCall(ActRec* ar, PC& pc) {
|
||||
bool VMExecutionContext::doFCall(ActRec* ar, PC& pc) {
|
||||
assert(getOuterVMFrame(ar) == m_fp);
|
||||
ar->m_savedRip = (uintptr_t)tx64->getRetFromInterpretedFrame();
|
||||
assert(isReturnHelper(ar->m_savedRip));
|
||||
@@ -5796,9 +5796,9 @@ void VMExecutionContext::doFCall(ActRec* ar, PC& pc) {
|
||||
assert(pcOff() >= m_fp->m_func->base());
|
||||
prepareFuncEntry(ar, pc);
|
||||
SYNC();
|
||||
if (!EventHook::FunctionEnter(ar, EventHook::NormalFunc)) {
|
||||
pc = m_pc;
|
||||
}
|
||||
if (EventHook::FunctionEnter(ar, EventHook::NormalFunc)) return true;
|
||||
pc = m_pc;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void OPTBLD_INLINE VMExecutionContext::iopFCall(PC& pc) {
|
||||
|
||||
@@ -159,7 +159,6 @@ bool EventHook::RunInterceptHandler(ActRec* ar) {
|
||||
if (doneFlag.toBoolean()) {
|
||||
Offset pcOff;
|
||||
ActRec* outer = g_vmContext->getPrevVMState(ar, &pcOff);
|
||||
assert(outer);
|
||||
|
||||
frame_free_locals_inl_no_hook<true>(ar, ar->m_func->numLocals());
|
||||
Stack& stack = g_vmContext->getStack();
|
||||
@@ -167,7 +166,7 @@ bool EventHook::RunInterceptHandler(ActRec* ar) {
|
||||
tvDup(ret.asTypedValue(), stack.allocTV());
|
||||
|
||||
g_vmContext->m_fp = outer;
|
||||
g_vmContext->m_pc = outer->m_func->unit()->at(pcOff);
|
||||
g_vmContext->m_pc = outer ? outer->m_func->unit()->at(pcOff) : nullptr;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -74,25 +74,6 @@ static void setupAfterProlog(ActRec* fp) {
|
||||
* Func's prologue entries initially point here. Vector into fcallHelper,
|
||||
* where we can translate a new prologue.
|
||||
*/
|
||||
|
||||
static TCA callAndResume(ActRec *ar) {
|
||||
if (!ar->m_func->isClonedClosure()) {
|
||||
/*
|
||||
* If the func is a cloned closure, then the original
|
||||
* closure has already run the prolog, and the prologs
|
||||
* array is just being used as entry points for the
|
||||
* dv funclets. Dont run the prolog again.
|
||||
*/
|
||||
VMRegAnchor _(ar);
|
||||
uint64_t rip = ar->m_savedRip;
|
||||
g_vmContext->doFCall(ar, g_vmContext->m_pc);
|
||||
ar->m_savedRip = rip;
|
||||
return Translator::Get()->getResumeHelperRet();
|
||||
}
|
||||
setupAfterProlog(ar);
|
||||
return Translator::Get()->getResumeHelper();
|
||||
}
|
||||
|
||||
static_assert(rStashedAR == reg::r15,
|
||||
"__fcallHelperThunk needs to be modified for ABI changes");
|
||||
asm(
|
||||
@@ -142,7 +123,33 @@ TCA fcallHelper(ActRec* ar) {
|
||||
if (tca) {
|
||||
return tca;
|
||||
}
|
||||
return callAndResume(ar);
|
||||
if (!ar->m_func->isClonedClosure()) {
|
||||
/*
|
||||
* If the func is a cloned closure, then the original
|
||||
* closure has already run the prolog, and the prologs
|
||||
* array is just being used as entry points for the
|
||||
* dv funclets. Dont run the prolog again.
|
||||
*/
|
||||
VMRegAnchor _(ar);
|
||||
uint64_t rip = ar->m_savedRip;
|
||||
if (g_vmContext->doFCall(ar, g_vmContext->m_pc)) {
|
||||
ar->m_savedRip = rip;
|
||||
return Translator::Get()->getResumeHelperRet();
|
||||
}
|
||||
// We've been asked to skip the function body
|
||||
// (fb_intercept). frame, stack and pc have
|
||||
// already been fixed - so just ensure that
|
||||
// we setup the registers, and return as
|
||||
// if from the call to ar
|
||||
DECLARE_FRAME_POINTER(framePtr);
|
||||
framePtr->m_savedRip = rip;
|
||||
framePtr->m_savedRbp = (uint64_t)g_vmContext->m_fp;
|
||||
sp = g_vmContext->m_stack.top();
|
||||
return nullptr;
|
||||
}
|
||||
setupAfterProlog(ar);
|
||||
assert(ar == g_vmContext->m_fp);
|
||||
return Translator::Get()->getResumeHelper();
|
||||
} catch (...) {
|
||||
/*
|
||||
The return address is set to __fcallHelperThunk,
|
||||
@@ -152,9 +159,8 @@ TCA fcallHelper(ActRec* ar) {
|
||||
function's return address (which will be in the
|
||||
tc).
|
||||
Note that the registers really are clean - we
|
||||
just came from callAndResume which cleaned
|
||||
them for us - so we just have to tell the unwinder
|
||||
that.
|
||||
cleaned them in the try above - so we just
|
||||
have to tell the unwinder that.
|
||||
*/
|
||||
DECLARE_FRAME_POINTER(framePtr);
|
||||
tl_regState = REGSTATE_CLEAN;
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário