~brian-murray/apport/test-fix

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
import unittest, tempfile, sys, os.path

datadir = os.environ.get('APPORT_DATA_DIR', '/usr/share/apport')
sys.path.insert(0, os.path.join(datadir, 'general-hooks'))

import parse_segv

# Default global registers, maps, and disassembly for testing
regs = '''eax            0xffffffff -1
ecx            0xbfc6af40   -1077498048
edx            0x1  1
ebx            0x26eff4 2551796
esp            0xbfc6af24   0xbfc6af24
ebp            0xbfc6af28   0xbfc6af28
esi            0x826bb60    136756064
edi            0x8083480    134755456
eip            0x808354e    0x808354e <main+14>
eflags         0x200286 [ PF SF IF ID ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x4  4
gs             0x33 51
'''
regs64 = '''rax            0xffffffffffffffff   -1
rbx            0x26eff4 2551796
rcx            0xffffffffffffffff   -1
rdx            0xffffffffff600180   -10485376
rsi            0x0  0
rdi            0x7fffffffe3b0   140737488348080
rbp            0x0  0x0
rsp            0x0000bfc6af24   0x0000bfc6af24
r8             0x0  0
r9             0x0  0
r10            0x7fffffffe140   140737488347456
r11            0x246    582
r12            0x7fffffffe400   140737488348160
r13            0x7fffffffe468   140737488348264
r14            0x1  1
r15            0x7fffffffe460   140737488348256
rip            0x7ffff790be10   0x7ffff790be10 <nanosleep+16>
eflags         0x246    [ PF ZF IF ]
cs             0x33 51
ss             0x2b 43
ds             0x0  0
es             0x0  0
fs             0x0  0
gs             0x0  0
fctrl          0x37f    895
fstat          0x0  0
ftag           0xffff   65535
fiseg          0x0  0
fioff          0x40303a 4206650
foseg          0x0  0
fooff          0x0  0
fop            0x5d8    1496
mxcsr          0x1f80   [ IM DM ZM OM UM PM ]
'''
maps = '''00110000-0026c000 r-xp 00000000 08:06 375131     /lib/tls/i686/cmov/libc-2.9.so
0026c000-0026d000 ---p 0015c000 08:06 375131     /lib/tls/i686/cmov/libc-2.9.so
0026d000-0026f000 r--p 0015c000 08:06 375131     /lib/tls/i686/cmov/libc-2.9.so
0026f000-00270000 rw-p 0015e000 08:06 375131     /lib/tls/i686/cmov/libc-2.9.so
00270000-00273000 rw-p 00000000 00:00 0
002c1000-002e5000 r-xp 00000000 08:06 375135     /lib/tls/i686/cmov/libm-2.9.so
002e5000-002e6000 r--p 00023000 08:06 375135     /lib/tls/i686/cmov/libm-2.9.so
002e6000-002e7000 rw-p 00024000 08:06 375135     /lib/tls/i686/cmov/libm-2.9.so
00318000-00334000 r-xp 00000000 08:06 977846     /lib/ld-2.9.so
00334000-00335000 r--p 0001b000 08:06 977846     /lib/ld-2.9.so
00335000-00336000 rw-p 0001c000 08:06 977846     /lib/ld-2.9.so
0056e000-005a1000 r-xp 00000000 08:06 65575      /lib/libncurses.so.5.7
005a1000-005a3000 r--p 00033000 08:06 65575      /lib/libncurses.so.5.7
005a3000-005a4000 rw-p 00035000 08:06 65575      /lib/libncurses.so.5.7
00b67000-00b68000 r-xp 00000000 00:00 0          [vdso]
00bb6000-00bcb000 r-xp 00000000 08:06 375202     /lib/tls/i686/cmov/libpthread-2.9.so
00bcb000-00bcc000 r--p 00014000 08:06 375202     /lib/tls/i686/cmov/libpthread-2.9.so
00bcc000-00bcd000 rw-p 00015000 08:06 375202     /lib/tls/i686/cmov/libpthread-2.9.so
00bcd000-00bcf000 rw-p 00000000 00:00 0
00beb000-00bed000 r-xp 00000000 08:06 375134     /lib/tls/i686/cmov/libdl-2.9.so
00bed000-00bee000 r--p 00001000 08:06 375134     /lib/tls/i686/cmov/libdl-2.9.so
00bee000-00bef000 rw-p 00002000 08:06 375134     /lib/tls/i686/cmov/libdl-2.9.so
00c56000-00c7a000 r-xp 00000000 08:06 1140420    /usr/lib/libexpat.so.1.5.2
00c7a000-00c7c000 r--p 00023000 08:06 1140420    /usr/lib/libexpat.so.1.5.2
00c7c000-00c7d000 rw-p 00025000 08:06 1140420    /usr/lib/libexpat.so.1.5.2
00dce000-00dfa000 r-xp 00000000 08:06 65612      /lib/libreadline.so.5.2
00dfa000-00dfb000 ---p 0002c000 08:06 65612      /lib/libreadline.so.5.2
00dfb000-00dfc000 r--p 0002c000 08:06 65612      /lib/libreadline.so.5.2
00dfc000-00dff000 rw-p 0002d000 08:06 65612      /lib/libreadline.so.5.2
00dff000-00e00000 rw-p 00000000 00:00 0
08048000-0831c000 r-xp 00000000 08:06 1140349    /usr/bin/gdb
0831c000-0831d000 r--p 002d3000 08:06 1140349    /usr/bin/gdb
0831d000-08325000 rw-p 002d4000 08:06 1140349    /usr/bin/gdb
08325000-0833f000 rw-p 00000000 00:00 0
b8077000-b807a000 rw-p 00000000 00:00 0
b8096000-b8098000 rw-p 00000000 00:00 0
bfc57000-bfc6c000 rw-p 00000000 00:00 0          [stack]
'''
disasm = '''0x08083540 <main+0>:    lea    0x4(%esp),%ecx
0x08083544 <main+4>:    and    $0xfffffff0,%esp
0x08083547 <main+7>:    pushl  -0x4(%ecx)
0x0808354a <main+10>:   push   %ebp
0x0808354b <main+11>:   mov    %esp,%ebp
0x0808354d <main+13>:   push   %ecx
0x0808354e <main+14>:   sub    $0x14,%esp
0x08083551 <main+17>:   mov    (%ecx),%eax
0x08083553 <main+19>:   mov    0x4(%ecx),%edx
0x08083556 <main+22>:   lea    -0x14(%ebp),%ecx
0x08083559 <main+25>:   movl   $0x0,-0xc(%ebp)
0x08083560 <main+32>:   movl   $0x826bc68,-0x8(%ebp)
0x08083567 <main+39>:   mov    %eax,-0x14(%ebp)
0x0808356a <main+42>:   mov    %edx,-0x10(%ebp)
0x0808356d <main+45>:   mov    %ecx,(%esp)
0x08083570 <main+48>:   call   0x8083580 <gdb_main>
0x08083575 <main+53>:   add    $0x14,%esp
0x08083578 <main+56>:   pop    %ecx
0x08083579 <main+57>:   pop    %ebp
0x0808357a <main+58>:   lea    -0x4(%ecx),%esp
0x0808357d <main+61>:   ret
'''


class T(unittest.TestCase):
    '''Test Segfault Parser'''

    def test_invalid_00_registers(self):
        '''Require valid registers'''

        regs = 'a 0x10\nb !!!\n'
        self.assertRaises(ValueError, parse_segv.ParseSegv, regs, '', '')
        try:
            segv = parse_segv.ParseSegv(regs, '', '')
        except ValueError as e:
            self.assertTrue('invalid literal for int()' in str(e), str(e))

        regs = 'a 0x10'
        disasm = '0x08083540 <main+0>:    lea    0x4(%esp),%ecx\n'
        segv = parse_segv.ParseSegv(regs, disasm, '')
        self.assertEqual(segv.regs['a'], 0x10, segv)

        segv.regs = None
        self.assertRaises(ValueError, segv.parse_disassembly, '')

    def test_invalid_01_disassembly(self):
        '''Require valid disassembly'''
        regs = 'a 0x10'

        disasm = ''
        self.assertRaises(ValueError, parse_segv.ParseSegv, regs, disasm, '')

        disasm = 'Dump ...'
        self.assertRaises(ValueError, parse_segv.ParseSegv, regs, disasm, '')

        disasm = 'Dump ...\nmonkey'
        self.assertRaises(ValueError, parse_segv.ParseSegv, regs, disasm, '')

        disasm = 'monkey'
        self.assertRaises(ValueError, parse_segv.ParseSegv, regs, disasm, '')

        disasm = '0x1111111111: Cannot access memory at address 0x1111111111\n'
        segv = parse_segv.ParseSegv(regs, disasm, '')
        self.assertEqual(segv.pc, 0x1111111111, segv.pc)
        self.assertEqual(segv.insn, None, segv.insn)
        self.assertEqual(segv.src, None, segv.src)
        self.assertEqual(segv.dest, None, segv.dest)

        disasm = '0x2111111111: \n'
        segv = parse_segv.ParseSegv(regs, disasm, '')
        self.assertEqual(segv.pc, 0x2111111111, segv.pc)
        self.assertEqual(segv.insn, None, segv.insn)
        self.assertEqual(segv.src, None, segv.src)
        self.assertEqual(segv.dest, None, segv.dest)

        disasm = '0x8069ff0 <fopen@plt+132220>: cmpb   $0x0,(%eax,%ebx,1)\n'
        segv = parse_segv.ParseSegv(regs, disasm, '')
        self.assertEqual(segv.pc, 0x8069ff0, segv.pc)
        self.assertEqual(segv.insn, 'cmpb', segv.insn)
        self.assertEqual(segv.src, '$0x0', segv.src)
        self.assertEqual(segv.dest, '(%eax,%ebx,1)', segv.dest)

        disasm = '0xb765bb48 <_XSend+440>:  call   *0x40(%edi)\n'
        segv = parse_segv.ParseSegv(regs, disasm, '')
        self.assertEqual(segv.pc, 0xb765bb48, segv.pc)
        self.assertEqual(segv.insn, 'call', segv.insn)
        self.assertEqual(segv.src, '*0x40(%edi)', segv.src)
        self.assertEqual(segv.dest, None, segv.dest)

        disasm = '0xb7aae5a0:   call   0xb7a805af <_Unwind_Find_FDE@plt+111>\n'
        segv = parse_segv.ParseSegv(regs, disasm, '')
        self.assertEqual(segv.pc, 0xb7aae5a0, segv.pc)
        self.assertEqual(segv.insn, 'call', segv.insn)
        self.assertEqual(segv.src, '0xb7a805af', segv.src)
        self.assertEqual(segv.dest, None, segv.dest)

        disasm = '0x09083540:    mov    0x4(%esp),%es:%ecx\n'
        segv = parse_segv.ParseSegv(regs, disasm, '')
        self.assertEqual(segv.pc, 0x09083540, segv.pc)
        self.assertEqual(segv.insn, 'mov', segv.insn)
        self.assertEqual(segv.src, '0x4(%esp)', segv.src)
        self.assertEqual(segv.dest, '%es:%ecx', segv.dest)

        disasm = '0x08083540 <main+0>:    lea    0x4(%esp),%ecx\n'
        segv = parse_segv.ParseSegv(regs, disasm, '')
        self.assertEqual(segv.pc, 0x08083540, segv.pc)
        self.assertEqual(segv.insn, 'lea', segv.insn)
        self.assertEqual(segv.src, '0x4(%esp)', segv.src)
        self.assertEqual(segv.dest, '%ecx', segv.dest)

        disasm = '''0x404127 <exo_mount_hal_device_mount+167>:
    repz cmpsb %es:(%rdi),%ds:(%rsi)\n'''
        segv = parse_segv.ParseSegv(regs, disasm, '')
        self.assertEqual(segv.pc, 0x0404127, segv.pc)
        self.assertEqual(segv.insn, 'repz cmpsb', segv.insn)
        self.assertEqual(segv.src, '%es:(%rdi)', segv.src)
        self.assertEqual(segv.dest, '%ds:(%rsi)', segv.dest)

        disasm = '0xb031765a <hufftab16+570>: add    0x3430433,%eax'
        segv = parse_segv.ParseSegv(regs, disasm, '')
        self.assertEqual(segv.pc, 0xb031765a, segv.pc)
        self.assertEqual(segv.insn, 'add', segv.insn)
        self.assertEqual(segv.src, '0x3430433', segv.src)
        self.assertEqual(segv.dest, '%eax', segv.dest)

        disasm = 'Dump ...\n0x08083540 <main+0>:    lea    0x4(%esp),%ecx\n'
        segv = parse_segv.ParseSegv(regs, disasm, '')
        self.assertEqual(segv.pc, 0x08083540, segv.pc)
        self.assertEqual(segv.insn, 'lea', segv.insn)
        self.assertEqual(segv.src, '0x4(%esp)', segv.src)
        self.assertEqual(segv.dest, '%ecx', segv.dest)

        disasm = '0x08083550 <main+0>:    nop\n'
        segv = parse_segv.ParseSegv(regs, disasm, '')
        self.assertEqual(segv.pc, 0x08083550, segv.pc)
        self.assertEqual(segv.insn, 'nop', segv.insn)
        self.assertEqual(segv.src, None, segv.src)
        self.assertEqual(segv.dest, None, segv.dest)

        regs = 'esp 0x444'
        disasm = '0x08083560 <main+0>:    push %ecx\n'
        segv = parse_segv.ParseSegv(regs, disasm, '')
        self.assertEqual(segv.pc, 0x08083560, segv.pc)
        self.assertEqual(segv.insn, 'push', segv.insn)
        self.assertEqual(segv.src, '%ecx', segv.src)
        self.assertEqual(segv.dest, '(%esp)', segv.dest)

        # GDB 7.1
        regs = 'esp 0x444'
        disasm = '=> 0x08083560 <main+0>:    push %ecx\n'
        segv = parse_segv.ParseSegv(regs, disasm, '')
        self.assertEqual(segv.pc, 0x08083560, segv.pc)
        self.assertEqual(segv.insn, 'push', segv.insn)
        self.assertEqual(segv.src, '%ecx', segv.src)
        self.assertEqual(segv.dest, '(%esp)', segv.dest)

    def test_ioport_operation(self):
        '''I/O port violations'''

        regs = 'rax            0x3  3'
        disasm = '''0x4087f1 <snd_pcm_hw_params_set_channels_near@plt+19345>:
    out    %al,$0xb3
'''
        maps = '''00400000-00412000 r-xp 00000000 08:04 10371157                           /usr/sbin/pommed
00611000-00614000 rw-p 00011000 08:04 10371157                           /usr/sbin/pommed
00614000-00635000 rw-p 00614000 00:00 0                                  [heap]
'''
        segv = parse_segv.ParseSegv(regs, disasm, maps)
        self.assertEqual(segv.pc, 0x4087f1, segv.pc)
        self.assertEqual(segv.insn, 'out', segv.insn)
        self.assertEqual(segv.src, '%al', segv.src)
        self.assertEqual(segv.dest, '$0xb3', segv.dest)

        understood, reason, details = segv.report()
        self.assertTrue(understood, details)
        self.assertTrue('disallowed I/O port operation on port 3' in reason, reason)

    def test_invalid_02_maps(self):
        '''Require valid maps'''
        regs = 'a 0x10'
        disasm = 'Dump ...\n0x08083540 <main+0>:    lea    0x4(%esp),%ecx\n'

        maps = 'asdlkfjaadf'
        self.assertRaises(ValueError, parse_segv.ParseSegv, regs, disasm, maps)

        maps = '''005a3000-005a4000 rw-p 00035000 08:06 65575      /lib/libncurses.so.5.7
00b67000-00b68000 r-xp 00000000 00:00 0          [vdso]
00c67000-00c68000 r--p 00000000 00:00 0 '''
        segv = parse_segv.ParseSegv(regs, disasm, maps)
        self.assertEqual(segv.maps[0]['start'], 0x005a3000, segv)
        self.assertEqual(segv.maps[0]['end'], 0x005a4000, segv)
        self.assertEqual(segv.maps[0]['perms'], 'rw-p', segv)
        self.assertEqual(segv.maps[0]['name'], '/lib/libncurses.so.5.7', segv)

        self.assertEqual(segv.maps[1]['start'], 0x00b67000, segv)
        self.assertEqual(segv.maps[1]['end'], 0x00b68000, segv)
        self.assertEqual(segv.maps[1]['perms'], 'r-xp', segv)
        self.assertEqual(segv.maps[1]['name'], '[vdso]', segv)

        self.assertEqual(segv.maps[2]['start'], 0x00c67000, segv)
        self.assertEqual(segv.maps[2]['end'], 0x00c68000, segv)
        self.assertEqual(segv.maps[2]['perms'], 'r--p', segv)
        self.assertEqual(segv.maps[2]['name'], None, segv)

    def test_debug(self):
        '''Debug mode works'''

        regs = 'a 0x10'
        disasm = 'Dump ...\n0x08083540 <main+0>:    lea    0x4(%esp),%ecx\n'
        maps = '''005a3000-005a4000 rw-p 00035000 08:06 65575      /lib/libncurses.so.5.7
00b67000-00b68000 r-xp 00000000 00:00 0          [vdso]
00c67000-00c68000 r--p 00000000 00:00 0 '''

        sys.stderr = tempfile.NamedTemporaryFile(prefix='parse_segv-stderr-')
        segv = parse_segv.ParseSegv(regs, disasm, maps, debug=True)
        self.assertTrue(segv is not None, segv)

    def test_register_values(self):
        '''Sub-register parsing'''

        disasm = '''0x08083540 <main+0>:    mov    $1,%ecx'''
        segv = parse_segv.ParseSegv(regs64, disasm, '')

        val = segv.register_value('%rdx')
        self.assertEqual(val, 0xffffffffff600180, hex(val))
        val = segv.register_value('%edx')
        self.assertEqual(val, 0xff600180, hex(val))
        val = segv.register_value('%dx')
        self.assertEqual(val, 0x0180, hex(val))
        val = segv.register_value('%dl')
        self.assertEqual(val, 0x80, hex(val))

    def test_segv_unknown(self):
        '''Handles unknown segfaults'''

        disasm = '''0x08083540 <main+0>:    mov    $1,%ecx'''
        segv = parse_segv.ParseSegv(regs, disasm, maps)
        understood, reason, details = segv.report()
        self.assertFalse(understood, details)

        # Verify calculations
        self.assertEqual(segv.calculate_arg('(%ecx)'), 0xbfc6af40, segv.regs['ecx'])
        self.assertEqual(segv.calculate_arg('0x10(%ecx)'), 0xbfc6af50, segv.regs['ecx'])
        self.assertEqual(segv.calculate_arg('-0x20(%ecx)'), 0xbfc6af20, segv.regs['ecx'])
        self.assertEqual(segv.calculate_arg('%fs:(%ecx)'), 0xbfc6af44, segv.regs['ecx'])
        self.assertEqual(segv.calculate_arg('0x3404403'), 0x3404403, '0x3404403')
        self.assertEqual(segv.calculate_arg('*0x40(%edi)'), 0x80834c0, segv.regs['edi'])
        self.assertEqual(segv.calculate_arg('(%edx,%ebx,1)'), 0x26eff5, segv.regs['ebx'])
        self.assertEqual(segv.calculate_arg('(%eax,%ebx,1)'), 0x26eff3, segv.regs['ebx'])
        self.assertEqual(segv.calculate_arg('0x10(,%ebx,1)'), 0x26f004, segv.regs['ebx'])

        # Again, but 64bit
        disasm = '''0x08083540 <main+0>:    mov    $1,%rcx'''
        segv = parse_segv.ParseSegv(regs64, disasm, maps)
        understood, reason, details = segv.report()
        self.assertFalse(understood, details)

        self.assertEqual(segv.calculate_arg('(%rax,%rbx,1)'), 0x26eff3, segv.regs['rbx'])

    def test_segv_pc_missing(self):
        '''Handles PC in missing VMA'''

        disasm = '''0x00083540 <main+0>:    lea    0x4(%esp),%ecx'''
        segv = parse_segv.ParseSegv(regs, disasm, maps)
        understood, reason, details = segv.report()
        self.assertTrue(understood, details)
        self.assertTrue('PC (0x00083540) not located in a known VMA region' in details, details)
        self.assertTrue('executing unknown VMA' in reason, reason)

        disasm = '''0x00083544:'''
        segv = parse_segv.ParseSegv(regs, disasm, maps)
        understood, reason, details = segv.report()
        self.assertTrue(understood, details)
        self.assertTrue('PC (0x00083544) not located in a known VMA region' in details, details)
        self.assertTrue('executing unknown VMA' in reason, reason)

    def test_segv_pc_null(self):
        '''Handles PC in NULL VMA'''

        disasm = '''0x00000540 <main+0>:    lea    0x4(%esp),%ecx'''
        segv = parse_segv.ParseSegv(regs, disasm, maps)
        understood, reason, details = segv.report()
        self.assertTrue(understood, details)
        self.assertTrue('PC (0x00000540) not located in a known VMA region' in details, details)
        self.assertTrue('executing NULL VMA' in reason, reason)

    def test_segv_pc_nx_writable(self):
        '''Handles PC in writable NX VMA'''

        disasm = '''0x005a3000 <main+0>:    lea    0x4(%esp),%ecx'''
        segv = parse_segv.ParseSegv(regs, disasm, maps)
        understood, reason, details = segv.report()
        self.assertTrue(understood, details)
        self.assertTrue('PC (0x005a3000) in non-executable VMA region:' in details, details)
        self.assertTrue('executing writable VMA /lib/libncurses.so.5.7' in reason, reason)

    def test_segv_pc_nx_unwritable(self):
        '''Handles PC in non-writable NX VMA'''

        disasm = '''0x00dfb000 <main+0>:    lea    0x4(%esp),%ecx'''
        segv = parse_segv.ParseSegv(regs, disasm, maps)
        understood, reason, details = segv.report()
        self.assertTrue(understood, details)
        self.assertTrue('PC (0x00dfb000) in non-executable VMA region:' in details, details)
        self.assertTrue('executing non-writable VMA /lib/libreadline.so.5.2' in reason, reason)

    def test_segv_src_missing(self):
        '''Handles source in missing VMA'''

        reg = regs + 'ecx            0x0006af24   0xbfc6af24'
        disasm = '0x08083547 <main+7>:    pushl  -0x4(%ecx)'

        # Valid crash
        segv = parse_segv.ParseSegv(reg, disasm, maps)
        understood, reason, details = segv.report()
        self.assertTrue(understood, details)
        self.assertTrue('source "-0x4(%ecx)" (0x0006af20) not located in a known VMA region' in details, details)
        self.assertTrue('reading unknown VMA' in reason, reason)

        # Valid crash
        disasm = '0x08083547 <main+7>:    callq  *%ecx'
        segv = parse_segv.ParseSegv(reg, disasm, maps)
        understood, reason, details = segv.report()
        self.assertTrue(understood, details)
        self.assertTrue('source "*%ecx" (0x0006af24) not located in a known VMA region' in details, details)
        self.assertTrue('reading unknown VMA' in reason, reason)

    def test_segv_src_null(self):
        '''Handles source in NULL VMA'''

        reg = regs + 'ecx            0x00000024   0xbfc6af24'
        disasm = '0x08083547 <main+7>:    pushl  -0x4(%ecx)'

        segv = parse_segv.ParseSegv(reg, disasm, maps)
        understood, reason, details = segv.report()
        self.assertTrue(understood, details)
        self.assertTrue('source "-0x4(%ecx)" (0x00000020) not located in a known VMA region' in details, details)
        self.assertTrue('reading NULL VMA' in reason, reason)

    def test_segv_src_not_readable(self):
        '''Handles source not in readable VMA'''

        reg = regs + 'ecx            0x0026c080   0xbfc6af24'
        disasm = '0x08083547 <main+7>:    pushl  -0x4(%ecx)'
        segv = parse_segv.ParseSegv(reg, disasm, maps)
        understood, reason, details = segv.report()
        self.assertTrue(understood, details)
        self.assertTrue('source "-0x4(%ecx)" (0x0026c07c) in non-readable VMA region:' in details, details)
        self.assertTrue('reading VMA /lib/tls/i686/cmov/libc-2.9.so' in reason, reason)
        self.assertFalse('Stack memory exhausted' in details, details)
        self.assertFalse('Stack pointer not within stack segment' in details, details)

    def test_segv_dest_missing(self):
        '''Handles destintation in missing VMA'''

        reg = regs + 'esp            0x0006af24   0xbfc6af24'
        disasm = '0x08083547 <main+7>:    pushl  -0x4(%ecx)'

        segv = parse_segv.ParseSegv(reg, disasm, maps)
        understood, reason, details = segv.report()
        self.assertTrue(understood, details)
        self.assertTrue('destination "(%esp)" (0x0006af24) not located in a known VMA region' in details, details)
        self.assertTrue('writing unknown VMA' in reason, reason)

    def test_segv_dest_null(self):
        '''Handles destintation in NULL VMA'''

        reg = regs + 'esp            0x00000024   0xbfc6af24'
        disasm = '0x08083547 <main+7>:    pushl  -0x4(%ecx)'

        segv = parse_segv.ParseSegv(reg, disasm, maps)
        understood, reason, details = segv.report()
        self.assertTrue(understood, details)
        self.assertTrue('destination "(%esp)" (0x00000024) not located in a known VMA region' in details, details)
        self.assertTrue('writing NULL VMA' in reason, reason)

    def test_segv_dest_not_writable(self):
        '''Handles destination not in writable VMA'''

        reg = regs + 'esp            0x08048080   0xbfc6af24'
        disasm = '0x08083547 <main+7>:    pushl  -0x4(%ecx)'
        segv = parse_segv.ParseSegv(reg, disasm, maps)
        understood, reason, details = segv.report()
        self.assertTrue(understood, details)
        self.assertTrue('destination "(%esp)" (0x08048080) in non-writable VMA region:' in details, details)
        self.assertTrue('writing VMA /usr/bin/gdb' in reason, reason)

    def test_segv_crackful_disasm(self):
        '''Rejects insane disassemblies'''

        disasm = '0x08083547 <main+7>:    pushl  -0x4(blah)'
        segv = parse_segv.ParseSegv(regs, disasm, maps)
        self.assertRaises(ValueError, segv.report)

        disasm = '0x08083547 <main+7>:    pushl  -04(%ecx)'
        segv = parse_segv.ParseSegv(regs, disasm, maps)
        self.assertRaises(ValueError, segv.report)

    def test_segv_stack_failure(self):
        '''Handles walking off the stack'''

        # Triggered via "push"
        reg = regs + 'esp            0xbfc56ff0   0xbfc56ff0'
        disasm = '0x08083547 <main+7>:    push  %eax'
        segv = parse_segv.ParseSegv(reg, disasm, maps)
        understood, reason, details = segv.report()
        self.assertTrue(understood, details)
        self.assertTrue('destination "(%esp)" (0xbfc56ff0) not located in a known VMA region (needed writable region)!' in details, details)

        # Triggered via "call"
        reg = regs + 'esp            0xbfc56fff   0xbfc56fff'
        disasm = '0x08083547 <main+7>:    callq  0x08083540'
        segv = parse_segv.ParseSegv(reg, disasm, maps)
        understood, reason, details = segv.report()
        self.assertTrue(understood, details)
        self.assertTrue('destination "(%esp)" (0xbfc56fff) not located in a known VMA region (needed writable region)!' in details, details)
        self.assertTrue('Stack memory exhausted' in details, details)

        # Triggered via unknown reason
        reg = regs + 'esp            0xdfc56000   0xdfc56000'
        disasm = '''0x08083540 <main+0>:    mov    $1,%rcx'''
        segv = parse_segv.ParseSegv(reg, disasm, maps)
        understood, reason, details = segv.report()
        self.assertTrue(understood, details)
        self.assertTrue('SP (0xdfc56000) not located in a known VMA region (needed readable region)!' in details, details)
        self.assertTrue('Stack pointer not within stack segment' in details, details)

    def test_segv_stack_kernel_segfault(self):
        '''Handles unknown segfaults in kernel'''

        # Crash in valid code path
        disasm = '''0x0056e010: ret'''
        segv = parse_segv.ParseSegv(regs, disasm, maps)
        understood, reason, details = segv.report()
        self.assertFalse(understood, details)
        self.assertTrue('Reason could not be automatically determined.' in details, details)
        self.assertFalse('(Unhandled exception in kernel code?)' in details, details)

        # Crash from kernel code path
        disasm = '''0x00b67422 <__kernel_vsyscall+2>: ret'''
        segv = parse_segv.ParseSegv(regs, disasm, maps)
        understood, reason, details = segv.report()
        self.assertFalse(understood, details)
        self.assertTrue('Reason could not be automatically determined. (Unhandled exception in kernel code?)' in details, details)


unittest.main()