// Copyright 2020 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/heap/base/stack.h" #include <limits> #include "src/base/platform/platform.h" #include "src/heap/cppgc/globals.h" #include "src/heap/cppgc/sanitizers.h" namespace heap { namespace base { using IterateStackCallback = void (*)(const Stack*, StackVisitor*, intptr_t*); extern "C" void PushAllRegistersAndIterateStack(const Stack*, StackVisitor*, IterateStackCallback); Stack::Stack(const void* stack_start) : stack_start_(stack_start) {} bool Stack::IsOnStack(void* slot) const { void* raw_slot = v8::base::Stack::GetStackSlot(slot); return v8::base::Stack::GetCurrentStackPosition() <= raw_slot && raw_slot <= stack_start_; } namespace { #ifdef V8_USE_ADDRESS_SANITIZER // No ASAN support as accessing fake frames otherwise results in // "stack-use-after-scope" warnings. NO_SANITIZE_ADDRESS void IterateAsanFakeFrameIfNecessary(StackVisitor* visitor, void* asan_fake_stack, const void* stack_start, const void* stack_end, void* address) { // When using ASAN fake stack a pointer to the fake frame is kept on the // native frame. In case |addr| points to a fake frame of the current stack // iterate the fake frame. Frame layout see // https://github.com/google/sanitizers/wiki/AddressSanitizerUseAfterReturn if (asan_fake_stack) { void* fake_frame_begin; void* fake_frame_end; void* real_stack_frame = __asan_addr_is_in_fake_stack( asan_fake_stack, address, &fake_frame_begin, &fake_frame_end); if (real_stack_frame) { // |address| points to a fake frame. Check that the fake frame is part // of this stack. if (stack_start >= real_stack_frame && real_stack_frame >= stack_end) { // Iterate the fake frame. for (void** current = reinterpret_cast<void**>(fake_frame_begin); current < fake_frame_end; ++current) { void* addr = *current; if (addr == nullptr) continue; visitor->VisitPointer(addr); } } } } } #endif // V8_USE_ADDRESS_SANITIZER void IterateSafeStackIfNecessary(StackVisitor* visitor) { #if defined(__has_feature) #if __has_feature(safe_stack) // Source: // https://github.com/llvm/llvm-project/blob/master/compiler-rt/lib/safestack/safestack.cpp constexpr size_t kSafeStackAlignmentBytes = 16; void* stack_end = __builtin___get_unsafe_stack_ptr(); void* stack_start = __builtin___get_unsafe_stack_top(); CHECK_GT(stack_start, stack_end); CHECK_EQ(0u, reinterpret_cast<uintptr_t>(stack_end) & (kSafeStackAlignmentBytes - 1)); CHECK_EQ(0u, reinterpret_cast<uintptr_t>(stack_start) & (kSafeStackAlignmentBytes - 1)); void** current = reinterpret_cast<void**>(stack_end); for (; current < stack_start; ++current) { void* address = *current; if (address == nullptr) continue; visitor->VisitPointer(address); } #endif // __has_feature(safe_stack) #endif // defined(__has_feature) } // Called by the trampoline that pushes registers on the stack. This method // should never be inlined to ensure that a possible redzone cannot contain // any data that needs to be scanned. V8_NOINLINE // No ASAN support as method accesses redzones while walking the stack. NO_SANITIZE_ADDRESS void IteratePointersImpl(const Stack* stack, StackVisitor* visitor, intptr_t* stack_end) { #ifdef V8_USE_ADDRESS_SANITIZER void* asan_fake_stack = __asan_get_current_fake_stack(); #endif // V8_USE_ADDRESS_SANITIZER // All supported platforms should have their stack aligned to at least // sizeof(void*). constexpr size_t kMinStackAlignment = sizeof(void*); void** current = reinterpret_cast<void**>(stack_end); CHECK_EQ(0u, reinterpret_cast<uintptr_t>(current) & (kMinStackAlignment - 1)); for (; current < stack->stack_start(); ++current) { // MSAN: Instead of unpoisoning the whole stack, the slot's value is copied // into a local which is unpoisoned. void* address = *current; MSAN_UNPOISON(&address, sizeof(address)); if (address == nullptr) continue; visitor->VisitPointer(address); #ifdef V8_USE_ADDRESS_SANITIZER IterateAsanFakeFrameIfNecessary(visitor, asan_fake_stack, stack->stack_start(), stack_end, address); #endif // V8_USE_ADDRESS_SANITIZER } } } // namespace void Stack::IteratePointers(StackVisitor* visitor) const { PushAllRegistersAndIterateStack(this, visitor, &IteratePointersImpl); // No need to deal with callee-saved registers as they will be kept alive by // the regular conservative stack iteration. IterateSafeStackIfNecessary(visitor); } } // namespace base } // namespace heap