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

Reply via email to