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_SET_REFCNT() is called with a reference count larger than
UINT32_MAX, clamp the value to UINT32_MAX to have a deterministic
behavior.

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 1d1bc93
Showing 1 changed file with 11 additions and 7 deletions.
18 changes: 11 additions & 7 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 @@ -356,8 +356,12 @@ static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) {
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);
if ((size_t)refcnt > (size_t)UINT32_MAX) {
ob->ob_ref_local = UINT32_MAX;
}
else {
ob->ob_ref_local = _Py_STATIC_CAST(uint32_t, refcnt);
}
ob->ob_ref_shared &= _Py_REF_SHARED_FLAG_MASK;
}
else {
Expand Down

0 comments on commit 1d1bc93

Please sign in to comment.