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