mirror of
https://github.com/python/cpython
synced 2026-09-29 12:10:30 +03:00
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:
parent
7031275776
commit
b11a384dc7
7 changed files with 236 additions and 30 deletions
|
|
@ -305,38 +305,69 @@ _PyObject_GenericSetAttrWithDict(PyObject *, PyObject *,
|
|||
|
||||
PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *);
|
||||
|
||||
/* Safely decref `op` and set `op` to `op2`.
|
||||
/* Safely decref `dst` and set `dst` to `src`.
|
||||
*
|
||||
* As in case of Py_CLEAR "the obvious" code can be deadly:
|
||||
*
|
||||
* Py_DECREF(op);
|
||||
* op = op2;
|
||||
* Py_DECREF(dst);
|
||||
* dst = src;
|
||||
*
|
||||
* The safe way is:
|
||||
*
|
||||
* Py_SETREF(op, op2);
|
||||
* Py_SETREF(dst, src);
|
||||
*
|
||||
* 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.
|
||||
* That arranges to set `dst` to `src` _before_ decref'ing, so that any code
|
||||
* triggered as a side-effect of `dst` getting torn down no longer believes
|
||||
* `dst` points to a valid object.
|
||||
*
|
||||
* Py_XSETREF is a variant of Py_SETREF that uses Py_XDECREF instead of
|
||||
* Py_DECREF.
|
||||
* Temporary variables are used to only evalutate macro arguments once and so
|
||||
* avoid the duplication of side effects. _Py_TYPEOF() or memcpy() is used to
|
||||
* avoid a miscompilation caused by type punning. See Py_CLEAR() comment for
|
||||
* implementation details about type punning.
|
||||
*
|
||||
* The memcpy() implementation does not emit a compiler warning if 'src' has
|
||||
* not the same type than 'src': any pointer type is accepted for 'src'.
|
||||
*/
|
||||
|
||||
#define Py_SETREF(op, op2) \
|
||||
do { \
|
||||
PyObject *_py_tmp = _PyObject_CAST(op); \
|
||||
(op) = (op2); \
|
||||
Py_DECREF(_py_tmp); \
|
||||
#ifdef _Py_TYPEOF
|
||||
#define Py_SETREF(dst, src) \
|
||||
do { \
|
||||
_Py_TYPEOF(dst)* _tmp_dst_ptr = &(dst); \
|
||||
_Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \
|
||||
*_tmp_dst_ptr = (src); \
|
||||
Py_DECREF(_tmp_old_dst); \
|
||||
} while (0)
|
||||
|
||||
#define Py_XSETREF(op, op2) \
|
||||
do { \
|
||||
PyObject *_py_tmp = _PyObject_CAST(op); \
|
||||
(op) = (op2); \
|
||||
Py_XDECREF(_py_tmp); \
|
||||
#else
|
||||
#define Py_SETREF(dst, src) \
|
||||
do { \
|
||||
PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \
|
||||
PyObject *_tmp_old_dst = (*_tmp_dst_ptr); \
|
||||
PyObject *_tmp_src = _PyObject_CAST(src); \
|
||||
memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \
|
||||
Py_DECREF(_tmp_old_dst); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/* Py_XSETREF() is a variant of Py_SETREF() that uses Py_XDECREF() instead of
|
||||
* Py_DECREF().
|
||||
*/
|
||||
#ifdef _Py_TYPEOF
|
||||
#define Py_XSETREF(dst, src) \
|
||||
do { \
|
||||
_Py_TYPEOF(dst)* _tmp_dst_ptr = &(dst); \
|
||||
_Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \
|
||||
*_tmp_dst_ptr = (src); \
|
||||
Py_XDECREF(_tmp_old_dst); \
|
||||
} while (0)
|
||||
#else
|
||||
#define Py_XSETREF(dst, src) \
|
||||
do { \
|
||||
PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \
|
||||
PyObject *_tmp_old_dst = (*_tmp_dst_ptr); \
|
||||
PyObject *_tmp_src = _PyObject_CAST(src); \
|
||||
memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \
|
||||
Py_XDECREF(_tmp_old_dst); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
|
||||
PyAPI_DATA(PyTypeObject) _PyNone_Type;
|
||||
|
|
|
|||
|
|
@ -598,15 +598,44 @@ static inline void Py_DECREF(PyObject *op)
|
|||
* one of those can't cause problems -- but in part that relies on that
|
||||
* 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.
|
||||
*
|
||||
* gh-98724: Use a temporary variable to only evaluate the macro argument once,
|
||||
* to avoid the duplication of side effects if the argument has side effects.
|
||||
*
|
||||
* gh-99701: If the PyObject* type is used with casting arguments to PyObject*,
|
||||
* the code can be miscompiled with strict aliasing because of type punning.
|
||||
* With strict aliasing, a compiler considers that two pointers of different
|
||||
* types cannot read or write the same memory which enables optimization
|
||||
* opportunities.
|
||||
*
|
||||
* If available, use _Py_TYPEOF() to use the 'op' type for temporary variables,
|
||||
* and so avoid type punning. Otherwise, use memcpy() which causes type erasure
|
||||
* and so prevents the compiler to reuse an old cached 'op' value after
|
||||
* Py_CLEAR().
|
||||
*/
|
||||
#define Py_CLEAR(op) \
|
||||
do { \
|
||||
PyObject *_py_tmp = _PyObject_CAST(op); \
|
||||
if (_py_tmp != NULL) { \
|
||||
(op) = NULL; \
|
||||
Py_DECREF(_py_tmp); \
|
||||
} \
|
||||
#ifdef _Py_TYPEOF
|
||||
#define Py_CLEAR(op) \
|
||||
do { \
|
||||
_Py_TYPEOF(op)* _tmp_op_ptr = &(op); \
|
||||
_Py_TYPEOF(op) _tmp_old_op = (*_tmp_op_ptr); \
|
||||
if (_tmp_old_op != NULL) { \
|
||||
*_tmp_op_ptr = _Py_NULL; \
|
||||
Py_DECREF(_tmp_old_op); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define Py_CLEAR(op) \
|
||||
do { \
|
||||
PyObject **_tmp_op_ptr = _Py_CAST(PyObject**, &(op)); \
|
||||
PyObject *_tmp_old_op = (*_tmp_op_ptr); \
|
||||
if (_tmp_old_op != NULL) { \
|
||||
PyObject *_null_ptr = _Py_NULL; \
|
||||
memcpy(_tmp_op_ptr, &_null_ptr, sizeof(PyObject*)); \
|
||||
Py_DECREF(_tmp_old_op); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
|
||||
/* Function to use in case the object pointer can be NULL: */
|
||||
static inline void Py_XINCREF(PyObject *op)
|
||||
|
|
|
|||
|
|
@ -698,6 +698,15 @@ extern char * _getpty(int *, int, mode_t, int);
|
|||
# define _Py__has_builtin(x) 0
|
||||
#endif
|
||||
|
||||
// _Py_TYPEOF(expr) gets the type of an expression.
|
||||
//
|
||||
// Example: _Py_TYPEOF(x) x_copy = (x);
|
||||
//
|
||||
// The macro is only defined if GCC or clang compiler is used.
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define _Py_TYPEOF(expr) __typeof__(expr)
|
||||
#endif
|
||||
|
||||
|
||||
/* A convenient way for code to know if sanitizers are enabled. */
|
||||
#if defined(__has_feature)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue