Project

General

Profile

Bug #21790 » python_dns_fork_test.py

Python Reproduction Script - adamoffat (Adam Moffat), 12/18/2025 06:39 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, 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)
(5-5/6)