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:
mwilliams
2013-06-12 20:09:51 -07:00
commit de Sara Golemon
commit 866290d835
4 arquivos alterados com 35 adições e 30 exclusões
+1 -1
Ver Arquivo
@@ -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:
+4 -4
Ver Arquivo
@@ -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) {
+1 -2
Ver Arquivo
@@ -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;
}
+29 -23
Ver Arquivo
@@ -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;