11#include " GarbageCollector.hpp"
22#include " Heap_test.hpp"
33
4+ #include < cstdint>
5+ #include < cstring>
6+
47namespace {
58
69static 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
5474TEST_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