From 7c7560bad1c5e2d1584b1db63b0e058ac20cfd57 Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Mon, 21 Sep 2026 21:05:34 +0200 Subject: [PATCH] Fix fcntl() flags not being passed by Subprocess on macOS (#6051) 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 --- app/scripts/fetch_xulrunner | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/scripts/fetch_xulrunner b/app/scripts/fetch_xulrunner index 065970ab22..60cd2f90ec 100755 --- a/app/scripts/fetch_xulrunner +++ b/app/scripts/fetch_xulrunner @@ -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