1a4f7f97bd
This adds a script which reproduces this bug after a lot of iterations in gdb and lets us get a backtrace
124 lines
4.4 KiB
Plaintext
124 lines
4.4 KiB
Plaintext
# GDB command file for reproducing UdpCommandDemuxer heisenbug
|
|
# This script runs salmanoff, waits a random time, sends SIGINT, and catches segfaults
|
|
|
|
# Disable pager so output doesn't pause for user input
|
|
set pagination off
|
|
|
|
# Set up signal handling - catch segfaults and stop
|
|
handle SIGSEGV stop print
|
|
# Allow SIGINT to pass through to program silently - make it unremarkable
|
|
# nostop: don't stop execution, noprint: don't print message, pass: pass to program
|
|
handle SIGINT nostop noprint pass
|
|
|
|
# Use Python to set up automatic handling of stop events and SIGINT injection
|
|
python
|
|
import time
|
|
import random
|
|
import threading
|
|
import os
|
|
import signal
|
|
|
|
sigint_thread_started = False
|
|
|
|
def send_sigint_after_delay():
|
|
# Wait random milliseconds between 2000-3000
|
|
delay_ms = random.randint(2000, 3000)
|
|
print(f"Waiting {delay_ms}ms before sending SIGINT...")
|
|
time.sleep(delay_ms / 1000.0)
|
|
|
|
# Send SIGINT directly to the process using its PID
|
|
# This works even when the program is running (unlike gdb.execute("signal SIGINT"))
|
|
try:
|
|
inferior = gdb.selected_inferior()
|
|
if inferior and inferior.is_valid():
|
|
pid = inferior.pid
|
|
print(f"Sending SIGINT to program (PID: {pid})...")
|
|
os.kill(pid, signal.SIGINT)
|
|
else:
|
|
print("Program is not running - cannot send SIGINT")
|
|
except Exception as e:
|
|
print(f"Failed to send SIGINT: {e}")
|
|
|
|
def start_sigint_thread():
|
|
global sigint_thread_started
|
|
if not sigint_thread_started:
|
|
sigint_thread_started = True
|
|
thread = threading.Thread(target=send_sigint_after_delay, daemon=True)
|
|
thread.start()
|
|
|
|
# Hook to check stop reason and handle segfaults
|
|
def stop_handler(event):
|
|
if isinstance(event, gdb.SignalEvent):
|
|
if event.stop_signal == "SIGSEGV":
|
|
# Segfault detected
|
|
print("\n=== SEGFAULT DETECTED ===")
|
|
gdb.execute("bt")
|
|
print("\n=== GDB is now interactive - you can inspect the state ===")
|
|
# Don't quit - stay in interactive mode
|
|
elif event.stop_signal == "SIGINT":
|
|
# SIGINT received - with "nostop pass", SIGINT should pass through automatically
|
|
# But if we get here (shouldn't happen with nostop), just let it pass
|
|
pass
|
|
elif isinstance(event, gdb.ExitedEvent):
|
|
# Program exited normally
|
|
if event.exit_code == 0:
|
|
print("\nProgram exited normally. Continuing loop...")
|
|
gdb.post_event(lambda: gdb.execute("quit", False))
|
|
else:
|
|
print(f"\nProgram exited with code {event.exit_code}")
|
|
gdb.post_event(lambda: gdb.execute("quit", False))
|
|
|
|
# Hook for when program continues/starts running
|
|
def cont_handler(event):
|
|
# When program continues (or starts running), start the SIGINT thread
|
|
start_sigint_thread()
|
|
|
|
# Register event handlers
|
|
gdb.events.stop.connect(stop_handler)
|
|
gdb.events.cont.connect(cont_handler)
|
|
|
|
# Start SIGINT thread before running - it will wait and then send SIGINT
|
|
# The thread will send SIGINT even if program is stopped (signal will be delivered on continue)
|
|
start_sigint_thread()
|
|
end
|
|
|
|
# Start the program
|
|
echo Starting program...\n
|
|
run
|
|
|
|
# After run completes, check if program exited or stopped
|
|
# If program exited, quit GDB. If program stopped (has threads), continue.
|
|
python
|
|
try:
|
|
inferior = gdb.selected_inferior()
|
|
if inferior and inferior.is_valid():
|
|
# Check if there are threads (indicates program has not exited)
|
|
try:
|
|
threads = inferior.threads()
|
|
if threads:
|
|
# Program has threads - continue execution
|
|
# SIGINT thread is already running and will send signal when ready
|
|
gdb.execute("continue", False)
|
|
else:
|
|
# No threads - program has exited
|
|
print("\nProgram has exited (no threads found).")
|
|
gdb.execute("quit", False)
|
|
except Exception as e:
|
|
# If we can't check threads, assume program exited
|
|
print(f"\nError checking threads: {e}")
|
|
print("Assuming program exited.")
|
|
gdb.execute("quit", False)
|
|
else:
|
|
# Inferior is not valid - program has exited
|
|
print("\nProgram has exited (inferior not valid).")
|
|
gdb.execute("quit", False)
|
|
except Exception as e:
|
|
print(f"Error checking program state: {e}")
|
|
# If we can't determine state, try to quit
|
|
try:
|
|
gdb.execute("quit", False)
|
|
except:
|
|
pass
|
|
end
|
|
|