[ABRT PATCH 1/2 v2] Add gdb python plugin which analyzes coredump for vulnerability
by Denys Vlasenko
Usage:
Run abrt-action-analyze-vulnerability in a directory which contains
./coredump file.
If crash looks exploitable, the tool creates ./exploitable file.
Example of such a file:
"""
Likely crash reason: Write to an invalid address
Exploitable rating (0-9 scale): 6
"""
This patch adds abrt-action-analyze-vulnerability invocation to
"EVENT=post-create analyzer=CCpp".
V2: switched to regexps; i18n of messages
TODO:
* instruction analyzer is x86 specific now, make it per-arch
Signed-off-by: Denys Vlasenko <dvlasenk(a)redhat.com>
---
src/plugins/Makefile.am | 10 +-
src/plugins/abrt-action-analyze-vulnerability | 41 ++
src/plugins/abrt-gdb-exploitable | 517 +++++++++++++++++++++
src/plugins/ccpp_event.conf | 5 +-
tests/abrt-exploitable/.gitignore | 10 +
tests/abrt-exploitable/Makefile | 67 +++
tests/abrt-exploitable/testDivideByZero.c | 10 +
tests/abrt-exploitable/testExecuteInvalid.c | 17 +
.../abrt-exploitable/testFloatingPointException.c | 10 +
tests/abrt-exploitable/testReadNull.c | 12 +
tests/abrt-exploitable/testReadRandom.c | 14 +
tests/abrt-exploitable/testSignalAbort.c | 10 +
tests/abrt-exploitable/testSignalIll.c | 10 +
tests/abrt-exploitable/testStackBufferOverflow.c | 23 +
tests/abrt-exploitable/testStackRecursion.c | 15 +
tests/abrt-exploitable/testWriteRandom.c | 15 +
16 files changed, 783 insertions(+), 3 deletions(-)
create mode 100755 src/plugins/abrt-action-analyze-vulnerability
create mode 100755 src/plugins/abrt-gdb-exploitable
create mode 100644 tests/abrt-exploitable/.gitignore
create mode 100644 tests/abrt-exploitable/Makefile
create mode 100644 tests/abrt-exploitable/testDivideByZero.c
create mode 100644 tests/abrt-exploitable/testExecuteInvalid.c
create mode 100644 tests/abrt-exploitable/testFloatingPointException.c
create mode 100644 tests/abrt-exploitable/testReadNull.c
create mode 100644 tests/abrt-exploitable/testReadRandom.c
create mode 100644 tests/abrt-exploitable/testSignalAbort.c
create mode 100644 tests/abrt-exploitable/testSignalIll.c
create mode 100644 tests/abrt-exploitable/testStackBufferOverflow.c
create mode 100644 tests/abrt-exploitable/testStackRecursion.c
create mode 100644 tests/abrt-exploitable/testWriteRandom.c
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index 767c045..20e0297 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -3,6 +3,7 @@
bin_SCRIPTS = \
abrt-action-install-debuginfo \
abrt-action-analyze-core \
+ abrt-action-analyze-vulnerability \
abrt-action-analyze-vmcore \
abrt-action-list-dsos \
abrt-action-perform-ccpp-analysis \
@@ -29,9 +30,12 @@ bin_PROGRAMS += \
abrt-bodhi
endif
-libexec_PROGRAMS = abrt-action-install-debuginfo-to-abrt-cache
+libexec_PROGRAMS = \
+ abrt-action-install-debuginfo-to-abrt-cache
-libexec_SCRIPTS = abrt-action-ureport
+libexec_SCRIPTS = \
+ abrt-action-ureport \
+ abrt-gdb-exploitable
#dist_pluginsconf_DATA = Python.conf
@@ -68,6 +72,7 @@ PYTHON_FILES = \
abrt-action-install-debuginfo.in \
abrt-action-list-dsos \
abrt-action-analyze-core \
+ abrt-action-analyze-vulnerability \
abrt-action-analyze-vmcore.in \
abrt-action-perform-ccpp-analysis.in
@@ -84,6 +89,7 @@ EXTRA_DIST = \
abrt-action-analyze-vmcore \
abrt-action-save-kernel-data \
abrt-action-ureport \
+ abrt-gdb-exploitable \
https-utils.h \
post_report.xml.in \
abrt-action-analyze-ccpp-local
diff --git a/src/plugins/abrt-action-analyze-vulnerability b/src/plugins/abrt-action-analyze-vulnerability
new file mode 100755
index 0000000..aa63ff0
--- /dev/null
+++ b/src/plugins/abrt-action-analyze-vulnerability
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# Do we have the tools we need?
+# If no, exit silently.
+type gdb >/dev/null 2>&1 || exit 0
+type eu-readelf >/dev/null 2>&1 || exit 0
+
+# Do we have coredump?
+test -r coredump || {
+ echo 'No file "coredump" in current directory' >&2
+ exit 1
+}
+
+# Find "cursig: N" and extract N.
+# This gets used by abrt-exploitable as a fallback
+# if gdb and/or kernel is uncooperative.
+# "grep -m1": take the first match (on Linux, every thread has its own
+# prstatus struct in the coredump, but the signal number which killed us
+# must be the same in all these structs).
+SIGNO_OF_THE_COREDUMP=$(eu-readelf -n coredump | grep -m1 -o 'cursig: *[0-9]*' | sed 's/[^0-9]//g')
+export SIGNO_OF_THE_COREDUMP
+
+# Run gdb, hiding its messages. Example:
+# Missing separate debuginfo for the main executable file
+# Core was generated by...
+# Program terminated with signal 11, Segmentation fault.
+# #0 0x09fa5348 in ?? ()
+# We don't want to see all this.
+# abrt-exploitable plugin is instructed to create ./exploitable file
+# with explanation if severity is >= 4
+GDBOUT=$(
+gdb --batch \
+ -ex 'python execfile("/usr/libexec/abrt-gdb-exploitable")' \
+ -ex 'core-file ./coredump' \
+ -ex 'abrt-exploitable 4 ./exploitable' \
+ 2>&1 \
+) && exit 0
+
+# There was an error. Show the messages.
+printf "Error while running gdb:\n%s\n" "$GDBOUT"
+exit 1
diff --git a/src/plugins/abrt-gdb-exploitable b/src/plugins/abrt-gdb-exploitable
new file mode 100755
index 0000000..474c0e4
--- /dev/null
+++ b/src/plugins/abrt-gdb-exploitable
@@ -0,0 +1,517 @@
+#!/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 gdb
+
+GETTEXT_PROGNAME = "abrt"
+_ = gettext.lgettext
+
+_WRITES_ALWAYS = -1
+_WRITES_IF_MEMREF = -2
+
+_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/32736...
+
+ #"[v]movXXX" - special-cased in the code
+ "mov":2
+
+ # Note: stack-writing instructions are omitted
+}
+
+_pushing_instr = (
+ "push",
+ "pusha",
+ "pushf",
+ "enter",
+ "call",
+ "lcall"
+)
+
+_intdiv_instr = ("div", "idiv")
+
+_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
+_return_instr = ("iret", "lret", "ret")
+
+def _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 Signal_and_insn:
+ def get_signal(self):
+ self.signo = 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")
+ # Requires patched gdb:
+ #sig = gdb.parse_and_eval("$_signo")
+ #
+ # type(sig) = <type 'gdb.Value'>, convert to plain int:
+ self.signo = int(sig)
+ 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
+
+ def get_instruction(self):
+ self.current_instruction = None
+ self.mnemonic = None
+ self.operands = ""
+ try:
+ # just "disassemble $pc" won't work if $pc doesn't point
+ # inside a known function
+ 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
+
+ raw_instructions = instructions
+ 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:
+ # not False! 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
+
+ mem_op1_regex = re.compile("^((-?0x)|[(])")
+ mem_op2_regex = re.compile("[,:]((-?0x)|[(])")
+
+ def instruction_is_writing(self):
+ operand = _fetch_insn_from_table(self.mnemonic, _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 Signal_and_insn.mem_op1_regex.search(self.operands):
+ mem_op_pos = 0
+ else:
+ match = Signal_and_insn.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 instruction_is_pushing(self):
+ if _fetch_insn_from_table(self.mnemonic, _pushing_instr):
+ return True
+ return False
+
+ def instruction_is_division(self):
+ if _fetch_insn_from_table(self.mnemonic, _intdiv_instr):
+ return True
+ return False
+
+ def instruction_is_jumping(self):
+ if _fetch_insn_from_table(self.mnemonic, _jumping_instr):
+ return True
+ return False
+
+ def instruction_is_return(self):
+ if _fetch_insn_from_table(self.mnemonic, _return_instr):
+ return True
+ return False
+
+ #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 = ""
+
+ if 0:
+ pass
+ # 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)
+ elif self.signo == signal.SIGABRT:
+ self.exploitable_rating = 0
+ self.exploitable_desc = _("ABRT signal (abort() was called?)")
+ elif self.signo == signal.SIGQUIT:
+ self.exploitable_rating = 0
+ self.exploitable_desc = _("QUIT signal (Ctrl-\\ pressed?)")
+ elif self.signo == signal.SIGXCPU:
+ self.exploitable_rating = 0
+ self.exploitable_desc = _("XCPU signal (over CPU time limit)")
+ elif self.signo == signal.SIGXFSZ:
+ self.exploitable_rating = 0
+ self.exploitable_desc = _("XFSZ signal (over file size limit)")
+ elif self.signo == signal.SIGTRAP:
+ self.exploitable_rating = 0
+ self.exploitable_desc = _("TRAP signal (can be a bug in a debugger/tracer)")
+ elif self.signo == signal.SIGSYS:
+ self.exploitable_rating = 1
+ self.exploitable_desc = _("SYS signal (unknown/masked syscall was called?)")
+
+ elif self.signo == signal.SIGFPE:
+ self.exploitable_rating = 1
+ self.exploitable_desc = _("Arithmetic exception")
+ if self.instruction_is_division():
+ self.exploitable_rating = 0
+ self.exploitable_desc = _("Division by zero")
+ elif self.signo == signal.SIGILL:
+ self.exploitable_rating = 5
+ self.exploitable_desc = _("Illegal instruction (jump to a random address?)")
+
+ # TODO: check that sig is SIGSEGV/SIGBUS?
+
+ elif self.instruction_is_pushing():
+ self.exploitable_rating = 4
+ self.exploitable_desc = _("Stack overflow")
+ elif self.instruction_is_writing():
+ self.exploitable_rating = 6
+ self.exploitable_desc = _("Write to an invalid address")
+ elif self.instruction_is_return():
+ self.exploitable_rating = 7
+ self.exploitable_desc = _("Subroutine return to an invalid address (corrupted stack?)")
+ # Note: we check "ret" first, _then_ jumps.
+ # Corrupted stack is different from corrupted data.
+ elif self.instruction_is_jumping():
+ self.exploitable_rating = 6
+ self.exploitable_desc = _("Jump to an invalid address")
+ elif not self.current_instruction:
+ self.exploitable_rating = 6
+ self.exploitable_desc = _("Jump to an invalid address")
+ elif self.signo == signal.SIGBUS:
+ self.exploitable_rating = 5
+ self.exploitable_desc = _("Access past the end of mapped file, invalid address, unaligned access, etc")
+ #elif self.signo = signal.SIGfoo:
+
+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
+ )
+
+ # Called when the command is invoked from GDB
+ def invoke(self, args, from_tty):
+ si = Signal_and_insn()
+ 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")
+ else:
+ sys.stderr.write(_("Exploitability analysis came up empty\n"))
+
+AbrtExploitable()
diff --git a/src/plugins/ccpp_event.conf b/src/plugins/ccpp_event.conf
index dfc4908..aa8cdb3 100644
--- a/src/plugins/ccpp_event.conf
+++ b/src/plugins/ccpp_event.conf
@@ -15,10 +15,13 @@ EVENT=post-create analyzer=CCpp
exit 1
fi
# Try generating backtrace, if it fails we can still use
- # the UUID generated by abrt-action-analyze-c
+ # the hash generated by abrt-action-analyze-c
##satyr migration:
#satyr abrt-create-core-stacktrace "$DUMP_DIR"
abrt-action-generate-core-backtrace
+ # Run GDB plugin to see if crash looks exploitable
+ abrt-action-analyze-vulnerability
+ # Generate hash
abrt-action-analyze-c &&
abrt-action-list-dsos -m maps -o dso_list &&
(
diff --git a/tests/abrt-exploitable/.gitignore b/tests/abrt-exploitable/.gitignore
new file mode 100644
index 0000000..b0c3b29
--- /dev/null
+++ b/tests/abrt-exploitable/.gitignore
@@ -0,0 +1,10 @@
+testDivideByZero
+testExecuteInvalid
+testFloatingPointException
+testReadNull
+testReadRandom
+testSignalAbort
+testSignalIll
+testStackBufferOverflow
+testStackRecursion
+testWriteRandom
diff --git a/tests/abrt-exploitable/Makefile b/tests/abrt-exploitable/Makefile
new file mode 100644
index 0000000..6672fe3
--- /dev/null
+++ b/tests/abrt-exploitable/Makefile
@@ -0,0 +1,67 @@
+# Copyright (C) 2010, 2011 Red Hat, Inc.
+#
+# This file is part of ABRT.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+CFLAGS=-g
+
+TESTS=\
+ testDivideByZero\
+ testReadNull\
+ testStackRecursion\
+ testReadRandom\
+ testSignalAbort\
+ testSignalIll\
+ testWriteRandom\
+ testExecuteInvalid\
+ testStackBufferOverflow\
+ testFloatingPointException\
+
+all: $(TESTS)
+
+clean:
+ rm -f $(TESTS)
+
+testlive:
+ for t in $(TESTS); do \
+ echo "====="; \
+ echo "Test: $$t"; \
+ gdb --batch \
+ -ex 'python execfile("../../src/plugins/abrt-gdb-exploitable")' \
+ -ex 'run' \
+ -ex 'disas $$pc-16,$$pc+16' \
+ -ex 'abrt-exploitable' \
+ -ex 'cont' \
+ -ex 'quit' \
+ ./$$t; \
+ done 2>&1 | tee testlive.log
+
+testcore:
+ rm ./core* 2>/dev/null; \
+ ulimit -c unlimited; \
+ for t in $(TESTS); do \
+ echo "====="; \
+ echo "Test: $$t"; \
+ ./$$t && { echo "No crash???"; continue; }; \
+ mv core* core || { echo "No corefile???"; continue; }; \
+ gdb --batch \
+ -ex 'python execfile("../../src/plugins/abrt-gdb-exploitable")' \
+ -ex 'core ./core' \
+ -ex 'disas $$pc-16,$$pc+16' \
+ -ex 'abrt-exploitable' \
+ -ex 'quit' \
+ ; \
+ rm core; \
+ done 2>&1 | tee testcore.log
diff --git a/tests/abrt-exploitable/testDivideByZero.c b/tests/abrt-exploitable/testDivideByZero.c
new file mode 100644
index 0000000..06d1551
--- /dev/null
+++ b/tests/abrt-exploitable/testDivideByZero.c
@@ -0,0 +1,10 @@
+/*
+ * Test a divide by zero error
+ * This error is not exploitable
+ */
+
+#include <stdio.h>
+
+int main(int argc, char *argv[]) {
+ printf("%d\n", 7/0);
+}
diff --git a/tests/abrt-exploitable/testExecuteInvalid.c b/tests/abrt-exploitable/testExecuteInvalid.c
new file mode 100644
index 0000000..86ce515
--- /dev/null
+++ b/tests/abrt-exploitable/testExecuteInvalid.c
@@ -0,0 +1,17 @@
+/*
+ * Test a crash attempting to execute an invalid address
+ * This error is exploitable
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+int (*function_pointer)();
+
+int main(int argc, char *argv[]) {
+ char *a;
+ a = malloc(1024);
+ a = (size_t)a * 1024; // This should put us well outside the valid memory range
+ function_pointer = a;
+ function_pointer();
+}
diff --git a/tests/abrt-exploitable/testFloatingPointException.c b/tests/abrt-exploitable/testFloatingPointException.c
new file mode 100644
index 0000000..7931914
--- /dev/null
+++ b/tests/abrt-exploitable/testFloatingPointException.c
@@ -0,0 +1,10 @@
+/*
+ * Test the floating point exception signal
+ * This error is exploitable
+ */
+
+#include <signal.h>
+
+int main(int argc, char *argv[]) {
+ raise(SIGFPE);
+}
diff --git a/tests/abrt-exploitable/testReadNull.c b/tests/abrt-exploitable/testReadNull.c
new file mode 100644
index 0000000..548cf73
--- /dev/null
+++ b/tests/abrt-exploitable/testReadNull.c
@@ -0,0 +1,12 @@
+/*
+ * Test a NULL read
+ * This error is not exploitable
+ */
+
+#include <stdio.h>
+
+int main(int argc, char *argv[]) {
+ char *a;
+ a = 0x0;
+ puts(a);
+}
diff --git a/tests/abrt-exploitable/testReadRandom.c b/tests/abrt-exploitable/testReadRandom.c
new file mode 100644
index 0000000..8493078
--- /dev/null
+++ b/tests/abrt-exploitable/testReadRandom.c
@@ -0,0 +1,14 @@
+/*
+ * Test a crash attempting to read invalid memory
+ * This error is not exploitable
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[]) {
+ char *a;
+ a = malloc(1024);
+ a = (size_t)a * 1024; // This should put us well outside the valid memory range
+ printf("%s\n", a);
+}
diff --git a/tests/abrt-exploitable/testSignalAbort.c b/tests/abrt-exploitable/testSignalAbort.c
new file mode 100644
index 0000000..fc41227
--- /dev/null
+++ b/tests/abrt-exploitable/testSignalAbort.c
@@ -0,0 +1,10 @@
+/*
+ * Test the abort signal
+ * This error is not exploitable
+ */
+
+#include <signal.h>
+
+int main(int argc, char *argv[]) {
+ raise(SIGABRT);
+}
diff --git a/tests/abrt-exploitable/testSignalIll.c b/tests/abrt-exploitable/testSignalIll.c
new file mode 100644
index 0000000..7783263
--- /dev/null
+++ b/tests/abrt-exploitable/testSignalIll.c
@@ -0,0 +1,10 @@
+/*
+ * Test the illegal instruction signal
+ * This error is exploitable
+ */
+
+#include <signal.h>
+
+int main(int argc, char *argv[]) {
+ raise(SIGILL);
+}
diff --git a/tests/abrt-exploitable/testStackBufferOverflow.c b/tests/abrt-exploitable/testStackBufferOverflow.c
new file mode 100644
index 0000000..fcbd95f
--- /dev/null
+++ b/tests/abrt-exploitable/testStackBufferOverflow.c
@@ -0,0 +1,23 @@
+/*
+ * Test a stack buffer overflow
+ * This test could be exploitable (it needs further analysis)
+ */
+
+#include <stdio.h>
+
+int i;
+
+int my_function() {
+ char a[2];
+
+ for (i = 0; i < 1024; i++) {
+ a[i] = 'A';
+ }
+ printf("%s\n", a);
+}
+
+int main(int argc, char *argv[]) {
+ my_function();
+ my_function();
+ return 0;
+}
diff --git a/tests/abrt-exploitable/testStackRecursion.c b/tests/abrt-exploitable/testStackRecursion.c
new file mode 100644
index 0000000..dddf583
--- /dev/null
+++ b/tests/abrt-exploitable/testStackRecursion.c
@@ -0,0 +1,15 @@
+/*
+ * Test a crash from stack recursion
+ * This error is not exploitable
+ */
+
+#include <stdio.h>
+
+void my_function() {
+ char a[1024];
+ my_function();
+}
+
+int main(int argc, char *argv[]) {
+ my_function();
+}
diff --git a/tests/abrt-exploitable/testWriteRandom.c b/tests/abrt-exploitable/testWriteRandom.c
new file mode 100644
index 0000000..812f94c
--- /dev/null
+++ b/tests/abrt-exploitable/testWriteRandom.c
@@ -0,0 +1,15 @@
+/*
+ * Test a crash attempting to write invalid memory
+ * This error is exploitable
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+int main(int argc, char *argv[]) {
+ char *a;
+ char b[] = "pwnt";
+ a = malloc(1024);
+ a = (size_t)a * 1024; // This should put us well outside the valid memory range
+ strcpy(a, b);
+}
--
1.8.1.4
11 years
Release of FAF 0.9 beta1
by Michal Toman
Hi,
many of you have recently shown an interest in deploying FAF. As we were
in the process of rewriting the core to make it easily pluginable for
3rd parties, my replies were mostly about waiting until a beta with very
basic functionality is finished.
Good news everyone - the day is here. I believe a brief introduction
into the design and basic ideas is covered on project wiki [1]. The wiki
also contains an installation guide [2] for the default use-case
(Fedora). Recommended host operating systems are Fedora 18, Fedora 19,
Fedora rawhide or RHEL6 - we have pre-built packages [3] for them and
have really tried to use them as hosts. Others may (but are not
guaranteed to) work.
The following functionality should be covered:
* Installing the server from scratch
* Pulling releases of Fedora
* Pulling components for a Fedora release
* Importing a yum repository and downloading its packages into storage
* Saving uReports dropped into /var/spool/faf/reports/incoming
* Showing the data in the webUI
The following functionality is *not* yet implemented:
* Retracing symbols
* Anything related to Red Hat Bugzilla
* Receiving reports from webUI (http://server/faf/reports/new)
* Clustering and creating problems
* Knowledgebase
We are focusing to finish all of these features ASAP and release a beta2
We would like to encourage you to send RFEs, ask questions or file bugs
on everything (code, API, documentation, usability...). Your help is
appreciated.
Michal & ABRT Team
[1] https://github.com/abrt/faf/wiki
[2] https://github.com/abrt/faf/wiki/Installation-Guide
[3] http://mtoman.fedorapeople.org/faf/
11 years
[SATYR PATCH] Fix broken sr_stacktrace_parse
by Martin Milata
Did not work for any other crash types than "core" due to the wrong
function being called.
Signed-off-by: Martin Milata <mmilata(a)redhat.com>
---
lib/gdb_stacktrace.c | 3 ++-
lib/generic_stacktrace.h | 12 ++++++++----
lib/java_stacktrace.c | 3 ++-
lib/koops_stacktrace.c | 3 ++-
lib/python_stacktrace.c | 3 ++-
tests/java_stacktrace.at | 14 ++++++++++++++
tests/koops_stacktrace.at | 17 +++++++++++++++++
7 files changed, 47 insertions(+), 8 deletions(-)
diff --git a/lib/gdb_stacktrace.c b/lib/gdb_stacktrace.c
index 6e4dc29..b77898b 100644
--- a/lib/gdb_stacktrace.c
+++ b/lib/gdb_stacktrace.c
@@ -43,10 +43,11 @@ gdb_return_null(struct sr_stacktrace *stacktrace)
DEFINE_THREADS_FUNC(gdb_threads, struct sr_gdb_stacktrace)
DEFINE_SET_THREADS_FUNC(gdb_set_threads, struct sr_gdb_stacktrace)
+DEFINE_PARSE_WRAPPER_FUNC(gdb_parse, SR_REPORT_GDB)
struct stacktrace_methods gdb_stacktrace_methods =
{
- .parse = (parse_fn_t) stacktrace_parse_wrapper,
+ .parse = (parse_fn_t) gdb_parse,
.parse_location = (parse_location_fn_t) sr_gdb_stacktrace_parse,
.to_short_text = (to_short_text_fn_t) sr_gdb_stacktrace_to_short_text,
.to_json = (to_json_fn_t) gdb_return_null,
diff --git a/lib/generic_stacktrace.h b/lib/generic_stacktrace.h
index 2c1f56d..bb6ba9f 100644
--- a/lib/generic_stacktrace.h
+++ b/lib/generic_stacktrace.h
@@ -54,7 +54,14 @@ extern struct stacktrace_methods core_stacktrace_methods, python_stacktrace_meth
#define DEFINE_SET_THREADS_FUNC(name, concrete_t) \
DEFINE_SETTER(name, threads, struct sr_stacktrace, concrete_t, struct sr_thread)
-/* XXX generic functions */
+#define DEFINE_PARSE_WRAPPER_FUNC(name, type) \
+ static struct sr_stacktrace * \
+ name(const char **input, char **error_message) \
+ { \
+ return stacktrace_parse_wrapper(type, input, error_message); \
+ }
+
+/* generic functions */
struct sr_stacktrace *
stacktrace_parse_wrapper(enum sr_report_type type, const char **input, char **error_message);
@@ -63,6 +70,3 @@ stacktrace_to_short_text(struct sr_stacktrace *stacktrace, int max_frames);
struct sr_thread *
stacktrace_one_thread_only(struct sr_stacktrace *stacktrace);
-
-struct sr_stacktrace *
-stacktrace_parse_wrapper(enum sr_report_type type, const char **input, char **error_message);
diff --git a/lib/java_stacktrace.c b/lib/java_stacktrace.c
index 0a7b583..b3231e7 100644
--- a/lib/java_stacktrace.c
+++ b/lib/java_stacktrace.c
@@ -33,10 +33,11 @@
DEFINE_THREADS_FUNC(java_threads, struct sr_java_stacktrace)
DEFINE_SET_THREADS_FUNC(java_set_threads, struct sr_java_stacktrace)
+DEFINE_PARSE_WRAPPER_FUNC(java_parse, SR_REPORT_JAVA)
struct stacktrace_methods java_stacktrace_methods =
{
- .parse = (parse_fn_t) stacktrace_parse_wrapper,
+ .parse = (parse_fn_t) java_parse,
.parse_location = (parse_location_fn_t) sr_java_stacktrace_parse,
.to_short_text = (to_short_text_fn_t) stacktrace_to_short_text,
.to_json = (to_json_fn_t) sr_java_stacktrace_to_json,
diff --git a/lib/koops_stacktrace.c b/lib/koops_stacktrace.c
index 121c330..5ac3fdf 100644
--- a/lib/koops_stacktrace.c
+++ b/lib/koops_stacktrace.c
@@ -58,6 +58,7 @@ struct sr_taint_flag sr_flags[] = {
DEFINE_FRAMES_FUNC(koops_frames, struct sr_koops_stacktrace)
DEFINE_SET_FRAMES_FUNC(koops_set_frames, struct sr_koops_stacktrace)
+DEFINE_PARSE_WRAPPER_FUNC(koops_parse, SR_REPORT_KERNELOOPS)
struct thread_methods koops_thread_methods =
{
@@ -71,7 +72,7 @@ struct thread_methods koops_thread_methods =
struct stacktrace_methods koops_stacktrace_methods =
{
- .parse = (parse_fn_t) stacktrace_parse_wrapper,
+ .parse = (parse_fn_t) koops_parse,
.parse_location = (parse_location_fn_t) sr_koops_stacktrace_parse,
.to_short_text = (to_short_text_fn_t) stacktrace_to_short_text,
.to_json = (to_json_fn_t) sr_koops_stacktrace_to_json,
diff --git a/lib/python_stacktrace.c b/lib/python_stacktrace.c
index 89a090c..3c1d030 100644
--- a/lib/python_stacktrace.c
+++ b/lib/python_stacktrace.c
@@ -36,6 +36,7 @@
DEFINE_FRAMES_FUNC(python_frames, struct sr_python_stacktrace)
DEFINE_SET_FRAMES_FUNC(python_set_frames, struct sr_python_stacktrace)
+DEFINE_PARSE_WRAPPER_FUNC(python_parse, SR_REPORT_PYTHON)
struct thread_methods python_thread_methods =
{
@@ -49,7 +50,7 @@ struct thread_methods python_thread_methods =
struct stacktrace_methods python_stacktrace_methods =
{
- .parse = (parse_fn_t) stacktrace_parse_wrapper,
+ .parse = (parse_fn_t) python_parse,
.parse_location = (parse_location_fn_t) sr_python_stacktrace_parse,
.to_short_text = (to_short_text_fn_t) stacktrace_to_short_text,
.to_json = (to_json_fn_t) sr_python_stacktrace_to_json,
diff --git a/tests/java_stacktrace.at b/tests/java_stacktrace.at
index d38074c..be55703 100644
--- a/tests/java_stacktrace.at
+++ b/tests/java_stacktrace.at
@@ -80,6 +80,7 @@ AT_TESTFUN([sr_java_stacktrace_parse],
#include "java/thread.h"
#include "java/frame.h"
#include "location.h"
+#include "stacktrace.h"
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
@@ -102,6 +103,19 @@ check(char *input,
{
assert(*input == '\0');
assert(0 == sr_java_stacktrace_cmp(stacktrace, expected_stacktrace));
+
+ /* check sr_stacktrace_parse */
+ char *error;
+ input = old_input;
+ struct sr_stacktrace *stacktrace2 = sr_stacktrace_parse(SR_REPORT_JAVA, &input, &error);
+ assert(stacktrace2);
+ char *json1 = sr_stacktrace_to_json(stacktrace);
+ char *json2 = sr_stacktrace_to_json(stacktrace2);
+ assert(json1 && json2 && 0 == strcmp(json1, json2));
+ free(json1);
+ free(json2);
+ sr_stacktrace_free(stacktrace2);
+
sr_java_stacktrace_free(stacktrace);
}
else
diff --git a/tests/koops_stacktrace.at b/tests/koops_stacktrace.at
index 86e694f..291b004 100644
--- a/tests/koops_stacktrace.at
+++ b/tests/koops_stacktrace.at
@@ -101,6 +101,7 @@ AT_TESTFUN([sr_koops_stacktrace_parse],
#include "location.h"
#include "utils.h"
#include "thread.h"
+#include "stacktrace.h"
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
@@ -177,6 +178,22 @@ check(const char *path,
}
assert(actual_module_count == module_count);
+ /* test parsing via sr_stacktrace_parse */
+ input = full_input;
+ char *error;
+ struct sr_stacktrace *stacktrace2 = sr_stacktrace_parse(SR_REPORT_KERNELOOPS, &input, &error);
+
+ assert(stacktrace2);
+ reason = sr_stacktrace_get_reason(stacktrace);
+ reason2 = sr_stacktrace_get_reason(stacktrace2);
+
+ assert(reason && reason2);
+ assert(0 == strcmp(reason, reason2));
+
+ free(reason);
+ free(reason2);
+ sr_stacktrace_free(stacktrace2);
+
sr_koops_stacktrace_free(stacktrace);
free(full_input);
}
--
1.7.11.7
11 years
[SATYR PATCH] Use build id when comparing core frames for distance
by Martin Milata
The function names might not be known and the fingerprints are
unreliable. On the other hand, we should always have build ID, which is
also very reliable.
Fixes #91.
Signed-off-by: Martin Milata <mmilata(a)redhat.com>
---
lib/core_frame.c | 36 +++++++++++++++++++++++++-----------
1 file changed, 25 insertions(+), 11 deletions(-)
diff --git a/lib/core_frame.c b/lib/core_frame.c
index a2d1ef0..1457a11 100644
--- a/lib/core_frame.c
+++ b/lib/core_frame.c
@@ -176,19 +176,33 @@ int
sr_core_frame_cmp_distance(struct sr_core_frame *frame1,
struct sr_core_frame *frame2)
{
- /* Function name. */
- int function_name = sr_strcmp0(frame1->function_name,
- frame2->function_name);
- if (function_name != 0)
- return function_name;
+ /* If both function names are present, compare those. */
+ if (frame1->function_name && frame2->function_name)
+ return sr_strcmp(frame1->function_name, frame2->function_name);
- /* Fingerprint. */
- int fingerprint = sr_strcmp0(frame1->fingerprint,
- frame2->fingerprint);
- if (fingerprint != 0)
- return fingerprint;
+ /* Try matching build ID and offset. */
+ int build_id = sr_strcmp0(frame1->build_id,
+ frame2->build_id);
- return 0;
+ int build_id_offset = frame1->build_id_offset - frame2->build_id_offset;
+
+ if (build_id == 0 && build_id_offset == 0)
+ return 0;
+
+ /* Build ID mismatch - this might still mean that the frames are the same
+ * but from a different build. Try falling back to fingerprints if those
+ * are present.
+ */
+ if (frame1->fingerprint && frame2->fingerprint)
+ return sr_strcmp(frame1->fingerprint, frame2->fingerprint);
+
+ /* Fingerprints are not present, return the result of build ID and offset
+ * comparison.
+ */
+ if (build_id)
+ return build_id;
+
+ return build_id_offset;
}
struct sr_core_frame *
--
1.7.11.7
11 years
[PATCH] abrt-cli status: fix the output
by Denys Vlasenko
Before:
ABRT has detected '2' problem(s). (For more info run: $ abrt-cli list --since 1372083129 )
After:
ABRT has detected 2 problem(s). For more info run: abrt-cli list --since 1372083129
Signed-off-by: Denys Vlasenko <dvlasenk(a)redhat.com>
---
src/cli/status.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/cli/status.c b/src/cli/status.c
index 08045b9..17a1865 100644
--- a/src/cli/status.c
+++ b/src/cli/status.c
@@ -102,7 +102,7 @@ int cmd_status(int argc, const char **argv)
else
{
char *list_arg = opt_since ? xasprintf(" --since %d", opt_since) : xstrdup("");
- printf(_("ABRT has detected '%u' problem(s). (For more info run: $ abrt-cli list%s )\n"), problem_count, list_arg);
+ printf(_("ABRT has detected %u problem(s). For more info run: abrt-cli list%s\n"), problem_count, list_arg);
free(list_arg);
}
}
--
1.8.1.4
11 years
[SATYR PATCH 0/6] python base classes and distance functions
by Martin Milata
This patchset adds distance functions for comparing pairs as well as lists of
threads regardless of their type, as per ticket #63.
The python bindings had to be significantly changed to take advantage of the
"type-agnostic" interface that was added in satyr in
7ef55a0d77016fc651e4fd5422a592eab083e6c7 (and further commits).
Martin Milata (6):
Add getters/setters that will be used in py bindings
python: base class for frames
python: base classes for threads and stacktraces
tests: refactor input file loading into common function
python: distance computation between pair of threads
python: type-agnostic distances for list of threads
include/distance.h | 5 +-
include/frame.h | 6 +
include/stacktrace.h | 12 ++
include/thread.h | 12 ++
lib/core_frame.c | 2 +
lib/core_stacktrace.c | 5 +
lib/core_thread.c | 6 +-
lib/gdb_frame.c | 2 +
lib/gdb_stacktrace.c | 5 +
lib/gdb_thread.c | 6 +-
lib/generic_frame.c | 9 ++
lib/generic_frame.h | 2 +
lib/generic_stacktrace.c | 15 +++
lib/generic_stacktrace.h | 11 ++
lib/generic_thread.c | 15 +++
lib/generic_thread.h | 18 ++-
lib/internal_utils.h | 19 ++-
lib/java_frame.c | 2 +
lib/java_stacktrace.c | 5 +
lib/java_thread.c | 6 +-
lib/koops_frame.c | 2 +
lib/koops_stacktrace.c | 7 +-
lib/python_frame.c | 2 +
lib/python_stacktrace.c | 7 +-
python/Makefile.am | 6 +
python/py_base_frame.c | 117 +++++++++++++++++
python/py_base_frame.h | 51 ++++++++
python/py_base_stacktrace.c | 293 ++++++++++++++++++++++++++++++++++++++++++
python/py_base_stacktrace.h | 64 +++++++++
python/py_base_thread.c | 244 +++++++++++++++++++++++++++++++++++
python/py_base_thread.h | 59 +++++++++
python/py_gdb_frame.c | 14 +-
python/py_gdb_frame.h | 8 +-
python/py_gdb_stacktrace.c | 94 ++++++--------
python/py_gdb_stacktrace.h | 9 +-
python/py_gdb_thread.c | 161 +++--------------------
python/py_gdb_thread.h | 14 +-
python/py_java_frame.c | 14 +-
python/py_java_frame.h | 8 +-
python/py_java_stacktrace.c | 147 +++------------------
python/py_java_stacktrace.h | 10 +-
python/py_java_thread.c | 130 ++-----------------
python/py_java_thread.h | 14 +-
python/py_koops_frame.c | 14 +-
python/py_koops_frame.h | 8 +-
python/py_koops_stacktrace.c | 141 ++------------------
python/py_koops_stacktrace.h | 7 +-
python/py_metrics.c | 45 ++++---
python/py_module.c | 9 ++
python/py_python_frame.c | 14 +-
python/py_python_frame.h | 8 +-
python/py_python_stacktrace.c | 135 ++-----------------
python/py_python_stacktrace.h | 7 +-
tests/python/gdb.py | 12 +-
tests/python/java.py | 11 +-
tests/python/koops.py | 17 +--
tests/python/metrics.py | 58 ++++++++-
tests/python/python.py | 15 +--
tests/python/test_helpers.py | 8 ++
59 files changed, 1266 insertions(+), 881 deletions(-)
create mode 100644 python/py_base_frame.c
create mode 100644 python/py_base_frame.h
create mode 100644 python/py_base_stacktrace.c
create mode 100644 python/py_base_stacktrace.h
create mode 100644 python/py_base_thread.c
create mode 100644 python/py_base_thread.h
--
1.7.11.7
11 years
[LIBREPORT PATCH 1/2] refactore: get rid of g_hash_table* calls in user_settings
by Jakub Filak
Related to abrt/abrt#594
Signed-off-by: Jakub Filak <jfilak(a)redhat.com>
---
src/lib/user_settings.c | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
diff --git a/src/lib/user_settings.c b/src/lib/user_settings.c
index 58c19af..56387cd 100644
--- a/src/lib/user_settings.c
+++ b/src/lib/user_settings.c
@@ -17,7 +17,7 @@
*/
#include "internal_libreport.h"
-static GHashTable *user_settings;
+static map_string_t *user_settings;
static char *conf_path;
static bool create_parentdir(char *path)
@@ -94,12 +94,8 @@ bool load_user_settings(const char *application_name)
conf_path = get_conf_path(application_name);
if (user_settings)
- g_hash_table_destroy(user_settings);
- user_settings = g_hash_table_new_full(
- /*hash_func*/ g_str_hash,
- /*key_equal_func:*/ g_str_equal,
- /*key_destroy_func:*/ free,
- /*value_destroy_func:*/ free);
+ free_map_string(user_settings);
+ user_settings = new_map_string();
return load_conf_file(conf_path, user_settings, false);
}
@@ -110,9 +106,9 @@ void set_user_setting(const char *name, const char *value)
return;
if (value)
- g_hash_table_replace(user_settings, xstrdup(name), xstrdup(value));
+ replace_map_string_item(user_settings, xstrdup(name), xstrdup(value));
else
- g_hash_table_remove(user_settings, name);
+ remove_map_string_item(user_settings, name);
}
const char *get_user_setting(const char *name)
@@ -120,7 +116,7 @@ const char *get_user_setting(const char *name)
if (!user_settings)
return NULL;
- return g_hash_table_lookup(user_settings, name);
+ return get_map_string_item_or_NULL(user_settings, name);
}
GList *load_forbidden_words(void)
--
1.8.1.4
11 years
[LIBREPORT PATCH] spec: add dependency on fros
by Jiri Moskovcak
---
libreport.spec.in | 1 +
1 file changed, 1 insertion(+)
diff --git a/libreport.spec.in b/libreport.spec.in
index 141db28..e11e435 100644
--- a/libreport.spec.in
+++ b/libreport.spec.in
@@ -131,6 +131,7 @@ Summary: GTK front-end for libreport
Group: User Interface/Desktops
Requires: libreport = %{version}-%{release}
Requires: libreport-plugin-reportuploader = %{version}-%{release}
+Requires: fros >= 1.0
%if 0%{?rhel} >= 6
%else
Requires: pygobject3
--
1.8.2.1
11 years
[LIBREPORT PATCH] use fros instead of hard dependency on recordmydesktop rhbz#958979
by Jiri Moskovcak
Signed-off-by: Jiri Moskovcak <jmoskovc(a)redhat.com>
---
src/gui-wizard-gtk/wizard.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/src/gui-wizard-gtk/wizard.c b/src/gui-wizard-gtk/wizard.c
index ba43a66..151d879 100644
--- a/src/gui-wizard-gtk/wizard.c
+++ b/src/gui-wizard-gtk/wizard.c
@@ -3142,9 +3142,8 @@ static void assistant_quit_cb(void *obj, void *data)
static void on_btn_startcast(GtkWidget *btn, gpointer user_data)
{
const char *args[15];
- args[0] = (char *) LIBEXEC_DIR"/abrt-screencast";
- args[1] = concat_path_file(g_dump_dir_name, "screencast.ogv");
- args[2] = NULL;
+ args[0] = (char *) "fros";
+ args[1] = NULL;
pid_t castapp = 0;
castapp = fork_execv_on_steroids(
@@ -3172,8 +3171,8 @@ static void on_btn_startcast(GtkWidget *btn, gpointer user_data)
static bool is_screencast_available()
{
const char *args[3];
- args[0] = (char *) LIBEXEC_DIR"/abrt-screencast";
- args[1] = "is-available";
+ args[0] = (char *) "fros";
+ args[1] = "--is-available";
args[2] = NULL;
pid_t castapp = 0;
--
1.8.2.1
11 years
[ABRT PATCH 1/2] Add gdb python plugin which analyzes coredump for vulnerability. Closes #596
by Denys Vlasenko
Usage:
Run abrt-action-analyze-vulnerability in a directory which contains
./coredump file.
If crash looks exploitable, the tool creates ./exploitable file.
Example of such a file:
"""
Likely crash reason: Write to an invalid address
Exploitable rating (0-9 scale): 6
"""
This patch adds abrt-action-analyze-vulnerability invocation to
"EVENT=post-create analyzer=CCpp".
TODO:
* instruction analyzer is x86 specific now, make it per-arch
Signed-off-by: Denys Vlasenko <dvlasenk(a)redhat.com>
---
src/plugins/Makefile.am | 10 +-
src/plugins/abrt-action-analyze-vulnerability | 41 ++
src/plugins/abrt-gdb-exploitable | 519 +++++++++++++++++++++
src/plugins/ccpp_event.conf | 5 +-
tests/abrt-exploitable/.gitignore | 10 +
tests/abrt-exploitable/Makefile | 67 +++
tests/abrt-exploitable/testDivideByZero.c | 10 +
tests/abrt-exploitable/testExecuteInvalid.c | 17 +
.../abrt-exploitable/testFloatingPointException.c | 10 +
tests/abrt-exploitable/testReadNull.c | 12 +
tests/abrt-exploitable/testReadRandom.c | 14 +
tests/abrt-exploitable/testSignalAbort.c | 10 +
tests/abrt-exploitable/testSignalIll.c | 10 +
tests/abrt-exploitable/testStackBufferOverflow.c | 23 +
tests/abrt-exploitable/testStackRecursion.c | 15 +
tests/abrt-exploitable/testWriteRandom.c | 15 +
16 files changed, 785 insertions(+), 3 deletions(-)
create mode 100755 src/plugins/abrt-action-analyze-vulnerability
create mode 100755 src/plugins/abrt-gdb-exploitable
create mode 100644 tests/abrt-exploitable/.gitignore
create mode 100644 tests/abrt-exploitable/Makefile
create mode 100644 tests/abrt-exploitable/testDivideByZero.c
create mode 100644 tests/abrt-exploitable/testExecuteInvalid.c
create mode 100644 tests/abrt-exploitable/testFloatingPointException.c
create mode 100644 tests/abrt-exploitable/testReadNull.c
create mode 100644 tests/abrt-exploitable/testReadRandom.c
create mode 100644 tests/abrt-exploitable/testSignalAbort.c
create mode 100644 tests/abrt-exploitable/testSignalIll.c
create mode 100644 tests/abrt-exploitable/testStackBufferOverflow.c
create mode 100644 tests/abrt-exploitable/testStackRecursion.c
create mode 100644 tests/abrt-exploitable/testWriteRandom.c
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index 767c045..20e0297 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -3,6 +3,7 @@
bin_SCRIPTS = \
abrt-action-install-debuginfo \
abrt-action-analyze-core \
+ abrt-action-analyze-vulnerability \
abrt-action-analyze-vmcore \
abrt-action-list-dsos \
abrt-action-perform-ccpp-analysis \
@@ -29,9 +30,12 @@ bin_PROGRAMS += \
abrt-bodhi
endif
-libexec_PROGRAMS = abrt-action-install-debuginfo-to-abrt-cache
+libexec_PROGRAMS = \
+ abrt-action-install-debuginfo-to-abrt-cache
-libexec_SCRIPTS = abrt-action-ureport
+libexec_SCRIPTS = \
+ abrt-action-ureport \
+ abrt-gdb-exploitable
#dist_pluginsconf_DATA = Python.conf
@@ -68,6 +72,7 @@ PYTHON_FILES = \
abrt-action-install-debuginfo.in \
abrt-action-list-dsos \
abrt-action-analyze-core \
+ abrt-action-analyze-vulnerability \
abrt-action-analyze-vmcore.in \
abrt-action-perform-ccpp-analysis.in
@@ -84,6 +89,7 @@ EXTRA_DIST = \
abrt-action-analyze-vmcore \
abrt-action-save-kernel-data \
abrt-action-ureport \
+ abrt-gdb-exploitable \
https-utils.h \
post_report.xml.in \
abrt-action-analyze-ccpp-local
diff --git a/src/plugins/abrt-action-analyze-vulnerability b/src/plugins/abrt-action-analyze-vulnerability
new file mode 100755
index 0000000..aa63ff0
--- /dev/null
+++ b/src/plugins/abrt-action-analyze-vulnerability
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# Do we have the tools we need?
+# If no, exit silently.
+type gdb >/dev/null 2>&1 || exit 0
+type eu-readelf >/dev/null 2>&1 || exit 0
+
+# Do we have coredump?
+test -r coredump || {
+ echo 'No file "coredump" in current directory' >&2
+ exit 1
+}
+
+# Find "cursig: N" and extract N.
+# This gets used by abrt-exploitable as a fallback
+# if gdb and/or kernel is uncooperative.
+# "grep -m1": take the first match (on Linux, every thread has its own
+# prstatus struct in the coredump, but the signal number which killed us
+# must be the same in all these structs).
+SIGNO_OF_THE_COREDUMP=$(eu-readelf -n coredump | grep -m1 -o 'cursig: *[0-9]*' | sed 's/[^0-9]//g')
+export SIGNO_OF_THE_COREDUMP
+
+# Run gdb, hiding its messages. Example:
+# Missing separate debuginfo for the main executable file
+# Core was generated by...
+# Program terminated with signal 11, Segmentation fault.
+# #0 0x09fa5348 in ?? ()
+# We don't want to see all this.
+# abrt-exploitable plugin is instructed to create ./exploitable file
+# with explanation if severity is >= 4
+GDBOUT=$(
+gdb --batch \
+ -ex 'python execfile("/usr/libexec/abrt-gdb-exploitable")' \
+ -ex 'core-file ./coredump' \
+ -ex 'abrt-exploitable 4 ./exploitable' \
+ 2>&1 \
+) && exit 0
+
+# There was an error. Show the messages.
+printf "Error while running gdb:\n%s\n" "$GDBOUT"
+exit 1
diff --git a/src/plugins/abrt-gdb-exploitable b/src/plugins/abrt-gdb-exploitable
new file mode 100755
index 0000000..4ad95e5
--- /dev/null
+++ b/src/plugins/abrt-gdb-exploitable
@@ -0,0 +1,519 @@
+#!/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 gdb
+
+_WRITES_ALWAYS = -1
+_WRITES_IF_MEMREF = -2
+
+_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/32736...
+
+ #"[v]movXXX" - special-cased in the code
+ "mov":2
+
+ # Note: stack-writing instructions are omitted
+}
+
+_pushing_instr = (
+ "push",
+ "pusha",
+ "pushf",
+ "enter",
+ "call",
+ "lcall"
+)
+
+_intdiv_instr = ("div", "idiv")
+
+_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
+_return_instr = ("iret", "lret", "ret")
+
+def _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 Signal_and_insn:
+ def get_signal(self):
+ self.signo = 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")
+ # Requires patched gdb:
+ #sig = gdb.parse_and_eval("$_signo")
+ #
+ # type(sig) = <type 'gdb.Value'>, convert to plain int:
+ self.signo = int(sig)
+ 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
+
+ def get_instruction(self):
+ self.current_instruction = None
+ self.mnemonic = None
+ self.operands = ""
+ try:
+ # just "disassemble $pc" won't work if $pc doesn't point
+ # inside a known function
+ 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
+
+ raw_instructions = instructions
+ 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:
+ # not False! 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
+
+ def instruction_is_writing(self):
+ operand = _fetch_insn_from_table(self.mnemonic, _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 self.operands.startswith("0x") or self.operands.startswith("-0x") or self.operands.startswith("("):
+ mem_op_pos = 0
+ else:
+ mem_op_pos = self.operands.find(",0x")
+ if mem_op_pos < 0:
+ mem_op_pos = self.operands.find(",-0x")
+ if mem_op_pos < 0:
+ mem_op_pos = self.operands.find(":0x")
+ if mem_op_pos < 0:
+ mem_op_pos = self.operands.find(":-0x")
+ if mem_op_pos < 0:
+ mem_op_pos = self.operands.find(",(")
+ if mem_op_pos < 0:
+ mem_op_pos = self.operands.find(":(")
+ if mem_op_pos < 0:
+ return False # no memory operands
+ mem_op_pos += 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 instruction_is_pushing(self):
+ if _fetch_insn_from_table(self.mnemonic, _pushing_instr):
+ return True
+ return False
+
+ def instruction_is_division(self):
+ if _fetch_insn_from_table(self.mnemonic, _intdiv_instr):
+ return True
+ return False
+
+ def instruction_is_jumping(self):
+ if _fetch_insn_from_table(self.mnemonic, _jumping_instr):
+ return True
+ return False
+
+ def instruction_is_return(self):
+ if _fetch_insn_from_table(self.mnemonic, _return_instr):
+ return True
+ return False
+
+ #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 = ""
+
+ if 0:
+ pass
+ # 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)
+ elif self.signo == signal.SIGABRT:
+ self.exploitable_rating = 0
+ self.exploitable_desc = "ABRT signal (abort() was called?)"
+ elif self.signo == signal.SIGQUIT:
+ self.exploitable_rating = 0
+ self.exploitable_desc = "QUIT signal (Ctrl-\\ pressed?)"
+ elif self.signo == signal.SIGXCPU:
+ self.exploitable_rating = 0
+ self.exploitable_desc = "XCPU signal (over CPU time limit)"
+ elif self.signo == signal.SIGXFSZ:
+ self.exploitable_rating = 0
+ self.exploitable_desc = "XFSZ signal (over file size limit)"
+ elif self.signo == signal.SIGTRAP:
+ self.exploitable_rating = 0
+ self.exploitable_desc = "TRAP signal (can be a bug in a debugger/tracer)"
+ elif self.signo == signal.SIGSYS:
+ self.exploitable_rating = 1
+ self.exploitable_desc = "SYS signal (unknown/masked syscall was called?)"
+
+ elif self.signo == signal.SIGFPE:
+ self.exploitable_rating = 1
+ self.exploitable_desc = "Arithmetic exception"
+ if self.instruction_is_division():
+ self.exploitable_rating = 0
+ self.exploitable_desc = "Division by zero"
+ elif self.signo == signal.SIGILL:
+ self.exploitable_rating = 5
+ self.exploitable_desc = "Illegal instruction (jump to a random address?)"
+
+ # TODO: check that sig is SIGSEGV/SIGBUS?
+
+ elif self.instruction_is_pushing():
+ self.exploitable_rating = 4
+ self.exploitable_desc = "Stack overflow"
+ elif self.instruction_is_writing():
+ self.exploitable_rating = 6
+ self.exploitable_desc = "Write to an invalid address"
+ elif self.instruction_is_return():
+ self.exploitable_rating = 7
+ self.exploitable_desc = "Subroutine return to an invalid address (corrupted stack?)"
+ # Note: we check "ret" first, _then_ jumps.
+ # Corrupted stack is different from corrupted data.
+ elif self.instruction_is_jumping():
+ self.exploitable_rating = 6
+ self.exploitable_desc = "Jump to an invalid address"
+ elif not self.current_instruction:
+ self.exploitable_rating = 6
+ self.exploitable_desc = "Jump to an invalid address"
+ elif self.signo == signal.SIGBUS:
+ self.exploitable_rating = 5
+ self.exploitable_desc = "Access past the end of mapped file, invalid address, unaligned access, etc"
+ #elif self.signo = signal.SIGfoo:
+
+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
+ )
+
+ # Called when the command is invoked from GDB
+ def invoke(self, args, from_tty):
+ si = Signal_and_insn()
+ 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")
+ else:
+ sys.stderr.write("Exploitability analysis came up empty\n")
+
+AbrtExploitable()
diff --git a/src/plugins/ccpp_event.conf b/src/plugins/ccpp_event.conf
index dfc4908..aa8cdb3 100644
--- a/src/plugins/ccpp_event.conf
+++ b/src/plugins/ccpp_event.conf
@@ -15,10 +15,13 @@ EVENT=post-create analyzer=CCpp
exit 1
fi
# Try generating backtrace, if it fails we can still use
- # the UUID generated by abrt-action-analyze-c
+ # the hash generated by abrt-action-analyze-c
##satyr migration:
#satyr abrt-create-core-stacktrace "$DUMP_DIR"
abrt-action-generate-core-backtrace
+ # Run GDB plugin to see if crash looks exploitable
+ abrt-action-analyze-vulnerability
+ # Generate hash
abrt-action-analyze-c &&
abrt-action-list-dsos -m maps -o dso_list &&
(
diff --git a/tests/abrt-exploitable/.gitignore b/tests/abrt-exploitable/.gitignore
new file mode 100644
index 0000000..b0c3b29
--- /dev/null
+++ b/tests/abrt-exploitable/.gitignore
@@ -0,0 +1,10 @@
+testDivideByZero
+testExecuteInvalid
+testFloatingPointException
+testReadNull
+testReadRandom
+testSignalAbort
+testSignalIll
+testStackBufferOverflow
+testStackRecursion
+testWriteRandom
diff --git a/tests/abrt-exploitable/Makefile b/tests/abrt-exploitable/Makefile
new file mode 100644
index 0000000..6672fe3
--- /dev/null
+++ b/tests/abrt-exploitable/Makefile
@@ -0,0 +1,67 @@
+# Copyright (C) 2010, 2011 Red Hat, Inc.
+#
+# This file is part of ABRT.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+CFLAGS=-g
+
+TESTS=\
+ testDivideByZero\
+ testReadNull\
+ testStackRecursion\
+ testReadRandom\
+ testSignalAbort\
+ testSignalIll\
+ testWriteRandom\
+ testExecuteInvalid\
+ testStackBufferOverflow\
+ testFloatingPointException\
+
+all: $(TESTS)
+
+clean:
+ rm -f $(TESTS)
+
+testlive:
+ for t in $(TESTS); do \
+ echo "====="; \
+ echo "Test: $$t"; \
+ gdb --batch \
+ -ex 'python execfile("../../src/plugins/abrt-gdb-exploitable")' \
+ -ex 'run' \
+ -ex 'disas $$pc-16,$$pc+16' \
+ -ex 'abrt-exploitable' \
+ -ex 'cont' \
+ -ex 'quit' \
+ ./$$t; \
+ done 2>&1 | tee testlive.log
+
+testcore:
+ rm ./core* 2>/dev/null; \
+ ulimit -c unlimited; \
+ for t in $(TESTS); do \
+ echo "====="; \
+ echo "Test: $$t"; \
+ ./$$t && { echo "No crash???"; continue; }; \
+ mv core* core || { echo "No corefile???"; continue; }; \
+ gdb --batch \
+ -ex 'python execfile("../../src/plugins/abrt-gdb-exploitable")' \
+ -ex 'core ./core' \
+ -ex 'disas $$pc-16,$$pc+16' \
+ -ex 'abrt-exploitable' \
+ -ex 'quit' \
+ ; \
+ rm core; \
+ done 2>&1 | tee testcore.log
diff --git a/tests/abrt-exploitable/testDivideByZero.c b/tests/abrt-exploitable/testDivideByZero.c
new file mode 100644
index 0000000..06d1551
--- /dev/null
+++ b/tests/abrt-exploitable/testDivideByZero.c
@@ -0,0 +1,10 @@
+/*
+ * Test a divide by zero error
+ * This error is not exploitable
+ */
+
+#include <stdio.h>
+
+int main(int argc, char *argv[]) {
+ printf("%d\n", 7/0);
+}
diff --git a/tests/abrt-exploitable/testExecuteInvalid.c b/tests/abrt-exploitable/testExecuteInvalid.c
new file mode 100644
index 0000000..86ce515
--- /dev/null
+++ b/tests/abrt-exploitable/testExecuteInvalid.c
@@ -0,0 +1,17 @@
+/*
+ * Test a crash attempting to execute an invalid address
+ * This error is exploitable
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+int (*function_pointer)();
+
+int main(int argc, char *argv[]) {
+ char *a;
+ a = malloc(1024);
+ a = (size_t)a * 1024; // This should put us well outside the valid memory range
+ function_pointer = a;
+ function_pointer();
+}
diff --git a/tests/abrt-exploitable/testFloatingPointException.c b/tests/abrt-exploitable/testFloatingPointException.c
new file mode 100644
index 0000000..7931914
--- /dev/null
+++ b/tests/abrt-exploitable/testFloatingPointException.c
@@ -0,0 +1,10 @@
+/*
+ * Test the floating point exception signal
+ * This error is exploitable
+ */
+
+#include <signal.h>
+
+int main(int argc, char *argv[]) {
+ raise(SIGFPE);
+}
diff --git a/tests/abrt-exploitable/testReadNull.c b/tests/abrt-exploitable/testReadNull.c
new file mode 100644
index 0000000..548cf73
--- /dev/null
+++ b/tests/abrt-exploitable/testReadNull.c
@@ -0,0 +1,12 @@
+/*
+ * Test a NULL read
+ * This error is not exploitable
+ */
+
+#include <stdio.h>
+
+int main(int argc, char *argv[]) {
+ char *a;
+ a = 0x0;
+ puts(a);
+}
diff --git a/tests/abrt-exploitable/testReadRandom.c b/tests/abrt-exploitable/testReadRandom.c
new file mode 100644
index 0000000..8493078
--- /dev/null
+++ b/tests/abrt-exploitable/testReadRandom.c
@@ -0,0 +1,14 @@
+/*
+ * Test a crash attempting to read invalid memory
+ * This error is not exploitable
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[]) {
+ char *a;
+ a = malloc(1024);
+ a = (size_t)a * 1024; // This should put us well outside the valid memory range
+ printf("%s\n", a);
+}
diff --git a/tests/abrt-exploitable/testSignalAbort.c b/tests/abrt-exploitable/testSignalAbort.c
new file mode 100644
index 0000000..fc41227
--- /dev/null
+++ b/tests/abrt-exploitable/testSignalAbort.c
@@ -0,0 +1,10 @@
+/*
+ * Test the abort signal
+ * This error is not exploitable
+ */
+
+#include <signal.h>
+
+int main(int argc, char *argv[]) {
+ raise(SIGABRT);
+}
diff --git a/tests/abrt-exploitable/testSignalIll.c b/tests/abrt-exploitable/testSignalIll.c
new file mode 100644
index 0000000..7783263
--- /dev/null
+++ b/tests/abrt-exploitable/testSignalIll.c
@@ -0,0 +1,10 @@
+/*
+ * Test the illegal instruction signal
+ * This error is exploitable
+ */
+
+#include <signal.h>
+
+int main(int argc, char *argv[]) {
+ raise(SIGILL);
+}
diff --git a/tests/abrt-exploitable/testStackBufferOverflow.c b/tests/abrt-exploitable/testStackBufferOverflow.c
new file mode 100644
index 0000000..fcbd95f
--- /dev/null
+++ b/tests/abrt-exploitable/testStackBufferOverflow.c
@@ -0,0 +1,23 @@
+/*
+ * Test a stack buffer overflow
+ * This test could be exploitable (it needs further analysis)
+ */
+
+#include <stdio.h>
+
+int i;
+
+int my_function() {
+ char a[2];
+
+ for (i = 0; i < 1024; i++) {
+ a[i] = 'A';
+ }
+ printf("%s\n", a);
+}
+
+int main(int argc, char *argv[]) {
+ my_function();
+ my_function();
+ return 0;
+}
diff --git a/tests/abrt-exploitable/testStackRecursion.c b/tests/abrt-exploitable/testStackRecursion.c
new file mode 100644
index 0000000..dddf583
--- /dev/null
+++ b/tests/abrt-exploitable/testStackRecursion.c
@@ -0,0 +1,15 @@
+/*
+ * Test a crash from stack recursion
+ * This error is not exploitable
+ */
+
+#include <stdio.h>
+
+void my_function() {
+ char a[1024];
+ my_function();
+}
+
+int main(int argc, char *argv[]) {
+ my_function();
+}
diff --git a/tests/abrt-exploitable/testWriteRandom.c b/tests/abrt-exploitable/testWriteRandom.c
new file mode 100644
index 0000000..812f94c
--- /dev/null
+++ b/tests/abrt-exploitable/testWriteRandom.c
@@ -0,0 +1,15 @@
+/*
+ * Test a crash attempting to write invalid memory
+ * This error is exploitable
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+int main(int argc, char *argv[]) {
+ char *a;
+ char b[] = "pwnt";
+ a = malloc(1024);
+ a = (size_t)a * 1024; // This should put us well outside the valid memory range
+ strcpy(a, b);
+}
--
1.8.1.4
11 years