mirror of https://github.com/python/cpython.git
gh-110481: Fix Py_SET_REFCNT() integer overflow (#112174)
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) { ~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~
This commit is contained in:
parent
c2982380f8
commit
5f6ac2d88a
|
@ -34,6 +34,9 @@ of Python objects.
|
|||
|
||||
Set the object *o* reference counter to *refcnt*.
|
||||
|
||||
On :ref:`Python build with Free Threading <free-threading-build>`, if
|
||||
*refcnt* is larger than ``UINT32_MAX``, the object is made :term:`immortal`.
|
||||
|
||||
This function has no effect on :term:`immortal` objects.
|
||||
|
||||
.. versionadded:: 3.9
|
||||
|
|
|
@ -287,6 +287,8 @@ General Options
|
|||
|
||||
.. versionadded:: 3.11
|
||||
|
||||
.. _free-threading-build:
|
||||
|
||||
.. option:: --disable-gil
|
||||
|
||||
Enables **experimental** support for running Python without the
|
||||
|
|
|
@ -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_CAST(Py_ssize_t, UINT_MAX)
|
||||
|
||||
#else
|
||||
/*
|
||||
|
@ -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_CAST(Py_ssize_t, UINT_MAX >> 2)
|
||||
#endif
|
||||
|
||||
// Py_GIL_DISABLED builds indicate immortal objects using `ob_ref_local`, which is
|
||||
|
@ -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_GIL_DISABLED)
|
||||
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))
|
||||
|
@ -350,15 +350,23 @@ static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) {
|
|||
if (_Py_IsImmortal(ob)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef Py_GIL_DISABLED
|
||||
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.
|
||||
|
@ -750,6 +758,7 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op)
|
|||
uint32_t local = _Py_atomic_load_uint32_relaxed(&op->ob_ref_local);
|
||||
uint32_t new_local = local + 1;
|
||||
if (new_local == 0) {
|
||||
// local is equal to _Py_IMMORTAL_REFCNT: do nothing
|
||||
return;
|
||||
}
|
||||
if (_Py_IsOwnedByCurrentThread(op)) {
|
||||
|
@ -763,6 +772,8 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op)
|
|||
PY_UINT32_T cur_refcnt = op->ob_refcnt_split[PY_BIG_ENDIAN];
|
||||
PY_UINT32_T new_refcnt = cur_refcnt + 1;
|
||||
if (new_refcnt == 0) {
|
||||
// cur_refcnt is equal to _Py_IMMORTAL_REFCNT: the object is immortal,
|
||||
// do nothing
|
||||
return;
|
||||
}
|
||||
op->ob_refcnt_split[PY_BIG_ENDIAN] = new_refcnt;
|
||||
|
|
Loading…
Reference in New Issue