gh-134087: enforce signature of threading.RLock (#134178)

- Reject positional and keyword arguments in `_thread.RLock.__new__`.
- Convert `_thread.lock.__new__` to AC.
This commit is contained in:
Bénédikt Tran 2025-05-19 11:26:14 +02:00 committed by GitHub
parent 9983c7d441
commit d6dc33ed80
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 115 additions and 53 deletions

View file

@ -19,8 +19,6 @@
# include <signal.h> // SIGINT
#endif
#include "clinic/_threadmodule.c.h"
// ThreadError is just an alias to PyExc_RuntimeError
#define ThreadError PyExc_RuntimeError
@ -31,6 +29,7 @@ static struct PyModuleDef thread_module;
typedef struct {
PyTypeObject *excepthook_type;
PyTypeObject *lock_type;
PyTypeObject *rlock_type;
PyTypeObject *local_type;
PyTypeObject *local_dummy_type;
PyTypeObject *thread_handle_type;
@ -48,6 +47,17 @@ get_thread_state(PyObject *module)
return (thread_module_state *)state;
}
static inline thread_module_state*
get_thread_state_by_cls(PyTypeObject *cls)
{
// Use PyType_GetModuleByDef() to handle (R)Lock subclasses.
PyObject *module = PyType_GetModuleByDef(cls, &thread_module);
if (module == NULL) {
return NULL;
}
return get_thread_state(module);
}
#ifdef MS_WINDOWS
typedef HRESULT (WINAPI *PF_GET_THREAD_DESCRIPTION)(HANDLE, PCWSTR*);
@ -59,9 +69,14 @@ static PF_SET_THREAD_DESCRIPTION pSetThreadDescription = NULL;
/*[clinic input]
module _thread
class _thread.lock "lockobject *" "clinic_state()->lock_type"
class _thread.RLock "rlockobject *" "clinic_state()->rlock_type"
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=be8dbe5cc4b16df7]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=c5a0f8c492a0c263]*/
#define clinic_state() get_thread_state_by_cls(type)
#include "clinic/_threadmodule.c.h"
#undef clinic_state
// _ThreadHandle type
@ -916,25 +931,21 @@ lock__at_fork_reinit(PyObject *op, PyObject *Py_UNUSED(dummy))
}
#endif /* HAVE_FORK */
static lockobject *newlockobject(PyObject *module);
/*[clinic input]
@classmethod
_thread.lock.__new__ as lock_new
[clinic start generated code]*/
static PyObject *
lock_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
lock_new_impl(PyTypeObject *type)
/*[clinic end generated code: output=eab660d5a4c05c8a input=260208a4e277d250]*/
{
// convert to AC?
if (!_PyArg_NoKeywords("lock", kwargs)) {
goto error;
lockobject *self = (lockobject *)type->tp_alloc(type, 0);
if (self == NULL) {
return NULL;
}
if (!_PyArg_CheckPositional("lock", PyTuple_GET_SIZE(args), 0, 0)) {
goto error;
}
PyObject *module = PyType_GetModuleByDef(type, &thread_module);
assert(module != NULL);
return (PyObject *)newlockobject(module);
error:
return NULL;
self->lock = (PyMutex){0};
return (PyObject *)self;
}
@ -1186,8 +1197,14 @@ PyDoc_STRVAR(rlock_is_owned_doc,
\n\
For internal use by `threading.Condition`.");
/*[clinic input]
@classmethod
_thread.RLock.__new__ as rlock_new
[clinic start generated code]*/
static PyObject *
rlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
rlock_new_impl(PyTypeObject *type)
/*[clinic end generated code: output=bb4fb1edf6818df5 input=013591361bf1ac6e]*/
{
rlockobject *self = (rlockobject *) type->tp_alloc(type, 0);
if (self == NULL) {
@ -1267,20 +1284,6 @@ static PyType_Spec rlock_type_spec = {
.slots = rlock_type_slots,
};
static lockobject *
newlockobject(PyObject *module)
{
thread_module_state *state = get_thread_state(module);
PyTypeObject *type = state->lock_type;
lockobject *self = (lockobject *)type->tp_alloc(type, 0);
if (self == NULL) {
return NULL;
}
self->lock = (PyMutex){0};
return self;
}
/* Thread-local objects */
/* Quick overview:
@ -2035,7 +2038,8 @@ Note: the default signal handler for SIGINT raises ``KeyboardInterrupt``."
static PyObject *
thread_PyThread_allocate_lock(PyObject *module, PyObject *Py_UNUSED(ignored))
{
return (PyObject *) newlockobject(module);
thread_module_state *state = get_thread_state(module);
return lock_new_impl(state->lock_type);
}
PyDoc_STRVAR(allocate_lock_doc,
@ -2645,15 +2649,13 @@ thread_module_exec(PyObject *module)
}
// RLock
PyTypeObject *rlock_type = (PyTypeObject *)PyType_FromSpec(&rlock_type_spec);
if (rlock_type == NULL) {
state->rlock_type = (PyTypeObject *)PyType_FromModuleAndSpec(module, &rlock_type_spec, NULL);
if (state->rlock_type == NULL) {
return -1;
}
if (PyModule_AddType(module, rlock_type) < 0) {
Py_DECREF(rlock_type);
if (PyModule_AddType(module, state->rlock_type) < 0) {
return -1;
}
Py_DECREF(rlock_type);
// Local dummy
state->local_dummy_type = (PyTypeObject *)PyType_FromSpec(&local_dummy_type_spec);
@ -2740,6 +2742,7 @@ thread_module_traverse(PyObject *module, visitproc visit, void *arg)
thread_module_state *state = get_thread_state(module);
Py_VISIT(state->excepthook_type);
Py_VISIT(state->lock_type);
Py_VISIT(state->rlock_type);
Py_VISIT(state->local_type);
Py_VISIT(state->local_dummy_type);
Py_VISIT(state->thread_handle_type);
@ -2752,6 +2755,7 @@ thread_module_clear(PyObject *module)
thread_module_state *state = get_thread_state(module);
Py_CLEAR(state->excepthook_type);
Py_CLEAR(state->lock_type);
Py_CLEAR(state->rlock_type);
Py_CLEAR(state->local_type);
Py_CLEAR(state->local_dummy_type);
Py_CLEAR(state->thread_handle_type);