-
-
Notifications
You must be signed in to change notification settings - Fork 30k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gh-91353: Fix void return type handling in ctypes (GH-32246) #32246
Conversation
_ctypes_get_ffi_type never returns ffi_type_void. If the return type is specified as None, we need set the libffi return type to void, but just taking the output from _ctypes_get_ffi_type will make the return type be sint. This fixes two spots where ctypes accidentally converts None return type to sint rather than void, causing crashes on Emscripten targets.
Would it be possible to add a test case for this change? Otherwise, this builds and tests fine on Windows. Adding @tiran to the loop due to his current work on WASM. |
On wasm targets, there is already test coverage -- Trying to test on other targets is kind of hard because these calls go directly into |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change looks correct to me. But I'm not an expert on ctypes code.
Please add a news entry with blurb CLI or blurb-it tool.
@@ -399,7 +399,7 @@ CThunkObject *_ctypes_alloc_callback(PyObject *callable, | |||
#endif | |||
result = ffi_prep_cif(&p->cif, cc, | |||
Py_SAFE_DOWNCAST(nargs, Py_ssize_t, int), | |||
_ctypes_get_ffi_type(restype), | |||
p->ffi_restype, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change makes sense. p->ffi_restype
is set about 15 lines earlier.
if (restype == Py_None) { | ||
rtype = &ffi_type_void; | ||
} else { | ||
rtype = _ctypes_get_ffi_type(restype); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not familiar with ctypes. Would it make sense to check for None in _ctypes_get_ffi_type()
rather than here?
ffi_type *_ctypes_get_ffi_type(PyObject *obj)
{
StgDictObject *dict;
if (obj == NULL)
return &ffi_type_sint;
if (obj == Py_None)
return &ffi_type_void;
dict = PyType_stgdict(obj);
if (dict == NULL)
return &ffi_type_sint;
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is better? I ran into a similar issue when writing the libffi
port: _ctypes_get_ffi_type
is used for the arguments. Return types need a little bit of special handling, like for instance it doesn't make sense to have an argument of type ffi_type_void
. I would argue that it would be safest to do something like:
if (obj == Py_None)
// Set an error, arguments can't have type None!
but I'm not that familiar with the ctypes interface -- are we supposed to use None
if we are not sure about the argument's type? Similarly,
if (obj == NULL)
return &ffi_type_sint;
looks kind of scary to me. Why did we pass in NULL
to this function? If this function set exceptions in these weird cases and they were handled as appropriate at the call site, the bug I'm fixing here probably wouldn't have happened in the first place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The NULL
value is used to indicate not-set and is quite pervasive in the ctypes codebase. Function prototypes are not required to supply argtypes or restype. It seems _ctypes_get_ffi_type()
is designed for arguments so the additional checking for Py_None
outside of it would be correct.
Anything left to do on this? |
bump |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Patch looks good to me.
The PR has not landed yet because I wanted to give the ctypes maintainers more time to review and comment on the change set.
_ctypes_get_ffi_type never returns ffi_type_void. If the
return type is specified as None, we need set the libffi
return type to void, but just taking the output from
_ctypes_get_ffi_type will make the return type be sint.
This fixes two spots where ctypes accidentally converts
None return type to sint rather than void, causing crashes
on Emscripten targets.
I tested this patch against Pyodide and it fixes
test_code
.https://bugs.python.org/issue47197