Preserve callable addresses in ctypes function pointer casts (#8965)

Read the stored executable address when casting a function pointer and accept function pointer types as cast destinations. The existing ownership dictionary retains the original callback through a cast roundtrip.

Extracted independently on the unchanged Python 3.14 target, without typed callback argument or broader py_object transport changes. One unique callback-address/ownership regression extends the existing ctypes snippet; canonical coverage was checked for duplicates.

Assisted-by: Codex:model-version-unavailable
This commit is contained in:
youknowdot authored and GitHub committed 2026-10-05 15:23:22 +09:00
1 parent e5f332fda4
commit f46ba48f10
2 files changed
+18 -1

No files matched your search

+7 -1
View File
@@ -509,7 +509,10 @@ fn cast_check_pointertype(ctype: &PyObject, vm: &VirtualMachine) -> bool {
return true;
}
// PyCFuncPtrTypeObject_Check - TODO
// PyCFuncPtrTypeObject_Check
if ctype.class().fast_issubclass(PyCFuncPtrType::static_type()) {
return true;
}
// simple pointer types via StgInfo.proto (c_void_p, c_char_p, etc.)
if let Ok(type_attr) = ctype.get_attr("_type_", vm)
@@ -562,6 +565,9 @@ pub(super) fn cast_impl(
// Simple type (c_void_p, c_char_p, etc.) → value from buffer
let buffer = simple.0.buffer.read();
rustpython_host_env::ctypes::read_pointer_from_buffer(&buffer)
} else if let Some(funcptr) = obj.downcast_ref::<PyCFuncPtr>() {
// A function pointer stores the executable address in its buffer.
funcptr.get_func_ptr()
} else if let Some(cdata) = obj.downcast_ref::<PyCData>() {
// Array, Structure, Union → buffer address (b_ptr)
cdata.buffer.read().as_ptr() as usize
+11
View File
@@ -1,4 +1,5 @@
import ctypes
import gc
import itertools
import os as _os
import sys as _sys
@@ -458,4 +459,14 @@ except MemoryError:
else:
raise AssertionError("an unallocatable array was created")
# A function-pointer cast retains the callback and its executable address.
Unary = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int)
callback = Unary(lambda value: value + 1)
address = ctypes.cast(callback, ctypes.c_void_p)
roundtrip = ctypes.cast(address, Unary)
assert roundtrip._objects[id(callback)] is callback
del callback, address
gc.collect()
assert roundtrip(41) == 42
print("done")