From 6cb88977e006ec426daa823921739e12620ac64c Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 14 Jul 2024 17:34:24 +0800 Subject: [PATCH 1/6] Fix races in tracebacks --- Objects/exceptions.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 6376f2f012a7d6..e0925190993386 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -308,7 +308,7 @@ BaseException_set_args(PyBaseExceptionObject *self, PyObject *val, void *Py_UNUS } static PyObject * -BaseException_get_tb(PyBaseExceptionObject *self, void *Py_UNUSED(ignored)) +BaseException_get_tb_lock_held(PyBaseExceptionObject *self, void *Py_UNUSED(ignored)) { if (self->traceback == NULL) { Py_RETURN_NONE; @@ -316,8 +316,18 @@ BaseException_get_tb(PyBaseExceptionObject *self, void *Py_UNUSED(ignored)) return Py_NewRef(self->traceback); } +static PyObject * +BaseException_get_tb(PyBaseExceptionObject *self, void *Py_UNUSED(ignored)) +{ + PyObject *res; + Py_BEGIN_CRITICAL_SECTION(self); + res = BaseException_get_tb_lock_held(self, NULL); + Py_END_CRITICAL_SECTION(); + return res; +} + static int -BaseException_set_tb(PyBaseExceptionObject *self, PyObject *tb, void *Py_UNUSED(ignored)) +BaseException_set_tb_lock_held(PyBaseExceptionObject *self, PyObject *tb, void *Py_UNUSED(ignored)) { if (tb == NULL) { PyErr_SetString(PyExc_TypeError, "__traceback__ may not be deleted"); @@ -337,6 +347,16 @@ BaseException_set_tb(PyBaseExceptionObject *self, PyObject *tb, void *Py_UNUSED( return 0; } +static int +BaseException_set_tb(PyBaseExceptionObject *self, PyObject *tb, void *Py_UNUSED(ignored)) +{ + int res; + Py_BEGIN_CRITICAL_SECTION(self); + res = BaseException_set_tb_lock_held(self, tb, NULL); + Py_END_CRITICAL_SECTION(); + return res; +} + static PyObject * BaseException_get_context(PyObject *self, void *Py_UNUSED(ignored)) { From 5bc855c165947e9e7a4a231c65c96f754392ef54 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 14 Jul 2024 17:41:28 +0800 Subject: [PATCH 2/6] use atomics in new_reference --- Objects/object.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Objects/object.c b/Objects/object.c index c4622359bb1035..eb72d58858ecaf 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2364,8 +2364,8 @@ new_reference(PyObject *op) op->_padding = 0; op->ob_mutex = (PyMutex){ 0 }; op->ob_gc_bits = 0; - op->ob_ref_local = 1; - op->ob_ref_shared = 0; + _Py_atomic_store_uint32_release(&op->ob_ref_local, 1); + _Py_atomic_store_ssize_release(&op->ob_ref_shared, 0); #endif #ifdef Py_TRACE_REFS _Py_AddToAllObjects(op); From da8415dee9fd519cd8fa9f2c6f6cad75c8d91ab0 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 14 Jul 2024 17:50:23 +0800 Subject: [PATCH 3/6] Add more atomics --- Include/cpython/pyatomic_gcc.h | 4 ++++ Include/cpython/pyatomic_msc.h | 13 +++++++++++++ Include/cpython/pyatomic_std.h | 8 ++++++++ Objects/object.c | 4 ++-- 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Include/cpython/pyatomic_gcc.h b/Include/cpython/pyatomic_gcc.h index ef09954d53ac1d..975bf9f988ae2e 100644 --- a/Include/cpython/pyatomic_gcc.h +++ b/Include/cpython/pyatomic_gcc.h @@ -516,6 +516,10 @@ static inline int _Py_atomic_load_int_acquire(const int *obj) { return __atomic_load_n(obj, __ATOMIC_ACQUIRE); } +static inline void +_Py_atomic_store_uint8_release(uint8_t *obj, uint8_t value) +{ __atomic_store_n(obj, value, __ATOMIC_RELEASE); } + static inline void _Py_atomic_store_uint32_release(uint32_t *obj, uint32_t value) { __atomic_store_n(obj, value, __ATOMIC_RELEASE); } diff --git a/Include/cpython/pyatomic_msc.h b/Include/cpython/pyatomic_msc.h index 84da21bdcbff4f..4c52f043fed99d 100644 --- a/Include/cpython/pyatomic_msc.h +++ b/Include/cpython/pyatomic_msc.h @@ -989,6 +989,19 @@ _Py_atomic_load_int_acquire(const int *obj) #endif } +static inline void +_Py_atomic_store_uint8_release(uint8_t *obj, uint8_t value) +{ +#if defined(_M_X64) || defined(_M_IX86) + *(uint8_t volatile *)obj = value; +#elif defined(_M_ARM64) + _Py_atomic_ASSERT_ARG_TYPE(unsigned __int8); + __stlr8((unsigned __int8 volatile *)obj, (unsigned __int8)value); +#else +# error "no implementation of _Py_atomic_store_uint32_release" +#endif +} + static inline void _Py_atomic_store_uint32_release(uint32_t *obj, uint32_t value) { diff --git a/Include/cpython/pyatomic_std.h b/Include/cpython/pyatomic_std.h index 7c71e94c68f8e6..74efb440e2768a 100644 --- a/Include/cpython/pyatomic_std.h +++ b/Include/cpython/pyatomic_std.h @@ -911,6 +911,14 @@ _Py_atomic_load_int_acquire(const int *obj) memory_order_acquire); } +static inline void +_Py_atomic_store_uint8_release(uint8_t *obj, uint8_t value) +{ + _Py_USING_STD; + atomic_store_explicit((_Atomic(uint8_t)*)obj, value, + memory_order_release); +} + static inline void _Py_atomic_store_uint32_release(uint32_t *obj, uint32_t value) { diff --git a/Objects/object.c b/Objects/object.c index eb72d58858ecaf..8a9630e3d30add 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2360,10 +2360,10 @@ new_reference(PyObject *op) #if !defined(Py_GIL_DISABLED) op->ob_refcnt = 1; #else - op->ob_tid = _Py_ThreadId(); + _Py_atomic_store_uintptr_release(&op->ob_tid, _Py_ThreadId()); op->_padding = 0; op->ob_mutex = (PyMutex){ 0 }; - op->ob_gc_bits = 0; + _Py_atomic_store_uint8_release(&op->ob_gc_bits, 0); _Py_atomic_store_uint32_release(&op->ob_ref_local, 1); _Py_atomic_store_ssize_release(&op->ob_ref_shared, 0); #endif From ef99cb93a5800c6b16bcd792bdedcbc5e5a91fa7 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Tue, 23 Jul 2024 00:21:37 +0800 Subject: [PATCH 4/6] Update Include/cpython/pyatomic_msc.h Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> --- Include/cpython/pyatomic_msc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/cpython/pyatomic_msc.h b/Include/cpython/pyatomic_msc.h index 4c52f043fed99d..9fe9320db943da 100644 --- a/Include/cpython/pyatomic_msc.h +++ b/Include/cpython/pyatomic_msc.h @@ -998,7 +998,7 @@ _Py_atomic_store_uint8_release(uint8_t *obj, uint8_t value) _Py_atomic_ASSERT_ARG_TYPE(unsigned __int8); __stlr8((unsigned __int8 volatile *)obj, (unsigned __int8)value); #else -# error "no implementation of _Py_atomic_store_uint32_release" +# error "no implementation of _Py_atomic_store_uint8_release" #endif } From 951a34d6743286eb9ee203d6c763df4c499ef081 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Wed, 24 Jul 2024 21:28:36 +0800 Subject: [PATCH 5/6] revert atomics --- Include/cpython/pyatomic_gcc.h | 4 ---- Include/cpython/pyatomic_msc.h | 13 ------------- Include/cpython/pyatomic_std.h | 8 -------- Objects/object.c | 8 ++++---- 4 files changed, 4 insertions(+), 29 deletions(-) diff --git a/Include/cpython/pyatomic_gcc.h b/Include/cpython/pyatomic_gcc.h index 975bf9f988ae2e..ef09954d53ac1d 100644 --- a/Include/cpython/pyatomic_gcc.h +++ b/Include/cpython/pyatomic_gcc.h @@ -516,10 +516,6 @@ static inline int _Py_atomic_load_int_acquire(const int *obj) { return __atomic_load_n(obj, __ATOMIC_ACQUIRE); } -static inline void -_Py_atomic_store_uint8_release(uint8_t *obj, uint8_t value) -{ __atomic_store_n(obj, value, __ATOMIC_RELEASE); } - static inline void _Py_atomic_store_uint32_release(uint32_t *obj, uint32_t value) { __atomic_store_n(obj, value, __ATOMIC_RELEASE); } diff --git a/Include/cpython/pyatomic_msc.h b/Include/cpython/pyatomic_msc.h index 9fe9320db943da..84da21bdcbff4f 100644 --- a/Include/cpython/pyatomic_msc.h +++ b/Include/cpython/pyatomic_msc.h @@ -989,19 +989,6 @@ _Py_atomic_load_int_acquire(const int *obj) #endif } -static inline void -_Py_atomic_store_uint8_release(uint8_t *obj, uint8_t value) -{ -#if defined(_M_X64) || defined(_M_IX86) - *(uint8_t volatile *)obj = value; -#elif defined(_M_ARM64) - _Py_atomic_ASSERT_ARG_TYPE(unsigned __int8); - __stlr8((unsigned __int8 volatile *)obj, (unsigned __int8)value); -#else -# error "no implementation of _Py_atomic_store_uint8_release" -#endif -} - static inline void _Py_atomic_store_uint32_release(uint32_t *obj, uint32_t value) { diff --git a/Include/cpython/pyatomic_std.h b/Include/cpython/pyatomic_std.h index 74efb440e2768a..7c71e94c68f8e6 100644 --- a/Include/cpython/pyatomic_std.h +++ b/Include/cpython/pyatomic_std.h @@ -911,14 +911,6 @@ _Py_atomic_load_int_acquire(const int *obj) memory_order_acquire); } -static inline void -_Py_atomic_store_uint8_release(uint8_t *obj, uint8_t value) -{ - _Py_USING_STD; - atomic_store_explicit((_Atomic(uint8_t)*)obj, value, - memory_order_release); -} - static inline void _Py_atomic_store_uint32_release(uint32_t *obj, uint32_t value) { diff --git a/Objects/object.c b/Objects/object.c index 8a9630e3d30add..c4622359bb1035 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2360,12 +2360,12 @@ new_reference(PyObject *op) #if !defined(Py_GIL_DISABLED) op->ob_refcnt = 1; #else - _Py_atomic_store_uintptr_release(&op->ob_tid, _Py_ThreadId()); + op->ob_tid = _Py_ThreadId(); op->_padding = 0; op->ob_mutex = (PyMutex){ 0 }; - _Py_atomic_store_uint8_release(&op->ob_gc_bits, 0); - _Py_atomic_store_uint32_release(&op->ob_ref_local, 1); - _Py_atomic_store_ssize_release(&op->ob_ref_shared, 0); + op->ob_gc_bits = 0; + op->ob_ref_local = 1; + op->ob_ref_shared = 0; #endif #ifdef Py_TRACE_REFS _Py_AddToAllObjects(op); From 6e0e17663260c5e7bec2cd29bfff4fa372411684 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Wed, 24 Jul 2024 22:09:54 +0800 Subject: [PATCH 6/6] Use argumentclinic --- Objects/clinic/exceptions.c.h | 61 +++++++++++++++++++++++++++++++++++ Objects/exceptions.c | 54 ++++++++++++++++--------------- 2 files changed, 89 insertions(+), 26 deletions(-) create mode 100644 Objects/clinic/exceptions.c.h diff --git a/Objects/clinic/exceptions.c.h b/Objects/clinic/exceptions.c.h new file mode 100644 index 00000000000000..fd7d69b20db1dc --- /dev/null +++ b/Objects/clinic/exceptions.c.h @@ -0,0 +1,61 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() + +#if defined(BaseException___traceback___HAS_DOCSTR) +# define BaseException___traceback___DOCSTR BaseException___traceback____doc__ +#else +# define BaseException___traceback___DOCSTR NULL +#endif +#if defined(BASEEXCEPTION___TRACEBACK___GETSETDEF) +# undef BASEEXCEPTION___TRACEBACK___GETSETDEF +# define BASEEXCEPTION___TRACEBACK___GETSETDEF {"__traceback__", (getter)BaseException___traceback___get, (setter)BaseException___traceback___set, BaseException___traceback___DOCSTR}, +#else +# define BASEEXCEPTION___TRACEBACK___GETSETDEF {"__traceback__", (getter)BaseException___traceback___get, NULL, BaseException___traceback___DOCSTR}, +#endif + +static PyObject * +BaseException___traceback___get_impl(PyBaseExceptionObject *self); + +static PyObject * +BaseException___traceback___get(PyBaseExceptionObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = BaseException___traceback___get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if defined(BASEEXCEPTION___TRACEBACK___HAS_DOCSTR) +# define BaseException___traceback___DOCSTR BaseException___traceback____doc__ +#else +# define BaseException___traceback___DOCSTR NULL +#endif +#if defined(BASEEXCEPTION___TRACEBACK___GETSETDEF) +# undef BASEEXCEPTION___TRACEBACK___GETSETDEF +# define BASEEXCEPTION___TRACEBACK___GETSETDEF {"__traceback__", (getter)BaseException___traceback___get, (setter)BaseException___traceback___set, BaseException___traceback___DOCSTR}, +#else +# define BASEEXCEPTION___TRACEBACK___GETSETDEF {"__traceback__", NULL, (setter)BaseException___traceback___set, NULL}, +#endif + +static int +BaseException___traceback___set_impl(PyBaseExceptionObject *self, + PyObject *value); + +static int +BaseException___traceback___set(PyBaseExceptionObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = BaseException___traceback___set_impl(self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} +/*[clinic end generated code: output=4821907e4f70fde6 input=a9049054013a1b77]*/ diff --git a/Objects/exceptions.c b/Objects/exceptions.c index e0925190993386..c6c49d2fcea013 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -16,6 +16,12 @@ #include "osdefs.h" // SEP +#include "clinic/exceptions.c.h" + +/*[clinic input] +class BaseException "PyBaseExceptionObject *" "&PyExc_BaseException" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=90558eb0fbf8a3d0]*/ /* Compatibility aliases */ PyObject *PyExc_EnvironmentError = NULL; // borrowed ref @@ -307,8 +313,15 @@ BaseException_set_args(PyBaseExceptionObject *self, PyObject *val, void *Py_UNUS return 0; } +/*[clinic input] +@critical_section +@getter +BaseException.__traceback__ +[clinic start generated code]*/ + static PyObject * -BaseException_get_tb_lock_held(PyBaseExceptionObject *self, void *Py_UNUSED(ignored)) +BaseException___traceback___get_impl(PyBaseExceptionObject *self) +/*[clinic end generated code: output=17cf874a52339398 input=a2277f0de62170cf]*/ { if (self->traceback == NULL) { Py_RETURN_NONE; @@ -316,27 +329,25 @@ BaseException_get_tb_lock_held(PyBaseExceptionObject *self, void *Py_UNUSED(igno return Py_NewRef(self->traceback); } -static PyObject * -BaseException_get_tb(PyBaseExceptionObject *self, void *Py_UNUSED(ignored)) -{ - PyObject *res; - Py_BEGIN_CRITICAL_SECTION(self); - res = BaseException_get_tb_lock_held(self, NULL); - Py_END_CRITICAL_SECTION(); - return res; -} +/*[clinic input] +@critical_section +@setter +BaseException.__traceback__ +[clinic start generated code]*/ static int -BaseException_set_tb_lock_held(PyBaseExceptionObject *self, PyObject *tb, void *Py_UNUSED(ignored)) +BaseException___traceback___set_impl(PyBaseExceptionObject *self, + PyObject *value) +/*[clinic end generated code: output=a82c86d9f29f48f0 input=12676035676badad]*/ { - if (tb == NULL) { + if (value == NULL) { PyErr_SetString(PyExc_TypeError, "__traceback__ may not be deleted"); return -1; } - if (PyTraceBack_Check(tb)) { - Py_XSETREF(self->traceback, Py_NewRef(tb)); + if (PyTraceBack_Check(value)) { + Py_XSETREF(self->traceback, Py_NewRef(value)); } - else if (tb == Py_None) { + else if (value == Py_None) { Py_CLEAR(self->traceback); } else { @@ -347,15 +358,6 @@ BaseException_set_tb_lock_held(PyBaseExceptionObject *self, PyObject *tb, void * return 0; } -static int -BaseException_set_tb(PyBaseExceptionObject *self, PyObject *tb, void *Py_UNUSED(ignored)) -{ - int res; - Py_BEGIN_CRITICAL_SECTION(self); - res = BaseException_set_tb_lock_held(self, tb, NULL); - Py_END_CRITICAL_SECTION(); - return res; -} static PyObject * BaseException_get_context(PyObject *self, void *Py_UNUSED(ignored)) @@ -419,7 +421,7 @@ BaseException_set_cause(PyObject *self, PyObject *arg, void *Py_UNUSED(ignored)) static PyGetSetDef BaseException_getset[] = { {"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict}, {"args", (getter)BaseException_get_args, (setter)BaseException_set_args}, - {"__traceback__", (getter)BaseException_get_tb, (setter)BaseException_set_tb}, + BASEEXCEPTION___TRACEBACK___GETSETDEF {"__context__", BaseException_get_context, BaseException_set_context, PyDoc_STR("exception context")}, {"__cause__", BaseException_get_cause, @@ -439,7 +441,7 @@ PyException_GetTraceback(PyObject *self) int PyException_SetTraceback(PyObject *self, PyObject *tb) { - return BaseException_set_tb(_PyBaseExceptionObject_cast(self), tb, NULL); + return BaseException___traceback___set_impl(_PyBaseExceptionObject_cast(self), tb); } PyObject *