Project

General

Profile

Bug #21790 » python_dns_fork_test.py

Python reproduction script - adamoffat (Adam Moffat), 12/18/2025 06:26 PM

 
#!/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 with Ruby, this causes a hang/crash.
On Python, this should work fine (demonstrating the bug is Ruby-specific).
"""

import socket
import os
import sys
import signal

TEST_HOST = "api.segment.io" # IPv4-only host
TIMEOUT_SECONDS = 90

print(f"Python version: {sys.version}")
print(f"Platform: {sys.platform}")
print()

# Parent does DNS resolution first (this "poisons" the child in Ruby)
print(f"Parent: Resolving {TEST_HOST}...")
socket.getaddrinfo(TEST_HOST, 443, socket.AF_UNSPEC, socket.SOCK_STREAM)
print("Parent: Done")

# Fork and try DNS in child
print("Forking...")
pid = os.fork()

if pid == 0:
# Child process
print(f"Child ({os.getpid()}): Attempting DNS resolution...")
# Set up alarm for timeout
def timeout_handler(signum, frame):
print("Child: FAILED - DNS resolution hung (bug present)")
sys.exit(1)
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(TIMEOUT_SECONDS)
try:
socket.getaddrinfo(TEST_HOST, 443, socket.AF_UNSPEC, socket.SOCK_STREAM)
signal.alarm(0) # Cancel the alarm
print("Child: SUCCESS - DNS resolution completed!")
sys.exit(0)
except Exception as e:
signal.alarm(0)
print(f"Child: ERROR - {e}")
sys.exit(1)
else:
# Parent process
_, status = os.waitpid(pid, 0)
exit_status = os.WEXITSTATUS(status)
print()
if exit_status == 0:
print("✅ Python does NOT have the DNS fork bug")
else:
print("❌ Python HAS the DNS fork bug")

(4-4/6)