Fix fcntl() flags not being passed by Subprocess on macOS (#6051)
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run

This showed up as devtools failing to start in a -d build on some machines, but any Subprocess.call() could fail because of it.

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
This commit is contained in:
windingwind 2026-09-21 21:05:34 +02:00 • committed by GitHub
parent b6f0f6c0ba
commit 7c7560bad1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -239,6 +239,26 @@ function modify_omni {
rm -rf default-theme
popd
# fcntl() is variadic, but js-ctypes declares it with a fixed third argument, which violates
# the ABI on every platform. It's harmless on Linux, where variadic and fixed integer
# arguments are passed in the same registers, but Apple's arm64 ABI passes variadic
# arguments on the stack, so the flag goes into a register the callee never reads and
# fcntl() takes it from an uninitialized stack slot instead. F_SETFD can therefore set
# FD_CLOFORK (bit 1, added in macOS 26) rather than FD_CLOEXEC, closing the pipe in the
# forked child and breaking Subprocess spawns, and F_SETFL can silently fail to set
# O_NONBLOCK. Since the value comes from leftover stack contents, this varies by build and
# call path instead of failing consistently -- don't assume it's fixed just because a given
# build works. This change should stay until this is fixed upstream.
if [[ $platform != win* ]]; then
replace_line 'ctypes\.int \/\* \.\.\. \*\/,' '"...",' modules/subprocess/subprocess_shared_unix.js
replace_line 'libc\.fcntl\(fds\[0\], LIBC\.F_SETFD, LIBC\.FD_CLOEXEC\);' 'libc.fcntl(fds[0], LIBC.F_SETFD, ctypes.int(LIBC.FD_CLOEXEC));' modules/subprocess/subprocess_unix.worker.js
replace_line 'libc\.fcntl\(fds\[1\], LIBC\.F_SETFD, LIBC\.FD_CLOEXEC\);' 'libc.fcntl(fds[1], LIBC.F_SETFD, ctypes.int(LIBC.FD_CLOEXEC));' modules/subprocess/subprocess_unix.worker.js
replace_line 'libc\.fcntl\(fds\[1\], LIBC\.F_SETFL, LIBC\.O_NONBLOCK\);' 'libc.fcntl(fds[1], LIBC.F_SETFL, ctypes.int(LIBC.O_NONBLOCK));' modules/subprocess/subprocess_unix.worker.js
replace_line 'libc\.fcntl\(fds\[0\], LIBC\.F_SETFL, LIBC\.O_NONBLOCK\);' 'libc.fcntl(fds[0], LIBC.F_SETFL, ctypes.int(LIBC.O_NONBLOCK));' modules/subprocess/subprocess_unix.sys.mjs
replace_line 'libc\.fcntl\(fds\[0\], LIBC\.F_SETFD, LIBC\.FD_CLOEXEC\);' 'libc.fcntl(fds[0], LIBC.F_SETFD, ctypes.int(LIBC.FD_CLOEXEC));' modules/subprocess/subprocess_unix.sys.mjs
replace_line 'libc\.fcntl\(fds\[1\], LIBC\.F_SETFD, LIBC\.FD_CLOEXEC\);' 'libc.fcntl(fds[1], LIBC.F_SETFD, ctypes.int(LIBC.FD_CLOEXEC));' modules/subprocess/subprocess_unix.sys.mjs
fi
# This file loads remote-settings.sys.mjs, but if it's empty Zotero crashes
replace_line 'async #init\(\) \{' 'async #init() { if (true) { return; }' modules/URLQueryStrippingListService.sys.mjs