Skip to content

Commit df267eb

Browse files
committed
pythongh-110481: Fix Py_SET_REFCNT() integer overflow
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) { ~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~
1 parent 7c50800 commit df267eb

File tree

1 file changed

+18
-10
lines changed

1 file changed

+18
-10
lines changed

Include/object.h

+18-10
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))
@@ -350,15 +350,23 @@ static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) {
350350
if (_Py_IsImmortal(ob)) {
351351
return;
352352
}
353+
353354
#ifndef Py_NOGIL
354355
ob->ob_refcnt = refcnt;
355356
#else
356357
if (_Py_IsOwnedByCurrentThread(ob)) {
357-
// Set local refcount to desired refcount and shared refcount to zero,
358-
// but preserve the shared refcount flags.
359-
assert(refcnt < UINT32_MAX);
360-
ob->ob_ref_local = _Py_STATIC_CAST(uint32_t, refcnt);
361-
ob->ob_ref_shared &= _Py_REF_SHARED_FLAG_MASK;
358+
if ((size_t)refcnt > (size_t)UINT32_MAX) {
359+
// On overflow, make the object immortal
360+
op->ob_tid = _Py_UNOWNED_TID;
361+
op->ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL;
362+
op->ob_ref_shared = 0;
363+
}
364+
else {
365+
// Set local refcount to desired refcount and shared refcount
366+
// to zero, but preserve the shared refcount flags.
367+
ob->ob_ref_local = _Py_STATIC_CAST(uint32_t, refcnt);
368+
ob->ob_ref_shared &= _Py_REF_SHARED_FLAG_MASK;
369+
}
362370
}
363371
else {
364372
// Set local refcount to zero and shared refcount to desired refcount.

0 commit comments

Comments
 (0)