Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix infinite recursive analysis and optimize context ##analysis #21171

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 77 additions & 47 deletions libr/anal/block.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,12 @@ static int __bb_addr_cmp(const void *incoming, const RBNode *in_tree, void *user
#define D if (anal && anal->verbose)

R_API void r_anal_block_ref(RAnalBlock *bb) {
// 0-refd must already be freed.
r_return_if_fail (bb->ref > 0);
bb->ref++;
// XXX we have R_REF for this
if (bb) {
// 0-refd must already be freed.
r_return_if_fail (bb->ref > 0);
bb->ref++;
}
}

#define DFLT_NINSTR 3
Expand Down Expand Up @@ -109,6 +112,9 @@ R_API void r_anal_block_reset(RAnal *a) {
}

R_API RAnalBlock *r_anal_get_block_at(RAnal *anal, ut64 addr) {
if (addr == UT64_MAX) {
return NULL;
}
RBNode *node = r_rbtree_find (anal->bb_tree, &addr, __bb_addr_cmp, NULL);
return node? unwrap (node): NULL;
}
Expand Down Expand Up @@ -420,13 +426,14 @@ R_API void r_anal_block_unref(RAnalBlock *bb) {
}
}

R_API bool r_anal_block_successor_addrs_foreach(RAnalBlock *block, RAnalAddrCb cb, void *user) {
#if 1
R_API void r_anal_block_successor_addrs_foreach(RAnalBlock *block, RAnalAddrCb cb, void *user) {
#define CB_ADDR(addr) do { \
if (addr == UT64_MAX) { \
break; \
} \
if (!cb (addr, user)) { \
return false; \
return; \
} \
} while (0);

Expand All @@ -439,86 +446,94 @@ R_API bool r_anal_block_successor_addrs_foreach(RAnalBlock *block, RAnalAddrCb c
CB_ADDR (caseop->jump);
}
}

return true;
#undef CB_ADDR
}
#else
R_API void r_anal_block_successor_addrs_foreach(RAnalBlock *block, RAnalAddrCb cb, void *user) {
cb (block->jump, user);
cb (block->fail, user);
if (block->switch_op && block->switch_op->cases) {
RListIter *iter;
RAnalCaseOp *caseop;
r_list_foreach (block->switch_op->cases, iter, caseop) {
cb (caseop->jump, user);
}
}
}
#endif

typedef struct r_anal_block_recurse_context_t {
RAnal *anal;
RPVector/*<RAnalBlock>*/ to_visit;
RList *to_visit;
HtUP *visited;
} RAnalBlockRecurseContext;

static RAnalBlockRecurseContext *recurse_context_new(RAnalBlock *block) {
RAnalBlockRecurseContext *ctx = R_NEW0 (RAnalBlockRecurseContext);
ctx->anal = block->anal;
ctx->to_visit = r_list_newf (NULL);
ctx->visited = ht_up_new0 (); // XXX we only use the key here, this can be a set
ht_up_insert (ctx->visited, block->addr, NULL);
return ctx;
}

static void recurse_context_free(RAnalBlockRecurseContext *ctx) {
if (ctx) {
ht_up_free (ctx->visited);
r_list_free (ctx->to_visit);
free (ctx);
}
}

static bool block_recurse_successor_cb(ut64 addr, void *user) {
RAnalBlockRecurseContext *ctx = user;
if (ht_up_find_kv (ctx->visited, addr, NULL)) {
// already visited
return true;
//return false;
}
ht_up_insert (ctx->visited, addr, NULL);
RAnalBlock *block = r_anal_get_block_at (ctx->anal, addr);
if (!block) {
r_list_push (ctx->to_visit, block);
return true;
}
r_pvector_push (&ctx->to_visit, block);
// always return true, otherwise the foreach stops
return true;
// return false;
}

R_API bool r_anal_block_recurse(RAnalBlock *block, RAnalBlockCb cb, void *user) {
bool breaked = false;
RAnalBlockRecurseContext ctx;
ctx.anal = block->anal;
r_pvector_init (&ctx.to_visit, NULL);
ctx.visited = ht_up_new0 ();
if (!ctx.visited) {
goto beach;
}
RAnalBlockRecurseContext *ctx = recurse_context_new (block);
r_list_append (ctx->to_visit, block);

ht_up_insert (ctx.visited, block->addr, NULL);
r_pvector_push (&ctx.to_visit, block);

while (!r_pvector_empty (&ctx.to_visit)) {
RAnalBlock *cur = r_pvector_pop (&ctx.to_visit);
while (!r_list_empty (ctx->to_visit)) {
RAnalBlock *cur = r_list_pop (ctx->to_visit);
breaked = !cb (cur, user);
if (breaked) {
break;
}
r_anal_block_successor_addrs_foreach (cur, block_recurse_successor_cb, &ctx);
r_anal_block_successor_addrs_foreach (cur, block_recurse_successor_cb, ctx);
}

beach:
ht_up_free (ctx.visited);
r_pvector_clear (&ctx.to_visit);
recurse_context_free (ctx);
return !breaked;
}

R_API bool r_anal_block_recurse_followthrough(RAnalBlock *block, RAnalBlockCb cb, void *user) {
bool breaked = false;
RAnalBlockRecurseContext ctx;
ctx.anal = block->anal;
r_pvector_init (&ctx.to_visit, NULL);
ctx.visited = ht_up_new0 ();
if (!ctx.visited) {
goto beach;
}

ht_up_insert (ctx.visited, block->addr, NULL);
r_pvector_push (&ctx.to_visit, block);

while (!r_pvector_empty (&ctx.to_visit)) {
RAnalBlock *cur = r_pvector_pop (&ctx.to_visit);
bool b = !cb (cur, user);
if (b) {
breaked = true;
RAnalBlockRecurseContext *ctx = recurse_context_new (block);
r_list_append (ctx->to_visit, block);
while (!r_list_empty (ctx->to_visit)) {
RAnalBlock *cur = r_list_pop (ctx->to_visit);
if (cur && cb (cur, user)) {
r_anal_block_successor_addrs_foreach (cur, block_recurse_successor_cb, ctx);
} else {
r_anal_block_successor_addrs_foreach (cur, block_recurse_successor_cb, &ctx);
breaked = true;
}
// eprintf ("tovisit %d\n", r_list_length (ctx->to_visit));
}

beach:
ht_up_free (ctx.visited);
r_pvector_clear (&ctx.to_visit);
recurse_context_free (ctx);
return !breaked;
}

Expand Down Expand Up @@ -778,6 +793,9 @@ static void noreturn_successor_free(HtUPKv *kv) {
}

static bool noreturn_successors_cb(RAnalBlock *block, void *user) {
if (!block) {
return false;
}
HtUP *succs = user;
NoreturnSuccessor *succ = R_NEW0 (NoreturnSuccessor);
if (!succ) {
Expand All @@ -791,6 +809,9 @@ static bool noreturn_successors_cb(RAnalBlock *block, void *user) {
}

static bool noreturn_successors_reachable_cb(RAnalBlock *block, void *user) {
if (!block) {
return false;
}
HtUP *succs = user;
NoreturnSuccessor *succ = ht_up_find (succs, block->addr, NULL);
if (succ) {
Expand Down Expand Up @@ -895,12 +916,18 @@ typedef struct {
} AutomergeCtx;

static bool count_successors_cb(ut64 addr, void *user) {
if (addr == UT64_MAX) {
return true;
}
AutomergeCtx *ctx = user;
ctx->cur_succ_count++;
return true;
}

static bool automerge_predecessor_successor_cb(ut64 addr, void *user) {
if (addr == UT64_MAX) {
return true;
}
AutomergeCtx *ctx = user;
ctx->cur_succ_count++;
RAnalBlock *block = ht_up_find (ctx->blocks, addr, NULL);
Expand All @@ -923,6 +950,9 @@ static bool automerge_predecessor_successor_cb(ut64 addr, void *user) {
}

static bool automerge_get_predecessors_cb(void *user, ut64 k) {
if (k == UT64_MAX) {
return true;
}
AutomergeCtx *ctx = user;
const RAnalFunction *fcn = (const RAnalFunction *)(size_t)k;
RListIter *it;
Expand Down
75 changes: 49 additions & 26 deletions libr/anal/fcn.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ static int read_ahead(ReadAhead *ra, RAnal *anal, ut64 addr, ut8 *buf, int len)
return len;
}

// R2_590 R_API bool r_anal_function_resize(RAnalFunction *fcn, int newsize) {
R_API int r_anal_function_resize(RAnalFunction *fcn, int newsize) {
RAnal *anal = fcn->anal;
RAnalBlock *bb;
Expand Down Expand Up @@ -1139,7 +1140,6 @@ static int fcn_recurse(RAnal *anal, RAnalFunction *fcn, ut64 addr, ut64 len, int
gotoBeach (R_ANAL_RET_END);
}
}
ret = r_anal_function_bb (anal, fcn, op->jump, depth);
int tc = anal->opt.tailcall_delta;
if (tc) {
int diff = op->jump - op->addr;
Expand All @@ -1149,6 +1149,7 @@ static int fcn_recurse(RAnal *anal, RAnalFunction *fcn, ut64 addr, ut64 len, int
gotoBeach (R_ANAL_RET_END);
}
}
ret = r_anal_function_bb (anal, fcn, op->jump, depth);
goto beach;
#endif
break;
Expand Down Expand Up @@ -1186,44 +1187,65 @@ static int fcn_recurse(RAnal *anal, RAnalFunction *fcn, ut64 addr, ut64 len, int
if (bb->cond) {
bb->cond->type = op->cond;
}
#if 1
if (anal->opt.jmptbl) {
if (op->ptr != UT64_MAX) {
ut64 table_size, default_case;
table_size = anal->cmpval + 1;
default_case = op->fail; // is this really default case?
if (anal->cmpval != UT64_MAX && default_case != UT64_MAX && (op->reg || op->ireg)) {
// TODO -1
if (op->ireg) {
ret = try_walkthrough_jmptbl (anal, fcn, bb, depth, op->addr, 0, op->ptr, op->ptr, anal->config->bits >> 3, table_size, default_case, ret);
} else { // op->reg
ret = walkthrough_arm_jmptbl_style (anal, fcn, bb, depth, op->addr, op->ptr, anal->config->bits >> 3, table_size, default_case, ret);
}
// check if op->jump and op->fail contain jump table location
// clear jump address, because it's jump table location
if (op->jump == op->ptr) {
op->jump = UT64_MAX;
} else if (op->fail == op->ptr) {
op->fail = UT64_MAX;
}
anal->cmpval = UT64_MAX;
ut64 table_size, default_case;
table_size = anal->cmpval + 1;
default_case = op->fail; // is this really default case?
if (anal->cmpval != UT64_MAX && default_case != UT64_MAX && (op->reg || op->ireg)) {
// TODO -1
if (op->ireg) {
ret = try_walkthrough_jmptbl (anal, fcn, bb, depth, op->addr, 0, op->ptr, op->ptr, anal->config->bits >> 3, table_size, default_case, ret);
} else { // op->reg
ret = walkthrough_arm_jmptbl_style (anal, fcn, bb, depth, op->addr, op->ptr, anal->config->bits >> 3, table_size, default_case, ret);
}
}
// check if op->jump and op->fail contain jump table location
// clear jump address, because it's jump table location
if (op->jump == op->ptr) {
op->jump = UT64_MAX;
} else if (op->fail == op->ptr) {
op->fail = UT64_MAX;
}
}
#else
anal->cmpval = UT64_MAX;
if (anal->opt.jmptbl && op->ptr != UT64_MAX) {
ut64 table_size, default_case;
table_size = anal->cmpval + 1;
default_case = op->fail; // is this really default case?
if (anal->cmpval != UT64_MAX && default_case != UT64_MAX && (op->reg || op->ireg)) {
// TODO -1
if (op->ireg) {
ret = try_walkthrough_jmptbl (anal, fcn, bb, depth, op->addr, 0, op->ptr, op->ptr, anal->config->bits >> 3, table_size, default_case, ret);
} else { // op->reg
ret = walkthrough_arm_jmptbl_style (anal, fcn, bb, depth, op->addr, op->ptr, anal->config->bits >> 3, table_size, default_case, ret);
}
// check if op->jump and op->fail contain jump table location
// clear jump address, because it's jump table location
if (op->jump == op->ptr) {
op->jump = UT64_MAX;
} else if (op->fail == op->ptr) {
op->fail = UT64_MAX;
}
anal->cmpval = UT64_MAX;
}
}
int saved_stack = fcn->stack;
#endif
const int saved_stack = fcn->stack;
// TODO: depth -1 in here
r_anal_function_bb (anal, fcn, op->jump, depth);
fcn->stack = saved_stack;
ret = r_anal_function_bb (anal, fcn, op->fail, depth);
fcn->stack = saved_stack;

// XXX breaks mips analysis too !op->delay
// break;
// this will be all x86, arm (at least)
// without which the analysis is really slow,
// presumably because each opcode would get revisited
// (and already covered by a bb) many times
goto beach;
// For some reason, branch delayed code (MIPS) needs to continue
break;
case R_ANAL_OP_TYPE_UCALL:
case R_ANAL_OP_TYPE_RCALL:
case R_ANAL_OP_TYPE_ICALL:
Expand Down Expand Up @@ -1498,7 +1520,7 @@ static int fcn_recurse(RAnal *anal, RAnalFunction *fcn, ut64 addr, ut64 len, int
if (bb && bb->size == 0) {
r_anal_function_remove_block (fcn, bb);
}
r_anal_block_update_hash (bb);
// r_anal_block_update_hash (bb); // XXX
r_anal_block_unref (bb);
free (movbasereg);
return ret;
Expand Down Expand Up @@ -1754,7 +1776,7 @@ R_API bool r_anal_function_add_bb(RAnal *a, RAnalFunction *fcn, ut64 addr, ut64
}

if (!block) {
D R_LOG_WARN ("r_anal_function_add_bb failed in fcn 0x%08"PFMT64x" at 0x%08"PFMT64x, fcn->addr, addr);
R_LOG_DEBUG ("r_anal_function_add_bb failed in fcn 0x%08"PFMT64x" at 0x%08"PFMT64x, fcn->addr, addr);
return false;
}

Expand Down Expand Up @@ -2240,7 +2262,8 @@ static bool analize_addr_cb(ut64 addr, void *user) {
}

static bool analize_descendents(RAnalBlock *bb, void *user) {
return r_anal_block_successor_addrs_foreach (bb, analize_addr_cb, user);
r_anal_block_successor_addrs_foreach (bb, analize_addr_cb, user);
return true;
}

static void free_ht_up(HtUPKv *kv) {
Expand Down
4 changes: 2 additions & 2 deletions libr/anal/global.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* radare - LGPL - Copyright 2021 - pancake */
/* radare - LGPL - Copyright 2021-2023 - pancake */

#include <r_anal.h>
#include <r_util/r_print.h>
Expand All @@ -18,7 +18,7 @@ R_API bool r_anal_global_add(RAnal *anal, ut64 addr, const char *type_name, cons
RFlag *flags = anal->flb.f;
char *fmtstr = r_type_format (anal->sdb_types, type_name);
if (!fmtstr) {
eprintf ("Unknown type\n");
R_LOG_ERROR ("Unknown type in format string for a global");
return false;
}
int fmtsize = r_print_format_struct_size (anal->print, fmtstr, 0, 0);
Expand Down
Loading