1 // Copyright 2017 The Chromium 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 #include "build/build_config.h"
6 
7 #include "third_party/blink/renderer/platform/wtf/stack_util.h"
8 
9 #include "third_party/blink/renderer/platform/wtf/assertions.h"
10 #include "third_party/blink/renderer/platform/wtf/threading.h"
11 
12 #if defined(OS_WIN)
13 #include <stddef.h>
14 #include <windows.h>
15 #include <winnt.h>
16 #elif defined(__GLIBC__)
17 extern "C" void* __libc_stack_end;  // NOLINT
18 #endif
19 
20 #if defined(OS_FREEBSD)
21 #include <sys/signal.h>
22 #include <pthread_np.h>
23 #endif
24 
25 namespace WTF {
26 
GetUnderestimatedStackSize()27 size_t GetUnderestimatedStackSize() {
28 // FIXME: ASAN bot uses a fake stack as a thread stack frame,
29 // and its size is different from the value which APIs tells us.
30 #if defined(ADDRESS_SANITIZER)
31   return 0;
32 
33 // FIXME: On Mac OSX and Linux, this method cannot estimate stack size
34 // correctly for the main thread.
35 
36 #elif defined(__GLIBC__) || defined(OS_ANDROID) || defined(OS_FREEBSD) || \
37     defined(OS_FUCHSIA)
38   // pthread_getattr_np() can fail if the thread is not invoked by
39   // pthread_create() (e.g., the main thread of blink_unittests).
40   // If so, a conservative size estimate is returned.
41 
42   pthread_attr_t attr;
43   int error;
44 #if defined(OS_FREEBSD)
45   pthread_attr_init(&attr);
46   error = pthread_attr_get_np(pthread_self(), &attr);
47 #else
48   error = pthread_getattr_np(pthread_self(), &attr);
49 #endif
50   if (!error) {
51     void* base;
52     size_t size;
53     error = pthread_attr_getstack(&attr, &base, &size);
54     CHECK(!error);
55     pthread_attr_destroy(&attr);
56     return size;
57   }
58 #if defined(OS_FREEBSD)
59   pthread_attr_destroy(&attr);
60 #endif
61 
62   // Return a 512k stack size, (conservatively) assuming the following:
63   //  - that size is much lower than the pthreads default (x86 pthreads has a 2M
64   //    default.)
65   //  - no one is running Blink with an RLIMIT_STACK override, let alone as
66   //    low as 512k.
67   //
68   return 512 * 1024;
69 #elif defined(OS_MACOSX)
70   // pthread_get_stacksize_np() returns too low a value for the main thread on
71   // OSX 10.9,
72   // http://mail.openjdk.java.net/pipermail/hotspot-dev/2013-October/011369.html
73   //
74   // Multiple workarounds possible, adopt the one made by
75   // https://github.com/robovm/robovm/issues/274
76   // (cf.
77   // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html
78   // on why hardcoding sizes is reasonable.)
79   if (pthread_main_np()) {
80 #if defined(IOS)
81     pthread_attr_t attr;
82     pthread_attr_init(&attr);
83     size_t guardSize = 0;
84     pthread_attr_getguardsize(&attr, &guardSize);
85     // Stack size for the main thread is 1MB on iOS including the guard page
86     // size.
87     return (1 * 1024 * 1024 - guardSize);
88 #else
89     // Stack size for the main thread is 8MB on OSX excluding the guard page
90     // size.
91     return (8 * 1024 * 1024);
92 #endif
93   }
94   return pthread_get_stacksize_np(pthread_self());
95 #elif defined(OS_WIN) && defined(COMPILER_MSVC)
96 return Threading::ThreadStackSize();
97 #else
98 #error "Stack frame size estimation not supported on this platform."
99   return 0;
100 #endif
101 }
102 
GetStackStart()103 void* GetStackStart() {
104 #if defined(__GLIBC__) || defined(OS_ANDROID) || defined(OS_FREEBSD) || \
105     defined(OS_FUCHSIA)
106   pthread_attr_t attr;
107   int error;
108 #if defined(OS_FREEBSD)
109   pthread_attr_init(&attr);
110   error = pthread_attr_get_np(pthread_self(), &attr);
111 #else
112   error = pthread_getattr_np(pthread_self(), &attr);
113 #endif
114   if (!error) {
115     void* base;
116     size_t size;
117     error = pthread_attr_getstack(&attr, &base, &size);
118     CHECK(!error);
119     pthread_attr_destroy(&attr);
120     return reinterpret_cast<uint8_t*>(base) + size;
121   }
122 #if defined(OS_FREEBSD)
123   pthread_attr_destroy(&attr);
124 #endif
125 #if defined(__GLIBC__)
126   // pthread_getattr_np can fail for the main thread. In this case
127   // just like NaCl we rely on the __libc_stack_end to give us
128   // the start of the stack.
129   // See https://code.google.com/p/nativeclient/issues/detail?id=3431.
130   return __libc_stack_end;
131 #else
132   NOTREACHED();
133   return nullptr;
134 #endif
135 #elif defined(OS_MACOSX)
136   return pthread_get_stackaddr_np(pthread_self());
137 #elif defined(OS_WIN) && defined(COMPILER_MSVC)
138 // On Windows stack limits for the current thread are available in
139 // the thread information block (TIB). Its fields can be accessed through
140 // FS segment register on x86 and GS segment register on x86_64.
141 // On Windows ARM64, stack limits could be retrieved by calling
142 // GetCurrentThreadStackLimits. This API doesn't work on x86 and x86_64 here
143 // because it requires Windows 8+.
144 #if defined(ARCH_CPU_X86_64)
145   return reinterpret_cast<void*>(__readgsqword(offsetof(NT_TIB64, StackBase)));
146 #elif defined(ARCH_CPU_X86)
147   return reinterpret_cast<void*>(__readfsdword(offsetof(NT_TIB, StackBase)));
148 #elif defined(ARCH_CPU_ARM64)
149   ULONG_PTR lowLimit, highLimit;
150   ::GetCurrentThreadStackLimits(&lowLimit, &highLimit);
151   return reinterpret_cast<void*>(highLimit);
152 #endif
153 #else
154 #error Unsupported getStackStart on this platform.
155 #endif
156 }
157 
GetCurrentStackPosition()158 uintptr_t GetCurrentStackPosition() {
159 #if defined(COMPILER_MSVC)
160   return reinterpret_cast<uintptr_t>(_AddressOfReturnAddress());
161 #else
162   return reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
163 #endif
164 }
165 
166 namespace internal {
167 
168 uintptr_t g_main_thread_stack_start = 0;
169 uintptr_t g_main_thread_underestimated_stack_size = 0;
170 
InitializeMainThreadStackEstimate()171 void InitializeMainThreadStackEstimate() {
172   // getStackStart is exclusive, not inclusive (i.e. it points past the last
173   // page of the stack in linear order). So, to ensure an inclusive comparison,
174   // subtract here and below.
175   g_main_thread_stack_start =
176       reinterpret_cast<uintptr_t>(GetStackStart()) - sizeof(void*);
177 
178   size_t underestimated_stack_size = GetUnderestimatedStackSize();
179   if (underestimated_stack_size > sizeof(void*)) {
180     underestimated_stack_size = underestimated_stack_size - sizeof(void*);
181   }
182   g_main_thread_underestimated_stack_size = underestimated_stack_size;
183 }
184 
185 #if defined(OS_WIN) && defined(COMPILER_MSVC)
ThreadStackSize()186 size_t ThreadStackSize() {
187   // Notice that we cannot use the TIB's StackLimit for the stack end, as i
188   // tracks the end of the committed range. We're after the end of the reserved
189   // stack area (most of which will be uncommitted, most times.)
190   MEMORY_BASIC_INFORMATION stack_info;
191   memset(&stack_info, 0, sizeof(MEMORY_BASIC_INFORMATION));
192   size_t result_size =
193       VirtualQuery(&stack_info, &stack_info, sizeof(MEMORY_BASIC_INFORMATION));
194   DCHECK_GE(result_size, sizeof(MEMORY_BASIC_INFORMATION));
195   uint8_t* stack_end = reinterpret_cast<uint8_t*>(stack_info.AllocationBase);
196 
197   uint8_t* stack_start = reinterpret_cast<uint8_t*>(WTF::GetStackStart());
198   CHECK(stack_start);
199   CHECK_GT(stack_start, stack_end);
200   size_t thread_stack_size = static_cast<size_t>(stack_start - stack_end);
201   // When the third last page of the reserved stack is accessed as a
202   // guard page, the second last page will be committed (along with removing
203   // the guard bit on the third last) _and_ a stack overflow exception
204   // is raised.
205   //
206   // We have zero interest in running into stack overflow exceptions while
207   // marking objects, so simply consider the last three pages + one above
208   // as off-limits and adjust the reported stack size accordingly.
209   //
210   // http://blogs.msdn.com/b/satyem/archive/2012/08/13/thread-s-stack-memory-management.aspx
211   // explains the details.
212   CHECK_GT(thread_stack_size, 4u * 0x1000);
213   thread_stack_size -= 4 * 0x1000;
214   return thread_stack_size;
215 }
216 #endif
217 
218 }  // namespace internal
219 
220 }  // namespace WTF
221