PHP FFI on Apple Silicon: your ioctl call is lying to you
I spent an evening building pseudo-terminal support for PHP and lost an hour of it to a bug that reports success. If you use FFI and ioctl anywhere near production, and your CI only runs on Linux, this one is worth ten minutes of your time. The setup PHP can already open a pseudo-terminal. proc_open() accepts ['pty'] descriptors, and on macOS you get a real /dev/ttysNNN back. What you do not get…
PHP developers using FFI (Foreign Function Interface) and ioctl on Apple Silicon may face an issue where their ioctl call reports success, but the pseudo-terminal window size is not updated. This can lead to problems for applications that require a full-screen UI, such as top, vim, or an agent CLI. The problem lies in the declaration of the ioctl function in PHP's FFI parser, which is not variadic.
This is different from the standard C declaration, which is variadic. On Apple Silicon, the ARM64 ABI causes the non-variadic declaration to fail, as the variadic arguments are passed on the stack instead of in registers. This results in the ioctl function writing garbage values to the struct winsize, which can be misinterpreted as success.
The fix is to declare the ioctl function as variadic, allowing the correct memory to be accessed and ensuring the window size is set accurately. This class of bug is not limited to PHP and can be reproduced with any FFI over libffi, affecting any variadic libc function you bind. To avoid this issue, developers should ensure their CFFI declarations are variadic and verify the behavior of their code on Apple Silicon platforms.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.