rizin/test/unit/test_analysis_block.c

764 lines
28 KiB
C

#include <rz_analysis.h>
#include <rz_core.h>
#include "minunit.h"
#include "mock_io.inl"
#include "test_analysis_block_invars.inl"
#define check_invariants block_check_invariants
#define check_leaks block_check_leaks
static size_t blocks_count(RzAnalysis *analysis) {
size_t count = 0;
RBIter iter;
RzAnalysisBlock *block;
rz_rbtree_foreach(analysis->bb_tree, iter, block, RzAnalysisBlock, _rb) {
count++;
}
return count;
}
bool test_rz_analysis_block_create() {
RzAnalysis *analysis = rz_analysis_new ();
assert_block_invariants (analysis);
mu_assert_eq (blocks_count (analysis), 0, "initial count");
RzAnalysisBlock *block = rz_analysis_create_block (analysis, 0x1337, 42);
assert_block_invariants (analysis);
mu_assert ("created block", block);
mu_assert_eq (block->addr, 0x1337, "created addr");
mu_assert_eq (block->size, 42, "created size");
mu_assert_eq (block->ref, 1, "created initial ref");
mu_assert_eq (blocks_count (analysis), 1, "count after create");
RzAnalysisBlock *block2 = rz_analysis_create_block (analysis, 0x133f, 100);
assert_block_invariants (analysis);
mu_assert ("created block (overlap)", block2);
mu_assert_eq (block2->addr, 0x133f, "created addr");
mu_assert_eq (block2->size, 100, "created size");
mu_assert_eq (block2->ref, 1, "created initial ref");
mu_assert_eq (blocks_count (analysis), 2, "count after create");
RzAnalysisBlock *block3 = rz_analysis_create_block (analysis, 0x1337, 5);
assert_block_invariants (analysis);
mu_assert ("no double create on same start", !block3);
mu_assert_eq (blocks_count (analysis), 2, "count after failed create");
rz_analysis_block_unref (block);
rz_analysis_block_unref (block2);
assert_block_leaks (analysis);
rz_analysis_free (analysis);
mu_end;
}
bool test_rz_analysis_block_contains() {
RzAnalysisBlock dummy = { 0 };
dummy.addr = 0x1337;
dummy.size = 42;
mu_assert ("contains before", !rz_analysis_block_contains (&dummy, 100));
mu_assert ("contains start", rz_analysis_block_contains (&dummy, 0x1337));
mu_assert ("contains inside", rz_analysis_block_contains (&dummy, 0x1339));
mu_assert ("contains last", rz_analysis_block_contains (&dummy, 0x1337 + 42 - 1));
mu_assert ("contains after", !rz_analysis_block_contains (&dummy, 0x1337 + 42));
mu_end;
}
bool test_rz_analysis_block_split() {
RzAnalysis *analysis = rz_analysis_new ();
assert_block_invariants (analysis);
RzAnalysisBlock *block = rz_analysis_create_block (analysis, 0x1337, 42);
assert_block_invariants (analysis);
mu_assert_eq (blocks_count (analysis), 1, "count after create");
block->jump = 0xdeadbeef;
block->fail = 0xc0ffee;
block->ninstr = 5;
rz_analysis_block_set_op_offset (block, 0, 0);
rz_analysis_block_set_op_offset (block, 1, 1);
rz_analysis_block_set_op_offset (block, 2, 2);
rz_analysis_block_set_op_offset (block, 3, 4);
rz_analysis_block_set_op_offset (block, 4, 30);
RzAnalysisBlock *second = rz_analysis_block_split (block, 0x1337);
assert_block_invariants (analysis);
mu_assert_ptreq (second, block, "nop split on first addr");
mu_assert_eq (blocks_count (analysis), 1, "count after nop split");
mu_assert_eq (block->ref, 2, "ref after nop split");
rz_analysis_block_unref (block);
second = rz_analysis_block_split (block, 0x1339);
assert_block_invariants (analysis);
mu_assert_ptrneq (second, block, "non-nop split");
mu_assert_eq (blocks_count (analysis), 2, "count after non-nop split");
mu_assert_eq (block->addr, 0x1337, "first addr after split");
mu_assert_eq (block->size, 2, "first size after split");
mu_assert_eq (second->addr, 0x1339, "first addr after split");
mu_assert_eq (second->size, 40, "first size after split");
mu_assert_eq (block->jump, second->addr, "first jump");
mu_assert_eq (block->fail, UT64_MAX, "first fail");
mu_assert_eq (second->jump, 0xdeadbeef, "second jump");
mu_assert_eq (second->fail, 0xc0ffee, "second fail");
mu_assert_eq (block->ninstr, 2, "first ninstr after split");
mu_assert_eq (rz_analysis_block_get_op_offset (block, 0), 0, "first op_pos[0]");
mu_assert_eq (rz_analysis_block_get_op_offset (block, 1), 1, "first op_pos[1]");
mu_assert_eq (second->ninstr, 3, "second ninstr after split");
mu_assert_eq (rz_analysis_block_get_op_offset (second, 0), 0, "second op_pos[0]");
mu_assert_eq (rz_analysis_block_get_op_offset (second, 1), 2, "second op_pos[1]");
mu_assert_eq (rz_analysis_block_get_op_offset (second, 2), 28, "second op_pos[2]");
rz_analysis_block_unref (block);
rz_analysis_block_unref (second);
assert_block_leaks (analysis);
rz_analysis_free (analysis);
mu_end;
}
bool test_rz_analysis_block_split_in_function() {
RzAnalysis *analysis = rz_analysis_new ();
assert_block_invariants (analysis);
RzAnalysisFunction *fcn = rz_analysis_create_function (analysis, "bbowner", 0x1337, 0, NULL);
assert_block_invariants (analysis);
RzAnalysisBlock *block = rz_analysis_create_block (analysis, 0x1337, 42);
assert_block_invariants (analysis);
mu_assert_eq (blocks_count (analysis), 1, "count after create");
rz_analysis_function_add_block (fcn, block);
assert_block_invariants (analysis);
mu_assert_eq (block->ref, 2, "block refs after adding to function");
RzAnalysisBlock *second = rz_analysis_block_split (block, 0x1339);
assert_block_invariants (analysis);
mu_assert_ptrneq (second, block, "non-nop split");
mu_assert_eq (blocks_count (analysis), 2, "count after non-nop split");
mu_assert_eq (block->ref, 2, "first block refs after adding to function");
mu_assert_eq (second->ref, 2, "second block refs after adding to function");
mu_assert ("function has first block after split", rz_list_contains (fcn->bbs, block));
mu_assert ("function has second block after split", rz_list_contains (fcn->bbs, second));
mu_assert ("second block is in function after split", rz_list_contains (block->fcns, fcn));
mu_assert ("second block is in function after split", rz_list_contains (second->fcns, fcn));
rz_analysis_block_unref (block);
rz_analysis_block_unref (second);
assert_block_leaks (analysis);
rz_analysis_free (analysis);
mu_end;
}
bool test_rz_analysis_block_merge() {
RzAnalysis *analysis = rz_analysis_new ();
assert_block_invariants (analysis);
RzAnalysisBlock *first = rz_analysis_create_block (analysis, 0x1337, 42);
RzAnalysisBlock *second = rz_analysis_create_block (analysis, 0x1337 + 42, 624);
assert_block_invariants (analysis);
mu_assert_eq (blocks_count (analysis), 2, "count after create");
second->jump = 0xdeadbeef;
second->fail = 0xc0ffee;
first->ninstr = 3;
rz_analysis_block_set_op_offset (first, 0, 0);
rz_analysis_block_set_op_offset (first, 1, 13);
rz_analysis_block_set_op_offset (first, 2, 16);
second->ninstr = 4;
rz_analysis_block_set_op_offset (second, 0, 0);
rz_analysis_block_set_op_offset (second, 1, 4);
rz_analysis_block_set_op_offset (second, 2, 9);
rz_analysis_block_set_op_offset (second, 3, 30);
bool success = rz_analysis_block_merge (first, second);
assert_block_invariants (analysis);
mu_assert ("merge success", success);
mu_assert_eq (blocks_count (analysis), 1, "count after merge");
mu_assert_eq (first->addr, 0x1337, "addr after merge");
mu_assert_eq (first->size, 666, "size after merge");
mu_assert_eq (first->jump, 0xdeadbeef, "jump after merge");
mu_assert_eq (first->fail, 0xc0ffee, "fail after merge");
mu_assert_eq (first->ninstr, 3+4, "ninstr after merge");
mu_assert_eq (rz_analysis_block_get_op_offset (first, 0), 0, "offset 0 after merge");
mu_assert_eq (rz_analysis_block_get_op_offset (first, 1), 13, "offset 1 after merge");
mu_assert_eq (rz_analysis_block_get_op_offset (first, 2), 16, "offset 2 after merge");
mu_assert_eq (rz_analysis_block_get_op_offset (first, 3), 42+0, "offset 3 after merge");
mu_assert_eq (rz_analysis_block_get_op_offset (first, 4), 42+4, "offset 4 after merge");
mu_assert_eq (rz_analysis_block_get_op_offset (first, 5), 42+9, "offset 5 after merge");
mu_assert_eq (rz_analysis_block_get_op_offset (first, 6), 42+30, "offset 6 after merge");
rz_analysis_block_unref (first);
// second must be already freed by the merge!
assert_block_invariants (analysis);
rz_analysis_free (analysis);
mu_end;
}
bool test_rz_analysis_block_merge_in_function() {
RzAnalysis *analysis = rz_analysis_new ();
assert_block_invariants (analysis);
RzAnalysisFunction *fcn = rz_analysis_create_function (analysis, "bbowner", 0x1337, 0, NULL);
RzAnalysisBlock *first = rz_analysis_create_block (analysis, 0x1337, 42);
RzAnalysisBlock *second = rz_analysis_create_block (analysis, 0x1337 + 42, 624);
assert_block_invariants (analysis);
mu_assert_eq (blocks_count (analysis), 2, "count after create");
rz_analysis_function_add_block (fcn, first);
assert_block_invariants (analysis);
rz_analysis_function_add_block (fcn, second);
assert_block_invariants (analysis);
bool success = rz_analysis_block_merge (first, second);
assert_block_invariants (analysis);
mu_assert ("merge success", success);
mu_assert_eq (blocks_count (analysis), 1, "count after merge");
mu_assert_eq (rz_list_length (fcn->bbs), 1, "fcn bbs after merge");
mu_assert_eq (rz_list_length (first->fcns), 1, "bb functions after merge");
mu_assert ("function has merged block", rz_list_contains (fcn->bbs, first));
mu_assert ("merged block is in function", rz_list_contains (first->fcns, fcn));
rz_analysis_block_unref (first);
// second must be already freed by the merge!
assert_block_invariants (analysis);
rz_analysis_free (analysis);
mu_end;
}
bool test_rz_analysis_block_delete() {
RzAnalysis *analysis = rz_analysis_new ();
assert_block_invariants (analysis);
RzAnalysisFunction *fcn = rz_analysis_create_function (analysis, "bbowner", 0x1337, 0, NULL);
RzAnalysisBlock *block = rz_analysis_create_block (analysis, 0x1337, 42);
assert_block_invariants (analysis);
mu_assert_eq (blocks_count (analysis), 1, "count after create");
rz_analysis_function_add_block (fcn, block);
assert_block_invariants (analysis);
mu_assert_eq (block->ref, 2, "refs after adding");
mu_assert_eq (rz_list_length (fcn->bbs), 1, "fcn bbs after add");
mu_assert_eq (rz_list_length (block->fcns), 1, "bb fcns after add");
rz_analysis_delete_block (block);
assert_block_invariants (analysis);
mu_assert_eq (block->ref, 1, "refs after delete");
mu_assert_eq (rz_list_length (fcn->bbs), 0, "fcn bbs after delete");
mu_assert_eq (rz_list_length (block->fcns), 0, "bb fcns after delete");
rz_analysis_block_unref (block);
rz_analysis_free (analysis);
mu_end;
}
bool test_rz_analysis_block_set_size() {
RzAnalysis *analysis = rz_analysis_new ();
assert_block_invariants (analysis);
RzAnalysisFunction *fcn = rz_analysis_create_function (analysis, "bbowner", 0x1337, 0, NULL);
RzAnalysisBlock *block = rz_analysis_create_block (analysis, 0x1337, 42);
assert_block_invariants (analysis);
rz_analysis_function_add_block (fcn, block);
assert_block_invariants (analysis);
rz_analysis_block_set_size (block, 300);
assert_block_invariants (analysis);
mu_assert_eq (block->size, 300, "size after set_size");
RzAnalysisBlock *second = rz_analysis_create_block (analysis, 0x1337+300, 100);
assert_block_invariants (analysis);
rz_analysis_function_add_block (fcn, block);
assert_block_invariants (analysis);
rz_analysis_function_linear_size (fcn); // trigger lazy calculation of min/max cache
assert_block_invariants (analysis);
rz_analysis_block_set_size (second, 500);
assert_block_invariants (analysis);
mu_assert_eq (second->size, 500, "size after set_size");
rz_analysis_block_set_size (block, 80);
assert_block_invariants (analysis);
mu_assert_eq (block->size, 80, "size after set_size");
rz_analysis_block_unref (block);
rz_analysis_block_unref (second);
assert_block_invariants (analysis);
rz_analysis_free (analysis);
mu_end;
}
bool test_rz_analysis_block_relocate() {
RzAnalysis *analysis = rz_analysis_new ();
assert_block_invariants (analysis);
RzAnalysisFunction *fcn = rz_analysis_create_function (analysis, "bbowner", 0x1337, 0, NULL);
RzAnalysisBlock *block = rz_analysis_create_block (analysis, 0x1337, 42);
assert_block_invariants (analysis);
rz_analysis_function_add_block (fcn, block);
assert_block_invariants (analysis);
rz_analysis_function_linear_size (fcn); // trigger lazy calculation of min/max cache
assert_block_invariants (analysis);
bool success = rz_analysis_block_relocate (block, 0x200, 0x100);
mu_assert ("relocate success", success);
assert_block_invariants (analysis);
mu_assert_eq (block->addr, 0x200, "addr after relocate");
mu_assert_eq (block->size, 0x100, "size after relocate");
RzAnalysisBlock *second = rz_analysis_create_block (analysis, 0x1337+300, 100);
assert_block_invariants (analysis);
rz_analysis_function_add_block (fcn, second);
assert_block_invariants (analysis);
success = rz_analysis_block_relocate (second, 0x400, 0x123);
mu_assert ("relocate success", success);
assert_block_invariants (analysis);
mu_assert_eq (second->addr, 0x400, "addr after relocate");
mu_assert_eq (second->size, 0x123, "size after relocate");
rz_analysis_function_linear_size (fcn); // trigger lazy calculation of min/max cache
assert_block_invariants (analysis);
success = rz_analysis_block_relocate (block, 0x400, 0x333);
mu_assert ("relocate fail on same addr", !success);
assert_block_invariants (analysis);
mu_assert_eq (block->addr, 0x200, "addr after failed relocate");
mu_assert_eq (block->size, 0x100, "size after failed relocate");
rz_analysis_function_linear_size (fcn); // trigger lazy calculation of min/max cache
assert_block_invariants (analysis);
// jump after the other block
success = rz_analysis_block_relocate (block, 0x500, 0x333);
mu_assert ("relocate success", success);
assert_block_invariants (analysis);
mu_assert_eq (block->addr, 0x500, "addr after failed relocate");
mu_assert_eq (block->size, 0x333, "size after failed relocate");
rz_analysis_function_linear_size (fcn); // trigger lazy calculation of min/max cache
assert_block_invariants (analysis);
// jump before the other block
success = rz_analysis_block_relocate (block, 0x10, 0x333);
mu_assert ("relocate success", success);
assert_block_invariants (analysis);
mu_assert_eq (block->addr, 0x10, "addr after failed relocate");
mu_assert_eq (block->size, 0x333, "size after failed relocate");
rz_analysis_block_unref (block);
rz_analysis_block_unref (second);
assert_block_invariants (analysis);
rz_analysis_free (analysis);
mu_end;
}
bool test_rz_analysis_block_query() {
RzAnalysis *analysis = rz_analysis_new ();
assert_block_invariants (analysis);
#define N 200
#define MAXSIZE 0x300
#define SPACE 0x10000
#define SAMPLES 300
RzAnalysisBlock *blocks[N];
size_t i;
for (i = 0; i < N; i++) {
blocks[i] = rz_analysis_create_block (analysis, rand () % SPACE, rand () % MAXSIZE); // may return null on duplicates
assert_block_invariants (analysis);
}
// --
// test rz_analysis_get_block_at()
for (i = 0; i < N; i++) {
if (!blocks[i]) {
continue;
}
mu_assert_ptreq (rz_analysis_get_block_at (analysis, blocks[i]->addr), blocks[i], "rz_analysis_get_block_at");
}
for (i = 0; i < SAMPLES; i++) {
ut64 addr = rand () % SPACE;
size_t j;
// goal is to check cases where rz_analysis_get_block_at() returns null,
// but since the addr is random, there may be a block sometimes too.
RzAnalysisBlock *block = NULL;
for (j = 0; j < N; j++) {
if (!blocks[j]) {
continue;
}
if (blocks[j]->addr == addr) {
block = blocks[j];
break;
}
}
mu_assert_ptreq (rz_analysis_get_block_at (analysis, addr), block, "rz_analysis_get_block_at");
}
// --
// test rz_analysis_get_blocks_in()
for (i = 0; i < SAMPLES; i++) {
ut64 addr = rand () % SPACE;
RzList *in = rz_analysis_get_blocks_in (analysis, addr);
RzAnalysisBlock *block;
RzListIter *it;
rz_list_foreach (in, it, block) {
mu_assert_eq (block->ref, 2, "block refd in returned list");
}
size_t linear_found = 0;
size_t j;
for (j = 0; j < N; j++) {
if (!blocks[j]) {
continue;
}
if (rz_analysis_block_contains (blocks[j], addr)) {
linear_found++;
mu_assert ("intersect linear found in list", rz_list_contains (in, blocks[j]));
}
}
mu_assert_eq ((size_t)rz_list_length (in), linear_found, "rz_analysis_get_blocks_in count");
rz_list_free (in);
}
// --
// test rz_analysis_get_blocks_intersect()
for (i = 0; i < SAMPLES; i++) {
ut64 addr = rand () % SPACE;
ut64 size = rand() % MAXSIZE;
RzList *in = rz_analysis_get_blocks_intersect (analysis, addr, size);
RzAnalysisBlock *block;
RzListIter *it;
rz_list_foreach (in, it, block) {
mu_assert_eq (block->ref, 2, "block refd in returned list");
}
size_t linear_found = 0;
size_t j;
for (j = 0; j < N; j++) {
RzAnalysisBlock *block = blocks[j];
if (!block || addr + size <= block->addr || addr >= block->addr + block->size) {
continue;
}
linear_found++;
mu_assert ("in linear found in list", rz_list_contains (in, blocks[j]));
}
mu_assert_eq ((size_t)rz_list_length (in), linear_found, "rz_analysis_get_blocks_intersect count");
rz_list_free (in);
}
for (i = 0; i < N; i++) {
rz_analysis_block_unref (blocks[i]);
}
assert_block_leaks (analysis);
rz_analysis_free (analysis);
mu_end;
}
bool addr_list_cb(ut64 addr, void *user) {
RzList *list = user;
rz_list_push (list, (void *)addr);
return true;
}
bool test_rz_analysis_block_successors() {
RzAnalysis *analysis = rz_analysis_new ();
assert_block_invariants (analysis);
RzAnalysisBlock *blocks[10];
blocks[0] = rz_analysis_create_block (analysis, 0x10, 0x10);
blocks[1] = rz_analysis_create_block (analysis, 0x30, 0x10);
blocks[2] = rz_analysis_create_block (analysis, 0x50, 0x10);
blocks[3] = rz_analysis_create_block (analysis, 0x100, 0x10);
blocks[4] = rz_analysis_create_block (analysis, 0x110, 0x10);
blocks[5] = rz_analysis_create_block (analysis, 0x120, 0x10);
blocks[6] = rz_analysis_create_block (analysis, 0x130, 0x10);
blocks[7] = rz_analysis_create_block (analysis, 0x140, 0x10);
blocks[8] = rz_analysis_create_block (analysis, 0xa0, 0x10);
blocks[9] = rz_analysis_create_block (analysis, 0xc0, 0x10);
assert_block_invariants (analysis);
blocks[0]->jump = 0x30;
blocks[0]->fail = 0x50;
blocks[1]->jump = 0x10;
blocks[1]->fail = 0x50;
blocks[2]->jump = 0x10;
RzAnalysisSwitchOp *sop = rz_analysis_switch_op_new (0x55, 0x13, 0x15, 0x42);
mu_assert_eq (sop->addr, 0x55, "addr");
mu_assert_eq (sop->min_val, 0x13, "addr");
mu_assert_eq (sop->max_val, 0x15, "addr");
mu_assert_eq (sop->def_val, 0x42, "addr");
rz_analysis_switch_op_add_case (sop, 0x55, 1, 0x100);
rz_analysis_switch_op_add_case (sop, 0x55, 2, 0x110);
rz_analysis_switch_op_add_case (sop, 0x55, 3, 0x120);
rz_analysis_switch_op_add_case (sop, 0x55, 4, 0x130);
rz_analysis_switch_op_add_case (sop, 0x55, 5, 0x140);
blocks[2]->switch_op = sop;
RzList *result = rz_list_new ();
rz_analysis_block_successor_addrs_foreach (blocks[0], addr_list_cb, result);
mu_assert_eq (rz_list_length (result), 2, "jump/fail successors count");
mu_assert ("jmp successor", rz_list_contains (result, (void *)0x30));
mu_assert ("fail successor", rz_list_contains (result, (void *)0x50));
rz_list_purge (result);
rz_analysis_block_successor_addrs_foreach (blocks[2], addr_list_cb, result);
mu_assert_eq (rz_list_length (result), 6, "switch successors count");
mu_assert ("jmp successor", rz_list_contains (result, (void *)0x10));
mu_assert ("case successor", rz_list_contains (result, (void *)0x100));
mu_assert ("case successor", rz_list_contains (result, (void *)0x110));
mu_assert ("case successor", rz_list_contains (result, (void *)0x120));
mu_assert ("case successor", rz_list_contains (result, (void *)0x130));
mu_assert ("case successor", rz_list_contains (result, (void *)0x140));
rz_list_free (result);
result = rz_analysis_block_recurse_list (blocks[0]);
RzAnalysisBlock *block;
RzListIter *it;
rz_list_foreach (result, it, block) {
mu_assert_eq (block->ref, 2, "block refd in returned list");
}
mu_assert_eq (rz_list_length (result), 8, "recursive successors count");
mu_assert ("recursive successor", rz_list_contains (result, blocks[0]));
mu_assert ("recursive successor", rz_list_contains (result, blocks[1]));
mu_assert ("recursive successor", rz_list_contains (result, blocks[2]));
mu_assert ("recursive successor", rz_list_contains (result, blocks[3]));
mu_assert ("recursive successor", rz_list_contains (result, blocks[4]));
mu_assert ("recursive successor", rz_list_contains (result, blocks[5]));
mu_assert ("recursive successor", rz_list_contains (result, blocks[6]));
mu_assert ("recursive successor", rz_list_contains (result, blocks[7]));
rz_list_free (result);
size_t i;
for (i = 0; i < sizeof (blocks) / sizeof (RzAnalysisBlock *); i++) {
rz_analysis_block_unref (blocks[i]);
}
assert_block_leaks (analysis);
rz_analysis_free (analysis);
mu_end;
}
bool test_rz_analysis_block_automerge() {
size_t i;
for (i = 0; i < SAMPLES; i++) {
RzAnalysis *analysis = rz_analysis_new ();
assert_block_invariants (analysis);
RzAnalysisBlock *a = rz_analysis_create_block (analysis, 0x100, 0x10);
RzAnalysisBlock *b = rz_analysis_create_block (analysis, 0x110, 0x10);
a->jump = b->addr;
RzAnalysisBlock *c = rz_analysis_create_block (analysis, 0x120, 0x10);
b->jump = c->addr;
c->fail = b->addr;
RzAnalysisBlock *d = rz_analysis_create_block (analysis, 0x130, 0x10);
c->jump = d->addr;
RzAnalysisBlock *e = rz_analysis_create_block (analysis, 0x140, 0x10);
d->jump = e->addr;
RzAnalysisBlock *f = rz_analysis_create_block (analysis, 0x150, 0x10);
e->jump = f->addr;
RzAnalysisFunction *fa = rz_analysis_create_function (analysis, "fcn", 0x100, RZ_ANALYSIS_FCN_TYPE_FCN, NULL);
rz_analysis_function_add_block (fa, a);
rz_analysis_function_add_block (fa, c);
rz_analysis_function_add_block (fa, d);
rz_analysis_function_add_block (fa, e);
rz_analysis_function_add_block (fa, f);
RzAnalysisFunction *fb = rz_analysis_create_function (analysis, "fcn2", 0x110, RZ_ANALYSIS_FCN_TYPE_FCN, NULL);
rz_analysis_function_add_block (fb, b);
rz_analysis_function_add_block (fb, c);
rz_analysis_function_add_block (fb, d);
rz_analysis_function_add_block (fb, e);
rz_analysis_function_add_block (fb, f);
RzList *all_blocks = rz_list_new ();
rz_list_push (all_blocks, a);
rz_list_push (all_blocks, b);
rz_list_push (all_blocks, c);
rz_list_push (all_blocks, d);
rz_list_push (all_blocks, e);
rz_list_push (all_blocks, f);
// Randomize the order in which we give the automerge the block.
// The outcome should always be the same but it can have some delicate implications on the algorithm inside.
RzList *shuffled_blocks = rz_list_newf ((RzListFree)rz_analysis_block_unref);
while (!rz_list_empty (all_blocks)) {
int n = rand () % rz_list_length (all_blocks);
rz_list_push (shuffled_blocks, rz_list_get_n (all_blocks, n));
rz_list_del_n (all_blocks, n);
}
rz_list_free (all_blocks);
rz_analysis_block_automerge (shuffled_blocks);
assert_block_invariants (analysis);
//mu_assert_eq (rz_list_length (shuffled_blocks), 4, "length after automerge");
mu_assert ("remaining blocks a", rz_list_contains (shuffled_blocks, a));
mu_assert ("remaining blocks b", rz_list_contains (shuffled_blocks, b));
mu_assert ("remaining blocks c", rz_list_contains (shuffled_blocks, c));
mu_assert ("remaining blocks d", rz_list_contains (shuffled_blocks, d));
mu_assert_eq (blocks_count (analysis), rz_list_length (shuffled_blocks), "blocks in analysis count");
RzListIter *it;
RzAnalysisBlock *block;
rz_list_foreach (shuffled_blocks, it, block) {
mu_assert_ptreq (rz_analysis_get_block_at (analysis, block->addr), block, "remaining blocks in analysis");
}
rz_list_free (shuffled_blocks);
assert_block_invariants (analysis);
assert_block_leaks (analysis);
rz_analysis_free (analysis);
}
mu_end;
}
bool test_rz_analysis_block_chop_noreturn(void) {
RzAnalysis *analysis = rz_analysis_new ();
assert_block_invariants (analysis);
RzAnalysisBlock *a = rz_analysis_create_block (analysis, 0x100, 0x10);
RzAnalysisBlock *b = rz_analysis_create_block (analysis, 0x110, 0x10);
RzAnalysisBlock *c = rz_analysis_create_block (analysis, 0x120, 0x10);
a->jump = c->addr;
b->jump = c->addr;
RzAnalysisFunction *fa = rz_analysis_create_function (analysis, "fcn", 0x100, RZ_ANALYSIS_FCN_TYPE_FCN, NULL);
rz_analysis_function_add_block (fa, a);
rz_analysis_function_add_block (fa, b);
rz_analysis_function_add_block (fa, c);
RzAnalysisFunction *fb = rz_analysis_create_function (analysis, "fcn2", 0x130, RZ_ANALYSIS_FCN_TYPE_FCN, NULL);
fb->is_noreturn = true;
rz_analysis_block_chop_noreturn (b, 0x111);
assert_block_invariants (analysis);
rz_analysis_free (analysis);
mu_end;
}
static const uint8_t example_code[0x18] = {
0x48, 0xc7, 0xc0, 0x2a, 0x00, 0x00, 0x00, // mov rax, 0x2a
0x48, 0x89, 0xc2, // mov rdx, rax
0x48, 0x81, 0xc2, 0x0f, 0x05, 0x00, 0x00, // add rdx, 0x50f
0x48, 0xc7, 0xc0, 0x37, 0x13, 0x00, 0x00 // mov rax, 0x1337
};
bool test_rz_analysis_block_analyze_ops(void) {
RzAnalysis *a = rz_analysis_new ();
rz_analysis_use (a, "x86");
rz_analysis_set_bits (a, 64);
IOMock io;
io_mock_init (&io, 0x1000, example_code, sizeof (example_code));
io_mock_bind (&io, &a->iob);
// clean block with valid code
RzAnalysisBlock *block = rz_analysis_create_block (a, 0x1000, 0x18);
mu_assert_eq (block->ninstr, 0, "clean block");
rz_analysis_block_analyze_ops (block);
mu_assert_eq (block->ninstr, 4, "ninstr");
mu_assert_eq (rz_analysis_block_get_op_offset (block, 0), 0, "op offset");
mu_assert_eq (rz_analysis_block_get_op_offset (block, 1), 0x7, "op offset");
mu_assert_eq (rz_analysis_block_get_op_offset (block, 2), 0xa, "op offset");
mu_assert_eq (rz_analysis_block_get_op_offset (block, 3), 0x11, "op offset");
mu_assert_eq (rz_analysis_block_get_op_addr (block, 0), 0x1000, "op addr");
mu_assert_eq (rz_analysis_block_get_op_addr (block, 1), 0x1007, "op addr");
mu_assert_eq (rz_analysis_block_get_op_addr (block, 2), 0x100a, "op addr");
mu_assert_eq (rz_analysis_block_get_op_addr (block, 3), 0x1011, "op addr");
mu_assert_eq (rz_analysis_block_get_op_size (block, 0), 0x7, "op size");
mu_assert_eq (rz_analysis_block_get_op_size (block, 1), 0x3, "op size");
mu_assert_eq (rz_analysis_block_get_op_size (block, 2), 0x7, "op size");
mu_assert_eq (rz_analysis_block_get_op_size (block, 3), 0x7, "op size");
mu_assert_eq (rz_analysis_block_get_op_addr_in (block, 0x1000), 0x1000, "op addr in");
mu_assert_eq (rz_analysis_block_get_op_addr_in (block, 0x1001), 0x1000, "op addr in");
mu_assert_eq (rz_analysis_block_get_op_addr_in (block, 0x1006), 0x1000, "op addr in");
mu_assert_eq (rz_analysis_block_get_op_addr_in (block, 0x1007), 0x1007, "op addr in");
mu_assert_eq (rz_analysis_block_get_op_addr_in (block, 0x1008), 0x1007, "op addr in");
// dirty block with valid code
rz_analysis_block_relocate (block, 0x1000, 0x11);
rz_analysis_block_analyze_ops (block);
mu_assert_eq (block->ninstr, 3, "ninstr");
mu_assert_eq (rz_analysis_block_get_op_offset (block, 0), 0, "op offset");
mu_assert_eq (rz_analysis_block_get_op_offset (block, 1), 0x7, "op offset");
mu_assert_eq (rz_analysis_block_get_op_offset (block, 2), 0xa, "op offset");
rz_analysis_block_unref (block);
// clean block with invalid code a the end
// when encountering invalid code, analysis should stop.
block = rz_analysis_create_block (a, 0x1000, 0x17);
mu_assert_eq (block->ninstr, 0, "clean block");
rz_analysis_block_analyze_ops (block);
mu_assert_eq (block->ninstr, 3, "ninstr");
mu_assert_eq (rz_analysis_block_get_op_offset (block, 0), 0, "op offset");
mu_assert_eq (rz_analysis_block_get_op_offset (block, 1), 0x7, "op offset");
mu_assert_eq (rz_analysis_block_get_op_offset (block, 2), 0xa, "op offset");
rz_analysis_block_unref (block);
assert_block_invariants (a);
assert_block_leaks (a);
rz_analysis_free (a);
io_mock_fini (&io);
mu_end;
}
int all_tests() {
mu_run_test (test_rz_analysis_block_chop_noreturn);
mu_run_test (test_rz_analysis_block_create);
mu_run_test (test_rz_analysis_block_contains);
mu_run_test (test_rz_analysis_block_split);
mu_run_test (test_rz_analysis_block_split_in_function);
mu_run_test (test_rz_analysis_block_merge);
mu_run_test (test_rz_analysis_block_merge_in_function);
mu_run_test (test_rz_analysis_block_delete);
mu_run_test (test_rz_analysis_block_set_size);
mu_run_test (test_rz_analysis_block_relocate);
mu_run_test (test_rz_analysis_block_query);
mu_run_test (test_rz_analysis_block_successors);
mu_run_test (test_rz_analysis_block_automerge);
mu_run_test (test_rz_analysis_block_analyze_ops);
return tests_passed != tests_run;
}
int main(int argc, char **argv) {
struct timeval tv;
gettimeofday (&tv, NULL);
unsigned int seed = argc > 1 ? strtoul (argv[1], NULL, 0) : tv.tv_sec + tv.tv_usec;
printf("seed for test_analysis_block: %u\n", seed);
return all_tests();
}