Path : /usr/libexec/ |
|
Current File : //usr/libexec/abrt-gdb-exploitable |
#!/usr/bin/python
# This is a GDB plugin.
# Usage:
# gdb --batch -ex 'python execfile("THIS_FILE")' -ex run -ex abrt-exploitable PROG
# or
# gdb --batch -ex 'python execfile("THIS_FILE")' -ex 'core COREDUMP' -ex abrt-exploitable
import sys
import os
import signal
import re
import gettext
import locale
import gdb
GETTEXT_PROGNAME = "abrt"
_ = gettext.lgettext
def init_gettext():
try:
locale.setlocale(locale.LC_ALL, "")
except locale.Error:
os.environ['LC_ALL'] = 'C'
locale.setlocale(locale.LC_ALL, "")
# Defeat "AttributeError: 'module' object has no attribute 'nl_langinfo'"
try:
gettext.bind_textdomain_codeset(GETTEXT_PROGNAME, locale.nl_langinfo(locale.CODESET))
except AttributeError:
pass
gettext.bindtextdomain(GETTEXT_PROGNAME, '/usr/share/locale')
gettext.textdomain(GETTEXT_PROGNAME)
_WRITES_ALWAYS = -1
_WRITES_IF_MEMREF = -2
_x86_writing_instr = {
# insn:N, where N:
# -1: this insn always writes to memory
# -2: writes to memory if any operand is a memory operand
# 2: writes to memory if 2nd (or later) operand is a memory operand
#
# Two-operand insns
"add": 2,
"adc": 2,
"sub": 2,
"sbb": 2,
"and": 2,
"xor": 2,
"or": 2,
"xadd": 2,
"cmpxchg": 2,
# One-operand insns. Can use 1 or _WRITES_IF_MEMREF
"inc": _WRITES_IF_MEMREF,
"dec": _WRITES_IF_MEMREF,
"neg": _WRITES_IF_MEMREF,
"not": _WRITES_IF_MEMREF,
"pop": _WRITES_IF_MEMREF,
# "Set byte on condition". One-operand insns.
"seta": _WRITES_IF_MEMREF,
"setae": _WRITES_IF_MEMREF,
"setb": _WRITES_IF_MEMREF,
"setbe": _WRITES_IF_MEMREF,
"setc": _WRITES_IF_MEMREF,
"sete": _WRITES_IF_MEMREF,
"setg": _WRITES_IF_MEMREF,
"setge": _WRITES_IF_MEMREF,
"setl": _WRITES_IF_MEMREF,
"setle": _WRITES_IF_MEMREF,
"setna": _WRITES_IF_MEMREF,
"setnae": _WRITES_IF_MEMREF,
"setnb": _WRITES_IF_MEMREF,
"setnbe": _WRITES_IF_MEMREF,
"setnc": _WRITES_IF_MEMREF,
"setne": _WRITES_IF_MEMREF,
"setng": _WRITES_IF_MEMREF,
"setnge": _WRITES_IF_MEMREF,
"setnl": _WRITES_IF_MEMREF,
"setnle": _WRITES_IF_MEMREF,
"setno": _WRITES_IF_MEMREF,
"setnp": _WRITES_IF_MEMREF,
"setns": _WRITES_IF_MEMREF,
"setnz": _WRITES_IF_MEMREF,
"seto": _WRITES_IF_MEMREF,
"setp": _WRITES_IF_MEMREF,
"setpe": _WRITES_IF_MEMREF,
"setpo": _WRITES_IF_MEMREF,
"sets": _WRITES_IF_MEMREF,
"setz": _WRITES_IF_MEMREF,
# Shifts.
# sarl $2,(%rcx)
# sarl (%rax) - *implicit* operand (shift count) 1.
# shld 11,%ecx,(%rdi) - *third* operand is r/m.
# Luckily, any memory operand is a destination, can use _WRITES_IF_MEMREF.
"shl": _WRITES_IF_MEMREF,
"shr": _WRITES_IF_MEMREF,
"sal": _WRITES_IF_MEMREF,
"sar": _WRITES_IF_MEMREF,
"rol": _WRITES_IF_MEMREF,
"ror": _WRITES_IF_MEMREF,
"rcl": _WRITES_IF_MEMREF,
"rcr": _WRITES_IF_MEMREF,
"shld": _WRITES_IF_MEMREF,
"shrd": _WRITES_IF_MEMREF,
# Bit tests. Any memory operand is a destination, can use _WRITES_IF_MEMREF.
"bts": _WRITES_IF_MEMREF,
"btr": _WRITES_IF_MEMREF,
"btc": _WRITES_IF_MEMREF,
# One-operand (register pair is another, implicit operand).
"cmpxchg8b": _WRITES_IF_MEMREF,
"cmpxchg16b": _WRITES_IF_MEMREF,
# Either mem operand indicates write to mem.
"xchg": _WRITES_IF_MEMREF,
# String store insns.
# Look similar to widening signed move "movs[bwl][wlq]",
# but aliasing doesn't happen since widening move has two siffixes
"movs": _WRITES_ALWAYS,
"stos": _WRITES_ALWAYS,
# Widening moves never store to mem.
# May look like we need to list them because otherwise they get caught
# by "movXXX", but thankfully their 2nd operand is never a memory reference,
# which "movXXX" wildcard checks.
#"mov[sz][bwl][wlq]":0,
# One-operand insn.
# These are system insns, but they do NOT cause exception in userspace.
"smsw": _WRITES_IF_MEMREF,
"sgdt": _WRITES_IF_MEMREF,
"sidt": _WRITES_IF_MEMREF,
"sldt": _WRITES_IF_MEMREF,
"str": _WRITES_IF_MEMREF,
# FPU/SIMD madness follows.
# FPU store insns. One-operand.
"fsts": _WRITES_IF_MEMREF,
"fstl": _WRITES_IF_MEMREF,
#"fstt" doesn't exist
"fstps": _WRITES_IF_MEMREF,
"fstpl": _WRITES_IF_MEMREF,
"fstpt": _WRITES_IF_MEMREF,
# Saving state. One-operand insns.
"fstcw": _WRITES_IF_MEMREF,
"fnstcw": _WRITES_IF_MEMREF,
"fstsw": _WRITES_IF_MEMREF,
"fnstsw": _WRITES_IF_MEMREF,
"fstenv": _WRITES_IF_MEMREF,
"fnstenv": _WRITES_IF_MEMREF,
"fsave": _WRITES_IF_MEMREF,
"fnsave": _WRITES_IF_MEMREF,
"fxsave": _WRITES_IF_MEMREF,
"xsave": _WRITES_IF_MEMREF,
"xsaveopt": _WRITES_IF_MEMREF,
"fsave64": _WRITES_IF_MEMREF,
"fnsave64": _WRITES_IF_MEMREF,
"fxsave64": _WRITES_IF_MEMREF,
"xsave64": _WRITES_IF_MEMREF,
"xsaveopt64": _WRITES_IF_MEMREF,
"stmxcsr": _WRITES_IF_MEMREF,
"vstmxcsr": _WRITES_IF_MEMREF,
# SIMD store insns.
# Three-operand insns. Any memory operand is a destination.
"vcvtps2ph": _WRITES_IF_MEMREF,
"extractps": _WRITES_IF_MEMREF,
"vextractps": _WRITES_IF_MEMREF,
#[v]extractpd does not exist
"vextractf128": _WRITES_IF_MEMREF,
"vextracti128": _WRITES_IF_MEMREF,
"pextr": _WRITES_IF_MEMREF, # covers pextr[bwq]
"pextrd": _WRITES_IF_MEMREF,
"vpextr": _WRITES_IF_MEMREF,
"vpextrd": _WRITES_IF_MEMREF,
"vmaskmovpd": _WRITES_IF_MEMREF,
"vmaskmovps": _WRITES_IF_MEMREF,
"vpmaskmovd": _WRITES_IF_MEMREF,
"vpmaskmovq": _WRITES_IF_MEMREF,
# These insns have implicit (%edi) dest operand:
"maskmovq": _WRITES_ALWAYS, # mmx version
"maskmovdqu": _WRITES_ALWAYS,
"vmaskmovdqu": _WRITES_ALWAYS,
# check binutils/gas/testsuite/gas/i386/* for more weird insns
# Instruction Set Reference, A-M and N-Z:
# http://download.intel.com/products/processor/manual/253666.pdf
# http://download.intel.com/products/processor/manual/253667.pdf
# SSE4:
# http://software.intel.com/sites/default/files/m/0/3/c/d/4/18187-d9156103.pdf
# Instruction Set Extensions:
# http://download-software.intel.com/sites/default/files/319433-014.pdf
# Xeon Phi:
# http://download-software.intel.com/sites/default/files/forum/278102/327364001en.pdf
#"[v]movXXX" - special-cased in the code
"mov": 2
# Note: stack-writing instructions are omitted
}
_x86_pushing_instr = (
"push",
"pusha",
"pushf",
"enter",
"call",
"lcall"
)
_x86_intdiv_instr = ("div", "idiv")
_x86_jumping_instr = (
"jmp", # indirect jumps/calls with garbage data
"call", # call: also possible that stack is exhausted (infinite recursion)
"ljmp",
"lcall",
# Yes, lret/iret isn't used in normal userspace code,
# but it does work (compile with "gcc -nostartfiles -nostdlib -m32"):
#
#_start: .globl _start
# pushf
# push %cs
# push $next
# iret # lret or ret would work too
#next:
# movl $42, %ebx
# movl $1, %eax
# int $0x80 # exit(42)
#
"iret",
"lret",
"ret"
)
# stack was smashed if we crash on one of these
_x86_return_instr = ("iret", "lret", "ret")
_x86_mem_op1_regex = re.compile("^((-?0x)|[(])")
_x86_mem_op2_regex = re.compile("[,:]((-?0x)|[(])")
def _x86_fetch_insn_from_table(ins, table):
if not ins:
return None
if ins in table:
if type(table) == dict:
return table[ins]
return ins
# Drop common byte/word/long/quad suffix and try again
if ins[-1] in ("b", "w", "l", "q"):
ins = ins[:-1]
if ins in table:
if type(table) == dict:
return table[ins]
return ins
return None
class SignalAndInsn:
def x86_instruction_is_store(self):
operand = _x86_fetch_insn_from_table(self.mnemonic, _x86_writing_instr)
if not operand:
if not self.mnemonic:
return False
# There are far too many SSE store instructions,
# don't want to pollute the table with them.
# Special-case the check for MOVxxx
# and its SIMD cousins VMOVxxx:
if self.mnemonic[:3] != "mov" and self.mnemonic[:4] != "vmov":
return False
operand = 2
if operand == _WRITES_ALWAYS: # no need to check operands, it's a write
return True
# Memory operands look like this: [%seg:][[-]0xHEXNUM][(%reg[,...])]
# Careful with immediate operands which are $0xHEXNUM
# and FPU register references which are st(N).
if _x86_mem_op1_regex.search(self.operands):
mem_op_pos = 0
else:
match = _x86_mem_op2_regex.search(self.operands)
if not match:
return False # no memory operands
mem_op_pos = match.start() + 1
if operand == _WRITES_IF_MEMREF: # any mem operand indicates write
return True
comma = self.operands.find(",")
if mem_op_pos < comma:
# "%cs:0x0(%rax,%rax,1),foo" - 1st operand is memory
# "%cs:0x0(%rax),foo" - 1st operand is memory
memory_operand = 1
elif comma < 0:
# "%cs:0x0(%rax)" - 1st operand is memory
memory_operand = 1
else:
# mem_op_pos is after comma
# "foo,%cs:0x0(%rax,%rax,1)" - 2nd operand is memory
# (It also can be a third, fourth etc operand)
memory_operand = 2
if operand == memory_operand:
return True
return False
def x86_get_instruction(self):
try:
# just "disassemble $pc" won't work if $pc doesn't point
# inside a known function
raw_instructions = gdb.execute("disassemble $pc,$pc+32", to_string=True)
except gdb.error:
# For example, if tracee already exited normally.
# Another observed case is if $pc points to unmapped area.
# We get "Python Exception <class 'gdb.error'> No registers"
return
instructions = []
current = None
for line in raw_instructions.split("\n"):
# line can be:
# "Dump of assembler code from 0xAAAA to 0xBBBB:"
# "[=>] 0x00000000004004dc[ <+0>]: push %rbp"
# (" <+0>" part is present when we run on a live process,
# on coredump it is absent)
# "End of assembler dump."
# "" (empty line)
if line.startswith("=>"):
line = line[2:]
current = len(instructions)
line = line.split(":", 1)
if len(line) < 2: # no ":"?
continue
line = line[1] # drop "foo:"
line = line.strip() # drop leading/trailing whitespace
if line:
instructions.append(line)
if current == None:
# we determined that $pc points to a bad address,
# which is an interesting fact.
return
# There can be a disasm comment: "insn op,op,op # comment";
# strip it, and whitespace on both ends:
t = instructions[current].split("#", 1)[0].strip()
self.current_instruction = t
# Strip prefixes:
while True:
t = t.split(None, 1)
self.mnemonic = t[0]
if len(t) < 2:
break
if self.mnemonic.startswith("rex."):
t = t[1]
continue
if self.mnemonic in (
"data32", "data16", "addr32", "addr16", "rex",
"cs", "ds", "es", "ss", "fs", "gs",
"lock", "rep", "repz", "repnz", "xacquire", "xrelease"
):
t = t[1]
continue
# First word isn't a prefix -> we found the insn word
self.operands = t[1]
break
self.instruction_is_pushing = (_x86_fetch_insn_from_table(self.mnemonic, _x86_pushing_instr) is not None)
self.instruction_is_division = (_x86_fetch_insn_from_table(self.mnemonic, _x86_intdiv_instr) is not None)
self.instruction_is_branch = (_x86_fetch_insn_from_table(self.mnemonic, _x86_jumping_instr) is not None)
self.instruction_is_return = (_x86_fetch_insn_from_table(self.mnemonic, _x86_return_instr) is not None)
self.instruction_is_store = self.x86_instruction_is_store()
def ppc_get_instruction(self):
try:
# just "disassemble $pc" won't work if $pc doesn't point
# inside a known function
raw_instructions = gdb.execute("disassemble $pc,$pc+32", to_string=True)
except gdb.error:
# For example, if tracee already exited normally.
# Another observed case is if $pc points to unmapped area.
# We get "Python Exception <class 'gdb.error'> No registers"
return
instructions = []
current = None
for line in raw_instructions.split("\n"):
# line can be:
# "Dump of assembler code from 0xAAAA to 0xBBBB:"
# "[=>] 0x00000000004004dc[ <+0>]: push %rbp"
# (" <+0>" part is present when we run on a live process,
# on coredump it is absent)
# "End of assembler dump."
# "" (empty line)
if line.startswith("=>"):
line = line[2:]
current = len(instructions)
line = line.split(":", 1)
if len(line) < 2: # no ":"?
continue
line = line[1] # drop "foo:"
line = line.strip() # drop leading/trailing whitespace
if line:
instructions.append(line)
if current is None:
# we determined that $pc points to a bad address,
# which is an interesting fact.
return
# There can be a disasm comment: "insn op,op,op # comment";
# strip it, and whitespace on both ends:
t = instructions[current].split("#", 1)[0].strip()
self.current_instruction = t
# Split it into mnemonic and operands
t = t.split(None, 1)
self.mnemonic = t[0]
if len(t) > 1:
self.operands = t[1]
self.instruction_is_store = self.mnemonic.startswith("st")
self.instruction_is_branch = self.mnemonic.startswith("b")
self.instruction_is_pushing = (self.instruction_is_store and "(r1)" in self.operands)
# Looks like div[o] insns on ppc don't cause exceptions
# (need to check whether, and how, FPE is generated)
#self.instruction_is_division =
# On ppc, return insn is b[cond]lr. TODO: is cond form ever used by gcc?
self.instruction_is_return = (self.mnemonic == "blr")
def get_instruction(self):
self.current_instruction = None
self.mnemonic = None
self.operands = ""
self.instruction_is_division = None
self.instruction_is_store = None
self.instruction_is_pushing = None
self.instruction_is_return = None
self.instruction_is_branch = None
try:
arch = gdb.execute("show architecture", to_string=True)
# Examples of the string we get:
# The target architecture is set automatically (currently i386)
# The target architecture is set automatically (currently i386:x86-64)
# The target architecture is set automatically (currently powerpc:common64)
if " i386" in arch:
return self.x86_get_instruction()
if " powerpc" in arch:
return self.ppc_get_instruction()
except gdb.error:
return
def get_signal(self):
self.signo = None
self.si_code = None
try:
# Requires new kernels which record complete siginfo
# in coredumps (Linux 3.9 still don't have it),
# and new gdb:
sig = gdb.parse_and_eval("$_siginfo.si_signo")
code = gdb.parse_and_eval("$_siginfo.si_code")
# Requires patched gdb:
#sig = gdb.parse_and_eval("$_signo")
#
# type(sig) = <type 'gdb.Value'>, convert to plain int:
self.signo = int(sig)
self.si_code = int(code)
except gdb.error:
# "Python Exception <class 'gdb.error'>
# Attempt to extract a component of a value that is not a structure"
# Possible reasons why $_siginfo doesn't exist:
# program is still running, program exited normally,
# we work with a coredump from an old kernel.
#
# Lets see whether we are running from the abrt and it
# provided us with signal number. Horrible hack :(
#
try:
self.signo = int(os.environ["SIGNO_OF_THE_COREDUMP"])
except KeyError:
return False
return True
#Our initial set of testing will use the list Apple included in their
#CrashWrangler announcement:
#
#Exploitable if:
# Crash on write instruction
# Crash executing invalid address
# Crash calling an invalid address
# Crash accessing an uninitialized or freed pointer as indicated by
# using the MallocScribble environment variable
# Illegal instruction exception
# Abort due to -fstack-protector, _FORTIFY_SOURCE, heap corruption
# detected
# Stack trace of crashing thread contains certain functions such as
# malloc, free, szone_error, objc_MsgSend, etc.
def is_exploitable(self):
self.exploitable_rating = 3
self.exploitable_desc = ""
# siginfo.si_code:
# If <= 0, then it's not a crash:
# SI_ASYNCNL = -60 /* asynch name lookup completion */
# SI_TKILL = -6 /* tkill (and tgkill?) */
# SI_SIGIO = -5 /* queued SIGIO */
# SI_ASYNCIO = -4 /* AIO completion */
# SI_MESGQ = -3 /* real time mesq state change */
# SI_TIMER = -2 /* timer expiration (timer_create() with SIGEV_SIGNAL) */
# SI_QUEUE = -1 /* sigqueue */
# SI_USER = 0 /* kill, sigsend */
# Crashes have si_code > 0:
# testDivideByZero: SIGFPE si_code=FPE_INTDIV(1), si_addr=0x40054a
# x86-64 opcode 0x62: SIGILL si_code=ILL_ILLOPN(2), si_addr=0x40053f
# x86-64 priv.insn.: SIGSEGV si_code=SI_KERNEL(128), si_addr=0
# testExecuteInvalid: SIGSEGV si_code=SEGV_MAPERR(1), si_addr=0x1c2404000
# testStackBufferOverflow ("ret" to bad address):
# SIGSEGV si_code=SI_KERNEL(128), si_addr=0
# testStackRecursion: SIGSEGV si_code=SEGV_MAPERR(1), si_addr=0x7fff4c216d28
# testWriteRandom: SIGSEGV si_code=SEGV_MAPERR(1), si_addr=0x1eb004004
# However:
# Keyboard signals (^C INT, ^\ QUIT, ^Z TSTP) also have si_code=SI_KERNEL.
# SIGWINCH has si_code=SI_KERNEL.
# SIGALRM from alarm(N) has si_code=SI_KERNEL.
# Surprisingly, SIGPIPE has si_code=SI_USER!
if self.si_code is not None:
# Filter out user-generated signals:
if self.si_code == -6 or self.si_code == -1: # SI_TKILL/SI_QUEUE
self.exploitable_rating = 0
self.exploitable_desc = _("Signal sent by userspace code")
return
if self.si_code < 0:
self.exploitable_rating = 0
self.exploitable_desc = _("Signal sent by timer/IO/async event")
return
# Unfortunately, this isn't reliable to flag user-sent signals:
# not only SIGPIPE, but some other kernel signals have SI_USER
# (grep kernel sources for "send_sig(sig, ..., 0)").
# At least we know it's not a crash.
if self.si_code == 0: # SI_USER
self.exploitable_rating = 0
self.exploitable_desc = _("Signal has siginfo.si_code = SI_USER")
# Special case (a kernel buglet?)
if self.signo == signal.SIGPIPE:
self.exploitable_desc = _("Signal due to write to closed pipe")
return
# And kernel-generated ones:
if self.si_code == 0x80: # SI_KERNEL
if self.signo in (signal.SIGINT, signal.SIGQUIT, signal.SIGTSTP):
self.exploitable_rating = 0
self.exploitable_desc = _("Signal sent by keyboard")
return
if self.signo in (signal.SIGTTIN, signal.SIGTTOU, signal.SIGHUP):
self.exploitable_rating = 0
self.exploitable_desc = _("Job control signal sent by kernel")
return
if self.signo == signal.SIGWINCH:
self.exploitable_rating = 0
self.exploitable_desc = _("Signal sent by window resize")
return
if self.signo == signal.SIGALRM:
self.exploitable_rating = 0
self.exploitable_desc = _("Signal sent by alarm(N) expiration")
return
# else: Can't rule out "crash" signal: may be FPE/ILL/BUS/SEGV.
# Fall through into signo/insn analysis.
# else: We are here if signal was not from user and not SI_KERNEL.
# Fall through into signo/insn analysis.
# else: We are here if si_code isn't known.
# Fall through into signo/insn analysis.
# Guessing here... it might be kill(2) as well.
# Should I add "Likely ..." to the descriptions?
if self.signo in (signal.SIGINT, signal.SIGQUIT, signal.SIGTSTP):
self.exploitable_rating = 0
self.exploitable_desc = _("Signal sent by keyboard")
return
if self.signo in (signal.SIGTTIN, signal.SIGTTOU, signal.SIGHUP):
self.exploitable_rating = 0
self.exploitable_desc = _("Job control signal sent by kernel")
return
if self.signo == signal.SIGPIPE:
self.exploitable_rating = 0
self.exploitable_desc = _("Signal due to write to broken pipe")
return
if self.signo == signal.SIGWINCH:
self.exploitable_rating = 0
self.exploitable_desc = _("Signal sent by window resize")
return
if self.signo == signal.SIGALRM:
self.exploitable_rating = 0
self.exploitable_desc = _("Signal sent by alarm(N) expiration")
return
# Which signals can coredump?
# SIGABRT Abort signal from abort(3)
# SIGQUIT Quit from keyboard
# SIGXCPU CPU time limit exceeded
# SIGXFSZ File size limit exceeded
# SIGTRAP Trace/breakpoint trap
# SIGSYS Bad argument to routine (SVr4)
# SIGFPE Floating point exception
# SIGILL Illegal Instruction
# SIGSEGV Invalid memory reference
# SIGBUS Bus error (bad memory access)
if self.signo == signal.SIGABRT:
self.exploitable_rating = 0
self.exploitable_desc = _("ABRT signal (abort() was called?)")
return
# Already handled above:
#if self.signo == signal.SIGQUIT:
# self.exploitable_rating = 0
# self.exploitable_desc = _("QUIT signal (Ctrl-\\ pressed?)")
# return
if self.signo == signal.SIGXCPU:
self.exploitable_rating = 0
self.exploitable_desc = _("XCPU signal (over CPU time limit)")
return
if self.signo == signal.SIGXFSZ:
self.exploitable_rating = 0
self.exploitable_desc = _("XFSZ signal (over file size limit)")
return
if self.signo == signal.SIGTRAP:
self.exploitable_rating = 0
self.exploitable_desc = _("TRAP signal (can be a bug in a debugger/tracer)")
return
if self.signo == signal.SIGSYS:
self.exploitable_rating = 1
self.exploitable_desc = _("SYS signal (unknown syscall was called?)")
return
if self.signo == signal.SIGFPE:
self.exploitable_rating = 1
self.exploitable_desc = _("Arithmetic exception")
# 1 is FPE_INTDIV
if self.si_code == 1 or self.instruction_is_division:
self.exploitable_rating = 0
self.exploitable_desc = _("Division by zero")
return
if self.signo == signal.SIGILL:
self.exploitable_rating = 5
self.exploitable_desc = _("Illegal instruction (jump to a random address?)")
return
if self.signo != signal.SIGSEGV and self.signo != signal.SIGBUS:
self.exploitable_rating = 2
# Pity that we can't give a more descriptive explanation
self.exploitable_desc = _("Non-crash related signal")
return
if self.instruction_is_pushing:
self.exploitable_rating = 4
self.exploitable_desc = _("Stack overflow")
return
if self.instruction_is_store:
self.exploitable_rating = 6
self.exploitable_desc = _("Write to an invalid address")
return
if self.instruction_is_return:
self.exploitable_rating = 7
self.exploitable_desc = _("Subroutine return to an invalid address (corrupted stack?)")
return
# Note: we check "ret" first, _then_ jumps.
# Corrupted stack is different from corrupted data.
if self.instruction_is_branch:
self.exploitable_rating = 6
self.exploitable_desc = _("Jump to an invalid address")
return
if not self.current_instruction:
self.exploitable_rating = 6
self.exploitable_desc = _("Jump to an invalid address")
return
if self.signo == signal.SIGBUS:
self.exploitable_rating = 5
self.exploitable_desc = _("Access past the end of mapped file, invalid address, unaligned access, etc")
return
# default values remain (e.g. description is "")
class AbrtExploitable(gdb.Command):
"Analyze a crash to determine exploitability"
def __init__(self):
super(AbrtExploitable, self).__init__(
"abrt-exploitable",
gdb.COMMAND_SUPPORT, # command class
gdb.COMPLETE_NONE, # completion method
False # => it's not a prefix command
)
init_gettext()
# Called when the command is invoked from GDB
def invoke(self, args, from_tty):
si = SignalAndInsn()
if not si.get_signal():
sys.stderr.write(_("Can't get signal no and do exploitability analysis\n"))
return
si.get_instruction()
min_rating = 0
if args:
args = args.split(None, 1)
min_rating = int(args[0])
si.is_exploitable()
if si.exploitable_desc:
if si.exploitable_rating >= min_rating:
f = sys.stdout
if args and len(args) > 1:
f = open(args[1], 'w')
f.write(_("Likely crash reason: ") + si.exploitable_desc + "\n")
f.write(_("Exploitable rating (0-9 scale): ") + str(si.exploitable_rating) + "\n")
if si.current_instruction:
f.write(_("Current instruction: ") + si.current_instruction + "\n")
else:
sys.stderr.write(_("Exploitability analysis came up empty\n"))
AbrtExploitable()