Skip to content

Commit

Permalink
Fix overwrite merging & add artifact CI (#26)
Browse files Browse the repository at this point in the history
* Fix overwrite merging

* Add CI for artifacts

* Fix naming

* minor bump
  • Loading branch information
mahaloz authored Jan 28, 2024
1 parent 033f570 commit e4353e8
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 11 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/artifact-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: Artifact Tests

on:
push:
branches: [ main ]
paths: ['libbs/artifacts/**']
pull_request:
branches: [ main ]
paths: ['libbs/artifacts/**']

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python 3.11
uses: actions/setup-python@v2
with:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
pip install .
- name: Pytest
run: |
pytest ./tests/test_artifacts.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: Headless Dec Tests
name: Decompiler Tests

on:
push:
Expand Down Expand Up @@ -34,4 +34,4 @@ jobs:
- name: Pytest
run: |
export GHIDRA_HEADLESS_PATH="$GHIDRA_INSTALL_DIR/support/analyzeHeadless"
pytest ./tests/tests.py
pytest ./tests/test_decompilers.py
2 changes: 1 addition & 1 deletion libbs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "0.21.0"
__version__ = "0.21.1"

import logging
logging.getLogger("libbs").addHandler(logging.NullHandler())
Expand Down
19 changes: 11 additions & 8 deletions libbs/artifacts/func.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,17 +346,20 @@ def overwrite_merge(self, obj2: "Artifact", **kwargs):
if not func2 or self == func2:
return merged_func

if func2.header is not None:
if merged_func.header is None:
merged_func.header = func2.header.copy() if func2.header else None

if merged_func.header:
merged_func.header = merged_func.header.overwrite_merge(func2.header)

for off, var in func2.stack_vars.items():
if var is not None:
if off in merged_func.stack_vars:
merged_var = merged_func.stack_vars[off].overwrite_merge(var)
else:
merged_var = var
for off, var in func2.stack_vars.items():
if var is not None:
if off in merged_func.stack_vars:
merged_var = merged_func.stack_vars[off].overwrite_merge(var)
else:
merged_var = var

merged_func.stack_vars[off] = merged_var
merged_func.stack_vars[off] = merged_var

return merged_func

Expand Down
130 changes: 130 additions & 0 deletions tests/test_artifacts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import sys
import json

import unittest

from libbs.artifacts import (
FunctionHeader, StackVariable, FunctionArgument, Function
)


class TestArtifacts(unittest.TestCase):
def test_func_diffing(self):
# setup top
func_addr = 0x400000
fh1 = FunctionHeader("main", func_addr, type_="int *", args={
0: FunctionArgument(0, "a1", "int", 4), 1: FunctionArgument(1, "a2", "long", 8)
})
fh2 = FunctionHeader("binsync_main", func_addr, type_="long *", args={
0: FunctionArgument(0, "a1", "int", 4), 1: FunctionArgument(1, "a2", "int", 4)
})

stack_vars1 = {
0x0: StackVariable(0, "v0", "int", 4, func_addr),
0x4: StackVariable(4, "v4", "int", 4, func_addr)
}
stack_vars2 = {
0x0: StackVariable(0, "v0", "int", 4, func_addr),
0x4: StackVariable(4, "v4", "long", 8, func_addr),
0x8: StackVariable(8, "v8", "long", 8, func_addr)
}

func1 = Function(func_addr, 0x100, header=fh1, stack_vars=stack_vars1)
func2 = Function(func_addr, 0x150, header=fh2, stack_vars=stack_vars2)

diff_dict = func1.diff(func2)
header_diff = diff_dict["header"]
vars_diff = diff_dict["stack_vars"]

# size should not match
assert func1.size != func2.size
assert diff_dict["size"]["before"] == func1.size
assert diff_dict["size"]["after"] == func2.size

# names should not match
assert header_diff["name"]["before"] == func1.name
assert header_diff["name"]["after"] == func2.name

# arg1 should match
assert not header_diff["args"][0]

# arg2 should not match
assert header_diff["args"][1]["type"]["before"] != header_diff["args"][1]["type"]["after"]

# v4 and v8 should differ
offsets = [0, 4, 8]
for off in offsets:
var_diff = vars_diff[off]
if off == 0:
assert not var_diff
if off == 0x4:
assert var_diff["size"]["before"] != var_diff["size"]["after"]
elif off == 0x8:
assert var_diff["addr"]["before"] is None
assert var_diff["addr"]["after"] == func1.addr

print(json.dumps(diff_dict, sort_keys=False, indent=4))

def test_func_nonconflict_merge(self):
# setup top
func_addr = 0x400000
fh1 = FunctionHeader("user1_func", func_addr, type_="int *", args={})
fh2 = FunctionHeader("main", func_addr, type_="long *", args={})

stack_vars1 = {
0x0: StackVariable(0, "v0", "int", 4, func_addr),
0x4: StackVariable(4, "my_var", "int", 4, func_addr)
}
stack_vars2 = {
0x0: StackVariable(0, "v0", "int", 4, func_addr),
0x4: StackVariable(4, "v4", "long", 8, func_addr),
0x8: StackVariable(8, "v8", "long", 8, func_addr)
}

func1 = Function(func_addr, 0x100, header=fh1, stack_vars=stack_vars1)
func2 = Function(func_addr, 0x100, header=fh2, stack_vars=stack_vars2)
merge_func = func1.nonconflict_merge(func2)

assert merge_func.name == "user1_func"
assert merge_func.header.type == "int *"
assert merge_func.stack_vars[0].name == "v0"
assert merge_func.stack_vars[4].name == "my_var"
assert merge_func.stack_vars[4].type == "int"
assert merge_func.stack_vars[8].name == "v8"

def test_func_overwrite_merge(self):
func_addr = 0x400000
func_size = 0x100
fh1 = FunctionHeader("main", func_addr, type_="int *", args={
0: FunctionArgument(0, "a1", "int", 4)
})
fh2 = FunctionHeader("binsync_main", func_addr, type_="long *", args={
1: FunctionArgument(1, "bs_2", "int", 4)
})

stack_vars1 = {
0x0: StackVariable(0, "v0", "int", 4, func_addr),
0x4: StackVariable(4, "v4", "long", 8, func_addr),
0x8: StackVariable(8, "v8", "long", 8, func_addr)
}
stack_vars2 = {
0x0: StackVariable(0, "v0", "long", 4, func_addr),
0x4: StackVariable(4, "my_var", "int", 4, func_addr)
}

func1 = Function(func_addr, func_size, header=fh1, stack_vars=stack_vars1)
func2 = Function(func_addr, func_size, header=fh2, stack_vars=stack_vars2)

merge_func = func1.overwrite_merge(func2)

assert merge_func.size == func1.size == func2.size
assert merge_func.name == func2.name
assert merge_func.header.args[0].name == func1.header.args[0].name
assert merge_func.stack_vars[0].name == stack_vars1[0].name
assert merge_func.stack_vars[0].type == stack_vars2[0].type
assert merge_func.stack_vars[0x4] == stack_vars2[0x4]
assert merge_func.stack_vars[0x8] == stack_vars1[0x8]


if __name__ == "__main__":
unittest.main(argv=sys.argv)
File renamed without changes.

0 comments on commit e4353e8

Please sign in to comment.