Skip to content

Commit 1d1bc93

Browse files
committed
gh-110481: Fix Py_SET_REFCNT() integer overflow
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) { ~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~
1 parent 7680da4 commit 1d1bc93

File tree

1 file changed

+11
-7
lines changed

1 file changed

+11
-7
lines changed

Include/object.h

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ having all the lower 32 bits set, which will avoid the reference count to go
8888
beyond the refcount limit. Immortality checks for reference count decreases will
8989
be done by checking the bit sign flag in the lower 32 bits.
9090
*/
91-
#define _Py_IMMORTAL_REFCNT UINT_MAX
91+
#define _Py_IMMORTAL_REFCNT (Py_ssize_t)UINT_MAX
9292

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

109109
// Py_NOGIL builds indicate immortal objects using `ob_ref_local`, which is
@@ -317,11 +317,11 @@ static inline Py_ssize_t Py_SIZE(PyObject *ob) {
317317
static inline Py_ALWAYS_INLINE int _Py_IsImmortal(PyObject *op)
318318
{
319319
#if defined(Py_NOGIL)
320-
return op->ob_ref_local == _Py_IMMORTAL_REFCNT_LOCAL;
320+
return (op->ob_ref_local == _Py_IMMORTAL_REFCNT_LOCAL);
321321
#elif SIZEOF_VOID_P > 4
322-
return _Py_CAST(PY_INT32_T, op->ob_refcnt) < 0;
322+
return (_Py_CAST(PY_INT32_T, op->ob_refcnt) < 0);
323323
#else
324-
return op->ob_refcnt == _Py_IMMORTAL_REFCNT;
324+
return (op->ob_refcnt == _Py_IMMORTAL_REFCNT);
325325
#endif
326326
}
327327
#define _Py_IsImmortal(op) _Py_IsImmortal(_PyObject_CAST(op))
@@ -356,8 +356,12 @@ static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) {
356356
if (_Py_IsOwnedByCurrentThread(ob)) {
357357
// Set local refcount to desired refcount and shared refcount to zero,
358358
// but preserve the shared refcount flags.
359-
assert(refcnt < UINT32_MAX);
360-
ob->ob_ref_local = _Py_STATIC_CAST(uint32_t, refcnt);
359+
if ((size_t)refcnt > (size_t)UINT32_MAX) {
360+
ob->ob_ref_local = UINT32_MAX;
361+
}
362+
else {
363+
ob->ob_ref_local = _Py_STATIC_CAST(uint32_t, refcnt);
364+
}
361365
ob->ob_ref_shared &= _Py_REF_SHARED_FLAG_MASK;
362366
}
363367
else {

0 commit comments

Comments
 (0)