// SPDX-FileCopyrightText: 2020 Florian Märkl // SPDX-License-Identifier: LGPL-3.0-only #include #include #include #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, RZ_ANALYSIS_FCN_TYPE_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, RZ_ANALYSIS_FCN_TYPE_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, RZ_ANALYSIS_FCN_TYPE_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, RZ_ANALYSIS_FCN_TYPE_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, RZ_ANALYSIS_FCN_TYPE_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 *)(size_t)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); 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); 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); 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); 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; rz_time_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(); }