|
#!/usr/bin/env python3
|
|
"""
|
|
python_dns_fork_test.py - Test for macOS Tahoe DNS fork bug
|
|
|
|
This script exercises the exact same conditions as the Ruby reproduction:
|
|
1. Parent process resolves DNS for an IPv4-only host (poisoning the child)
|
|
2. Fork a child process
|
|
3. Child attempts DNS resolution
|
|
|
|
On macOS Tahoe, this causes a crash (SIGSEGV) in both Ruby and Python,
|
|
demonstrating this is an OS-level bug, not language-specific.
|
|
|
|
To compare with Ruby behavior, run:
|
|
ruby ruby_dns_fork_bug.rb
|
|
"""
|
|
|
|
import socket
|
|
import os
|
|
import sys
|
|
|
|
TEST_HOST = "api.segment.io" # IPv4-only host
|
|
|
|
print(f"Python version: {sys.version}", flush=True)
|
|
print(f"Platform: {sys.platform}", flush=True)
|
|
print("", flush=True)
|
|
|
|
# Parent does DNS resolution first (this "poisons" the child in Ruby)
|
|
print(f"Parent: Resolving {TEST_HOST}...", flush=True)
|
|
result = socket.getaddrinfo(TEST_HOST, 443, socket.AF_UNSPEC, socket.SOCK_STREAM)
|
|
print(f"Parent: Done (got {len(result)} results)", flush=True)
|
|
|
|
# Fork and try DNS in child
|
|
print("Forking...", flush=True)
|
|
sys.stdout.flush()
|
|
|
|
pid = os.fork()
|
|
|
|
if pid == 0:
|
|
# Child process
|
|
print(f"Child ({os.getpid()}): Attempting DNS resolution...", flush=True)
|
|
|
|
try:
|
|
result = socket.getaddrinfo(TEST_HOST, 443, socket.AF_UNSPEC, socket.SOCK_STREAM)
|
|
print(f"Child: SUCCESS - DNS resolution completed! (got {len(result)} results)", flush=True)
|
|
os._exit(0)
|
|
except Exception as e:
|
|
print(f"Child: ERROR - {e}", flush=True)
|
|
os._exit(1)
|
|
else:
|
|
# Parent process waits for child
|
|
_, status = os.waitpid(pid, 0)
|
|
|
|
print("", flush=True)
|
|
if os.WIFSIGNALED(status):
|
|
sig = os.WTERMSIG(status)
|
|
print(f"❌ Python HAS the DNS fork bug (child killed by signal {sig})", flush=True)
|
|
elif os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0:
|
|
print("✅ Python does NOT have the DNS fork bug", flush=True)
|
|
else:
|
|
print(f"❌ Python HAS the DNS fork bug (exit status {os.WEXITSTATUS(status)})", flush=True)
|