[ABRT PATCH 1/2 v2] abrt-gdb-exploitable: x86 factoring-out

Denys Vlasenko dvlasenk at redhat.com
Tue Jul 2 14:33:13 UTC 2013


No substantial logic changes.
Mostly renaming stuff and moving blocks of code around.

v2: Python style fixes

Signed-off-by: Denys Vlasenko <dvlasenk at redhat.com>
---
 src/plugins/abrt-gdb-exploitable | 228 ++++++++++++++++++++-------------------
 1 file changed, 117 insertions(+), 111 deletions(-)

diff --git a/src/plugins/abrt-gdb-exploitable b/src/plugins/abrt-gdb-exploitable
index e3d637b..f33ec45 100755
--- a/src/plugins/abrt-gdb-exploitable
+++ b/src/plugins/abrt-gdb-exploitable
@@ -30,11 +30,10 @@ def init_gettext():
     gettext.bindtextdomain(GETTEXT_PROGNAME, '/usr/share/locale')
     gettext.textdomain(GETTEXT_PROGNAME)
 
-
 _WRITES_ALWAYS = -1
 _WRITES_IF_MEMREF = -2
 
-_writing_instr = {
+_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
@@ -198,7 +197,7 @@ _writing_instr = {
     # Note: stack-writing instructions are omitted
 }
 
-_pushing_instr = (
+_x86_pushing_instr = (
     "push",
     "pusha",
     "pushf",
@@ -207,9 +206,9 @@ _pushing_instr = (
     "lcall"
 )
 
-_intdiv_instr = ("div", "idiv")
+_x86_intdiv_instr = ("div", "idiv")
 
-_jumping_instr = (
+_x86_jumping_instr = (
     "jmp",  # indirect jumps/calls with garbage data
     "call", # call: also possible that stack is exhausted (infinite recursion)
     "ljmp",
@@ -233,7 +232,10 @@ _jumping_instr = (
 )
 
 # stack was smashed if we crash on one of these
-_return_instr = ("iret", "lret", "ret")
+_x86_return_instr = ("iret", "lret", "ret")
+
+_x86_mem_op1_regex = re.compile("^((-?0x)|[(])")
+_x86_mem_op2_regex = re.compile("[,:]((-?0x)|[(])")
 
 def _fetch_insn_from_table(ins, table):
     if not ins:
@@ -251,50 +253,68 @@ def _fetch_insn_from_table(ins, table):
             return ins
     return None
 
+
 class SignalAndInsn:
-    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:
+
+    def x86_instruction_is_store(self):
+        operand = _fetch_insn_from_table(self.mnemonic, _x86_writing_instr)
+        if not operand:
+            if not self.mnemonic:
                 return False
-        return True
+            # 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
 
-    def get_instruction(self):
-        self.current_instruction = None
-        self.mnemonic = None
-        self.operands = ""
+        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
-            instructions = gdb.execute("disassemble $pc,$pc+32", to_string=True)
+            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
 
-        raw_instructions = instructions
         instructions = []
         current = None
         for line in raw_instructions.split("\n"):
@@ -316,7 +336,7 @@ class SignalAndInsn:
             if line:
                 instructions.append(line)
         if current == None:
-            # not False! we determined that $pc points to a bad address,
+            # we determined that $pc points to a bad address,
             # which is an interesting fact.
             return
 
@@ -344,76 +364,62 @@ class SignalAndInsn:
             self.operands = t[1]
             break
 
-    mem_op1_regex = re.compile("^((-?0x)|[(])")
-    mem_op2_regex = re.compile("[,:]((-?0x)|[(])")
+        self.instruction_is_pushing = (_fetch_insn_from_table(self.mnemonic, _x86_pushing_instr) is not None)
+        self.instruction_is_division = (_fetch_insn_from_table(self.mnemonic, _x86_intdiv_instr) is not None)
+        self.instruction_is_branch = (_fetch_insn_from_table(self.mnemonic, _x86_jumping_instr) is not None)
+        self.instruction_is_return = (_fetch_insn_from_table(self.mnemonic, _x86_return_instr) is not None)
+        self.instruction_is_store = self.x86_instruction_is_store()
 
-    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 SignalAndInsn.mem_op1_regex.search(self.operands):
-            mem_op_pos = 0
-        else:
-            match = SignalAndInsn.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 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 instruction_is_return(self):
-        if _fetch_insn_from_table(self.mnemonic, _return_instr):
-            return True
-        return False
+    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
 
     #Our initial set of testing will use the list Apple included in their
     #CrashWrangler announcement:
@@ -467,7 +473,7 @@ class SignalAndInsn:
         elif self.signo == signal.SIGFPE:
             self.exploitable_rating = 1
             self.exploitable_desc = _("Arithmetic exception")
-            if self.instruction_is_division():
+            if self.instruction_is_division:
                 self.exploitable_rating = 0
                 self.exploitable_desc = _("Division by zero")
         elif self.signo == signal.SIGILL:
@@ -476,18 +482,18 @@ class SignalAndInsn:
 
         # TODO: check that sig is SIGSEGV/SIGBUS?
 
-        elif self.instruction_is_pushing():
+        elif self.instruction_is_pushing:
             self.exploitable_rating = 4
             self.exploitable_desc = _("Stack overflow")
-        elif self.instruction_is_writing():
+        elif self.instruction_is_store:
             self.exploitable_rating = 6
             self.exploitable_desc = _("Write to an invalid address")
-        elif self.instruction_is_return():
+        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():
+        elif self.instruction_is_branch:
             self.exploitable_rating = 6
             self.exploitable_desc = _("Jump to an invalid address")
         elif not self.current_instruction:
-- 
1.8.1.4



More information about the Crash-catcher mailing list