GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — ctypes array cache can return distinct types when types are created concurrently · Issue #160978 · python/cpython

ctypes array cache can return distinct types when types are created concurrently #160978

New issue
Copy link
New issue
Copy link
Open
Open
ctypes array cache can return distinct types when types are created concurrently#160978
Copy link
Assignees
kumaraditya303
Labels
extension-modulesC modules in the Modules dirC modules in the Modules dirtopic-ctypestopic-free-threadingtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@ngoldbaum
ngoldbaum
opened on Oct 30, 2026
Issue body actions

Bug report

On the free-threaded build, this fails with TypeError: expected c_int_Array_1 instance instead of c_int_Array_1:

from concurrent.futures import ThreadPoolExecutor
from ctypes import c_int
from threading import Barrier

barrier = Barrier(8)

def make_type(n):
    barrier.wait()
    return c_int * n

with ThreadPoolExecutor(8) as pool:
    for n in range(1, 1001):
        types = list(pool.map(make_type, [n] * 8))
        for t in types:
            types[0].from_param(t())

It passes on the GIL-enabled build because there isn't an opportunity for a GIL thread switch.

If I instead create a type that has a Python hook, I can deterministically trigger the same race on the GIL-enabled build as well:

import ctypes
from concurrent.futures import ThreadPoolExecutor
from threading import Barrier

barrier = Barrier(2)

class Meta(type(ctypes.c_int)):
    def __set_name__(cls, owner, name):
        barrier.wait(timeout=5)

class C(ctypes.c_int, metaclass=Meta):
    pass

with ThreadPoolExecutor(2) as pool:
    a, b = pool.map(lambda _: C * 100, range(2))

print(a is b)
a.from_param(b())

This second script prints False and then TypeError: expected C_Array_100 instance instead of C_Array_100 on both builds, because defining __set_name__ makes python code run after the cache miss on the array type cache during creation of the array class.

I think the fix is to rewrite how PyCArrayType_from_ctype uses the array_cache:

cpython/Modules/_ctypes/_ctypes.c

Lines 5363 to 5417 in 4a9b981

PyObject *
PyCArrayType_from_ctype(ctypes_state *st, PyObject *itemtype, Py_ssize_t length)
{
PyObject *key;
char name[256];
PyObject *len;
assert(st->array_cache != NULL);
len = PyLong_FromSsize_t(length);
if (len == NULL)
return NULL;
key = _PyTuple_FromPairSteal(Py_NewRef(itemtype), len);
if (!key)
return NULL;
PyObject *result;
if (_PyDict_GetItemProxy(st->array_cache, key, &result) != 0) {
// found or error
Py_DECREF(key);
return result;
}
// not found
if (!PyType_Check(itemtype)) {
PyErr_SetString(PyExc_TypeError,
"Expected a type object");
Py_DECREF(key);
return NULL;
}
#ifdef MS_WIN64
sprintf(name, "%.200s_Array_%Id",
((PyTypeObject *)itemtype)->tp_name, length);
#else
sprintf(name, "%.200s_Array_%ld",
((PyTypeObject *)itemtype)->tp_name, (long)length);
#endif
result = PyObject_CallFunction((PyObject *)st->PyCArrayType_Type,
"s(O){s:n,s:O}",
name,
st->PyCArray_Type,
"_length_",
length,
"_type_",
itemtype
);
if (result == NULL) {
Py_DECREF(key);
return NULL;
}
if (PyDict_SetItemProxy(st, st->array_cache, key, result) < 0) {
Py_DECREF(key);
Py_DECREF(result);
return NULL;
}
Py_DECREF(key);
return result;

The current implementation has a check-then-insert TOCTOU bug.

I found this looking at the root cause of numpy/numpy#29943. See also #127945.

CPython versions tested on:

3.14, 3.15, 3.16, main

Operating systems tested on:

macOS (M1)

Linked PRs

  • gh-160978: Return the same ctypes array type when created concurrently #161494

Activity

  1. ngoldbaum
    assigned
    kumaraditya303
    on Oct 30, 2026
  2. ngoldbaum
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    topic-ctypes
    extension-modulesC modules in the Modules dirC modules in the Modules dir
    topic-free-threading
    on Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

  • @kumaraditya303
    kumaraditya303

Labels

extension-modulesC modules in the Modules dirC modules in the Modules dirtopic-ctypestopic-free-threadingtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    You are visitor 01402503 — GeezerVison does not store the pages you look up.
    Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.