https://github.com/python/cpython/commit/6e91d1f9aafc6e375092b8c14f6e30ebc74e4004
commit: 6e91d1f9aafc6e375092b8c14f6e30ebc74e4004
branch: main
author: Peter Bierma <[email protected]>
committer: vstinner <[email protected]>
date: 2025-04-02T17:04:25+02:00
summary:
gh-131974: Fix usages of `locked_deref` in `ctypes` (#131975)
files:
A Misc/NEWS.d/next/Library/2025-04-01-09-20-32.gh-issue-131974.AIzshA.rst
M Modules/_ctypes/_ctypes.c
diff --git
a/Misc/NEWS.d/next/Library/2025-04-01-09-20-32.gh-issue-131974.AIzshA.rst
b/Misc/NEWS.d/next/Library/2025-04-01-09-20-32.gh-issue-131974.AIzshA.rst
new file mode 100644
index 00000000000000..abf2f54f8c2035
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-04-01-09-20-32.gh-issue-131974.AIzshA.rst
@@ -0,0 +1,2 @@
+Fix several thread-safety issues in :mod:`ctypes` on the :term:`free
+threaded <free threading>` build.
diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
index 59ea579627e56f..efefc0157237bd 100644
--- a/Modules/_ctypes/_ctypes.c
+++ b/Modules/_ctypes/_ctypes.c
@@ -5326,13 +5326,13 @@ static PyType_Spec pycsimple_spec = {
PyCPointer_Type
*/
static PyObject *
-Pointer_item(PyObject *myself, Py_ssize_t index)
+Pointer_item_lock_held(PyObject *myself, Py_ssize_t index)
{
CDataObject *self = _CDataObject_CAST(myself);
Py_ssize_t size;
Py_ssize_t offset;
PyObject *proto;
- void *deref = locked_deref(self);
+ void *deref = *(void **)self->b_ptr;
if (deref == NULL) {
PyErr_SetString(PyExc_ValueError,
@@ -5364,8 +5364,23 @@ Pointer_item(PyObject *myself, Py_ssize_t index)
index, size, (char *)((char *)deref + offset));
}
+static PyObject *
+Pointer_item(PyObject *myself, Py_ssize_t index)
+{
+ CDataObject *self = _CDataObject_CAST(myself);
+ PyObject *res;
+ // TODO: The plan is to make LOCK_PTR() a mutex instead of a critical
+ // section someday, so when that happens, this needs to get refactored
+ // to be re-entrant safe.
+ // This goes for all the locks here.
+ LOCK_PTR(self);
+ res = Pointer_item_lock_held(myself, index);
+ UNLOCK_PTR(self);
+ return res;
+}
+
static int
-Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value)
+Pointer_ass_item_lock_held(PyObject *myself, Py_ssize_t index, PyObject *value)
{
CDataObject *self = _CDataObject_CAST(myself);
Py_ssize_t size;
@@ -5378,7 +5393,7 @@ Pointer_ass_item(PyObject *myself, Py_ssize_t index,
PyObject *value)
return -1;
}
- void *deref = locked_deref(self);
+ void *deref = *(void **)self->b_ptr;
if (deref == NULL) {
PyErr_SetString(PyExc_ValueError,
"NULL pointer access");
@@ -5409,10 +5424,21 @@ Pointer_ass_item(PyObject *myself, Py_ssize_t index,
PyObject *value)
index, size, ((char *)deref + offset));
}
+static int
+Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value)
+{
+ CDataObject *self = _CDataObject_CAST(myself);
+ int res;
+ LOCK_PTR(self);
+ res = Pointer_ass_item_lock_held(myself, index, value);
+ UNLOCK_PTR(self);
+ return res;
+}
+
static PyObject *
-Pointer_get_contents(PyObject *self, void *closure)
+Pointer_get_contents_lock_held(PyObject *self, void *closure)
{
- void *deref = locked_deref(_CDataObject_CAST(self));
+ void *deref = *(void **)_CDataObject_CAST(self)->b_ptr;
if (deref == NULL) {
PyErr_SetString(PyExc_ValueError,
"NULL pointer access");
@@ -5429,6 +5455,17 @@ Pointer_get_contents(PyObject *self, void *closure)
return PyCData_FromBaseObj(st, stginfo->proto, self, 0, deref);
}
+static PyObject *
+Pointer_get_contents(PyObject *myself, void *closure)
+{
+ CDataObject *self = _CDataObject_CAST(myself);
+ PyObject *res;
+ LOCK_PTR(self);
+ res = Pointer_get_contents_lock_held(myself, closure);
+ UNLOCK_PTR(self);
+ return res;
+}
+
static int
Pointer_set_contents(PyObject *op, PyObject *value, void *closure)
{
@@ -5462,7 +5499,15 @@ Pointer_set_contents(PyObject *op, PyObject *value, void
*closure)
}
dst = (CDataObject *)value;
- locked_deref_assign(self, dst->b_ptr);
+ if (dst != self) {
+ LOCK_PTR(dst);
+ locked_deref_assign(self, dst->b_ptr);
+ UNLOCK_PTR(dst);
+ } else {
+ LOCK_PTR(self);
+ *((void **)self->b_ptr) = dst->b_ptr;
+ UNLOCK_PTR(self);
+ }
/*
A Pointer instance must keep the value it points to alive. So, a
@@ -5514,6 +5559,23 @@ Pointer_new(PyTypeObject *type, PyObject *args, PyObject
*kw)
return generic_pycdata_new(st, type, args, kw);
}
+static int
+copy_pointer_to_list_lock_held(PyObject *myself, PyObject *np, Py_ssize_t len,
+ Py_ssize_t start, Py_ssize_t step)
+{
+ Py_ssize_t i;
+ size_t cur;
+ for (cur = start, i = 0; i < len; cur += step, i++) {
+ PyObject *v = Pointer_item_lock_held(myself, cur);
+ if (!v) {
+ return -1;
+ }
+ PyList_SET_ITEM(np, i, v);
+ }
+
+ return 0;
+}
+
static PyObject *
Pointer_subscript(PyObject *myself, PyObject *item)
{
@@ -5595,7 +5657,6 @@ Pointer_subscript(PyObject *myself, PyObject *item)
}
assert(iteminfo);
if (iteminfo->getfunc == _ctypes_get_fielddesc("c")->getfunc) {
- char *ptr = locked_deref(self);
char *dest;
if (len <= 0)
@@ -5603,6 +5664,7 @@ Pointer_subscript(PyObject *myself, PyObject *item)
if (step == 1) {
PyObject *res;
LOCK_PTR(self);
+ char *ptr = *(void **)self->b_ptr;
res = PyBytes_FromStringAndSize(ptr + start,
len);
UNLOCK_PTR(self);
@@ -5612,6 +5674,7 @@ Pointer_subscript(PyObject *myself, PyObject *item)
if (dest == NULL)
return PyErr_NoMemory();
LOCK_PTR(self);
+ char *ptr = *(void **)self->b_ptr;
for (cur = start, i = 0; i < len; cur += step, i++) {
dest[i] = ptr[cur];
}
@@ -5621,7 +5684,6 @@ Pointer_subscript(PyObject *myself, PyObject *item)
return np;
}
if (iteminfo->getfunc == _ctypes_get_fielddesc("u")->getfunc) {
- wchar_t *ptr = locked_deref(self);
wchar_t *dest;
if (len <= 0)
@@ -5629,6 +5691,7 @@ Pointer_subscript(PyObject *myself, PyObject *item)
if (step == 1) {
PyObject *res;
LOCK_PTR(self);
+ wchar_t *ptr = *(wchar_t **)self->b_ptr;
res = PyUnicode_FromWideChar(ptr + start,
len);
UNLOCK_PTR(self);
@@ -5638,6 +5701,7 @@ Pointer_subscript(PyObject *myself, PyObject *item)
if (dest == NULL)
return PyErr_NoMemory();
LOCK_PTR(self);
+ wchar_t *ptr = *(wchar_t **)self->b_ptr;
for (cur = start, i = 0; i < len; cur += step, i++) {
dest[i] = ptr[cur];
}
@@ -5651,14 +5715,15 @@ Pointer_subscript(PyObject *myself, PyObject *item)
if (np == NULL)
return NULL;
- for (cur = start, i = 0; i < len; cur += step, i++) {
- PyObject *v = Pointer_item(myself, cur);
- if (!v) {
- Py_DECREF(np);
- return NULL;
- }
- PyList_SET_ITEM(np, i, v);
+ int res;
+ LOCK_PTR(self);
+ res = copy_pointer_to_list_lock_held(myself, np, len, start, step);
+ UNLOCK_PTR(self);
+ if (res < 0) {
+ Py_DECREF(np);
+ return NULL;
}
+
return np;
}
else {
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]