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