summaryrefslogtreecommitdiffstats
path: root/Include/object.h
diff options
context:
space:
mode:
Diffstat (limited to 'Include/object.h')
-rw-r--r--Include/object.h43
1 files changed, 36 insertions, 7 deletions
diff --git a/Include/object.h b/Include/object.h
index 75624fe..3774f12 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -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)