diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34289b3..a7856c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,9 @@ jobs: - name: flake8 run: pipenv run flake8 - name: pylint - run: pipenv run pylint src + run: | + pipenv run pylint src test + pipenv run pylint -d duplicate-code,invalid-name test/templates/*.py Coverage: needs: [Lint, Baseline] runs-on: ubuntu-latest diff --git a/test/templates/common.py b/test/templates/common.py index 5b5704c..e1dd3cd 100644 --- a/test/templates/common.py +++ b/test/templates/common.py @@ -1,31 +1,34 @@ +""" +Common code shared by all generators +""" + import subprocess import sys import re import os -_me = os.path.basename(__file__) -verbose = 0 +_ME = os.path.basename(__file__) def set_basename(file): - global _me - _me = file + global _ME # pylint: disable=global-statement + _ME = file def error(msg): """ Print nicely-formatted error message and exit. """ - sys.stderr.write(f'{_me}: error: {msg}\n') + sys.stderr.write(f'{_ME}: error: {msg}\n') sys.exit(1) -def _run(cmd, stdin=None): +def _run(cmd, stdin=None, verbose=0): """ Run process and abort on error. """ if verbose: - print(f"{_me}: running command: {' '.join(cmd)}") + print(f"{_ME}: running command: {' '.join(cmd)}") with subprocess.Popen(cmd, stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as process: out, err = process.communicate() @@ -65,15 +68,15 @@ def strip_binary(file): _run(['strip', '-s', file]) -def grep(s, regex): - lines = s.split('\n') +def grep(text, regex): + lines = text.split('\n') return list(filter(lambda s: re.search(regex, s), lines)) def find_address(file, name): out = _run(['readelf', '-sW', file]) lines = grep(out, fr'{name}$') - assert len(lines) >= 1, f"failed to locate symbol {name} in\n{out}" + assert len(lines) >= 1, f'failed to locate symbol {name} in\n{out}' line = lines[0] # Num: Value Size Type Bind Vis Ndx Name # 27: 0000000000001030 11 FUNC GLOBAL DEFAULT 9 foo diff --git a/test/templates/gen_calls.py b/test/templates/gen_calls.py index 8554e28..436026c 100755 --- a/test/templates/gen_calls.py +++ b/test/templates/gen_calls.py @@ -26,7 +26,7 @@ call_type = 'Non-PIC-call' if direct else 'PIC-call' strip_type = 'stripped' if strip else 'UNstripped' plt_type = 'PLT' if plt else 'PLT-less' - print(f"Checking {disasm_type} {pic_type} {plt_type} {call_type} {strip_type}") + print(f'Checking {disasm_type} {pic_type} {plt_type} {call_type} {strip_type}') # Generate object code diff --git a/test/templates/gen_funtable.py b/test/templates/gen_funtable.py index 896149f..05a52b1 100755 --- a/test/templates/gen_funtable.py +++ b/test/templates/gen_funtable.py @@ -19,7 +19,7 @@ disasm_type = 'GDB' if gdb else 'objdump' pic_type = 'position-INdependent' if pic else 'position-dependent' stripped = 'stripped' if strip else 'UNstripped' - print(f"Checking {disasm_type} {pic_type} {stripped}") + print(f'Checking {disasm_type} {pic_type} {stripped}') # Generate object code diff --git a/test/templates/gen_jumps.py b/test/templates/gen_jumps.py index b19400f..2bd7f74 100755 --- a/test/templates/gen_jumps.py +++ b/test/templates/gen_jumps.py @@ -21,7 +21,7 @@ pic_type = 'position-INdependent' if pic else 'position-dependent' opt_type = 'optimized' if opt else 'UNoptimized' stripped = 'stripped' if strip else 'UNstripped' - print(f"Checking {disasm_type} {pic_type} {opt_type} {stripped}") + print(f'Checking {disasm_type} {pic_type} {opt_type} {stripped}') # Generate object code diff --git a/test/templates/gen_jumptable.py b/test/templates/gen_jumptable.py index 81a173e..3bd3710 100755 --- a/test/templates/gen_jumptable.py +++ b/test/templates/gen_jumptable.py @@ -19,7 +19,7 @@ disasm_type = 'GDB' if gdb else 'objdump' pic_type = 'position-INdependent' if pic else 'position-dependent' stripped = 'stripped' if strip else 'UNstripped' - print(f"Checking {disasm_type} {pic_type} {stripped}") + print(f'Checking {disasm_type} {pic_type} {stripped}') # Generate object code diff --git a/test/test_parser.py b/test/test_parser.py index 6159b1d..a73be1e 100644 --- a/test/test_parser.py +++ b/test/test_parser.py @@ -1,3 +1,7 @@ +""" +Unittests of asm2cfg's assembly parser +""" + import unittest from src.asm2cfg import asm2cfg @@ -9,6 +13,10 @@ def _get_the_source_block(blocks): class ParseLineTestCase(unittest.TestCase): + """ + Tests of parse_line function + """ + @unittest.expectedFailure def test_linear_sequence(self): lines = '''\ @@ -79,10 +87,10 @@ def test_conditional(self): self.assertEqual(len(dst_block.instructions), 2) @unittest.expectedFailure - def test_jumptables(): + def test_jumptables(self): lines = '''\ Dump of assembler code for function bar: - 0x0000000000001070 <+0>: endbr64 + 0x0000000000001070 <+0>: endbr64 0x0000000000001074 <+4>: cmp $0x9,%edi 0x0000000000001077 <+7>: ja 0x1120 0x000000000000107d <+13>: lea 0xf80(%rip),%rdx # 0x2004 @@ -96,44 +104,42 @@ def test_jumptables(): 0x000000000000109a <+42>: callq 0x1040 0x000000000000109f <+47>: mov $0xc,%eax 0x00000000000010a4 <+52>: add $0x8,%rsp - 0x00000000000010a8 <+56>: retq + 0x00000000000010a8 <+56>: retq 0x00000000000010a9 <+57>: nopl 0x0(%rax) 0x00000000000010b0 <+64>: lea 0xf49(%rip),%rdi # 0x2000 0x00000000000010b7 <+71>: xor %eax,%eax 0x00000000000010b9 <+73>: callq 0x1030 0x00000000000010be <+78>: mov $0xffffffe7,%eax 0x00000000000010c3 <+83>: add $0x8,%rsp - 0x00000000000010c7 <+87>: retq + 0x00000000000010c7 <+87>: retq 0x00000000000010c8 <+88>: nopl 0x0(%rax,%rax,1) 0x00000000000010d0 <+96>: mov $0xffffffe5,%eax 0x00000000000010d5 <+101>: add $0x8,%rsp - 0x00000000000010d9 <+105>: retq + 0x00000000000010d9 <+105>: retq 0x00000000000010da <+106>: nopw 0x0(%rax,%rax,1) 0x00000000000010e0 <+112>: mov $0x21,%eax 0x00000000000010e5 <+117>: add $0x8,%rsp - 0x00000000000010e9 <+121>: retq + 0x00000000000010e9 <+121>: retq 0x00000000000010ea <+122>: nopw 0x0(%rax,%rax,1) 0x00000000000010f0 <+128>: mov $0x23,%eax 0x00000000000010f5 <+133>: add $0x8,%rsp - 0x00000000000010f9 <+137>: retq + 0x00000000000010f9 <+137>: retq 0x00000000000010fa <+138>: nopw 0x0(%rax,%rax,1) 0x0000000000001100 <+144>: mov $0x63,%eax 0x0000000000001105 <+149>: add $0x8,%rsp - 0x0000000000001109 <+153>: retq + 0x0000000000001109 <+153>: retq 0x000000000000110a <+154>: nopw 0x0(%rax,%rax,1) 0x0000000000001110 <+160>: mov $0xa,%eax 0x0000000000001115 <+165>: add $0x8,%rsp - 0x0000000000001119 <+169>: retq + 0x0000000000001119 <+169>: retq 0x000000000000111a <+170>: nopw 0x0(%rax,%rax,1) - 0x0000000000001120 <+176>: retq -''' + 0x0000000000001120 <+176>: retq +'''.split('\n') _, blocks = asm2cfg.parse_lines(lines, False) # TODO: special block for indirect jumps - self.assertEqual(len(blocks), 2) - + self.assertEqual(len(blocks), 4) # TODO: # - functions (with and w/o calls) - # - jumptables # - skip calls diff --git a/test/test_regex.py b/test/test_regex.py index a267047..113b0de 100644 --- a/test/test_regex.py +++ b/test/test_regex.py @@ -1,9 +1,17 @@ +""" +Unittests of asm2cfg's regexes +""" + import unittest from src.asm2cfg import asm2cfg class FunctionHeaderTestCase(unittest.TestCase): + """ + Tests of function header regex + """ + def test_gdb_unstripped(self): line = 'Dump of assembler code for function test_function:' strip, fun = asm2cfg.get_stripped_and_function_name(line) @@ -28,6 +36,10 @@ def test_objdump(self): class CallPatternTestCase(unittest.TestCase): + """ + Tests of call instruction regex + """ + def setUp(self): self.strip_regex = asm2cfg.get_call_pattern(True) self.unstrip_regex = asm2cfg.get_call_pattern(False) @@ -144,6 +156,10 @@ def test_objdump_indirect_call(self): class JumpPatternTestCase(unittest.TestCase): + """ + Tests of jump instruction regex + """ + def test_gdb_stripped(self): line = '0x000055555555600f: jmp 0x55555555603d' pattern = asm2cfg.get_jump_pattern(True, 'does_not_matter')