xref: /aosp_15_r20/external/cronet/base/allocator/partition_allocator/src/partition_alloc/stack/stack.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2021 The Chromium Authors
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 "partition_alloc/stack/stack.h"
6 
7 #include <cstdint>
8 #include <limits>
9 
10 #include "build/build_config.h"
11 #include "partition_alloc/partition_alloc_base/compiler_specific.h"
12 #include "partition_alloc/partition_alloc_buildflags.h"
13 #include "partition_alloc/partition_alloc_check.h"
14 
15 #if BUILDFLAG(IS_WIN)
16 #include <windows.h>
17 #else
18 #include <pthread.h>
19 #endif
20 
21 #if defined(LIBC_GLIBC)
22 extern "C" void* __libc_stack_end;
23 #endif
24 
25 namespace partition_alloc::internal {
26 
27 #if BUILDFLAG(IS_WIN)
28 
GetStackTop()29 void* GetStackTop() {
30 #if defined(ARCH_CPU_X86_64)
31   return reinterpret_cast<void*>(
32       reinterpret_cast<NT_TIB64*>(NtCurrentTeb())->StackBase);
33 #elif defined(ARCH_CPU_32_BITS)
34   return reinterpret_cast<void*>(
35       reinterpret_cast<NT_TIB*>(NtCurrentTeb())->StackBase);
36 #elif defined(ARCH_CPU_ARM64)
37   // Windows 8 and later, see
38   // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentthreadstacklimits
39   ULONG_PTR lowLimit, highLimit;
40   ::GetCurrentThreadStackLimits(&lowLimit, &highLimit);
41   return reinterpret_cast<void*>(highLimit);
42 #else
43 #error "Unsupported GetStackStart"
44 #endif
45 }
46 
47 #elif BUILDFLAG(IS_APPLE)
48 
49 void* GetStackTop() {
50   return pthread_get_stackaddr_np(pthread_self());
51 }
52 
53 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
54 
55 void* GetStackTop() {
56   pthread_attr_t attr;
57   int error = pthread_getattr_np(pthread_self(), &attr);
58   if (!error) {
59     void* base;
60     size_t size;
61     error = pthread_attr_getstack(&attr, &base, &size);
62     PA_CHECK(!error);
63     pthread_attr_destroy(&attr);
64     return reinterpret_cast<uint8_t*>(base) + size;
65   }
66 
67 #if defined(LIBC_GLIBC)
68   // pthread_getattr_np can fail for the main thread. In this case
69   // just like NaCl we rely on the __libc_stack_end to give us
70   // the start of the stack.
71   // See https://code.google.com/p/nativeclient/issues/detail?id=3431.
72   return __libc_stack_end;
73 #else
74   return nullptr;
75 #endif  // defined(LIBC_GLIBC)
76 }
77 
78 #else  // BUILDFLAG(IS_WIN)
79 #error "Unsupported GetStackTop"
80 #endif  // BUILDFLAG(IS_WIN)
81 
82 using IterateStackCallback = void (*)(const Stack*, StackVisitor*, uintptr_t*);
83 extern "C" void PAPushAllRegistersAndIterateStack(const Stack*,
84                                                   StackVisitor*,
85                                                   IterateStackCallback);
86 
Stack(void * stack_top)87 Stack::Stack(void* stack_top) : stack_top_(stack_top) {
88   PA_DCHECK(stack_top);
89 }
90 
GetStackPointer()91 PA_NOINLINE uintptr_t* GetStackPointer() {
92   return reinterpret_cast<uintptr_t*>(__builtin_frame_address(0));
93 }
94 
95 namespace {
96 
IterateSafeStackIfNecessary(StackVisitor * visitor)97 [[maybe_unused]] void IterateSafeStackIfNecessary(StackVisitor* visitor) {
98 #if defined(__has_feature)
99 #if __has_feature(safe_stack)
100   // Source:
101   // https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/safestack/safestack.cpp
102   constexpr size_t kSafeStackAlignmentBytes = 16;
103   void* stack_ptr = __builtin___get_unsafe_stack_ptr();
104   void* stack_top = __builtin___get_unsafe_stack_top();
105   PA_CHECK(stack_top > stack_ptr);
106   PA_CHECK(0u == (reinterpret_cast<uintptr_t>(stack_ptr) &
107                   (kSafeStackAlignmentBytes - 1)));
108   PA_CHECK(0u == (reinterpret_cast<uintptr_t>(stack_top) &
109                   (kSafeStackAlignmentBytes - 1)));
110   visitor->VisitStack(reinterpret_cast<uintptr_t*>(stack_ptr),
111                       reinterpret_cast<uintptr_t*>(stack_top));
112 #endif  // __has_feature(safe_stack)
113 #endif  // defined(__has_feature)
114 }
115 
116 // Called by the trampoline that pushes registers on the stack. This method
117 // should never be inlined to ensure that a possible redzone cannot contain
118 // any data that needs to be scanned.
119 // No ASAN support as method accesses redzones while walking the stack.
IteratePointersImpl(const Stack * stack,StackVisitor * visitor,uintptr_t * stack_ptr)120 [[maybe_unused]] PA_NOINLINE PA_NO_SANITIZE("address") void IteratePointersImpl(
121     const Stack* stack,
122     StackVisitor* visitor,
123     uintptr_t* stack_ptr) {
124   PA_DCHECK(stack);
125   PA_DCHECK(visitor);
126   PA_CHECK(nullptr != stack->stack_top());
127   // All supported platforms should have their stack aligned to at least
128   // sizeof(void*).
129   constexpr size_t kMinStackAlignment = sizeof(void*);
130   PA_CHECK(0u ==
131            (reinterpret_cast<uintptr_t>(stack_ptr) & (kMinStackAlignment - 1)));
132   visitor->VisitStack(stack_ptr,
133                       reinterpret_cast<uintptr_t*>(stack->stack_top()));
134 }
135 
136 }  // namespace
137 
IteratePointers(StackVisitor * visitor) const138 void Stack::IteratePointers(StackVisitor* visitor) const {
139 #if BUILDFLAG(STACK_SCAN_SUPPORTED)
140   PAPushAllRegistersAndIterateStack(this, visitor, &IteratePointersImpl);
141   // No need to deal with callee-saved registers as they will be kept alive by
142   // the regular conservative stack iteration.
143   IterateSafeStackIfNecessary(visitor);
144 #endif  // BUILDFLAG(STACK_SCAN_SUPPORTED)
145 }
146 
147 StackTopRegistry::StackTopRegistry() = default;
148 StackTopRegistry::~StackTopRegistry() = default;
149 
150 // static
Get()151 StackTopRegistry& StackTopRegistry::Get() {
152   static base::NoDestructor<StackTopRegistry> instance;
153   return *instance.get();
154 }
155 
NotifyThreadCreated(void * stack_top)156 void StackTopRegistry::NotifyThreadCreated(void* stack_top) {
157   const auto tid = base::PlatformThread::CurrentId();
158   ScopedGuard guard(lock_);
159   stack_tops_.insert({tid, stack_top});
160   // Insertion may fail due to an existing entry
161   // (i.e. `insert(...).second == false`), but we allow it instead of `CHECK`ing
162   // it. Guaranteeing this function to be called exactly once is quite hard and
163   // we aim to guarantee "at least once".
164 }
165 
NotifyThreadDestroyed()166 void StackTopRegistry::NotifyThreadDestroyed() {
167   const auto tid = base::PlatformThread::CurrentId();
168   ScopedGuard guard(lock_);
169   PA_DCHECK(1 == stack_tops_.count(tid));
170   stack_tops_.erase(tid);
171 }
172 
GetCurrentThreadStackTop() const173 void* StackTopRegistry::GetCurrentThreadStackTop() const {
174   const auto tid = base::PlatformThread::CurrentId();
175   ScopedGuard guard(lock_);
176   auto it = stack_tops_.find(tid);
177   return it != stack_tops_.end() ? it->second : nullptr;
178 }
179 
180 }  // namespace partition_alloc::internal
181