https://github.com/python/cpython/commit/d47584aae6fab1b767e2d2ea6611b9c0c3ff36e2 commit: d47584aae6fab1b767e2d2ea6611b9c0c3ff36e2 branch: main author: Kumar Aditya <kumaradi...@python.org> committer: kumaraditya303 <kumaradi...@python.org> date: 2025-04-09T18:18:40Z summary:
gh-131336: fix thread safety for ctypes functions (#132232) files: M Modules/_ctypes/_ctypes.c M Modules/_ctypes/callproc.c M Modules/_ctypes/clinic/callproc.c.h diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 55e5eee0eb081a..8e4794866e8b20 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -2843,16 +2843,24 @@ PyCData_GetContainer(CDataObject *self) while (self->b_base) { self = self->b_base; } + CDataObject *res = self; + Py_BEGIN_CRITICAL_SECTION(self); + // avoid using return directly in this block because critical section + // needs to be released before returning if (self->b_objects == NULL) { if (self->b_length) { self->b_objects = PyDict_New(); - if (self->b_objects == NULL) - return NULL; + if (self->b_objects == NULL) { + res = NULL; + goto exit; + } } else { self->b_objects = Py_NewRef(Py_None); } } - return self; +exit:; + Py_END_CRITICAL_SECTION(); + return res; } static PyObject * diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 158422fb9b0471..352487ed964b54 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -106,8 +106,9 @@ module _ctypes #if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX) #include "../_complex.h" // complex #endif - +#define clinic_state() (get_module_state(module)) #include "clinic/callproc.c.h" +#undef clinic_state #define CTYPES_CAPSULE_NAME_PYMEM "_ctypes pymem" @@ -1731,15 +1732,20 @@ call_cdeclfunction(PyObject *self, PyObject *args) /***************************************************************** * functions */ -PyDoc_STRVAR(sizeof_doc, -"sizeof(C type) -> integer\n" -"sizeof(C instance) -> integer\n" -"Return the size in bytes of a C instance"); + +/*[clinic input] +_ctypes.sizeof + obj: object + / +Return the size in bytes of a C instance. + +[clinic start generated code]*/ static PyObject * -sizeof_func(PyObject *self, PyObject *obj) +_ctypes_sizeof(PyObject *module, PyObject *obj) +/*[clinic end generated code: output=ed38a3f364d7bd3e input=321fd0f65cb2d623]*/ { - ctypes_state *st = get_module_state(self); + ctypes_state *st = get_module_state(module); StgInfo *info; if (PyStgInfo_FromType(st, obj, &info) < 0) { @@ -1750,7 +1756,11 @@ sizeof_func(PyObject *self, PyObject *obj) } if (CDataObject_Check(st, obj)) { - return PyLong_FromSsize_t(((CDataObject *)obj)->b_size); + PyObject *ret = NULL; + Py_BEGIN_CRITICAL_SECTION(obj); + ret = PyLong_FromSsize_t(((CDataObject *)obj)->b_size); + Py_END_CRITICAL_SECTION(); + return ret; } PyErr_SetString(PyExc_TypeError, "this type has no size"); @@ -1778,40 +1788,24 @@ align_func(PyObject *self, PyObject *obj) return NULL; } -PyDoc_STRVAR(byref_doc, -"byref(C instance[, offset=0]) -> byref-object\n" -"Return a pointer lookalike to a C instance, only usable\n" -"as function argument"); -/* - * We must return something which can be converted to a parameter, - * but still has a reference to self. - */ +/*[clinic input] +@critical_section obj +_ctypes.byref + obj: object(subclass_of="clinic_state()->PyCData_Type") + offset: Py_ssize_t = 0 + / +Return a pointer lookalike to a C instance, only usable as function argument. + +[clinic start generated code]*/ + static PyObject * -byref(PyObject *self, PyObject *args) +_ctypes_byref_impl(PyObject *module, PyObject *obj, Py_ssize_t offset) +/*[clinic end generated code: output=60dec5ed520c71de input=6ec02d95d15fbd56]*/ { - PyCArgObject *parg; - PyObject *obj; - PyObject *pyoffset = NULL; - Py_ssize_t offset = 0; - - if (!PyArg_UnpackTuple(args, "byref", 1, 2, - &obj, &pyoffset)) - return NULL; - if (pyoffset) { - offset = PyNumber_AsSsize_t(pyoffset, NULL); - if (offset == -1 && PyErr_Occurred()) - return NULL; - } - ctypes_state *st = get_module_state(self); - if (!CDataObject_Check(st, obj)) { - PyErr_Format(PyExc_TypeError, - "byref() argument must be a ctypes instance, not '%s'", - Py_TYPE(obj)->tp_name); - return NULL; - } + ctypes_state *st = get_module_state(module); - parg = PyCArgObject_new(st); + PyCArgObject *parg = PyCArgObject_new(st); if (parg == NULL) return NULL; @@ -1822,19 +1816,19 @@ byref(PyObject *self, PyObject *args) return (PyObject *)parg; } -PyDoc_STRVAR(addressof_doc, -"addressof(C instance) -> integer\n" -"Return the address of the C instance internal buffer"); +/*[clinic input] +@critical_section obj +_ctypes.addressof + obj: object(subclass_of="clinic_state()->PyCData_Type") + / +Return the address of the C instance internal buffer + +[clinic start generated code]*/ static PyObject * -addressof(PyObject *self, PyObject *obj) +_ctypes_addressof_impl(PyObject *module, PyObject *obj) +/*[clinic end generated code: output=30d8e80c4bab70c7 input=d83937d105d3a442]*/ { - ctypes_state *st = get_module_state(self); - if (!CDataObject_Check(st, obj)) { - PyErr_SetString(PyExc_TypeError, - "invalid type"); - return NULL; - } if (PySys_Audit("ctypes.addressof", "(O)", obj) < 0) { return NULL; } @@ -1878,18 +1872,20 @@ My_Py_DECREF(PyObject *self, PyObject *arg) return arg; } -static PyObject * -resize(PyObject *self, PyObject *args) -{ - CDataObject *obj; - Py_ssize_t size; +/*[clinic input] +@critical_section obj +_ctypes.resize + obj: object(subclass_of="clinic_state()->PyCData_Type", type="CDataObject *") + size: Py_ssize_t + / - if (!PyArg_ParseTuple(args, - "On:resize", - &obj, &size)) - return NULL; +[clinic start generated code]*/ - ctypes_state *st = get_module_state(self); +static PyObject * +_ctypes_resize_impl(PyObject *module, CDataObject *obj, Py_ssize_t size) +/*[clinic end generated code: output=11c89c7dbdbcd53f input=bf5a6aaea8514261]*/ +{ + ctypes_state *st = get_module_state(module); StgInfo *info; int result = PyStgInfo_FromObject(st, (PyObject *)obj, &info); if (result < 0) { @@ -2103,7 +2099,7 @@ PyMethodDef _ctypes_module_methods[] = { CREATE_POINTER_INST_METHODDEF {"_unpickle", unpickle, METH_VARARGS }, {"buffer_info", buffer_info, METH_O, "Return buffer interface information"}, - {"resize", resize, METH_VARARGS, "Resize the memory buffer of a ctypes instance"}, + _CTYPES_RESIZE_METHODDEF #ifdef MS_WIN32 {"get_last_error", get_last_error, METH_NOARGS}, {"set_last_error", set_last_error, METH_VARARGS}, @@ -2122,9 +2118,9 @@ PyMethodDef _ctypes_module_methods[] = { {"_dyld_shared_cache_contains_path", py_dyld_shared_cache_contains_path, METH_VARARGS, "check if path is in the shared cache"}, #endif {"alignment", align_func, METH_O, alignment_doc}, - {"sizeof", sizeof_func, METH_O, sizeof_doc}, - {"byref", byref, METH_VARARGS, byref_doc}, - {"addressof", addressof, METH_O, addressof_doc}, + _CTYPES_SIZEOF_METHODDEF + _CTYPES_BYREF_METHODDEF + _CTYPES_ADDRESSOF_METHODDEF {"call_function", call_function, METH_VARARGS }, {"call_cdeclfunction", call_cdeclfunction, METH_VARARGS }, {"PyObj_FromPtr", My_PyObj_FromPtr, METH_VARARGS }, @@ -2132,9 +2128,3 @@ PyMethodDef _ctypes_module_methods[] = { {"Py_DECREF", My_Py_DECREF, METH_O }, {NULL, NULL} /* Sentinel */ }; - -/* - Local Variables: - compile-command: "cd .. && python setup.py -q build -g && python setup.py -q build install --home ~" - End: -*/ diff --git a/Modules/_ctypes/clinic/callproc.c.h b/Modules/_ctypes/clinic/callproc.c.h index a787693ae67cd8..8a5c8f6427b7f0 100644 --- a/Modules/_ctypes/clinic/callproc.c.h +++ b/Modules/_ctypes/clinic/callproc.c.h @@ -2,6 +2,147 @@ preserve [clinic start generated code]*/ +#include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() +#include "pycore_modsupport.h" // _PyArg_CheckPositional() + +PyDoc_STRVAR(_ctypes_sizeof__doc__, +"sizeof($module, obj, /)\n" +"--\n" +"\n" +"Return the size in bytes of a C instance."); + +#define _CTYPES_SIZEOF_METHODDEF \ + {"sizeof", (PyCFunction)_ctypes_sizeof, METH_O, _ctypes_sizeof__doc__}, + +PyDoc_STRVAR(_ctypes_byref__doc__, +"byref($module, obj, offset=0, /)\n" +"--\n" +"\n" +"Return a pointer lookalike to a C instance, only usable as function argument."); + +#define _CTYPES_BYREF_METHODDEF \ + {"byref", _PyCFunction_CAST(_ctypes_byref), METH_FASTCALL, _ctypes_byref__doc__}, + +static PyObject * +_ctypes_byref_impl(PyObject *module, PyObject *obj, Py_ssize_t offset); + +static PyObject * +_ctypes_byref(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *obj; + Py_ssize_t offset = 0; + + if (!_PyArg_CheckPositional("byref", nargs, 1, 2)) { + goto exit; + } + if (!PyObject_TypeCheck(args[0], clinic_state()->PyCData_Type)) { + _PyArg_BadArgument("byref", "argument 1", (clinic_state()->PyCData_Type)->tp_name, args[0]); + goto exit; + } + obj = args[0]; + if (nargs < 2) { + goto skip_optional; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + offset = ival; + } +skip_optional: + Py_BEGIN_CRITICAL_SECTION(obj); + return_value = _ctypes_byref_impl(module, obj, offset); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(_ctypes_addressof__doc__, +"addressof($module, obj, /)\n" +"--\n" +"\n" +"Return the address of the C instance internal buffer"); + +#define _CTYPES_ADDRESSOF_METHODDEF \ + {"addressof", (PyCFunction)_ctypes_addressof, METH_O, _ctypes_addressof__doc__}, + +static PyObject * +_ctypes_addressof_impl(PyObject *module, PyObject *obj); + +static PyObject * +_ctypes_addressof(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *obj; + + if (!PyObject_TypeCheck(arg, clinic_state()->PyCData_Type)) { + _PyArg_BadArgument("addressof", "argument", (clinic_state()->PyCData_Type)->tp_name, arg); + goto exit; + } + obj = arg; + Py_BEGIN_CRITICAL_SECTION(obj); + return_value = _ctypes_addressof_impl(module, obj); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(_ctypes_resize__doc__, +"resize($module, obj, size, /)\n" +"--\n" +"\n"); + +#define _CTYPES_RESIZE_METHODDEF \ + {"resize", _PyCFunction_CAST(_ctypes_resize), METH_FASTCALL, _ctypes_resize__doc__}, + +static PyObject * +_ctypes_resize_impl(PyObject *module, CDataObject *obj, Py_ssize_t size); + +static PyObject * +_ctypes_resize(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + CDataObject *obj; + Py_ssize_t size; + + if (!_PyArg_CheckPositional("resize", nargs, 2, 2)) { + goto exit; + } + if (!PyObject_TypeCheck(args[0], clinic_state()->PyCData_Type)) { + _PyArg_BadArgument("resize", "argument 1", (clinic_state()->PyCData_Type)->tp_name, args[0]); + goto exit; + } + obj = (CDataObject *)args[0]; + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + size = ival; + } + Py_BEGIN_CRITICAL_SECTION(obj); + return_value = _ctypes_resize_impl(module, obj, size); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + PyDoc_STRVAR(create_pointer_type__doc__, "POINTER($module, type, /)\n" "--\n" @@ -29,4 +170,4 @@ PyDoc_STRVAR(create_pointer_inst__doc__, #define CREATE_POINTER_INST_METHODDEF \ {"pointer", (PyCFunction)create_pointer_inst, METH_O, create_pointer_inst__doc__}, -/*[clinic end generated code: output=51b311ea369e5adf input=a9049054013a1b77]*/ +/*[clinic end generated code: output=46a3841cbe5ddc96 input=a9049054013a1b77]*/ _______________________________________________ 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