Skip to content

Commit 8ebc69e

Browse files
committed
gc: scrub popped-frame stack residue in GC completeness tests
1 parent 05e6c2e commit 8ebc69e

1 file changed

Lines changed: 22 additions & 0 deletions

File tree

src/memory/GarbageCollector_tests.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#include "GarbageCollector.hpp"
22
#include "Heap_test.hpp"
33

4+
#include <cstdint>
5+
#include <cstring>
6+
47
namespace {
58

69
static int64_t g_counter = 0;
@@ -49,6 +52,23 @@ static_assert(false, "compiler not supported");
4952
ASSERT_EQ(ptr4->foo, 4);
5053
ASSERT_EQ(ptr5->foo, 5);
5154
}
55+
56+
// Overwrites the stack region just vacated by a popped frame. Unoptimized
57+
// builds give every temporary its own slot and the collector's call chain
58+
// does not reliably overwrite all of them before the conservative scan runs,
59+
// so a stale copy of a dead GC pointer can be picked up as a root. Zeroing
60+
// the dead region makes collection of unreachable objects deterministic.
61+
// no_stack_protector: the scrub zeroes everything between `buffer` and the
62+
// frame header (alignment padding can hold a word of residue), which would
63+
// destroy a stack-protector canary placed in that range.
64+
__attribute__((noinline, no_stack_protector)) void scrub_dead_stack()
65+
{
66+
uint8_t buffer[16 * 1024];
67+
auto *frame_top = static_cast<uint8_t *>(__builtin_frame_address(0));
68+
std::memset(buffer, 0, static_cast<size_t>(frame_top - buffer));
69+
// keep the memset from being eliminated as a dead store
70+
asm volatile("" ::"r"(buffer) : "memory");
71+
}
5272
}// namespace
5373

5474
TEST_F(TestHeap, GarbageCollectorDoesNotDeallocateGCPointersOnTheStack)
@@ -74,6 +94,7 @@ TEST_F(TestHeap, GarbageCollectorDeallocatesGCPointersWhenStackFrameIsPopped)
7494

7595
new_stack_frame_function(*m_heap);
7696

97+
scrub_dead_stack();
7798
m_heap->collect_garbage();
7899

79100
ASSERT_EQ(g_counter, 5);
@@ -122,6 +143,7 @@ TEST_F(TestHeap, MutuallyReferencingObjectsAreCollected)
122143

123144
allocate_cycle_in_popped_frame(*m_heap, counter);
124145

146+
scrub_dead_stack();
125147
m_heap->collect_garbage();
126148

127149
ASSERT_EQ(counter, 2);

0 commit comments

Comments
 (0)