1 // Copyright 2020 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 #include "src/heap/base/stack.h"
6
7 #include <limits>
8
9 #include "src/base/platform/platform.h"
10 #include "src/heap/cppgc/globals.h"
11 #include "src/heap/cppgc/sanitizers.h"
12
13 namespace heap {
14 namespace base {
15
16 using IterateStackCallback = void (*)(const Stack*, StackVisitor*, intptr_t*);
17 extern "C" void PushAllRegistersAndIterateStack(const Stack*, StackVisitor*,
18 IterateStackCallback);
19
Stack(const void * stack_start)20 Stack::Stack(const void* stack_start) : stack_start_(stack_start) {}
21
IsOnStack(void * slot) const22 bool Stack::IsOnStack(void* slot) const {
23 #ifdef V8_USE_ADDRESS_SANITIZER
24 // If the slot is part of a fake frame, then it is definitely on the stack.
25 void* real_frame = __asan_addr_is_in_fake_stack(
26 __asan_get_current_fake_stack(), reinterpret_cast<void*>(slot), nullptr,
27 nullptr);
28 if (real_frame) {
29 return true;
30 }
31 // Fall through as there is still a regular stack present even when running
32 // with ASAN fake stacks.
33 #endif // V8_USE_ADDRESS_SANITIZER
34 return v8::base::Stack::GetCurrentStackPosition() <= slot &&
35 slot <= stack_start_;
36 }
37
38 namespace {
39
40 #ifdef V8_USE_ADDRESS_SANITIZER
41
42 // No ASAN support as accessing fake frames otherwise results in
43 // "stack-use-after-scope" warnings.
44 NO_SANITIZE_ADDRESS
IterateAsanFakeFrameIfNecessary(StackVisitor * visitor,void * asan_fake_stack,const void * stack_start,const void * stack_end,void * address)45 void IterateAsanFakeFrameIfNecessary(StackVisitor* visitor,
46 void* asan_fake_stack,
47 const void* stack_start,
48 const void* stack_end, void* address) {
49 // When using ASAN fake stack a pointer to the fake frame is kept on the
50 // native frame. In case |addr| points to a fake frame of the current stack
51 // iterate the fake frame. Frame layout see
52 // https://github.com/google/sanitizers/wiki/AddressSanitizerUseAfterReturn
53 if (asan_fake_stack) {
54 void* fake_frame_begin;
55 void* fake_frame_end;
56 void* real_stack_frame = __asan_addr_is_in_fake_stack(
57 asan_fake_stack, address, &fake_frame_begin, &fake_frame_end);
58 if (real_stack_frame) {
59 // |address| points to a fake frame. Check that the fake frame is part
60 // of this stack.
61 if (stack_start >= real_stack_frame && real_stack_frame >= stack_end) {
62 // Iterate the fake frame.
63 for (void** current = reinterpret_cast<void**>(fake_frame_begin);
64 current < fake_frame_end; ++current) {
65 void* addr = *current;
66 if (addr == nullptr) continue;
67 visitor->VisitPointer(addr);
68 }
69 }
70 }
71 }
72 }
73
74 #endif // V8_USE_ADDRESS_SANITIZER
75
IterateSafeStackIfNecessary(StackVisitor * visitor)76 void IterateSafeStackIfNecessary(StackVisitor* visitor) {
77 #if defined(__has_feature)
78 #if __has_feature(safe_stack)
79 // Source:
80 // https://github.com/llvm/llvm-project/blob/master/compiler-rt/lib/safestack/safestack.cpp
81 constexpr size_t kSafeStackAlignmentBytes = 16;
82 void* stack_end = __builtin___get_unsafe_stack_ptr();
83 void* stack_start = __builtin___get_unsafe_stack_top();
84 CHECK_GT(stack_start, stack_end);
85 CHECK_EQ(0u, reinterpret_cast<uintptr_t>(stack_end) &
86 (kSafeStackAlignmentBytes - 1));
87 CHECK_EQ(0u, reinterpret_cast<uintptr_t>(stack_start) &
88 (kSafeStackAlignmentBytes - 1));
89 void** current = reinterpret_cast<void**>(stack_end);
90 for (; current < stack_start; ++current) {
91 void* address = *current;
92 if (address == nullptr) continue;
93 visitor->VisitPointer(address);
94 }
95 #endif // __has_feature(safe_stack)
96 #endif // defined(__has_feature)
97 }
98
99 // Called by the trampoline that pushes registers on the stack. This method
100 // should never be inlined to ensure that a possible redzone cannot contain
101 // any data that needs to be scanned.
102 V8_NOINLINE
103 // No ASAN support as method accesses redzones while walking the stack.
104 NO_SANITIZE_ADDRESS
IteratePointersImpl(const Stack * stack,StackVisitor * visitor,intptr_t * stack_end)105 void IteratePointersImpl(const Stack* stack, StackVisitor* visitor,
106 intptr_t* stack_end) {
107 #ifdef V8_USE_ADDRESS_SANITIZER
108 void* asan_fake_stack = __asan_get_current_fake_stack();
109 #endif // V8_USE_ADDRESS_SANITIZER
110 // All supported platforms should have their stack aligned to at least
111 // sizeof(void*).
112 constexpr size_t kMinStackAlignment = sizeof(void*);
113 void** current = reinterpret_cast<void**>(stack_end);
114 CHECK_EQ(0u, reinterpret_cast<uintptr_t>(current) & (kMinStackAlignment - 1));
115 for (; current < stack->stack_start(); ++current) {
116 // MSAN: Instead of unpoisoning the whole stack, the slot's value is copied
117 // into a local which is unpoisoned.
118 void* address = *current;
119 MSAN_UNPOISON(&address, sizeof(address));
120 if (address == nullptr) continue;
121 visitor->VisitPointer(address);
122 #ifdef V8_USE_ADDRESS_SANITIZER
123 IterateAsanFakeFrameIfNecessary(visitor, asan_fake_stack,
124 stack->stack_start(), stack_end, address);
125 #endif // V8_USE_ADDRESS_SANITIZER
126 }
127 }
128
129 } // namespace
130
IteratePointers(StackVisitor * visitor) const131 void Stack::IteratePointers(StackVisitor* visitor) const {
132 PushAllRegistersAndIterateStack(this, visitor, &IteratePointersImpl);
133 // No need to deal with callee-saved registers as they will be kept alive by
134 // the regular conservative stack iteration.
135 IterateSafeStackIfNecessary(visitor);
136 }
137
138 } // namespace base
139 } // namespace heap
140