https://github.com/python/cpython/commit/f8eefc2f35c002b6702990add7f33d445977de8d
commit: f8eefc2f35c002b6702990add7f33d445977de8d
branch: main
author: Bénédikt Tran <10796600+picn...@users.noreply.github.com>
committer: encukou <encu...@gmail.com>
date: 2025-02-24T13:42:39+01:00
summary:

gh-111178: fix UBSan failures in `Modules/_threadmodule.c` (GH-129794)

Fix UBSan failures for `PyThreadHandleObject`, `lockobject`, `rlockobject`, 
`localdummyobject`, `localobject`

Add safe casts

Clean up module functions

Use semantically correct parameter names

files:
M Modules/_threadmodule.c

diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c
index e251736fb36aa9..eb06d711057ea0 100644
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -582,6 +582,8 @@ typedef struct {
     ThreadHandle *handle;
 } PyThreadHandleObject;
 
+#define PyThreadHandleObject_CAST(op)   ((PyThreadHandleObject *)(op))
+
 static PyThreadHandleObject *
 PyThreadHandleObject_new(PyTypeObject *type)
 {
@@ -609,8 +611,7 @@ PyThreadHandleObject_tp_new(PyTypeObject *type, PyObject 
*args, PyObject *kwds)
 }
 
 static int
-PyThreadHandleObject_traverse(PyThreadHandleObject *self, visitproc visit,
-                              void *arg)
+PyThreadHandleObject_traverse(PyObject *self, visitproc visit, void *arg)
 {
     Py_VISIT(Py_TYPE(self));
     return 0;
@@ -619,7 +620,7 @@ PyThreadHandleObject_traverse(PyThreadHandleObject *self, 
visitproc visit,
 static void
 PyThreadHandleObject_dealloc(PyObject *op)
 {
-    PyThreadHandleObject *self = (PyThreadHandleObject*)op;
+    PyThreadHandleObject *self = PyThreadHandleObject_CAST(op);
     PyObject_GC_UnTrack(self);
     PyTypeObject *tp = Py_TYPE(self);
     ThreadHandle_decref(self->handle);
@@ -630,23 +631,23 @@ PyThreadHandleObject_dealloc(PyObject *op)
 static PyObject *
 PyThreadHandleObject_repr(PyObject *op)
 {
-    PyThreadHandleObject *self = (PyThreadHandleObject*)op;
+    PyThreadHandleObject *self = PyThreadHandleObject_CAST(op);
     PyThread_ident_t ident = ThreadHandle_ident(self->handle);
     return PyUnicode_FromFormat("<%s object: ident=%" PY_FORMAT_THREAD_IDENT_T 
">",
                                 Py_TYPE(self)->tp_name, ident);
 }
 
 static PyObject *
-PyThreadHandleObject_get_ident(PyObject *op, void *Py_UNUSED(ignored))
+PyThreadHandleObject_get_ident(PyObject *op, void *Py_UNUSED(closure))
 {
-    PyThreadHandleObject *self = (PyThreadHandleObject*)op;
+    PyThreadHandleObject *self = PyThreadHandleObject_CAST(op);
     return PyLong_FromUnsignedLongLong(ThreadHandle_ident(self->handle));
 }
 
 static PyObject *
 PyThreadHandleObject_join(PyObject *op, PyObject *args)
 {
-    PyThreadHandleObject *self = (PyThreadHandleObject*)op;
+    PyThreadHandleObject *self = PyThreadHandleObject_CAST(op);
 
     PyObject *timeout_obj = NULL;
     if (!PyArg_ParseTuple(args, "|O:join", &timeout_obj)) {
@@ -668,9 +669,9 @@ PyThreadHandleObject_join(PyObject *op, PyObject *args)
 }
 
 static PyObject *
-PyThreadHandleObject_is_done(PyObject *op, PyObject *Py_UNUSED(ignored))
+PyThreadHandleObject_is_done(PyObject *op, PyObject *Py_UNUSED(dummy))
 {
-    PyThreadHandleObject *self = (PyThreadHandleObject*)op;
+    PyThreadHandleObject *self = PyThreadHandleObject_CAST(op);
     if (_PyEvent_IsSet(&self->handle->thread_is_exiting)) {
         Py_RETURN_TRUE;
     }
@@ -680,9 +681,9 @@ PyThreadHandleObject_is_done(PyObject *op, PyObject 
*Py_UNUSED(ignored))
 }
 
 static PyObject *
-PyThreadHandleObject_set_done(PyObject *op, PyObject *Py_UNUSED(ignored))
+PyThreadHandleObject_set_done(PyObject *op, PyObject *Py_UNUSED(dummy))
 {
-    PyThreadHandleObject *self = (PyThreadHandleObject*)op;
+    PyThreadHandleObject *self = PyThreadHandleObject_CAST(op);
     if (ThreadHandle_set_done(self->handle) < 0) {
         return NULL;
     }
@@ -726,6 +727,8 @@ typedef struct {
     PyMutex lock;
 } lockobject;
 
+#define lockobject_CAST(op) ((lockobject *)(op))
+
 static int
 lock_traverse(PyObject *self, visitproc visit, void *arg)
 {
@@ -734,13 +737,12 @@ lock_traverse(PyObject *self, visitproc visit, void *arg)
 }
 
 static void
-lock_dealloc(PyObject *op)
+lock_dealloc(PyObject *self)
 {
-    lockobject *self = (lockobject*)op;
     PyObject_GC_UnTrack(self);
-    PyObject_ClearWeakRefs((PyObject *) self);
+    PyObject_ClearWeakRefs(self);
     PyTypeObject *tp = Py_TYPE(self);
-    tp->tp_free((PyObject*)self);
+    tp->tp_free(self);
     Py_DECREF(tp);
 }
 
@@ -794,7 +796,7 @@ lock_acquire_parse_args(PyObject *args, PyObject *kwds,
 static PyObject *
 lock_PyThread_acquire_lock(PyObject *op, PyObject *args, PyObject *kwds)
 {
-    lockobject *self = (lockobject*)op;
+    lockobject *self = lockobject_CAST(op);
 
     PyTime_t timeout;
     if (lock_acquire_parse_args(args, kwds, &timeout) < 0) {
@@ -834,9 +836,9 @@ PyDoc_STRVAR(enter_doc,
 Lock the lock.");
 
 static PyObject *
-lock_PyThread_release_lock(PyObject *op, PyObject *Py_UNUSED(ignored))
+lock_PyThread_release_lock(PyObject *op, PyObject *Py_UNUSED(dummy))
 {
-    lockobject *self = (lockobject*)op;
+    lockobject *self = lockobject_CAST(op);
     /* Sanity check: the lock must be locked */
     if (_PyMutex_TryUnlock(&self->lock) < 0) {
         PyErr_SetString(ThreadError, "release unlocked lock");
@@ -867,9 +869,9 @@ PyDoc_STRVAR(lock_exit_doc,
 Release the lock.");
 
 static PyObject *
-lock_locked_lock(PyObject *op, PyObject *Py_UNUSED(ignored))
+lock_locked_lock(PyObject *op, PyObject *Py_UNUSED(dummy))
 {
-    lockobject *self = (lockobject*)op;
+    lockobject *self = lockobject_CAST(op);
     return PyBool_FromLong(PyMutex_IsLocked(&self->lock));
 }
 
@@ -888,16 +890,16 @@ An obsolete synonym of locked().");
 static PyObject *
 lock_repr(PyObject *op)
 {
-    lockobject *self = (lockobject*)op;
+    lockobject *self = lockobject_CAST(op);
     return PyUnicode_FromFormat("<%s %s object at %p>",
         PyMutex_IsLocked(&self->lock) ? "locked" : "unlocked", 
Py_TYPE(self)->tp_name, self);
 }
 
 #ifdef HAVE_FORK
 static PyObject *
-lock__at_fork_reinit(PyObject *op, PyObject *Py_UNUSED(args))
+lock__at_fork_reinit(PyObject *op, PyObject *Py_UNUSED(dummy))
 {
-    lockobject *self = (lockobject *)op;
+    lockobject *self = lockobject_CAST(op);
     _PyMutex_at_fork_reinit(&self->lock);
     Py_RETURN_NONE;
 }
@@ -989,8 +991,10 @@ typedef struct {
     _PyRecursiveMutex lock;
 } rlockobject;
 
+#define rlockobject_CAST(op)    ((rlockobject *)(op))
+
 static int
-rlock_traverse(rlockobject *self, visitproc visit, void *arg)
+rlock_traverse(PyObject *self, visitproc visit, void *arg)
 {
     Py_VISIT(Py_TYPE(self));
     return 0;
@@ -998,11 +1002,10 @@ rlock_traverse(rlockobject *self, visitproc visit, void 
*arg)
 
 
 static void
-rlock_dealloc(PyObject *op)
+rlock_dealloc(PyObject *self)
 {
-    rlockobject *self = (rlockobject*)op;
     PyObject_GC_UnTrack(self);
-    PyObject_ClearWeakRefs((PyObject *) self);
+    PyObject_ClearWeakRefs(self);
     PyTypeObject *tp = Py_TYPE(self);
     tp->tp_free(self);
     Py_DECREF(tp);
@@ -1012,7 +1015,7 @@ rlock_dealloc(PyObject *op)
 static PyObject *
 rlock_acquire(PyObject *op, PyObject *args, PyObject *kwds)
 {
-    rlockobject *self = (rlockobject*)op;
+    rlockobject *self = rlockobject_CAST(op);
     PyTime_t timeout;
 
     if (lock_acquire_parse_args(args, kwds, &timeout) < 0) {
@@ -1052,10 +1055,9 @@ PyDoc_STRVAR(rlock_enter_doc,
 Lock the lock.");
 
 static PyObject *
-rlock_release(PyObject *op, PyObject *Py_UNUSED(ignored))
+rlock_release(PyObject *op, PyObject *Py_UNUSED(dummy))
 {
-    rlockobject *self = (rlockobject*)op;
-
+    rlockobject *self = rlockobject_CAST(op);
     if (_PyRecursiveMutex_TryUnlock(&self->lock) < 0) {
         PyErr_SetString(PyExc_RuntimeError,
                         "cannot release un-acquired lock");
@@ -1086,7 +1088,7 @@ Release the lock.");
 static PyObject *
 rlock_acquire_restore(PyObject *op, PyObject *args)
 {
-    rlockobject *self = (rlockobject*)op;
+    rlockobject *self = rlockobject_CAST(op);
     PyThread_ident_t owner;
     Py_ssize_t count;
 
@@ -1107,9 +1109,9 @@ PyDoc_STRVAR(rlock_acquire_restore_doc,
 For internal use by `threading.Condition`.");
 
 static PyObject *
-rlock_release_save(PyObject *op, PyObject *Py_UNUSED(ignored))
+rlock_release_save(PyObject *op, PyObject *Py_UNUSED(dummy))
 {
-    rlockobject *self = (rlockobject*)op;
+    rlockobject *self = rlockobject_CAST(op);
 
     if (!_PyRecursiveMutex_IsLockedByCurrentThread(&self->lock)) {
         PyErr_SetString(PyExc_RuntimeError,
@@ -1131,9 +1133,9 @@ PyDoc_STRVAR(rlock_release_save_doc,
 For internal use by `threading.Condition`.");
 
 static PyObject *
-rlock_recursion_count(PyObject *op, PyObject *Py_UNUSED(ignored))
+rlock_recursion_count(PyObject *op, PyObject *Py_UNUSED(dummy))
 {
-    rlockobject *self = (rlockobject*)op;
+    rlockobject *self = rlockobject_CAST(op);
     if (_PyRecursiveMutex_IsLockedByCurrentThread(&self->lock)) {
         return PyLong_FromSize_t(self->lock.level + 1);
     }
@@ -1147,9 +1149,9 @@ PyDoc_STRVAR(rlock_recursion_count_doc,
 For internal use by reentrancy checks.");
 
 static PyObject *
-rlock_is_owned(PyObject *op, PyObject *Py_UNUSED(ignored))
+rlock_is_owned(PyObject *op, PyObject *Py_UNUSED(dummy))
 {
-    rlockobject *self = (rlockobject*)op;
+    rlockobject *self = rlockobject_CAST(op);
     long owned = _PyRecursiveMutex_IsLockedByCurrentThread(&self->lock);
     return PyBool_FromLong(owned);
 }
@@ -1174,7 +1176,7 @@ rlock_new(PyTypeObject *type, PyObject *args, PyObject 
*kwds)
 static PyObject *
 rlock_repr(PyObject *op)
 {
-    rlockobject *self = (rlockobject*)op;
+    rlockobject *self = rlockobject_CAST(op);
     PyThread_ident_t owner = self->lock.thread;
     size_t count = self->lock.level + 1;
     return PyUnicode_FromFormat(
@@ -1187,8 +1189,9 @@ rlock_repr(PyObject *op)
 
 #ifdef HAVE_FORK
 static PyObject *
-rlock__at_fork_reinit(rlockobject *self, PyObject *Py_UNUSED(args))
+rlock__at_fork_reinit(PyObject *op, PyObject *Py_UNUSED(dummy))
 {
+    rlockobject *self = rlockobject_CAST(op);
     self->lock = (_PyRecursiveMutex){0};
     Py_RETURN_NONE;
 }
@@ -1213,7 +1216,7 @@ static PyMethodDef rlock_methods[] = {
     {"__exit__",    rlock_release,
      METH_VARARGS, rlock_exit_doc},
 #ifdef HAVE_FORK
-    {"_at_fork_reinit",    (PyCFunction)rlock__at_fork_reinit,
+    {"_at_fork_reinit", rlock__at_fork_reinit,
      METH_NOARGS, NULL},
 #endif
     {NULL,           NULL}              /* sentinel */
@@ -1310,14 +1313,17 @@ typedef struct {
     PyObject *weakreflist;      /* List of weak references to self */
 } localdummyobject;
 
+#define localdummyobject_CAST(op)   ((localdummyobject *)(op))
+
 static void
 localdummy_dealloc(PyObject *op)
 {
-    localdummyobject *self = (localdummyobject*)op;
-    if (self->weakreflist != NULL)
-        PyObject_ClearWeakRefs((PyObject *) self);
+    localdummyobject *self = localdummyobject_CAST(op);
+    if (self->weakreflist != NULL) {
+        PyObject_ClearWeakRefs(op);
+    }
     PyTypeObject *tp = Py_TYPE(self);
-    tp->tp_free((PyObject*)self);
+    tp->tp_free(self);
     Py_DECREF(tp);
 }
 
@@ -1353,6 +1359,8 @@ typedef struct {
     PyObject *thread_watchdogs;
 } localobject;
 
+#define localobject_CAST(op)    ((localobject *)(op))
+
 /* Forward declaration */
 static int create_localsdict(localobject *self, thread_module_state *state,
                              PyObject **localsdict, PyObject **sentinel_wr);
@@ -1363,7 +1371,7 @@ static PyObject *
 create_sentinel_wr(localobject *self)
 {
     static PyMethodDef wr_callback_def = {
-        "clear_locals", (PyCFunction) clear_locals, METH_O
+        "clear_locals", clear_locals, METH_O
     };
 
     PyThreadState *tstate = PyThreadState_Get();
@@ -1455,8 +1463,9 @@ local_new(PyTypeObject *type, PyObject *args, PyObject 
*kw)
 }
 
 static int
-local_traverse(localobject *self, visitproc visit, void *arg)
+local_traverse(PyObject *op, visitproc visit, void *arg)
 {
+    localobject *self = localobject_CAST(op);
     Py_VISIT(Py_TYPE(self));
     Py_VISIT(self->args);
     Py_VISIT(self->kw);
@@ -1466,8 +1475,9 @@ local_traverse(localobject *self, visitproc visit, void 
*arg)
 }
 
 static int
-local_clear(localobject *self)
+local_clear(PyObject *op)
 {
+    localobject *self = localobject_CAST(op);
     Py_CLEAR(self->args);
     Py_CLEAR(self->kw);
     Py_CLEAR(self->localdicts);
@@ -1476,20 +1486,18 @@ local_clear(localobject *self)
 }
 
 static void
-local_dealloc(localobject *self)
+local_dealloc(PyObject *op)
 {
+    localobject *self = localobject_CAST(op);
     /* Weakrefs must be invalidated right now, otherwise they can be used
        from code called below, which is very dangerous since Py_REFCNT(self) 
== 0 */
     if (self->weakreflist != NULL) {
-        PyObject_ClearWeakRefs((PyObject *) self);
+        PyObject_ClearWeakRefs(op);
     }
-
     PyObject_GC_UnTrack(self);
-
-    local_clear(self);
-
+    (void)local_clear(op);
     PyTypeObject *tp = Py_TYPE(self);
-    tp->tp_free((PyObject*)self);
+    tp->tp_free(self);
     Py_DECREF(tp);
 }
 
@@ -1636,8 +1644,9 @@ _ldict(localobject *self, thread_module_state *state)
 }
 
 static int
-local_setattro(localobject *self, PyObject *name, PyObject *v)
+local_setattro(PyObject *op, PyObject *name, PyObject *v)
 {
+    localobject *self = localobject_CAST(op);
     PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &thread_module);
     assert(module != NULL);
     thread_module_state *state = get_thread_state(module);
@@ -1658,8 +1667,7 @@ local_setattro(localobject *self, PyObject *name, 
PyObject *v)
         goto err;
     }
 
-    int st =
-        _PyObject_GenericSetAttrWithDict((PyObject *)self, name, v, ldict);
+    int st = _PyObject_GenericSetAttrWithDict(op, name, v, ldict);
     Py_DECREF(ldict);
     return st;
 
@@ -1668,7 +1676,7 @@ local_setattro(localobject *self, PyObject *name, 
PyObject *v)
     return -1;
 }
 
-static PyObject *local_getattro(localobject *, PyObject *);
+static PyObject *local_getattro(PyObject *, PyObject *);
 
 static PyMemberDef local_type_members[] = {
     {"__weaklistoffset__", Py_T_PYSSIZET, offsetof(localobject, weakreflist), 
Py_READONLY},
@@ -1676,12 +1684,12 @@ static PyMemberDef local_type_members[] = {
 };
 
 static PyType_Slot local_type_slots[] = {
-    {Py_tp_dealloc, (destructor)local_dealloc},
-    {Py_tp_getattro, (getattrofunc)local_getattro},
-    {Py_tp_setattro, (setattrofunc)local_setattro},
+    {Py_tp_dealloc, local_dealloc},
+    {Py_tp_getattro, local_getattro},
+    {Py_tp_setattro, local_setattro},
     {Py_tp_doc, "_local()\n--\n\nThread-local data"},
-    {Py_tp_traverse, (traverseproc)local_traverse},
-    {Py_tp_clear, (inquiry)local_clear},
+    {Py_tp_traverse, local_traverse},
+    {Py_tp_clear, local_clear},
     {Py_tp_new, local_new},
     {Py_tp_members, local_type_members},
     {0, 0}
@@ -1696,8 +1704,9 @@ static PyType_Spec local_type_spec = {
 };
 
 static PyObject *
-local_getattro(localobject *self, PyObject *name)
+local_getattro(PyObject *op, PyObject *name)
 {
+    localobject *self = localobject_CAST(op);
     PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &thread_module);
     assert(module != NULL);
     thread_module_state *state = get_thread_state(module);
@@ -1717,8 +1726,7 @@ local_getattro(localobject *self, PyObject *name)
 
     if (!Py_IS_TYPE(self, state->local_type)) {
         /* use generic lookup for subtypes */
-        PyObject *res =
-            _PyObject_GenericGetAttrWithDict((PyObject *)self, name, ldict, 0);
+        PyObject *res = _PyObject_GenericGetAttrWithDict(op, name, ldict, 0);
         Py_DECREF(ldict);
         return res;
     }
@@ -1732,8 +1740,7 @@ local_getattro(localobject *self, PyObject *name)
     }
 
     /* Fall back on generic to get __class__ and __dict__ */
-    PyObject *res =
-        _PyObject_GenericGetAttrWithDict((PyObject *)self, name, ldict, 0);
+    PyObject *res = _PyObject_GenericGetAttrWithDict(op, name, ldict, 0);
     Py_DECREF(ldict);
     return res;
 }
@@ -1744,7 +1751,7 @@ static PyObject *
 clear_locals(PyObject *locals_and_key, PyObject *dummyweakref)
 {
     PyObject *localweakref = PyTuple_GetItem(locals_and_key, 0);
-    localobject *self = (localobject *)_PyWeakref_GET_REF(localweakref);
+    localobject *self = localobject_CAST(_PyWeakref_GET_REF(localweakref));
     if (self == NULL) {
         Py_RETURN_NONE;
     }
@@ -2525,13 +2532,13 @@ _thread_set_name_impl(PyObject *module, PyObject 
*name_obj)
 
 
 static PyMethodDef thread_methods[] = {
-    {"start_new_thread",        (PyCFunction)thread_PyThread_start_new_thread,
+    {"start_new_thread",        thread_PyThread_start_new_thread,
      METH_VARARGS, start_new_thread_doc},
-    {"start_new",               (PyCFunction)thread_PyThread_start_new_thread,
+    {"start_new",               thread_PyThread_start_new_thread,
      METH_VARARGS, start_new_doc},
     {"start_joinable_thread",   
_PyCFunction_CAST(thread_PyThread_start_joinable_thread),
      METH_VARARGS | METH_KEYWORDS, start_joinable_doc},
-    {"daemon_threads_allowed",  (PyCFunction)thread_daemon_threads_allowed,
+    {"daemon_threads_allowed",  thread_daemon_threads_allowed,
      METH_NOARGS, daemon_threads_allowed_doc},
     {"allocate_lock",           thread_PyThread_allocate_lock,
      METH_NOARGS, allocate_lock_doc},
@@ -2541,7 +2548,7 @@ static PyMethodDef thread_methods[] = {
      METH_NOARGS, exit_thread_doc},
     {"exit",                    thread_PyThread_exit_thread,
      METH_NOARGS, exit_doc},
-    {"interrupt_main",          (PyCFunction)thread_PyThread_interrupt_main,
+    {"interrupt_main",          thread_PyThread_interrupt_main,
      METH_VARARGS, interrupt_doc},
     {"get_ident",               thread_get_ident,
      METH_NOARGS, get_ident_doc},
@@ -2551,7 +2558,7 @@ static PyMethodDef thread_methods[] = {
 #endif
     {"_count",                  thread__count,
      METH_NOARGS, _count_doc},
-    {"stack_size",              (PyCFunction)thread_stack_size,
+    {"stack_size",              thread_stack_size,
      METH_VARARGS, stack_size_doc},
     {"_excepthook",             thread_excepthook,
      METH_O, excepthook_doc},
@@ -2723,7 +2730,7 @@ thread_module_clear(PyObject *module)
 static void
 thread_module_free(void *module)
 {
-    thread_module_clear((PyObject *)module);
+    (void)thread_module_clear((PyObject *)module);
 }
 
 

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to