Changeset 161180 in webkit
- Timestamp:
- Dec 31, 2013, 2:26:49 AM (11 years ago)
- Location:
- branches/jsCStack/Source/JavaScriptCore
- Files:
-
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/jsCStack/Source/JavaScriptCore/ChangeLog
r161175 r161180 1 2013-12-31 Mark Lam <mark.lam@apple.com> 2 3 CStack: Need a separate stack limit for the JS stack and the C stack. 4 https://bugs.webkit.org/show_bug.cgi?id=126320. 5 6 Not yet reviewed. 7 8 With this patch, we now accurately track how much JS stack space the 9 VM has used, and cap that at the value specified by Options::maxStackSize(). 10 11 The JS stack space is measured in chunks, one for each VMEntryScope. Each 12 VMEntryScope will also keep a running total of the sum of all the stack 13 usage of previous VMEntryScopes to simplify the calculation of the total 14 usage. 15 16 * interpreter/Interpreter.cpp: 17 (JSC::Interpreter::execute): 18 - In Interpreter::execute() for programs, we were installing the VMEntryScope 19 even if we end up only processing a JSON object. This poses a problem 20 because the code that processes the JSON object will call functions that 21 will re-enter the VM thereby installing another VMEntryScope. Each 22 VMEntryScope expects VM.topCallFrame and VM.stackPointerAtVMEntry to be 23 set up properly for the previous VMEntryScope. However, in this case, 24 we've installed the VMEntryScope for a potential ProgramExecutable but 25 never executed a program to initialize its values for VM.topCallFrame and 26 VM.stackPointerAtVMEntry. This in turn causes a crash when the second 27 VMEntryScope makes use of VM.topCallFrame. 28 The fix is simply to defer installation of the VMEntryScope for the 29 program till we are actually sure that we will attempt to execute a 30 program. 31 32 * llint/LLIntSlowPaths.cpp: 33 (JSC::LLInt::LLINT_SLOW_PATH_DECL(stack_check)): 34 - In llint_stack_check(), we pop the frame that we failed to set up before 35 throwing the StackOverflowError. Added an update of topCallFrame here to 36 reflect this popping action. 37 38 * llint/LowLevelInterpreter64.asm: 39 - The VMEntryScope will have to calculate and set up a jsStackLimit value 40 before we actually know what the stack pointer is at the point when we 41 re-enter the VM. So, instead of using the real stackPointerAtVMEntry for 42 the jsStackLimit calculation, we use an estimate. 43 doCallToJavaScript() here will set VM.stackPointerAtVMEntry to its 44 real value. But before it does that, it needs to adjust VM.jsStackLimit 45 by the delta between the estimate and the real stackPointerAtVMEntry. 46 47 - If doCallToJavaScript() fails its stack check for incoming args, it also 48 needs to clear VM.stackPointerAtVMEntry so that C code can assert that 49 the various pointers into the stack remain consistent. 50 51 By consistent, I mean that, given that the stack grows down, and that 52 VM.stackPointerAtVMEntry and VM.topCallFrame are properly initialized, 53 then the following should always be true: 54 55 let topOfStack = VM.topCallFrame->topOfFrame(); 56 stackPointerAtVMEntry >= topOfStack >= stack pointer in a C helper function 57 and 58 JSStack.containsAddress(stackPointerAtVMEntry) 59 and 60 JSStack.containsAddress(topOfStack) 61 62 Clearing VM.stackPointerAtVMEntry, if the stack check in doCallToJavaScript() 63 fails, indicates we failed to enter the current VMEntryScope and that we 64 should be using the previous VMEntryScope's stackPointerAtVMEntry and 65 topCallFrame for the above assertions. 66 67 NOTE: These assertions are done in VMEntryScope::updateStackLimits(). 68 69 * runtime/VM.cpp: 70 (JSC::VM::VM): 71 * runtime/VM.h: 72 * runtime/VMEntryScope.cpp: 73 (JSC::VMEntryScope::VMEntryScope): 74 (JSC::VMEntryScope::~VMEntryScope): 75 (JSC::VMEntryScope::stackUsageFor): 76 (JSC::VMEntryScope::updateStackLimits): 77 (JSC::VMEntryScope::currentStackPointer): 78 (JSC::VMEntryScope::requiredCapacity): 79 - requiredCapacity() now computes the excess amount C stack capacity that 80 we have available, and discount it in the computation of the JS stack 81 limit. By excess, I mean the amount of unused C stack space that 82 exceeds the amount of unused JS stack space capacity. 83 84 * runtime/VMEntryScope.h: 85 1 86 2013-12-30 Mark Lam <mark.lam@apple.com> 2 87 -
branches/jsCStack/Source/JavaScriptCore/interpreter/Interpreter.cpp
r160967 r161180 771 771 return jsNull(); 772 772 773 VMEntryScope entryScope(vm, scope->globalObject());774 773 if (!vm.isSafeToRecurse()) 775 774 return checkedReturn(throwStackOverflowError(callFrame)); … … 878 877 // object. 879 878 879 VMEntryScope entryScope(vm, scope->globalObject()); 880 880 881 // Compile source to bytecode if necessary: 881 882 if (JSObject* error = program->initializeGlobalProperties(vm, callFrame, scope)) -
branches/jsCStack/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
r161084 r161180 459 459 // throw the StackOverflowError unconditionally. 460 460 #if ENABLE(LLINT_C_LOOP) 461 ASSERT(! exec->vm().interpreter->stack().containsAddress(exec->topOfFrame()));461 ASSERT(!vm.interpreter->stack().containsAddress(exec->topOfFrame())); 462 462 if (LIKELY(vm.interpreter->stack().ensureCapacityFor(exec->topOfFrame()))) 463 463 LLINT_RETURN_TWO(pc, 0); … … 465 465 466 466 exec = exec->callerFrame(); 467 vm.topCallFrame = exec; 467 468 Interpreter::ErrorHandlingMode mode(exec); 468 469 CommonSlowPaths::interpreterThrowInCaller(exec, createStackOverflowError(exec)); -
branches/jsCStack/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
r160947 r161180 180 180 checkStackPointerAlignment(temp2, 0xbad0dc01) 181 181 182 # The jsStackLimit was previously computed in VMEntryScope using an 183 # estimated stackPointerAtVMEntry value. Adjust the jsStackLimit by 184 # the delta between the actual stackPointerAtVMEntry and the estimate 185 # that we used previously. 186 subp VM::stackPointerAtVMEntry[vm], sp, temp2 187 subp VM::m_jsStackLimit[vm], temp2, temp2 188 storep temp2, VM::m_jsStackLimit[vm] 189 storep sp, VM::stackPointerAtVMEntry[vm] 190 182 191 # The stack host zone ensures that we have adequate space for the 183 192 # VMEntrySentinelFrame. Proceed with allocating and initializing the … … 211 220 end 212 221 222 storep 0, VM::stackPointerAtVMEntry[vm] 213 223 cCall2(_llint_throw_stack_overflow_error, vm, protoCallFrame) 214 224 callToJavaScriptEpilogue() -
branches/jsCStack/Source/JavaScriptCore/runtime/VM.cpp
r161174 r161180 168 168 , clientData(0) 169 169 , topCallFrame(CallFrame::noCaller()) 170 , stackPointerAtVMEntry(0) 170 171 , arrayConstructorTable(adoptPtr(new HashTable(JSC::arrayConstructorTable))) 171 172 , arrayPrototypeTable(adoptPtr(new HashTable(JSC::arrayPrototypeTable))) -
branches/jsCStack/Source/JavaScriptCore/runtime/VM.h
r161174 r161180 228 228 ClientData* clientData; 229 229 ExecState* topCallFrame; 230 void* stackPointerAtVMEntry; 230 231 Watchdog watchdog; 231 232 -
branches/jsCStack/Source/JavaScriptCore/runtime/VMEntryScope.cpp
r161174 r161180 27 27 #include "VMEntryScope.h" 28 28 29 #include "Options.h" 29 30 #include "VM.h" 30 31 #include <wtf/StackBounds.h> … … 36 37 , m_stack(wtfThreadData().stack()) 37 38 , m_globalObject(globalObject) 39 , m_prevStackUsage(0) 38 40 , m_prevFirstEntryScope(vm.firstEntryScope) 39 41 , m_prevTopEntryScope(vm.topEntryScope) … … 43 45 #endif 44 46 , m_prevLastStackTop(vm.lastStackTop()) 47 , m_prevStackPointerAtVMEntry(vm.stackPointerAtVMEntry) 48 , m_prevTopCallFrame(vm.topCallFrame) 45 49 { 50 ASSERT(wtfThreadData().stack().isGrowingDownward()); 51 ASSERT(!vm.topCallFrame || currentStackPointer() <= reinterpret_cast<char*>(vm.topCallFrame->topOfFrame())); 52 53 // Step 1: Compute the stack usage of the last VM entry before we install 54 // the current entry scope below. 55 if (vm.topEntryScope) { 56 char* topOfStack = reinterpret_cast<char*>(vm.topCallFrame->topOfFrame()); 57 m_prevStackUsage = vm.topEntryScope->stackUsageFor(topOfStack); 58 } 59 60 // Step 2: Install the current entry scope. 46 61 if (!vm.firstEntryScope) { 47 62 #if ENABLE(ASSEMBLER) … … 55 70 vm.resetDateCache(); 56 71 } 72 vm.stackPointerAtVMEntry = 0; 57 73 vm.topEntryScope = this; 58 74 … … 60 76 vm.clearExceptionStack(); 61 77 78 vm.setLastStackTop(m_stack.origin()); 79 80 // Step 3: Compute the stack limit using the installed entry scope. 62 81 updateStackLimits(); 63 vm.setLastStackTop(m_stack.origin());64 82 } 65 83 … … 73 91 #endif 74 92 m_vm.setLastStackTop(m_prevLastStackTop); 93 m_vm.stackPointerAtVMEntry = m_prevStackPointerAtVMEntry; 94 m_vm.topCallFrame = m_prevTopCallFrame; 95 } 96 97 size_t VMEntryScope::stackUsageFor(char* topOfStack) const 98 { 99 size_t currentStackUsage = 0; 100 ASSERT(m_vm.stackPointerAtVMEntry); 101 char* startOfStack = reinterpret_cast<char*>(m_vm.stackPointerAtVMEntry); 102 ASSERT(topOfStack <= startOfStack); 103 currentStackUsage = startOfStack - topOfStack; 104 105 ASSERT(Options::maxStackSize() >= m_prevStackUsage + currentStackUsage); 106 return m_prevStackUsage + currentStackUsage; 75 107 } 76 108 77 109 void VMEntryScope::updateStackLimits() 78 110 { 111 ASSERT(wtfThreadData().stack().isGrowingDownward()); 112 char* topOfStack = currentStackPointer(); 113 79 114 #if !ENABLE(LLINT_C_LOOP) 80 void* jsStackLimit = m_stack.recursionLimit(requiredCapacity(JSStackCapacity)); 115 char* topOfJSStack = m_vm.topCallFrame ? reinterpret_cast<char*>(m_vm.topCallFrame->topOfFrame()) : topOfStack; 116 117 // If we have not re-entered the VM yet via callToJavaScript / callToNativeFunction, 118 // then stackPointerAtVMEntry will not have been set up yet. Instead, we'll 119 // compute the stack limit relative to the current topOfJSStack (as an estimate 120 // of stackPointerAtVMEntry). When we enter callToJavaScript later, we'll adjust 121 // the stack limit with the delta between the actual stackPointerAtVMEntry and 122 // the estimate value that we use here. 123 if (!m_vm.stackPointerAtVMEntry) 124 m_vm.stackPointerAtVMEntry = topOfJSStack; 125 126 void* jsStackLimit = m_stack.recursionLimit(requiredCapacity(topOfJSStack, JSStackCapacity)); 127 #ifndef NDEBUG 128 char* startOfStack = reinterpret_cast<char*>(m_vm.stackPointerAtVMEntry); 129 char* stackLimit = reinterpret_cast<char*>(jsStackLimit); 130 ASSERT(m_prevStackUsage + (startOfStack - stackLimit) <= Options::maxStackSize()); 131 #endif 81 132 m_vm.setJSStackLimit(jsStackLimit); 82 #endif 83 void* nativeStackLimit = m_stack.recursionLimit(requiredCapacity(NativeStackCapacity)); 133 134 // Some sanity checks for our pointers into the stack: 135 ASSERT(m_vm.interpreter->stack().containsAddress(reinterpret_cast<Register*>(m_vm.stackPointerAtVMEntry))); 136 ASSERT(m_vm.interpreter->stack().containsAddress(reinterpret_cast<Register*>(topOfJSStack))); 137 ASSERT(currentStackPointer() <= topOfJSStack); 138 ASSERT(topOfJSStack <= m_vm.stackPointerAtVMEntry); 139 #endif // !ENABLE(LLINT_C_LOOP) 140 141 void* nativeStackLimit = m_stack.recursionLimit(requiredCapacity(topOfStack, NativeStackCapacity)); 84 142 m_vm.setStackLimit(nativeStackLimit); 85 143 } 86 144 87 size_t VMEntryScope::requiredCapacity(CapacityType type) const145 char* VMEntryScope::currentStackPointer() const 88 146 { 147 char* p; 148 #if ENABLE(LLINT_C_LOOP) 149 p = reinterpret_cast<char*>(m_vm.topCallFrame->topOfFrame()); 150 #else 151 p = reinterpret_cast<char*>(&p); 152 #endif 153 return p; 154 } 155 156 size_t VMEntryScope::requiredCapacity(char* topOfStack, CapacityType type) const 157 { 158 ASSERT(m_stack.isGrowingDownward()); 159 160 size_t excessCStackSize = 0; 161 #if !ENABLE(LLINT_C_LOOP) 162 if (type == JSStackCapacity) { 163 ASSERT(Options::maxStackSize() >= stackUsageFor(topOfStack)); 164 size_t availableJSStack = Options::maxStackSize() - stackUsageFor(topOfStack); 165 166 char* bottomOfStack = reinterpret_cast<char*>(m_stack.origin()); 167 size_t availableCStack = m_stack.size() - (bottomOfStack - topOfStack); 168 if (availableCStack > availableJSStack) 169 excessCStackSize = availableCStack - availableJSStack; 170 } 171 #else 89 172 UNUSED_PARAM(type); 173 ASSERT(type == NativeStackCapacity); 174 #endif 90 175 91 176 // We require a smaller stack budget for the error stack. This is to allow … … 101 186 Interpreter* interpreter = m_vm.interpreter; 102 187 size_t requiredCapacity = interpreter->isInErrorHandlingMode() ? errorModeRequiredStack : requiredStack; 188 requiredCapacity += excessCStackSize; 189 103 190 RELEASE_ASSERT(m_stack.size() >= requiredCapacity); 104 191 return requiredCapacity; -
branches/jsCStack/Source/JavaScriptCore/runtime/VMEntryScope.h
r161174 r161180 49 49 NativeStackCapacity, 50 50 }; 51 size_t requiredCapacity(CapacityType) const; 51 size_t requiredCapacity(char* topOfStack, CapacityType) const; 52 char* currentStackPointer() const; 53 size_t stackUsageFor(char* topOfStack) const; 52 54 53 55 VM& m_vm; … … 55 57 StackBounds m_stack; 56 58 JSGlobalObject* m_globalObject; 59 size_t m_prevStackUsage; 57 60 58 61 // The following pointers may point to a different thread's stack. … … 64 67 #endif 65 68 void* m_prevLastStackTop; 69 void* m_prevStackPointerAtVMEntry; 70 ExecState* m_prevTopCallFrame; 66 71 }; 67 72
Note:
See TracChangeset
for help on using the changeset viewer.