Skip to content

Commit

Permalink
Fix code path that can overflow the native stack
Browse files Browse the repository at this point in the history
We had a bug where a PHP program can infinitely recurse in a certain way
that doesn't hit any of our native stack overflow checks and causes the
process to segfault. Below I've included a snippet of the callstack that
caused HHVM to crash.

The fix is to make invokeFunc(), invokeFuncFew(), and invokeContFunc()
unconditionally perform a stack overflow check for the native stack. I
tried to keep the fix minimal and non-invasive so that we can get this
hotfixed if needed.

  #0  0x00000000032ba2ef in malloc ()
  #1  0x000000000257401e in HPHP::Util::canonicalize(char const*, unsigned long, bool) ()
  #2  0x0000000001c15f0e in HPHP::resolve_include(HPHP::String const&, char const*, bool (*)(HPHP::String const&, void*), void*) ()
  #3  0x0000000001bd0327 in HPHP::Eval::resolveVmInclude(HPHP::StringData*, char const*, stat*) ()
  #4  0x0000000001fe47d0 in HPHP::VMExecutionContext::lookupPhpFile(HPHP::StringData*, char const*, bool*) ()
  #5  0x0000000001fe521a in HPHP::VMExecutionContext::evalInclude(HPHP::StringData*, HPHP::StringData const*, bool*) ()
  #6  0x0000000001c17231 in HPHP::AutoloadHandler::Result HPHP::AutoloadHandler::loadFromMap<HPHP::ConstantExistsChecker>(HPHP::String const&, HPHP::String const&, bool, HPHP::ConstantExistsChecker const&) ()
  #7  0x0000000001c174ed in HPHP::AutoloadHandler::autoloadConstant(HPHP::StringData*) ()
  #8  0x00000000020c1c73 in HPHP::Unit::loadCns(HPHP::StringData const*) ()
  #9  0x0000000001f1d10e in HPHP::Transl::lookupCnsHelper(HPHP::TypedValue const*, HPHP::StringData*, bool) ()
  #10 0x000000002827f855 in ?? ()
  #11 0x00000000026e566e in enterTCHelper ()
  #12 0x0000000001f35ef0 in HPHP::Transl::TranslatorX64::enterTC(unsigned char*, void*) ()
  #13 0x0000000002018004 in HPHP::VMExecutionContext::enterVM(HPHP::TypedValue*, HPHP::ActRec*) ()
  #14 0x000000000201821c in HPHP::VMExecutionContext::reenterVM(HPHP::TypedValue*, HPHP::ActRec*, HPHP::TypedValue*) ()
  #15 0x0000000002018612 in HPHP::VMExecutionContext::invokeFunc(HPHP::TypedValue*, HPHP::Func const*, HPHP::Array const&, HPHP::ObjectData*, HPHP::Class*, HPHP::VarEnv*, HPHP::StringData*, HPHP::VMExecutionContext::InvokeFlags) ()
  #16 0x0000000001c146d0 in HPHP::vm_call_user_func(HPHP::Variant const&, HPHP::Array const&, bool) ()
  #17 0x0000000001c17107 in HPHP::AutoloadHandler::Result HPHP::AutoloadHandler::loadFromMap<HPHP::ConstantExistsChecker>(HPHP::String const&, HPHP::String const&, bool, HPHP::ConstantExistsChecker const&) ()
  #18 0x0000000001c174ed in HPHP::AutoloadHandler::autoloadConstant(HPHP::StringData*) ()
  #19 0x00000000020c1c73 in HPHP::Unit::loadCns(HPHP::StringData const*) ()
  #20 0x0000000001f1d10e in HPHP::Transl::lookupCnsHelper(HPHP::TypedValue const*, HPHP::StringData*, bool) ()
  #21 0x000000002827f855 in ?? ()
  #22 0x00000000026e566e in enterTCHelper ()
  #23 0x0000000001f35ef0 in HPHP::Transl::TranslatorX64::enterTC(unsigned char*, void*) ()
  #24 0x0000000002018004 in HPHP::VMExecutionContext::enterVM(HPHP::TypedValue*, HPHP::ActRec*) ()
  ...
  #28992 0x0000000001c174ed in HPHP::AutoloadHandler::autoloadConstant(HPHP::StringData*) ()
  #28993 0x00000000020c1c73 in HPHP::Unit::loadCns(HPHP::StringData const*) ()
  #28994 0x0000000001f1d10e in HPHP::Transl::lookupCnsHelper(HPHP::TypedValue const*, HPHP::StringData*, bool) ()
  #28995 0x000000002827f855 in ?? ()
  #28996 0x00000000026e566e in enterTCHelper ()
  #28997 0x0000000001f35ef0 in HPHP::Transl::TranslatorX64::enterTC(unsigned char*, void*) ()
  #28998 0x0000000002018004 in HPHP::VMExecutionContext::enterVM(HPHP::TypedValue*, HPHP::ActRec*) ()
  #28999 0x000000000201821c in HPHP::VMExecutionContext::reenterVM(HPHP::TypedValue*, HPHP::ActRec*, HPHP::TypedValue*) ()
  #29000 0x0000000002018612 in HPHP::VMExecutionContext::invokeFunc(HPHP::TypedValue*, HPHP::Func const*, HPHP::Array const&, HPHP::ObjectData*, HPHP::Class*, HPHP::VarEnv*, HPHP::StringData*, HPHP::VMExecutionContext::InvokeFlags) ()
  #29001 0x0000000001c146d0 in HPHP::vm_call_user_func(HPHP::Variant const&, HPHP::Array const&, bool) ()
  #29002 0x0000000001c17676 in HPHP::AutoloadHandler::Result HPHP::AutoloadHandler::loadFromMap<HPHP::ClassExistsChecker>(HPHP::String const&, HPHP::String const&, bool, HPHP::ClassExistsChecker const&) ()
  #29003 0x0000000001c17a57 in HPHP::AutoloadHandler::invokeHandler(HPHP::String const&, bool) ()
  #29004 0x00000000020cb504 in HPHP::Unit::loadClass(HPHP::NamedEntity const*, HPHP::StringData const*) ()
  #29005 0x000000000203247e in void HPHP::VMExecutionContext::dispatchImpl<2>(int) ()
  #29006 0x000000000203ef43 in HPHP::VMExecutionContext::dispatchBB() ()
  #29007 0x0000000001f35f70 in HPHP::Transl::TranslatorX64::enterTC(unsigned char*, void*) ()
  #29008 0x0000000002017ed7 in HPHP::VMExecutionContext::enterVM(HPHP::TypedValue*, HPHP::ActRec*) ()
  #29009 0x000000000201888d in HPHP::VMExecutionContext::invokeFunc(HPHP::TypedValue*, HPHP::Func const*, HPHP::Array const&, HPHP::ObjectData*, HPHP::Class*, HPHP::VarEnv*, HPHP::StringData*, HPHP::VMExecutionContext::InvokeFlags) ()
  #29010 0x0000000002018ea0 in HPHP::VMExecutionContext::invokeUnit(HPHP::TypedValue*, HPHP::Unit*) ()
  #29011 0x0000000001c15964 in HPHP::invoke_file(HPHP::String const&, bool, char const*) ()
  #29012 0x0000000001c19a52 in HPHP::include_impl_invoke(HPHP::String const&, bool, char const*) ()
  #29013 0x0000000001c5d883 in HPHP::hphp_invoke(HPHP::ExecutionContext*, std::basic_fbstring<char, std::char_traits<char>, std::allocator<char>, std::fbstring_core<char> > const&, bool, HPHP::Array const&, HPHP::VRefParamValue const&, std::basic_fbstring<char, std::char_traits<char>, std::allocator<char>, std::fbstring_core<char> > const&, std::basic_fbstring<char, std::char_traits<char>, std::allocator<char>, std::fbstring_core<char> > const&, bool&, std::basic_fbstring<char, std::char_traits<char>, std::allocator<char>, std::fbstring_core<char> >&, bool, bool, bool) ()
  #29014 0x0000000001b2659e in HPHP::RPCRequestHandler::executePHPFunction(HPHP::Transport*, HPHP::SourceRootInfo&, HPHP::RPCRequestHandler::ReturnEncodeType) ()
  #29015 0x0000000001b285d3 in HPHP::RPCRequestHandler::handleRequest(HPHP::Transport*) ()
  #29016 0x0000000001b6b85b in HPHP::XboxWorker::doJob(HPHP::XboxTransport*) ()
  #29017 0x0000000001b669a3 in HPHP::JobQueueWorker<HPHP::XboxTransport*, HPHP::Server*, true, false, HPHP::JobQueueDropVMStack>::start() ()
  #29018 0x000000000252c127 in HPHP::AsyncFuncImpl::ThreadFunc(void*) ()
  #29019 0x00007f1ce3787f88 in start_thread (arg=0x7f1c377ff700)

Reviewed By: @jdelong

Differential Revision: D1052293
  • Loading branch information
paroski authored and sgolemon committed Nov 13, 2013
1 parent 26c9664 commit de2b766
Showing 1 changed file with 34 additions and 2 deletions.
36 changes: 34 additions & 2 deletions hphp/runtime/vm/bytecode.cpp
Expand Up @@ -1358,13 +1358,26 @@ void VMExecutionContext::shuffleMagicArgs(ActRec* ar) {
ar->setNumArgs(2);
}

// This helper only does a stack overflow check for the native stack
static inline void checkNativeStack() {
ThreadInfo* info = ThreadInfo::s_threadInfo.getNoCheck();
// Check whether func's maximum stack usage would overflow the stack.
// Both native and VM stack overflows are independently possible.
if (!stack_in_bounds(info)) {
TRACE(1, "Maximum stack depth exceeded.\n");
raise_error("Stack overflow");
}
}

// This helper does a stack overflow check on *both* the native stack
// and the VM stack.
static inline void checkStack(Stack& stk, const Func* f) {
ThreadInfo* info = ThreadInfo::s_threadInfo.getNoCheck();
// Check whether func's maximum stack usage would overflow the stack.
// Both native and VM stack overflows are independently possible.
if (!stack_in_bounds(info) ||
stk.wouldOverflow(f->maxStackCells() + kStackCheckPadding)) {
TRACE(1, "Maximum VM stack depth exceeded.\n");
TRACE(1, "Maximum stack depth exceeded.\n");
raise_error("Stack overflow");
}
}
Expand Down Expand Up @@ -1663,7 +1676,12 @@ void VMExecutionContext::invokeFunc(TypedValue* retval,

if (f->attrs() & AttrPhpLeafFn ||
f->numParams() > kStackCheckReenterPadding - kNumActRecCells) {
// Check both the native stack and VM stack for overflow
checkStack(m_stack, f);
} else {
// invokeFunc() must always check the native stack for overflow no
// matter what
checkNativeStack();
}

if (flags & InvokePseudoMain) {
Expand Down Expand Up @@ -1825,10 +1843,17 @@ void VMExecutionContext::invokeFuncFew(TypedValue* retval,
thiz->incRefCount();
}
Cell* savedSP = m_stack.top();

if (f->attrs() & AttrPhpLeafFn ||
argc > kStackCheckReenterPadding - kNumActRecCells) {
// Check both the native stack and VM stack for overflow
checkStack(m_stack, f);
} else {
// invokeFuncFew() must always check the native stack for overflow
// no matter what
checkNativeStack();
}

ActRec* ar = m_stack.allocA();
ar->m_soff = 0;
ar->m_savedRbp = 0;
Expand Down Expand Up @@ -1882,8 +1907,15 @@ void VMExecutionContext::invokeContFunc(const Func* f,

Cell* savedSP = m_stack.top();

// no need to check stack due to ReenterPadding
assert(kStackCheckReenterPadding - kNumActRecCells >= 1);
if (f->attrs() & AttrPhpLeafFn) {
// Check both the native stack and VM stack for overflow
checkStack(m_stack, f);
} else {
// invokeContFunc() must always check the native stack for overflow
// no matter what
checkNativeStack();
}

ActRec* ar = m_stack.allocA();
ar->m_savedRbp = 0;
Expand Down

0 comments on commit de2b766

Please sign in to comment.