Skip to content

Commit cfba2e5

Browse files
mlippautzCommit Bot
authored andcommitted
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 <[email protected]> Reviewed-by: Ulan Degenbaev <[email protected]> Cr-Commit-Position: refs/heads/master@{#70899}
1 parent 9f2dce8 commit cfba2e5

9 files changed

Lines changed: 65 additions & 50 deletions

File tree

src/base/platform/platform-aix.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ void OS::SignalCodeMovingGC() {}
130130
void OS::AdjustSchedulingParams() {}
131131

132132
// static
133-
void* Stack::GetStackStart() {
133+
Stack::StackSlot Stack::GetStackStart() {
134134
// pthread_getthrds_np creates 3 values:
135135
// __pi_stackaddr, __pi_stacksize, __pi_stackend
136136

src/base/platform/platform-freebsd.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ void OS::SignalCodeMovingGC() {}
9898
void OS::AdjustSchedulingParams() {}
9999

100100
// static
101-
void* Stack::GetStackStart() {
101+
Stack::StackSlot Stack::GetStackStart() {
102102
pthread_attr_t attr;
103103
int error;
104104
pthread_attr_init(&attr);

src/base/platform/platform-macos.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ void OS::AdjustSchedulingParams() {
9494
}
9595

9696
// static
97-
void* Stack::GetStackStart() {
97+
Stack::StackSlot Stack::GetStackStart() {
9898
return pthread_get_stackaddr_np(pthread_self());
9999
}
100100

src/base/platform/platform-posix.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,7 +1003,7 @@ void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
10031003
!defined(V8_OS_SOLARIS)
10041004

10051005
// static
1006-
void* Stack::GetStackStart() {
1006+
Stack::StackSlot Stack::GetStackStart() {
10071007
pthread_attr_t attr;
10081008
int error = pthread_getattr_np(pthread_self(), &attr);
10091009
if (!error) {
@@ -1029,7 +1029,9 @@ void* Stack::GetStackStart() {
10291029
// !defined(_AIX) && !defined(V8_OS_SOLARIS)
10301030

10311031
// static
1032-
void* Stack::GetCurrentStackPosition() { return __builtin_frame_address(0); }
1032+
Stack::StackSlot Stack::GetCurrentStackPosition() {
1033+
return __builtin_frame_address(0);
1034+
}
10331035

10341036
#undef LOG_TAG
10351037
#undef MAP_ANONYMOUS

src/base/platform/platform-win32.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,7 +1395,7 @@ void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
13951395
void OS::AdjustSchedulingParams() {}
13961396

13971397
// static
1398-
void* Stack::GetStackStart() {
1398+
Stack::StackSlot Stack::GetStackStart() {
13991399
#if defined(V8_TARGET_ARCH_X64)
14001400
return reinterpret_cast<void*>(
14011401
reinterpret_cast<NT_TIB64*>(NtCurrentTeb())->StackBase);
@@ -1414,7 +1414,7 @@ void* Stack::GetStackStart() {
14141414
}
14151415

14161416
// static
1417-
void* Stack::GetCurrentStackPosition() {
1417+
Stack::StackSlot Stack::GetCurrentStackPosition() {
14181418
#if V8_CC_MSVC
14191419
return _AddressOfReturnAddress();
14201420
#else

src/base/platform/platform.h

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#define V8_BASE_PLATFORM_PLATFORM_H_
2323

2424
#include <cstdarg>
25+
#include <cstdint>
2526
#include <string>
2627
#include <vector>
2728

@@ -433,30 +434,43 @@ class V8_BASE_EXPORT Thread {
433434
// TODO(v8:10354): Make use of the stack utilities here in V8.
434435
class V8_BASE_EXPORT Stack {
435436
public:
437+
// Convenience wrapper to use stack slots as unsigned values or void*
438+
// pointers.
439+
struct StackSlot {
440+
// NOLINTNEXTLINE
441+
StackSlot(void* value) : value(reinterpret_cast<uintptr_t>(value)) {}
442+
StackSlot(uintptr_t value) : value(value) {} // NOLINT
443+
444+
// NOLINTNEXTLINE
445+
operator void*() const { return reinterpret_cast<void*>(value); }
446+
operator uintptr_t() const { return value; } // NOLINT
447+
448+
uintptr_t value;
449+
};
450+
436451
// Gets the start of the stack of the current thread.
437-
static void* GetStackStart();
452+
static StackSlot GetStackStart();
438453

439454
// Returns the current stack top. Works correctly with ASAN and SafeStack.
440455
// GetCurrentStackPosition() should not be inlined, because it works on stack
441456
// frames if it were inlined into a function with a huge stack frame it would
442457
// return an address significantly above the actual current stack position.
443-
static V8_NOINLINE void* GetCurrentStackPosition();
458+
static V8_NOINLINE StackSlot GetCurrentStackPosition();
444459

445-
// Translates an ASAN-based slot to a real stack slot if necessary.
446-
static void* GetStackSlot(void* slot) {
460+
// Returns the real stack frame if slot is part of a fake frame, and slot
461+
// otherwise.
462+
static StackSlot GetRealStackAddressForSlot(StackSlot slot) {
447463
#ifdef V8_USE_ADDRESS_SANITIZER
448-
void* fake_stack = __asan_get_current_fake_stack();
449-
if (fake_stack) {
450-
void* fake_frame_start;
451-
void* real_frame = __asan_addr_is_in_fake_stack(
452-
fake_stack, slot, &fake_frame_start, nullptr);
453-
if (real_frame) {
454-
return reinterpret_cast<void*>(
455-
reinterpret_cast<uintptr_t>(real_frame) +
456-
(reinterpret_cast<uintptr_t>(slot) -
457-
reinterpret_cast<uintptr_t>(fake_frame_start)));
458-
}
459-
}
464+
// ASAN fetches the real stack deeper in the __asan_addr_is_in_fake_stack()
465+
// call (precisely, deeper in __asan_stack_malloc_()), which results in a
466+
// real frame that could be outside of stack bounds. Adjust for this
467+
// impreciseness here.
468+
constexpr size_t kAsanRealFrameOffsetBytes = 32;
469+
void* real_frame = __asan_addr_is_in_fake_stack(
470+
__asan_get_current_fake_stack(), slot, nullptr, nullptr);
471+
return real_frame
472+
? (static_cast<char*>(real_frame) + kAsanRealFrameOffsetBytes)
473+
: slot;
460474
#endif // V8_USE_ADDRESS_SANITIZER
461475
return slot;
462476
}

src/handles/global-handles.cc

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -749,8 +749,7 @@ class GlobalHandles::OnStackTracedNodeSpace final {
749749

750750
void SetStackStart(void* stack_start) {
751751
CHECK(on_stack_nodes_.empty());
752-
stack_start_ =
753-
GetRealStackAddressForSlot(reinterpret_cast<uintptr_t>(stack_start));
752+
stack_start_ = base::Stack::GetRealStackAddressForSlot(stack_start);
754753
}
755754

756755
V8_INLINE bool IsOnStack(uintptr_t slot) const;
@@ -770,10 +769,6 @@ class GlobalHandles::OnStackTracedNodeSpace final {
770769
GlobalHandles* global_handles;
771770
};
772771

773-
// Returns the real stack frame if slot is part of a fake frame, and slot
774-
// otherwise.
775-
V8_INLINE uintptr_t GetRealStackAddressForSlot(uintptr_t slot) const;
776-
777772
// Keeps track of registered handles. The data structure is cleaned on
778773
// iteration and when adding new references using the current stack address.
779774
// Cleaning is based on current stack address and the key of the map which is
@@ -795,17 +790,6 @@ class GlobalHandles::OnStackTracedNodeSpace final {
795790
size_t acquire_count_ = 0;
796791
};
797792

798-
uintptr_t GlobalHandles::OnStackTracedNodeSpace::GetRealStackAddressForSlot(
799-
uintptr_t slot) const {
800-
#ifdef V8_USE_ADDRESS_SANITIZER
801-
void* real_frame = __asan_addr_is_in_fake_stack(
802-
__asan_get_current_fake_stack(), reinterpret_cast<void*>(slot), nullptr,
803-
nullptr);
804-
return real_frame ? reinterpret_cast<uintptr_t>(real_frame) : slot;
805-
#endif // V8_USE_ADDRESS_SANITIZER
806-
return slot;
807-
}
808-
809793
bool GlobalHandles::OnStackTracedNodeSpace::IsOnStack(uintptr_t slot) const {
810794
#ifdef V8_USE_ADDRESS_SANITIZER
811795
if (__asan_addr_is_in_fake_stack(__asan_get_current_fake_stack(),
@@ -814,7 +798,7 @@ bool GlobalHandles::OnStackTracedNodeSpace::IsOnStack(uintptr_t slot) const {
814798
return true;
815799
}
816800
#endif // V8_USE_ADDRESS_SANITIZER
817-
return stack_start_ >= slot && slot > GetCurrentStackPosition();
801+
return stack_start_ >= slot && slot > base::Stack::GetCurrentStackPosition();
818802
}
819803

820804
void GlobalHandles::OnStackTracedNodeSpace::NotifyEmptyEmbedderStack() {
@@ -858,12 +842,13 @@ GlobalHandles::TracedNode* GlobalHandles::OnStackTracedNodeSpace::Acquire(
858842
entry.node.Free(nullptr);
859843
entry.global_handles = global_handles_;
860844
#ifdef V8_USE_ADDRESS_SANITIZER
861-
auto pair = on_stack_nodes_.insert({GetRealStackAddressForSlot(slot), {}});
845+
auto pair = on_stack_nodes_.insert(
846+
{base::Stack::GetRealStackAddressForSlot(slot), {}});
862847
pair.first->second.push_back(std::move(entry));
863848
TracedNode* result = &(pair.first->second.back().node);
864849
#else // !V8_USE_ADDRESS_SANITIZER
865850
auto pair = on_stack_nodes_.insert(
866-
{GetRealStackAddressForSlot(slot), std::move(entry)});
851+
{base::Stack::GetRealStackAddressForSlot(slot), std::move(entry)});
867852
if (!pair.second) {
868853
// Insertion failed because there already was an entry present for that
869854
// stack address. This can happen because cleanup is conservative in which
@@ -880,7 +865,8 @@ GlobalHandles::TracedNode* GlobalHandles::OnStackTracedNodeSpace::Acquire(
880865

881866
void GlobalHandles::OnStackTracedNodeSpace::CleanupBelowCurrentStackPosition() {
882867
if (on_stack_nodes_.empty()) return;
883-
const auto it = on_stack_nodes_.upper_bound(GetCurrentStackPosition());
868+
const auto it =
869+
on_stack_nodes_.upper_bound(base::Stack::GetCurrentStackPosition());
884870
on_stack_nodes_.erase(on_stack_nodes_.begin(), it);
885871
}
886872

src/heap/base/stack.cc

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,19 @@ extern "C" void PushAllRegistersAndIterateStack(const Stack*, StackVisitor*,
2020
Stack::Stack(const void* stack_start) : stack_start_(stack_start) {}
2121

2222
bool Stack::IsOnStack(void* slot) const {
23-
void* raw_slot = v8::base::Stack::GetStackSlot(slot);
24-
return v8::base::Stack::GetCurrentStackPosition() <= raw_slot &&
25-
raw_slot <= stack_start_;
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_;
2636
}
2737

2838
namespace {

test/unittests/base/platform/platform-unittest.cc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,12 @@ TEST(StackTest, GetCurrentStackPosition) {
9191

9292
TEST(StackTest, StackVariableInBounds) {
9393
void* dummy;
94-
ASSERT_GT(Stack::GetStackStart(), Stack::GetCurrentStackPosition());
95-
EXPECT_GT(Stack::GetStackStart(), Stack::GetStackSlot(&dummy));
96-
EXPECT_LT(Stack::GetCurrentStackPosition(), Stack::GetStackSlot(&dummy));
94+
ASSERT_GT(static_cast<void*>(Stack::GetStackStart()),
95+
Stack::GetCurrentStackPosition());
96+
EXPECT_GT(static_cast<void*>(Stack::GetStackStart()),
97+
Stack::GetRealStackAddressForSlot(&dummy));
98+
EXPECT_LT(static_cast<void*>(Stack::GetCurrentStackPosition()),
99+
Stack::GetRealStackAddressForSlot(&dummy));
97100
}
98101

99102
} // namespace base

0 commit comments

Comments
 (0)