From cdc65153b83fde6b81dee128f37d89f9257a7d0b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 20 Aug 2026 18:27:21 +0300 Subject: [PATCH] gh-156124: Fix a crash when deleting ctypes Pointer.contents In the free-threaded build the setter passed the value to Py_BEGIN_CRITICAL_SECTION2() before checking it for NULL. --- Lib/test/test_ctypes/test_delattr.py | 7 ++++++- .../2026-08-20-16-30-00.gh-issue-156124.Kq3vXz.rst | 2 ++ Modules/_ctypes/_ctypes.c | 10 +++++----- 3 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-08-20-16-30-00.gh-issue-156124.Kq3vXz.rst diff --git a/Lib/test/test_ctypes/test_delattr.py b/Lib/test/test_ctypes/test_delattr.py index e80b5fa6efb5455..eb99c0dafc86563 100644 --- a/Lib/test/test_ctypes/test_delattr.py +++ b/Lib/test/test_ctypes/test_delattr.py @@ -1,5 +1,5 @@ import unittest -from ctypes import Structure, c_char, c_int +from ctypes import POINTER, Structure, c_char, c_int class X(Structure): @@ -16,6 +16,11 @@ def test_chararray(self): with self.assertRaises(TypeError): del chararray.value + def test_pointer_contents(self): + ptr = POINTER(c_int)(c_int(42)) + with self.assertRaises(TypeError): + del ptr.contents + def test_struct(self): struct = X() with self.assertRaises(TypeError): diff --git a/Misc/NEWS.d/next/Library/2026-08-20-16-30-00.gh-issue-156124.Kq3vXz.rst b/Misc/NEWS.d/next/Library/2026-08-20-16-30-00.gh-issue-156124.Kq3vXz.rst new file mode 100644 index 000000000000000..64882cbc40e2450 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-20-16-30-00.gh-issue-156124.Kq3vXz.rst @@ -0,0 +1,2 @@ +Fix a crash in the free-threaded build when deleting the :attr:`!contents` +attribute of a :mod:`ctypes` pointer. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 034f26807f84aa8..3882b9a5ddff3c6 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -5713,11 +5713,6 @@ Pointer_set_contents_lock_held(PyObject *op, PyObject *value, void *closure) PyObject *keep; CDataObject *self = _CDataObject_CAST(op); - if (value == NULL) { - PyErr_SetString(PyExc_TypeError, - "Pointer does not support item deletion"); - return -1; - } ctypes_state *st = get_module_state_by_def(Py_TYPE(Py_TYPE(self))); StgInfo *stginfo; if (PyStgInfo_FromObject(st, op, &stginfo) < 0) { @@ -5761,6 +5756,11 @@ Pointer_set_contents_lock_held(PyObject *op, PyObject *value, void *closure) static int Pointer_set_contents(PyObject *op, PyObject *value, void *closure) { + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, + "Pointer does not support item deletion"); + return -1; + } int res; Py_BEGIN_CRITICAL_SECTION2(op, value); res = Pointer_set_contents_lock_held(op, value, closure);