1 // Copyright 2019 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_EXECUTION_STACK_GUARD_H_ 6 #define V8_EXECUTION_STACK_GUARD_H_ 7 8 #include "include/v8-internal.h" 9 #include "src/base/atomicops.h" 10 #include "src/common/globals.h" 11 12 namespace v8 { 13 namespace internal { 14 15 class ExecutionAccess; 16 class InterruptsScope; 17 class Isolate; 18 class Object; 19 20 // StackGuard contains the handling of the limits that are used to limit the 21 // number of nested invocations of JavaScript and the stack size used in each 22 // invocation. 23 class V8_EXPORT_PRIVATE StackGuard final { 24 public: StackGuard(Isolate * isolate)25 explicit StackGuard(Isolate* isolate) : isolate_(isolate) {} 26 27 // Pass the address beyond which the stack should not grow. The stack 28 // is assumed to grow downwards. 29 void SetStackLimit(uintptr_t limit); 30 31 // The simulator uses a separate JS stack. Limits on the JS stack might have 32 // to be adjusted in order to reflect overflows of the C stack, because we 33 // cannot rely on the interleaving of frames on the simulator. 34 void AdjustStackLimitForSimulator(); 35 36 // Threading support. 37 char* ArchiveStackGuard(char* to); 38 char* RestoreStackGuard(char* from); ArchiveSpacePerThread()39 static int ArchiveSpacePerThread() { return sizeof(ThreadLocal); } 40 void FreeThreadResources(); 41 // Sets up the default stack guard for this thread. 42 void InitThread(const ExecutionAccess& lock); 43 44 #define INTERRUPT_LIST(V) \ 45 V(TERMINATE_EXECUTION, TerminateExecution, 0) \ 46 V(GC_REQUEST, GC, 1) \ 47 V(INSTALL_CODE, InstallCode, 2) \ 48 V(API_INTERRUPT, ApiInterrupt, 3) \ 49 V(DEOPT_MARKED_ALLOCATION_SITES, DeoptMarkedAllocationSites, 4) \ 50 V(GROW_SHARED_MEMORY, GrowSharedMemory, 5) \ 51 V(LOG_WASM_CODE, LogWasmCode, 6) \ 52 V(WASM_CODE_GC, WasmCodeGC, 7) 53 54 #define V(NAME, Name, id) \ 55 inline bool Check##Name() { return CheckInterrupt(NAME); } \ 56 inline void Request##Name() { RequestInterrupt(NAME); } \ 57 inline void Clear##Name() { ClearInterrupt(NAME); } 58 INTERRUPT_LIST(V) 59 #undef V 60 61 // Flag used to set the interrupt causes. 62 enum InterruptFlag { 63 #define V(NAME, Name, id) NAME = (1 << id), 64 INTERRUPT_LIST(V) 65 #undef V 66 #define V(NAME, Name, id) NAME | 67 ALL_INTERRUPTS = INTERRUPT_LIST(V) 0 68 #undef V 69 }; 70 climit()71 uintptr_t climit() { return thread_local_.climit(); } jslimit()72 uintptr_t jslimit() { return thread_local_.jslimit(); } 73 // This provides an asynchronous read of the stack limits for the current 74 // thread. There are no locks protecting this, but it is assumed that you 75 // have the global V8 lock if you are using multiple V8 threads. real_climit()76 uintptr_t real_climit() { return thread_local_.real_climit_; } real_jslimit()77 uintptr_t real_jslimit() { return thread_local_.real_jslimit_; } address_of_jslimit()78 Address address_of_jslimit() { 79 return reinterpret_cast<Address>(&thread_local_.jslimit_); 80 } address_of_real_jslimit()81 Address address_of_real_jslimit() { 82 return reinterpret_cast<Address>(&thread_local_.real_jslimit_); 83 } 84 85 // If the stack guard is triggered, but it is not an actual 86 // stack overflow, then handle the interruption accordingly. 87 Object HandleInterrupts(); 88 89 static constexpr int kSizeInBytes = 7 * kSystemPointerSize; 90 91 private: 92 bool CheckInterrupt(InterruptFlag flag); 93 void RequestInterrupt(InterruptFlag flag); 94 void ClearInterrupt(InterruptFlag flag); 95 int FetchAndClearInterrupts(); 96 97 // You should hold the ExecutionAccess lock when calling this method. has_pending_interrupts(const ExecutionAccess & lock)98 bool has_pending_interrupts(const ExecutionAccess& lock) { 99 return thread_local_.interrupt_flags_ != 0; 100 } 101 102 // You should hold the ExecutionAccess lock when calling this method. 103 inline void set_interrupt_limits(const ExecutionAccess& lock); 104 105 // Reset limits to actual values. For example after handling interrupt. 106 // You should hold the ExecutionAccess lock when calling this method. 107 inline void reset_limits(const ExecutionAccess& lock); 108 109 // Enable or disable interrupts. 110 void EnableInterrupts(); 111 void DisableInterrupts(); 112 113 #if V8_TARGET_ARCH_64_BIT 114 static const uintptr_t kInterruptLimit = uintptr_t{0xfffffffffffffffe}; 115 static const uintptr_t kIllegalLimit = uintptr_t{0xfffffffffffffff8}; 116 #else 117 static const uintptr_t kInterruptLimit = 0xfffffffe; 118 static const uintptr_t kIllegalLimit = 0xfffffff8; 119 #endif 120 121 void PushInterruptsScope(InterruptsScope* scope); 122 void PopInterruptsScope(); 123 124 class ThreadLocal final { 125 public: ThreadLocal()126 ThreadLocal() {} 127 128 void Initialize(Isolate* isolate, const ExecutionAccess& lock); 129 130 // The stack limit is split into a JavaScript and a C++ stack limit. These 131 // two are the same except when running on a simulator where the C++ and 132 // JavaScript stacks are separate. Each of the two stack limits have two 133 // values. The one with the real_ prefix is the actual stack limit 134 // set for the VM. The one without the real_ prefix has the same value as 135 // the actual stack limit except when there is an interruption (e.g. debug 136 // break or preemption) in which case it is lowered to make stack checks 137 // fail. Both the generated code and the runtime system check against the 138 // one without the real_ prefix. 139 140 // Actual JavaScript stack limit set for the VM. 141 uintptr_t real_jslimit_ = kIllegalLimit; 142 // Actual C++ stack limit set for the VM. 143 uintptr_t real_climit_ = kIllegalLimit; 144 145 // jslimit_ and climit_ can be read without any lock. 146 // Writing requires the ExecutionAccess lock. 147 base::AtomicWord jslimit_ = kIllegalLimit; 148 base::AtomicWord climit_ = kIllegalLimit; 149 jslimit()150 uintptr_t jslimit() { 151 return bit_cast<uintptr_t>(base::Relaxed_Load(&jslimit_)); 152 } set_jslimit(uintptr_t limit)153 void set_jslimit(uintptr_t limit) { 154 return base::Relaxed_Store(&jslimit_, 155 static_cast<base::AtomicWord>(limit)); 156 } climit()157 uintptr_t climit() { 158 return bit_cast<uintptr_t>(base::Relaxed_Load(&climit_)); 159 } set_climit(uintptr_t limit)160 void set_climit(uintptr_t limit) { 161 return base::Relaxed_Store(&climit_, 162 static_cast<base::AtomicWord>(limit)); 163 } 164 165 InterruptsScope* interrupt_scopes_ = nullptr; 166 intptr_t interrupt_flags_ = 0; 167 }; 168 169 // TODO(isolates): Technically this could be calculated directly from a 170 // pointer to StackGuard. 171 Isolate* isolate_; 172 ThreadLocal thread_local_; 173 174 friend class Isolate; 175 friend class StackLimitCheck; 176 friend class InterruptsScope; 177 178 DISALLOW_COPY_AND_ASSIGN(StackGuard); 179 }; 180 181 STATIC_ASSERT(StackGuard::kSizeInBytes == sizeof(StackGuard)); 182 183 } // namespace internal 184 } // namespace v8 185 186 #endif // V8_EXECUTION_STACK_GUARD_H_ 187