mirror of
https://github.com/radareorg/radare2.git
synced 2026-04-14 05:46:31 +02:00
620 lines
27 KiB
C
620 lines
27 KiB
C
#include <r_anal.h>
|
|
#include <r_core.h>
|
|
#include "minunit.h"
|
|
#include <string.h>
|
|
|
|
#include "test_anal_block_invars.inl"
|
|
|
|
bool ht_up_count(void *user, const ut64 k, const void *v) {
|
|
size_t *count = user;
|
|
(*count)++;
|
|
return true;
|
|
}
|
|
|
|
bool ht_pp_count(void *user, const void *k, const void *v) {
|
|
size_t *count = user;
|
|
(*count)++;
|
|
return true;
|
|
}
|
|
|
|
static int reg_index(RAnal *anal, const char *name) {
|
|
RRegItem *ri = r_reg_get (anal->reg, name, -1);
|
|
int index = ri? ri->index: -1;
|
|
r_unref (ri);
|
|
return index;
|
|
}
|
|
|
|
static RAnalFcnRegArg *find_register_param(RAnalFcnContext *ctx, const char *reg) {
|
|
RListIter *iter;
|
|
RAnalFcnRegArg *arg;
|
|
|
|
r_list_foreach (ctx->reg_args, iter, arg) {
|
|
if (arg && arg->reg && !strcmp (arg->reg, reg)) {
|
|
return arg;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static RAnalFcnSlot *find_stack_slot(RAnalFcnContext *ctx, const char *name) {
|
|
RListIter *iter;
|
|
RAnalFcnSlot *slot;
|
|
|
|
r_list_foreach (ctx->slots, iter, slot) {
|
|
if (slot && slot->name && !strcmp (slot->name, name)) {
|
|
return slot;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
typedef struct {
|
|
int count;
|
|
ut64 block_addr;
|
|
ut64 switch_addr;
|
|
ut64 default_addr;
|
|
ut64 first_case_addr;
|
|
ut64 first_case_value;
|
|
} SwitchForeachCtx;
|
|
|
|
static bool count_switches_cb(RAnalFunction *fcn, RAnalBlock *block, RAnalSwitchOp *op, void *user) {
|
|
SwitchForeachCtx *ctx = user;
|
|
mu_assert_notnull (fcn, "switch callback function");
|
|
mu_assert_notnull (block, "switch callback block");
|
|
mu_assert_notnull (op, "switch callback switch");
|
|
ctx->count++;
|
|
ctx->block_addr = block->addr;
|
|
ctx->switch_addr = op->addr;
|
|
ctx->default_addr = op->def_val;
|
|
RAnalCaseOp *caseop = r_list_first (op->cases);
|
|
mu_assert_notnull (caseop, "switch callback first case");
|
|
ctx->first_case_addr = caseop->jump;
|
|
ctx->first_case_value = caseop->value;
|
|
return true;
|
|
}
|
|
|
|
static bool function_check_invariants(RAnal *anal) {
|
|
if (!block_check_invariants (anal)) {
|
|
return false;
|
|
}
|
|
|
|
RListIter *it;
|
|
RAnalFunction *fcn;
|
|
r_list_foreach (anal->fcns, it, fcn) {
|
|
mu_assert_ptreq (ht_up_find (anal->ht_addr_fun, fcn->addr, NULL), fcn, "function in addr ht");
|
|
mu_assert_ptreq (ht_pp_find (anal->ht_name_fun, fcn->name, NULL), fcn, "function in name ht");
|
|
}
|
|
|
|
size_t addr_count = 0;
|
|
ht_up_foreach (anal->ht_addr_fun, ht_up_count, &addr_count);
|
|
mu_assert_eq (addr_count, r_list_length (anal->fcns), "function addr ht count");
|
|
|
|
size_t name_count = 0;
|
|
ht_pp_foreach (anal->ht_name_fun, ht_pp_count, &name_count);
|
|
mu_assert_eq (name_count, r_list_length (anal->fcns), "function name ht count");
|
|
|
|
return true;
|
|
}
|
|
|
|
#define check_invariants function_check_invariants
|
|
#define check_leaks block_check_leaks
|
|
|
|
#define assert_invariants(anal) do { if (!check_invariants (anal)) { return false; } } while (0)
|
|
#define assert_leaks(anal) do { if (!check_leaks (anal)) { return false; } } while (0)
|
|
|
|
bool test_r_anal_function_relocate(void) {
|
|
RAnal *anal = r_anal_new ();
|
|
assert_invariants (anal);
|
|
|
|
RAnalFunction *fa = r_anal_create_function (anal, "do_something", 0x1337, 0, NULL);
|
|
assert_invariants (anal);
|
|
RAnalFunction *fb = r_anal_create_function (anal, "do_something_else", 0xdeadbeef, 0, NULL);
|
|
assert_invariants (anal);
|
|
r_anal_create_function (anal, "do_something_different", 0xc0ffee, 0, NULL);
|
|
assert_invariants (anal);
|
|
|
|
bool success = r_anal_function_relocate (fa, fb->addr);
|
|
assert_invariants (anal);
|
|
mu_assert_false (success, "failed relocate");
|
|
mu_assert_eq (fa->addr, 0x1337, "failed relocate addr");
|
|
|
|
success = r_anal_function_relocate (fa, 0x1234);
|
|
assert_invariants (anal);
|
|
mu_assert_true (success, "successful relocate");
|
|
mu_assert_eq (fa->addr, 0x1234, "successful relocate addr");
|
|
|
|
assert_leaks (anal);
|
|
r_anal_free (anal);
|
|
mu_end;
|
|
}
|
|
|
|
bool test_r_anal_function_labels(void) {
|
|
RAnal *anal = r_anal_new ();
|
|
|
|
RAnalFunction *f = r_anal_create_function (anal, "do_something", 0x1337, 0, NULL);
|
|
|
|
bool s = r_anal_function_set_label (f, "smartfriend", 0x1339);
|
|
mu_assert_true (s, "set label");
|
|
s = r_anal_function_set_label (f, "stray", 0x133c);
|
|
mu_assert_true (s, "set label");
|
|
s = r_anal_function_set_label (f, "the", 0x1340);
|
|
mu_assert_true (s, "set label");
|
|
s = r_anal_function_set_label (f, "stray", 0x1234);
|
|
mu_assert_false (s, "set label (existing name)");
|
|
s = r_anal_function_set_label (f, "henlo", 0x133c);
|
|
mu_assert_false (s, "set label (existing addr)");
|
|
|
|
ut64 addr = r_anal_function_get_label (f, "smartfriend");
|
|
mu_assert_eq (addr, 0x1339, "get label");
|
|
addr = r_anal_function_get_label (f, "stray");
|
|
mu_assert_eq (addr, 0x133c, "get label");
|
|
addr = r_anal_function_get_label (f, "skies");
|
|
mu_assert_eq (addr, UT64_MAX, "get label (unknown)");
|
|
|
|
const char *name = r_anal_function_get_label_at (f, 0x1339);
|
|
mu_assert_streq (name, "smartfriend", "get label at");
|
|
name = r_anal_function_get_label_at (f, 0x133c);
|
|
mu_assert_streq (name, "stray", "get label at");
|
|
name = r_anal_function_get_label_at (f, 0x1234);
|
|
mu_assert_null (name, "get label at (unknown)");
|
|
|
|
r_anal_function_delete_label (f, "stray");
|
|
addr = r_anal_function_get_label (f, "stray");
|
|
mu_assert_eq (addr, UT64_MAX, "get label (deleted)");
|
|
name = r_anal_function_get_label_at (f, 0x133c);
|
|
mu_assert_null (name, "get label at (deleted)");
|
|
addr = r_anal_function_get_label (f, "smartfriend");
|
|
mu_assert_eq (addr, 0x1339, "get label (unaffected by delete)");
|
|
name = r_anal_function_get_label_at (f, 0x1339);
|
|
mu_assert_streq (name, "smartfriend", "get label at (unaffected by delete)");
|
|
|
|
r_anal_function_delete_label_at (f, 0x1340);
|
|
addr = r_anal_function_get_label (f, "the");
|
|
mu_assert_eq (addr, UT64_MAX, "get label (deleted)");
|
|
name = r_anal_function_get_label_at (f, 0x340);
|
|
mu_assert_null (name, "get label at (deleted)");
|
|
addr = r_anal_function_get_label (f, "smartfriend");
|
|
mu_assert_eq (addr, 0x1339, "get label (unaffected by delete)");
|
|
name = r_anal_function_get_label_at (f, 0x1339);
|
|
mu_assert_streq (name, "smartfriend", "get label at (unaffected by delete)");
|
|
|
|
r_anal_free (anal);
|
|
mu_end;
|
|
}
|
|
|
|
bool test_r_anal_str_to_fcn_returns_status(void) {
|
|
RAnal *anal = r_anal_new ();
|
|
mu_assert_notnull (anal, "Couldn't create new RAnal");
|
|
RAnalFunction *f = r_anal_create_function (anal, "sigtest", 0x1000, 0, NULL);
|
|
mu_assert_notnull (f, "Couldn't create function for signature test");
|
|
|
|
bool ok = r_anal_str_to_fcn (anal, f, "int sigtest (int arg0);");
|
|
mu_assert_true (ok, "valid signature must return success");
|
|
|
|
char *typed_name = r_type_func_name (anal->sdb_types, f->name);
|
|
mu_assert_notnull (typed_name, "valid signature must create a type entry");
|
|
|
|
const char *ret = r_type_func_ret (anal->sdb_types, typed_name);
|
|
int argc = r_type_func_args_count (anal->sdb_types, typed_name);
|
|
char *arg0 = r_type_func_args_type (anal->sdb_types, typed_name, 0);
|
|
mu_assert_true (ret && (!strcmp (ret, "int") || !strcmp (ret, "int32_t")),
|
|
"valid signature should set integer return type");
|
|
mu_assert_eq (argc, 1, "valid signature should set one argument");
|
|
mu_assert_true (arg0 && (!strcmp (arg0, "int") || !strcmp (arg0, "int32_t")),
|
|
"valid signature should keep first argument type");
|
|
free (arg0);
|
|
|
|
ok = r_anal_str_to_fcn (anal, f, "int sigtest (");
|
|
mu_assert_false (ok, "invalid signature must return failure");
|
|
|
|
ret = r_type_func_ret (anal->sdb_types, typed_name);
|
|
argc = r_type_func_args_count (anal->sdb_types, typed_name);
|
|
arg0 = r_type_func_args_type (anal->sdb_types, typed_name, 0);
|
|
mu_assert_true (ret && (!strcmp (ret, "int") || !strcmp (ret, "int32_t")),
|
|
"invalid signature must not clobber existing return type");
|
|
mu_assert_eq (argc, 1, "invalid signature must not clobber existing argc");
|
|
mu_assert_true (arg0 && (!strcmp (arg0, "int") || !strcmp (arg0, "int32_t")),
|
|
"invalid signature must not clobber existing argument type");
|
|
free (arg0);
|
|
free (typed_name);
|
|
r_anal_free (anal);
|
|
mu_end;
|
|
}
|
|
|
|
bool test_r_core_anal_fcn_prefers_exact_start_match(void) {
|
|
RCore *core = r_core_new ();
|
|
mu_assert_notnull (core, "Couldn't create new RCore");
|
|
RAnal *anal = core->anal;
|
|
r_config_set_b (core->config, "anal.esil", false);
|
|
|
|
RAnalFunction *outer = r_anal_create_function (anal, "outer", 0x100, R_ANAL_FCN_TYPE_FCN, NULL);
|
|
RAnalFunction *target = r_anal_create_function (anal, "target", 0x120, R_ANAL_FCN_TYPE_LOC, NULL);
|
|
mu_assert_notnull (outer, "Couldn't create outer function");
|
|
mu_assert_notnull (target, "Couldn't create target function");
|
|
|
|
RAnalBlock *outer_bb = r_anal_create_block (anal, 0x100, 0x30);
|
|
RAnalBlock *target_bb = r_anal_create_block (anal, 0x120, 0x10);
|
|
mu_assert_notnull (outer_bb, "Couldn't create outer block");
|
|
mu_assert_notnull (target_bb, "Couldn't create target block");
|
|
r_anal_function_add_block (outer, outer_bb);
|
|
r_anal_function_add_block (target, target_bb);
|
|
r_unref (outer_bb);
|
|
r_unref (target_bb);
|
|
|
|
bool ret = r_core_anal_fcn (core, 0x120, 0x104, R_ANAL_REF_TYPE_CALL, 1);
|
|
mu_assert_false (ret, "Exact-start function should short-circuit analysis");
|
|
mu_assert_eq (r_anal_xrefs_count (anal), 0, "Exact-start match should not synthesize a new xref");
|
|
|
|
r_core_free (core);
|
|
mu_end;
|
|
}
|
|
|
|
bool test_r_anal_function_get_signature(void) {
|
|
RAnal *anal = r_anal_new ();
|
|
mu_assert_notnull (anal, "Couldn't create new RAnal");
|
|
mu_assert_true (r_anal_cc_set (anal, "void amd64 (rdi, rsi, rdx, rcx, r8, r9, stack)"),
|
|
"must seed amd64 calling convention");
|
|
RAnalFunction *f = r_anal_create_function (anal, "sigread", 0x2000, 0, NULL);
|
|
mu_assert_notnull (f, "Couldn't create function for typed cache test");
|
|
bool ok = r_anal_str_to_fcn (anal, f, "int sigread (int arg0, char *arg1);");
|
|
mu_assert_true (ok, "valid signature must apply before cache refresh");
|
|
|
|
char *typed_name = r_type_func_name (anal->sdb_types, f->name);
|
|
mu_assert_notnull (typed_name, "typed function name");
|
|
char *typed_cc = r_str_newf ("func.%s.cc=amd64\n", typed_name);
|
|
mu_assert_notnull (typed_cc, "persisted callconv key");
|
|
r_anal_save_parsed_type (anal, typed_cc);
|
|
|
|
RAnalFunctionSignature *signature = r_anal_function_get_signature (f);
|
|
mu_assert_notnull (signature, "typed signature must be readable");
|
|
mu_assert_streq (f->name, "sigread", "typed function name");
|
|
mu_assert_streq (signature->ret_type, "int", "typed return type");
|
|
mu_assert_streq (signature->callconv, "amd64", "typed callconv");
|
|
mu_assert_eq ((int)r_list_length (signature->params), 2, "typed param count");
|
|
RAnalFunctionParam *arg0 = r_list_get_n (signature->params, 0);
|
|
RAnalFunctionParam *arg1 = r_list_get_n (signature->params, 1);
|
|
mu_assert_notnull (arg0, "first typed param");
|
|
mu_assert_notnull (arg1, "second typed param");
|
|
mu_assert_streq (arg0->type, "int", "first typed param type");
|
|
mu_assert_streq (arg1->type, "char *", "second typed param type");
|
|
mu_assert_notnull (signature->signature, "typed signature string");
|
|
|
|
free (typed_cc);
|
|
free (typed_name);
|
|
r_anal_function_signature_free (signature);
|
|
r_anal_free (anal);
|
|
mu_end;
|
|
}
|
|
|
|
bool test_r_anal_function_set_signature_uses_canonical_type_name(void) {
|
|
RAnal *anal = r_anal_new ();
|
|
mu_assert_notnull (anal, "Couldn't create new RAnal");
|
|
bool ok = r_anal_import_c_decls (anal, "int scanf (const char *fmt);", NULL);
|
|
mu_assert_true (ok, "seed canonical scanf signature");
|
|
|
|
RAnalFunction *f = r_anal_create_function (anal, "sym.imp.__isoc99_scanf", 0x3000, 0, NULL);
|
|
mu_assert_notnull (f, "Couldn't create function for typed apply test");
|
|
RAnalFunction *alias = r_anal_create_function (anal, "scanf", 0x3001, 0, NULL);
|
|
mu_assert_notnull (alias, "Couldn't create alias function for typed apply test");
|
|
RAnalFunctionSignature *signature = r_anal_function_get_signature (alias);
|
|
mu_assert_notnull (signature, "alias typed signature must warm");
|
|
r_anal_function_signature_free (signature);
|
|
|
|
RAnalFunctionParam params_data[] = {
|
|
{ .name = "format", .type = "const char *" },
|
|
{ .name = "value", .type = "int *" },
|
|
};
|
|
RList *params = r_list_new ();
|
|
mu_assert_notnull (params, "Couldn't create typed apply param list");
|
|
r_list_append (params, ¶ms_data[0]);
|
|
r_list_append (params, ¶ms_data[1]);
|
|
|
|
RAnalFunctionSignature input = {
|
|
.ret_type = "int",
|
|
.callconv = "amd64",
|
|
.params = params,
|
|
.noreturn = false,
|
|
};
|
|
ok = r_anal_function_set_signature (anal, f, &input);
|
|
mu_assert_true (ok, "typed signature apply must succeed");
|
|
r_list_free (params);
|
|
|
|
char *typed_name = r_type_func_name (anal->sdb_types, f->name);
|
|
mu_assert_notnull (typed_name, "canonical typed name");
|
|
mu_assert_streq (typed_name, "scanf", "apply must reuse canonical type name");
|
|
mu_assert_eq (r_type_func_args_count (anal->sdb_types, typed_name), 2, "typed apply param count");
|
|
mu_assert_null (sdb_const_get (anal->sdb_types, f->name, 0), "apply must not create duplicate import-scoped signature");
|
|
|
|
signature = r_anal_function_get_signature (f);
|
|
mu_assert_notnull (signature, "typed signature read");
|
|
mu_assert_streq (signature->ret_type, "int", "typed apply return type");
|
|
mu_assert_streq (signature->callconv, "amd64", "typed apply callconv");
|
|
mu_assert_eq ((int)r_list_length (signature->params), 2, "typed apply context param count");
|
|
RAnalFunctionParam *arg0 = r_list_get_n (signature->params, 0);
|
|
RAnalFunctionParam *arg1 = r_list_get_n (signature->params, 1);
|
|
mu_assert_notnull (arg0, "first typed param");
|
|
mu_assert_notnull (arg1, "second typed param");
|
|
mu_assert_streq (arg0->name, "format", "first typed param name");
|
|
mu_assert_streq (arg0->type, "const char *", "first typed param type");
|
|
mu_assert_streq (arg1->name, "value", "second typed param name");
|
|
mu_assert_streq (arg1->type, "int *", "second typed param type");
|
|
mu_assert_streq (signature->signature, "int scanf (const char *format, int *value);", "canonical signature string");
|
|
r_anal_function_signature_free (signature);
|
|
mu_assert_streq (f->callconv, "amd64", "typed apply must sync live callconv");
|
|
|
|
input.ret_type = "void";
|
|
input.callconv = "cdecl";
|
|
input.params = NULL;
|
|
input.noreturn = true;
|
|
ok = r_anal_function_set_signature (anal, f, &input);
|
|
mu_assert_true (ok, "typed signature overwrite must succeed");
|
|
signature = r_anal_function_get_signature (f);
|
|
mu_assert_notnull (signature, "typed overwrite signature read");
|
|
mu_assert_streq (signature->ret_type, "void", "typed overwrite return type");
|
|
mu_assert_streq (signature->callconv, "cdecl", "typed overwrite callconv");
|
|
mu_assert_true (f->is_noreturn, "typed overwrite noreturn");
|
|
mu_assert_eq ((int)r_list_length (signature->params), 0, "typed overwrite clears params");
|
|
r_anal_function_signature_free (signature);
|
|
|
|
mu_assert_eq (r_type_func_args_count (anal->sdb_types, typed_name), 0, "typed overwrite argc");
|
|
signature = r_anal_function_get_signature (alias);
|
|
mu_assert_notnull (signature, "alias signature must refresh after overwrite");
|
|
mu_assert_streq (signature->ret_type, "void", "alias return type must refresh after overwrite");
|
|
mu_assert_streq (signature->callconv, "cdecl", "alias callconv must refresh after overwrite");
|
|
mu_assert_eq ((int)r_list_length (signature->params), 0, "alias params must refresh after overwrite");
|
|
r_anal_function_signature_free (signature);
|
|
free (typed_name);
|
|
|
|
r_anal_free (anal);
|
|
mu_end;
|
|
}
|
|
|
|
bool test_r_anal_function_get_signature_string_uses_import_flag_name(void) {
|
|
RCore *core = r_core_new ();
|
|
mu_assert_notnull (core, "Couldn't create new RCore");
|
|
RAnal *anal = core->anal;
|
|
bool ok = r_anal_import_c_decls (anal, "int scanf (const char *fmt);", NULL);
|
|
mu_assert_true (ok, "seed canonical scanf signature");
|
|
|
|
RAnalFunction *f = r_anal_create_function (anal, "fcn.00003000", 0x3000, 0, NULL);
|
|
mu_assert_notnull (f, "Couldn't create function for import flag test");
|
|
mu_assert_notnull (
|
|
r_flag_set_inspace (core->flags, R_FLAGS_FS_IMPORTS, "sym.imp.__isoc99_scanf", f->addr, 0),
|
|
"Couldn't create import flag for function"
|
|
);
|
|
|
|
char *sig = r_anal_function_get_signature_string (f);
|
|
mu_assert_notnull (sig, "import flag signature");
|
|
mu_assert_streq (sig, "int scanf (const char *fmt);", "import flag must resolve canonical type name");
|
|
free (sig);
|
|
r_core_free (core);
|
|
mu_end;
|
|
}
|
|
|
|
bool test_r_anal_function_get_signature_uses_basename_for_dbg_prefixed_function(void) {
|
|
RAnal *anal = r_anal_new ();
|
|
mu_assert_notnull (anal, "Couldn't create new RAnal");
|
|
bool ok = r_anal_import_c_decls (anal, "char * alloc_and_copy (char *src, size_t len);", NULL);
|
|
mu_assert_true (ok, "seed canonical alloc_and_copy signature");
|
|
|
|
RAnalFunction *f = r_anal_create_function (anal, "dbg.alloc_and_copy", 0x3100, 0, NULL);
|
|
mu_assert_notnull (f, "Couldn't create dbg-prefixed function");
|
|
|
|
RAnalFunctionSignature *signature = r_anal_function_get_signature (f);
|
|
mu_assert_notnull (signature, "dbg-prefixed function must resolve canonical basename signature");
|
|
mu_assert_streq (signature->ret_type, "char *", "dbg-prefixed return type");
|
|
mu_assert_eq ((int)r_list_length (signature->params), 2, "dbg-prefixed param count");
|
|
RAnalFunctionParam *arg0 = r_list_get_n (signature->params, 0);
|
|
RAnalFunctionParam *arg1 = r_list_get_n (signature->params, 1);
|
|
mu_assert_notnull (arg0, "first dbg-prefixed param");
|
|
mu_assert_notnull (arg1, "second dbg-prefixed param");
|
|
mu_assert_streq (arg0->name, "src", "first dbg-prefixed param name");
|
|
mu_assert_streq (arg0->type, "char *", "first dbg-prefixed param type");
|
|
mu_assert_streq (arg1->name, "len", "second dbg-prefixed param name");
|
|
mu_assert_streq (arg1->type, "size_t", "second dbg-prefixed param type");
|
|
r_anal_function_signature_free (signature);
|
|
r_anal_free (anal);
|
|
mu_end;
|
|
}
|
|
|
|
bool test_r_anal_function_get_signature_string_falls_back_to_vars(void) {
|
|
RAnal *anal = r_anal_new ();
|
|
mu_assert_notnull (anal, "Couldn't create new RAnal");
|
|
RAnalFunction *f = r_anal_create_function (anal, "foo", 0x4000, 0, NULL);
|
|
mu_assert_notnull (f, "Couldn't create function for var fallback test");
|
|
mu_assert_notnull (
|
|
r_anal_function_set_var (f, 8, R_ANAL_VAR_KIND_BPV, "int32_t", 4, true, "arg_ch"),
|
|
"Couldn't add second arg var");
|
|
mu_assert_notnull (
|
|
r_anal_function_set_var (f, 4, R_ANAL_VAR_KIND_BPV, "int32_t", 4, true, "arg_8h"),
|
|
"Couldn't add first arg var");
|
|
|
|
char *sig = r_anal_function_get_signature_string (f);
|
|
mu_assert_notnull (sig, "var fallback signature");
|
|
mu_assert_streq (sig, "void foo (int32_t arg_8h, int32_t arg_ch);", "signature must fall back to sorted arg vars");
|
|
RAnalFunctionSignature *signature = r_anal_function_get_signature (f);
|
|
mu_assert_notnull (signature, "var fallback signature read");
|
|
mu_assert_eq ((int)r_list_length (signature->params), 2, "var fallback param count");
|
|
r_anal_function_signature_free (signature);
|
|
free (sig);
|
|
r_anal_free (anal);
|
|
mu_end;
|
|
}
|
|
|
|
bool test_r_anal_function_get_signature_string_hides_variadic_placeholder(void) {
|
|
RAnal *anal = r_anal_new ();
|
|
mu_assert_notnull (anal, "Couldn't create new RAnal");
|
|
RAnalFunction *f = r_anal_create_function (anal, "foo.bar", 0x5000, 0, NULL);
|
|
mu_assert_notnull (f, "Couldn't create function for variadic signature test");
|
|
|
|
bool ok = r_anal_str_to_fcn (anal, f, "char foo.bar (int a, ...);");
|
|
mu_assert_true (ok, "variadic signature must parse");
|
|
|
|
char *sig = r_anal_function_get_signature_string (f);
|
|
mu_assert_notnull (sig, "variadic signature string");
|
|
mu_assert_streq (sig, "char foo.bar (int a, ...);", "variadic placeholder name must stay hidden");
|
|
free (sig);
|
|
r_anal_free (anal);
|
|
mu_end;
|
|
}
|
|
|
|
bool test_r_anal_function_get_signature_falls_back_to_valid_callconv(void) {
|
|
RAnal *anal = r_anal_new ();
|
|
mu_assert_notnull (anal, "Couldn't create new RAnal");
|
|
r_anal_cc_reset (anal);
|
|
mu_assert_true (r_anal_cc_set (anal, "void amd64 (rdi, rsi, rdx, rcx, r8, r9, stack)"),
|
|
"must seed amd64 calling convention");
|
|
r_anal_set_cc_default (anal, "amd64");
|
|
|
|
RAnalFunction *f = r_anal_create_function (anal, "sigcc", 0x6000, 0, NULL);
|
|
mu_assert_notnull (f, "Couldn't create function for callconv fallback test");
|
|
bool ok = r_anal_str_to_fcn (anal, f, "void sigcc (size_t sz);");
|
|
mu_assert_true (ok, "signature must parse");
|
|
|
|
RAnalFunctionSignature *signature = r_anal_function_get_signature (f);
|
|
mu_assert_notnull (signature, "typed signature must be readable");
|
|
mu_assert_streq (signature->callconv, "amd64", "invalid persisted cc must fall back to a valid live cc");
|
|
r_anal_function_signature_free (signature);
|
|
r_anal_free (anal);
|
|
mu_end;
|
|
}
|
|
|
|
bool test_r_anal_function_context_collect_is_conservative_for_stack_slots(void) {
|
|
RAnal *anal = r_anal_new ();
|
|
mu_assert_notnull (anal, "Couldn't create new RAnal");
|
|
r_anal_use (anal, "x86");
|
|
r_anal_set_bits (anal, 64);
|
|
mu_assert_true (r_anal_cc_set (anal, "rax ctxcall(rdi, rdx, stack)"), "Couldn't seed test-local calling convention");
|
|
|
|
RAnalFunction *fcn = r_anal_create_function (anal, "fcn_ctx", 0x1000, R_ANAL_FCN_TYPE_FCN, NULL);
|
|
mu_assert_notnull (fcn, "Couldn't create function for function-context test");
|
|
fcn->callconv = r_str_constpool_get (&anal->constpool, "ctxcall");
|
|
|
|
RAnalFunctionParam params_data[] = {
|
|
{ .name = "first", .type = "int" },
|
|
{ .name = "second", .type = "int" },
|
|
{ .name = "third", .type = "int" },
|
|
{ .name = "fourth", .type = "int" },
|
|
};
|
|
RList *params = r_list_new ();
|
|
mu_assert_notnull (params, "Couldn't create param list for function-context test");
|
|
r_list_append (params, ¶ms_data[0]);
|
|
r_list_append (params, ¶ms_data[1]);
|
|
r_list_append (params, ¶ms_data[2]);
|
|
r_list_append (params, ¶ms_data[3]);
|
|
RAnalFunctionSignature signature = {
|
|
.ret_type = "int",
|
|
.callconv = "ctxcall",
|
|
.params = params,
|
|
.noreturn = false,
|
|
};
|
|
mu_assert_true (r_anal_function_set_signature (anal, fcn, &signature), "typed signature apply for function-context test");
|
|
r_list_free (params);
|
|
|
|
const int rdi = reg_index (anal, "rdi");
|
|
const int rdx = reg_index (anal, "rdx");
|
|
mu_assert ("rdi register index must resolve", rdi >= 0);
|
|
mu_assert ("rdx register index must resolve", rdx >= 0);
|
|
|
|
RAnalVar *home_source = r_anal_function_set_var (fcn, rdi, R_ANAL_VAR_KIND_REG, "int", 4, true, "arg1");
|
|
RAnalVar *sparse_reg = r_anal_function_set_var (fcn, rdx, R_ANAL_VAR_KIND_REG, "int", 4, true, "arg3");
|
|
RAnalVar *home_slot = r_anal_function_set_var (fcn, -8, R_ANAL_VAR_KIND_BPV, "int", 4, false, "arg1_home");
|
|
RAnalVar *stack_arg = r_anal_function_set_var (fcn, 0x28, R_ANAL_VAR_KIND_SPV, "int", 4, true, "stack_input");
|
|
RAnalVar *saved_named = r_anal_function_set_var (fcn, -0x10, R_ANAL_VAR_KIND_BPV, "int", 4, false, "saved_rbx");
|
|
RAnalVar *arg_named_local = r_anal_function_set_var (fcn, 0x30, R_ANAL_VAR_KIND_SPV, "int", 4, false, "arg2");
|
|
mu_assert_notnull (home_source, "create register home source");
|
|
mu_assert_notnull (sparse_reg, "create sparse register arg");
|
|
mu_assert_notnull (home_slot, "create home slot");
|
|
mu_assert_notnull (stack_arg, "create stack arg");
|
|
mu_assert_notnull (saved_named, "create saved-named local");
|
|
mu_assert_notnull (arg_named_local, "create arg-named local");
|
|
free (home_source->regname);
|
|
home_source->regname = strdup ("rdi");
|
|
free (sparse_reg->regname);
|
|
sparse_reg->regname = strdup ("rdx");
|
|
|
|
r_anal_var_set_access (anal, home_source, "rdi", 0x1010, R_PERM_R, 0);
|
|
r_anal_var_set_access (anal, home_slot, "rbp", 0x1010, R_PERM_W, -8);
|
|
|
|
RAnalFcnContext *ctx = r_anal_function_context_collect (anal, fcn);
|
|
mu_assert_notnull (ctx, "collect typed function context");
|
|
|
|
RAnalFcnRegArg *rdx_param = find_register_param (ctx, "rdx");
|
|
mu_assert_notnull (rdx_param, "sparse register arg must be collected");
|
|
|
|
RAnalFcnSlot *home_ctx = find_stack_slot (ctx, "arg1_home");
|
|
RAnalFcnSlot *stack_arg_ctx = find_stack_slot (ctx, "stack_input");
|
|
RAnalFcnSlot *saved_ctx = find_stack_slot (ctx, "saved_rbx");
|
|
RAnalFcnSlot *arg_named_local_ctx = find_stack_slot (ctx, "arg2");
|
|
mu_assert_notnull (home_ctx, "home slot must be present in function context");
|
|
mu_assert_notnull (stack_arg_ctx, "stack arg slot must be present in function context");
|
|
mu_assert_notnull (saved_ctx, "saved-named slot must be present in function context");
|
|
mu_assert_notnull (arg_named_local_ctx, "arg-named local slot must be present in function context");
|
|
|
|
mu_assert_eq (home_ctx->role, R_ANAL_FCN_SLOT_HOME, "register-home stack slot must stay param-home");
|
|
mu_assert_eq (home_ctx->arg_index, 0, "param-home slot must use source register param index");
|
|
mu_assert_streq (home_ctx->arg_name, "first", "param-home slot must inherit canonical signature name");
|
|
mu_assert_streq (home_ctx->home_reg, "rdi", "param-home slot must keep source register");
|
|
|
|
mu_assert_eq (stack_arg_ctx->role, R_ANAL_FCN_SLOT_ARG, "stack arg slot must stay stack-arg");
|
|
mu_assert_eq (stack_arg_ctx->arg_index, -1, "stack arg slot must not synthesize param indexes from sparse register args");
|
|
mu_assert_null (stack_arg_ctx->arg_name, "stack arg slot must not synthesize a signature param name without a canonical index");
|
|
|
|
mu_assert_eq (saved_ctx->role, R_ANAL_FCN_SLOT_LOCAL, "saved-named local must not be reclassified from its spelling");
|
|
mu_assert_eq (arg_named_local_ctx->role, R_ANAL_FCN_SLOT_LOCAL, "arg-named local must not become a param-home without a proven register home");
|
|
|
|
r_anal_function_context_free (ctx);
|
|
r_anal_free (anal);
|
|
mu_end;
|
|
}
|
|
|
|
bool test_r_anal_function_switches_foreach(void) {
|
|
RAnal *anal = r_anal_new ();
|
|
mu_assert_notnull (anal, "Couldn't create new RAnal");
|
|
RAnalFunction *fcn = r_anal_create_function (anal, "switchy", 0x1000, R_ANAL_FCN_TYPE_FCN, NULL);
|
|
mu_assert_notnull (fcn, "Couldn't create function for switch iteration test");
|
|
RAnalBlock *switch_bb = r_anal_create_block (anal, 0x1000, 0x10);
|
|
RAnalBlock *fallthrough_bb = r_anal_create_block (anal, 0x1010, 0x10);
|
|
mu_assert_notnull (switch_bb, "Couldn't create switch block");
|
|
mu_assert_notnull (fallthrough_bb, "Couldn't create fallthrough block");
|
|
r_anal_function_add_block (fcn, switch_bb);
|
|
r_anal_function_add_block (fcn, fallthrough_bb);
|
|
r_anal_block_add_switch_case (switch_bb, 0x1008, 0, 0x1020);
|
|
r_anal_block_add_switch_case (switch_bb, 0x1008, 1, 0x1030);
|
|
switch_bb->switch_op->def_val = 0x1040;
|
|
switch_bb->switch_op->amount = 2;
|
|
SwitchForeachCtx ctx = {0};
|
|
mu_assert_true (r_anal_function_switches_foreach (fcn, count_switches_cb, &ctx), "switch iteration must succeed");
|
|
mu_assert_eq (ctx.count, 1, "switch iteration count");
|
|
mu_assert_eq (ctx.block_addr, 0x1000, "switch iteration block addr");
|
|
mu_assert_eq (ctx.switch_addr, 0x1008, "switch iteration switch addr");
|
|
mu_assert_eq (ctx.default_addr, 0x1040, "switch iteration default addr");
|
|
mu_assert_eq (ctx.first_case_addr, 0x1020, "switch iteration first case addr");
|
|
mu_assert_eq (ctx.first_case_value, 0, "switch iteration first case value");
|
|
r_unref (switch_bb);
|
|
r_unref (fallthrough_bb);
|
|
r_anal_free (anal);
|
|
mu_end;
|
|
}
|
|
|
|
int all_tests(void) {
|
|
mu_run_test (test_r_anal_function_relocate);
|
|
mu_run_test (test_r_anal_function_labels);
|
|
mu_run_test (test_r_anal_str_to_fcn_returns_status);
|
|
mu_run_test (test_r_core_anal_fcn_prefers_exact_start_match);
|
|
mu_run_test (test_r_anal_function_get_signature);
|
|
mu_run_test (test_r_anal_function_set_signature_uses_canonical_type_name);
|
|
mu_run_test (test_r_anal_function_get_signature_string_uses_import_flag_name);
|
|
mu_run_test (test_r_anal_function_get_signature_uses_basename_for_dbg_prefixed_function);
|
|
mu_run_test (test_r_anal_function_get_signature_string_falls_back_to_vars);
|
|
mu_run_test (test_r_anal_function_get_signature_string_hides_variadic_placeholder);
|
|
mu_run_test (test_r_anal_function_get_signature_falls_back_to_valid_callconv);
|
|
mu_run_test (test_r_anal_function_context_collect_is_conservative_for_stack_slots);
|
|
mu_run_test (test_r_anal_function_switches_foreach);
|
|
return tests_passed != tests_run;
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
return all_tests();
|
|
}
|