8000 GH-133261: Make sure that the GC doesn't untrack objects in trashcan … · python/cpython@f554237 · GitHub
[go: up one dir, main page]

Skip to content

Commit f554237

Browse files
authored
GH-133261: Make sure that the GC doesn't untrack objects in trashcan (GH-133431)
1 parent 8d5f3cd commit f554237

File tree

3 files changed

+36
-6
lines changed

3 files changed

+36
-6
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix bug where the cycle GC could untrack objects in the trashcan because
2+
they looked like they were immortal. When objects are added to the trashcan,
3+
we take care to ensure they keep a mortal reference count.

Objects/object.c

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2930,6 +2930,33 @@ Py_ReprLeave(PyObject *obj)
29302930

29312931
/* Trashcan support. */
29322932

2933+
#ifndef Py_GIL_DISABLED
2934+
/* We need to store a pointer in the refcount field of
2935+
* an object. It is important that we never store 0 (NULL).
2936+
* It is also important to not make the object appear immortal,
2937+
* or it might be untracked by the cycle GC. */
2938+
static uintptr_t
2939+
pointer_to_safe_refcount(void *ptr)
2940+
{
2941+
uintptr_t full = (uintptr_t)ptr;
2942+
assert((full & 3) == 0);
2943+
uint32_t refcnt = (uint32_t)full;
2944+
if (refcnt >= (uint32_t)_Py_IMMORTAL_MINIMUM_REFCNT) {
2945+
full = full - ((uintptr_t)_Py_IMMORTAL_MINIMUM_REFCNT) + 1;
2946+
}
2947+
return full + 2;
2948+
}
2949+
2950+
static void *
2951+
safe_refcount_to_pointer(uintptr_t refcnt)
2952+
{
2953+
if (refcnt & 1) {
2954+
refcnt += _Py_IMMORTAL_MINIMUM_REFCNT - 1;
2955+
}
2956+
return (void *)(refcnt - 2);
2957+
}
2958+
#endif
2959+
29332960
/* Add op to the gcstate->trash_delete_later list. Called when the current
29342961
* call-stack depth gets large. op must be a currently untracked gc'ed
29352962
* object, with refcount 0. Py_DECREF must already have been called on it.
@@ -2941,11 +2968,10 @@ _PyTrash_thread_deposit_object(PyThreadState *tstate, PyObject *op)
29412968
#ifdef Py_GIL_DISABLED
29422969
op->ob_tid = (uintptr_t)tstate->delete_later;
29432970
#else
2944-
/* Store the delete_later pointer in the refcnt field.
2945-
* As this object may still be tracked by the GC,
2946-
* it is important that we never store 0 (NULL). */
2947-
uintptr_t refcnt = (uintptr_t)tstate->delete_later;
2948-
*((uintptr_t*)op) = refcnt+1;
2971+
/* Store the delete_later pointer in the refcnt field. */
2972+
uintptr_t refcnt = pointer_to_safe_refcount(tstate->delete_later);
2973+
*((uintptr_t*)op) = refcnt;
2974+
assert(!_Py_IsImmortal(op));
29492975
#endif
29502976
tstate->delete_later = op;
29512977
}
@@ -2967,7 +2993,7 @@ _PyTrash_thread_destroy_chain(PyThreadState *tstate)
29672993
/* Get the delete_later pointer from the refcnt field.
29682994
* See _PyTrash_thread_deposit_object(). */
29692995
uintptr_t refcnt = *((uintptr_t*)op);
2970-
tstate->delete_later = (PyObject *)(refcnt - 1);
2996+
tstate->delete_later = safe_refcount_to_pointer(refcnt);
29712997
op->ob_refcnt = 0;
29722998
#endif
29732999

Python/gc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,7 @@ update_refs(PyGC_Head *containers)
493493
next = GC_NEXT(gc);
494494
PyObject *op = FROM_GC(gc);
495495
if (_Py_IsImmortal(op)) {
496+
assert(!_Py_IsStaticImmortal(op));
496497
_PyObject_GC_UNTRACK(op);
497498
gc = next;
498499
continue;

0 commit comments

Comments
 (0)
0