Skip to content

Commit 72a218b

Browse files
committed
gh-156933: Widen narrow integer results in ctypes callbacks
1 parent 878b5e2 commit 72a218b

2 files changed

Lines changed: 54 additions & 8 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :mod:`ctypes` callbacks returning an integer narrower than a machine
2+
register: the result is now widened to fill the register, as libffi's
3+
closure contract requires.

Modules/_ctypes/callbacks.c

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,22 @@ TryAddRef(PyObject *cnv, CDataObject *obj)
101101
}
102102
#endif
103103

104+
static int
105+
is_narrow_int_ffi_type(int type)
106+
{
107+
switch (type) {
108+
case FFI_TYPE_SINT8:
109+
case FFI_TYPE_UINT8:
110+
case FFI_TYPE_SINT16:
111+
case FFI_TYPE_UINT16:
112+
case FFI_TYPE_SINT32:
113+
case FFI_TYPE_UINT32:
114+
return 1;
115+
default:
116+
return 0;
117+
}
118+
}
119+
104120
/******************************************************************************
105121
*
106122
* Call the python object with all arguments
@@ -222,13 +238,14 @@ static void _CallPythonObject(ctypes_state *st,
222238
if (restype != &ffi_type_void && result) {
223239
assert(setfunc);
224240

225-
#ifdef WORDS_BIGENDIAN
226-
/* See the corresponding code in _ctypes_callproc():
227-
in callproc.c, around line 1219. */
228-
if (restype->type != FFI_TYPE_FLOAT && restype->size < sizeof(ffi_arg)) {
229-
mem = (char *)mem + sizeof(ffi_arg) - restype->size;
230-
}
231-
#endif
241+
/* libffi's closure contract requires integral results narrower
242+
than ffi_arg to fill a whole register, sign-extended if signed;
243+
setfunc() only writes restype->size bytes. Cf. _ctypes_callproc()
244+
in callproc.c. */
245+
ffi_arg widened = 0;
246+
int narrow = restype->size < sizeof(ffi_arg) &&
247+
is_narrow_int_ffi_type(restype->type);
248+
void *resmem = narrow ? &widened : mem;
232249

233250
/* keep is an object we have to keep alive so that the result
234251
stays valid. If there is no such object, the setfunc will
@@ -239,7 +256,33 @@ static void _CallPythonObject(ctypes_state *st,
239256
be the result. EXCEPT when restype is py_object - Python
240257
itself knows how to manage the refcount of these objects.
241258
*/
242-
PyObject *keep = setfunc(mem, result, restype->size);
259+
PyObject *keep = setfunc(resmem, result, restype->size);
260+
261+
if (narrow && keep != NULL) {
262+
switch (restype->type) {
263+
case FFI_TYPE_SINT8:
264+
widened = (ffi_arg)(ffi_sarg)*(int8_t *)&widened;
265+
break;
266+
case FFI_TYPE_SINT16:
267+
widened = (ffi_arg)(ffi_sarg)*(int16_t *)&widened;
268+
break;
269+
case FFI_TYPE_SINT32:
270+
widened = (ffi_arg)(ffi_sarg)*(int32_t *)&widened;
271+
break;
272+
case FFI_TYPE_UINT8:
273+
widened = *(uint8_t *)&widened;
274+
break;
275+
case FFI_TYPE_UINT16:
276+
widened = *(uint16_t *)&widened;
277+
break;
278+
case FFI_TYPE_UINT32:
279+
widened = *(uint32_t *)&widened;
280+
break;
281+
default:
282+
break;
283+
}
284+
memcpy(mem, &widened, sizeof(ffi_arg));
285+
}
243286

244287
if (keep == NULL) {
245288
/* Could not convert callback result. */

0 commit comments

Comments
 (0)