_PyFrame_Copy() copied interpreter frames into generator and
frame-object storage without initializing the visited byte. Incremental
GC later reads frame->visited in mark_stacks() on non-start passes, so
copied frames could expose an uninitialized value once they became live
on a thread stack again.
Reset visited when copying a frame so copied frames start with defined
GC bookkeeping state. Preserve lltrace in Py_DEBUG builds.
int stacktop = (int)(src->stackpointer - src->localsplus);
assert(stacktop >= 0);
dest->stackpointer = dest->localsplus + stacktop;
+ // visited is GC bookkeeping for the current stack walk, not frame state.
+ dest->visited = 0;
+#ifdef Py_DEBUG
+ dest->lltrace = src->lltrace;
+#endif
for (int i = 0; i < stacktop; i++) {
dest->localsplus[i] = PyStackRef_MakeHeapSafe(src->localsplus[i]);
}
--- /dev/null
+Initialize ``_PyInterpreterFrame.visited`` when copying interpreter frames so
+incremental GC does not read an uninitialized byte from generator and
+frame-object copies.