Cargo build hanging on macOS when spawning cc subprocess

Every now and then when building my project using cargo build the build process will hang on building a third-party crate. Up until now it's always been aws-lc-sys, but today I'm getting what looks like the same behaviour with mimalloc-sys.

Through verbose cargo output I trace this to the execution of build-script-build hanging. I then ran that binary manually which seemed to suggest the C compiler was hanging. Running again with CC_ENABLE_DEBUG_OUTPUT=1 confirmed that. The last output line I see is

running: "cc" "-E" "<project_root>/target/release/build/libmimalloc-sys-8a98c87ac87a2446/out/13408290981209617847detect_compiler_family.c"
Full build-script-build output
CARGO=/Users/pepijn/.rustup/toolchains/stable-aarch64-apple-darwin/bin/cargo CARGO_CFG_FEATURE='' CARGO_CFG_PANIC=unwind CARGO_CFG_TARGET_ABI='' CARGO_CFG_TARGET_ARCH=aarch64 CARGO_CFG_TARGET_ENDIAN=little CARGO_CFG_TARGET_ENV='' CARGO_CFG_TARGET_FAMILY=unix CARGO_CFG_TARGET_FEATURE=aes,crc,dit,dotprod,dpb,dpb2,fcma,fhm,flagm,fp16,frintts,jsconv,lor,lse,neon,paca,pacg,pan,pmuv3,ras,rcpc,rcpc2,rdm,sb,sha2,sha3,ssbs,vh CARGO_CFG_TARGET_HAS_ATOMIC=128,16,32,64,8,ptr CARGO_CFG_TARGET_OS=macos CARGO_CFG_TARGET_POINTER_WIDTH=64 CARGO_CFG_TARGET_VENDOR=apple CARGO_CFG_UNIX='' CARGO_ENCODED_RUSTFLAGS='' CARGO_MANIFEST_DIR=/Users/pepijn/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libmimalloc-sys-0.1.44 CARGO_MANIFEST_LINKS=mimalloc CARGO_MANIFEST_PATH=/Users/pepijn/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libmimalloc-sys-0.1.44/Cargo.toml CARGO_PKG_AUTHORS='Octavian Oncescu <octavonce@gmail.com>' CARGO_PKG_DESCRIPTION='Sys crate wrapping the mimalloc allocator' CARGO_PKG_HOMEPAGE='' CARGO_PKG_LICENSE=MIT CARGO_PKG_LICENSE_FILE='' CARGO_PKG_NAME=libmimalloc-sys CARGO_PKG_README='' CARGO_PKG_REPOSITORY='https://github.com/purpleprotocol/mimalloc_rust/tree/master/libmimalloc-sys' CARGO_PKG_RUST_VERSION='' CARGO_PKG_VERSION=0.1.44 CARGO_PKG_VERSION_MAJOR=0 CARGO_PKG_VERSION_MINOR=1 CARGO_PKG_VERSION_PATCH=44 CARGO_PKG_VERSION_PRE='' DEBUG=false DYLD_FALLBACK_LIBRARY_PATH='<project root>/target/release/deps:<project root>/target/release:/Users/pepijn/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib:/Users/pepijn/.rustup/toolchains/stable-aarch64-apple-darwin/lib:/Users/pepijn/lib:/usr/local/lib:/usr/lib' HOST=aarch64-apple-darwin NUM_JOBS=10 OPT_LEVEL=3 RUST_BACKTRACE=1 OUT_DIR=<project root>/target/release/build/libmimalloc-sys-8a98c87ac87a2446/out PROFILE=release CC_ENABLE_DEBUG_OUTPUT=true RUSTC=/Users/pepijn/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rustc RUSTDOC=/Users/pepijn/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rustdoc TARGET=aarch64-apple-darwin <project root>/target/release/build/libmimalloc-sys-c9219c1a459c644f/build-script-build --help
OPT_LEVEL = Some(3)
OUT_DIR = Some(<project root>/target/release/build/libmimalloc-sys-8a98c87ac87a2446/out)
TARGET = Some(aarch64-apple-darwin)
HOST = Some(aarch64-apple-darwin)
cargo:rerun-if-env-changed=CC_aarch64-apple-darwin
CC_aarch64-apple-darwin = None
cargo:rerun-if-env-changed=CC_aarch64_apple_darwin
CC_aarch64_apple_darwin = None
cargo:rerun-if-env-changed=HOST_CC
HOST_CC = None
cargo:rerun-if-env-changed=CC
CC = None
cargo:rerun-if-env-changed=CC_ENABLE_DEBUG_OUTPUT
running: "cc" "-E" "<project root>/target/release/build/libmimalloc-sys-8a98c87ac87a2446/out/13408290981209617847detect_compiler_family.c"

at this point in activity monitor I can see a clang subprocess running with 0% cpu usage. A dump of the process show it's hanging in a write syscall.

clang call stack
Call graph:
    2587 Thread_6468193   DispatchQueue_1: com.apple.main-thread  (serial)
      2587 start  (in dyld) + 6076  [0x181efeb98]
        2587 main  (in clang) + 84  [0x100ccb944]
          2587 clang_main(int, char**, llvm::ToolContext const&)  (in clang) + 216  [0x100cb3084]
            2587 driver_main(int, char**, llvm::ToolContext const&)  (in clang) + 4752  [0x100cb4378]
              2587 ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&)  (in clang) + 1144  [0x100cb4900]
                2587 cc1_main(llvm::ArrayRef<char const*>, char const*, void*)  (in clang) + 2160  [0x100cb95a8]
                  2587 clang::ExecuteCompilerInvocation(clang::CompilerInstance*)  (in clang) + 460  [0x101314344]
                    2587 clang::CompilerInstance::ExecuteAction(clang::FrontendAction&)  (in clang) + 452  [0x102eaf30c]
                      2587 <deduplicated_symbol>  (in clang) + 532  [0x104bf3a78]
                        2587 llvm::raw_fd_ostream::write_impl(char const*, unsigned long)  (in clang) + 72  [0x104bf4898]
                          2587 write  (in libsystem_kernel.dylib) + 8  [0x1822616f4]

Running the exact same cc invocation manually works fine and produces the following output

cc output
"cc" "-E" "/Users/pepijn/RustroverProjects/datafusion_playground/target/release/build/libmimalloc-sys-8a98c87ac87a2446/out/13408290981209617847detect_compiler_family.c"
# 1 "/Users/pepijn/RustroverProjects/datafusion_playground/target/release/build/libmimalloc-sys-8a98c87ac87a2446/out/13408290981209617847detect_compiler_family.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 466 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "/Users/pepijn/RustroverProjects/datafusion_playground/target/release/build/libmimalloc-sys-8a98c87ac87a2446/out/13408290981209617847detect_compiler_family.c" 2
/Users/pepijn/RustroverProjects/datafusion_playground/target/release/build/libmimalloc-sys-8a98c87ac87a2446/out/13408290981209617847detect_compiler_family.c:2:9: warning: clang [-W#pragma-messages]
    2 | #pragma message "clang"
      |         ^
                                                                                                                                                                                
#pragma message("clang")/Users/pepijn/RustroverProjects/datafusion_playground/target/release/build/libmimalloc-sys-8a98c87ac87a2446/out/13408290981209617847detect_compiler_family.c:6:9: warning: gcc [-W#pragma-messages]
    6 | #pragma message "gcc"
      |         ^
                                                                                                                                                                                



#pragma message("gcc")
2 warnings generated.

The best I can make of this is that something's going wrong in the communication between the parent and child process. Perhaps the child's stdout or stderr pipe is not being drained properly or something like that.
Does anyone have an idea what could be going on here or how I can troubleshoot further?

Are you sure that's not just normal Apple's attempt to protect you from yourself?

I doubt it's XProtect since it just sits there doing nothing forever. I need to kill the process in order for it to terminate.

In the meantime, the plot has thickened a bit. The 'workaround' I've been using so far is to reboot which magically fixed the problem. I was running cargo from the embedded terminal in Rustrover. Today I tried shutting down all applications and running cargo build --release again from another terminal (Ghostty, but I doubt that matters). Presto, the mimalloc-sys build completed in no time and cargo went straight to linking. Must be some weird interaction between Rustrover and cargo.

For completeness here are the samples I'm seeing for the child process chain that's hanging.

build-script-main [4227] blocking in read
Analysis of sampling build-script-main (pid 4227) every 1 millisecond
Process:         build-script-main [4227]
Path:            /Users/USER/*/build-script-main
Load Address:    0x10087c000
Identifier:      build-script-main
Version:         0
Code Type:       ARM64
Platform:        macOS
Parent Process:  cargo [4098]
Target Type:     live task

Date/Time:       2025-12-10 21:10:34.663 +0100
Launch Time:     2025-12-10 21:07:52.743 +0100
OS Version:      macOS 15.6.1 (24G90)
Report Version:  7
Analysis Tool:   /usr/bin/sample

Physical footprint:         2401K
Physical footprint (peak):  2401K
Idle exit:                  untracked
----

Call graph:
    2588 Thread_6508031   DispatchQueue_1: com.apple.main-thread  (serial)
      2588 start  (in dyld) + 6076  [0x181efeb98]
        2588 main  (in build-script-main) + 36  [0x10089c5dc]
          2588 std::rt::lang_start::hf5072fce9362c079  (in build-script-main) + 60  [0x100883740]
            2588 std::rt::lang_start_internal::hd700ba983d3377dc  (in build-script-main) + 1024  [0x10092fa20]
              2588 std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::h9908174134c03cb5  (in build-script-main) + 16  [0x10088375c]
                2588 std::sys::backtrace::__rust_begin_short_backtrace::h8d57a016ab69e3bb  (in build-script-main) + 12  [0x10088756c]
                  2588 core::ops::function::FnOnce::call_once::h8a9345821edca9f9  (in build-script-main) + 16  [0x100888a88]
                    2588 build_script_main::main::hbb1d747265a429d0  (in build-script-main) + 2652  [0x10089bb9c]
                      2588 _$LT$build_script_main..cc_builder..CcBuilder$u20$as$u20$build_script_main..Builder$GT$::build::h7b27655e32a704f5  (in build-script-main) + 136  [0x100885a40]
                        2588 build_script_main::cc_builder::CcBuilder::build_library::h9c4a3e92a3cf2c7d  (in build-script-main) + 32  [0x10087eb74]
                          2588 build_script_main::cc_builder::CcBuilder::prepare_builder::hc221d044aad5f8c4  (in build-script-main) + 64  [0x10087f790]
                            2588 build_script_main::cc_builder::CcBuilder::collect_universal_build_options::h19e0b500037bbfea  (in build-script-main) + 368  [0x10087fefc]
                              2588 build_script_main::cc_builder::CcBuilder::compiler_check::hd5088928b37ebcdb  (in build-script-main) + 1252  [0x10087f258]
                                2588 cc::Build::get_compiler::hcfadff8f2c5a9c09  (in build-script-main) + 36  [0x1008b31ac]
                                  2588 cc::Build::try_get_compiler::h8c409d65d5707875  (in build-script-main) + 348  [0x1008b51ec]
                                    2588 cc::Build::get_base_compiler::h678c8b85a8469f72  (in build-script-main) + 3156  [0x1008babf4]
                                      2588 cc::tool::Tool::new::ha47ff335050d707b  (in build-script-main) + 144  [0x1008e6288]
                                        2588 cc::tool::Tool::with_features::h0edccc26866a421d  (in build-script-main) + 216  [0x1008e3da4]
                                          2588 cc::tool::Tool::with_features::_$u7b$$u7b$closure$u7d$$u7d$::h9cbf88562f90ff83  (in build-script-main) + 656  [0x1008e5960]
                                            2588 cc::tool::Tool::with_features::detect_family_inner::h6a54c6f59f6b94d1  (in build-script-main) + 1452  [0x1008e452c]
                                              2588 std::sys::fd::unix::FileDesc::read_to_end::h90678055a872cb01  (in build-script-main) + 332  [0x100930c00]
                                                2588 read  (in libsystem_kernel.dylib) + 8  [0x18225e7dc]
clang [4347] blocking in __wait4_nocancel
Analysis of sampling clang (pid 4347) every 1 millisecond
Process:         clang [4347]
Path:            /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
Load Address:    0x100d6c000
Identifier:      clang
Version:         17.0.0 (1700.4.4.1)
Code Type:       ARM64
Platform:        macOS
Parent Process:  build-script-main [4227]
Target Type:     live task

Date/Time:       2025-12-10 21:09:39.956 +0100
Launch Time:     2025-12-10 21:07:52.940 +0100
OS Version:      macOS 15.6.1 (24G90)
Report Version:  7
Analysis Tool:   /usr/bin/sample

Physical footprint:         2241K
Physical footprint (peak):  2241K
Idle exit:                  untracked
----

Call graph:
    2591 Thread_6508332   DispatchQueue_1: com.apple.main-thread  (serial)
      2591 start  (in dyld) + 6076  [0x181efeb98]
        2591 main  (in clang) + 84  [0x101097944]
          2591 clang_main(int, char**, llvm::ToolContext const&)  (in clang) + 216  [0x10107f084]
            2591 driver_main(int, char**, llvm::ToolContext const&)  (in clang) + 2420  [0x10107fa5c]
              2591 clang::driver::Driver::ExecuteCompilation(clang::driver::Compilation&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&)  (in clang) + 152  [0x10334a0ac]
                2591 clang::driver::Compilation::ExecuteJobs(clang::driver::JobList const&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&, bool) const  (in clang) + 96  [0x10333b890]
                  2591 clang::driver::Compilation::ExecuteCommand(clang::driver::Command const&, clang::driver::Command const*&, bool) const  (in clang) + 132  [0x10333b5c8]
                    2591 clang::driver::Command::Execute(llvm::ArrayRef<std::optional<llvm::StringRef>>, std::basic_string<char>*, bool*) const  (in clang) + 876  [0x10335dea0]
                      2591 llvm::sys::ExecuteAndWait(llvm::StringRef, llvm::ArrayRef<llvm::StringRef>, std::optional<llvm::ArrayRef<llvm::StringRef>>, llvm::ArrayRef<std::optional<llvm::StringRef>>, unsigned int, unsigned int, std::basic_string<char>*, bool*, std::optional<llvm::sys::ProcessStatistics>*, llvm::BitVector*)  (in clang) + 156  [0x104fd1334]
                        2591 llvm::sys::Wait(llvm::sys::ProcessInfo const&, std::optional<unsigned int>, std::basic_string<char>*, std::optional<llvm::sys::ProcessStatistics>*, bool)  (in clang) + 152  [0x104fd1ad4]
                          2591 __wait4_nocancel  (in libsystem_kernel.dylib) + 8  [0x18226a05c]
clang [4362] blocking in write
Analysis of sampling clang (pid 4362) every 1 millisecond
Process:         clang [4362]
Path:            /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
Load Address:    0x10004c000
Identifier:      clang
Version:         17.0.0 (1700.4.4.1)
Code Type:       ARM64
Platform:        macOS
Parent Process:  clang [4347]
Target Type:     live task

Date/Time:       2025-12-10 21:08:40.091 +0100
Launch Time:     2025-12-10 21:07:52.975 +0100
OS Version:      macOS 15.6.1 (24G90)
Report Version:  7
Analysis Tool:   /usr/bin/sample

Physical footprint:         3377K
Physical footprint (peak):  3377K
Idle exit:                  untracked
----

Call graph:
    2591 Thread_6508348   DispatchQueue_1: com.apple.main-thread  (serial)
      2591 start  (in dyld) + 6076  [0x181efeb98]
        2591 main  (in clang) + 84  [0x100377944]
          2591 clang_main(int, char**, llvm::ToolContext const&)  (in clang) + 216  [0x10035f084]
            2591 driver_main(int, char**, llvm::ToolContext const&)  (in clang) + 4752  [0x100360378]
              2591 ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&)  (in clang) + 1144  [0x100360900]
                2591 cc1_main(llvm::ArrayRef<char const*>, char const*, void*)  (in clang) + 2160  [0x1003655a8]
                  2591 clang::ExecuteCompilerInvocation(clang::CompilerInstance*)  (in clang) + 460  [0x1009c0344]
                    2591 clang::CompilerInstance::ExecuteAction(clang::FrontendAction&)  (in clang) + 452  [0x10255b30c]
                      2591 <deduplicated_symbol>  (in clang) + 532  [0x10429fa78]
                        2591 llvm::raw_fd_ostream::write_impl(char const*, unsigned long)  (in clang) + 72  [0x1042a0898]
                          2591 write  (in libsystem_kernel.dylib) + 8  [0x1822616f4]

Could it be a deadlock due to cc waiting to print its output, but whatever spawned the process isn't reading its stdout because it's waiting for it to exit or to close its stdin first?

That’s what I’m thinking as well. Doesn’t happen consistently though, so I’m thinking there’s a race condition somewhere. In the other hand, once it starts happening it is consistent until reboot.
The chain of child processes is Rustrover > zsh > cargo > build-script-main > clang > clang. That’s going to take some work to figure out who’s the culprit here.