Skip to content

Fix variadic calls on Apple platforms with a non-OS libffi - #265

Merged
mattip merged 2 commits into
python-cffi:mainfrom
ambv:fix-apple-variadic-nonsystem-libffi
Jul 26, 2026
Merged

Fix variadic calls on Apple platforms with a non-OS libffi#265
mattip merged 2 commits into
python-cffi:mainfrom
ambv:fix-apple-variadic-nonsystem-libffi

Conversation

@ambv

@ambv ambv commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Closes #264

The CFFI_CHECK_FFI_* feature checks only enabled ffi_prep_cif_var(), ffi_closure_alloc() and ffi_prep_closure_loc() on Apple platforms when FFI_AVAILABLE_APPLE was defined. That macro comes from the libffi header shipped with the macOS SDK, so any build against a different libffi - the static libffi that iOS/tvOS/watchOS wheels are built against, or a Homebrew libffi on macOS - silently fell into the fallback branch that disables all three functions.

Without ffi_prep_cif_var(), variadic functions are called through a cif prepared by ffi_prep_cif(), i.e. with the fixed-arguments calling convention. On arm64 Apple platforms variadic arguments are passed on the stack, unlike named arguments, so the callee read garbage: integers came back as random values and pointer arguments typically crashed (e.g. any printf-family callee dereferencing a garbage %s).

There is no runtime-availability question in this configuration: the libffi the module was linked against always provides all three functions, so the new branch enables them unconditionally.

The two "for an unknown reason" iOS markers in test_c.py were this bug: test_call_function_9 (garbage int return) and test_FILE (fscanf() reading nothing on 3.14, crashing on 3.13). Remove the markers so both act as regression tests. Verified on an arm64 iPhone simulator with CPython 3.13: the full test_c.py passes (208 passed, 21 skipped), and both tests fail again when the fix is reverted.

ambv and others added 2 commits July 23, 2026 12:24
Closes python-cffi#264

The CFFI_CHECK_FFI_* feature checks only enabled ffi_prep_cif_var(),
ffi_closure_alloc() and ffi_prep_closure_loc() on Apple platforms when
FFI_AVAILABLE_APPLE was defined.  That macro comes from the libffi
header shipped with the macOS SDK, so any build against a different
libffi - the static libffi that iOS/tvOS/watchOS wheels are built
against, or a Homebrew libffi on macOS - silently fell into the fallback
branch that disables all three functions.

Without ffi_prep_cif_var(), variadic functions are called through a cif
prepared by ffi_prep_cif(), i.e. with the fixed-arguments calling
convention.  On arm64 Apple platforms variadic arguments are passed on
the stack, unlike named arguments, so the callee read garbage: integers
came back as random values and pointer arguments typically crashed
(e.g. any printf-family callee dereferencing a garbage %s).

There is no runtime-availability question in this configuration: the
libffi the module was linked against always provides all three
functions, so the new branch enables them unconditionally.

The two "for an unknown reason" iOS markers in test_c.py were this bug:
test_call_function_9 (garbage int return) and test_FILE (fscanf()
reading nothing on 3.14, crashing on 3.13).  Remove the markers so both
act as regression tests.  Verified on an arm64 iPhone simulator with
CPython 3.13: the full test_c.py passes (208 passed, 21 skipped), and
both tests fail again when the fix is reverted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mattip

mattip commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Thanks @ambv. This could enable some of the skipped tests in test_c.py, but not needed in this PR.

@mattip
mattip merged commit 5488611 into python-cffi:main Jul 26, 2026
38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants