Enable RzIL VM to halt on exceptions. (#5305)

* Print warnings if a runtime exception state is reached.

* Enable the RzIL VM to halt on exceptions.

---------

Co-authored-by: NOT XVilka <notxvilka@proton.me>
This commit is contained in:
Rot127 2025-08-25 12:43:01 +00:00 committed by GitHub
parent faf3c53168
commit 2602b1cdfa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 311 additions and 70 deletions

View file

@ -8,6 +8,44 @@
* @{
*/
static RzILEventException get_halt_exc_options(RzCoreBind *coreb, void *core) {
// core might be NULL for rz-asm.
const char *exc = core ? coreb->cfgGet(core, "rzil.step.events.halt_on_exc") : NULL;
if (RZ_STR_ISEMPTY(exc) || RZ_STR_EQ(exc, "none")) {
return RZ_IL_EVENT_EXC_NONE;
} else if (RZ_STR_EQ(exc, "all")) {
return RZ_IL_EVENT_EXC_DIV_ZERO |
RZ_IL_EVENT_EXC_FP_DIV_ZERO |
RZ_IL_EVENT_EXC_FP_INVALID_OP |
RZ_IL_EVENT_EXC_FP_OVERFLOW |
RZ_IL_EVENT_EXC_FP_UNDERFLOW |
RZ_IL_EVENT_EXC_FP_INEXACT;
}
RzILEventException he = 0;
RzList *opts = rz_str_split_duplist_n(exc, ",", 0, true);
RzListIter *it;
const char *e;
rz_list_foreach (opts, it, e) {
if (RZ_STR_EQ("div0", e)) {
he |= RZ_IL_EVENT_EXC_DIV_ZERO;
} else if (RZ_STR_EQ("fp_div0", e)) {
he |= RZ_IL_EVENT_EXC_FP_DIV_ZERO;
} else if (RZ_STR_EQ("fp_inexact", e)) {
he |= RZ_IL_EVENT_EXC_FP_INEXACT;
} else if (RZ_STR_EQ("fp_underflow", e)) {
he |= RZ_IL_EVENT_EXC_FP_UNDERFLOW;
} else if (RZ_STR_EQ("fp_overflow", e)) {
he |= RZ_IL_EVENT_EXC_FP_OVERFLOW;
} else if (RZ_STR_EQ("fp_invalid_op", e)) {
he |= RZ_IL_EVENT_EXC_FP_INVALID_OP;
} else {
RZ_LOG_WARN("Exception type '%s' in rzil.step.events.halt_on_exc is not handled!\n", e);
}
}
rz_list_free(opts);
return he;
}
static void var_state_free(void *e, void *user) {
RzAnalysisILInitStateVar *s = e;
if (!s) {
@ -175,7 +213,7 @@ new_real:
}
static void setup_vm_from_config(RzAnalysis *analysis, RzAnalysisILVM *vm, RzAnalysisILConfig *cfg) {
vm->vm = rz_il_vm_new(0, cfg->pc_size, cfg->big_endian);
vm->vm = rz_il_vm_new(0, cfg->pc_size, cfg->big_endian, get_halt_exc_options(&analysis->coreb, analysis->core));
if (!vm->vm) {
return;
}

View file

@ -3024,6 +3024,16 @@ static bool cb_flirt(void *user, void *data) {
return true;
}
static bool rzil_halt_on_exec(void *user, void *data) {
rz_return_val_if_fail(data, false);
RzConfigNode *node = (RzConfigNode *)data;
if (*node->value == '?') {
print_node_options(node);
return false;
}
return true;
}
RZ_API int rz_core_config_init(RzCore *core) {
int i;
char buf[128], *p, *tmpdir;
@ -3920,6 +3930,9 @@ RZ_API int rz_core_config_init(RzCore *core) {
/* RzIL config */
SETB("rzil.step.events.read", false, "enables/disables printing aezse read event");
SETB("rzil.step.events.write", true, "enables/disables printing aezse write event");
n = NODECB("rzil.step.events.halt_on_exc", "div0,fp_invalid_op", &rzil_halt_on_exec);
SETDESC(n, "Enables/disable exceptions the VM should halt if reached.");
SETOPTIONS(n, "div0", "fp_div0", "fp_inexact", "fp_underflow", "fp_overflow", "fp_invalid_op", "none", "all", NULL);
/* FLIRT config */
SETBPREF("flirt.sig.library", RZ_FLIRT_LIBRARY_NAME_DFL, "FLIRT library name for sig format");

View file

@ -4,6 +4,31 @@
#include <rz_il/rz_il_events.h>
/**
* Get the exception message for the exception id.
* \param id The exception identifier.
* \return The exception message.
*/
RZ_API const char *rz_il_event_exception_msg(const RzILEventException id) {
switch (id) {
case RZ_IL_EVENT_EXC_NONE:
return "None";
case RZ_IL_EVENT_EXC_DIV_ZERO:
return "division by zero";
case RZ_IL_EVENT_EXC_FP_INVALID_OP:
return "float invalid operation";
case RZ_IL_EVENT_EXC_FP_DIV_ZERO:
return "float division by zero";
case RZ_IL_EVENT_EXC_FP_OVERFLOW:
return "float overflow";
case RZ_IL_EVENT_EXC_FP_UNDERFLOW:
return "float underflow";
case RZ_IL_EVENT_EXC_FP_INEXACT:
return "float inexact";
}
return "unknown exception";
}
/**
* Frees an RzILEvent struct
* \param evt, RzILEvent, pointer to the RzILEvent to free
@ -14,7 +39,6 @@ RZ_API void rz_il_event_free(RZ_NULLABLE RzILEvent *evt) {
}
switch (evt->type) {
case RZ_IL_EVENT_EXCEPTION:
free(evt->data.exception);
break;
case RZ_IL_EVENT_PC_WRITE:
rz_bv_free(evt->data.pc_write.old_pc);
@ -47,10 +71,11 @@ RZ_API void rz_il_event_free(RZ_NULLABLE RzILEvent *evt) {
/**
* Creates an RzILEvent of type RZ_IL_EVENT_EXCEPTION
* \param exception, const char, pointer to the exception message
* \param id The exception identifier.
* \param msg Pointer to the exception message
*/
RZ_API RZ_OWN RzILEvent *rz_il_event_exception_new(RZ_NONNULL const char *exception) {
rz_return_val_if_fail(exception, NULL);
RZ_API RZ_OWN RzILEvent *rz_il_event_exception_new(RzILEventException id) {
rz_return_val_if_fail(id != RZ_IL_EVENT_EXC_NONE, NULL);
RzILEvent *evt = RZ_NEW(RzILEvent);
if (!evt) {
@ -59,12 +84,7 @@ RZ_API RZ_OWN RzILEvent *rz_il_event_exception_new(RZ_NONNULL const char *except
}
evt->type = RZ_IL_EVENT_EXCEPTION;
evt->data.exception = rz_str_dup(exception);
if (!evt->data.exception) {
rz_il_event_free(evt);
RZ_LOG_ERROR("RzIL: cannot allocate exception string\n");
return NULL;
}
evt->data.exception = id;
return evt;
}
@ -198,4 +218,4 @@ RZ_API RZ_OWN RzILEvent *rz_il_event_var_write_new(RZ_NONNULL const char *name,
}
return evt;
}
}

View file

@ -872,7 +872,7 @@ RZ_API void rz_il_event_json(RZ_NONNULL RzILEvent *evt, RZ_NONNULL PJ *pj) {
case RZ_IL_EVENT_EXCEPTION:
pj_o(pj);
pj_ks(pj, "type", "exception");
pj_ks(pj, "exception", evt->data.exception);
pj_ks(pj, "exception", rz_il_event_exception_msg(evt->data.exception));
pj_end(pj);
break;
case RZ_IL_EVENT_PC_WRITE:

View file

@ -1069,7 +1069,7 @@ RZ_API void rz_il_event_stringify(RZ_NONNULL const RzILEvent *evt, RZ_NONNULL Rz
switch (evt->type) {
case RZ_IL_EVENT_EXCEPTION:
rz_strbuf_appendf(sb, "exception(%s)", evt->data.exception);
rz_strbuf_appendf(sb, "exception(%s)", rz_il_event_exception_msg(evt->data.exception));
break;
case RZ_IL_EVENT_PC_WRITE:
tmp0 = rz_bv_as_hex_string(evt->data.pc_write.old_pc, false);

View file

@ -18,8 +18,9 @@ extern RZ_IPI RzILOpEffectHandler rz_il_op_handler_effect_table_default[RZ_IL_OP
* \param vm RzILVM, pointer to an empty VM
* \param start_addr ut64, initiation pc address
* \param addr_size ut32, size of the address in VM
* \param halt_exc The exceptions the VM should halt for if encountered.
*/
RZ_API bool rz_il_vm_init(RzILVM *vm, ut64 start_addr, ut32 addr_size, bool big_endian) {
RZ_API bool rz_il_vm_init(RzILVM *vm, ut64 start_addr, ut32 addr_size, bool big_endian, RzILEventException halt_exc) {
rz_return_val_if_fail(vm, false);
if (!rz_il_var_set_init(&vm->global_vars)) {
@ -60,6 +61,8 @@ RZ_API bool rz_il_vm_init(RzILVM *vm, ut64 start_addr, ut32 addr_size, bool big_
vm->val_count = 0;
vm->addr_size = addr_size;
vm->big_endian = big_endian;
vm->halt_exceptions = halt_exc;
vm->halt = false;
vm->events = rz_pvector_new((RzPVectorFree)rz_il_event_free);
if (!vm->events) {
@ -101,13 +104,14 @@ RZ_API void rz_il_vm_fini(RzILVM *vm) {
* \param vm RzILVM, pointer to an empty VM
* \param start_addr ut64, initiation pc address
* \param addr_size ut32, size of the address in VM
* \param halt_exc The exceptions the VM should halt for if encountered.
*/
RZ_API RzILVM *rz_il_vm_new(ut64 start_addr, ut32 addr_size, bool big_endian) {
RZ_API RzILVM *rz_il_vm_new(ut64 start_addr, ut32 addr_size, bool big_endian, RzILEventException halt_exc) {
RzILVM *vm = RZ_NEW0(RzILVM);
if (!vm) {
return NULL;
}
rz_il_vm_init(vm, start_addr, addr_size, big_endian);
rz_il_vm_init(vm, start_addr, addr_size, big_endian, halt_exc);
return vm;
}

View file

@ -284,6 +284,10 @@ RZ_API void rz_il_vm_event_add(RzILVM *vm, RzILEvent *evt) {
rz_warn_if_reached();
rz_il_event_free(evt);
}
if (evt->type == RZ_IL_EVENT_EXCEPTION && (vm->halt_exceptions & evt->data.exception)) {
RZ_LOG_WARN("Reached exception '%s'. Requesting VM to halt.\n", rz_il_event_exception_msg(evt->data.exception));
vm->halt = true;
}
}
/**
@ -305,12 +309,23 @@ RZ_API bool rz_il_vm_step(RzILVM *vm, RzILOpEffect *op, ut64 fallthrough_addr) {
rz_il_vm_clear_events(vm);
// Set the successor pc **before** evaluating. Any jmp/goto may then overwrite it again.
ut64 old_pc = rz_bv_to_ut64(vm->pc);
RzBitVector *next_pc = rz_bv_new_from_ut64(vm->pc->len, fallthrough_addr);
rz_il_vm_event_add(vm, rz_il_event_pc_write_new(vm->pc, next_pc));
rz_bv_free(vm->pc);
vm->pc = next_pc;
bool succ = rz_il_evaluate_effect(vm, op);
if (vm->halt) {
RZ_LOG_WARN("Halting VM at 0x%" PFMT64x "!\n", old_pc);
// Reset PC to halting instruction.
RzBitVector *prev_pc = rz_bv_new_from_ut64(vm->pc->len, old_pc);
rz_bv_free(vm->pc);
vm->pc = prev_pc;
// Don't reset vm->halt flag.
// User should re-init VM.
return false;
}
// remove any local defined variable (local pure vars are unbound automatically)
rz_il_var_set_reset(&vm->local_vars);

View file

@ -230,7 +230,7 @@ void *rz_il_handler_div(RzILVM *vm, RzILOpBitVector *op, RzILTypePure *type) {
if (rz_bv_is_zero_vector(y)) {
result = rz_bv_new(y->len);
rz_bv_set_all(result, true);
rz_il_vm_event_add(vm, rz_il_event_exception_new("division by zero"));
rz_il_vm_event_add(vm, rz_il_event_exception_new(RZ_IL_EVENT_EXC_DIV_ZERO));
} else {
result = rz_bv_div(x, y);
}

View file

@ -343,19 +343,19 @@ void *rz_il_handler_fexcept(RzILVM *vm, RzILOpPure *op, RzILTypePure *type) {
switch (args.e) {
case RZ_FLOAT_E_DIV_ZERO:
e = n->exception & RZ_FLOAT_E_DIV_ZERO;
rz_il_vm_event_add(vm, rz_il_event_exception_new("float division by zero"));
rz_il_vm_event_add(vm, rz_il_event_exception_new(RZ_IL_EVENT_EXC_FP_DIV_ZERO));
break;
case RZ_FLOAT_E_OVERFLOW:
e = n->exception & RZ_FLOAT_E_OVERFLOW;
rz_il_vm_event_add(vm, rz_il_event_exception_new("float overflow"));
rz_il_vm_event_add(vm, rz_il_event_exception_new(RZ_IL_EVENT_EXC_FP_OVERFLOW));
break;
case RZ_FLOAT_E_UNDERFLOW:
e = n->exception & RZ_FLOAT_E_UNDERFLOW;
rz_il_vm_event_add(vm, rz_il_event_exception_new("float underflow"));
rz_il_vm_event_add(vm, rz_il_event_exception_new(RZ_IL_EVENT_EXC_FP_UNDERFLOW));
break;
case RZ_FLOAT_E_INEXACT:
e = n->exception & RZ_FLOAT_E_INEXACT;
rz_il_vm_event_add(vm, rz_il_event_exception_new("float inexact"));
rz_il_vm_event_add(vm, rz_il_event_exception_new(RZ_IL_EVENT_EXC_FP_INEXACT));
break;
default:;
}

View file

@ -27,6 +27,16 @@ typedef enum rz_il_event_id_t {
RZ_IL_EVENT_VAR_WRITE,
} RzILEventId;
typedef enum {
RZ_IL_EVENT_EXC_NONE = 0,
RZ_IL_EVENT_EXC_DIV_ZERO = 1 << 1,
RZ_IL_EVENT_EXC_FP_INVALID_OP = 1 << 2,
RZ_IL_EVENT_EXC_FP_DIV_ZERO = 1 << 3,
RZ_IL_EVENT_EXC_FP_OVERFLOW = 1 << 4,
RZ_IL_EVENT_EXC_FP_UNDERFLOW = 1 << 5,
RZ_IL_EVENT_EXC_FP_INEXACT = 1 << 6,
} RzILEventException;
typedef struct rz_il_vm_event_mem_read_t {
RzILMemIndex index;
RZ_NONNULL RzBitVector *address;
@ -59,7 +69,7 @@ typedef struct rz_il_vm_event_var_write_t {
typedef struct rz_il_vm_event_t {
RzILEventId type;
union {
char *exception;
RzILEventException exception;
RzILEventPCWrite pc_write;
RzILEventMemRead mem_read;
RzILEventMemWrite mem_write;
@ -68,13 +78,14 @@ typedef struct rz_il_vm_event_t {
} data;
} RzILEvent;
RZ_API RZ_OWN RzILEvent *rz_il_event_exception_new(RZ_NONNULL const char *exception);
RZ_API RZ_OWN RzILEvent *rz_il_event_exception_new(RzILEventException type);
RZ_API RZ_OWN RzILEvent *rz_il_event_pc_write_new(RZ_NONNULL const RzBitVector *old_pc, RZ_NONNULL const RzBitVector *new_pc);
RZ_API RZ_OWN RzILEvent *rz_il_event_mem_read_new(RzILMemIndex index, RZ_NONNULL const RzBitVector *addr, RZ_NULLABLE const RzBitVector *value);
RZ_API RZ_OWN RzILEvent *rz_il_event_var_read_new(RZ_NONNULL const char *name, RZ_NULLABLE const RzILVal *value);
RZ_API RZ_OWN RzILEvent *rz_il_event_mem_write_new(RzILMemIndex index, RZ_NONNULL const RzBitVector *addr, RZ_NONNULL const RzBitVector *old_v, RZ_NONNULL const RzBitVector *new_v);
RZ_API RZ_OWN RzILEvent *rz_il_event_var_write_new(RZ_NONNULL const char *name, RZ_NULLABLE const RzILVal *old_v, RZ_NONNULL const RzILVal *new_v);
RZ_API void rz_il_event_free(RZ_NULLABLE RzILEvent *evt);
RZ_API const char *rz_il_event_exception_msg(const RzILEventException id);
// Printing/Export
RZ_API void rz_il_event_stringify(RZ_NONNULL const RzILEvent *evt, RZ_NONNULL RzStrBuf *sb);

View file

@ -49,12 +49,14 @@ struct rz_il_vm_t {
RzILOpEffectHandler *op_handler_effect_table; ///< Array of Handler, handler can be indexed by opcode
RzPVector /*<RzILEvent *>*/ *events; ///< List of events that has happened in the last step
bool big_endian; ///< Sets the endianness of the memory reads/writes operations
RzILEventException halt_exceptions; ///< The exceptions the VM should halt if encountered.
bool halt; ///< If set the VM should halt and notify the user.
};
// VM high level operations
RZ_API RzILVM *rz_il_vm_new(ut64 start_addr, ut32 addr_size, bool big_endian);
RZ_API RzILVM *rz_il_vm_new(ut64 start_addr, ut32 addr_size, bool big_endian, RzILEventException halt_exc);
RZ_API void rz_il_vm_free(RzILVM *vm);
RZ_API bool rz_il_vm_init(RzILVM *vm, ut64 start_addr, ut32 addr_size, bool big_endian);
RZ_API bool rz_il_vm_init(RzILVM *vm, ut64 start_addr, ut32 addr_size, bool big_endian, RzILEventException halt_exc);
RZ_API void rz_il_vm_fini(RzILVM *vm);
RZ_API ut32 rz_il_vm_get_pc_len(RzILVM *vm);

View file

@ -805,9 +805,11 @@ el~RzIL
EOF
EXPECT=<<EOF
asm.cmt.il: Show RzIL expressions as comments
rzil.step.events.halt_on_exc: Enables/disable exceptions the VM should halt if reached.
rzil.step.events.read: enables/disables printing aezse read event
rzil.step.events.write: enables/disables printing aezse write event
---
rzil.step.events.halt_on_exc: Enables/disable exceptions the VM should halt if reached.
rzil.step.events.read: enables/disables printing aezse read event
rzil.step.events.write: enables/disables printing aezse write event
---
@ -826,10 +828,12 @@ echo "---"
el~RzIL
EOF
EXPECT=<<EOF
rzil.step.events.halt_on_exc: Enables/disable exceptions the VM should halt if reached.
rzil.step.events.read: enables/disables printing aezse read event
rzil.step.events.write: enables/disables printing aezse write event
---
asm.cmt.il: Show RzIL expressions as comments
rzil.step.events.halt_on_exc: Enables/disable exceptions the VM should halt if reached.
rzil.step.events.read: enables/disables printing aezse read event
rzil.step.events.write: enables/disables printing aezse write event
---
@ -849,13 +853,16 @@ el~RzIL
EOF
EXPECT=<<EOF
asm.cmt.il: Show RzIL expressions as comments
rzil.step.events.halt_on_exc: Enables/disable exceptions the VM should halt if reached.
rzil.step.events.read: enables/disables printing aezse read event
rzil.step.events.write: enables/disables printing aezse write event
---
rzil.step.events.halt_on_exc: Enables/disable exceptions the VM should halt if reached.
rzil.step.events.read: enables/disables printing aezse read event
rzil.step.events.write: enables/disables printing aezse write event
---
asm.cmt.il: Show RzIL expressions as comments
rzil.step.events.halt_on_exc: Enables/disable exceptions the VM should halt if reached.
rzil.step.events.read: enables/disables printing aezse read event
rzil.step.events.write: enables/disables printing aezse write event
EOF

79
test/db/rzil/vm Normal file
View file

@ -0,0 +1,79 @@
NAME=Halt VM on exception
FILE==
ARGS=-a ppc -e cfg.bigendian=true -b 64
CMDS=<<EOF
# li 3, 4
# li 4, 0
# divdu 5, 3, 4
# invalid
wx 38600004388000007ca3239200000000
pd 4
e rzil.step.events.halt_on_exc=none
aezi
aezse 4
aezv PC
e rzil.step.events.halt_on_exc=div0
s 0x0
aezi
aezse 4
aezv PC
e rzil.step.events.halt_on_exc=all
s 0x0
aezi
aezse 4
aezv PC
e rzil.step.events.halt_on_exc=fp_div0
s 0x0
aezi
aezse 4
aezv PC
EOF
EXPECT_ERR=<<EOF
WARNING: Reached exception 'division by zero'. Requesting VM to halt.
WARNING: Halting VM at 0x8!
ERROR: RzIL: stepping failed with PC at 0x8.
WARNING: Reached exception 'division by zero'. Requesting VM to halt.
WARNING: Halting VM at 0x8!
ERROR: RzIL: stepping failed with PC at 0x8.
EOF
EXPECT=<<EOF
0x00000000 li r3, 4
0x00000004 li r4, 0
0x00000008 divdu r5, r3, r4
0x0000000c invalid
pc_write(old: 0x0, new: 0x4)
var_write(name: r3, old: 0x0, new: 0x4)
pc_write(old: 0x4, new: 0x8)
var_write(name: r4, old: 0x0, new: 0x0)
pc_write(old: 0x8, new: 0xc)
exception(division by zero)
var_write(name: r5, old: 0x0, new: 0xffffffffffffffff)
pc_write(old: 0xc, new: 0x10)
PC: 0x0000000000000010
pc_write(old: 0x0, new: 0x4)
var_write(name: r3, old: 0x4, new: 0x4)
pc_write(old: 0x4, new: 0x8)
var_write(name: r4, old: 0x0, new: 0x0)
PC: 0x0000000000000008
pc_write(old: 0x0, new: 0x4)
var_write(name: r3, old: 0x4, new: 0x4)
pc_write(old: 0x4, new: 0x8)
var_write(name: r4, old: 0x0, new: 0x0)
PC: 0x0000000000000008
pc_write(old: 0x0, new: 0x4)
var_write(name: r3, old: 0x4, new: 0x4)
pc_write(old: 0x4, new: 0x8)
var_write(name: r4, old: 0x0, new: 0x0)
pc_write(old: 0x8, new: 0xc)
exception(division by zero)
var_write(name: r5, old: 0xffffffffffffffff, new: 0xffffffffffffffff)
pc_write(old: 0xc, new: 0x10)
PC: 0x0000000000000010
EOF
RUN

View file

@ -12,7 +12,7 @@ static bool test_il_extract32() {
RzILValidateReport report;
RzILValidateGlobalContext *ctx = rz_il_validate_global_context_new_empty(24);
bool valid = false;
RzILVM *vm = rz_il_vm_new(0, 32, false);
RzILVM *vm = rz_il_vm_new(0, 32, false, RZ_IL_EVENT_EXC_NONE);
RzILVal *vm_result = NULL;
// Extract all
@ -65,7 +65,7 @@ static bool test_il_extract64() {
RzILValidateReport report;
RzILValidateGlobalContext *ctx = rz_il_validate_global_context_new_empty(24);
bool valid = false;
RzILVM *vm = rz_il_vm_new(0, 64, false);
RzILVM *vm = rz_il_vm_new(0, 64, false, RZ_IL_EVENT_EXC_NONE);
RzILVal *vm_result = NULL;
// Extract all
@ -118,7 +118,7 @@ static bool test_il_sextract64() {
RzILValidateReport report;
RzILValidateGlobalContext *ctx = rz_il_validate_global_context_new_empty(24);
bool valid = false;
RzILVM *vm = rz_il_vm_new(0, 64, false);
RzILVM *vm = rz_il_vm_new(0, 64, false, RZ_IL_EVENT_EXC_NONE);
RzILVal *vm_result = NULL;
// Extract all
@ -171,7 +171,7 @@ static bool test_il_deposit32() {
RzILValidateReport report;
RzILValidateGlobalContext *ctx = rz_il_validate_global_context_new_empty(24);
bool valid = false;
RzILVM *vm = rz_il_vm_new(0, 32, false);
RzILVM *vm = rz_il_vm_new(0, 32, false, RZ_IL_EVENT_EXC_NONE);
RzILVal *vm_result = NULL;
// Deposit all
@ -241,7 +241,7 @@ static bool test_il_deposit64() {
RzILValidateReport report;
RzILValidateGlobalContext *ctx = rz_il_validate_global_context_new_empty(24);
bool valid = false;
RzILVM *vm = rz_il_vm_new(0, 32, false);
RzILVM *vm = rz_il_vm_new(0, 32, false, RZ_IL_EVENT_EXC_NONE);
RzILVal *vm_result = NULL;
// Deposit all
@ -311,7 +311,7 @@ static bool test_il_bswap16() {
RzILValidateReport report;
RzILValidateGlobalContext *ctx = rz_il_validate_global_context_new_empty(24);
bool valid = false;
RzILVM *vm = rz_il_vm_new(0, 32, false);
RzILVM *vm = rz_il_vm_new(0, 32, false, RZ_IL_EVENT_EXC_NONE);
RzILVal *vm_result = NULL;
// Deposit all
@ -336,7 +336,7 @@ static bool test_il_bswap32() {
RzILValidateReport report;
RzILValidateGlobalContext *ctx = rz_il_validate_global_context_new_empty(24);
bool valid = false;
RzILVM *vm = rz_il_vm_new(0, 32, false);
RzILVM *vm = rz_il_vm_new(0, 32, false, RZ_IL_EVENT_EXC_NONE);
RzILVal *vm_result = NULL;
// Deposit all
@ -361,7 +361,7 @@ static bool test_il_bswap64() {
RzILValidateReport report;
RzILValidateGlobalContext *ctx = rz_il_validate_global_context_new_empty(24);
bool valid = false;
RzILVM *vm = rz_il_vm_new(0, 32, false);
RzILVM *vm = rz_il_vm_new(0, 32, false, RZ_IL_EVENT_EXC_NONE);
RzILVal *vm_result = NULL;
// Deposit all
@ -382,7 +382,7 @@ static bool test_il_bswap64() {
}
static bool test_il_fneq() {
RzILVM *vm = rz_il_vm_new(0, 64, false);
RzILVM *vm = rz_il_vm_new(0, 64, false, RZ_IL_EVENT_EXC_NONE);
RzILBool *vm_result = NULL;
RzILOpBool *result = NULL;
@ -452,7 +452,7 @@ static bool test_il_fneq() {
}
static bool test_il_feq() {
RzILVM *vm = rz_il_vm_new(0, 64, false);
RzILVM *vm = rz_il_vm_new(0, 64, false, RZ_IL_EVENT_EXC_NONE);
RzILBool *vm_result = NULL;
RzILOpBool *result = NULL;
@ -522,7 +522,7 @@ static bool test_il_feq() {
}
static bool test_il_flt() {
RzILVM *vm = rz_il_vm_new(0, 64, false);
RzILVM *vm = rz_il_vm_new(0, 64, false, RZ_IL_EVENT_EXC_NONE);
RzILBool *vm_result = NULL;
RzILOpBool *result = NULL;
@ -592,7 +592,7 @@ static bool test_il_flt() {
}
static bool test_il_fle() {
RzILVM *vm = rz_il_vm_new(0, 64, false);
RzILVM *vm = rz_il_vm_new(0, 64, false, RZ_IL_EVENT_EXC_NONE);
RzILBool *vm_result = NULL;
RzILOpBool *result = NULL;
@ -662,7 +662,7 @@ static bool test_il_fle() {
}
static bool test_il_fgt() {
RzILVM *vm = rz_il_vm_new(0, 64, false);
RzILVM *vm = rz_il_vm_new(0, 64, false, RZ_IL_EVENT_EXC_NONE);
RzILBool *vm_result = NULL;
RzILOpBool *result = NULL;
@ -732,7 +732,7 @@ static bool test_il_fgt() {
}
static bool test_il_fge() {
RzILVM *vm = rz_il_vm_new(0, 64, false);
RzILVM *vm = rz_il_vm_new(0, 64, false, RZ_IL_EVENT_EXC_NONE);
RzILBool *vm_result = NULL;
RzILOpBool *result = NULL;

View file

@ -197,7 +197,7 @@ static bool test_il_vm_sync_to_reg() {
rz_reg_setv(reg, "af", 0);
rz_reg_setv(reg, "bf", 0);
RzILVM *vm = rz_il_vm_new(0, 64, false);
RzILVM *vm = rz_il_vm_new(0, 64, false, RZ_IL_EVENT_EXC_NONE);
RzILRegBinding *rb = rz_il_reg_binding_exactly(reg, RZ_ARRAY_SIZE(bind), bind);
rz_il_vm_setup_reg_binding(vm, rb);
@ -274,7 +274,7 @@ static bool test_il_vm_sync_from_reg() {
rz_reg_setv(reg, "af", 0);
rz_reg_setv(reg, "bf", 1);
RzILVM *vm = rz_il_vm_new(0, 64, false);
RzILVM *vm = rz_il_vm_new(0, 64, false, RZ_IL_EVENT_EXC_NONE);
RzILRegBinding *rb = rz_il_reg_binding_exactly(reg, RZ_ARRAY_SIZE(bind), bind);
rz_il_vm_setup_reg_binding(vm, rb);

View file

@ -4,16 +4,18 @@
#include <rz_il.h>
#include <rz_util.h>
#include "minunit.h"
#include "rz_il/rz_il_events.h"
#include "rz_il/rz_il_opcodes.h"
static bool test_rzil_vm_init() {
RzILVM *vm = rz_il_vm_new(0, 8, true);
RzILVM *vm = rz_il_vm_new(0, 8, true, RZ_IL_EVENT_EXC_NONE);
mu_assert_eq(vm->addr_size, 8, "VM Init");
rz_il_vm_free(vm);
mu_end;
}
static bool test_rzil_vm_global_vars() {
RzILVM *vm = rz_il_vm_new(0, 8, true);
RzILVM *vm = rz_il_vm_new(0, 8, true, RZ_IL_EVENT_EXC_NONE);
// 1. create variables
RzILVar *var_r1 = rz_il_vm_create_global_var(vm, "r1", rz_il_sort_pure_bool());
@ -60,7 +62,7 @@ static bool test_rzil_vm_global_vars() {
}
static bool test_rzil_vm_labels() {
RzILVM *vm = rz_il_vm_new(0, 8, true);
RzILVM *vm = rz_il_vm_new(0, 8, true, RZ_IL_EVENT_EXC_NONE);
// create label
RzBitVector *addr = rz_bv_new_from_ut64(16, 233);
RzILEffectLabel *blackhole = rz_il_vm_create_label(vm, "blackhole", addr);
@ -100,7 +102,7 @@ static bool test_rzil_vm_labels() {
}
static bool test_rzil_vm_root_evaluation() {
RzILVM *vm = rz_il_vm_new(0, 8, false);
RzILVM *vm = rz_il_vm_new(0, 8, false, RZ_IL_EVENT_EXC_NONE);
// (ite (add 23 19)
// true
@ -132,7 +134,7 @@ static bool test_rzil_vm_root_evaluation() {
}
static bool test_rzil_vm_step() {
RzILVM *vm = rz_il_vm_new(0, 16, false);
RzILVM *vm = rz_il_vm_new(0, 16, false, RZ_IL_EVENT_EXC_NONE);
RzILVar *var_r1 = rz_il_vm_create_global_var(vm, "r1", rz_il_sort_pure_bv(32));
rz_il_vm_create_global_var(vm, "r2", rz_il_sort_pure_bv(32));
@ -164,8 +166,57 @@ static bool test_rzil_vm_step() {
mu_end;
}
static bool test_rzil_vm_halt_on_exc() {
ut64 expected_last_pc = 0x332; // The PC of the halting (div 0) instruction
RzILEventException halt_for = RZ_IL_EVENT_EXC_DIV_ZERO;
do {
RzILVM *vm = rz_il_vm_new(0, 16, false, halt_for);
RzILVar *var_r1 = rz_il_vm_create_global_var(vm, "r1", rz_il_sort_pure_bv(32));
RzILVar *var_r2 = rz_il_vm_create_global_var(vm, "r2", rz_il_sort_pure_bv(32));
RzILOpEffect *op = rz_il_op_new_set("r1", false, rz_il_op_new_bitv_from_ut64(32, 42));
bool succ = rz_il_vm_step(vm, op, 0x331);
rz_il_op_effect_free(op);
mu_assert_true(succ, "success set r1");
op = rz_il_op_new_set("r2", false, rz_il_op_new_bitv_from_ut64(32, 0));
succ = rz_il_vm_step(vm, op, 0x332);
rz_il_op_effect_free(op);
mu_assert_true(succ, "success set r2");
RzILOpPure *r1 = rz_il_op_new_var(var_r1->name, RZ_IL_VAR_KIND_GLOBAL);
mu_assert_notnull(r1, "get val");
RzILOpPure *r2 = rz_il_op_new_var(var_r2->name, RZ_IL_VAR_KIND_GLOBAL);
mu_assert_notnull(r2, "get val");
op = rz_il_op_new_set("r2", false, rz_il_op_new_div(r1, r2));
succ = rz_il_vm_step(vm, op, 0x333);
rz_il_op_effect_free(op);
if (halt_for == RZ_IL_EVENT_EXC_DIV_ZERO) {
mu_assert_false(succ, "VM did not halt after div0.");
mu_assert_true(vm->halt, "VM halt flag not set.");
} else {
mu_assert_true(succ, "VM did halt after div0, but should not have.");
mu_assert_false(vm->halt, "VM halt flag is set.");
}
RzBitVector *pc = vm->pc;
mu_assert_notnull(pc, "pc");
mu_assert_eq(rz_bv_to_ut64(pc), expected_last_pc, "VM pc doesn't match.");
rz_il_vm_free(vm);
// Do it again, this time don't halt on div 0
// Expected PC is now one past div0 instruction (fallthrough address).
expected_last_pc++;
halt_for = RZ_IL_EVENT_EXC_NONE;
} while (expected_last_pc == 0x334);
mu_end;
}
static bool test_rzil_vm_op_let() {
RzILVM *vm = rz_il_vm_new(0, 8, false);
RzILVM *vm = rz_il_vm_new(0, 8, false, RZ_IL_EVENT_EXC_NONE);
// simple case:
// let preanswer = 41 in preanswer + 1
@ -214,7 +265,7 @@ static bool test_rzil_vm_op_let() {
}
static bool test_rzil_vm_op_cast() {
RzILVM *vm = rz_il_vm_new(0, 8, false);
RzILVM *vm = rz_il_vm_new(0, 8, false, RZ_IL_EVENT_EXC_NONE);
// 8 -> 8
RzILOpPure *op = rz_il_op_new_cast(8, rz_il_op_new_b0(), rz_il_op_new_bitv_from_ut64(8, 0x42));
@ -259,7 +310,7 @@ static bool test_rzil_vm_op_cast() {
}
static bool test_rzil_vm_op_unsigned() {
RzILVM *vm = rz_il_vm_new(0, 8, false);
RzILVM *vm = rz_il_vm_new(0, 8, false, RZ_IL_EVENT_EXC_NONE);
// msb not set, filled with 0
RzILOpPure *op = rz_il_op_new_unsigned(13, rz_il_op_new_bitv_from_ut64(8, 0x42));
@ -286,7 +337,7 @@ static bool test_rzil_vm_op_unsigned() {
}
static bool test_rzil_vm_op_signed() {
RzILVM *vm = rz_il_vm_new(0, 8, false);
RzILVM *vm = rz_il_vm_new(0, 8, false, RZ_IL_EVENT_EXC_NONE);
// msb not set, filled with 0
RzILOpPure *op = rz_il_op_new_signed(13, rz_il_op_new_bitv_from_ut64(8, 0x42));
@ -313,7 +364,7 @@ static bool test_rzil_vm_op_signed() {
}
static bool test_rzil_vm_op_set() {
RzILVM *vm = rz_il_vm_new(0, 8, false);
RzILVM *vm = rz_il_vm_new(0, 8, false, RZ_IL_EVENT_EXC_NONE);
RzILVar *var_r1 = rz_il_vm_create_global_var(vm, "r1", rz_il_sort_pure_bv(32));
rz_il_vm_create_global_var(vm, "r2", rz_il_sort_pure_bv(32));
@ -401,7 +452,7 @@ static bool test_rzil_vm_op_set() {
}
static bool test_rzil_vm_op_jmp() {
RzILVM *vm = rz_il_vm_new(0, 8, false);
RzILVM *vm = rz_il_vm_new(0, 8, false, RZ_IL_EVENT_EXC_NONE);
RzILOpEffect *op = rz_il_op_new_jmp(rz_il_op_new_bitv_from_ut64(8, 0x42));
bool succ = rz_il_evaluate_effect(vm, op);
@ -414,7 +465,7 @@ static bool test_rzil_vm_op_jmp() {
}
static bool test_rzil_vm_op_goto_addr() {
RzILVM *vm = rz_il_vm_new(0, 8, false);
RzILVM *vm = rz_il_vm_new(0, 8, false, RZ_IL_EVENT_EXC_NONE);
RzBitVector *dst = rz_bv_new_from_ut64(8, 0x42);
rz_il_vm_create_label(vm, "beach", dst);
@ -431,7 +482,7 @@ static bool test_rzil_vm_op_goto_addr() {
}
static bool test_rzil_vm_op_blk() {
RzILVM *vm = rz_il_vm_new(0, 8, false);
RzILVM *vm = rz_il_vm_new(0, 8, false, RZ_IL_EVENT_EXC_NONE);
RzILVar *var = rz_il_vm_create_global_var(vm, "leetbap", rz_il_sort_pure_bv(8));
rz_il_vm_set_global_var(vm, var->name, rz_il_value_new_bitv(rz_bv_new_from_ut64(8, 0x42)));
@ -475,7 +526,7 @@ static bool test_rzil_vm_op_blk() {
* In the end, leetbap == 30618
*/
static bool test_rzil_vm_op_repeat() {
RzILVM *vm = rz_il_vm_new(0, 8, false);
RzILVM *vm = rz_il_vm_new(0, 8, false, RZ_IL_EVENT_EXC_NONE);
RzILVar *var = rz_il_vm_create_global_var(vm, "leetbap", rz_il_sort_pure_bv(16));
rz_il_vm_set_global_var(vm, var->name, rz_il_value_new_bitv(rz_bv_new_from_ut64(16, 42)));
@ -515,7 +566,7 @@ static void hook_test(RzILVM *vm, RzILOpEffect *op) {
}
static bool test_rzil_vm_op_goto_hook() {
RzILVM *vm = rz_il_vm_new(0, 8, false);
RzILVM *vm = rz_il_vm_new(0, 8, false, RZ_IL_EVENT_EXC_NONE);
rz_il_vm_create_global_var(vm, "myvar", rz_il_sort_pure_bv(32));
@ -541,7 +592,7 @@ static bool test_rzil_vm_op_goto_hook() {
static bool test_rzil_vm_op_load() {
const ut8 data[] = { 0x10, 0x11, 0x12, 0x42, 0x14, 0x15 };
RzILVM *vm = rz_il_vm_new(0, 12, false);
RzILVM *vm = rz_il_vm_new(0, 12, false, RZ_IL_EVENT_EXC_NONE);
RzBuffer *buf = rz_buf_new_with_pointers(data, sizeof(data), false);
rz_buf_set_overflow_byte(buf, 0xaa);
rz_il_vm_add_mem(vm, 0, rz_il_mem_new(buf, 16));
@ -591,7 +642,7 @@ static bool test_rzil_vm_op_load() {
static bool test_rzil_vm_op_store() {
ut8 data[] = { 0x10, 0x11, 0x12, 0x42, 0x14, 0x15 };
RzILVM *vm = rz_il_vm_new(0, 12, false);
RzILVM *vm = rz_il_vm_new(0, 12, false, RZ_IL_EVENT_EXC_NONE);
RzBuffer *buf = rz_buf_new_with_pointers(data, sizeof(data), false);
rz_il_vm_add_mem(vm, 0, rz_il_mem_new(buf, 16));
rz_buf_free(buf);
@ -622,7 +673,7 @@ static bool test_rzil_vm_op_store() {
static bool test_rzil_vm_op_loadw_le() {
const ut8 data[] = { 0x0, 0x1, 0x2, 0x42, 0x4, 0x5 };
RzILVM *vm = rz_il_vm_new(0, 12, false);
RzILVM *vm = rz_il_vm_new(0, 12, false, RZ_IL_EVENT_EXC_NONE);
RzBuffer *buf = rz_buf_new_with_pointers(data, sizeof(data), false);
rz_buf_set_overflow_byte(buf, 0xaa);
rz_il_vm_add_mem(vm, 0, rz_il_mem_new(buf, 16));
@ -653,7 +704,7 @@ static bool test_rzil_vm_op_loadw_le() {
static bool test_rzil_vm_op_storew_le() {
ut8 data[] = { 0x0, 0x1, 0x2, 0x42, 0x4, 0x5 };
RzILVM *vm = rz_il_vm_new(0, 12, false);
RzILVM *vm = rz_il_vm_new(0, 12, false, RZ_IL_EVENT_EXC_NONE);
RzBuffer *buf = rz_buf_new_with_pointers(data, sizeof(data), false);
rz_il_vm_add_mem(vm, 0, rz_il_mem_new(buf, 16));
rz_buf_free(buf);
@ -684,7 +735,7 @@ static bool test_rzil_vm_op_storew_le() {
static bool test_rzil_vm_op_loadw_be() {
const ut8 data[] = { 0x0, 0x1, 0x2, 0x42, 0x4, 0x5 };
RzILVM *vm = rz_il_vm_new(0, 12, true);
RzILVM *vm = rz_il_vm_new(0, 12, true, RZ_IL_EVENT_EXC_NONE);
RzBuffer *buf = rz_buf_new_with_pointers(data, sizeof(data), false);
rz_buf_set_overflow_byte(buf, 0xaa);
rz_il_vm_add_mem(vm, 0, rz_il_mem_new(buf, 16));
@ -715,7 +766,7 @@ static bool test_rzil_vm_op_loadw_be() {
static bool test_rzil_vm_op_storew_be() {
ut8 data[] = { 0x0, 0x1, 0x2, 0x42, 0x4, 0x5 };
RzILVM *vm = rz_il_vm_new(0, 8, true);
RzILVM *vm = rz_il_vm_new(0, 8, true, RZ_IL_EVENT_EXC_NONE);
RzBuffer *buf = rz_buf_new_with_pointers(data, sizeof(data), false);
rz_il_vm_add_mem(vm, 0, rz_il_mem_new(buf, 16));
rz_buf_free(buf);
@ -745,7 +796,7 @@ static bool test_rzil_vm_op_storew_be() {
}
static bool test_rzil_vm_op_append() {
RzILVM *vm = rz_il_vm_new(0, 8, true);
RzILVM *vm = rz_il_vm_new(0, 8, true, RZ_IL_EVENT_EXC_NONE);
RzILOpPure *op = rz_il_op_new_append(rz_il_op_new_bitv_from_ut64(16, 0xc0ff), rz_il_op_new_bitv_from_ut64(8, 0xee));
RzBitVector *r = rz_il_evaluate_bitv(vm, op);
@ -760,7 +811,7 @@ static bool test_rzil_vm_op_append() {
}
static bool test_rzil_vm_op_shiftr() {
RzILVM *vm = rz_il_vm_new(0, 8, true);
RzILVM *vm = rz_il_vm_new(0, 8, true, RZ_IL_EVENT_EXC_NONE);
RzILOpPure *op = rz_il_op_new_shiftr(rz_il_op_new_b0(),
rz_il_op_new_bitv_from_ut64(16, 0xc0ff), rz_il_op_new_bitv_from_ut64(4, 3));
@ -785,7 +836,7 @@ static bool test_rzil_vm_op_shiftr() {
}
static bool test_rzil_vm_op_shiftl() {
RzILVM *vm = rz_il_vm_new(0, 8, true);
RzILVM *vm = rz_il_vm_new(0, 8, true, RZ_IL_EVENT_EXC_NONE);
RzILOpPure *op = rz_il_op_new_shiftl(rz_il_op_new_b0(),
rz_il_op_new_bitv_from_ut64(16, 0xc0ff), rz_il_op_new_bitv_from_ut64(4, 3));
@ -810,7 +861,7 @@ static bool test_rzil_vm_op_shiftl() {
}
static bool test_rzil_vm_op_compare() {
RzILVM *vm = rz_il_vm_new(0, 8, true);
RzILVM *vm = rz_il_vm_new(0, 8, true, RZ_IL_EVENT_EXC_NONE);
#define TEST_COMPARE(sign, name, lv, rv, expect) \
do { \
RzILOpBool *op = rz_il_op_new_##sign##name(rz_il_op_new_bitv_from_##sign##t64(32, lv), rz_il_op_new_bitv_from_##sign##t64(32, rv)); \
@ -869,7 +920,7 @@ static bool test_rzil_vm_op_compare() {
}
static bool test_rzil_vm_op_float() {
RzILVM *vm = rz_il_vm_new(0, 64, false);
RzILVM *vm = rz_il_vm_new(0, 64, false, RZ_IL_EVENT_EXC_NONE);
// let f = 2.14 in
// (ite (is_fneg f)
@ -946,7 +997,7 @@ static bool test_rzil_vm_op_float() {
static bool test_rzil_vm_op_fcast() {
// cast of float
RzILVM *vm = rz_il_vm_new(0, 64, false);
RzILVM *vm = rz_il_vm_new(0, 64, false, RZ_IL_EVENT_EXC_NONE);
RzFloat *act_float;
RzFloat *expect_float;
@ -1028,7 +1079,7 @@ static bool test_rzil_vm_op_fexcept() {
* 4. inexact result
*/
RzILVM *vm = rz_il_vm_new(0, 32, false);
RzILVM *vm = rz_il_vm_new(0, 32, false, RZ_IL_EVENT_EXC_NONE);
RzFloat *result;
RzILBool *e;
RzILOpFloat *op;
@ -1115,6 +1166,7 @@ bool all_tests() {
mu_run_test(test_rzil_vm_op_float);
mu_run_test(test_rzil_vm_op_fcast);
mu_run_test(test_rzil_vm_op_fexcept);
mu_run_test(test_rzil_vm_halt_on_exc);
return tests_passed != tests_run;
}