/* * Copyright (C) 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef JSStackInlines_h #define JSStackInlines_h #include "CallFrame.h" #include "CodeBlock.h" #include "JSStack.h" #include namespace JSC { inline Register* JSStack::getTopOfFrame(CallFrame* frame) { if (UNLIKELY(!frame)) return begin(); return frame->frameExtent(); } inline Register* JSStack::getTopOfStack() { return getTopOfFrame(m_topCallFrame); } inline Register* JSStack::getStartOfFrame(CallFrame* frame) { CallFrame* callerFrame = frame->callerFrameNoFlags(); return getTopOfFrame(callerFrame); } inline CallFrame* JSStack::pushFrame(CallFrame* callerFrame, class CodeBlock* codeBlock, JSScope* scope, int argsCount, JSObject* callee) { ASSERT(!!scope); Register* oldEnd = getTopOfStack(); // Ensure that we have enough space for the parameters: size_t paddedArgsCount = argsCount; if (codeBlock) { size_t numParameters = codeBlock->numParameters(); if (paddedArgsCount < numParameters) paddedArgsCount = numParameters; } Register* newCallFrameSlot = oldEnd + paddedArgsCount + JSStack::CallFrameHeaderSize; #if ENABLE(DEBUG_JSSTACK) newCallFrameSlot += JSStack::FenceSize; #endif Register* newEnd = newCallFrameSlot; if (!!codeBlock) newEnd += codeBlock->m_numCalleeRegisters; // Ensure that we have the needed stack capacity to push the new frame: if (!grow(newEnd)) return 0; // Compute the address of the new frame for this invocation: CallFrame* newCallFrame = CallFrame::create(newCallFrameSlot); ASSERT(!!newCallFrame); // The caller frame should always be the real previous frame on the stack, // and not a potential GlobalExec that was passed in. Point callerFrame to // the top frame on the stack. callerFrame = m_topCallFrame; // Initialize the frame header: newCallFrame->init(codeBlock, 0, scope, callerFrame->addHostCallFrameFlag(), argsCount, callee); ASSERT(!!newCallFrame->scope()); // Pad additional args if needed: // Note: we need to subtract 1 from argsCount and paddedArgsCount to // exclude the this pointer. for (size_t i = argsCount-1; i < paddedArgsCount-1; ++i) newCallFrame->setArgument(i, jsUndefined()); installFence(newCallFrame, __FUNCTION__, __LINE__); validateFence(newCallFrame, __FUNCTION__, __LINE__); installTrapsAfterFrame(newCallFrame); // Push the new frame: m_topCallFrame = newCallFrame; return newCallFrame; } inline void JSStack::popFrame(CallFrame* frame) { validateFence(frame, __FUNCTION__, __LINE__); CallFrame* callerFrame = frame->callerFrameNoFlags(); // Pop to the caller: m_topCallFrame = callerFrame; // If we are popping the very first frame from the stack i.e. no more // frames before this, then we can now safely shrink the stack. In // this case, we're shrinking all the way to the beginning since there // are no more frames on the stack. if (!callerFrame) shrink(begin()); installTrapsAfterFrame(callerFrame); } #if ENABLE(DEBUG_JSSTACK) inline JSValue JSStack::generateFenceValue(size_t argIndex) { unsigned fenceBits = 0xfacebad0 | ((argIndex+1) & 0xf); JSValue fenceValue = JSValue(fenceBits); return fenceValue; } // The JSStack fences mechanism works as follows: // 1. A fence is a number (JSStack::FenceSize) of JSValues that are initialized // with values generated by JSStack::generateFenceValue(). // 2. When pushFrame() is called, the fence is installed after the max extent // of the previous topCallFrame and the last arg of the new frame: // // | ... | // |--------------------------------------| // | Frame Header of previous frame | // |--------------------------------------| // topCallFrame --> | | // | Locals of previous frame | // |--------------------------------------| // | *** the Fence *** | // |--------------------------------------| // | Args of new frame | // |--------------------------------------| // | Frame Header of new frame | // |--------------------------------------| // frame --> | Locals of new frame | // | | // // 3. In popFrame() and elsewhere, we can call JSStack::validateFence() to // assert that the fence contains the values we expect. inline void JSStack::installFence(CallFrame* frame, const char *function, int lineNo) { UNUSED_PARAM(function); UNUSED_PARAM(lineNo); Register* startOfFrame = getStartOfFrame(frame); // The last argIndex is at: size_t maxIndex = frame->argIndexForRegister(startOfFrame) + 1; size_t startIndex = maxIndex - FenceSize; for (size_t i = startIndex; i < maxIndex; ++i) { JSValue fenceValue = generateFenceValue(i); frame->setArgument(i, fenceValue); } } inline void JSStack::validateFence(CallFrame* frame, const char *function, int lineNo) { UNUSED_PARAM(function); UNUSED_PARAM(lineNo); ASSERT(!!frame->scope()); Register* startOfFrame = getStartOfFrame(frame); size_t maxIndex = frame->argIndexForRegister(startOfFrame) + 1; size_t startIndex = maxIndex - FenceSize; for (size_t i = startIndex; i < maxIndex; ++i) { JSValue fenceValue = generateFenceValue(i); JSValue actualValue = frame->getArgumentUnsafe(i); ASSERT(fenceValue == actualValue); } } // When debugging the JSStack, we install bad values after the extent of the // topCallFrame at the end of pushFrame() and popFrame(). The intention is // to trigger crashes in the event that memory in this supposedly unused // region is read and consumed without proper initialization. After the trap // words are installed, the stack looks like this: // // | ... | // |-----------------------------| // | Frame Header of frame | // |-----------------------------| // topCallFrame --> | | // | Locals of frame | // |-----------------------------| // | *** Trap words *** | // |-----------------------------| // | Unused space ... | // | ... | inline void JSStack::installTrapsAfterFrame(CallFrame* frame) { Register* topOfFrame = getTopOfFrame(frame); const int sizeOfTrap = 64; int32_t* startOfTrap = reinterpret_cast(topOfFrame); int32_t* endOfTrap = startOfTrap + sizeOfTrap; int32_t* endOfCommitedMemory = reinterpret_cast(m_commitEnd); // Make sure we're not exceeding the amount of available memory to write to: if (endOfTrap > endOfCommitedMemory) endOfTrap = endOfCommitedMemory; // Lay the traps: int32_t* p = startOfTrap; while (p < endOfTrap) *p++ = 0xabadcafe; // A bad word to trigger a crash if deref'ed. } #endif // ENABLE(DEBUG_JSSTACK) } // namespace JSC #endif // JSStackInlines_h