diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-05-20 09:56:07 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-05-20 09:56:07 +0000 |
commit | 41386e9cb918eed93b3f13648cbef387e371e451 (patch) | |
tree | a97f9d7bd1d9d091833286085f72da9d83fd0606 /Source/JavaScriptCore/interpreter/JSStackInlines.h | |
parent | e15dd966d523731101f70ccf768bba12435a0208 (diff) | |
download | WebKitGtk-tarball-41386e9cb918eed93b3f13648cbef387e371e451.tar.gz |
webkitgtk-2.4.9webkitgtk-2.4.9
Diffstat (limited to 'Source/JavaScriptCore/interpreter/JSStackInlines.h')
-rw-r--r-- | Source/JavaScriptCore/interpreter/JSStackInlines.h | 263 |
1 files changed, 232 insertions, 31 deletions
diff --git a/Source/JavaScriptCore/interpreter/JSStackInlines.h b/Source/JavaScriptCore/interpreter/JSStackInlines.h index 69508ab5d..5a2aff117 100644 --- a/Source/JavaScriptCore/interpreter/JSStackInlines.h +++ b/Source/JavaScriptCore/interpreter/JSStackInlines.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,61 +33,262 @@ namespace JSC { -inline bool JSStack::ensureCapacityFor(Register* newTopOfStack) +inline Register* JSStack::getTopOfFrame(CallFrame* frame) { -#if !ENABLE(JIT) - return grow(newTopOfStack); -#else - ASSERT(wtfThreadData().stack().isGrowingDownward()); - return newTopOfStack >= m_vm.stackLimit(); -#endif + if (UNLIKELY(!frame)) + return getBaseOfStack(); + return frame->frameExtent(); } -#if !ENABLE(JIT) +inline Register* JSStack::getTopOfStack() +{ + return getTopOfFrame(m_topCallFrame); +} -inline Register* JSStack::topOfFrameFor(CallFrame* frame) +inline Register* JSStack::getStartOfFrame(CallFrame* frame) { - if (UNLIKELY(!frame)) - return baseOfStack(); - return frame->topOfFrame() - 1; + CallFrame* callerFrame = frame->callerFrameSkippingVMEntrySentinel(); + return getTopOfFrame(callerFrame); } -inline Register* JSStack::topOfStack() +inline bool JSStack::entryCheck(class CodeBlock* codeBlock, int argsCount) { - return topOfFrameFor(m_topCallFrame); + 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 - (2 * JSStack::CallFrameHeaderSize) + 1; + +#if ENABLE(DEBUG_JSSTACK) + newCallFrameSlot -= JSStack::FenceSize; +#endif + + Register* newEnd = newCallFrameSlot; + if (!!codeBlock) + newEnd += virtualRegisterForLocal(codeBlock->frameRegisterCount()).offset(); + + // Ensure that we have the needed stack capacity to push the new frame: + if (!grow(newEnd)) + return false; + + return true; } -inline void JSStack::shrink(Register* newTopOfStack) +inline CallFrame* JSStack::pushFrame(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 - (2 * JSStack::CallFrameHeaderSize) + 1; + +#if ENABLE(DEBUG_JSSTACK) + newCallFrameSlot -= JSStack::FenceSize; +#endif + + Register* newEnd = newCallFrameSlot; + if (!!codeBlock) + newEnd += virtualRegisterForLocal(codeBlock->frameRegisterCount()).offset(); + + // Ensure that we have the needed stack capacity to push the new frame: + if (!grow(newEnd)) + return 0; + + // Compute the address of the new VM sentinel frame for this invocation: + CallFrame* newVMEntrySentinelFrame = CallFrame::create(newCallFrameSlot + paddedArgsCount + JSStack::CallFrameHeaderSize); + ASSERT(!!newVMEntrySentinelFrame); + + // 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. + CallFrame* callerFrame = m_topCallFrame; + + // Initialize the VM sentinel frame header: + newVMEntrySentinelFrame->initializeVMEntrySentinelFrame(callerFrame); + + // Initialize the callee frame header: + newCallFrame->init(codeBlock, 0, scope, newVMEntrySentinelFrame, 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__); + + // Pop off the callee frame and the sentinel frame. + CallFrame* callerFrame = frame->callerFrame()->vmEntrySentinelCallerFrame(); + + // 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(getBaseOfStack()); + + installTrapsAfterFrame(callerFrame); +} + +inline void JSStack::shrink(Register* newEnd) { - Register* newEnd = newTopOfStack - 1; if (newEnd >= m_end) return; - setStackLimit(newTopOfStack); - // Note: Clang complains of an unresolved linkage to maxExcessCapacity if - // invoke std::max() with it as an argument. To work around this, we first - // assign the constant to a local variable, and use the local instead. - ptrdiff_t maxExcessCapacity = JSStack::maxExcessCapacity; - ptrdiff_t maxExcessInRegisters = std::max(maxExcessCapacity, m_reservedZoneSizeInRegisters); - if (m_end == baseOfStack() && (highAddress() - m_commitTop) >= maxExcessInRegisters) + updateStackLimit(newEnd); + if (m_end == getBaseOfStack() && (m_commitEnd - getBaseOfStack()) >= maxExcessCapacity) releaseExcessCapacity(); } -inline bool JSStack::grow(Register* newTopOfStack) +inline bool JSStack::grow(Register* newEnd) { - Register* newEnd = newTopOfStack - 1; if (newEnd >= m_end) return true; - return growSlowCase(newTopOfStack); + return growSlowCase(newEnd); } -inline void JSStack::setStackLimit(Register* newTopOfStack) +inline void JSStack::updateStackLimit(Register* newEnd) { - Register* newEnd = newTopOfStack - 1; m_end = newEnd; - m_vm.setJSStackLimit(newTopOfStack); +#if USE(SEPARATE_C_AND_JS_STACK) + m_vm.setJSStackLimit(newEnd); +#endif +} + +#if ENABLE(DEBUG_JSSTACK) +inline JSValue JSStack::generateFenceValue(size_t argIndex) +{ + unsigned fenceBits = 0xfacebad0 | ((argIndex+1) & 0xf); + JSValue fenceValue = JSValue(fenceBits); + return fenceValue; } -#endif // !ENABLE(JIT) +// 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 *** | +// |--------------------------------------| +// | VM entry sentinel frame header | +// |--------------------------------------| +// | 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<int32_t*>(topOfFrame); + int32_t* endOfTrap = startOfTrap - sizeOfTrap; + int32_t* endOfCommitedMemory = reinterpret_cast<int32_t*>(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 |