Back to index

apport  2.3
Public Member Functions | Public Attributes
parse_segv.ParseSegv Class Reference

List of all members.

Public Member Functions

def __init__
def find_vma
def parse_maps
def parse_regs
def parse_disassembly
def validate_vma
def register_value
def calculate_arg
def report

Public Attributes

 regs
 sp
 dest
 stack_vma
 maps

Detailed Description

Definition at line 18 of file parse_segv.py.


Constructor & Destructor Documentation

def parse_segv.ParseSegv.__init__ (   self,
  registers,
  disassembly,
  maps,
  debug = False 
)

Definition at line 19 of file parse_segv.py.

00019 
00020     def __init__(self, registers, disassembly, maps, debug=False):
00021         if debug:
00022             if sys.version > '3':
00023                 logging.basicConfig(level=logging.DEBUG,
00024                                     stream=io.TextIOWrapper(sys.stderr, encoding='UTF-8'))
00025             else:
00026                 logging.basicConfig(level=logging.DEBUG, stream=sys.stderr)
00027 
00028         self.regs = self.parse_regs(registers)
00029         self.sp = None
00030         for reg in ['rsp', 'esp']:
00031             if reg in self.regs:
00032                 self.sp = self.regs[reg]
00033 
00034         self.line, self.pc, self.insn, self.src, self.dest = \
00035             self.parse_disassembly(disassembly)
00036 
00037         self.stack_vma = None
00038         self.maps = self.parse_maps(maps)


Member Function Documentation

def parse_segv.ParseSegv.calculate_arg (   self,
  arg 
)

Definition at line 201 of file parse_segv.py.

00201 
00202     def calculate_arg(self, arg):
00203         # Check for and pre-remove segment offset
00204         segment = 0
00205         if arg.startswith('%') and ':' in arg:
00206             parts = arg.split(':', 1)
00207             segment = self.regs[parts[0][1:]]
00208             arg = parts[1]
00209 
00210         # Handle standard offsets
00211         parts = arg.split('(')
00212         offset = parts[0]
00213         # Handle negative signs
00214         sign = 1
00215         if offset.startswith('-'):
00216             sign = -1
00217             offset = offset[1:]
00218         # Skip call target dereferences
00219         if offset.startswith('*'):
00220             offset = offset[1:]
00221         if len(offset) > 0:
00222             if offset.startswith('%'):
00223                 # Handle the *%REG case
00224                 add = self.regs[offset[1:]]
00225             else:
00226                 if not offset.startswith('0x'):
00227                     raise ValueError('Unknown offset literal: %s' % (parts[0]))
00228                 add = int(offset[2:], 16) * sign
00229         else:
00230             add = 0
00231 
00232         def _reg_val(self, text, val=0):
00233             if text.startswith('%'):
00234                 val = self.regs[text[1:]]
00235             elif text == "":
00236                 val = 0
00237             else:
00238                 val = int(text)
00239             return val
00240 
00241         # (%ebx, %ecx, 4) style
00242         value = 0
00243         if len(parts) > 1:
00244             parens = parts[1][0:-1]
00245             reg_list = parens.split(',')
00246 
00247             base = 0
00248             if len(reg_list) > 0:
00249                 base = _reg_val(self, reg_list[0], base)
00250             index = 0
00251             if len(reg_list) > 1:
00252                 index = _reg_val(self, reg_list[1], index)
00253             scale = 1
00254             if len(reg_list) > 2:
00255                 scale = _reg_val(self, reg_list[2], scale)
00256             value = base + index * scale
00257 
00258         value = segment + value + add
00259         if 'esp' in self.regs:
00260             # 32bit
00261             return value % 0x100000000
00262         else:
00263             # 64bit
00264             return value % 0x10000000000000000

Here is the caller graph for this function:

def parse_segv.ParseSegv.find_vma (   self,
  addr 
)

Definition at line 39 of file parse_segv.py.

00039 
00040     def find_vma(self, addr):
00041         for vma in self.maps:
00042             if addr >= vma['start'] and addr < vma['end']:
00043                 return vma
00044         return None

Here is the caller graph for this function:

def parse_segv.ParseSegv.parse_disassembly (   self,
  disassembly 
)

Definition at line 72 of file parse_segv.py.

00072 
00073     def parse_disassembly(self, disassembly):
00074         if not self.regs:
00075             raise ValueError('Registers not loaded yet!?')
00076         lines = disassembly.splitlines()
00077         # Throw away possible 'Dump' gdb report line
00078         if len(lines) > 0 and lines[0].startswith('Dump'):
00079             lines.pop(0)
00080         if len(lines) < 1:
00081             raise ValueError('Failed to load empty disassembly')
00082         line = lines[0].strip()
00083         # Drop GDB 7.1's leading $pc mark
00084         if line.startswith('=>'):
00085             line = line[2:].strip()
00086         logging.debug(line)
00087         pc_str = line.split()[0]
00088         if pc_str.startswith('0x'):
00089             pc = int(pc_str.split(':')[0], 16)
00090         else:
00091             # Could not identify this instruction line
00092             raise ValueError('Could not parse PC "%s" from disassembly line: %s' % (pc_str, line))
00093         logging.debug('pc: 0x%08x', pc)
00094 
00095         full_insn_str = line.split(':', 1)[1].strip()
00096         # Handle invalid memory
00097         if 'Cannot access memory at address' in full_insn_str or (full_insn_str == '' and len(lines) == 1):
00098             return line, pc, None, None, None
00099         # Handle wrapped lines
00100         if full_insn_str == '' and lines[1].startswith(' '):
00101             line = line + ' ' + lines[1].strip()
00102             full_insn_str = line.split(':', 1)[1].strip()
00103 
00104         insn_parts = full_insn_str.split()
00105         # Drop call target names "call   0xb7a805af <_Unwind_Find_FDE@plt+111>"
00106         if insn_parts[-1].endswith('>') and insn_parts[-1].startswith('<'):
00107             insn_parts.pop(-1)
00108         # Attempt to find arguments
00109         args_str = ''
00110         if len(insn_parts) > 1:
00111             args_str = insn_parts.pop(-1)
00112         # Assume remainder is the insn itself
00113         insn = ' '.join(insn_parts)
00114         logging.debug('insn: %s', insn)
00115 
00116         args = []
00117         src = None
00118         dest = None
00119         if args_str == '':
00120             # Could not find insn args
00121             args = None
00122         else:
00123             logging.debug('args: "%s"', args_str)
00124 
00125             for m in re.finditer('([^,\(]*(\(:?[^\)]+\))*)', args_str):
00126                 if len(m.group(0)):
00127                     args.append(m.group(0))
00128             if len(args) > 0:
00129                 src = args[0]
00130                 logging.debug('src: %s', src)
00131             if len(args) > 1:
00132                 dest = args[1]
00133                 logging.debug('dest: %s', dest)
00134 
00135         # Set up possible implicit memory destinations (stack actions)
00136         if insn in ['push', 'pop', 'pushl', 'popl', 'call', 'callq', 'ret', 'retq']:
00137             for reg in ['rsp', 'esp']:
00138                 if reg in self.regs:
00139                     dest = '(%%%s)' % (reg)
00140                     break
00141 
00142         return line, pc, insn, src, dest

def parse_segv.ParseSegv.parse_maps (   self,
  maps_str 
)

Definition at line 45 of file parse_segv.py.

00045 
00046     def parse_maps(self, maps_str):
00047         maps = []
00048         for line in maps_str.splitlines():
00049             items = line.strip().split()
00050             try:
00051                 span, perms, bits, dev = items[0:4]
00052             except:
00053                 raise ValueError('Cannot parse maps line: %s' % (line.strip()))
00054             if len(items) == 5:
00055                 name = None
00056             else:
00057                 name = items[5]
00058             start, end = [int(x, 16) for x in span.split('-')]
00059             if name == '[stack]':
00060                 self.stack_vma = len(maps)
00061             maps.append({'start': start, 'end': end, 'perms': perms, 'name': name})
00062             logging.debug('start: %s, end: %s, perms: %s, name: %s', start, end, perms, name)
00063         return maps

def parse_segv.ParseSegv.parse_regs (   self,
  reg_str 
)

Definition at line 64 of file parse_segv.py.

00064 
00065     def parse_regs(self, reg_str):
00066         regs = dict()
00067         for line in reg_str.splitlines():
00068             reg, hexvalue = line.split()[0:2]
00069             regs[reg] = int(hexvalue, 16)
00070             logging.debug('%s:0x%08x', reg, regs[reg])
00071         return regs

def parse_segv.ParseSegv.register_value (   self,
  reg 
)

Definition at line 164 of file parse_segv.py.

00164 
00165     def register_value(self, reg):
00166         reg_orig = reg
00167 
00168         #print reg
00169         mask = 0
00170         if reg.startswith('%'):
00171             #print('%s -> %s' % (reg, reg[1:]))
00172             reg = reg[1:]
00173         if reg in self.regs:
00174             #print('got %s (%d & %d == %d)' % (reg, self.regs[reg], mask, self.regs[reg] & ~mask))
00175             return self.regs[reg]
00176 
00177         if len(reg) == 2 and reg.endswith('l'):
00178             mask |= 0xff00
00179             #print('%s -> %sx' % (reg, reg[0]))
00180             reg = '%sx' % reg[0]
00181         if reg in self.regs:
00182             #print('got %s (%d & %d == %d)' % (reg, self.regs[reg], mask, self.regs[reg] & ~mask))
00183             return self.regs[reg] & ~mask
00184 
00185         if len(reg) == 2 and reg.endswith('x'):
00186             mask |= 0xffff0000
00187             #print('%s -> e%s' % (reg, reg))
00188             reg = 'e%s' % reg
00189         if reg in self.regs:
00190             #print('got %s (%d & %d == %d)' % (reg, self.regs[reg], mask, self.regs[reg] & ~mask))
00191             return self.regs[reg] & ~mask
00192 
00193         if len(reg) == 3 and reg.startswith('e'):
00194             mask |= 0xffffffff00000000
00195             #print('%s -> r%s' % (reg, reg[1:]))
00196             reg = 'r%s' % reg[1:]
00197         if reg in self.regs:
00198             #print('got %s (%d & %d == %d)' % (reg, self.regs[reg], mask, self.regs[reg] & ~mask))
00199             return self.regs[reg] & ~mask
00200         raise ValueError("Could not resolve register '%s'" % (reg_orig))

Here is the caller graph for this function:

Definition at line 265 of file parse_segv.py.

00265 
00266     def report(self):
00267         understood = False
00268         reason = []
00269         details = ['Segfault happened at: %s' % (self.line)]
00270 
00271         # Verify PC is in an executable region
00272         valid, out, short = self.validate_vma('x', self.pc, 'PC')
00273         details.append(out)
00274         if not valid:
00275             reason.append(short)
00276             understood = True
00277 
00278         if self.insn in ['lea', 'leal']:
00279             # Short-circuit for instructions that do not cause vma access
00280             details.append('insn (%s) does not access VMA' % (self.insn))
00281         else:
00282             # Verify source is readable
00283             if self.src:
00284                 if not ':' in self.src and (self.src[0] in ['%', '$', '*']) and not self.src.startswith('*%'):
00285                     details.append('source "%s" ok' % (self.src))
00286                 else:
00287                     addr = self.calculate_arg(self.src)
00288                     valid, out, short = self.validate_vma('r', addr, 'source "%s"' % (self.src))
00289                     details.append(out)
00290                     if not valid:
00291                         reason.append(short)
00292                         understood = True
00293 
00294             # Verify destination is writable
00295             if self.dest:
00296                 if not ':' in self.dest and (self.dest[0] in ['%', '$', '*']):
00297                     details.append('destination "%s" ok' % (self.dest))
00298                 else:
00299                     addr = self.calculate_arg(self.dest)
00300                     valid, out, short = self.validate_vma('w', addr, 'destination "%s"' % (self.dest))
00301                     details.append(out)
00302                     if not valid:
00303                         reason.append(short)
00304                         understood = True
00305 
00306         # Handle I/O port operations
00307         if self.insn in ['out', 'in'] and not understood:
00308             reason.append('disallowed I/O port operation on port %d' % (self.register_value(self.src)))
00309             details.append('disallowed I/O port operation on port %d' % (self.register_value(self.src)))
00310             understood = True
00311 
00312         # Note position of SP with regard to "[stack]" VMA
00313         if self.sp is not None:
00314             if self.stack_vma is not None:
00315                 if self.sp < self.maps[self.stack_vma]['start']:
00316                     details.append("Stack memory exhausted (SP below stack segment)")
00317                 if self.sp >= self.maps[self.stack_vma]['end']:
00318                     details.append("Stack pointer not within stack segment")
00319             if not understood:
00320                 valid, out, short = self.validate_vma('r', self.sp, 'SP')
00321                 details.append(out)
00322                 if not valid:
00323                     reason.append(short)
00324                     understood = True
00325 
00326         if not understood:
00327             vma = self.find_vma(self.pc)
00328             if vma and (vma['name'] == '[vdso]' or vma['name'] == '[vsyscall]'):
00329                 reason.append('Reason could not be automatically determined. (Unhandled exception in kernel code?)')
00330                 details.append('Reason could not be automatically determined. (Unhandled exception in kernel code?)')
00331             else:
00332                 reason.append('Reason could not be automatically determined.')
00333                 details.append('Reason could not be automatically determined.')
00334         return understood, '\n'.join(reason), '\n'.join(details)
00335 

Here is the call graph for this function:

def parse_segv.ParseSegv.validate_vma (   self,
  perm,
  addr,
  name 
)

Definition at line 143 of file parse_segv.py.

00143 
00144     def validate_vma(self, perm, addr, name):
00145         perm_name = {'x': ['executable', 'executing'], 'r': ['readable', 'reading'], 'w': ['writable', 'writing']}
00146         vma = self.find_vma(addr)
00147         if vma is None:
00148             alarmist = 'unknown'
00149             if addr < 65536:
00150                 alarmist = 'NULL'
00151             return False, '%s (0x%08x) not located in a known VMA region (needed %s region)!' % (name, addr, perm_name[perm][0]), '%s %s VMA' % (perm_name[perm][1], alarmist)
00152         elif perm not in vma['perms']:
00153             alarmist = ''
00154             if perm == 'x':
00155                 if 'w' in vma['perms']:
00156                     alarmist = 'writable '
00157                 else:
00158                     alarmist = 'non-writable '
00159             short = '%s %sVMA %s' % (perm_name[perm][1], alarmist, vma['name'])
00160 
00161             return False, '%s (0x%08x) in non-%s VMA region: 0x%08x-0x%08x %s %s' % (name, addr, perm_name[perm][0], vma['start'], vma['end'], vma['perms'], vma['name']), short
00162         else:
00163             return True, '%s (0x%08x) ok' % (name, addr), '%s ok' % (perm_name[perm][1])

Here is the call graph for this function:

Here is the caller graph for this function:


Member Data Documentation

Definition at line 33 of file parse_segv.py.

Definition at line 37 of file parse_segv.py.

Definition at line 27 of file parse_segv.py.

Definition at line 28 of file parse_segv.py.

Definition at line 36 of file parse_segv.py.


The documentation for this class was generated from the following file: