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