#!/usr/bin/python import logging import signal import os import time from ptrace.debugger.debugger import PtraceDebugger from ptrace.debugger.child import createChild from ptrace.debugger.process_event import ProcessExit from ptrace.debugger.ptrace_signal import ProcessSignal from target import targetutils from .abstractverifierservermanager import AbstractVerifierServerManager from servercrashdata import ServerCrashData class DebugServerManager(AbstractVerifierServerManager): def __init__(self, config, queue_sync, queue_out, targetPort): AbstractVerifierServerManager.__init__(self, config, queue_sync, queue_out, targetPort) self.dbg = None self.crashEvent = None self.proc = None self.p = None if config["debug"]: logging.basicConfig(level=logging.DEBUG) def _startServer(self): # create child via ptrace debugger # API: createChild(arguments[], no_stdout, env=None) logging.debug("DebugServerManager: starting " + str(targetutils.getInvokeTargetArgs(self.config, self.targetPort + 1000))) self.pid = createChild( targetutils.getInvokeTargetArgs(self.config, self.targetPort), True, # no_stdout None, ) # Attach to the process with ptrace and let it run self.dbg = PtraceDebugger() self.proc = self.dbg.addProcess(self.pid, True) self.proc.cont() time.sleep(1) # i dont think this works here... # FIXME event = self.dbg.waitProcessEvent(blocking=False) if event is not None and type(event) == ProcessExit: logging.error("DebugServerManager: Started server, but it already exited: " + str(event)) return False return True def _stopServer(self): try: self.dbg.quit() os.kill(self.pid, signal.SIGTERM) except: # is already dead... pass def _waitForCrash(self): while True: logging.info("ServerManager: Debug: Waiting for process event") event = self.dbg.waitProcessEvent() logging.info("ServerManager: Debug: " + str(event)) # If this is a process exit we need to check if it was abnormal if type(event) == ProcessExit: if event.signum is None or event.exitcode == 0: # Clear the event since this was a normal exit event = None # If this is a signal we need to check if we're ignoring it elif type(event) == ProcessSignal: if event.signum == signal.SIGCHLD: # Ignore these signals and continue waiting continue elif event.signum == signal.SIGTERM: # server cannot be started, return event = None self.queue_sync.put( ("err", event.signum) ) break if event is not None and event.signum != 15: logging.info("ServerManager: Debug: Event Result: Crash") self.crashEvent = event return True else: logging.info("ServerManager: Debug: Event Result: No crash") self.crashEvent = None return False def _getCrashDetails(self): event = self.crashEvent # Get the address where the crash occurred faultAddress = 0 try: faultAddress = event.process.getInstrPointer() except Exception as e: # process already dead, hmm logging.info("DebugServerManager: GetCrashDetails exception: " + str(e)) # Find the module that contains this address # Now we need to turn the address into an offset. This way when the process # is loaded again if the module is loaded at another address, due to ASLR, # the offset will be the same and we can correctly detect those as the same # crash module = None faultOffset = 0 try: for mapping in event.process.readMappings(): if faultAddress >= mapping.start and faultAddress < mapping.end: module = mapping.pathname faultOffset = faultAddress - mapping.start break except Exception as error: logging.info("DebugServerManager: getCrashDetails Exception: " + str(error)) # it always has a an exception... pass # Apparently the address didn't fall within a mapping if module is None: module = "Unknown" faultOffset = faultAddress # Get the signal sig = event.signum # Get the details of the crash details = None details = "" stackAddr = 0 stackPtr = 0 backtraceFrames = None pRegisters = None try: if event._analyze() is not None: details = event._analyze().text # more data stackAddr = self.proc.findStack() stackPtr = self.proc.getStackPointer() # convert backtrace backtrace = self.proc.getBacktrace() backtraceFrames = [] for frame in backtrace.frames: backtraceFrames.append( str(frame) ) # convert registers from ctype to python registers = self.proc.getregs() pRegisters = {} for field_name, field_type in registers._fields_: regName = str(field_name) regValue = str(getattr(registers, field_name)) pRegisters[regName] = regValue except Exception as e: # process already dead, hmm logging.info(("DebugServerManager: GetCrashDetails exception: " + str(e))) asanOutput = targetutils.getAsanOutput(self.config, self.pid) serverCrashData = ServerCrashData( faultAddress=faultAddress, faultOffset=faultOffset, module=module, sig=sig, details=details, stackPointer=stackPtr, stackAddr=str(stackAddr), backtrace=backtraceFrames, registers=pRegisters, asanOutput=asanOutput, analyzerType='ptrace', ) return serverCrashData