Skip to content

Commit 372eabc

Browse files
committed
Make sure that the GC doesn't untrack objects in trashcan
1 parent d530e74 commit 372eabc

File tree

2 files changed

+39
-6
lines changed

2 files changed

+39
-6
lines changed

Objects/object.c

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

29312931
/* Trashcan support. */
29322932

2933+
/* We need to store a pointer in the refcount field of
2934+
* an object. It is important that we never store 0 (NULL).
2935+
* It is also important to not make the object appear immortal,
2936+
* or it might be untracked by the cycle GC. */
2937+
uintptr_t
2938+
pointer_to_safe_refcount(void *ptr)
2939+
{
2940+
uintptr_t full = (uintptr_t)ptr;
2941+
assert((full & 3) == 0);
2942+
#if defined(Py_GIL_DISABLED)
2943+
return full + 1;
2944+
#else
2945+
uint32_t refcnt = (uint32_t)full;
2946+
if (refcnt >= (uint32_t)_Py_IMMORTAL_MINIMUM_REFCNT) {
2947+
full = full - ((uintptr_t)_Py_IMMORTAL_MINIMUM_REFCNT) + 1;
2948+
}
2949+
return full + 2;
2950+
#endif
2951+
}
2952+
2953+
void *
2954+
safe_refcount_to_pointer(uintptr_t refcnt)
2955+
{
2956+
#if defined(Py_GIL_DISABLED)
2957+
return (void *)(refcnt - 1);
2958+
#else
2959+
if (refcnt & 1) {
2960+
refcnt += _Py_IMMORTAL_MINIMUM_REFCNT - 1;
2961+
}
2962+
return (void *)(refcnt - 2);
2963+
#endif
2964+
}
2965+
29332966
/* Add op to the gcstate->trash_delete_later list. Called when the current
29342967
* call-stack depth gets large. op must be a currently untracked gc'ed
29352968
* object, with refcount 0. Py_DECREF must already have been called on it.
@@ -2941,11 +2974,10 @@ _PyTrash_thread_deposit_object(PyThreadState *tstate, PyObject *op)
29412974
#ifdef Py_GIL_DISABLED
29422975
op->ob_tid = (uintptr_t)tstate->delete_later;
29432976
#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;
2977+
/* Store the delete_later pointer in the refcnt field. */
2978+
uintptr_t refcnt = pointer_to_safe_refcount(tstate->delete_later);
2979+
*((uintptr_t*)op) = refcnt;
2980+
assert(!_Py_IsImmortal(op));
29492981
#endif
29502982
tstate->delete_later = op;
29512983
}
@@ -2967,7 +2999,7 @@ _PyTrash_thread_destroy_chain(PyThreadState *tstate)
29672999
/* Get the delete_later pointer from the refcnt field.
29683000
* See _PyTrash_thread_deposit_object(). */
29693001
uintptr_t refcnt = *((uintptr_t*)op);
2970-
tstate->delete_later = (PyObject *)(refcnt - 1);
3002+
tstate->delete_later = safe_refcount_to_pointer(refcnt);
29713003
op->ob_refcnt = 0;
29723004
#endif
29733005

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)