diff --git a/Doc/c-api/refcounting.rst b/Doc/c-api/refcounting.rst index 391907c8c2976a..6c3ab270c7f095 100644 --- a/Doc/c-api/refcounting.rst +++ b/Doc/c-api/refcounting.rst @@ -25,7 +25,7 @@ objects. .. c:function:: void Py_XINCREF(PyObject *o) - Increment the reference count for object *o*. The object may be ``NULL``, in + Increment the reference count for object *o*. The object can be ``NULL``, in which case the macro has no effect. See also :c:func:`Py_XNewRef`. @@ -78,6 +78,9 @@ objects. The object must not be ``NULL``; if you aren't sure that it isn't ``NULL``, use :c:func:`Py_XDECREF`. + Use :c:func:`Py_CLEAR` to set a variable to ``NULL``. See also + :c:func:`Py_SetRef`. + .. warning:: The deallocation function can cause arbitrary Python code to be invoked (e.g. @@ -92,19 +95,61 @@ objects. .. c:function:: void Py_XDECREF(PyObject *o) - Decrement the reference count for object *o*. The object may be ``NULL``, in + Decrement the reference count for object *o*. The object can be ``NULL``, in which case the macro has no effect; otherwise the effect is the same as for :c:func:`Py_DECREF`, and the same warning applies. + Use :c:func:`Py_CLEAR` to set a variable to ``NULL``. See also + :c:func:`Py_XSetRef`. -.. c:function:: void Py_CLEAR(PyObject *o) - Decrement the reference count for object *o*. The object may be ``NULL``, in - which case the macro has no effect; otherwise the effect is the same as for - :c:func:`Py_DECREF`, except that the argument is also set to ``NULL``. The warning - for :c:func:`Py_DECREF` does not apply with respect to the object passed because - the macro carefully uses a temporary variable and sets the argument to ``NULL`` - before decrementing its reference count. +.. c:function:: void Py_SetRef(PyObject **ref, PyObject *new_obj) + + Set the :term:`strong reference` ``*ref`` to *new_obj* object (without + increasing its reference count), and then decrement the reference count of + the object previously pointed by ``*ref``. + + *ref* and ``*ref`` must not be ``NULL``. + + This function avoids creating a temporary dangling pointer. Example:: + + Py_DECREF(global_var); // unsafe + global_var = new_obj; + + If the :c:func:`Py_DECREF` call destroys the object previously pointed by + *global_var*, the object finalizer can run arbitrary code which can use the + *global_var* variable. Since the *global_var* became a dangling pointer, + using it crashs Python. The example can be fixed by using + :c:func:`Py_SetRef`: + + Py_SetRef(&global_var, new_obj); // safe + + See also the :c:func:`Py_CLEAR` macro. + + .. versionadded:: 3.10 + + +.. c:function:: void Py_XSetRef(PyObject **ref, PyObject *new_obj) + + Similar to :c:func:`Py_SetRef`, but ``*ref`` can be ``NULL``. + + If ``*ref`` is ``NULL``, do nothing. + + See also :c:func:`Py_CLEAR` macro. + + .. versionadded:: 3.10 + + +.. c:function:: void Py_CLEAR(PyObject *op) + + Clear a :term:`strong reference`: decrement the reference count for object + *op* and set *op* to ``NULL``. + + If *op* is ``NULL``, do nothing. + + The ``Py_CLEAR(var)`` macro is implemented with the :c:func:`Py_XSetRef` + function as ``Py_XSetRef(&var, NULL)`` to avoid creating a temporary + dangling pointer. It is a good idea to use this macro whenever decrementing the reference count of an object that might be traversed during garbage collection. diff --git a/Doc/glossary.rst b/Doc/glossary.rst index b410585ca818c1..d7ec4b71a73fff 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -1118,9 +1118,12 @@ Glossary decrements the object reference count when it is deleted. The :c:func:`Py_NewRef` function can be used to create a strong reference - to an object. Usually, the :c:func:`Py_DECREF` function must be called on - the strong reference before exiting the scope of the strong reference, to - avoid leaking one reference. + to an object. Usually, the :c:func:`Py_CLEAR` or :c:func:`Py_DECREF` + function must be called on the strong reference before exiting the scope + of the strong reference, to avoid leaking a reference. + + The :c:func:`Py_SetRef` function can be used to set a strong reference to + a new object without creating a temporary dangling pointer. See also :term:`borrowed reference`. diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index a735bf235435ca..17de0631b42a5e 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -429,6 +429,11 @@ New Features slot. (Contributed by Hai Shi in :issue:`41832`.) +* Added :c:func:`Py_SetRef` and :c:func:`Py_XSetRef` functions to set a + :term:`strong reference` to a new object without a creating temporary + dangling pointer. + (Contributed by Victor Stinner in :issue:`42294`.) + Porting to Python 3.10 ---------------------- diff --git a/Include/cpython/object.h b/Include/cpython/object.h index 0db53c312f07b7..891eeffca95bf9 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -337,38 +337,15 @@ _PyObject_GenericSetAttrWithDict(PyObject *, PyObject *, PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *); -/* Safely decref `op` and set `op` to `op2`. - * - * As in case of Py_CLEAR "the obvious" code can be deadly: - * - * Py_DECREF(op); - * op = op2; - * - * The safe way is: - * - * Py_SETREF(op, op2); - * - * That arranges to set `op` to `op2` _before_ decref'ing, so that any code - * triggered as a side-effect of `op` getting torn down no longer believes - * `op` points to a valid object. - * - * Py_XSETREF is a variant of Py_SETREF that uses Py_XDECREF instead of - * Py_DECREF. - */ - -#define Py_SETREF(op, op2) \ - do { \ - PyObject *_py_tmp = _PyObject_CAST(op); \ - (op) = (op2); \ - Py_DECREF(_py_tmp); \ - } while (0) - -#define Py_XSETREF(op, op2) \ - do { \ - PyObject *_py_tmp = _PyObject_CAST(op); \ - (op) = (op2); \ - Py_XDECREF(_py_tmp); \ - } while (0) +// Set op to new_obj without creating a temporary dangling pointer (op). +// See also the Py_SetRef() function and the Py_CLEAR() macro. +#define Py_SETREF(op, new_obj) \ + Py_SetRef(_PyObjectPtr_CAST(&(op)), _PyObject_CAST(new_obj)) + +// Similar to Py_SETREF() but op can be NULL. +// See also the Py_XSetRef() function and the Py_CLEAR() macro. +#define Py_XSETREF(op, new_obj) \ + Py_XSetRef(_PyObjectPtr_CAST(&(op)), _PyObject_CAST(new_obj)) PyAPI_DATA(PyTypeObject) _PyNone_Type; diff --git a/Include/object.h b/Include/object.h index eab3228f3abe41..046502c059fcc8 100644 --- a/Include/object.h +++ b/Include/object.h @@ -112,6 +112,9 @@ typedef struct _object { #define _PyObject_CAST(op) ((PyObject*)(op)) #define _PyObject_CAST_CONST(op) ((const PyObject*)(op)) +/* Cast argument to PyObject** type. */ +#define _PyObjectPtr_CAST(op) ((PyObject**)(op)) + typedef struct { PyObject ob_base; Py_ssize_t ob_size; /* Number of items in variable part */ @@ -491,14 +494,7 @@ static inline void _Py_DECREF( * Python integers aren't currently weakly referencable. Best practice is * to use Py_CLEAR() even if you can't think of a reason for why you need to. */ -#define Py_CLEAR(op) \ - do { \ - PyObject *_py_tmp = _PyObject_CAST(op); \ - if (_py_tmp != NULL) { \ - (op) = NULL; \ - Py_DECREF(_py_tmp); \ - } \ - } while (0) +#define Py_CLEAR(op) Py_XSetRef(_PyObjectPtr_CAST(&(op)), NULL) /* Function to use in case the object pointer can be NULL: */ static inline void _Py_XINCREF(PyObject *op) @@ -551,6 +547,35 @@ static inline PyObject* _Py_XNewRef(PyObject *obj) #define Py_NewRef(obj) _Py_NewRef(obj) #define Py_XNewRef(obj) _Py_XNewRef(obj) +// Set a strong reference *ref to obj and then decrement the reference count +// of the object previously pointed by *ref. +// See also the Py_SETREF() macro. +PyAPI_FUNC(void) Py_SetRef(PyObject **ref, PyObject *new_obj); + +// Similar to Py_SetRef(), but *ref can be NULL. +// See also the Py_XSETREF() macros. +PyAPI_FUNC(void) Py_XSetRef(PyObject **ref, PyObject *new_obj); + +static inline void _Py_SetRef(PyObject **ref, PyObject *new_obj) +{ + PyObject *old = *ref; + *ref = new_obj; + Py_DECREF(old); +} + +static inline void _Py_XSetRef(PyObject **ref, PyObject *new_obj) +{ + PyObject *old = *ref; + *ref = new_obj; + Py_XDECREF(old); +} + +// Py_SetRef() and Py_XSetRef() are exported as functions for the stable ABI. +// Names overriden with macros by static inline functions for best +// performances. +#define Py_SetRef(ref, new_obj) _Py_SetRef(ref, new_obj) +#define Py_XSetRef(ref, new_obj) _Py_XSetRef(ref, new_obj) + /* _Py_NoneStruct is an object of undefined type which can be used in contexts diff --git a/Misc/NEWS.d/next/C API/2020-11-09-14-30-09.bpo-42294.PJ90wP.rst b/Misc/NEWS.d/next/C API/2020-11-09-14-30-09.bpo-42294.PJ90wP.rst new file mode 100644 index 00000000000000..7cf3f322aa2a37 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-11-09-14-30-09.bpo-42294.PJ90wP.rst @@ -0,0 +1,3 @@ +Add :c:func:`Py_SetRef` and :c:func:`Py_XSetRef` functions to set a +:term:`strong reference` to a new reference without creating a temporary +dangling pointer. Patch by Victor Stinner. diff --git a/Objects/object.c b/Objects/object.c index be7790eefd118f..5046561c234f30 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2224,6 +2224,23 @@ Py_XNewRef(PyObject *obj) return _Py_XNewRef(obj); } + +#undef Py_SetRef +#undef Py_XSetRef + +// Export Py_SetRef() and Py_XSetRef() as regular functions for the stable ABI. +void +Py_SetRef(PyObject **ref, PyObject *new_obj) +{ + return _Py_SetRef(ref, new_obj); +} + +void +Py_XSetRef(PyObject **ref, PyObject *new_obj) +{ + return _Py_XSetRef(ref, new_obj); +} + #ifdef __cplusplus } #endif diff --git a/PC/python3dll.c b/PC/python3dll.c index d1fdd0ac54ca8d..64dfd756438af3 100644 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -79,9 +79,11 @@ EXPORT_FUNC(Py_SetPath) EXPORT_FUNC(Py_SetProgramName) EXPORT_FUNC(Py_SetPythonHome) EXPORT_FUNC(Py_SetRecursionLimit) +EXPORT_FUNC(Py_SetRef) EXPORT_FUNC(Py_SymtableString) EXPORT_FUNC(Py_VaBuildValue) EXPORT_FUNC(Py_XNewRef) +EXPORT_FUNC(Py_XSetRef) EXPORT_FUNC(PyArg_Parse) EXPORT_FUNC(PyArg_ParseTuple) EXPORT_FUNC(PyArg_ParseTupleAndKeywords)