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*.
|
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.
|
This function has no effect on :term:`immortal` objects.
|
||||||
|
|
||||||
.. versionadded:: 3.9
|
.. versionadded:: 3.9
|
||||||
|
|
|
@ -287,6 +287,8 @@ General Options
|
||||||
|
|
||||||
.. versionadded:: 3.11
|
.. versionadded:: 3.11
|
||||||
|
|
||||||
|
.. _free-threading-build:
|
||||||
|
|
||||||
.. option:: --disable-gil
|
.. option:: --disable-gil
|
||||||
|
|
||||||
Enables **experimental** support for running Python without the
|
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
|
beyond the refcount limit. Immortality checks for reference count decreases will
|
||||||
be done by checking the bit sign flag in the lower 32 bits.
|
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
|
#else
|
||||||
/*
|
/*
|
||||||
|
@ -103,7 +103,7 @@ immortality, but the execution would still be correct.
|
||||||
Reference count increases and decreases will first go through an immortality
|
Reference count increases and decreases will first go through an immortality
|
||||||
check by comparing the reference count field to the immortality reference count.
|
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
|
#endif
|
||||||
|
|
||||||
// Py_GIL_DISABLED builds indicate immortal objects using `ob_ref_local`, which is
|
// 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)
|
static inline Py_ALWAYS_INLINE int _Py_IsImmortal(PyObject *op)
|
||||||
{
|
{
|
||||||
#if defined(Py_GIL_DISABLED)
|
#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
|
#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
|
#else
|
||||||
return op->ob_refcnt == _Py_IMMORTAL_REFCNT;
|
return (op->ob_refcnt == _Py_IMMORTAL_REFCNT);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#define _Py_IsImmortal(op) _Py_IsImmortal(_PyObject_CAST(op))
|
#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)) {
|
if (_Py_IsImmortal(ob)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef Py_GIL_DISABLED
|
#ifndef Py_GIL_DISABLED
|
||||||
ob->ob_refcnt = refcnt;
|
ob->ob_refcnt = refcnt;
|
||||||
#else
|
#else
|
||||||
if (_Py_IsOwnedByCurrentThread(ob)) {
|
if (_Py_IsOwnedByCurrentThread(ob)) {
|
||||||
// Set local refcount to desired refcount and shared refcount to zero,
|
if ((size_t)refcnt > (size_t)UINT32_MAX) {
|
||||||
// but preserve the shared refcount flags.
|
// On overflow, make the object immortal
|
||||||
assert(refcnt < UINT32_MAX);
|
op->ob_tid = _Py_UNOWNED_TID;
|
||||||
ob->ob_ref_local = _Py_STATIC_CAST(uint32_t, refcnt);
|
op->ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL;
|
||||||
ob->ob_ref_shared &= _Py_REF_SHARED_FLAG_MASK;
|
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 {
|
else {
|
||||||
// Set local refcount to zero and shared refcount to desired refcount.
|
// 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 local = _Py_atomic_load_uint32_relaxed(&op->ob_ref_local);
|
||||||
uint32_t new_local = local + 1;
|
uint32_t new_local = local + 1;
|
||||||
if (new_local == 0) {
|
if (new_local == 0) {
|
||||||
|
// local is equal to _Py_IMMORTAL_REFCNT: do nothing
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_Py_IsOwnedByCurrentThread(op)) {
|
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 cur_refcnt = op->ob_refcnt_split[PY_BIG_ENDIAN];
|
||||||
PY_UINT32_T new_refcnt = cur_refcnt + 1;
|
PY_UINT32_T new_refcnt = cur_refcnt + 1;
|
||||||
if (new_refcnt == 0) {
|
if (new_refcnt == 0) {
|
||||||
|
// cur_refcnt is equal to _Py_IMMORTAL_REFCNT: the object is immortal,
|
||||||
|
// do nothing
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
op->ob_refcnt_split[PY_BIG_ENDIAN] = new_refcnt;
|
op->ob_refcnt_split[PY_BIG_ENDIAN] = new_refcnt;
|
||||||
|
|
Loading…
Reference in New Issue