Skip to content

Commit

Permalink
pythongh-110481: Fix Py_SET_REFCNT() integer overflow
Browse files Browse the repository at this point in the history
If Py_NOGIL is defined and Py_SET_REFCNT() is called with a reference
count larger than UINT32_MAX, make the object immortal.

Set _Py_IMMORTAL_REFCNT constant type to Py_ssize_t to fix the
following compiler warning:

Include/internal/pycore_global_objects_fini_generated.h:14:24:
warning: comparison of integers of different signs: 'Py_ssize_t'
(aka 'long') and 'unsigned int' [-Wsign-compare]

    if (Py_REFCNT(obj) < _Py_IMMORTAL_REFCNT) {
        ~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~
  • Loading branch information
vstinner committed Nov 16, 2023
1 parent 7680da4 commit 7d64093
Showing 1 changed file with 18 additions and 10 deletions.
28 changes: 18 additions & 10 deletions Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ having all the lower 32 bits set, which will avoid the reference count to go
beyond the refcount limit. Immortality checks for reference count decreases will
be done by checking the bit sign flag in the lower 32 bits.
*/
#define _Py_IMMORTAL_REFCNT UINT_MAX
#define _Py_IMMORTAL_REFCNT (Py_ssize_t)UINT_MAX

#else
/*
Expand All @@ -103,7 +103,7 @@ immortality, but the execution would still be correct.
Reference count increases and decreases will first go through an immortality
check by comparing the reference count field to the immortality reference count.
*/
#define _Py_IMMORTAL_REFCNT (UINT_MAX >> 2)
#define _Py_IMMORTAL_REFCNT (Py_ssize_t)(UINT_MAX >> 2)
#endif

// Py_NOGIL builds indicate immortal objects using `ob_ref_local`, which is
Expand Down Expand Up @@ -317,11 +317,11 @@ static inline Py_ssize_t Py_SIZE(PyObject *ob) {
static inline Py_ALWAYS_INLINE int _Py_IsImmortal(PyObject *op)
{
#if defined(Py_NOGIL)
return op->ob_ref_local == _Py_IMMORTAL_REFCNT_LOCAL;
return (op->ob_ref_local == _Py_IMMORTAL_REFCNT_LOCAL);
#elif SIZEOF_VOID_P > 4
return _Py_CAST(PY_INT32_T, op->ob_refcnt) < 0;
return (_Py_CAST(PY_INT32_T, op->ob_refcnt) < 0);
#else
return op->ob_refcnt == _Py_IMMORTAL_REFCNT;
return (op->ob_refcnt == _Py_IMMORTAL_REFCNT);
#endif
}
#define _Py_IsImmortal(op) _Py_IsImmortal(_PyObject_CAST(op))
Expand Down Expand Up @@ -350,15 +350,23 @@ static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) {
if (_Py_IsImmortal(ob)) {
return;
}

#ifndef Py_NOGIL
ob->ob_refcnt = refcnt;
#else
if (_Py_IsOwnedByCurrentThread(ob)) {
// Set local refcount to desired refcount and shared refcount to zero,
// but preserve the shared refcount flags.
assert(refcnt < UINT32_MAX);
ob->ob_ref_local = _Py_STATIC_CAST(uint32_t, refcnt);
ob->ob_ref_shared &= _Py_REF_SHARED_FLAG_MASK;
if ((size_t)refcnt > (size_t)UINT32_MAX) {
// On overflow, make the object immortal
op->ob_tid = _Py_UNOWNED_TID;
op->ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL;
op->ob_ref_shared = 0;
}
else {
// Set local refcount to desired refcount and shared refcount
// to zero, but preserve the shared refcount flags.
ob->ob_ref_local = _Py_STATIC_CAST(uint32_t, refcnt);
ob->ob_ref_shared &= _Py_REF_SHARED_FLAG_MASK;
}
}
else {
// Set local refcount to zero and shared refcount to desired refcount.
Expand Down

0 comments on commit 7d64093

Please sign in to comment.