From cfced14417ff6b9e16ccddfb06c4400fb98d680a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 25 Jan 2025 16:29:35 +0100 Subject: [PATCH 1/8] fix UBSan failures for `TkappObject` --- Modules/_tkinter.c | 80 +++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index 45897817a56051..ea1a77ea2bc625 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -290,12 +290,14 @@ static PyThreadState *tcl_tstate = NULL; tcl_tstate = tstate; } #define CHECK_TCL_APPARTMENT \ - if (((TkappObject *)self)->threaded && \ - ((TkappObject *)self)->thread_id != Tcl_GetCurrentThread()) { \ - PyErr_SetString(PyExc_RuntimeError, \ - "Calling Tcl from different apartment"); \ - return 0; \ - } + do { \ + if (_TkappObject_CAST(self)->threaded && \ + _TkappObject_CAST(self)->thread_id != Tcl_GetCurrentThread()) { \ + PyErr_SetString(PyExc_RuntimeError, \ + "Calling Tcl from different apartment"); \ + return 0; \ + } \ + } while (0) #ifndef FREECAST #define FREECAST (char *) @@ -328,7 +330,8 @@ typedef struct { const Tcl_ObjType *PixelType; } TkappObject; -#define Tkapp_Interp(v) (((TkappObject *) (v))->interp) +#define _TkappObject_CAST(op) ((TkappObject *)(op)) +#define Tkapp_Interp(v) (_TkappObject_CAST(v)->interp) /**** Error Handling ****/ @@ -1457,7 +1460,7 @@ Tkapp_Call(PyObject *selfptr, PyObject *args) Tcl_Obj **objv = NULL; Tcl_Size objc; PyObject *res = NULL; - TkappObject *self = (TkappObject*)selfptr; + TkappObject *self = _TkappObject_CAST(selfptr); int flags = TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL; /* If args is a single tuple, replace with contents of tuple */ @@ -1742,7 +1745,7 @@ var_proc(Tcl_Event *evPtr, int flags) static PyObject* var_invoke(EventFunc func, PyObject *selfptr, PyObject *args, int flags) { - TkappObject *self = (TkappObject*)selfptr; + TkappObject *self = _TkappObject_CAST(selfptr); if (self->threaded && self->thread_id != Tcl_GetCurrentThread()) { VarEvent *ev; // init 'res' and 'exc' to make static analyzers happy @@ -1800,11 +1803,11 @@ SetVar(TkappObject *self, PyObject *args, int flags) return NULL; if (flags & TCL_GLOBAL_ONLY) { - TRACE((TkappObject *)self, ("((ssssO))", "uplevel", "#0", "set", - name1, newValue)); + TRACE(self, ("((ssssO))", "uplevel", "#0", "set", + name1, newValue)); } else { - TRACE((TkappObject *)self, ("((ssO))", "set", name1, newValue)); + TRACE(self, ("((ssO))", "set", name1, newValue)); } ENTER_TCL @@ -1827,16 +1830,16 @@ SetVar(TkappObject *self, PyObject *args, int flags) /* XXX must hold tcl lock already??? */ newval = AsObj(newValue); - if (((TkappObject *)self)->trace) { + if (self->trace) { if (flags & TCL_GLOBAL_ONLY) { - TRACE((TkappObject *)self, ("((sssNO))", "uplevel", "#0", "set", - PyUnicode_FromFormat("%s(%s)", name1, name2), - newValue)); + TRACE(self, ("((sssNO))", "uplevel", "#0", "set", + PyUnicode_FromFormat("%s(%s)", name1, name2), + newValue)); } else { - TRACE((TkappObject *)self, ("((sNO))", "set", - PyUnicode_FromFormat("%s(%s)", name1, name2), - newValue)); + TRACE(self, ("((sNO))", "set", + PyUnicode_FromFormat("%s(%s)", name1, name2), + newValue)); } } @@ -1921,29 +1924,30 @@ UnsetVar(TkappObject *self, PyObject *args, int flags) int code; PyObject *res = NULL; - if (!PyArg_ParseTuple(args, "s|s:unsetvar", &name1, &name2)) + if (!PyArg_ParseTuple(args, "s|s:unsetvar", &name1, &name2)) { return NULL; + } CHECK_STRING_LENGTH(name1); CHECK_STRING_LENGTH(name2); - if (((TkappObject *)self)->trace) { + if (self->trace) { if (flags & TCL_GLOBAL_ONLY) { if (name2) { - TRACE((TkappObject *)self, ("((sssN))", "uplevel", "#0", "unset", - PyUnicode_FromFormat("%s(%s)", name1, name2))); + TRACE(self, ("((sssN))", "uplevel", "#0", "unset", + PyUnicode_FromFormat("%s(%s)", name1, name2))); } else { - TRACE((TkappObject *)self, ("((ssss))", "uplevel", "#0", "unset", name1)); + TRACE(self, ("((ssss))", "uplevel", "#0", "unset", name1)); } } else { if (name2) { - TRACE((TkappObject *)self, ("((sN))", "unset", - PyUnicode_FromFormat("%s(%s)", name1, name2))); + TRACE(self, ("((sN))", "unset", + PyUnicode_FromFormat("%s(%s)", name1, name2))); } else { - TRACE((TkappObject *)self, ("((ss))", "unset", name1)); + TRACE(self, ("((ss))", "unset", name1)); } } } @@ -2958,16 +2962,17 @@ _tkinter_tkapp_loadtk_impl(TkappObject *self) } static PyObject * -Tkapp_WantObjects(PyObject *self, PyObject *args) +Tkapp_WantObjects(PyObject *op, PyObject *args) { - + TkappObject *self = _TkappObject_CAST(op); int wantobjects = -1; - if (!PyArg_ParseTuple(args, "|i:wantobjects", &wantobjects)) + if (!PyArg_ParseTuple(args, "|i:wantobjects", &wantobjects)) { return NULL; - if (wantobjects == -1) - return PyLong_FromLong(((TkappObject*)self)->wantobjects); - ((TkappObject*)self)->wantobjects = wantobjects; - + } + if (wantobjects == -1) { + return PyLong_FromLong(self->wantobjects); + } + self->wantobjects = wantobjects; Py_RETURN_NONE; } @@ -3030,14 +3035,15 @@ _tkinter_tkapp_willdispatch_impl(TkappObject *self) /**** Tkapp Type Methods ****/ static void -Tkapp_Dealloc(PyObject *self) +Tkapp_Dealloc(PyObject *op) { - PyObject *tp = (PyObject *) Py_TYPE(self); + TkappObject *self = _TkappObject_CAST(op); + PyTypeObject *tp = Py_TYPE(self); /*CHECK_TCL_APPARTMENT;*/ ENTER_TCL Tcl_DeleteInterp(Tkapp_Interp(self)); LEAVE_TCL - Py_XDECREF(((TkappObject *)self)->trace); + Py_XDECREF(self->trace); PyObject_Free(self); Py_DECREF(tp); DisableEventHook(); From d2b723dea977a23e3de3b9e4c7b8ada312d05ed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 25 Jan 2025 16:38:27 +0100 Subject: [PATCH 2/8] fix UBSan failures for `PyTclObject` --- Modules/_tkinter.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index ea1a77ea2bc625..aaf01fcaaf4167 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -766,6 +766,8 @@ typedef struct { PyObject *string; /* This cannot cause cycles. */ } PyTclObject; +#define _PyTclObject_CAST(op) ((PyTclObject *)(op)) + static PyObject *PyTclObject_Type; #define PyTclObject_Check(v) Py_IS_TYPE(v, (PyTypeObject *) PyTclObject_Type) @@ -785,7 +787,7 @@ newPyTclObject(Tcl_Obj *arg) static void PyTclObject_dealloc(PyObject *_self) { - PyTclObject *self = (PyTclObject *)_self; + PyTclObject *self = _PyTclObject_CAST(_self); PyObject *tp = (PyObject *) Py_TYPE(self); Tcl_DecrRefCount(self->value); Py_XDECREF(self->string); @@ -798,9 +800,9 @@ PyDoc_STRVAR(PyTclObject_string__doc__, "the string representation of this object, either as str or bytes"); static PyObject * -PyTclObject_string(PyObject *_self, void *ignored) +PyTclObject_string(PyObject *_self, void *Py_UNUSED(closure)) { - PyTclObject *self = (PyTclObject *)_self; + PyTclObject *self = _PyTclObject_CAST(_self); if (!self->string) { self->string = unicodeFromTclObj(NULL, self->value); if (!self->string) @@ -812,7 +814,7 @@ PyTclObject_string(PyObject *_self, void *ignored) static PyObject * PyTclObject_str(PyObject *_self) { - PyTclObject *self = (PyTclObject *)_self; + PyTclObject *self = _PyTclObject_CAST(_self); if (self->string) { return Py_NewRef(self->string); } @@ -823,7 +825,7 @@ PyTclObject_str(PyObject *_self) static PyObject * PyTclObject_repr(PyObject *_self) { - PyTclObject *self = (PyTclObject *)_self; + PyTclObject *self = _PyTclObject_CAST(_self); PyObject *repr, *str = PyTclObject_str(_self); if (str == NULL) return NULL; @@ -849,21 +851,25 @@ PyTclObject_richcompare(PyObject *self, PyObject *other, int op) Py_RETURN_NOTIMPLEMENTED; } - if (self == other) + if (self == other) { /* fast path when self and other are identical */ result = 0; - else + } + else { + // 'self' and 'other' are known to be of correct type, + // so we can fast cast them (no need to use the macro) result = strcmp(Tcl_GetString(((PyTclObject *)self)->value), Tcl_GetString(((PyTclObject *)other)->value)); + } Py_RETURN_RICHCOMPARE(result, 0, op); } PyDoc_STRVAR(get_typename__doc__, "name of the Tcl type"); static PyObject* -get_typename(PyObject *self, void* ignored) +get_typename(PyObject *self, void *Py_UNUSED(closure)) { - PyTclObject *obj = (PyTclObject *)self; + PyTclObject *obj = _PyTclObject_CAST(self); return unicodeFromTclString(obj->value->typePtr->name); } @@ -1708,7 +1714,7 @@ varname_converter(PyObject *in, void *_out) return 1; } if (PyTclObject_Check(in)) { - *out = Tcl_GetString(((PyTclObject *)in)->value); + *out = Tcl_GetString(((PyTclObject *)in)->value); // safe fast cast return 1; } PyErr_Format(PyExc_TypeError, From 8c0e55eac21a9384bab1eb817cbba8e19373ea12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 25 Jan 2025 16:48:08 +0100 Subject: [PATCH 3/8] fix UBSan failures for `TkttObject` --- Modules/_tkinter.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index aaf01fcaaf4167..7b075d7b217cfc 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -2689,6 +2689,8 @@ _tkinter_tkapp_deletefilehandler(TkappObject *self, PyObject *file) /**** Tktt Object (timer token) ****/ static PyObject *Tktt_Type; +#define _TkttObject_Check(op) \ + PyObject_TypeCheck((op), (PyTypeObject *)Tktt_Type) typedef struct { PyObject_HEAD @@ -2696,6 +2698,10 @@ typedef struct { PyObject *func; } TkttObject; +#define _TkttObject_FAST_CAST(op) ((TkttObject *)(op)) +#define _TkttObject_CAST(op) (assert(_TkttObject_Check(op)), \ + _TkttObject_FAST_CAST(op)) + /*[clinic input] _tkinter.tktimertoken.deletetimerhandler @@ -2740,7 +2746,7 @@ Tktt_New(PyObject *func) static void Tktt_Dealloc(PyObject *self) { - TkttObject *v = (TkttObject *)self; + TkttObject *v = _TkttObject_CAST(self); PyObject *func = v->func; PyObject *tp = (PyObject *) Py_TYPE(self); @@ -2753,7 +2759,7 @@ Tktt_Dealloc(PyObject *self) static PyObject * Tktt_Repr(PyObject *self) { - TkttObject *v = (TkttObject *)self; + TkttObject *v = _TkttObject_CAST(self); return PyUnicode_FromFormat("", v, v->func == NULL ? ", handler deleted" : ""); @@ -2764,7 +2770,7 @@ Tktt_Repr(PyObject *self) static void TimerHandler(ClientData clientData) { - TkttObject *v = (TkttObject *)clientData; + TkttObject *v = _TkttObject_CAST(clientData); PyObject *func = v->func; PyObject *res; From 96625280999272abeb08334dceaa151e476b38b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 8 Feb 2025 10:20:29 +0100 Subject: [PATCH 4/8] Do Do not use `_` + capital letter in new code as it is also UB. --- Modules/_tkinter.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index 7b075d7b217cfc..b9629c24f7b796 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -330,8 +330,8 @@ typedef struct { const Tcl_ObjType *PixelType; } TkappObject; -#define _TkappObject_CAST(op) ((TkappObject *)(op)) -#define Tkapp_Interp(v) (_TkappObject_CAST(v)->interp) +#define TkappObject_CAST(op) ((TkappObject *)(op)) +#define Tkapp_Interp(v) (TkappObject_CAST(v)->interp) /**** Error Handling ****/ @@ -766,7 +766,7 @@ typedef struct { PyObject *string; /* This cannot cause cycles. */ } PyTclObject; -#define _PyTclObject_CAST(op) ((PyTclObject *)(op)) +#define PyTclObject_CAST(op) ((PyTclObject *)(op)) static PyObject *PyTclObject_Type; #define PyTclObject_Check(v) Py_IS_TYPE(v, (PyTypeObject *) PyTclObject_Type) @@ -787,7 +787,7 @@ newPyTclObject(Tcl_Obj *arg) static void PyTclObject_dealloc(PyObject *_self) { - PyTclObject *self = _PyTclObject_CAST(_self); + PyTclObject *self = PyTclObject_CAST(_self); PyObject *tp = (PyObject *) Py_TYPE(self); Tcl_DecrRefCount(self->value); Py_XDECREF(self->string); @@ -802,7 +802,7 @@ PyDoc_STRVAR(PyTclObject_string__doc__, static PyObject * PyTclObject_string(PyObject *_self, void *Py_UNUSED(closure)) { - PyTclObject *self = _PyTclObject_CAST(_self); + PyTclObject *self = PyTclObject_CAST(_self); if (!self->string) { self->string = unicodeFromTclObj(NULL, self->value); if (!self->string) @@ -814,7 +814,7 @@ PyTclObject_string(PyObject *_self, void *Py_UNUSED(closure)) static PyObject * PyTclObject_str(PyObject *_self) { - PyTclObject *self = _PyTclObject_CAST(_self); + PyTclObject *self = PyTclObject_CAST(_self); if (self->string) { return Py_NewRef(self->string); } @@ -825,7 +825,7 @@ PyTclObject_str(PyObject *_self) static PyObject * PyTclObject_repr(PyObject *_self) { - PyTclObject *self = _PyTclObject_CAST(_self); + PyTclObject *self = PyTclObject_CAST(_self); PyObject *repr, *str = PyTclObject_str(_self); if (str == NULL) return NULL; @@ -869,7 +869,7 @@ PyDoc_STRVAR(get_typename__doc__, "name of the Tcl type"); static PyObject* get_typename(PyObject *self, void *Py_UNUSED(closure)) { - PyTclObject *obj = _PyTclObject_CAST(self); + PyTclObject *obj = PyTclObject_CAST(self); return unicodeFromTclString(obj->value->typePtr->name); } @@ -1466,7 +1466,7 @@ Tkapp_Call(PyObject *selfptr, PyObject *args) Tcl_Obj **objv = NULL; Tcl_Size objc; PyObject *res = NULL; - TkappObject *self = _TkappObject_CAST(selfptr); + TkappObject *self = TkappObject_CAST(selfptr); int flags = TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL; /* If args is a single tuple, replace with contents of tuple */ @@ -1751,7 +1751,7 @@ var_proc(Tcl_Event *evPtr, int flags) static PyObject* var_invoke(EventFunc func, PyObject *selfptr, PyObject *args, int flags) { - TkappObject *self = _TkappObject_CAST(selfptr); + TkappObject *self = TkappObject_CAST(selfptr); if (self->threaded && self->thread_id != Tcl_GetCurrentThread()) { VarEvent *ev; // init 'res' and 'exc' to make static analyzers happy @@ -2689,7 +2689,7 @@ _tkinter_tkapp_deletefilehandler(TkappObject *self, PyObject *file) /**** Tktt Object (timer token) ****/ static PyObject *Tktt_Type; -#define _TkttObject_Check(op) \ +#define TkttObject_Check(op) \ PyObject_TypeCheck((op), (PyTypeObject *)Tktt_Type) typedef struct { @@ -2698,9 +2698,9 @@ typedef struct { PyObject *func; } TkttObject; -#define _TkttObject_FAST_CAST(op) ((TkttObject *)(op)) -#define _TkttObject_CAST(op) (assert(_TkttObject_Check(op)), \ - _TkttObject_FAST_CAST(op)) +#define TkttObject_FAST_CAST(op) ((TkttObject *)(op)) +#define TkttObject_CAST(op) \ + (assert(TkttObject_Check(op)), TkttObject_FAST_CAST(op)) /*[clinic input] _tkinter.tktimertoken.deletetimerhandler @@ -2746,7 +2746,7 @@ Tktt_New(PyObject *func) static void Tktt_Dealloc(PyObject *self) { - TkttObject *v = _TkttObject_CAST(self); + TkttObject *v = TkttObject_CAST(self); PyObject *func = v->func; PyObject *tp = (PyObject *) Py_TYPE(self); @@ -2759,7 +2759,7 @@ Tktt_Dealloc(PyObject *self) static PyObject * Tktt_Repr(PyObject *self) { - TkttObject *v = _TkttObject_CAST(self); + TkttObject *v = TkttObject_CAST(self); return PyUnicode_FromFormat("", v, v->func == NULL ? ", handler deleted" : ""); @@ -2770,7 +2770,7 @@ Tktt_Repr(PyObject *self) static void TimerHandler(ClientData clientData) { - TkttObject *v = _TkttObject_CAST(clientData); + TkttObject *v = TkttObject_CAST(clientData); PyObject *func = v->func; PyObject *res; @@ -2976,7 +2976,7 @@ _tkinter_tkapp_loadtk_impl(TkappObject *self) static PyObject * Tkapp_WantObjects(PyObject *op, PyObject *args) { - TkappObject *self = _TkappObject_CAST(op); + TkappObject *self = TkappObject_CAST(op); int wantobjects = -1; if (!PyArg_ParseTuple(args, "|i:wantobjects", &wantobjects)) { return NULL; @@ -3049,7 +3049,7 @@ _tkinter_tkapp_willdispatch_impl(TkappObject *self) static void Tkapp_Dealloc(PyObject *op) { - TkappObject *self = _TkappObject_CAST(op); + TkappObject *self = TkappObject_CAST(op); PyTypeObject *tp = Py_TYPE(self); /*CHECK_TCL_APPARTMENT;*/ ENTER_TCL From 6df44b6e9da67abaeb097797b73548bc03bca9b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 8 Feb 2025 10:37:01 +0100 Subject: [PATCH 5/8] fix compilation --- Modules/_tkinter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index b9629c24f7b796..75a111b5dc095c 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -291,8 +291,8 @@ static PyThreadState *tcl_tstate = NULL; #define CHECK_TCL_APPARTMENT \ do { \ - if (_TkappObject_CAST(self)->threaded && \ - _TkappObject_CAST(self)->thread_id != Tcl_GetCurrentThread()) { \ + if (TkappObject_CAST(self)->threaded && \ + TkappObject_CAST(self)->thread_id != Tcl_GetCurrentThread()) { \ PyErr_SetString(PyExc_RuntimeError, \ "Calling Tcl from different apartment"); \ return 0; \ From 08efc4c3f260f100673d3eee174656d3b11d88d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 25 Feb 2025 12:12:13 +0100 Subject: [PATCH 6/8] remove unused `TkttObject_FAST_CAST` macro --- Modules/_tkinter.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index 75a111b5dc095c..8212b879eb3e82 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -766,6 +766,8 @@ typedef struct { PyObject *string; /* This cannot cause cycles. */ } PyTclObject; +// TODO(picnixz): maybe assert that 'op' is really a PyTclObject (we might want +// to also add a FAST_CAST macro to bypass the check if needed). #define PyTclObject_CAST(op) ((PyTclObject *)(op)) static PyObject *PyTclObject_Type; @@ -2698,9 +2700,8 @@ typedef struct { PyObject *func; } TkttObject; -#define TkttObject_FAST_CAST(op) ((TkttObject *)(op)) #define TkttObject_CAST(op) \ - (assert(TkttObject_Check(op)), TkttObject_FAST_CAST(op)) + (assert(TkttObject_Check(op)), ((TkttObject *)(op))) /*[clinic input] _tkinter.tktimertoken.deletetimerhandler From f5f4efc3903d83bfb2a76681a82995793c6b9ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 25 Feb 2025 12:25:59 +0100 Subject: [PATCH 7/8] remove redundant macros and improve `CHECK_TCL_APPARTMENT` --- Modules/_tkinter.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index 8212b879eb3e82..ac2a1cff54d6ff 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -289,10 +289,10 @@ static PyThreadState *tcl_tstate = NULL; if(tcl_lock)PyThread_acquire_lock(tcl_lock, 1); \ tcl_tstate = tstate; } -#define CHECK_TCL_APPARTMENT \ +#define CHECK_TCL_APPARTMENT(op) \ do { \ - if (TkappObject_CAST(self)->threaded && \ - TkappObject_CAST(self)->thread_id != Tcl_GetCurrentThread()) { \ + TkappObject *app = TkappObject_CAST(op); \ + if (app->threaded && app->thread_id != Tcl_GetCurrentThread()) { \ PyErr_SetString(PyExc_RuntimeError, \ "Calling Tcl from different apartment"); \ return 0; \ @@ -771,7 +771,7 @@ typedef struct { #define PyTclObject_CAST(op) ((PyTclObject *)(op)) static PyObject *PyTclObject_Type; -#define PyTclObject_Check(v) Py_IS_TYPE(v, (PyTypeObject *) PyTclObject_Type) +#define PyTclObject_Check(v) Py_IS_TYPE(v, (PyTypeObject *)PyTclObject_Type) static PyObject * newPyTclObject(Tcl_Obj *arg) @@ -1553,7 +1553,7 @@ _tkinter_tkapp_eval_impl(TkappObject *self, const char *script) int err; CHECK_STRING_LENGTH(script); - CHECK_TCL_APPARTMENT; + CHECK_TCL_APPARTMENT(self); TRACE(self, ("((ss))", "eval", script)); @@ -1584,7 +1584,7 @@ _tkinter_tkapp_evalfile_impl(TkappObject *self, const char *fileName) int err; CHECK_STRING_LENGTH(fileName); - CHECK_TCL_APPARTMENT; + CHECK_TCL_APPARTMENT(self); TRACE(self, ("((ss))", "source", fileName)); @@ -1615,7 +1615,7 @@ _tkinter_tkapp_record_impl(TkappObject *self, const char *script) int err; CHECK_STRING_LENGTH(script); - CHECK_TCL_APPARTMENT; + CHECK_TCL_APPARTMENT(self); TRACE(self, ("((ssss))", "history", "add", script, "exec")); @@ -1643,7 +1643,7 @@ _tkinter_tkapp_adderrorinfo_impl(TkappObject *self, const char *msg) /*[clinic end generated code: output=52162eaca2ee53cb input=f4b37aec7c7e8c77]*/ { CHECK_STRING_LENGTH(msg); - CHECK_TCL_APPARTMENT; + CHECK_TCL_APPARTMENT(self); ENTER_TCL Tcl_AddErrorInfo(Tkapp_Interp(self), msg); @@ -2124,7 +2124,7 @@ _tkinter_tkapp_exprstring_impl(TkappObject *self, const char *s) int retval; CHECK_STRING_LENGTH(s); - CHECK_TCL_APPARTMENT; + CHECK_TCL_APPARTMENT(self); TRACE(self, ("((ss))", "expr", s)); @@ -2156,7 +2156,7 @@ _tkinter_tkapp_exprlong_impl(TkappObject *self, const char *s) long v; CHECK_STRING_LENGTH(s); - CHECK_TCL_APPARTMENT; + CHECK_TCL_APPARTMENT(self); TRACE(self, ("((ss))", "expr", s)); @@ -2188,7 +2188,7 @@ _tkinter_tkapp_exprdouble_impl(TkappObject *self, const char *s) int retval; CHECK_STRING_LENGTH(s); - CHECK_TCL_APPARTMENT; + CHECK_TCL_APPARTMENT(self); TRACE(self, ("((ss))", "expr", s)); @@ -2220,7 +2220,7 @@ _tkinter_tkapp_exprboolean_impl(TkappObject *self, const char *s) int v; CHECK_STRING_LENGTH(s); - CHECK_TCL_APPARTMENT; + CHECK_TCL_APPARTMENT(self); TRACE(self, ("((ss))", "expr", s)); @@ -2632,7 +2632,7 @@ _tkinter_tkapp_createfilehandler_impl(TkappObject *self, PyObject *file, FileHandler_ClientData *data; int tfile; - CHECK_TCL_APPARTMENT; + CHECK_TCL_APPARTMENT(self); tfile = PyObject_AsFileDescriptor(file); if (tfile < 0) @@ -2669,7 +2669,7 @@ _tkinter_tkapp_deletefilehandler(TkappObject *self, PyObject *file) { int tfile; - CHECK_TCL_APPARTMENT; + CHECK_TCL_APPARTMENT(self); tfile = PyObject_AsFileDescriptor(file); if (tfile < 0) @@ -2700,8 +2700,7 @@ typedef struct { PyObject *func; } TkttObject; -#define TkttObject_CAST(op) \ - (assert(TkttObject_Check(op)), ((TkttObject *)(op))) +#define TkttObject_CAST(op) (assert(TkttObject_Check(op)), (TkttObject *)(op)) /*[clinic input] _tkinter.tktimertoken.deletetimerhandler @@ -2817,7 +2816,7 @@ _tkinter_tkapp_createtimerhandler_impl(TkappObject *self, int milliseconds, return NULL; } - CHECK_TCL_APPARTMENT; + CHECK_TCL_APPARTMENT(self); TRACE(self, ("((siO))", "after", milliseconds, func)); @@ -2847,7 +2846,7 @@ _tkinter_tkapp_mainloop_impl(TkappObject *self, int threshold) { PyThreadState *tstate = PyThreadState_Get(); - CHECK_TCL_APPARTMENT; + CHECK_TCL_APPARTMENT(self); self->dispatching = 1; quitMainLoop = 0; @@ -2950,7 +2949,7 @@ _tkinter_tkapp_loadtk_impl(TkappObject *self) int err; /* We want to guard against calling Tk_Init() multiple times */ - CHECK_TCL_APPARTMENT; + CHECK_TCL_APPARTMENT(self); ENTER_TCL err = Tcl_Eval(Tkapp_Interp(self), "info exists tk_version"); ENTER_OVERLAP From 5f2287cd409e5cdddf5c009821af3648c25a3233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 25 Feb 2025 13:35:43 +0100 Subject: [PATCH 8/8] implement CHECK_TCL_APPARTMENT as a static inline function --- Modules/_tkinter.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index ac2a1cff54d6ff..f608fca8ccc297 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -289,16 +289,6 @@ static PyThreadState *tcl_tstate = NULL; if(tcl_lock)PyThread_acquire_lock(tcl_lock, 1); \ tcl_tstate = tstate; } -#define CHECK_TCL_APPARTMENT(op) \ - do { \ - TkappObject *app = TkappObject_CAST(op); \ - if (app->threaded && app->thread_id != Tcl_GetCurrentThread()) { \ - PyErr_SetString(PyExc_RuntimeError, \ - "Calling Tcl from different apartment"); \ - return 0; \ - } \ - } while (0) - #ifndef FREECAST #define FREECAST (char *) #endif @@ -333,6 +323,24 @@ typedef struct { #define TkappObject_CAST(op) ((TkappObject *)(op)) #define Tkapp_Interp(v) (TkappObject_CAST(v)->interp) +static inline int +check_tcl_appartment(TkappObject *app) +{ + if (app->threaded && app->thread_id != Tcl_GetCurrentThread()) { + PyErr_SetString(PyExc_RuntimeError, + "Calling Tcl from different apartment"); + return -1; + } + return 0; +} + +#define CHECK_TCL_APPARTMENT(APP) \ + do { \ + if (check_tcl_appartment(APP) < 0) { \ + return 0; \ + } \ + } while (0) + /**** Error Handling ****/