From: "adamoffat (Adam Moffat) via ruby-core" Date: 2025-12-17T18:03:47+00:00 Subject: [ruby-core:124288] [Ruby Bug#21790] `Socket.getaddrinfo` hangs after `fork()` on macOS 26.1 (Tahoe) for IPv4-only hosts Issue #21790 has been reported by adamoffat (Adam Moffat). ---------------------------------------- Bug #21790: `Socket.getaddrinfo` hangs after `fork()` on macOS 26.1 (Tahoe) for IPv4-only hosts https://bugs.ruby-lang.org/issues/21790 * Author: adamoffat (Adam Moffat) * Status: Open * ruby -v: 3.3.8 * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- Ruby's `Socket.getaddrinfo` hangs indefinitely in forked child processes on macOS 26.1 (Tahoe) when resolving IPv4-only hostnames. This is a regression that does not occur on macOS 15.x (Sonoma) or earlier. **Ruby version:** ruby 3.3.8 (2025-04-09 revision b200bad6cd) [arm64-darwin24] Also confirmed this affects Ruby 3.2.6 and 3.4.1. **Reproducible script:** ``` ruby require "socket" require "timeout" puts "Ruby #{RUBY_VERSION} on #{RUBY_PLATFORM}" Socket.getaddrinfo("api.segment.io", 443, nil, :STREAM) puts "Parent: DNS completed" pid = fork do puts "Child: Attempting DNS resolution..." begin Timeout.timeout(90) do Socket.getaddrinfo("api.segment.io", 443, nil, :STREAM) end puts "Child: SUCCESS" exit 0 rescue Timeout::Error puts "Child: FAILED - hung for 90 seconds" exit 1 end end Process.wait(pid) ``` **Note:** Remove the `Timeout.timeout(90)` wrapper to observe the hang indefinitely. The timeout is included only to allow the script to exit for testing purposes. **Result of reproduce process:** ``` Ruby 3.3.8 on arm64-darwin24 Parent: DNS completed Child: Attempting DNS resolution... Child: FAILED - hung for 90 seconds ``` The child process hangs with one thread consuming 100% CPU. Expected result: The child process should complete DNS resolution successfully, as it does on macOS 15.x and earlier. **Analysis:** Stack trace shows: Main thread: Blocked in `wait_getaddrinfo` ��� `_pthread_cond_wait` DNS thread: Spinning in `_gai_nat64_second_pass` ��� `nw_path_access_agent_cache` ��� `_os_log_preferences_refresh` ��� `SIGSEGV` The crash occurs in macOS's NAT64 synthesis code path. Ruby's signal handler catches the `SIGSEGV` but cannot recover, causing the DNS thread to spin. **Key observations:** - Only affects IPv4-only hosts. Hosts with IPv6 (like google.com) work correctly. - Using `AF_INET` instead of `AF_UNSPEC` works. `Socket.getaddrinfo("api.segment.io", 443, Socket::AF_INET, :STREAM)` succeeds. - Python is not affected. Python calls `getaddrinfo()` synchronously without a background thread. - Parent must do DNS before fork. If the parent has not called getaddrinfo(), the child works correctly. **Workaround:** - Use `resolv-replace` to bypass the native DNS resolver: `require "resolv-replace"` **Impact:** This breaks all Ruby applications using pre-forking worker models (Resque, Unicorn, Puma, Sidekiq, Passenger) on macOS Tahoe. **Apple Bug Report:** Filed with Apple as Feedback Assistant #FB21364061 ---Files-------------------------------- stack_trace.txt (66.6 KB) ruby_dns_fork_bug.rb (1.02 KB) -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/