Commit cfba2e5d authored by Michael Lippautz's avatar Michael Lippautz Committed by Commit Bot

platform, cppgc: Fix stack handling routines

- Provide GetRealStackAddressForSlot that deals with ASAN fake stacks
  properly, also accounting for the fact that ASAN gets its real stack
  address in a nested call.
- Fix cppgc on-stack getter.
- Reuse platform routines in global handles.

Bug: chromium:1139914, chromium:1056170
Change-Id: If11a40d543b33edcea220bb70f170ac018e15053
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2509594
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70899}
parent 9f2dce88
...@@ -130,7 +130,7 @@ void OS::SignalCodeMovingGC() {} ...@@ -130,7 +130,7 @@ void OS::SignalCodeMovingGC() {}
void OS::AdjustSchedulingParams() {} void OS::AdjustSchedulingParams() {}
// static // static
void* Stack::GetStackStart() { Stack::StackSlot Stack::GetStackStart() {
// pthread_getthrds_np creates 3 values: // pthread_getthrds_np creates 3 values:
// __pi_stackaddr, __pi_stacksize, __pi_stackend // __pi_stackaddr, __pi_stacksize, __pi_stackend
......
...@@ -98,7 +98,7 @@ void OS::SignalCodeMovingGC() {} ...@@ -98,7 +98,7 @@ void OS::SignalCodeMovingGC() {}
void OS::AdjustSchedulingParams() {} void OS::AdjustSchedulingParams() {}
// static // static
void* Stack::GetStackStart() { Stack::StackSlot Stack::GetStackStart() {
pthread_attr_t attr; pthread_attr_t attr;
int error; int error;
pthread_attr_init(&attr); pthread_attr_init(&attr);
......
...@@ -94,7 +94,7 @@ void OS::AdjustSchedulingParams() { ...@@ -94,7 +94,7 @@ void OS::AdjustSchedulingParams() {
} }
// static // static
void* Stack::GetStackStart() { Stack::StackSlot Stack::GetStackStart() {
return pthread_get_stackaddr_np(pthread_self()); return pthread_get_stackaddr_np(pthread_self());
} }
......
...@@ -1003,7 +1003,7 @@ void Thread::SetThreadLocal(LocalStorageKey key, void* value) { ...@@ -1003,7 +1003,7 @@ void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
!defined(V8_OS_SOLARIS) !defined(V8_OS_SOLARIS)
// static // static
void* Stack::GetStackStart() { Stack::StackSlot Stack::GetStackStart() {
pthread_attr_t attr; pthread_attr_t attr;
int error = pthread_getattr_np(pthread_self(), &attr); int error = pthread_getattr_np(pthread_self(), &attr);
if (!error) { if (!error) {
...@@ -1029,7 +1029,9 @@ void* Stack::GetStackStart() { ...@@ -1029,7 +1029,9 @@ void* Stack::GetStackStart() {
// !defined(_AIX) && !defined(V8_OS_SOLARIS) // !defined(_AIX) && !defined(V8_OS_SOLARIS)
// static // static
void* Stack::GetCurrentStackPosition() { return __builtin_frame_address(0); } Stack::StackSlot Stack::GetCurrentStackPosition() {
return __builtin_frame_address(0);
}
#undef LOG_TAG #undef LOG_TAG
#undef MAP_ANONYMOUS #undef MAP_ANONYMOUS
......
...@@ -1395,7 +1395,7 @@ void Thread::SetThreadLocal(LocalStorageKey key, void* value) { ...@@ -1395,7 +1395,7 @@ void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
void OS::AdjustSchedulingParams() {} void OS::AdjustSchedulingParams() {}
// static // static
void* Stack::GetStackStart() { Stack::StackSlot Stack::GetStackStart() {
#if defined(V8_TARGET_ARCH_X64) #if defined(V8_TARGET_ARCH_X64)
return reinterpret_cast<void*>( return reinterpret_cast<void*>(
reinterpret_cast<NT_TIB64*>(NtCurrentTeb())->StackBase); reinterpret_cast<NT_TIB64*>(NtCurrentTeb())->StackBase);
...@@ -1414,7 +1414,7 @@ void* Stack::GetStackStart() { ...@@ -1414,7 +1414,7 @@ void* Stack::GetStackStart() {
} }
// static // static
void* Stack::GetCurrentStackPosition() { Stack::StackSlot Stack::GetCurrentStackPosition() {
#if V8_CC_MSVC #if V8_CC_MSVC
return _AddressOfReturnAddress(); return _AddressOfReturnAddress();
#else #else
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#define V8_BASE_PLATFORM_PLATFORM_H_ #define V8_BASE_PLATFORM_PLATFORM_H_
#include <cstdarg> #include <cstdarg>
#include <cstdint>
#include <string> #include <string>
#include <vector> #include <vector>
...@@ -433,30 +434,43 @@ class V8_BASE_EXPORT Thread { ...@@ -433,30 +434,43 @@ class V8_BASE_EXPORT Thread {
// TODO(v8:10354): Make use of the stack utilities here in V8. // TODO(v8:10354): Make use of the stack utilities here in V8.
class V8_BASE_EXPORT Stack { class V8_BASE_EXPORT Stack {
public: public:
// Convenience wrapper to use stack slots as unsigned values or void*
// pointers.
struct StackSlot {
// NOLINTNEXTLINE
StackSlot(void* value) : value(reinterpret_cast<uintptr_t>(value)) {}
StackSlot(uintptr_t value) : value(value) {} // NOLINT
// NOLINTNEXTLINE
operator void*() const { return reinterpret_cast<void*>(value); }
operator uintptr_t() const { return value; } // NOLINT
uintptr_t value;
};
// Gets the start of the stack of the current thread. // Gets the start of the stack of the current thread.
static void* GetStackStart(); static StackSlot GetStackStart();
// Returns the current stack top. Works correctly with ASAN and SafeStack. // Returns the current stack top. Works correctly with ASAN and SafeStack.
// GetCurrentStackPosition() should not be inlined, because it works on stack // GetCurrentStackPosition() should not be inlined, because it works on stack
// frames if it were inlined into a function with a huge stack frame it would // frames if it were inlined into a function with a huge stack frame it would
// return an address significantly above the actual current stack position. // return an address significantly above the actual current stack position.
static V8_NOINLINE void* GetCurrentStackPosition(); static V8_NOINLINE StackSlot GetCurrentStackPosition();
// Translates an ASAN-based slot to a real stack slot if necessary. // Returns the real stack frame if slot is part of a fake frame, and slot
static void* GetStackSlot(void* slot) { // otherwise.
static StackSlot GetRealStackAddressForSlot(StackSlot slot) {
#ifdef V8_USE_ADDRESS_SANITIZER #ifdef V8_USE_ADDRESS_SANITIZER
void* fake_stack = __asan_get_current_fake_stack(); // ASAN fetches the real stack deeper in the __asan_addr_is_in_fake_stack()
if (fake_stack) { // call (precisely, deeper in __asan_stack_malloc_()), which results in a
void* fake_frame_start; // real frame that could be outside of stack bounds. Adjust for this
// impreciseness here.
constexpr size_t kAsanRealFrameOffsetBytes = 32;
void* real_frame = __asan_addr_is_in_fake_stack( void* real_frame = __asan_addr_is_in_fake_stack(
fake_stack, slot, &fake_frame_start, nullptr); __asan_get_current_fake_stack(), slot, nullptr, nullptr);
if (real_frame) { return real_frame
return reinterpret_cast<void*>( ? (static_cast<char*>(real_frame) + kAsanRealFrameOffsetBytes)
reinterpret_cast<uintptr_t>(real_frame) + : slot;
(reinterpret_cast<uintptr_t>(slot) -
reinterpret_cast<uintptr_t>(fake_frame_start)));
}
}
#endif // V8_USE_ADDRESS_SANITIZER #endif // V8_USE_ADDRESS_SANITIZER
return slot; return slot;
} }
......
...@@ -749,8 +749,7 @@ class GlobalHandles::OnStackTracedNodeSpace final { ...@@ -749,8 +749,7 @@ class GlobalHandles::OnStackTracedNodeSpace final {
void SetStackStart(void* stack_start) { void SetStackStart(void* stack_start) {
CHECK(on_stack_nodes_.empty()); CHECK(on_stack_nodes_.empty());
stack_start_ = stack_start_ = base::Stack::GetRealStackAddressForSlot(stack_start);
GetRealStackAddressForSlot(reinterpret_cast<uintptr_t>(stack_start));
} }
V8_INLINE bool IsOnStack(uintptr_t slot) const; V8_INLINE bool IsOnStack(uintptr_t slot) const;
...@@ -770,10 +769,6 @@ class GlobalHandles::OnStackTracedNodeSpace final { ...@@ -770,10 +769,6 @@ class GlobalHandles::OnStackTracedNodeSpace final {
GlobalHandles* global_handles; GlobalHandles* global_handles;
}; };
// Returns the real stack frame if slot is part of a fake frame, and slot
// otherwise.
V8_INLINE uintptr_t GetRealStackAddressForSlot(uintptr_t slot) const;
// Keeps track of registered handles. The data structure is cleaned on // Keeps track of registered handles. The data structure is cleaned on
// iteration and when adding new references using the current stack address. // iteration and when adding new references using the current stack address.
// Cleaning is based on current stack address and the key of the map which is // Cleaning is based on current stack address and the key of the map which is
...@@ -795,17 +790,6 @@ class GlobalHandles::OnStackTracedNodeSpace final { ...@@ -795,17 +790,6 @@ class GlobalHandles::OnStackTracedNodeSpace final {
size_t acquire_count_ = 0; size_t acquire_count_ = 0;
}; };
uintptr_t GlobalHandles::OnStackTracedNodeSpace::GetRealStackAddressForSlot(
uintptr_t slot) const {
#ifdef V8_USE_ADDRESS_SANITIZER
void* real_frame = __asan_addr_is_in_fake_stack(
__asan_get_current_fake_stack(), reinterpret_cast<void*>(slot), nullptr,
nullptr);
return real_frame ? reinterpret_cast<uintptr_t>(real_frame) : slot;
#endif // V8_USE_ADDRESS_SANITIZER
return slot;
}
bool GlobalHandles::OnStackTracedNodeSpace::IsOnStack(uintptr_t slot) const { bool GlobalHandles::OnStackTracedNodeSpace::IsOnStack(uintptr_t slot) const {
#ifdef V8_USE_ADDRESS_SANITIZER #ifdef V8_USE_ADDRESS_SANITIZER
if (__asan_addr_is_in_fake_stack(__asan_get_current_fake_stack(), if (__asan_addr_is_in_fake_stack(__asan_get_current_fake_stack(),
...@@ -814,7 +798,7 @@ bool GlobalHandles::OnStackTracedNodeSpace::IsOnStack(uintptr_t slot) const { ...@@ -814,7 +798,7 @@ bool GlobalHandles::OnStackTracedNodeSpace::IsOnStack(uintptr_t slot) const {
return true; return true;
} }
#endif // V8_USE_ADDRESS_SANITIZER #endif // V8_USE_ADDRESS_SANITIZER
return stack_start_ >= slot && slot > GetCurrentStackPosition(); return stack_start_ >= slot && slot > base::Stack::GetCurrentStackPosition();
} }
void GlobalHandles::OnStackTracedNodeSpace::NotifyEmptyEmbedderStack() { void GlobalHandles::OnStackTracedNodeSpace::NotifyEmptyEmbedderStack() {
...@@ -858,12 +842,13 @@ GlobalHandles::TracedNode* GlobalHandles::OnStackTracedNodeSpace::Acquire( ...@@ -858,12 +842,13 @@ GlobalHandles::TracedNode* GlobalHandles::OnStackTracedNodeSpace::Acquire(
entry.node.Free(nullptr); entry.node.Free(nullptr);
entry.global_handles = global_handles_; entry.global_handles = global_handles_;
#ifdef V8_USE_ADDRESS_SANITIZER #ifdef V8_USE_ADDRESS_SANITIZER
auto pair = on_stack_nodes_.insert({GetRealStackAddressForSlot(slot), {}}); auto pair = on_stack_nodes_.insert(
{base::Stack::GetRealStackAddressForSlot(slot), {}});
pair.first->second.push_back(std::move(entry)); pair.first->second.push_back(std::move(entry));
TracedNode* result = &(pair.first->second.back().node); TracedNode* result = &(pair.first->second.back().node);
#else // !V8_USE_ADDRESS_SANITIZER #else // !V8_USE_ADDRESS_SANITIZER
auto pair = on_stack_nodes_.insert( auto pair = on_stack_nodes_.insert(
{GetRealStackAddressForSlot(slot), std::move(entry)}); {base::Stack::GetRealStackAddressForSlot(slot), std::move(entry)});
if (!pair.second) { if (!pair.second) {
// Insertion failed because there already was an entry present for that // Insertion failed because there already was an entry present for that
// stack address. This can happen because cleanup is conservative in which // stack address. This can happen because cleanup is conservative in which
...@@ -880,7 +865,8 @@ GlobalHandles::TracedNode* GlobalHandles::OnStackTracedNodeSpace::Acquire( ...@@ -880,7 +865,8 @@ GlobalHandles::TracedNode* GlobalHandles::OnStackTracedNodeSpace::Acquire(
void GlobalHandles::OnStackTracedNodeSpace::CleanupBelowCurrentStackPosition() { void GlobalHandles::OnStackTracedNodeSpace::CleanupBelowCurrentStackPosition() {
if (on_stack_nodes_.empty()) return; if (on_stack_nodes_.empty()) return;
const auto it = on_stack_nodes_.upper_bound(GetCurrentStackPosition()); const auto it =
on_stack_nodes_.upper_bound(base::Stack::GetCurrentStackPosition());
on_stack_nodes_.erase(on_stack_nodes_.begin(), it); on_stack_nodes_.erase(on_stack_nodes_.begin(), it);
} }
......
...@@ -20,9 +20,19 @@ extern "C" void PushAllRegistersAndIterateStack(const Stack*, StackVisitor*, ...@@ -20,9 +20,19 @@ extern "C" void PushAllRegistersAndIterateStack(const Stack*, StackVisitor*,
Stack::Stack(const void* stack_start) : stack_start_(stack_start) {} Stack::Stack(const void* stack_start) : stack_start_(stack_start) {}
bool Stack::IsOnStack(void* slot) const { bool Stack::IsOnStack(void* slot) const {
void* raw_slot = v8::base::Stack::GetStackSlot(slot); #ifdef V8_USE_ADDRESS_SANITIZER
return v8::base::Stack::GetCurrentStackPosition() <= raw_slot && // If the slot is part of a fake frame, then it is definitely on the stack.
raw_slot <= stack_start_; void* real_frame = __asan_addr_is_in_fake_stack(
__asan_get_current_fake_stack(), reinterpret_cast<void*>(slot), nullptr,
nullptr);
if (real_frame) {
return true;
}
// Fall through as there is still a regular stack present even when running
// with ASAN fake stacks.
#endif // V8_USE_ADDRESS_SANITIZER
return v8::base::Stack::GetCurrentStackPosition() <= slot &&
slot <= stack_start_;
} }
namespace { namespace {
......
...@@ -91,9 +91,12 @@ TEST(StackTest, GetCurrentStackPosition) { ...@@ -91,9 +91,12 @@ TEST(StackTest, GetCurrentStackPosition) {
TEST(StackTest, StackVariableInBounds) { TEST(StackTest, StackVariableInBounds) {
void* dummy; void* dummy;
ASSERT_GT(Stack::GetStackStart(), Stack::GetCurrentStackPosition()); ASSERT_GT(static_cast<void*>(Stack::GetStackStart()),
EXPECT_GT(Stack::GetStackStart(), Stack::GetStackSlot(&dummy)); Stack::GetCurrentStackPosition());
EXPECT_LT(Stack::GetCurrentStackPosition(), Stack::GetStackSlot(&dummy)); EXPECT_GT(static_cast<void*>(Stack::GetStackStart()),
Stack::GetRealStackAddressForSlot(&dummy));
EXPECT_LT(static_cast<void*>(Stack::GetCurrentStackPosition()),
Stack::GetRealStackAddressForSlot(&dummy));
} }
} // namespace base } // namespace base
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment