gh-98724: Fix Py_CLEAR() macro side effects (#99100) (#100070)

The Py_CLEAR(), Py_SETREF() and Py_XSETREF() macros now only evaluate
their arguments once. If an argument has side effects, these side
effects are no longer duplicated.

Use temporary variables to avoid duplicating side effects of macro
arguments. If available, use _Py_TYPEOF() to avoid type punning.
Otherwise, use memcpy() for the assignment to prevent a
miscompilation with strict aliasing caused by type punning.

Add _Py_TYPEOF() macro: __typeof__() on GCC and clang.

Add test_py_clear() and test_py_setref() unit tests to _testcapi.
This commit is contained in:
Victor Stinner 2022-12-07 15:22:38 +01:00 committed by GitHub
parent 7031275776
commit b11a384dc7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 236 additions and 30 deletions

View file

@ -7,8 +7,8 @@
Reference Counting
******************
The macros in this section are used for managing reference counts of Python
objects.
The functions and macros in this section are used for managing reference counts
of Python objects.
.. c:function:: Py_ssize_t Py_REFCNT(PyObject *o)
@ -129,6 +129,11 @@ objects.
It is a good idea to use this macro whenever decrementing the reference
count of an object that might be traversed during garbage collection.
.. versionchanged:: 3.12
The macro argument is now only evaluated once. If the argument has side
effects, these are no longer duplicated.
.. c:function:: void Py_IncRef(PyObject *o)
Increment the reference count for object *o*. A function version of :c:func:`Py_XINCREF`.
@ -139,3 +144,40 @@ objects.
Decrement the reference count for object *o*. A function version of :c:func:`Py_XDECREF`.
It can be used for runtime dynamic embedding of Python.
.. c:macro:: Py_SETREF(dst, src)
Macro safely decrementing the `dst` reference count and setting `dst` to
`src`.
As in case of :c:func:`Py_CLEAR`, "the obvious" code can be deadly::
Py_DECREF(dst);
dst = src;
The safe way is::
Py_SETREF(dst, src);
That arranges to set `dst` to `src` _before_ decrementing reference count of
*dst* old value, so that any code triggered as a side-effect of `dst`
getting torn down no longer believes `dst` points to a valid object.
.. versionadded:: 3.6
.. versionchanged:: 3.12
The macro arguments are now only evaluated once. If an argument has side
effects, these are no longer duplicated.
.. c:macro:: Py_XSETREF(dst, src)
Variant of :c:macro:`Py_SETREF` macro that uses :c:func:`Py_XDECREF` instead
of :c:func:`Py_DECREF`.
.. versionadded:: 3.6
.. versionchanged:: 3.12
The macro arguments are now only evaluated once. If an argument has side
effects, these are no longer duplicated.