Skip to content

GH-133932: Tagged ints are heap safe #134244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 22, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 7 additions & 13 deletions Include/internal/pycore_stackref.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ PyStackRef_IsNullOrInt(_PyStackRef ref);
#else

#define Py_INT_TAG 3
#define Py_TAG_REFCNT 1

static inline bool
PyStackRef_IsTaggedInt(_PyStackRef i)
Expand All @@ -264,7 +265,7 @@ PyStackRef_UntagInt(_PyStackRef i)

#ifdef Py_GIL_DISABLED

#define Py_TAG_DEFERRED (1)
#define Py_TAG_DEFERRED Py_TAG_REFCNT

#define Py_TAG_PTR ((uintptr_t)0)
#define Py_TAG_BITS ((uintptr_t)1)
Expand Down Expand Up @@ -443,14 +444,13 @@ PyStackRef_AsStrongReference(_PyStackRef stackref)
/* References to immortal objects always have their tag bit set to Py_TAG_REFCNT
* as they can (must) have their reclamation deferred */

#define Py_TAG_BITS 1
#define Py_TAG_REFCNT 1
#define Py_TAG_BITS 3
#if _Py_IMMORTAL_FLAGS != Py_TAG_REFCNT
# error "_Py_IMMORTAL_FLAGS != Py_TAG_REFCNT"
#endif

#define BITS_TO_PTR(REF) ((PyObject *)((REF).bits))
#define BITS_TO_PTR_MASKED(REF) ((PyObject *)(((REF).bits) & (~Py_TAG_BITS)))
#define BITS_TO_PTR_MASKED(REF) ((PyObject *)(((REF).bits) & (~Py_TAG_REFCNT)))

#define PyStackRef_NULL_BITS Py_TAG_REFCNT
static const _PyStackRef PyStackRef_NULL = { .bits = PyStackRef_NULL_BITS };
Expand Down Expand Up @@ -530,7 +530,7 @@ PyStackRef_FromPyObjectSteal(PyObject *obj)
{
assert(obj != NULL);
#if SIZEOF_VOID_P > 4
unsigned int tag = obj->ob_flags & Py_TAG_BITS;
unsigned int tag = obj->ob_flags & Py_TAG_REFCNT;
#else
unsigned int tag = _Py_IsImmortal(obj) ? Py_TAG_REFCNT : 0;
#endif
Expand All @@ -549,12 +549,6 @@ PyStackRef_FromPyObjectStealMortal(PyObject *obj)
return ref;
}

// Check if a stackref is exactly the same as another stackref, including the
// the deferred bit. This can only be used safely if you know that the deferred
// bits of `a` and `b` match.
#define PyStackRef_IsExactly(a, b) \
(assert(((a).bits & Py_TAG_BITS) == ((b).bits & Py_TAG_BITS)), (a).bits == (b).bits)

static inline _PyStackRef
_PyStackRef_FromPyObjectNew(PyObject *obj)
{
Expand Down Expand Up @@ -607,7 +601,7 @@ PyStackRef_DUP(_PyStackRef ref)
static inline bool
PyStackRef_IsHeapSafe(_PyStackRef ref)
{
return (ref.bits & Py_TAG_BITS) == 0 || ref.bits == PyStackRef_NULL_BITS || _Py_IsImmortal(BITS_TO_PTR_MASKED(ref));
return (ref.bits & Py_TAG_BITS) != Py_TAG_REFCNT || ref.bits == PyStackRef_NULL_BITS || _Py_IsImmortal(BITS_TO_PTR_MASKED(ref));
}

static inline _PyStackRef
Expand Down Expand Up @@ -682,7 +676,7 @@ PyStackRef_XCLOSE(_PyStackRef ref)

// Note: this is a macro because MSVC (Windows) has trouble inlining it.

#define PyStackRef_Is(a, b) (((a).bits & (~Py_TAG_BITS)) == ((b).bits & (~Py_TAG_BITS)))
#define PyStackRef_Is(a, b) (((a).bits & (~Py_TAG_REFCNT)) == ((b).bits & (~Py_TAG_REFCNT)))


#endif // !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG)
Expand Down
Loading