From 1fd89fcb5b7734b8b45d1019d6a0ae5339a40aa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Mon, 20 Jul 2026 17:45:03 +0200 Subject: [PATCH 001/334] Add exclusions to config saving This will be used for future experimental options that are subject to frequent change and should not pollute projects. --- librz/config/serialize_config.c | 54 +++++++++++++++++++++--------- librz/core/cfile.c | 4 +-- librz/core/cmd/cmd_system.c | 2 +- librz/core/serialize_core.c | 55 +++++++++++++++++++------------ librz/include/rz_config.h | 2 +- test/unit/test_serialize_config.c | 41 ++++++++++++++++++----- 6 files changed, 110 insertions(+), 48 deletions(-) diff --git a/librz/config/serialize_config.c b/librz/config/serialize_config.c index e33eee2dbd8..e7ba69749f9 100644 --- a/librz/config/serialize_config.c +++ b/librz/config/serialize_config.c @@ -5,15 +5,40 @@ #include #include "config_internal.h" +static RzSetS *build_exclude_set(const char **strings) { + if (!strings) { + return NULL; + } + HtSP *r = rz_set_s_new(HT_STR_DUP); + if (!r) { + return NULL; + } + for (; *strings; strings++) { + rz_set_s_add(r, *strings); + } + return r; +} + +typedef struct serialze_ctx_t { + Sdb *db; + HtSP *exclude; +} SerializeCtx; + static bool config_serialize_to_sdb(const RzConfigEntry *entry, void *user) { - Sdb *db = user; + SerializeCtx *ctx = user; if (entry->is_variable) { + if (ctx->exclude && rz_set_s_contains(ctx->exclude, entry->var.name)) { + return true; + } char *value = rz_config_var_as_string(&entry->var); - sdb_set(db, entry->var.name, value); + sdb_set(ctx->db, entry->var.name, value); free(value); } else { const RzConfigNode *node = &entry->node; - sdb_set(db, node->name, node->value); + if (ctx->exclude && rz_set_s_contains(ctx->exclude, node->name)) { + return true; + } + sdb_set(ctx->db, node->name, node->value); } return true; } @@ -29,11 +54,16 @@ static bool config_serialize_to_sdb(const RzConfigEntry *entry, void *user) { * ... * */ -RZ_API void rz_serialize_config_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzConfig *config) { - rz_config_iterate_over(config, config_serialize_to_sdb, db); +RZ_API void rz_serialize_config_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzConfig *config, RZ_NULLABLE const char **exclude) { + rz_return_if_fail(db && config); + SerializeCtx ctx = { .db = db, .exclude = build_exclude_set(exclude) }; + if (exclude && !ctx.exclude) { + return; + } + rz_config_iterate_over(config, config_serialize_to_sdb, &ctx); } -typedef struct deserialize_ctx_s { +typedef struct deserialize_ctx_t { RzConfig *config; HtSP *exclude; } DeserializeCtx; @@ -65,15 +95,9 @@ static bool config_deserialize_from_sdb(void *user, const SdbKv *kv) { RZ_API bool rz_serialize_config_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzConfig *config, RZ_NULLABLE const char **exclude) { rz_return_val_if_fail(db && config, false); - DeserializeCtx ctx = { config, NULL }; - if (exclude) { - ctx.exclude = ht_sp_new(HT_STR_DUP, NULL, NULL); - if (!ctx.exclude) { - return false; - } - for (; *exclude; exclude++) { - ht_sp_insert(ctx.exclude, *exclude, NULL); - } + DeserializeCtx ctx = { .config = config, .exclude = build_exclude_set(exclude) }; + if (exclude && !ctx.exclude) { + return false; } sdb_foreach(db, config_deserialize_from_sdb, &ctx); ht_sp_free(ctx.exclude); diff --git a/librz/core/cfile.c b/librz/core/cfile.c index 66793f1ce01..b7d1c9e5bb7 100644 --- a/librz/core/cfile.c +++ b/librz/core/cfile.c @@ -609,7 +609,7 @@ RZ_API void rz_core_sysenv_begin(RzCore *core) { rz_sys_setenv("RZ_ENDIAN", rz_asm_is_big_endian_set(core->rasm) ? "big" : "little"); rz_sys_setenv("RZ_BSIZE", rz_strf(tmpbuf, "%d", core->blocksize)); - // dump current config file so other r2 tools can use the same options + // dump current config file so other rizin tools can use the same options char *config_sdb_path = NULL; int config_sdb_fd = rz_file_mkstemp(NULL, &config_sdb_path); if (config_sdb_fd >= 0) { @@ -617,7 +617,7 @@ RZ_API void rz_core_sysenv_begin(RzCore *core) { } Sdb *config_sdb = sdb_new(NULL, config_sdb_path, 0); - rz_serialize_config_save(config_sdb, core->config); + rz_serialize_config_save(config_sdb, core->config, NULL); sdb_sync(config_sdb); sdb_free(config_sdb); rz_sys_setenv("RZ_CONFIG", config_sdb_path); diff --git a/librz/core/cmd/cmd_system.c b/librz/core/cmd/cmd_system.c index 4386c6778a8..5ff9ea57a48 100644 --- a/librz/core/cmd/cmd_system.c +++ b/librz/core/cmd/cmd_system.c @@ -28,7 +28,7 @@ static char *config_path(RzCore *core) { free(path); return NULL; } - rz_serialize_config_save(sdb, core->config); + rz_serialize_config_save(sdb, core->config, NULL); sdb_sync(sdb); sdb_free(sdb); diff --git a/librz/core/serialize_core.c b/librz/core/serialize_core.c index d5ab35c323b..af2c51dc0c7 100644 --- a/librz/core/serialize_core.c +++ b/librz/core/serialize_core.c @@ -22,28 +22,20 @@ static void file_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, RZ_NULLABLE c static bool file_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, RZ_NULLABLE const char *prj_file, RZ_NULLABLE RzSerializeResultInfo *res); -RZ_API void rz_serialize_core_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, RZ_NULLABLE const char *prj_file) { - file_save(sdb_ns(db, "file", true), core, prj_file); - rz_serialize_config_save(sdb_ns(db, "config", true), core->config); - rz_serialize_flag_save(sdb_ns(db, "flags", true), core->flags); - rz_serialize_mark_save(sdb_ns(db, "marks", true), core->marks); - rz_serialize_analysis_save(sdb_ns(db, "analysis", true), core->analysis); - rz_serialize_debug_save(sdb_ns(db, "debug", true), core->dbg); - rz_serialize_core_seek_save(sdb_ns(db, "seek", true), core); - - char buf[0x20]; - if (snprintf(buf, sizeof(buf), "0x%" PFMT64x, core->offset) < 0) { - return; - } - sdb_set(db, "offset", buf); +/* + * Config Exclusions: + * Most exlusions only affect loading and are still written to the file, in case + * they become interesting to load later, or for informative purposes. + * Some other config vars may be considered experimental and subject to frequent change, + * so they should also be exluded from saving to avoid polluting project files and + * having to introduce migrations. + */ - if (snprintf(buf, sizeof(buf), "0x%" PFMT32x, core->blocksize) < 0) { - return; - } - sdb_set(db, "blocksize", buf); -} +static const char *config_exclude_save[] = { + NULL +}; -static const char *config_exclude[] = { +static const char *config_exclude_load[] = { "dir.home", "dir.libs", "dir.magic", @@ -70,6 +62,27 @@ static const char *config_exclude[] = { NULL }; +RZ_API void rz_serialize_core_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, RZ_NULLABLE const char *prj_file) { + file_save(sdb_ns(db, "file", true), core, prj_file); + rz_serialize_config_save(sdb_ns(db, "config", true), core->config, config_exclude_save); + rz_serialize_flag_save(sdb_ns(db, "flags", true), core->flags); + rz_serialize_mark_save(sdb_ns(db, "marks", true), core->marks); + rz_serialize_analysis_save(sdb_ns(db, "analysis", true), core->analysis); + rz_serialize_debug_save(sdb_ns(db, "debug", true), core->dbg); + rz_serialize_core_seek_save(sdb_ns(db, "seek", true), core); + + char buf[0x20]; + if (snprintf(buf, sizeof(buf), "0x%" PFMT64x, core->offset) < 0) { + return; + } + sdb_set(db, "offset", buf); + + if (snprintf(buf, sizeof(buf), "0x%" PFMT32x, core->blocksize) < 0) { + return; + } + sdb_set(db, "blocksize", buf); +} + RZ_API bool rz_serialize_core_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, bool load_bin_io, RZ_NULLABLE const char *prj_file, RZ_NULLABLE RzSerializeResultInfo *res) { Sdb *subdb; @@ -79,7 +92,7 @@ RZ_API bool rz_serialize_core_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, if (load_bin_io) { SUB("file", file_load(subdb, core, prj_file, res)); } - SUB("config", rz_serialize_config_load(subdb, core->config, config_exclude)); + SUB("config", rz_serialize_config_load(subdb, core->config, config_exclude_load)); SUB("flags", rz_serialize_flag_load(subdb, core->flags, res)); SUB("marks", rz_serialize_mark_load(subdb, core->marks, res)); SUB("analysis", rz_serialize_analysis_load(subdb, core->analysis, res)); diff --git a/librz/include/rz_config.h b/librz/include/rz_config.h index ef0ae99237c..7b3fa437f6c 100644 --- a/librz/include/rz_config.h +++ b/librz/include/rz_config.h @@ -233,7 +233,7 @@ static inline bool rz_config_node_is_str(const RzConfigNode *node) { /* serialize */ -RZ_API void rz_serialize_config_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzConfig *config); +RZ_API void rz_serialize_config_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzConfig *config, RZ_NULLABLE const char **exclude); RZ_API bool rz_serialize_config_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzConfig *config, RZ_NULLABLE const char **exclude); #endif diff --git a/test/unit/test_serialize_config.c b/test/unit/test_serialize_config.c index 122a08d316b..6832a2fea19 100644 --- a/test/unit/test_serialize_config.c +++ b/test/unit/test_serialize_config.c @@ -5,10 +5,12 @@ #include "minunit.h" #include "test_sdb.h" -Sdb *ref_db() { +Sdb *ref_db(bool excluded) { Sdb *db = sdb_new0(); - sdb_set(db, "somestring", "somevalue"); - sdb_set(db, "someint", "42"); + if (!excluded) { + sdb_set(db, "somestring", "somevalue"); + sdb_set(db, "someint", "42"); + } sdb_set(db, "somebiggerint", "0x00001337"); return db; } @@ -20,10 +22,32 @@ bool test_config_save() { rz_config_set_i(config, "somebiggerint", 0x1337); Sdb *db = sdb_new0(); - rz_serialize_config_save(db, config); + rz_serialize_config_save(db, config, NULL); rz_config_free(config); - Sdb *expected = ref_db(); + Sdb *expected = ref_db(false); + assert_sdb_eq(db, expected, "config save"); + sdb_free(db); + sdb_free(expected); + mu_end; +} + +bool test_config_save_exclude() { + static const char *exclude[] = { + "somestring", + "someint", + NULL + }; + RzConfig *config = rz_config_new(NULL); + rz_config_set(config, "somestring", "somevalue"); + rz_config_set_i(config, "someint", 42); + rz_config_set_i(config, "somebiggerint", 0x1337); + + Sdb *db = sdb_new0(); + rz_serialize_config_save(db, config, exclude); + rz_config_free(config); + + Sdb *expected = ref_db(true); assert_sdb_eq(db, expected, "config save"); sdb_free(db); sdb_free(expected); @@ -36,7 +60,7 @@ bool test_config_load() { rz_config_set_i(config, "someint", 0); rz_config_set_i(config, "somebiggerint", 0); - Sdb *db = ref_db(); + Sdb *db = ref_db(false); sdb_set(db, "sneaky", "not part of config"); bool loaded = rz_serialize_config_load(db, config, NULL); sdb_free(db); @@ -61,7 +85,7 @@ bool test_config_load_exclude() { rz_config_set_i(config, "someint", 123); rz_config_set_i(config, "somebiggerint", 0); - Sdb *db = ref_db(); + Sdb *db = ref_db(false); // must load non-excluded here! bool loaded = rz_serialize_config_load(db, config, exclude); sdb_free(db); mu_assert("load success", loaded); @@ -76,9 +100,10 @@ bool test_config_load_exclude() { int all_tests() { mu_run_test(test_config_save); + mu_run_test(test_config_save_exclude); mu_run_test(test_config_load); mu_run_test(test_config_load_exclude); return tests_passed != tests_run; } -mu_main(all_tests) \ No newline at end of file +mu_main(all_tests) From 5591fa6c372927c36740f9ad861ede05ca2c550d Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 5 Nov 2025 06:42:19 -0500 Subject: [PATCH 002/334] Add basic structures for new Study module --- .github/labeler.yml | 5 +++ librz/include/rz_study.h | 20 +++++++++ librz/meson.build | 1 + librz/study/README.md | 14 ++++++ librz/study/interpreter.c | 12 ++++++ librz/study/meson.build | 62 +++++++++++++++++++++++++++ librz/study/p/study_abstr_int_proto.c | 0 7 files changed, 114 insertions(+) create mode 100644 librz/include/rz_study.h create mode 100644 librz/study/README.md create mode 100644 librz/study/interpreter.c create mode 100644 librz/study/meson.build create mode 100644 librz/study/p/study_abstr_int_proto.c diff --git a/.github/labeler.yml b/.github/labeler.yml index c314078a40c..4ee6f9abf1b 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -165,6 +165,11 @@ RzSocket: - any-glob-to-any-file: - librz/socket/**/* +RzStudy: +- changed-files: + - any-glob-to-any-file: + - librz/study/**/* + RzSyscall: - changed-files: - any-glob-to-any-file: diff --git a/librz/include/rz_study.h b/librz/include/rz_study.h new file mode 100644 index 00000000000..b69bec8d93a --- /dev/null +++ b/librz/include/rz_study.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2025 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef RZ_STUDY +#define RZ_STUDY + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +RZ_API bool rz_study_abstract_interpretation(RzAnalysis *analysis, RzIO *io); + +#ifdef __cplusplus +} +#endif +#endif // RZ_STUDY diff --git a/librz/meson.build b/librz/meson.build index aea186af952..4f70ef37777 100644 --- a/librz/meson.build +++ b/librz/meson.build @@ -28,6 +28,7 @@ subdir('sign') subdir('egg') subdir('debug') subdir('core') +subdir('study') subdir('main') diff --git a/librz/study/README.md b/librz/study/README.md new file mode 100644 index 00000000000..667984feabc --- /dev/null +++ b/librz/study/README.md @@ -0,0 +1,14 @@ + + + +# RzStudy Module + +Module implementing basic and advanced binary analysis. + +## Directory structure + +```c +. // API implementation +└── p // Plugin implementations + └── abstr_inter_proto // The prototype abstract interpreter for basic analysis. +``` diff --git a/librz/study/interpreter.c b/librz/study/interpreter.c new file mode 100644 index 00000000000..71c36b0edb2 --- /dev/null +++ b/librz/study/interpreter.c @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2025 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +/** + * \file The API implementation for analysis interpreters. + */ + +#include + +RZ_API bool rz_study_abstract_interpretation(RzAnalysis *analysis, RzIO *io) { + return true; +} diff --git a/librz/study/meson.build b/librz/study/meson.build new file mode 100644 index 00000000000..2b7042f2ac4 --- /dev/null +++ b/librz/study/meson.build @@ -0,0 +1,62 @@ +# SPDX-FileCopyrightText: 2025 RizinOrg +# SPDX-License-Identifier: LGPL-3.0-only + +study_plugins_list = [ + 'abstr_int_proto', +] + +study_plugins = { + 'base_name': 'rz_study', + 'base_struct': 'RzStudy', + 'list': study_plugins_list, +} + +rz_study_sources = [ + 'interpreter.c', + 'p/study_abstr_int_proto.c', +] + +rz_study_inc = [platform_inc] + +rz_study = library('rz_study', rz_study_sources, + include_directories: rz_study_inc, + c_args: ['-DRZ_API_STUDY_ONLY=1'], + dependencies: [ + rz_arch_dep, # Legacy analysis module + rz_cons_dep, + rz_hash_dep, + rz_io_dep, + rz_magic_dep, + rz_search_dep, + rz_syscall_dep, + rz_type_dep, + rz_util_dep, + ], + install: true, + implicit_include_directories: false, + install_rpath: rpath_lib, + soversion: rizin_libversion, + version: rizin_version, + name_suffix: lib_name_suffix, + name_prefix: lib_name_prefix, +) + +rz_study_dep = declare_dependency(link_with: rz_study, + include_directories: rz_study_inc) +meson.override_dependency('rz_study', rz_study_dep) + +modules += { 'rz_study': { + 'target': rz_study, + 'dependencies': [ + 'rz_arch', + 'rz_cons', + 'rz_hash', + 'rz_io', + 'rz_magic', + 'rz_search', + 'rz_syscall', + 'rz_type', + 'rz_util' + ], + 'plugins': [study_plugins] +}} diff --git a/librz/study/p/study_abstr_int_proto.c b/librz/study/p/study_abstr_int_proto.c new file mode 100644 index 00000000000..e69de29bb2d From a714f2e34e697b2c4bc232250ee3c8fa9ec57d51 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 5 Nov 2025 09:09:08 -0500 Subject: [PATCH 003/334] Add prototype API for interpreter. --- librz/include/rz_study.h | 70 ++++++++++++++++++++++++++++++++++++++- librz/study/interpreter.c | 5 ++- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/librz/include/rz_study.h b/librz/include/rz_study.h index b69bec8d93a..7c5eca4b938 100644 --- a/librz/include/rz_study.h +++ b/librz/include/rz_study.h @@ -10,9 +10,77 @@ extern "C" { #include #include +#include #include +#include -RZ_API bool rz_study_abstract_interpretation(RzAnalysis *analysis, RzIO *io); +/** + * \brief The abstractions this module supports. + */ +typedef enum { + /** + * \brief Value abstraction into constant and bottom values. + */ + RZ_STUDY_ABSTRACTION_CONST, +} RzStudyAbstraction; + +/** + * \brief An abitrary abstract value. + */ +typedef struct { + RzStudyAbstraction kind; + void *abstr_data; +} RzStudyAbstrVal; + +typedef enum { + RZ_STUDY_YIELD_KIND_ABSTR_VAL, ///< Yield object is an abstract value. + RZ_STUDY_YIELD_KIND_CFG_EDGE, ///< Yield object a discovered CFG edge. +} RzStudyYieldKind; + +typedef void *RzStudyYield; ///< A yield of an interpreter. Type is implied by the queue. + +// TODO: Could be private +/** + * \brief A filter for abstract values to decide if they should be pushed into + * the yield queue or not. + */ +typedef bool (*RzStudyYieldFilter)(RzStudyYieldKind kind, const RzStudyYield *yield); + +/** + * \brief A queue to push interpretation yields into. + */ +typedef struct { + RzStudyAbstraction kind; + RzStudyYieldFilter *filter; + RzThreadQueue /**/ *yield_queue; +} RzStudyYieldQueue; + +/** + * \brief The IL effect scopes pushed over the IL queue. + * The elements are always RzILOpEffects, but they can represent + * more or less instructions. + */ +typedef enum { + RZ_STUDY_IL_QUEUE_ELEM_SCOPE_IPKT, ///< RzILOpEffect scope is one atomically execute instruction packet. + RZ_STUDY_IL_QUEUE_ELEM_SCOPE_BB, ///< RzILOpEffect scope is one basic block (n instruction packets with a branch or termination at the end). +} RzStudyILQueueElemScope; + +typedef struct { + RzStudyILQueueElemScope effect_scope; + RzILOpEffect *il_effect; +} RzStudyILQueueElement; + +typedef struct { + RzThreadQueue /**/ *yield_queue; +} RzStudyILQueue; + +/** + * \brief Performs abstract interpretation. + */ +RZ_API bool rz_study_abstract_interpretation( + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, + RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues); #ifdef __cplusplus } diff --git a/librz/study/interpreter.c b/librz/study/interpreter.c index 71c36b0edb2..a6a5f634910 100644 --- a/librz/study/interpreter.c +++ b/librz/study/interpreter.c @@ -7,6 +7,9 @@ #include -RZ_API bool rz_study_abstract_interpretation(RzAnalysis *analysis, RzIO *io) { +RZ_API bool rz_study_abstract_interpretation( + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, + RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues) { return true; } From 0726ec23e339c1601adb69eb77e73f0bbac6c6d7 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 5 Nov 2025 09:47:59 -0500 Subject: [PATCH 004/334] Add an rudimentary interpreter IPI. --- librz/include/rz_study.h | 21 +++++++++++++++++- librz/study/README.md | 2 +- librz/study/meson.build | 2 +- librz/study/p/study_abstr_int_proto.c | 0 librz/study/p/study_interpreter_prototype.c | 24 +++++++++++++++++++++ 5 files changed, 46 insertions(+), 3 deletions(-) delete mode 100644 librz/study/p/study_abstr_int_proto.c create mode 100644 librz/study/p/study_interpreter_prototype.c diff --git a/librz/include/rz_study.h b/librz/include/rz_study.h index 7c5eca4b938..e799d3abf22 100644 --- a/librz/include/rz_study.h +++ b/librz/include/rz_study.h @@ -21,7 +21,7 @@ typedef enum { /** * \brief Value abstraction into constant and bottom values. */ - RZ_STUDY_ABSTRACTION_CONST, + RZ_STUDY_ABSTRACTION_CONST = 1 << 0, } RzStudyAbstraction; /** @@ -74,6 +74,25 @@ typedef struct { RzThreadQueue /**/ *yield_queue; } RzStudyILQueue; +typedef struct { + const char *name; + const char *author; + const char *version; + const char *desc; + const char *license; + RzStudyAbstraction supported_abstractions; + bool (*init)(void **plugin_data); + bool (*fini)(void *plugin_data); + // TODO: Configuration or initial setup of interpreter not yet implemented. + bool (*interpret)( + // TODO: The entry point could be in the reveive queue already. + // Saves one more parameter, keeps the IPI on point. + ut64 entry_point, + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, + RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues); +} RzStudyInterpreterPlugin; + /** * \brief Performs abstract interpretation. */ diff --git a/librz/study/README.md b/librz/study/README.md index 667984feabc..9bcca2ffba0 100644 --- a/librz/study/README.md +++ b/librz/study/README.md @@ -10,5 +10,5 @@ Module implementing basic and advanced binary analysis. ```c . // API implementation └── p // Plugin implementations - └── abstr_inter_proto // The prototype abstract interpreter for basic analysis. + └── interpreter_prototype // The prototype abstract interpreter for basic analysis. ``` diff --git a/librz/study/meson.build b/librz/study/meson.build index 2b7042f2ac4..a4bc46515a9 100644 --- a/librz/study/meson.build +++ b/librz/study/meson.build @@ -13,7 +13,7 @@ study_plugins = { rz_study_sources = [ 'interpreter.c', - 'p/study_abstr_int_proto.c', + 'p/study_interpreter_prototype.c', ] rz_study_inc = [platform_inc] diff --git a/librz/study/p/study_abstr_int_proto.c b/librz/study/p/study_abstr_int_proto.c deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/librz/study/p/study_interpreter_prototype.c b/librz/study/p/study_interpreter_prototype.c new file mode 100644 index 00000000000..f27e0a88107 --- /dev/null +++ b/librz/study/p/study_interpreter_prototype.c @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2025 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#include + +bool interpret( + ut64 entry_point, + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, + RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues) { + return true; +} + +static RzStudyInterpreterPlugin interpreter_prototype = { + .name = "abstr_int_prototype", + .author = "Rot127", + .version = "0.1p", + .desc = "A prototype interpreter for constant/bottom abstractions.", + .license = "LGPL-3.0-only", + .supported_abstractions = RZ_STUDY_ABSTRACTION_CONST, + .init = NULL, + .fini = NULL, + .interpret = interpret +}; From 1900b74df547efe3b0aad3fbd9f9df1fc5688eb9 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 5 Nov 2025 09:56:30 -0500 Subject: [PATCH 005/334] Rename RzStudy -> RzInquiry --- .github/labeler.yml | 4 +- librz/include/{rz_study.h => rz_inquiry.h} | 60 +++++++++---------- librz/{study => inquiry}/README.md | 2 +- librz/{study => inquiry}/interpreter.c | 8 +-- librz/{study => inquiry}/meson.build | 33 +++++----- .../p/inquiry_interpreter_prototype.c} | 10 ++-- librz/meson.build | 2 +- 7 files changed, 59 insertions(+), 60 deletions(-) rename librz/include/{rz_study.h => rz_inquiry.h} (50%) rename librz/{study => inquiry}/README.md (95%) rename librz/{study => inquiry}/interpreter.c (51%) rename librz/{study => inquiry}/meson.build (55%) rename librz/{study/p/study_interpreter_prototype.c => inquiry/p/inquiry_interpreter_prototype.c} (59%) diff --git a/.github/labeler.yml b/.github/labeler.yml index 4ee6f9abf1b..f0fec3f040e 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -165,10 +165,10 @@ RzSocket: - any-glob-to-any-file: - librz/socket/**/* -RzStudy: +RzInquiry: - changed-files: - any-glob-to-any-file: - - librz/study/**/* + - librz/inquiry/**/* RzSyscall: - changed-files: diff --git a/librz/include/rz_study.h b/librz/include/rz_inquiry.h similarity index 50% rename from librz/include/rz_study.h rename to librz/include/rz_inquiry.h index e799d3abf22..1df432550d2 100644 --- a/librz/include/rz_study.h +++ b/librz/include/rz_inquiry.h @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: 2025 RizinOrg // SPDX-License-Identifier: LGPL-3.0-only -#ifndef RZ_STUDY -#define RZ_STUDY +#ifndef RZ_INQUIRY +#define RZ_INQUIRY #ifdef __cplusplus extern "C" { @@ -21,39 +21,39 @@ typedef enum { /** * \brief Value abstraction into constant and bottom values. */ - RZ_STUDY_ABSTRACTION_CONST = 1 << 0, -} RzStudyAbstraction; + RZ_INQUIRY_ABSTRACTION_CONST = 1 << 0, +} RzInquiryAbstraction; /** * \brief An abitrary abstract value. */ typedef struct { - RzStudyAbstraction kind; + RzInquiryAbstraction kind; void *abstr_data; -} RzStudyAbstrVal; +} RzInquiryAbstrVal; typedef enum { - RZ_STUDY_YIELD_KIND_ABSTR_VAL, ///< Yield object is an abstract value. - RZ_STUDY_YIELD_KIND_CFG_EDGE, ///< Yield object a discovered CFG edge. -} RzStudyYieldKind; + RZ_INQUIRY_YIELD_KIND_ABSTR_VAL, ///< Yield object is an abstract value. + RZ_INQUIRY_YIELD_KIND_CFG_EDGE, ///< Yield object a discovered CFG edge. +} RzInquiryYieldKind; -typedef void *RzStudyYield; ///< A yield of an interpreter. Type is implied by the queue. +typedef void *RzInquiryYield; ///< A yield of an interpreter. Type is implied by the queue. // TODO: Could be private /** * \brief A filter for abstract values to decide if they should be pushed into * the yield queue or not. */ -typedef bool (*RzStudyYieldFilter)(RzStudyYieldKind kind, const RzStudyYield *yield); +typedef bool (*RzInquiryYieldFilter)(RzInquiryYieldKind kind, const RzInquiryYield *yield); /** * \brief A queue to push interpretation yields into. */ typedef struct { - RzStudyAbstraction kind; - RzStudyYieldFilter *filter; - RzThreadQueue /**/ *yield_queue; -} RzStudyYieldQueue; + RzInquiryAbstraction kind; + RzInquiryYieldFilter *filter; + RzThreadQueue /**/ *yield_queue; +} RzInquiryYieldQueue; /** * \brief The IL effect scopes pushed over the IL queue. @@ -61,18 +61,18 @@ typedef struct { * more or less instructions. */ typedef enum { - RZ_STUDY_IL_QUEUE_ELEM_SCOPE_IPKT, ///< RzILOpEffect scope is one atomically execute instruction packet. - RZ_STUDY_IL_QUEUE_ELEM_SCOPE_BB, ///< RzILOpEffect scope is one basic block (n instruction packets with a branch or termination at the end). -} RzStudyILQueueElemScope; + RZ_INQUIRY_IL_QUEUE_ELEM_SCOPE_IPKT, ///< RzILOpEffect scope is one atomically execute instruction packet. + RZ_INQUIRY_IL_QUEUE_ELEM_SCOPE_BB, ///< RzILOpEffect scope is one basic block (n instruction packets with a branch or termination at the end). +} RzInquiryILQueueElemScope; typedef struct { - RzStudyILQueueElemScope effect_scope; + RzInquiryILQueueElemScope effect_scope; RzILOpEffect *il_effect; -} RzStudyILQueueElement; +} RzInquiryILQueueElement; typedef struct { - RzThreadQueue /**/ *yield_queue; -} RzStudyILQueue; + RzThreadQueue /**/ *yield_queue; +} RzInquiryILQueue; typedef struct { const char *name; @@ -80,7 +80,7 @@ typedef struct { const char *version; const char *desc; const char *license; - RzStudyAbstraction supported_abstractions; + RzInquiryAbstraction supported_abstractions; bool (*init)(void **plugin_data); bool (*fini)(void *plugin_data); // TODO: Configuration or initial setup of interpreter not yet implemented. @@ -89,19 +89,19 @@ typedef struct { // Saves one more parameter, keeps the IPI on point. ut64 entry_point, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, - RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues); -} RzStudyInterpreterPlugin; + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, + RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues); +} RzInquiryInterpreterPlugin; /** * \brief Performs abstract interpretation. */ -RZ_API bool rz_study_abstract_interpretation( +RZ_API bool rz_inquiry_abstract_interpretation( RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, - RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues); + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, + RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues); #ifdef __cplusplus } #endif -#endif // RZ_STUDY +#endif // RZ_INQUIRY diff --git a/librz/study/README.md b/librz/inquiry/README.md similarity index 95% rename from librz/study/README.md rename to librz/inquiry/README.md index 9bcca2ffba0..61f041c5ed4 100644 --- a/librz/study/README.md +++ b/librz/inquiry/README.md @@ -1,7 +1,7 @@ -# RzStudy Module +# RzInquiry Module Module implementing basic and advanced binary analysis. diff --git a/librz/study/interpreter.c b/librz/inquiry/interpreter.c similarity index 51% rename from librz/study/interpreter.c rename to librz/inquiry/interpreter.c index a6a5f634910..33ccabde045 100644 --- a/librz/study/interpreter.c +++ b/librz/inquiry/interpreter.c @@ -5,11 +5,11 @@ * \file The API implementation for analysis interpreters. */ -#include +#include -RZ_API bool rz_study_abstract_interpretation( +RZ_API bool rz_inquiry_abstract_interpretation( RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, - RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues) { + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, + RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues) { return true; } diff --git a/librz/study/meson.build b/librz/inquiry/meson.build similarity index 55% rename from librz/study/meson.build rename to librz/inquiry/meson.build index a4bc46515a9..23e5a17cee5 100644 --- a/librz/study/meson.build +++ b/librz/inquiry/meson.build @@ -1,26 +1,25 @@ # SPDX-FileCopyrightText: 2025 RizinOrg # SPDX-License-Identifier: LGPL-3.0-only -study_plugins_list = [ +inquiry_plugins_list = [ 'abstr_int_proto', ] -study_plugins = { - 'base_name': 'rz_study', - 'base_struct': 'RzStudy', - 'list': study_plugins_list, +inquiry_plugins = { + 'base_name': 'rz_inquiry', + 'base_struct': 'Rzinquiry', + 'list': inquiry_plugins_list, } -rz_study_sources = [ +rz_inquiry_sources = [ 'interpreter.c', - 'p/study_interpreter_prototype.c', + 'p/inquiry_interpreter_prototype.c', ] -rz_study_inc = [platform_inc] +rz_inquiry_inc = [platform_inc] -rz_study = library('rz_study', rz_study_sources, - include_directories: rz_study_inc, - c_args: ['-DRZ_API_STUDY_ONLY=1'], +rz_inquiry = library('rz_inquiry', rz_inquiry_sources, + include_directories: rz_inquiry_inc, dependencies: [ rz_arch_dep, # Legacy analysis module rz_cons_dep, @@ -41,12 +40,12 @@ rz_study = library('rz_study', rz_study_sources, name_prefix: lib_name_prefix, ) -rz_study_dep = declare_dependency(link_with: rz_study, - include_directories: rz_study_inc) -meson.override_dependency('rz_study', rz_study_dep) +rz_inquiry_dep = declare_dependency(link_with: rz_inquiry, + include_directories: rz_inquiry_inc) +meson.override_dependency('rz_inquiry', rz_inquiry_dep) -modules += { 'rz_study': { - 'target': rz_study, +modules += { 'rz_inquiry': { + 'target': rz_inquiry, 'dependencies': [ 'rz_arch', 'rz_cons', @@ -58,5 +57,5 @@ modules += { 'rz_study': { 'rz_type', 'rz_util' ], - 'plugins': [study_plugins] + 'plugins': [inquiry_plugins] }} diff --git a/librz/study/p/study_interpreter_prototype.c b/librz/inquiry/p/inquiry_interpreter_prototype.c similarity index 59% rename from librz/study/p/study_interpreter_prototype.c rename to librz/inquiry/p/inquiry_interpreter_prototype.c index f27e0a88107..9002f6ad3a6 100644 --- a/librz/study/p/study_interpreter_prototype.c +++ b/librz/inquiry/p/inquiry_interpreter_prototype.c @@ -1,23 +1,23 @@ // SPDX-FileCopyrightText: 2025 RizinOrg // SPDX-License-Identifier: LGPL-3.0-only -#include +#include bool interpret( ut64 entry_point, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, - RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues) { + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, + RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues) { return true; } -static RzStudyInterpreterPlugin interpreter_prototype = { +static RzInquiryInterpreterPlugin interpreter_prototype = { .name = "abstr_int_prototype", .author = "Rot127", .version = "0.1p", .desc = "A prototype interpreter for constant/bottom abstractions.", .license = "LGPL-3.0-only", - .supported_abstractions = RZ_STUDY_ABSTRACTION_CONST, + .supported_abstractions = RZ_INQUIRY_ABSTRACTION_CONST, .init = NULL, .fini = NULL, .interpret = interpret diff --git a/librz/meson.build b/librz/meson.build index 4f70ef37777..09f6412ecf2 100644 --- a/librz/meson.build +++ b/librz/meson.build @@ -28,7 +28,7 @@ subdir('sign') subdir('egg') subdir('debug') subdir('core') -subdir('study') +subdir('inquiry') subdir('main') From 1d50ae885e4bc4c4215ea537ee07eab81c12544d Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 6 Nov 2025 11:58:22 -0500 Subject: [PATCH 006/334] Add plugin and lib structures for RzInquiry. --- librz/core/libs.c | 2 + librz/core/meson.build | 2 + librz/include/rz_core.h | 2 + librz/include/rz_inquiry.h | 18 ++++++++ librz/include/rz_lib.h | 1 + librz/inquiry/inquiry.c | 45 +++++++++++++++++++ librz/inquiry/meson.build | 5 ++- .../inquiry/p/inquiry_interpreter_prototype.c | 11 +++++ librz/meson.build | 2 +- 9 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 librz/inquiry/inquiry.c diff --git a/librz/core/libs.c b/librz/core/libs.c index 9ee9e7010e4..fc26a64510a 100644 --- a/librz/core/libs.c +++ b/librz/core/libs.c @@ -37,6 +37,7 @@ CB(bin, bin) CB(demangler, bin->demangler) CB(egg, egg) CB(hash, hash) +CB(inquiry, inquiry) static bool lib_arch_cb(RzLibPlugin *pl, void *user, void *data) { RzArchPlugin *hand = (RzArchPlugin *)data; @@ -122,6 +123,7 @@ RZ_API void rz_core_loadlibs_init(RzCore *core) { DF(EGG, "egg plugins", egg); DF(HASH, "hash plugins", hash); DF(ARCH, "(dis)assembler & analysis plugins", arch); + DF(INTERPRETER, "inquiry interpreter plugins", inquiry); core->times->loadlibs_init_time = rz_time_now_mono() - prev; } diff --git a/librz/core/meson.build b/librz/core/meson.build index 9db4c9736d4..dc5e9d1fbf9 100644 --- a/librz/core/meson.build +++ b/librz/core/meson.build @@ -166,6 +166,7 @@ rz_core_deps = [ rz_config_dep, rz_bin_dep, rz_mark_dep, + rz_inquiry_dep, platform_deps, dependency('rzgdb'), rzheap_dep, @@ -204,6 +205,7 @@ modules += { 'rz_core': { 'rz_socket', 'rz_type', 'rz_io', + 'rz_inquiry', 'rz_lang', 'rz_hash', 'rz_flag', diff --git a/librz/include/rz_core.h b/librz/include/rz_core.h index ac4ba471675..089117dfffc 100644 --- a/librz/include/rz_core.h +++ b/librz/include/rz_core.h @@ -4,6 +4,7 @@ #ifndef RZ_CORE_H #define RZ_CORE_H +#include #include #include #include @@ -294,6 +295,7 @@ struct rz_core_t { ut8 ptr_alignment_III; // NOTE: Do not change the order of fields above! // They are used in pointer passing hacks in rz_types.h. + RzInquiry *inquiry; RzIO *io; HtSP /**/ *plugins; ///< List of registered core plugins HtSP /**/ *plugin_contexts; ///< Per-core plugin state diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 1df432550d2..f0fb336177b 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -93,6 +93,24 @@ typedef struct { RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues); } RzInquiryInterpreterPlugin; +typedef struct rz_inquiry_plugin_t { + RzInquiryInterpreterPlugin *p_interpreter; + // RzInquiryAlgorithm *p_algorithm; +} RzInquiryPlugin; + +typedef struct { + /** + * \brief RzInquiry interpreter plugins. Indexed by name. + */ + HtSP /**/ *plugins; +} RzInquiry; + +RZ_API bool rz_inquiry_plugin_add(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NONNULL RzInquiryPlugin *plugin); +RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NONNULL RzInquiryPlugin *plugin); + +RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void); +RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *a); + /** * \brief Performs abstract interpretation. */ diff --git a/librz/include/rz_lib.h b/librz/include/rz_lib.h index f652ba80a4a..e0a00bfcea0 100644 --- a/librz/include/rz_lib.h +++ b/librz/include/rz_lib.h @@ -51,6 +51,7 @@ typedef enum { RZ_LIB_TYPE_EGG, ///< rz_egg plugin RZ_LIB_TYPE_DEMANGLER, ///< demanglers RZ_LIB_TYPE_ARCH, ///< demanglers + RZ_LIB_TYPE_INTERPRETER, ///< Analysis interpreters RZ_LIB_TYPE_UNKNOWN } RzLibType; diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c new file mode 100644 index 00000000000..6df9e9c1754 --- /dev/null +++ b/librz/inquiry/inquiry.c @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2025 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include + +#include "rz_inquiry_plugins.h" + +RZ_LIB_VERSION(rz_inquiry); + +static RzInquiryPlugin *inquiry_static_plugins[] = { RZ_INQUIRY_STATIC_PLUGINS }; + +RZ_API const size_t rz_inquiry_get_n_plugins() { + return RZ_ARRAY_SIZE(inquiry_static_plugins); +} + +RZ_API RZ_BORROW RzInquiryPlugin *rz_inquiry_get_plugin(size_t index) { + if (index >= RZ_ARRAY_SIZE(inquiry_static_plugins)) { + return NULL; + } + return inquiry_static_plugins[index]; +} + +RZ_API bool rz_inquiry_plugin_add(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OWN RZ_NONNULL RzInquiryPlugin *plugin) { + rz_return_val_if_fail(inquiry && plugin, false); + if (plugin->p_interpreter) { + if (!ht_sp_insert(inquiry->plugins, plugin->p_interpreter->name, plugin)) { + RZ_LOG_WARN("Plugin '%s' was already added.\n", plugin->p_interpreter->name); + } + return true; + } + + rz_warn_if_reached(); + return false; +} + +RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OWN RZ_NONNULL RzInquiryPlugin *plugin) { + rz_return_val_if_fail(inquiry && plugin, false); + + if (plugin->p_interpreter) { + return ht_sp_delete(inquiry->plugins, plugin->p_interpreter->name); + } + rz_warn_if_reached(); + return false; +} diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index 23e5a17cee5..97009074248 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -2,17 +2,18 @@ # SPDX-License-Identifier: LGPL-3.0-only inquiry_plugins_list = [ - 'abstr_int_proto', + 'interpreter_prototype', ] inquiry_plugins = { 'base_name': 'rz_inquiry', - 'base_struct': 'Rzinquiry', + 'base_struct': 'RzInquiryPlugin', 'list': inquiry_plugins_list, } rz_inquiry_sources = [ 'interpreter.c', + 'inquiry.c', 'p/inquiry_interpreter_prototype.c', ] diff --git a/librz/inquiry/p/inquiry_interpreter_prototype.c b/librz/inquiry/p/inquiry_interpreter_prototype.c index 9002f6ad3a6..ef6f89a1bb3 100644 --- a/librz/inquiry/p/inquiry_interpreter_prototype.c +++ b/librz/inquiry/p/inquiry_interpreter_prototype.c @@ -22,3 +22,14 @@ static RzInquiryInterpreterPlugin interpreter_prototype = { .fini = NULL, .interpret = interpret }; + +RZ_API RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype = { + .p_interpreter = &interpreter_prototype, +}; + +#ifndef RZ_PLUGIN_INCORE +RZ_API RzLibStruct rizin_plugin = { + .type = RZ_LIB_TYPE_INTERPRETER, + .data = &interpreter_prototype +}; +#endif diff --git a/librz/meson.build b/librz/meson.build index 09f6412ecf2..7eaf8923d4a 100644 --- a/librz/meson.build +++ b/librz/meson.build @@ -27,8 +27,8 @@ subdir('arch') subdir('sign') subdir('egg') subdir('debug') -subdir('core') subdir('inquiry') +subdir('core') subdir('main') From cc77eba9911606e6201bd76446be41712d2aded8 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 6 Nov 2025 12:10:39 -0500 Subject: [PATCH 007/334] Move the interpreter defintions into their own header for cleaniness. --- librz/include/meson.build | 6 ++ librz/include/rz_inquiry.h | 74 +-------------------- librz/include/rz_inquiry/rz_interpreter.h | 81 +++++++++++++++++++++++ 3 files changed, 88 insertions(+), 73 deletions(-) create mode 100644 librz/include/rz_inquiry/rz_interpreter.h diff --git a/librz/include/meson.build b/librz/include/meson.build index e6ca0ef5f77..81623839184 100644 --- a/librz/include/meson.build +++ b/librz/include/meson.build @@ -29,6 +29,7 @@ include_files = [ 'rz_heap_glibc.h', 'rz_heap_jemalloc.h', 'rz_il.h', + 'rz_inquiry.h', 'rz_io.h', 'rz_lang.h', 'rz_lib.h', @@ -142,6 +143,11 @@ rz_util_files = [ ] install_headers(rz_util_files, install_dir: join_paths(rizin_incdir, 'rz_util')) +rz_interpreter_files = [ + 'rz_inquiry/rz_interpreter.h', +] +install_headers(rz_interpreter_files, install_dir: join_paths(rizin_incdir, 'rz_interpreter')) + rz_il_definitions_files = [ 'rz_il/definitions/bool.h', 'rz_il/definitions/definitions.h', diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index f0fb336177b..6b5b7751000 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -8,71 +8,7 @@ extern "C" { #endif -#include -#include -#include -#include -#include - -/** - * \brief The abstractions this module supports. - */ -typedef enum { - /** - * \brief Value abstraction into constant and bottom values. - */ - RZ_INQUIRY_ABSTRACTION_CONST = 1 << 0, -} RzInquiryAbstraction; - -/** - * \brief An abitrary abstract value. - */ -typedef struct { - RzInquiryAbstraction kind; - void *abstr_data; -} RzInquiryAbstrVal; - -typedef enum { - RZ_INQUIRY_YIELD_KIND_ABSTR_VAL, ///< Yield object is an abstract value. - RZ_INQUIRY_YIELD_KIND_CFG_EDGE, ///< Yield object a discovered CFG edge. -} RzInquiryYieldKind; - -typedef void *RzInquiryYield; ///< A yield of an interpreter. Type is implied by the queue. - -// TODO: Could be private -/** - * \brief A filter for abstract values to decide if they should be pushed into - * the yield queue or not. - */ -typedef bool (*RzInquiryYieldFilter)(RzInquiryYieldKind kind, const RzInquiryYield *yield); - -/** - * \brief A queue to push interpretation yields into. - */ -typedef struct { - RzInquiryAbstraction kind; - RzInquiryYieldFilter *filter; - RzThreadQueue /**/ *yield_queue; -} RzInquiryYieldQueue; - -/** - * \brief The IL effect scopes pushed over the IL queue. - * The elements are always RzILOpEffects, but they can represent - * more or less instructions. - */ -typedef enum { - RZ_INQUIRY_IL_QUEUE_ELEM_SCOPE_IPKT, ///< RzILOpEffect scope is one atomically execute instruction packet. - RZ_INQUIRY_IL_QUEUE_ELEM_SCOPE_BB, ///< RzILOpEffect scope is one basic block (n instruction packets with a branch or termination at the end). -} RzInquiryILQueueElemScope; - -typedef struct { - RzInquiryILQueueElemScope effect_scope; - RzILOpEffect *il_effect; -} RzInquiryILQueueElement; - -typedef struct { - RzThreadQueue /**/ *yield_queue; -} RzInquiryILQueue; +#include typedef struct { const char *name; @@ -111,14 +47,6 @@ RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NO RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void); RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *a); -/** - * \brief Performs abstract interpretation. - */ -RZ_API bool rz_inquiry_abstract_interpretation( - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, - RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues); - #ifdef __cplusplus } #endif diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h new file mode 100644 index 00000000000..24b308cabc2 --- /dev/null +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2025 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef RZ_INTERPRETER +#define RZ_INTERPRETER + +#include +#include +#include +#include +#include + +/** + * \brief The abstractions this module supports. + */ +typedef enum { + /** + * \brief Value abstraction into constant and bottom values. + */ + RZ_INQUIRY_ABSTRACTION_CONST = 1 << 0, +} RzInquiryAbstraction; + +/** + * \brief An abitrary abstract value. + */ +typedef struct { + RzInquiryAbstraction kind; + void *abstr_data; +} RzInquiryAbstrVal; + +typedef enum { + RZ_INQUIRY_YIELD_KIND_ABSTR_VAL, ///< Yield object is an abstract value. + RZ_INQUIRY_YIELD_KIND_CFG_EDGE, ///< Yield object a discovered CFG edge. +} RzInquiryYieldKind; + +typedef void *RzInquiryYield; ///< A yield of an interpreter. Type is implied by the queue. + +// TODO: Could be private +/** + * \brief A filter for abstract values to decide if they should be pushed into + * the yield queue or not. + */ +typedef bool (*RzInquiryYieldFilter)(RzInquiryYieldKind kind, const RzInquiryYield *yield); + +/** + * \brief A queue to push interpretation yields into. + */ +typedef struct { + RzInquiryAbstraction kind; + RzInquiryYieldFilter *filter; + RzThreadQueue /**/ *yield_queue; +} RzInquiryYieldQueue; + +/** + * \brief The IL effect scopes pushed over the IL queue. + * The elements are always RzILOpEffects, but they can represent + * more or less instructions. + */ +typedef enum { + RZ_INQUIRY_IL_QUEUE_ELEM_SCOPE_IPKT, ///< RzILOpEffect scope is one atomically execute instruction packet. + RZ_INQUIRY_IL_QUEUE_ELEM_SCOPE_BB, ///< RzILOpEffect scope is one basic block (n instruction packets with a branch or termination at the end). +} RzInquiryILQueueElemScope; + +typedef struct { + RzInquiryILQueueElemScope effect_scope; + RzILOpEffect *il_effect; +} RzInquiryILQueueElement; + +typedef struct { + RzThreadQueue /**/ *yield_queue; +} RzInquiryILQueue; + +/** + * \brief Performs abstract interpretation. + */ +RZ_API bool rz_inquiry_abstract_interpretation( + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, + RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues); + +#endif // RZ_INTERPRETER From 01c1f8ee752956d56cd6e93931b10fcbbb867a2f Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 6 Nov 2025 12:29:15 -0500 Subject: [PATCH 008/334] Move the interpreters into their own logical unit. --- librz/include/rz_inquiry.h | 7 ++- librz/include/rz_inquiry/rz_interpreter.h | 43 +++++++++++-------- librz/inquiry/{ => interpreter}/interpreter.c | 8 +++- librz/inquiry/meson.build | 2 +- .../inquiry/p/inquiry_interpreter_prototype.c | 2 +- 5 files changed, 38 insertions(+), 24 deletions(-) rename librz/inquiry/{ => interpreter}/interpreter.c (59%) diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 6b5b7751000..18f60c300bf 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -1,6 +1,11 @@ // SPDX-FileCopyrightText: 2025 RizinOrg // SPDX-License-Identifier: LGPL-3.0-only +/** + * \file The header file for the RzInquiry module provides the declarations + * for RzInquiry plugins. As well as access to broader analysis functions. + */ + #ifndef RZ_INQUIRY #define RZ_INQUIRY @@ -16,7 +21,7 @@ typedef struct { const char *version; const char *desc; const char *license; - RzInquiryAbstraction supported_abstractions; + RzInterpreterAbstraction supported_abstractions; bool (*init)(void **plugin_data); bool (*fini)(void *plugin_data); // TODO: Configuration or initial setup of interpreter not yet implemented. diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 24b308cabc2..ceb90926752 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -1,6 +1,11 @@ // SPDX-FileCopyrightText: 2025 RizinOrg // SPDX-License-Identifier: LGPL-3.0-only +/** + * \file The header file for the RzInterpreter contains declarations for + * all RzIL based interpreters. + */ + #ifndef RZ_INTERPRETER #define RZ_INTERPRETER @@ -17,39 +22,39 @@ typedef enum { /** * \brief Value abstraction into constant and bottom values. */ - RZ_INQUIRY_ABSTRACTION_CONST = 1 << 0, -} RzInquiryAbstraction; + RZ_INTERPRETER_ABSTRACTION_CONST = 1 << 0, +} RzInterpreterAbstraction; /** * \brief An abitrary abstract value. */ typedef struct { - RzInquiryAbstraction kind; + RzInterpreterAbstraction kind; void *abstr_data; -} RzInquiryAbstrVal; +} RzInterpreterAbstrVal; typedef enum { - RZ_INQUIRY_YIELD_KIND_ABSTR_VAL, ///< Yield object is an abstract value. - RZ_INQUIRY_YIELD_KIND_CFG_EDGE, ///< Yield object a discovered CFG edge. -} RzInquiryYieldKind; + RZ_INTERPRETER_YIELD_KIND_ABSTR_VAL, ///< Yield object is an abstract value. + RZ_INTERPRETER_YIELD_KIND_CFG_EDGE, ///< Yield object a discovered CFG edge. +} RzInterpreterYieldKind; -typedef void *RzInquiryYield; ///< A yield of an interpreter. Type is implied by the queue. +typedef void *RzInterpreterYield; ///< A yield of an interpreter. Type is implied by the queue. // TODO: Could be private /** * \brief A filter for abstract values to decide if they should be pushed into * the yield queue or not. */ -typedef bool (*RzInquiryYieldFilter)(RzInquiryYieldKind kind, const RzInquiryYield *yield); +typedef bool (*RzInterpreterYieldFilter)(RzInterpreterYieldKind kind, const RzInterpreterYield *yield); /** * \brief A queue to push interpretation yields into. */ typedef struct { - RzInquiryAbstraction kind; - RzInquiryYieldFilter *filter; + RzInterpreterAbstraction kind; + RzInterpreterYieldFilter *filter; RzThreadQueue /**/ *yield_queue; -} RzInquiryYieldQueue; +} RzInterpreterYieldQueue; /** * \brief The IL effect scopes pushed over the IL queue. @@ -57,23 +62,23 @@ typedef struct { * more or less instructions. */ typedef enum { - RZ_INQUIRY_IL_QUEUE_ELEM_SCOPE_IPKT, ///< RzILOpEffect scope is one atomically execute instruction packet. - RZ_INQUIRY_IL_QUEUE_ELEM_SCOPE_BB, ///< RzILOpEffect scope is one basic block (n instruction packets with a branch or termination at the end). -} RzInquiryILQueueElemScope; + RZ_INTERPRETER_IL_QUEUE_ELEM_SCOPE_IPKT, ///< RzILOpEffect scope is one atomically execute instruction packet. + RZ_INTERPRETER_IL_QUEUE_ELEM_SCOPE_BB, ///< RzILOpEffect scope is one basic block (n instruction packets with a branch or termination at the end). +} RzInterpreterILQueueElemScope; typedef struct { - RzInquiryILQueueElemScope effect_scope; + RzInterpreterILQueueElemScope effect_scope; RzILOpEffect *il_effect; -} RzInquiryILQueueElement; +} RzInterpreterILQueueElement; typedef struct { RzThreadQueue /**/ *yield_queue; -} RzInquiryILQueue; +} RzInterpreterILQueue; /** * \brief Performs abstract interpretation. */ -RZ_API bool rz_inquiry_abstract_interpretation( +RZ_API bool rz_interpreter_run( RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues); diff --git a/librz/inquiry/interpreter.c b/librz/inquiry/interpreter/interpreter.c similarity index 59% rename from librz/inquiry/interpreter.c rename to librz/inquiry/interpreter/interpreter.c index 33ccabde045..d404db146eb 100644 --- a/librz/inquiry/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -2,12 +2,16 @@ // SPDX-License-Identifier: LGPL-3.0-only /** - * \file The API implementation for analysis interpreters. + * \file The API implementation for all analysis interpreters. */ #include -RZ_API bool rz_inquiry_abstract_interpretation( +/** + * \brief Runs a set of interpreters to inquire the requested yield. + * What interpreters are spawned depend on the queues in \p yield_queues. + */ +RZ_API bool rz_interpreter_run( RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues) { diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index 97009074248..f6f9060f0fa 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -12,8 +12,8 @@ inquiry_plugins = { } rz_inquiry_sources = [ - 'interpreter.c', 'inquiry.c', + 'interpreter/interpreter.c', 'p/inquiry_interpreter_prototype.c', ] diff --git a/librz/inquiry/p/inquiry_interpreter_prototype.c b/librz/inquiry/p/inquiry_interpreter_prototype.c index ef6f89a1bb3..b32903e4b48 100644 --- a/librz/inquiry/p/inquiry_interpreter_prototype.c +++ b/librz/inquiry/p/inquiry_interpreter_prototype.c @@ -17,7 +17,7 @@ static RzInquiryInterpreterPlugin interpreter_prototype = { .version = "0.1p", .desc = "A prototype interpreter for constant/bottom abstractions.", .license = "LGPL-3.0-only", - .supported_abstractions = RZ_INQUIRY_ABSTRACTION_CONST, + .supported_abstractions = RZ_INTERPRETER_ABSTRACTION_CONST, .init = NULL, .fini = NULL, .interpret = interpret From 6891b33721bfea5a95bd223bc402578fe6431dad Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 6 Nov 2025 12:46:16 -0500 Subject: [PATCH 009/334] Add a test command --- librz/core/cmd/cmd_analysis.c | 6 +++++ librz/core/cmd_descs/cmd_analysis.yaml | 10 +++++++- librz/core/cmd_descs/cmd_descs.c | 23 ++++++++++++++++--- librz/core/cmd_descs/cmd_descs.h | 2 ++ librz/inquiry/interpreter/interpreter.c | 1 + .../inquiry/p/inquiry_interpreter_prototype.c | 3 ++- 6 files changed, 40 insertions(+), 5 deletions(-) diff --git a/librz/core/cmd/cmd_analysis.c b/librz/core/cmd/cmd_analysis.c index 1eca5574951..cd6eadf5ec5 100644 --- a/librz/core/cmd/cmd_analysis.c +++ b/librz/core/cmd/cmd_analysis.c @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -5906,6 +5907,11 @@ RZ_IPI RzCmdStatus rz_analyze_everything_experimental_handler(RzCore *core, int return RZ_CMD_STATUS_OK; } +RZ_IPI RzCmdStatus rz_analyze_interpreter_prototype_handler(RzCore *core, int argc, const char **argv) { + rz_interpreter_run(NULL, NULL, NULL); + return RZ_CMD_STATUS_OK; +} + RZ_IPI RzCmdStatus rz_analyze_all_function_calls_handler(RzCore *core, int argc, const char **argv) { rz_core_analysis_calls(core, false); return RZ_CMD_STATUS_OK; diff --git a/librz/core/cmd_descs/cmd_analysis.yaml b/librz/core/cmd_descs/cmd_analysis.yaml index 22a7ea38f58..066acb46481 100644 --- a/librz/core/cmd_descs/cmd_analysis.yaml +++ b/librz/core/cmd_descs/cmd_analysis.yaml @@ -16,7 +16,15 @@ commands: args: [] - name: aaaa summary: Experimental analysis - cname: analyze_everything_experimental + subcommands: + - name: aaaa + summary: Legacy experimental analysis + cname: analyze_everything_experimental + args: [] + - name: aaaaP + summary: Abstract Interpreter Prototype + cname: analyze_interpreter_prototype + args: [] args: [] - name: aac summary: Analysis function calls commands diff --git a/librz/core/cmd_descs/cmd_descs.c b/librz/core/cmd_descs/cmd_descs.c index 01391ad1207..8d96c924a58 100644 --- a/librz/core/cmd_descs/cmd_descs.c +++ b/librz/core/cmd_descs/cmd_descs.c @@ -4038,14 +4038,29 @@ static const RzCmdDescHelp analyze_everything_help = { .args = analyze_everything_args, }; +static const RzCmdDescArg aaaa_args[] = { + { 0 }, +}; +static const RzCmdDescHelp aaaa_help = { + .summary = "Experimental analysis", + .args = aaaa_args, +}; static const RzCmdDescArg analyze_everything_experimental_args[] = { { 0 }, }; static const RzCmdDescHelp analyze_everything_experimental_help = { - .summary = "Experimental analysis", + .summary = "Legacy experimental analysis", .args = analyze_everything_experimental_args, }; +static const RzCmdDescArg analyze_interpreter_prototype_args[] = { + { 0 }, +}; +static const RzCmdDescHelp analyze_interpreter_prototype_help = { + .summary = "Abstract Interpreter Prototype", + .args = analyze_interpreter_prototype_args, +}; + static const RzCmdDescHelp aac_help = { .summary = "Analysis function calls commands", }; @@ -22995,8 +23010,10 @@ RZ_IPI void rzshell_cmddescs_init(RzCore *core) { RzCmdDesc *analyze_everything_cd = rz_cmd_desc_argv_new(core->rcmd, aa_cd, "aaa", rz_analyze_everything_handler, &analyze_everything_help); rz_warn_if_fail(analyze_everything_cd); - RzCmdDesc *analyze_everything_experimental_cd = rz_cmd_desc_argv_new(core->rcmd, aa_cd, "aaaa", rz_analyze_everything_experimental_handler, &analyze_everything_experimental_help); - rz_warn_if_fail(analyze_everything_experimental_cd); + RzCmdDesc *aaaa_cd = rz_cmd_desc_group_new(core->rcmd, aa_cd, "aaaa", rz_analyze_everything_experimental_handler, &analyze_everything_experimental_help, &aaaa_help); + rz_warn_if_fail(aaaa_cd); + RzCmdDesc *analyze_interpreter_prototype_cd = rz_cmd_desc_argv_new(core->rcmd, aaaa_cd, "aaaaP", rz_analyze_interpreter_prototype_handler, &analyze_interpreter_prototype_help); + rz_warn_if_fail(analyze_interpreter_prototype_cd); RzCmdDesc *aac_cd = rz_cmd_desc_group_new(core->rcmd, aa_cd, "aac", rz_analyze_all_function_calls_handler, &analyze_all_function_calls_help, &aac_help); rz_warn_if_fail(aac_cd); diff --git a/librz/core/cmd_descs/cmd_descs.h b/librz/core/cmd_descs/cmd_descs.h index 2521aa4b607..ca12c6dfc04 100644 --- a/librz/core/cmd_descs/cmd_descs.h +++ b/librz/core/cmd_descs/cmd_descs.h @@ -342,6 +342,8 @@ RZ_IPI RzCmdStatus rz_analyze_simple_handler(RzCore *core, int argc, const char RZ_IPI RzCmdStatus rz_analyze_everything_handler(RzCore *core, int argc, const char **argv); // "aaaa" RZ_IPI RzCmdStatus rz_analyze_everything_experimental_handler(RzCore *core, int argc, const char **argv); +// "aaaaP" +RZ_IPI RzCmdStatus rz_analyze_interpreter_prototype_handler(RzCore *core, int argc, const char **argv); // "aac" RZ_IPI RzCmdStatus rz_analyze_all_function_calls_handler(RzCore *core, int argc, const char **argv); // "aaci" diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index d404db146eb..b658b005a6f 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -15,5 +15,6 @@ RZ_API bool rz_interpreter_run( RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues) { + RZ_LOG_WARN("Hello from rz_interpreter_run.\n"); return true; } diff --git a/librz/inquiry/p/inquiry_interpreter_prototype.c b/librz/inquiry/p/inquiry_interpreter_prototype.c index b32903e4b48..5df4bfcc801 100644 --- a/librz/inquiry/p/inquiry_interpreter_prototype.c +++ b/librz/inquiry/p/inquiry_interpreter_prototype.c @@ -8,7 +8,8 @@ bool interpret( RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues) { - return true; + RZ_LOG_WARN("Hello from Protoype.\n"); + return true; } static RzInquiryInterpreterPlugin interpreter_prototype = { From c5b8c160185f34ffff49224187d0fe23b1101cd5 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 6 Nov 2025 23:05:10 -0500 Subject: [PATCH 010/334] Move RzInquiry commands to own file. --- librz/core/cmd/cmd_analysis.c | 6 ------ librz/core/cmd/cmd_inquiry.c | 20 ++++++++++++++++++++ librz/core/cmd_descs/cmd_analysis.yaml | 16 +++++++++++----- librz/core/cmd_descs/cmd_descs.c | 15 ++++++++++----- librz/core/cmd_descs/cmd_descs.h | 4 ++-- librz/core/meson.build | 1 + librz/inquiry/inquiry_helpers.c | 8 ++++++++ librz/inquiry/meson.build | 1 + 8 files changed, 53 insertions(+), 18 deletions(-) create mode 100644 librz/core/cmd/cmd_inquiry.c create mode 100644 librz/inquiry/inquiry_helpers.c diff --git a/librz/core/cmd/cmd_analysis.c b/librz/core/cmd/cmd_analysis.c index cd6eadf5ec5..1eca5574951 100644 --- a/librz/core/cmd/cmd_analysis.c +++ b/librz/core/cmd/cmd_analysis.c @@ -4,7 +4,6 @@ #include #include -#include #include #include #include @@ -5907,11 +5906,6 @@ RZ_IPI RzCmdStatus rz_analyze_everything_experimental_handler(RzCore *core, int return RZ_CMD_STATUS_OK; } -RZ_IPI RzCmdStatus rz_analyze_interpreter_prototype_handler(RzCore *core, int argc, const char **argv) { - rz_interpreter_run(NULL, NULL, NULL); - return RZ_CMD_STATUS_OK; -} - RZ_IPI RzCmdStatus rz_analyze_all_function_calls_handler(RzCore *core, int argc, const char **argv) { rz_core_analysis_calls(core, false); return RZ_CMD_STATUS_OK; diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c new file mode 100644 index 00000000000..e59a1d9ff6f --- /dev/null +++ b/librz/core/cmd/cmd_inquiry.c @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2025 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include +#include +#include + + +RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int argc, const char **argv) { + // Generate first basic block + // Setup receive queue + // setup send queue + // Push entry point + // Setup yield queue. + // Dispatch interpreter into thread + // pop wait for address. + rz_interpreter_run(NULL, NULL, NULL); + return RZ_CMD_STATUS_OK; +} diff --git a/librz/core/cmd_descs/cmd_analysis.yaml b/librz/core/cmd_descs/cmd_analysis.yaml index 066acb46481..935be819f26 100644 --- a/librz/core/cmd_descs/cmd_analysis.yaml +++ b/librz/core/cmd_descs/cmd_analysis.yaml @@ -21,11 +21,17 @@ commands: summary: Legacy experimental analysis cname: analyze_everything_experimental args: [] - - name: aaaaP - summary: Abstract Interpreter Prototype - cname: analyze_interpreter_prototype - args: [] - args: [] + - name: aaaaI + summary: New RzInquiry analysis. + subcommands: + - name: aaaaIp + summary: Abstract Interpreter Prototype + cname: inquiry_interpreter_prototype + args: + - name: entry_points + type: RZ_CMD_ARG_TYPE_RZNUM + flags: RZ_CMD_ARG_FLAG_ARRAY + optional: true - name: aac summary: Analysis function calls commands subcommands: diff --git a/librz/core/cmd_descs/cmd_descs.c b/librz/core/cmd_descs/cmd_descs.c index 8d96c924a58..7c05184d9bf 100644 --- a/librz/core/cmd_descs/cmd_descs.c +++ b/librz/core/cmd_descs/cmd_descs.c @@ -4053,12 +4053,15 @@ static const RzCmdDescHelp analyze_everything_experimental_help = { .args = analyze_everything_experimental_args, }; -static const RzCmdDescArg analyze_interpreter_prototype_args[] = { +static const RzCmdDescHelp aaaaI_help = { + .summary = "New RzInquiry analysis.", +}; +static const RzCmdDescArg inquiry_interpreter_prototype_args[] = { { 0 }, }; -static const RzCmdDescHelp analyze_interpreter_prototype_help = { +static const RzCmdDescHelp inquiry_interpreter_prototype_help = { .summary = "Abstract Interpreter Prototype", - .args = analyze_interpreter_prototype_args, + .args = inquiry_interpreter_prototype_args, }; static const RzCmdDescHelp aac_help = { @@ -23012,8 +23015,10 @@ RZ_IPI void rzshell_cmddescs_init(RzCore *core) { RzCmdDesc *aaaa_cd = rz_cmd_desc_group_new(core->rcmd, aa_cd, "aaaa", rz_analyze_everything_experimental_handler, &analyze_everything_experimental_help, &aaaa_help); rz_warn_if_fail(aaaa_cd); - RzCmdDesc *analyze_interpreter_prototype_cd = rz_cmd_desc_argv_new(core->rcmd, aaaa_cd, "aaaaP", rz_analyze_interpreter_prototype_handler, &analyze_interpreter_prototype_help); - rz_warn_if_fail(analyze_interpreter_prototype_cd); + RzCmdDesc *aaaaI_cd = rz_cmd_desc_group_new(core->rcmd, aaaa_cd, "aaaaI", NULL, NULL, &aaaaI_help); + rz_warn_if_fail(aaaaI_cd); + RzCmdDesc *inquiry_interpreter_prototype_cd = rz_cmd_desc_argv_new(core->rcmd, aaaaI_cd, "aaaaIp", rz_inquiry_interpreter_prototype_handler, &inquiry_interpreter_prototype_help); + rz_warn_if_fail(inquiry_interpreter_prototype_cd); RzCmdDesc *aac_cd = rz_cmd_desc_group_new(core->rcmd, aa_cd, "aac", rz_analyze_all_function_calls_handler, &analyze_all_function_calls_help, &aac_help); rz_warn_if_fail(aac_cd); diff --git a/librz/core/cmd_descs/cmd_descs.h b/librz/core/cmd_descs/cmd_descs.h index ca12c6dfc04..c9a03d15684 100644 --- a/librz/core/cmd_descs/cmd_descs.h +++ b/librz/core/cmd_descs/cmd_descs.h @@ -342,8 +342,8 @@ RZ_IPI RzCmdStatus rz_analyze_simple_handler(RzCore *core, int argc, const char RZ_IPI RzCmdStatus rz_analyze_everything_handler(RzCore *core, int argc, const char **argv); // "aaaa" RZ_IPI RzCmdStatus rz_analyze_everything_experimental_handler(RzCore *core, int argc, const char **argv); -// "aaaaP" -RZ_IPI RzCmdStatus rz_analyze_interpreter_prototype_handler(RzCore *core, int argc, const char **argv); +// "aaaaIp" +RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int argc, const char **argv); // "aac" RZ_IPI RzCmdStatus rz_analyze_all_function_calls_handler(RzCore *core, int argc, const char **argv); // "aaci" diff --git a/librz/core/meson.build b/librz/core/meson.build index dc5e9d1fbf9..c599dbb02f8 100644 --- a/librz/core/meson.build +++ b/librz/core/meson.build @@ -90,6 +90,7 @@ rz_core_sources = [ 'cmd/cmd_help.c', 'cmd/cmd_history.c', 'cmd/cmd_info.c', + 'cmd/cmd_inquiry.c', 'cmd/cmd_interactive.c', 'cmd/cmd_interactive_panel.c', 'cmd/cmd_interpret.c', diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c new file mode 100644 index 00000000000..f13dcf67051 --- /dev/null +++ b/librz/inquiry/inquiry_helpers.c @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2025 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +/** + * \file Helper functions for the new analysis. Some of them not yet temporarily. + */ + + diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index f6f9060f0fa..06a2fb9a46e 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -13,6 +13,7 @@ inquiry_plugins = { rz_inquiry_sources = [ 'inquiry.c', + 'inquiry_helpers.c', 'interpreter/interpreter.c', 'p/inquiry_interpreter_prototype.c', ] From 06cb0998aecdcb4ff83fc337a74185e5f62195bf Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 6 Nov 2025 23:14:58 -0500 Subject: [PATCH 011/334] Move TODOs to PR --- librz/include/rz_inquiry.h | 4 ---- librz/include/rz_inquiry/rz_interpreter.h | 1 - librz/inquiry/p/inquiry_interpreter_prototype.c | 1 - 3 files changed, 6 deletions(-) diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 18f60c300bf..f18640275d4 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -24,11 +24,7 @@ typedef struct { RzInterpreterAbstraction supported_abstractions; bool (*init)(void **plugin_data); bool (*fini)(void *plugin_data); - // TODO: Configuration or initial setup of interpreter not yet implemented. bool (*interpret)( - // TODO: The entry point could be in the reveive queue already. - // Saves one more parameter, keeps the IPI on point. - ut64 entry_point, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues); diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index ceb90926752..5d85c5e7dd9 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -40,7 +40,6 @@ typedef enum { typedef void *RzInterpreterYield; ///< A yield of an interpreter. Type is implied by the queue. -// TODO: Could be private /** * \brief A filter for abstract values to decide if they should be pushed into * the yield queue or not. diff --git a/librz/inquiry/p/inquiry_interpreter_prototype.c b/librz/inquiry/p/inquiry_interpreter_prototype.c index 5df4bfcc801..a4bc50117e5 100644 --- a/librz/inquiry/p/inquiry_interpreter_prototype.c +++ b/librz/inquiry/p/inquiry_interpreter_prototype.c @@ -4,7 +4,6 @@ #include bool interpret( - ut64 entry_point, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues) { From 51c9f508fd4bd5f972930330ff673f19c0321a8a Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 10 Nov 2025 13:31:11 -0500 Subject: [PATCH 012/334] Add helper to check if RzAnalysisOp changes the control flow. --- librz/arch/analysis.c | 32 ++++++++++++++++++++++++++++++++ librz/include/rz_analysis.h | 1 + 2 files changed, 33 insertions(+) diff --git a/librz/arch/analysis.c b/librz/arch/analysis.c index 74c117739e5..fec232bf7a0 100644 --- a/librz/arch/analysis.c +++ b/librz/arch/analysis.c @@ -986,6 +986,38 @@ RZ_API bool rz_analysis_op_is_call(RZ_NONNULL const RzAnalysisOp *op) { return false; } +/** + * \brief Checks if \p op changes the control flow. + * It checks op->type, op->eob for it. So any jump, call, trap, or illegal + * instruction. + * It also returns true for conditional instructions. + * + * \return True if \p op changes the control flow. False otherwise. + */ +RZ_API bool rz_analysis_op_changes_control_flow(RZ_NONNULL const RzAnalysisOp *op) { + rz_return_val_if_fail(op, false); + if (rz_analysis_op_is_eob(op)) { + return true; + } + switch (op->type & RZ_ANALYSIS_OP_TYPE_MASK) { + case RZ_ANALYSIS_OP_TYPE_RCALL: + case RZ_ANALYSIS_OP_TYPE_ICALL: + case RZ_ANALYSIS_OP_TYPE_IRCALL: + case RZ_ANALYSIS_OP_TYPE_CCALL: + case RZ_ANALYSIS_OP_TYPE_UCCALL: + case RZ_ANALYSIS_OP_TYPE_RET: + case RZ_ANALYSIS_OP_TYPE_CRET: + case RZ_ANALYSIS_OP_TYPE_ILL: + case RZ_ANALYSIS_OP_TYPE_UNK: + case RZ_ANALYSIS_OP_TYPE_SWI: + case RZ_ANALYSIS_OP_TYPE_CSWI: + case RZ_ANALYSIS_OP_TYPE_TRAP: + return true; + default: + return false; + } +} + RZ_API void rz_analysis_purge(RzAnalysis *analysis) { rz_analysis_hint_clear(analysis); rz_interval_tree_fini(&analysis->meta); diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index f6782431f75..af18f77ff6c 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -1500,6 +1500,7 @@ RZ_API bool rz_analysis_op_fini(RzAnalysisOp *op); RZ_API int rz_analysis_op_reg_delta(RzAnalysis *analysis, ut64 addr, const char *name); RZ_API bool rz_analysis_op_is_eob(const RzAnalysisOp *op); RZ_API bool rz_analysis_op_is_call(RZ_NONNULL const RzAnalysisOp *op); +RZ_API bool rz_analysis_op_changes_control_flow(RZ_NONNULL const RzAnalysisOp *op); RZ_API RzList /**/ *rz_analysis_op_list_new(void); RZ_API int rz_analysis_op(RZ_NONNULL RzAnalysis *analysis, RZ_OUT RzAnalysisOp *op, ut64 addr, const ut8 *data, ut64 len, RzAnalysisOpMask mask); RZ_API RzAnalysisOp *rz_analysis_op_hexstr(RzAnalysis *analysis, ut64 addr, const char *hexstr); From 35e5fc2b800f866701d7ca7c5d36749624eefd5e Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 6 Nov 2025 23:57:20 -0500 Subject: [PATCH 013/334] Add helper to generate a basic block IL effect. --- librz/include/rz_inquiry.h | 3 ++ librz/inquiry/inquiry_helpers.c | 80 +++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index f18640275d4..2464ba2a3a7 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -48,6 +48,9 @@ RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NO RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void); RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *a); +RZ_API bool rz_inquiry_op_type_is_eob(_RzAnalysisOpType type); +RZ_API RZ_OWN RzILOpEffect *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr); + #ifdef __cplusplus } #endif diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c index f13dcf67051..48f7871cbe6 100644 --- a/librz/inquiry/inquiry_helpers.c +++ b/librz/inquiry/inquiry_helpers.c @@ -5,4 +5,84 @@ * \file Helper functions for the new analysis. Some of them not yet temporarily. */ +#include +#include +#include +#include +#include +RZ_API bool rz_inquiry_op_type_is_eob(_RzAnalysisOpType type) { + switch (type) { + default: + false; + case RZ_ANALYSIS_OP_TYPE_JMP: + case RZ_ANALYSIS_OP_TYPE_UJMP: + case RZ_ANALYSIS_OP_TYPE_RJMP: + case RZ_ANALYSIS_OP_TYPE_IJMP: + case RZ_ANALYSIS_OP_TYPE_IRJMP: + case RZ_ANALYSIS_OP_TYPE_CJMP: + case RZ_ANALYSIS_OP_TYPE_RCJMP: + case RZ_ANALYSIS_OP_TYPE_MJMP: + case RZ_ANALYSIS_OP_TYPE_MCJMP: + case RZ_ANALYSIS_OP_TYPE_UCJMP: + case RZ_ANALYSIS_OP_TYPE_CALL: + case RZ_ANALYSIS_OP_TYPE_UCALL: + case RZ_ANALYSIS_OP_TYPE_RCALL: + case RZ_ANALYSIS_OP_TYPE_ICALL: + case RZ_ANALYSIS_OP_TYPE_IRCALL: + case RZ_ANALYSIS_OP_TYPE_CCALL: + case RZ_ANALYSIS_OP_TYPE_UCCALL: + case RZ_ANALYSIS_OP_TYPE_RET: + case RZ_ANALYSIS_OP_TYPE_CRET: + case RZ_ANALYSIS_OP_TYPE_ILL: + case RZ_ANALYSIS_OP_TYPE_UNK: + case RZ_ANALYSIS_OP_TYPE_TRAP: + case RZ_ANALYSIS_OP_TYPE_SWI: + case RZ_ANALYSIS_OP_TYPE_CSWI: + case RZ_ANALYSIS_OP_TYPE_LEAVE: + case RZ_ANALYSIS_OP_TYPE_SWITCH: + return true; + } +} + +RZ_API RZ_OWN RzILOpEffect *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr) { + rz_return_val_if_fail(analysis && analysis->cur && io, NULL); + RzILOpEffect *bb = NULL; + RzAnalysisOp op = { 0 }; + rz_analysis_op_init(&op); + // Estimate a reasonable number of bytes to read. + int max_read_size = (analysis->cur->bits / 8) * 16; + int actual_size = max_read_size; + ut8 *buf = RZ_NEWS0(ut8, actual_size); + if (!actual_size || !buf) { + goto fail; + } + bool changes_cf = true; + do { + actual_size = rz_io_nread_at(io, addr, buf, max_read_size); + if (actual_size < 0) { + RZ_LOG_WARN("inquiry: Failed to read memory for IL basic block generation.\n"); + goto fail; + } + if (rz_analysis_op(analysis, &op, addr, buf, actual_size, RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_BASIC) <= 0 || !op.il_op) { + rz_analysis_op_fini(&op); + break; + } + bb = rz_il_op_new_seq(bb, op.il_op); + // Take ownership of IL op pointer. + op.il_op = NULL; + changes_cf = rz_analysis_op_changes_control_flow(&op); + rz_analysis_op_fini(&op); + addr += op.size; + rz_mem_memzero(buf, max_read_size); + } while (!changes_cf); + + free(buf); + return bb; + +fail: + free(buf); + rz_analysis_op_fini(&op); + rz_il_op_effect_free(bb); + return NULL; +} From 3206028a02b735568ae44cdfc4132fc72dab86c1 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 7 Nov 2025 14:21:33 -0500 Subject: [PATCH 014/334] Add basic command to run the prototype interpreter. --- librz/core/cmd/cmd_inquiry.c | 109 +++++++++++- librz/core/cmd_descs/cmd_descs.c | 12 +- librz/include/rz_inquiry.h | 4 +- librz/include/rz_inquiry/rz_interpreter.h | 60 +++++-- librz/inquiry/inquiry.c | 17 ++ librz/inquiry/inquiry_helpers.c | 4 +- librz/inquiry/interpreter/interpreter.c | 161 +++++++++++++++++- librz/inquiry/meson.build | 2 + .../inquiry/p/inquiry_interpreter_prototype.c | 2 +- 9 files changed, 334 insertions(+), 37 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index e59a1d9ff6f..8147766f48e 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -1,20 +1,113 @@ // SPDX-FileCopyrightText: 2025 RizinOrg // SPDX-License-Identifier: LGPL-3.0-only +#include +#include +#include #include #include +#include #include #include - RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int argc, const char **argv) { - // Generate first basic block - // Setup receive queue - // setup send queue - // Push entry point - // Setup yield queue. + rz_return_val_if_fail(core->analysis && core->io, RZ_CMD_STATUS_ERROR); + + RzThreadQueue *il_queue = rz_th_queue_new(RZ_INTERPRETER_IL_QUEUE_SIZE, (RzListFree)rz_interpreter_il_queue_free); + if (!il_queue) { + return RZ_CMD_STATUS_ERROR; + } + RzILOpEffect *eff = NULL; + if (argc == 1) { + ut64 entry_point = rz_bin_get_first_entrypoint(core->bin->cur->o); + eff = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); + if (!eff) { + rz_th_queue_free(il_queue); + RZ_LOG_WARN("Could not get entry point IL operation at 0x%" PRIx64 "\n", entry_point); + return RZ_CMD_STATUS_ERROR; + } + } else { + // Add all entry points given as arguments. + for (size_t i = 1; i < argc; i++) { + ut64 entry_point = rz_num_get(core->num, argv[i]); + eff = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); + if (!eff) { + rz_th_queue_free(il_queue); + return RZ_CMD_STATUS_ERROR; + } + } + } + rz_th_queue_push(il_queue, eff, true); + + RzThreadQueue *addr_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, (RzListFree)rz_interpreter_addr_queue_free); + if (!addr_queue) { + rz_th_queue_free(il_queue); + return RZ_CMD_STATUS_ERROR; + } + RzInterval iv = { .addr = 0, .size = UT64_MAX }; + RzList *boundaries = rz_io_get_boundaries_all_io_maps(core->io, iv); + if (!boundaries) { + rz_th_queue_free(addr_queue); + rz_th_queue_free(il_queue); + return RZ_CMD_STATUS_ERROR; + } + RzInterpreterYieldQueue *yield_queue = rz_interpreter_yield_queue_new( + RZ_INTERPRETER_YIELD_KIND_XREF, + (RzInterpreterYieldFilter *)rz_inquiry_xref_interpreter_filter, + boundaries); + if (!yield_queue) { + rz_th_queue_free(addr_queue); + rz_th_queue_free(il_queue); + return RZ_CMD_STATUS_ERROR; + } + RzPVector *yield_queues = rz_pvector_new((RzPVectorFree)rz_interpreter_yield_queue_free); + if (!yield_queue || !yield_queues) { + rz_th_queue_free(addr_queue); + rz_th_queue_free(il_queue); + rz_interpreter_yield_queue_free(yield_queue); + rz_pvector_free(yield_queues); + return RZ_CMD_STATUS_ERROR; + } + rz_pvector_push(yield_queues, yield_queue); + RzAtomicBool *is_running = rz_atomic_bool_new(true); + RzInterpreterQueueSet *qset = rz_interpreter_queue_set_new(addr_queue, il_queue, yield_queues, is_running); + if (!qset) { + rz_th_queue_free(addr_queue); + rz_th_queue_free(il_queue); + rz_interpreter_yield_queue_free(yield_queue); + rz_pvector_free(yield_queues); + rz_atomic_bool_free(is_running); + return RZ_CMD_STATUS_ERROR; + } + // Dispatch interpreter into thread - // pop wait for address. - rz_interpreter_run(NULL, NULL, NULL); + // and do a busy loop on the queue for now + RZ_LOG_WARN("INQUIRY: Start thread.\n"); + RzThread *iterpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, qset); + RZ_LOG_WARN("INQUIRY: Start loop.\n"); + while (rz_atomic_bool_get(is_running)) { + ut64 *addr = rz_th_queue_pop(addr_queue, false); + if (!addr) { + // Some artifical lag for testing. + rz_sys_sleep(1); + RZ_LOG_WARN("INQUIRY: Sleep over.\n"); + continue; + } + RZ_LOG_WARN("INQUIRY: Received %" PRIx64 ".\n", (*addr)); + RzILOpEffect *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); + free(addr); + if (!bb) { + // Stop interpreter. + rz_atomic_bool_set(is_running, false); + continue; + } + RZ_LOG_WARN("INQUIRY: Send %p.\n", bb); + rz_th_queue_push(il_queue, bb, true); + } + // Wait for thread to finish before cleaning. + rz_th_wait(iterpr_th); + rz_th_free(iterpr_th); + rz_interpreter_queue_set_free(qset); + return RZ_CMD_STATUS_OK; } diff --git a/librz/core/cmd_descs/cmd_descs.c b/librz/core/cmd_descs/cmd_descs.c index 7c05184d9bf..d4f9e9ac6dc 100644 --- a/librz/core/cmd_descs/cmd_descs.c +++ b/librz/core/cmd_descs/cmd_descs.c @@ -251,6 +251,7 @@ static const RzCmdDescArg input_msg_args[2]; static const RzCmdDescArg input_conditional_args[2]; static const RzCmdDescArg get_addr_references_args[2]; static const RzCmdDescArg push_escaped_args[2]; +static const RzCmdDescArg inquiry_interpreter_prototype_args[2]; static const RzCmdDescArg analysis_all_esil_args[2]; static const RzCmdDescArg analyze_all_consecutive_functions_in_section_args[2]; static const RzCmdDescArg analyze_xrefs_section_bytes_args[2]; @@ -4038,12 +4039,8 @@ static const RzCmdDescHelp analyze_everything_help = { .args = analyze_everything_args, }; -static const RzCmdDescArg aaaa_args[] = { - { 0 }, -}; static const RzCmdDescHelp aaaa_help = { .summary = "Experimental analysis", - .args = aaaa_args, }; static const RzCmdDescArg analyze_everything_experimental_args[] = { { 0 }, @@ -4057,6 +4054,13 @@ static const RzCmdDescHelp aaaaI_help = { .summary = "New RzInquiry analysis.", }; static const RzCmdDescArg inquiry_interpreter_prototype_args[] = { + { + .name = "entry_points", + .type = RZ_CMD_ARG_TYPE_RZNUM, + .flags = RZ_CMD_ARG_FLAG_ARRAY, + .optional = true, + + }, { 0 }, }; static const RzCmdDescHelp inquiry_interpreter_prototype_help = { diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 2464ba2a3a7..5d605acfed6 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -27,7 +27,7 @@ typedef struct { bool (*interpret)( RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, - RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues); + RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues); } RzInquiryInterpreterPlugin; typedef struct rz_inquiry_plugin_t { @@ -51,6 +51,8 @@ RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *a); RZ_API bool rz_inquiry_op_type_is_eob(_RzAnalysisOpType type); RZ_API RZ_OWN RzILOpEffect *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr); +RZ_API bool rz_inquiry_xref_interpreter_filter(const RzAnalysisXRef *xref, const RzList /**/ *allowed_io_maps); + #ifdef __cplusplus } #endif diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 5d85c5e7dd9..731b12aa323 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -15,6 +15,10 @@ #include #include +#define RZ_INTERPRETER_IL_QUEUE_SIZE 128 +#define RZ_INTERPRETER_ADDR_QUEUE_SIZE 1024 +#define RZ_INTERPRETER_YIELD_QUEUE_SIZE 4096 + /** * \brief The abstractions this module supports. */ @@ -34,25 +38,33 @@ typedef struct { } RzInterpreterAbstrVal; typedef enum { - RZ_INTERPRETER_YIELD_KIND_ABSTR_VAL, ///< Yield object is an abstract value. - RZ_INTERPRETER_YIELD_KIND_CFG_EDGE, ///< Yield object a discovered CFG edge. + RZ_INTERPRETER_YIELD_KIND_XREF } RzInterpreterYieldKind; -typedef void *RzInterpreterYield; ///< A yield of an interpreter. Type is implied by the queue. +/** + * \brief A yield of an interpreter. Type is implied by the queue. + * Object is a union so the elements pushed over the queu are small. + */ +typedef union { + RzAnalysisXRef *abstr_const; +} RzInterpreterYield; /** * \brief A filter for abstract values to decide if they should be pushed into * the yield queue or not. */ -typedef bool (*RzInterpreterYieldFilter)(RzInterpreterYieldKind kind, const RzInterpreterYield *yield); +typedef bool (*RzInterpreterYieldFilter)(const void *element, const void *filter_data); /** * \brief A queue to push interpretation yields into. */ typedef struct { - RzInterpreterAbstraction kind; - RzInterpreterYieldFilter *filter; - RzThreadQueue /**/ *yield_queue; + RzInterpreterYieldKind kind; + const RzInterpreterYieldFilter *filter; + union { + RzList /**/ *io_boundaries; + } filter_data; + RzThreadQueue /**/ *yield_queue; } RzInterpreterYieldQueue; /** @@ -70,16 +82,32 @@ typedef struct { RzILOpEffect *il_effect; } RzInterpreterILQueueElement; -typedef struct { - RzThreadQueue /**/ *yield_queue; -} RzInterpreterILQueue; - /** - * \brief Performs abstract interpretation. + * \brief The set of required queues for an interpreter to run. */ -RZ_API bool rz_interpreter_run( - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, - RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues); +typedef struct { + RzThreadQueue /**/ *addr_queue; ///< The queue to send requests to the cache what address to get the next IL op from. + RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. + // TODO: We need to decide how to distirbute the yield. + RzPVector /**/ *yield_queues; ///< The queues to push the yield of interpretation into. + RzAtomicBool *is_running_flag; ///< Flag for the interpreter thread to toggle when done. +} RzInterpreterQueueSet; + +RZ_API void rz_interpreter_il_queue_free(RZ_OWN RZ_NULLABLE RzThreadQueue /**/ *q); +RZ_API void rz_interpreter_addr_queue_free(RZ_OWN RZ_NULLABLE RzThreadQueue /**/ *q); +RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldQueue *yield_queue); + +RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpreterYieldKind kind, + const RzInterpreterYieldFilter *filter, + RZ_OWN RZ_NULLABLE void *filter_data); + +RZ_API RZ_OWN RzInterpreterQueueSet *rz_interpreter_queue_set_new( + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *addr_queue, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, + RZ_NONNULL RZ_OWN RzPVector /**/ *yield_queues, + RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag); +RZ_API void rz_interpreter_queue_set_free(RZ_NULLABLE RZ_OWN RzInterpreterQueueSet *qset); + +RZ_API bool rz_interpreter_run(RZ_OWN RZ_NONNULL RzInterpreterQueueSet *queue_set); #endif // RZ_INTERPRETER diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 6df9e9c1754..5f25e05f272 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -5,6 +5,9 @@ #include #include "rz_inquiry_plugins.h" +#include "rz_list.h" +#include "rz_types_base.h" +#include "rz_util/rz_assert.h" RZ_LIB_VERSION(rz_inquiry); @@ -43,3 +46,17 @@ RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OW rz_warn_if_reached(); return false; } + +RZ_API bool rz_inquiry_xref_interpreter_filter(const RzAnalysisXRef *xref, const RzList /**/ *allowed_io_maps) { + rz_return_val_if_fail(xref && allowed_io_maps, false); + const RzIOMap *map; + RzListIter *it; + rz_list_foreach (allowed_io_maps, it, map) { + ut64 start = map->itv.addr; + ut64 end = map->itv.addr + map->itv.size; + if (RZ_BETWEEN(start, xref->to, end)) { + return true; + } + } + return false; +} diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c index 48f7871cbe6..ac5a3b2c1a3 100644 --- a/librz/inquiry/inquiry_helpers.c +++ b/librz/inquiry/inquiry_helpers.c @@ -14,7 +14,7 @@ RZ_API bool rz_inquiry_op_type_is_eob(_RzAnalysisOpType type) { switch (type) { default: - false; + return false; case RZ_ANALYSIS_OP_TYPE_JMP: case RZ_ANALYSIS_OP_TYPE_UJMP: case RZ_ANALYSIS_OP_TYPE_RJMP: @@ -68,7 +68,7 @@ RZ_API RZ_OWN RzILOpEffect *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis rz_analysis_op_fini(&op); break; } - bb = rz_il_op_new_seq(bb, op.il_op); + bb = bb ? rz_il_op_new_seq(bb, op.il_op) : op.il_op; // Take ownership of IL op pointer. op.il_op = NULL; changes_cf = rz_analysis_op_changes_control_flow(&op); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index b658b005a6f..d783bcdfd2d 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -5,16 +5,167 @@ * \file The API implementation for all analysis interpreters. */ +#include +#include +#include +#include #include +RZ_API void rz_interpreter_il_queue_free(RZ_OWN RZ_NULLABLE RzThreadQueue /**/ *q) { + if (!q) { + return; + } + RzILOpEffect *eff = rz_th_queue_pop(q, false); + while (eff) { + rz_il_op_effect_free(eff); + eff = rz_th_queue_pop(q, false); + } +} + +RZ_API void rz_interpreter_addr_queue_free(RZ_OWN RZ_NULLABLE RzThreadQueue /**/ *q) { + if (!q) { + return; + } + ut64 *addr = rz_th_queue_pop(q, false); + while (addr) { + free(addr); + addr = rz_th_queue_pop(q, false); + } +} + +static void const_yield_queue_free(RZ_OWN RZ_NULLABLE RzThreadQueue /**/ *q) { + if (!q) { + return; + } + RzInterpreterYield *yield = rz_th_queue_pop(q, false); + while (yield) { + free(yield->abstr_const); + yield = rz_th_queue_pop(q, false); + } +} + +RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldQueue *yield_queue) { + if (!yield_queue) { + return; + } + if (yield_queue->yield_queue) { + rz_th_queue_free(yield_queue->yield_queue); + } + switch (yield_queue->kind) { + default: + break; + case RZ_INTERPRETER_YIELD_KIND_XREF: + // Free the RzIOMap list. + rz_list_free(yield_queue->filter_data.io_boundaries); + break; + } + free(yield_queue); +} + +RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpreterYieldKind kind, + const RzInterpreterYieldFilter *filter, + RZ_OWN RZ_NULLABLE void *filter_data) { + RzInterpreterYieldQueue *yield_queue = RZ_NEW0(RzInterpreterYieldQueue); + if (!yield_queue) { + return NULL; + } + RzThreadQueue *queue = NULL; + switch (kind) { + case RZ_INTERPRETER_YIELD_KIND_XREF: + queue = rz_th_queue_new(RZ_INTERPRETER_YIELD_QUEUE_SIZE, (RzListFree)const_yield_queue_free); + break; + } + if (!queue) { + free(yield_queue); + return NULL; + } + yield_queue->kind = kind; + yield_queue->yield_queue = queue; + yield_queue->filter = filter; + // TODO Doesn't look good. Void pointers are not nice. + switch (kind) { + default: + rz_return_val_if_fail(!filter_data, NULL); + break; + case RZ_INTERPRETER_YIELD_KIND_XREF: + yield_queue->filter_data.io_boundaries = filter_data; + break; + } + return yield_queue; +} + +/** + * \brief Initializes a new RzInterpreterQueueSet and returns it. + * If it fails, all arguments are freed. + * + * \param request_il The request queue. + * \param receive_il The receive queue. + * \param yield_queues The yield queues. + */ +RZ_API RZ_OWN RzInterpreterQueueSet *rz_interpreter_queue_set_new( + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *addr_queue, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, + RZ_NONNULL RZ_OWN RzPVector /**/ *yield_queues, + RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag) { + RzInterpreterQueueSet *set = RZ_NEW0(RzInterpreterQueueSet); + if (!set) { + rz_th_queue_free(addr_queue); + rz_th_queue_free(il_queue); + rz_pvector_free(yield_queues); + rz_atomic_bool_free(is_running_flag); + return NULL; + } + set->il_queue = il_queue; + set->addr_queue = addr_queue; + set->yield_queues = yield_queues; + set->is_running_flag = is_running_flag; + return set; +} + +RZ_API void rz_interpreter_queue_set_free(RZ_NULLABLE RZ_OWN RzInterpreterQueueSet *qset) { + if (!qset) { + return; + } + if (qset->addr_queue) { + rz_th_queue_free(qset->addr_queue); + } + if (qset->il_queue) { + rz_th_queue_free(qset->il_queue); + } + if (qset->yield_queues) { + rz_pvector_free(qset->yield_queues); + } + if (qset->is_running_flag) { + rz_atomic_bool_free(qset->is_running_flag); + } + free(qset); +} + /** * \brief Runs a set of interpreters to inquire the requested yield. * What interpreters are spawned depend on the queues in \p yield_queues. */ -RZ_API bool rz_interpreter_run( - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, - RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues) { - RZ_LOG_WARN("Hello from rz_interpreter_run.\n"); +RZ_API bool rz_interpreter_run(RZ_OWN RzInterpreterQueueSet *qset) { + RZ_LOG_WARN("INTERPRETER: Hello.\n"); + RzThreadQueue *il_queue = qset->il_queue; + RzThreadQueue *addr_queue = qset->addr_queue; + + RzILOpEffect *effect = rz_th_queue_pop(il_queue, false); + RZ_LOG_WARN("INTERPRETER: Got IL op: %p\n", effect); + rz_il_op_effect_free(effect); + + ut64 *a = RZ_NEW(ut64); + *a = 0x08000080ul; + rz_th_queue_push(addr_queue, a, true); + RZ_LOG_WARN("INTERPRETER: Requested: %" PRIx64 "\n", 0x08000080ul); + + effect = rz_th_queue_pop(il_queue, true); + while (rz_atomic_bool_get(qset->is_running_flag) && !effect) { + effect = rz_th_queue_pop(il_queue, true); + } + RZ_LOG_WARN("INTERPRETER: Got IL op: %p\n", effect); + rz_il_op_effect_free(effect); + + rz_atomic_bool_set(qset->is_running_flag, false); return true; } diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index 06a2fb9a46e..0559ae6d02c 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -26,6 +26,7 @@ rz_inquiry = library('rz_inquiry', rz_inquiry_sources, rz_arch_dep, # Legacy analysis module rz_cons_dep, rz_hash_dep, + rz_il_dep, rz_io_dep, rz_magic_dep, rz_search_dep, @@ -52,6 +53,7 @@ modules += { 'rz_inquiry': { 'rz_arch', 'rz_cons', 'rz_hash', + 'rz_il', 'rz_io', 'rz_magic', 'rz_search', diff --git a/librz/inquiry/p/inquiry_interpreter_prototype.c b/librz/inquiry/p/inquiry_interpreter_prototype.c index a4bc50117e5..90025725092 100644 --- a/librz/inquiry/p/inquiry_interpreter_prototype.c +++ b/librz/inquiry/p/inquiry_interpreter_prototype.c @@ -6,7 +6,7 @@ bool interpret( RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, - RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues) { + RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues) { RZ_LOG_WARN("Hello from Protoype.\n"); return true; } From 3c450dfbc68e15a84c601bd68dd3cb1b47c8a526 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 12 Nov 2025 09:01:07 -0500 Subject: [PATCH 015/334] Refine API --- librz/core/cmd/cmd_inquiry.c | 15 +++++++------ librz/include/rz_inquiry.h | 14 ++++++++---- librz/include/rz_inquiry/rz_interpreter.h | 22 +++++++++++++++---- librz/inquiry/interpreter/interpreter.c | 6 ++--- .../inquiry/p/inquiry_interpreter_prototype.c | 12 +++++----- 5 files changed, 45 insertions(+), 24 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 8147766f48e..95bd78749fe 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -23,7 +23,7 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar eff = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); if (!eff) { rz_th_queue_free(il_queue); - RZ_LOG_WARN("Could not get entry point IL operation at 0x%" PRIx64 "\n", entry_point); + RZ_LOG_WARN("Could not get entry point IL operation at 0x%" PFMT64x "\n", (ut64)entry_point); return RZ_CMD_STATUS_ERROR; } } else { @@ -51,8 +51,9 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar rz_th_queue_free(il_queue); return RZ_CMD_STATUS_ERROR; } + RzInterpreterYieldKind yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; RzInterpreterYieldQueue *yield_queue = rz_interpreter_yield_queue_new( - RZ_INTERPRETER_YIELD_KIND_XREF, + yield_kind, (RzInterpreterYieldFilter *)rz_inquiry_xref_interpreter_filter, boundaries); if (!yield_queue) { @@ -60,22 +61,22 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar rz_th_queue_free(il_queue); return RZ_CMD_STATUS_ERROR; } - RzPVector *yield_queues = rz_pvector_new((RzPVectorFree)rz_interpreter_yield_queue_free); + HtUP *yield_queues = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_yield_queue_free); if (!yield_queue || !yield_queues) { rz_th_queue_free(addr_queue); rz_th_queue_free(il_queue); rz_interpreter_yield_queue_free(yield_queue); - rz_pvector_free(yield_queues); + ht_up_free(yield_queues); return RZ_CMD_STATUS_ERROR; } - rz_pvector_push(yield_queues, yield_queue); + ht_up_insert(yield_queues, yield_kind, yield_queue); RzAtomicBool *is_running = rz_atomic_bool_new(true); RzInterpreterQueueSet *qset = rz_interpreter_queue_set_new(addr_queue, il_queue, yield_queues, is_running); if (!qset) { rz_th_queue_free(addr_queue); rz_th_queue_free(il_queue); rz_interpreter_yield_queue_free(yield_queue); - rz_pvector_free(yield_queues); + ht_up_free(yield_queues); rz_atomic_bool_free(is_running); return RZ_CMD_STATUS_ERROR; } @@ -93,7 +94,7 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar RZ_LOG_WARN("INQUIRY: Sleep over.\n"); continue; } - RZ_LOG_WARN("INQUIRY: Received %" PRIx64 ".\n", (*addr)); + RZ_LOG_WARN("INQUIRY: Received %" PFMT64x ".\n", (*addr)); RzILOpEffect *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); free(addr); if (!bb) { diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 5d605acfed6..42af7c99448 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -21,13 +21,19 @@ typedef struct { const char *version; const char *desc; const char *license; + /** + * \brief Supported abstractions. Multiple flags can be set. + */ RzInterpreterAbstraction supported_abstractions; + /** + * \brief The yield type this interpreter generates. + */ + RzInterpreterYieldKind supported_yields; bool (*init)(void **plugin_data); bool (*fini)(void *plugin_data); - bool (*interpret)( - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, - RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues); + bool (*eval)(RZ_NONNULL RZ_BORROW RzInterpreterAbstrState *state, + RZ_NONNULL RZ_BORROW RzILOpEffect *effect, + RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues); } RzInquiryInterpreterPlugin; typedef struct rz_inquiry_plugin_t { diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 731b12aa323..bac5030e54d 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -27,18 +27,32 @@ typedef enum { * \brief Value abstraction into constant and bottom values. */ RZ_INTERPRETER_ABSTRACTION_CONST = 1 << 0, + /** + * \brief Value abstraction into Heap[base, offset] and bottom values. + */ + RZ_INTERPRETER_ABSTRACTION_HEAP = 1 << 1, + /** + * \brief Value abstraction into Stack[base, offset] and bottom values. + */ + RZ_INTERPRETER_ABSTRACTION_STACK = 1 << 2, } RzInterpreterAbstraction; /** * \brief An abitrary abstract value. */ typedef struct { - RzInterpreterAbstraction kind; + RzInterpreterAbstraction kind; ///< The abstraction of the value. void *abstr_data; } RzInterpreterAbstrVal; +typedef struct { + RzInterpreterAbstraction kinds; ///< The abstractions of the state. + HtSP *reg_map; ///< The register file. Currently a hashmap. + void *state; +} RzInterpreterAbstrState; + typedef enum { - RZ_INTERPRETER_YIELD_KIND_XREF + RZ_INTERPRETER_YIELD_KIND_XREF = 1 << 0, } RzInterpreterYieldKind; /** @@ -89,7 +103,7 @@ typedef struct { RzThreadQueue /**/ *addr_queue; ///< The queue to send requests to the cache what address to get the next IL op from. RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. // TODO: We need to decide how to distirbute the yield. - RzPVector /**/ *yield_queues; ///< The queues to push the yield of interpretation into. + HtUP /**/ *yield_queues; ///< The queues to push the yield of interpretation into. RzAtomicBool *is_running_flag; ///< Flag for the interpreter thread to toggle when done. } RzInterpreterQueueSet; @@ -104,7 +118,7 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre RZ_API RZ_OWN RzInterpreterQueueSet *rz_interpreter_queue_set_new( RZ_NONNULL RZ_OWN RzThreadQueue /**/ *addr_queue, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, - RZ_NONNULL RZ_OWN RzPVector /**/ *yield_queues, + RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag); RZ_API void rz_interpreter_queue_set_free(RZ_NULLABLE RZ_OWN RzInterpreterQueueSet *qset); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index d783bcdfd2d..6b9c44dbac7 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -105,13 +105,13 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre RZ_API RZ_OWN RzInterpreterQueueSet *rz_interpreter_queue_set_new( RZ_NONNULL RZ_OWN RzThreadQueue /**/ *addr_queue, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, - RZ_NONNULL RZ_OWN RzPVector /**/ *yield_queues, + RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag) { RzInterpreterQueueSet *set = RZ_NEW0(RzInterpreterQueueSet); if (!set) { rz_th_queue_free(addr_queue); rz_th_queue_free(il_queue); - rz_pvector_free(yield_queues); + ht_up_free(yield_queues); rz_atomic_bool_free(is_running_flag); return NULL; } @@ -133,7 +133,7 @@ RZ_API void rz_interpreter_queue_set_free(RZ_NULLABLE RZ_OWN RzInterpreterQueueS rz_th_queue_free(qset->il_queue); } if (qset->yield_queues) { - rz_pvector_free(qset->yield_queues); + ht_up_free(qset->yield_queues); } if (qset->is_running_flag) { rz_atomic_bool_free(qset->is_running_flag); diff --git a/librz/inquiry/p/inquiry_interpreter_prototype.c b/librz/inquiry/p/inquiry_interpreter_prototype.c index 90025725092..bf7cbcf0d43 100644 --- a/librz/inquiry/p/inquiry_interpreter_prototype.c +++ b/librz/inquiry/p/inquiry_interpreter_prototype.c @@ -3,11 +3,10 @@ #include -bool interpret( - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *request_il, - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *receive_il, - RZ_NONNULL RZ_BORROW RzPVector /**/ *yield_queues) { - RZ_LOG_WARN("Hello from Protoype.\n"); +bool eval(RZ_NONNULL RZ_BORROW RzInterpreterAbstrState *state, + RZ_NONNULL RZ_BORROW RzILOpEffect *effect, + RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues) { + RZ_LOG_WARN("Hello from Protoype eval.\n"); return true; } @@ -18,9 +17,10 @@ static RzInquiryInterpreterPlugin interpreter_prototype = { .desc = "A prototype interpreter for constant/bottom abstractions.", .license = "LGPL-3.0-only", .supported_abstractions = RZ_INTERPRETER_ABSTRACTION_CONST, + .supported_yields = RZ_INTERPRETER_YIELD_KIND_XREF, .init = NULL, .fini = NULL, - .interpret = interpret + .eval = eval }; RZ_API RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype = { From 14a64c9cea9c52b24e094963d980f4c67bf60e82 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 15 Nov 2025 18:06:10 +0100 Subject: [PATCH 016/334] Move the setup work of the interpreter to RzInquiry as better example. --- librz/core/cmd/cmd_inquiry.c | 100 +------------------- librz/include/rz_inquiry.h | 2 + librz/include/rz_inquiry/rz_interpreter.h | 2 +- librz/inquiry/interpreter/interpreter.c | 106 ++++++++++++++++++++++ librz/inquiry/meson.build | 2 + 5 files changed, 112 insertions(+), 100 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 95bd78749fe..5711cd0235c 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -12,103 +12,5 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int argc, const char **argv) { rz_return_val_if_fail(core->analysis && core->io, RZ_CMD_STATUS_ERROR); - - RzThreadQueue *il_queue = rz_th_queue_new(RZ_INTERPRETER_IL_QUEUE_SIZE, (RzListFree)rz_interpreter_il_queue_free); - if (!il_queue) { - return RZ_CMD_STATUS_ERROR; - } - RzILOpEffect *eff = NULL; - if (argc == 1) { - ut64 entry_point = rz_bin_get_first_entrypoint(core->bin->cur->o); - eff = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); - if (!eff) { - rz_th_queue_free(il_queue); - RZ_LOG_WARN("Could not get entry point IL operation at 0x%" PFMT64x "\n", (ut64)entry_point); - return RZ_CMD_STATUS_ERROR; - } - } else { - // Add all entry points given as arguments. - for (size_t i = 1; i < argc; i++) { - ut64 entry_point = rz_num_get(core->num, argv[i]); - eff = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); - if (!eff) { - rz_th_queue_free(il_queue); - return RZ_CMD_STATUS_ERROR; - } - } - } - rz_th_queue_push(il_queue, eff, true); - - RzThreadQueue *addr_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, (RzListFree)rz_interpreter_addr_queue_free); - if (!addr_queue) { - rz_th_queue_free(il_queue); - return RZ_CMD_STATUS_ERROR; - } - RzInterval iv = { .addr = 0, .size = UT64_MAX }; - RzList *boundaries = rz_io_get_boundaries_all_io_maps(core->io, iv); - if (!boundaries) { - rz_th_queue_free(addr_queue); - rz_th_queue_free(il_queue); - return RZ_CMD_STATUS_ERROR; - } - RzInterpreterYieldKind yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; - RzInterpreterYieldQueue *yield_queue = rz_interpreter_yield_queue_new( - yield_kind, - (RzInterpreterYieldFilter *)rz_inquiry_xref_interpreter_filter, - boundaries); - if (!yield_queue) { - rz_th_queue_free(addr_queue); - rz_th_queue_free(il_queue); - return RZ_CMD_STATUS_ERROR; - } - HtUP *yield_queues = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_yield_queue_free); - if (!yield_queue || !yield_queues) { - rz_th_queue_free(addr_queue); - rz_th_queue_free(il_queue); - rz_interpreter_yield_queue_free(yield_queue); - ht_up_free(yield_queues); - return RZ_CMD_STATUS_ERROR; - } - ht_up_insert(yield_queues, yield_kind, yield_queue); - RzAtomicBool *is_running = rz_atomic_bool_new(true); - RzInterpreterQueueSet *qset = rz_interpreter_queue_set_new(addr_queue, il_queue, yield_queues, is_running); - if (!qset) { - rz_th_queue_free(addr_queue); - rz_th_queue_free(il_queue); - rz_interpreter_yield_queue_free(yield_queue); - ht_up_free(yield_queues); - rz_atomic_bool_free(is_running); - return RZ_CMD_STATUS_ERROR; - } - - // Dispatch interpreter into thread - // and do a busy loop on the queue for now - RZ_LOG_WARN("INQUIRY: Start thread.\n"); - RzThread *iterpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, qset); - RZ_LOG_WARN("INQUIRY: Start loop.\n"); - while (rz_atomic_bool_get(is_running)) { - ut64 *addr = rz_th_queue_pop(addr_queue, false); - if (!addr) { - // Some artifical lag for testing. - rz_sys_sleep(1); - RZ_LOG_WARN("INQUIRY: Sleep over.\n"); - continue; - } - RZ_LOG_WARN("INQUIRY: Received %" PFMT64x ".\n", (*addr)); - RzILOpEffect *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); - free(addr); - if (!bb) { - // Stop interpreter. - rz_atomic_bool_set(is_running, false); - continue; - } - RZ_LOG_WARN("INQUIRY: Send %p.\n", bb); - rz_th_queue_push(il_queue, bb, true); - } - // Wait for thread to finish before cleaning. - rz_th_wait(iterpr_th); - rz_th_free(iterpr_th); - rz_interpreter_queue_set_free(qset); - - return RZ_CMD_STATUS_OK; + return rz_inquiry_interpreter(core, argc, argv) ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; } diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 42af7c99448..244b80c48fb 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -59,6 +59,8 @@ RZ_API RZ_OWN RzILOpEffect *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis RZ_API bool rz_inquiry_xref_interpreter_filter(const RzAnalysisXRef *xref, const RzList /**/ *allowed_io_maps); +RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv); + #ifdef __cplusplus } #endif diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index bac5030e54d..24d09f701e9 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -102,7 +102,7 @@ typedef struct { typedef struct { RzThreadQueue /**/ *addr_queue; ///< The queue to send requests to the cache what address to get the next IL op from. RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. - // TODO: We need to decide how to distirbute the yield. + // TODO: We need to decide how to distribute the yield. HtUP /**/ *yield_queues; ///< The queues to push the yield of interpretation into. RzAtomicBool *is_running_flag; ///< Flag for the interpreter thread to toggle when done. } RzInterpreterQueueSet; diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 6b9c44dbac7..4cd58404269 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -5,6 +5,7 @@ * \file The API implementation for all analysis interpreters. */ +#include #include #include #include @@ -169,3 +170,108 @@ RZ_API bool rz_interpreter_run(RZ_OWN RzInterpreterQueueSet *qset) { rz_atomic_bool_set(qset->is_running_flag, false); return true; } + +/** + * A function to call the prototype interpreter. + * Usually these tasks will be split between different caches and yield consumers. + */ +RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { + RzThreadQueue *il_queue = rz_th_queue_new(RZ_INTERPRETER_IL_QUEUE_SIZE, (RzListFree)rz_interpreter_il_queue_free); + if (!il_queue) { + return false; + } + RzILOpEffect *eff = NULL; + if (argc == 1) { + ut64 entry_point = rz_bin_get_first_entrypoint(core->bin->cur->o); + eff = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); + if (!eff) { + rz_th_queue_free(il_queue); + RZ_LOG_WARN("Could not get entry point IL operation at 0x%" PFMT64x "\n", (ut64)entry_point); + return false; + } + } else { + // Add all entry points given as arguments. + for (size_t i = 1; i < argc; i++) { + ut64 entry_point = rz_num_get(core->num, argv[i]); + eff = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); + if (!eff) { + rz_th_queue_free(il_queue); + return false; + } + } + } + rz_th_queue_push(il_queue, eff, true); + + RzThreadQueue *addr_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, (RzListFree)rz_interpreter_addr_queue_free); + if (!addr_queue) { + rz_th_queue_free(il_queue); + return false; + } + RzInterval iv = { .addr = 0, .size = UT64_MAX }; + RzList *boundaries = rz_io_get_boundaries_all_io_maps(core->io, iv); + if (!boundaries) { + rz_th_queue_free(addr_queue); + rz_th_queue_free(il_queue); + return false; + } + RzInterpreterYieldKind yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; + RzInterpreterYieldQueue *yield_queue = rz_interpreter_yield_queue_new( + yield_kind, + (RzInterpreterYieldFilter *)rz_inquiry_xref_interpreter_filter, + boundaries); + if (!yield_queue) { + rz_th_queue_free(addr_queue); + rz_th_queue_free(il_queue); + return false; + } + HtUP *yield_queues = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_yield_queue_free); + if (!yield_queue || !yield_queues) { + rz_th_queue_free(addr_queue); + rz_th_queue_free(il_queue); + rz_interpreter_yield_queue_free(yield_queue); + ht_up_free(yield_queues); + return false; + } + ht_up_insert(yield_queues, yield_kind, yield_queue); + RzAtomicBool *is_running = rz_atomic_bool_new(true); + RzInterpreterQueueSet *qset = rz_interpreter_queue_set_new(addr_queue, il_queue, yield_queues, is_running); + if (!qset) { + rz_th_queue_free(addr_queue); + rz_th_queue_free(il_queue); + rz_interpreter_yield_queue_free(yield_queue); + ht_up_free(yield_queues); + rz_atomic_bool_free(is_running); + return false; + } + + // Dispatch interpreter into thread + // and do a busy loop on the queue for now + RZ_LOG_WARN("INQUIRY: Start thread.\n"); + RzThread *iterpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, qset); + RZ_LOG_WARN("INQUIRY: Start loop.\n"); + while (rz_atomic_bool_get(is_running)) { + ut64 *addr = rz_th_queue_pop(addr_queue, false); + if (!addr) { + // Some artifical lag for testing. + rz_sys_sleep(1); + RZ_LOG_WARN("INQUIRY: Sleep over.\n"); + continue; + } + RZ_LOG_WARN("INQUIRY: Received %" PFMT64x ".\n", (*addr)); + RzILOpEffect *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); + free(addr); + if (!bb) { + // Stop interpreter. + rz_atomic_bool_set(is_running, false); + continue; + } + RZ_LOG_WARN("INQUIRY: Send %p.\n", bb); + rz_th_queue_push(il_queue, bb, true); + } + // Wait for thread to finish before cleaning. + rz_th_wait(iterpr_th); + rz_th_free(iterpr_th); + rz_interpreter_queue_set_free(qset); + + return true; +} diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index 0559ae6d02c..51e0452a569 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -24,6 +24,7 @@ rz_inquiry = library('rz_inquiry', rz_inquiry_sources, include_directories: rz_inquiry_inc, dependencies: [ rz_arch_dep, # Legacy analysis module + rz_bin_dep, rz_cons_dep, rz_hash_dep, rz_il_dep, @@ -51,6 +52,7 @@ modules += { 'rz_inquiry': { 'target': rz_inquiry, 'dependencies': [ 'rz_arch', + 'rz_bin', 'rz_cons', 'rz_hash', 'rz_il', From feefc650ffdcc32476a77fc0bba9dd3d2a022d49 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 15 Nov 2025 18:39:54 +0100 Subject: [PATCH 017/334] Move the ownership of the effects to interpreter.c and add descriptions. --- librz/include/rz_inquiry.h | 2 +- librz/include/rz_inquiry/rz_interpreter.h | 19 +----- librz/inquiry/interpreter/interpreter.c | 59 ++++++++++++++++--- .../inquiry/p/inquiry_interpreter_prototype.c | 2 +- 4 files changed, 55 insertions(+), 27 deletions(-) diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 244b80c48fb..5918fc61660 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -32,7 +32,7 @@ typedef struct { bool (*init)(void **plugin_data); bool (*fini)(void *plugin_data); bool (*eval)(RZ_NONNULL RZ_BORROW RzInterpreterAbstrState *state, - RZ_NONNULL RZ_BORROW RzILOpEffect *effect, + RZ_NONNULL const RzILOpEffect *effect, RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues); } RzInquiryInterpreterPlugin; diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 24d09f701e9..1e292d320af 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -81,27 +81,12 @@ typedef struct { RzThreadQueue /**/ *yield_queue; } RzInterpreterYieldQueue; -/** - * \brief The IL effect scopes pushed over the IL queue. - * The elements are always RzILOpEffects, but they can represent - * more or less instructions. - */ -typedef enum { - RZ_INTERPRETER_IL_QUEUE_ELEM_SCOPE_IPKT, ///< RzILOpEffect scope is one atomically execute instruction packet. - RZ_INTERPRETER_IL_QUEUE_ELEM_SCOPE_BB, ///< RzILOpEffect scope is one basic block (n instruction packets with a branch or termination at the end). -} RzInterpreterILQueueElemScope; - -typedef struct { - RzInterpreterILQueueElemScope effect_scope; - RzILOpEffect *il_effect; -} RzInterpreterILQueueElement; - /** * \brief The set of required queues for an interpreter to run. */ typedef struct { RzThreadQueue /**/ *addr_queue; ///< The queue to send requests to the cache what address to get the next IL op from. - RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. + RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. // TODO: We need to decide how to distribute the yield. HtUP /**/ *yield_queues; ///< The queues to push the yield of interpretation into. RzAtomicBool *is_running_flag; ///< Flag for the interpreter thread to toggle when done. @@ -117,7 +102,7 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre RZ_API RZ_OWN RzInterpreterQueueSet *rz_interpreter_queue_set_new( RZ_NONNULL RZ_OWN RzThreadQueue /**/ *addr_queue, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag); RZ_API void rz_interpreter_queue_set_free(RZ_NULLABLE RZ_OWN RzInterpreterQueueSet *qset); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 4cd58404269..69783109771 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -105,7 +105,7 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre */ RZ_API RZ_OWN RzInterpreterQueueSet *rz_interpreter_queue_set_new( RZ_NONNULL RZ_OWN RzThreadQueue /**/ *addr_queue, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag) { RzInterpreterQueueSet *set = RZ_NEW0(RzInterpreterQueueSet); @@ -151,9 +151,9 @@ RZ_API bool rz_interpreter_run(RZ_OWN RzInterpreterQueueSet *qset) { RzThreadQueue *il_queue = qset->il_queue; RzThreadQueue *addr_queue = qset->addr_queue; - RzILOpEffect *effect = rz_th_queue_pop(il_queue, false); + // The effect is owned by the cache. So it is constant. + const RzILOpEffect *effect = rz_th_queue_pop(il_queue, false); RZ_LOG_WARN("INTERPRETER: Got IL op: %p\n", effect); - rz_il_op_effect_free(effect); ut64 *a = RZ_NEW(ut64); *a = 0x08000080ul; @@ -165,7 +165,6 @@ RZ_API bool rz_interpreter_run(RZ_OWN RzInterpreterQueueSet *qset) { effect = rz_th_queue_pop(il_queue, true); } RZ_LOG_WARN("INTERPRETER: Got IL op: %p\n", effect); - rz_il_op_effect_free(effect); rz_atomic_bool_set(qset->is_running_flag, false); return true; @@ -176,10 +175,19 @@ RZ_API bool rz_interpreter_run(RZ_OWN RzInterpreterQueueSet *qset) { * Usually these tasks will be split between different caches and yield consumers. */ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { - RzThreadQueue *il_queue = rz_th_queue_new(RZ_INTERPRETER_IL_QUEUE_SIZE, (RzListFree)rz_interpreter_il_queue_free); + // The pseudo cache of IL effects. + // This is only a vector so we can simulate the ownership separation + // of the pointers. + RzPVector *il_cache = rz_pvector_new((RzPVectorFree)rz_il_op_effect_free); + // The queue to pass the Effects to the interpreter. + // This is only one queue for the prototype. + // In practice it would be one for each interpreter. + RzThreadQueue *il_queue = rz_th_queue_new(RZ_INTERPRETER_IL_QUEUE_SIZE, NULL); if (!il_queue) { return false; } + + // Add the Effect for each entry point. RzILOpEffect *eff = NULL; if (argc == 1) { ut64 entry_point = rz_bin_get_first_entrypoint(core->bin->cur->o); @@ -189,6 +197,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { RZ_LOG_WARN("Could not get entry point IL operation at 0x%" PFMT64x "\n", (ut64)entry_point); return false; } + rz_th_queue_push(il_queue, eff, true); + rz_pvector_push(il_cache, eff); } else { // Add all entry points given as arguments. for (size_t i = 1; i < argc; i++) { @@ -199,21 +209,36 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { return false; } } + rz_th_queue_push(il_queue, eff, true); + rz_pvector_push(il_cache, eff); } - rz_th_queue_push(il_queue, eff, true); + // The address queue. It is the queue the interpreter can request new Effects. + // Of course, currently there is only a single one for the prototype. + // In practice there would be one for each interpreter instance. RzThreadQueue *addr_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, (RzListFree)rz_interpreter_addr_queue_free); if (!addr_queue) { rz_th_queue_free(il_queue); + rz_pvector_free(il_cache); return false; } + + // Here we build the filter for the yield queue. + // The prototype generates constant xrefs. + // So the filter checks the generated xrefs, if they are within the IO map + // boundaries. RzInterval iv = { .addr = 0, .size = UT64_MAX }; RzList *boundaries = rz_io_get_boundaries_all_io_maps(core->io, iv); if (!boundaries) { rz_th_queue_free(addr_queue); rz_th_queue_free(il_queue); + rz_pvector_free(il_cache); return false; } + + // Now create a set of yield queue(s). + // These yield queues can be shared between different interpreters. + // So we have one yield queue for each yield type. RzInterpreterYieldKind yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; RzInterpreterYieldQueue *yield_queue = rz_interpreter_yield_queue_new( yield_kind, @@ -222,30 +247,45 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { if (!yield_queue) { rz_th_queue_free(addr_queue); rz_th_queue_free(il_queue); + rz_pvector_free(il_cache); return false; } + + // Multiple yield queus can be used by a single interpreter. + // E.g. if the interpreter has a complex abstract memory model + // for stack, heap and constant values. + // Then it can produce three kind of yields. HtUP *yield_queues = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_yield_queue_free); if (!yield_queue || !yield_queues) { rz_th_queue_free(addr_queue); rz_th_queue_free(il_queue); + rz_pvector_free(il_cache); rz_interpreter_yield_queue_free(yield_queue); ht_up_free(yield_queues); return false; } ht_up_insert(yield_queues, yield_kind, yield_queue); + // Create the running flag. RzAtomicBool *is_running = rz_atomic_bool_new(true); + + // Bundle all the queues into one object to pass it to the thread. + // Later we would pass a unique qset to each interpreter with + // the required queues only. + // But for the prototype we have only one qset with all queues. RzInterpreterQueueSet *qset = rz_interpreter_queue_set_new(addr_queue, il_queue, yield_queues, is_running); if (!qset) { rz_th_queue_free(addr_queue); rz_th_queue_free(il_queue); + rz_pvector_free(il_cache); rz_interpreter_yield_queue_free(yield_queue); ht_up_free(yield_queues); rz_atomic_bool_free(is_running); return false; } - // Dispatch interpreter into thread - // and do a busy loop on the queue for now + // Dispatch prototype interpreter into a thread. + // This part plays the role of the cache now. + // Waiting for new Effects to be requested and sending them. RZ_LOG_WARN("INQUIRY: Start thread.\n"); RzThread *iterpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, qset); RZ_LOG_WARN("INQUIRY: Start loop.\n"); @@ -266,12 +306,15 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { continue; } RZ_LOG_WARN("INQUIRY: Send %p.\n", bb); + rz_pvector_push(il_cache, bb); rz_th_queue_push(il_queue, bb, true); } // Wait for thread to finish before cleaning. rz_th_wait(iterpr_th); rz_th_free(iterpr_th); rz_interpreter_queue_set_free(qset); + // Empty pseudo-cache. + rz_pvector_free(il_cache); return true; } diff --git a/librz/inquiry/p/inquiry_interpreter_prototype.c b/librz/inquiry/p/inquiry_interpreter_prototype.c index bf7cbcf0d43..b55322def29 100644 --- a/librz/inquiry/p/inquiry_interpreter_prototype.c +++ b/librz/inquiry/p/inquiry_interpreter_prototype.c @@ -4,7 +4,7 @@ #include bool eval(RZ_NONNULL RZ_BORROW RzInterpreterAbstrState *state, - RZ_NONNULL RZ_BORROW RzILOpEffect *effect, + RZ_NONNULL const RzILOpEffect *effect, RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues) { RZ_LOG_WARN("Hello from Protoype eval.\n"); return true; From e951bf0bc7be1f2bc46749f4e7cc4215727c0539 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 15 Nov 2025 18:59:26 +0100 Subject: [PATCH 018/334] Randomize sleep time. --- librz/inquiry/interpreter/interpreter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 69783109771..ad1cdf2c607 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -293,7 +293,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { ut64 *addr = rz_th_queue_pop(addr_queue, false); if (!addr) { // Some artifical lag for testing. - rz_sys_sleep(1); + rz_sys_usleep(rz_num_rand32(1000)); RZ_LOG_WARN("INQUIRY: Sleep over.\n"); continue; } From 449b164b62a18a0a27657cf945e4e37d72a4d027 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 15 Nov 2025 22:51:27 +0100 Subject: [PATCH 019/334] Refactor to isolate interpretator into its own module. --- librz/include/rz_inquiry.h | 23 +- librz/include/rz_inquiry/rz_interpreter.h | 47 +++- librz/inquiry/inquiry.c | 156 ++++++++++++- librz/inquiry/interpreter/interpreter.c | 212 +++--------------- librz/inquiry/interpreter/meson.build | 53 +++++ .../p/interpreter_prototype.c} | 7 +- librz/inquiry/meson.build | 6 +- 7 files changed, 290 insertions(+), 214 deletions(-) create mode 100644 librz/inquiry/interpreter/meson.build rename librz/inquiry/{p/inquiry_interpreter_prototype.c => interpreter/p/interpreter_prototype.c} (85%) diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 5918fc61660..885aaf91909 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -15,29 +15,8 @@ extern "C" { #include -typedef struct { - const char *name; - const char *author; - const char *version; - const char *desc; - const char *license; - /** - * \brief Supported abstractions. Multiple flags can be set. - */ - RzInterpreterAbstraction supported_abstractions; - /** - * \brief The yield type this interpreter generates. - */ - RzInterpreterYieldKind supported_yields; - bool (*init)(void **plugin_data); - bool (*fini)(void *plugin_data); - bool (*eval)(RZ_NONNULL RZ_BORROW RzInterpreterAbstrState *state, - RZ_NONNULL const RzILOpEffect *effect, - RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues); -} RzInquiryInterpreterPlugin; - typedef struct rz_inquiry_plugin_t { - RzInquiryInterpreterPlugin *p_interpreter; + RzInterpreterPlugin *p_interpreter; // RzInquiryAlgorithm *p_algorithm; } RzInquiryPlugin; diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 1e292d320af..e1d2f2e1e90 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -81,6 +81,43 @@ typedef struct { RzThreadQueue /**/ *yield_queue; } RzInterpreterYieldQueue; +typedef struct { + const char *name; + const char *author; + const char *version; + const char *desc; + const char *license; + /** + * \brief Supported abstractions. Multiple flags can be set. + */ + RzInterpreterAbstraction supported_abstractions; + /** + * \brief The yield type this interpreter generates. + */ + RzInterpreterYieldKind supported_yields; + bool (*init)(void **plugin_data); + bool (*fini)(void *plugin_data); + /** + * \brief Evaluates an effect with the mutable state. + */ + bool (*eval)(RZ_NONNULL RZ_BORROW RzInterpreterAbstrState *state, + RZ_NONNULL const RzILOpEffect *effect, + RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, + void *plugin_data); + /** + * \brief Hashes the state. + */ + ut64 (*hash_state)(RZ_NONNULL const RzInterpreterAbstrState *state, + void *plugin_data); + /** + * \brief Determines the next successor addresses from state. + */ + bool (*successors)(RZ_NONNULL const RzInterpreterAbstrState *state, + RZ_OUT ut64 *addr_arr, + size_t addr_array_size, + void *plugin_data); +} RzInterpreterPlugin; + /** * \brief The set of required queues for an interpreter to run. */ @@ -90,7 +127,8 @@ typedef struct { // TODO: We need to decide how to distribute the yield. HtUP /**/ *yield_queues; ///< The queues to push the yield of interpretation into. RzAtomicBool *is_running_flag; ///< Flag for the interpreter thread to toggle when done. -} RzInterpreterQueueSet; + RzInterpreterPlugin *plugin; +} RzInterpreterSet; RZ_API void rz_interpreter_il_queue_free(RZ_OWN RZ_NULLABLE RzThreadQueue /**/ *q); RZ_API void rz_interpreter_addr_queue_free(RZ_OWN RZ_NULLABLE RzThreadQueue /**/ *q); @@ -100,13 +138,14 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre const RzInterpreterYieldFilter *filter, RZ_OWN RZ_NULLABLE void *filter_data); -RZ_API RZ_OWN RzInterpreterQueueSet *rz_interpreter_queue_set_new( +RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( + RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *addr_queue, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag); -RZ_API void rz_interpreter_queue_set_free(RZ_NULLABLE RZ_OWN RzInterpreterQueueSet *qset); +RZ_API void rz_interpreter_queue_set_free(RZ_NULLABLE RZ_OWN RzInterpreterSet *qset); -RZ_API bool rz_interpreter_run(RZ_OWN RZ_NONNULL RzInterpreterQueueSet *queue_set); +RZ_API bool rz_interpreter_run(RZ_BORROW RZ_NONNULL RzInterpreterSet *queue_set); #endif // RZ_INTERPRETER diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 5f25e05f272..358bd0a4b1b 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -1,13 +1,14 @@ // SPDX-FileCopyrightText: 2025 RizinOrg // SPDX-License-Identifier: LGPL-3.0-only +#include #include #include #include "rz_inquiry_plugins.h" -#include "rz_list.h" -#include "rz_types_base.h" -#include "rz_util/rz_assert.h" +#include +#include +#include RZ_LIB_VERSION(rz_inquiry); @@ -60,3 +61,152 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(const RzAnalysisXRef *xref, const } return false; } + +/** + * A function to call the prototype interpreter. + * Usually these tasks will be split between different caches and yield consumers. + */ +RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { + // The pseudo cache of IL effects. + // This is only a vector so we can simulate the ownership separation + // of the pointers. + RzPVector *il_cache = rz_pvector_new((RzPVectorFree)rz_il_op_effect_free); + // The queue to pass the Effects to the interpreter. + // This is only one queue for the prototype. + // In practice it would be one for each interpreter. + RzThreadQueue *il_queue = rz_th_queue_new(RZ_INTERPRETER_IL_QUEUE_SIZE, NULL); + if (!il_queue) { + return false; + } + + // Add the Effect for each entry point. + RzILOpEffect *eff = NULL; + if (argc == 1) { + ut64 entry_point = rz_bin_get_first_entrypoint(core->bin->cur->o); + eff = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); + if (!eff) { + rz_th_queue_free(il_queue); + RZ_LOG_WARN("Could not get entry point IL operation at 0x%" PFMT64x "\n", (ut64)entry_point); + return false; + } + rz_th_queue_push(il_queue, eff, true); + rz_pvector_push(il_cache, eff); + } else { + // Add all entry points given as arguments. + for (size_t i = 1; i < argc; i++) { + ut64 entry_point = rz_num_get(core->num, argv[i]); + eff = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); + if (!eff) { + rz_th_queue_free(il_queue); + return false; + } + } + rz_th_queue_push(il_queue, eff, true); + rz_pvector_push(il_cache, eff); + } + + // The address queue. It is the queue the interpreter can request new Effects. + // Of course, currently there is only a single one for the prototype. + // In practice there would be one for each interpreter instance. + RzThreadQueue *addr_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, (RzListFree)rz_interpreter_addr_queue_free); + if (!addr_queue) { + rz_th_queue_free(il_queue); + rz_pvector_free(il_cache); + return false; + } + + // Here we build the filter for the yield queue. + // The prototype generates constant xrefs. + // So the filter checks the generated xrefs, if they are within the IO map + // boundaries. + RzInterval iv = { .addr = 0, .size = UT64_MAX }; + RzList *boundaries = rz_io_get_boundaries_all_io_maps(core->io, iv); + if (!boundaries) { + rz_th_queue_free(addr_queue); + rz_th_queue_free(il_queue); + rz_pvector_free(il_cache); + return false; + } + + // Now create a set of yield queue(s). + // These yield queues can be shared between different interpreters. + // So we have one yield queue for each yield type. + RzInterpreterYieldKind yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; + RzInterpreterYieldQueue *yield_queue = rz_interpreter_yield_queue_new( + yield_kind, + (RzInterpreterYieldFilter *)rz_inquiry_xref_interpreter_filter, + boundaries); + if (!yield_queue) { + rz_th_queue_free(addr_queue); + rz_th_queue_free(il_queue); + rz_pvector_free(il_cache); + return false; + } + + // Multiple yield queues can be used by a single interpreter. + // E.g. if the interpreter has a complex abstract memory model + // for stack, heap and constant values. + // Then it can produce three kind of yields. + HtUP *yield_queues = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_yield_queue_free); + if (!yield_queue || !yield_queues) { + rz_th_queue_free(addr_queue); + rz_th_queue_free(il_queue); + rz_pvector_free(il_cache); + rz_interpreter_yield_queue_free(yield_queue); + ht_up_free(yield_queues); + return false; + } + ht_up_insert(yield_queues, yield_kind, yield_queue); + // Create the running flag. + RzAtomicBool *is_running = rz_atomic_bool_new(true); + + // Bundle all the queues into one object to pass it to the thread. + // Later we would pass a unique iset to each interpreter with + // the required queues only. + // But for the prototype we have only one iset with all queues. + RzInterpreterSet *iset = rz_interpreter_set_new(rz_inquiry_plugin_interpreter_prototype.p_interpreter, addr_queue, il_queue, yield_queues, is_running); + if (!iset) { + rz_th_queue_free(addr_queue); + rz_th_queue_free(il_queue); + rz_pvector_free(il_cache); + rz_interpreter_yield_queue_free(yield_queue); + ht_up_free(yield_queues); + rz_atomic_bool_free(is_running); + return false; + } + + // Dispatch prototype interpreter into a thread. + // This part plays the role of the cache now. + // Waiting for new Effects to be requested and sending them. + RZ_LOG_WARN("INQUIRY: Start main interpretation thread.\n"); + RzThread *iterpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); + RZ_LOG_WARN("INQUIRY: Start IL providing loop.\n"); + while (rz_atomic_bool_get(is_running)) { + ut64 *addr = rz_th_queue_pop(addr_queue, false); + if (!addr) { + // Some artificial lag for testing. + rz_sys_usleep(rz_num_rand32(1000)); + RZ_LOG_WARN("INQUIRY: Sleep over.\n"); + continue; + } + RZ_LOG_WARN("INQUIRY: Received %" PFMT64x ".\n", (*addr)); + RzILOpEffect *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); + free(addr); + if (!bb) { + // Stop interpreter. + rz_atomic_bool_set(is_running, false); + continue; + } + RZ_LOG_WARN("INQUIRY: Send %p.\n", bb); + rz_pvector_push(il_cache, bb); + rz_th_queue_push(il_queue, bb, true); + } + // Wait for thread to finish before cleaning. + rz_th_wait(iterpr_th); + rz_th_free(iterpr_th); + rz_interpreter_queue_set_free(iset); + // Empty pseudo-cache. + rz_pvector_free(il_cache); + + return true; +} diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index ad1cdf2c607..b7bbf402a56 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -5,12 +5,10 @@ * \file The API implementation for all analysis interpreters. */ -#include #include #include #include #include -#include RZ_API void rz_interpreter_il_queue_free(RZ_OWN RZ_NULLABLE RzThreadQueue /**/ *q) { if (!q) { @@ -96,19 +94,21 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre } /** - * \brief Initializes a new RzInterpreterQueueSet and returns it. + * \brief Initializes a new RzInterpreterSet and returns it. * If it fails, all arguments are freed. * + * \param plugin The interpreter plugin. * \param request_il The request queue. * \param receive_il The receive queue. * \param yield_queues The yield queues. */ -RZ_API RZ_OWN RzInterpreterQueueSet *rz_interpreter_queue_set_new( +RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( + RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *addr_queue, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag) { - RzInterpreterQueueSet *set = RZ_NEW0(RzInterpreterQueueSet); + RzInterpreterSet *set = RZ_NEW0(RzInterpreterSet); if (!set) { rz_th_queue_free(addr_queue); rz_th_queue_free(il_queue); @@ -123,198 +123,50 @@ RZ_API RZ_OWN RzInterpreterQueueSet *rz_interpreter_queue_set_new( return set; } -RZ_API void rz_interpreter_queue_set_free(RZ_NULLABLE RZ_OWN RzInterpreterQueueSet *qset) { - if (!qset) { +RZ_API void rz_interpreter_queue_set_free(RZ_NULLABLE RZ_OWN RzInterpreterSet *iset) { + if (!iset) { return; } - if (qset->addr_queue) { - rz_th_queue_free(qset->addr_queue); + if (iset->addr_queue) { + rz_th_queue_free(iset->addr_queue); } - if (qset->il_queue) { - rz_th_queue_free(qset->il_queue); + if (iset->il_queue) { + rz_th_queue_free(iset->il_queue); } - if (qset->yield_queues) { - ht_up_free(qset->yield_queues); + if (iset->yield_queues) { + ht_up_free(iset->yield_queues); } - if (qset->is_running_flag) { - rz_atomic_bool_free(qset->is_running_flag); + if (iset->is_running_flag) { + rz_atomic_bool_free(iset->is_running_flag); } - free(qset); + free(iset); } /** - * \brief Runs a set of interpreters to inquire the requested yield. - * What interpreters are spawned depend on the queues in \p yield_queues. + * \brief Runs the interpretation with a single interpreter plugin. */ -RZ_API bool rz_interpreter_run(RZ_OWN RzInterpreterQueueSet *qset) { - RZ_LOG_WARN("INTERPRETER: Hello.\n"); - RzThreadQueue *il_queue = qset->il_queue; - RzThreadQueue *addr_queue = qset->addr_queue; +static bool perform_interpretation(RzInterpreterPlugin *plugin, RZ_BORROW RzInterpreterSet *iset) { + RzThreadQueue *il_queue = iset->il_queue; + RzThreadQueue *addr_queue = iset->addr_queue; // The effect is owned by the cache. So it is constant. const RzILOpEffect *effect = rz_th_queue_pop(il_queue, false); - RZ_LOG_WARN("INTERPRETER: Got IL op: %p\n", effect); - - ut64 *a = RZ_NEW(ut64); - *a = 0x08000080ul; - rz_th_queue_push(addr_queue, a, true); - RZ_LOG_WARN("INTERPRETER: Requested: %" PRIx64 "\n", 0x08000080ul); - - effect = rz_th_queue_pop(il_queue, true); - while (rz_atomic_bool_get(qset->is_running_flag) && !effect) { - effect = rz_th_queue_pop(il_queue, true); + RZ_LOG_WARN("INTERPRETER Instance: Got IL op: %p\n", effect); + if (effect) { + // No entry point given. + RZ_LOG_WARN("INTERPRETER Instance: No entry point.\n"); + return false; } - RZ_LOG_WARN("INTERPRETER: Got IL op: %p\n", effect); - - rz_atomic_bool_set(qset->is_running_flag, false); return true; } /** - * A function to call the prototype interpreter. - * Usually these tasks will be split between different caches and yield consumers. + * Main thread of the interpretation. */ -RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { - // The pseudo cache of IL effects. - // This is only a vector so we can simulate the ownership separation - // of the pointers. - RzPVector *il_cache = rz_pvector_new((RzPVectorFree)rz_il_op_effect_free); - // The queue to pass the Effects to the interpreter. - // This is only one queue for the prototype. - // In practice it would be one for each interpreter. - RzThreadQueue *il_queue = rz_th_queue_new(RZ_INTERPRETER_IL_QUEUE_SIZE, NULL); - if (!il_queue) { - return false; - } - - // Add the Effect for each entry point. - RzILOpEffect *eff = NULL; - if (argc == 1) { - ut64 entry_point = rz_bin_get_first_entrypoint(core->bin->cur->o); - eff = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); - if (!eff) { - rz_th_queue_free(il_queue); - RZ_LOG_WARN("Could not get entry point IL operation at 0x%" PFMT64x "\n", (ut64)entry_point); - return false; - } - rz_th_queue_push(il_queue, eff, true); - rz_pvector_push(il_cache, eff); - } else { - // Add all entry points given as arguments. - for (size_t i = 1; i < argc; i++) { - ut64 entry_point = rz_num_get(core->num, argv[i]); - eff = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); - if (!eff) { - rz_th_queue_free(il_queue); - return false; - } - } - rz_th_queue_push(il_queue, eff, true); - rz_pvector_push(il_cache, eff); - } - - // The address queue. It is the queue the interpreter can request new Effects. - // Of course, currently there is only a single one for the prototype. - // In practice there would be one for each interpreter instance. - RzThreadQueue *addr_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, (RzListFree)rz_interpreter_addr_queue_free); - if (!addr_queue) { - rz_th_queue_free(il_queue); - rz_pvector_free(il_cache); - return false; - } - - // Here we build the filter for the yield queue. - // The prototype generates constant xrefs. - // So the filter checks the generated xrefs, if they are within the IO map - // boundaries. - RzInterval iv = { .addr = 0, .size = UT64_MAX }; - RzList *boundaries = rz_io_get_boundaries_all_io_maps(core->io, iv); - if (!boundaries) { - rz_th_queue_free(addr_queue); - rz_th_queue_free(il_queue); - rz_pvector_free(il_cache); - return false; - } - - // Now create a set of yield queue(s). - // These yield queues can be shared between different interpreters. - // So we have one yield queue for each yield type. - RzInterpreterYieldKind yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; - RzInterpreterYieldQueue *yield_queue = rz_interpreter_yield_queue_new( - yield_kind, - (RzInterpreterYieldFilter *)rz_inquiry_xref_interpreter_filter, - boundaries); - if (!yield_queue) { - rz_th_queue_free(addr_queue); - rz_th_queue_free(il_queue); - rz_pvector_free(il_cache); - return false; - } - - // Multiple yield queus can be used by a single interpreter. - // E.g. if the interpreter has a complex abstract memory model - // for stack, heap and constant values. - // Then it can produce three kind of yields. - HtUP *yield_queues = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_yield_queue_free); - if (!yield_queue || !yield_queues) { - rz_th_queue_free(addr_queue); - rz_th_queue_free(il_queue); - rz_pvector_free(il_cache); - rz_interpreter_yield_queue_free(yield_queue); - ht_up_free(yield_queues); - return false; - } - ht_up_insert(yield_queues, yield_kind, yield_queue); - // Create the running flag. - RzAtomicBool *is_running = rz_atomic_bool_new(true); - - // Bundle all the queues into one object to pass it to the thread. - // Later we would pass a unique qset to each interpreter with - // the required queues only. - // But for the prototype we have only one qset with all queues. - RzInterpreterQueueSet *qset = rz_interpreter_queue_set_new(addr_queue, il_queue, yield_queues, is_running); - if (!qset) { - rz_th_queue_free(addr_queue); - rz_th_queue_free(il_queue); - rz_pvector_free(il_cache); - rz_interpreter_yield_queue_free(yield_queue); - ht_up_free(yield_queues); - rz_atomic_bool_free(is_running); - return false; - } - - // Dispatch prototype interpreter into a thread. - // This part plays the role of the cache now. - // Waiting for new Effects to be requested and sending them. - RZ_LOG_WARN("INQUIRY: Start thread.\n"); - RzThread *iterpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, qset); - RZ_LOG_WARN("INQUIRY: Start loop.\n"); - while (rz_atomic_bool_get(is_running)) { - ut64 *addr = rz_th_queue_pop(addr_queue, false); - if (!addr) { - // Some artifical lag for testing. - rz_sys_usleep(rz_num_rand32(1000)); - RZ_LOG_WARN("INQUIRY: Sleep over.\n"); - continue; - } - RZ_LOG_WARN("INQUIRY: Received %" PFMT64x ".\n", (*addr)); - RzILOpEffect *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); - free(addr); - if (!bb) { - // Stop interpreter. - rz_atomic_bool_set(is_running, false); - continue; - } - RZ_LOG_WARN("INQUIRY: Send %p.\n", bb); - rz_pvector_push(il_cache, bb); - rz_th_queue_push(il_queue, bb, true); - } - // Wait for thread to finish before cleaning. - rz_th_wait(iterpr_th); - rz_th_free(iterpr_th); - rz_interpreter_queue_set_free(qset); - // Empty pseudo-cache. - rz_pvector_free(il_cache); - - return true; +RZ_API bool rz_interpreter_run(RZ_BORROW RzInterpreterSet *iset) { + RZ_LOG_WARN("INTERPRETER Main: Hello.\n"); + // This can be the place to spawn multiple interpreters in threads. + bool result = perform_interpretation(iset->plugin, iset); + rz_atomic_bool_set(iset->is_running_flag, false); + return result; } diff --git a/librz/inquiry/interpreter/meson.build b/librz/inquiry/interpreter/meson.build new file mode 100644 index 00000000000..972da4a8edc --- /dev/null +++ b/librz/inquiry/interpreter/meson.build @@ -0,0 +1,53 @@ +# SPDX-FileCopyrightText: 2025 RizinOrg +# SPDX-License-Identifier: LGPL-3.0-only + +interpreter_plugins_list = [ + 'prototype', +] + +interpreter_plugins = { + 'base_name': 'rz_interpreter', + 'base_struct': 'RzInterpreterPlugin', + 'list': interpreter_plugins_list, +} + +rz_interpreter_sources = [ + 'interpreter.c', + 'p/interpreter_prototype.c', +] + +rz_interpreter_inc = [platform_inc] + +rz_interpreter = library('rz_interpreter', rz_interpreter_sources, + include_directories: rz_interpreter_inc, + dependencies: [ + rz_cons_dep, + rz_hash_dep, + rz_il_dep, + rz_type_dep, + rz_util_dep, + ], + install: true, + implicit_include_directories: false, + install_rpath: rpath_lib, + soversion: rizin_libversion, + version: rizin_version, + name_suffix: lib_name_suffix, + name_prefix: lib_name_prefix, +) + +rz_interpreter_dep = declare_dependency(link_with: rz_interpreter, + include_directories: rz_interpreter_inc) +meson.override_dependency('rz_interpreter', rz_interpreter_dep) + +modules += { 'rz_interpreter': { + 'target': rz_interpreter, + 'dependencies': [ + 'rz_cons', + 'rz_hash', + 'rz_il', + 'rz_type', + 'rz_util' + ], + 'plugins': [interpreter_plugins] +}} diff --git a/librz/inquiry/p/inquiry_interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c similarity index 85% rename from librz/inquiry/p/inquiry_interpreter_prototype.c rename to librz/inquiry/interpreter/p/interpreter_prototype.c index b55322def29..bab59e46846 100644 --- a/librz/inquiry/p/inquiry_interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -5,12 +5,13 @@ bool eval(RZ_NONNULL RZ_BORROW RzInterpreterAbstrState *state, RZ_NONNULL const RzILOpEffect *effect, - RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues) { + RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, + void *plugin_data) { RZ_LOG_WARN("Hello from Protoype eval.\n"); return true; } -static RzInquiryInterpreterPlugin interpreter_prototype = { +static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .name = "abstr_int_prototype", .author = "Rot127", .version = "0.1p", @@ -24,7 +25,7 @@ static RzInquiryInterpreterPlugin interpreter_prototype = { }; RZ_API RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype = { - .p_interpreter = &interpreter_prototype, + .p_interpreter = &rz_interpreter_plugin_prototype, }; #ifndef RZ_PLUGIN_INCORE diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index 51e0452a569..ff1a2f9b4b7 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -14,10 +14,10 @@ inquiry_plugins = { rz_inquiry_sources = [ 'inquiry.c', 'inquiry_helpers.c', - 'interpreter/interpreter.c', - 'p/inquiry_interpreter_prototype.c', ] +subdir('interpreter') + rz_inquiry_inc = [platform_inc] rz_inquiry = library('rz_inquiry', rz_inquiry_sources, @@ -28,6 +28,7 @@ rz_inquiry = library('rz_inquiry', rz_inquiry_sources, rz_cons_dep, rz_hash_dep, rz_il_dep, + rz_interpreter_dep, rz_io_dep, rz_magic_dep, rz_search_dep, @@ -56,6 +57,7 @@ modules += { 'rz_inquiry': { 'rz_cons', 'rz_hash', 'rz_il', + 'rz_interpreter', 'rz_io', 'rz_magic', 'rz_search', From 827a85d53f52d18f4f3830f06f074d2cc0fa5393 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sun, 16 Nov 2025 01:06:23 +0100 Subject: [PATCH 020/334] Initialize abstract state in iset. --- librz/include/rz_inquiry/rz_interpreter.h | 27 ++++- librz/inquiry/inquiry.c | 36 ++++++- librz/inquiry/interpreter/interpreter.c | 99 ++++++++++++++----- .../interpreter/p/interpreter_prototype.c | 2 +- librz/inquiry/meson.build | 2 + 5 files changed, 136 insertions(+), 30 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index e1d2f2e1e90..14c553b5576 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -23,6 +23,10 @@ * \brief The abstractions this module supports. */ typedef enum { + /** + * \brief An undefined abstracted value. + */ + RZ_INTERPRETER_ABSTRACTION_UNDEF = 0, /** * \brief Value abstraction into constant and bottom values. */ @@ -38,17 +42,17 @@ typedef enum { } RzInterpreterAbstraction; /** - * \brief An abitrary abstract value. + * \brief An arbitrary abstract value. */ typedef struct { RzInterpreterAbstraction kind; ///< The abstraction of the value. - void *abstr_data; + void *abstr_data; ///< The abstract data. It is managed by individual interpreter. } RzInterpreterAbstrVal; typedef struct { RzInterpreterAbstraction kinds; ///< The abstractions of the state. - HtSP *reg_map; ///< The register file. Currently a hashmap. - void *state; + HtSP /**/ *reg_map; ///< The register file. Currently a hash map. + void *prop; ///< Optional state properties. Managed by indiviual interpreters. } RzInterpreterAbstrState; typedef enum { @@ -57,7 +61,7 @@ typedef enum { /** * \brief A yield of an interpreter. Type is implied by the queue. - * Object is a union so the elements pushed over the queu are small. + * Object is a union so the elements pushed over the queue are small. */ typedef union { RzAnalysisXRef *abstr_const; @@ -97,6 +101,14 @@ typedef struct { RzInterpreterYieldKind supported_yields; bool (*init)(void **plugin_data); bool (*fini)(void *plugin_data); + /** + * \brief Initializes the abstract state. + */ + bool (*init_state)(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data); + /** + * \brief Closes the abstract state and frees all its abstract data. + */ + bool (*fini_state)(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data); /** * \brief Evaluates an effect with the mutable state. */ @@ -122,6 +134,7 @@ typedef struct { * \brief The set of required queues for an interpreter to run. */ typedef struct { + RzInterpreterAbstrState *state; ///< The abstract state of the interpreter. RzThreadQueue /**/ *addr_queue; ///< The queue to send requests to the cache what address to get the next IL op from. RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. // TODO: We need to decide how to distribute the yield. @@ -134,12 +147,16 @@ RZ_API void rz_interpreter_il_queue_free(RZ_OWN RZ_NULLABLE RzThreadQueue /**/ *q); RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldQueue *yield_queue); +RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new(RzInterpreterAbstraction kinds, RZ_NULLABLE const RzPVector *reg_names); +RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbstrState *state); + RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpreterYieldKind kind, const RzInterpreterYieldFilter *filter, RZ_OWN RZ_NULLABLE void *filter_data); RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, + RZ_NONNULL RZ_OWN RzInterpreterAbstrState *state, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *addr_queue, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 358bd0a4b1b..4359ca383f9 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -5,7 +5,11 @@ #include #include +#include "rz_analysis.h" +#include "rz_inquiry/rz_interpreter.h" #include "rz_inquiry_plugins.h" +#include "rz_reg.h" +#include "rz_vector.h" #include #include #include @@ -62,6 +66,31 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(const RzAnalysisXRef *xref, const return false; } +static RzPVector *get_reg_names(RzAnalysis *analysis) { + RzPVector *reg_names = rz_pvector_new(free); + if (analysis->cur->il_config) { + RzAnalysisILConfig *config = analysis->cur->il_config(analysis); + if (config->reg_bindings) { + for (size_t i = 0; config->reg_bindings[i]; i++) { + rz_pvector_push(reg_names, rz_str_dup(config->reg_bindings[i])); + } + rz_analysis_il_config_free(config); + return reg_names; + } + rz_analysis_il_config_free(config); + } + const RzList *regs = rz_reg_get_list(analysis->reg, RZ_REG_TYPE_ANY); + if (!regs) { + return NULL; + } + RzRegItem *reg; + RzListIter *iter; + rz_list_foreach (regs, iter, reg) { + rz_pvector_push(reg_names, rz_str_dup(reg->name)); + } + return reg_names; +} + /** * A function to call the prototype interpreter. * Usually these tasks will be split between different caches and yield consumers. @@ -160,11 +189,16 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // Create the running flag. RzAtomicBool *is_running = rz_atomic_bool_new(true); + // Initialize the abstract state with the architecture's registers. + RzPVector *reg_names = get_reg_names(core->analysis); + RzInterpreterAbstrState *abstr_state = rz_interpreter_abstr_state_new(RZ_INTERPRETER_ABSTRACTION_CONST, reg_names); + rz_pvector_free(reg_names); + // Bundle all the queues into one object to pass it to the thread. // Later we would pass a unique iset to each interpreter with // the required queues only. // But for the prototype we have only one iset with all queues. - RzInterpreterSet *iset = rz_interpreter_set_new(rz_inquiry_plugin_interpreter_prototype.p_interpreter, addr_queue, il_queue, yield_queues, is_running); + RzInterpreterSet *iset = rz_interpreter_set_new(rz_inquiry_plugin_interpreter_prototype.p_interpreter, abstr_state, addr_queue, il_queue, yield_queues, is_running); if (!iset) { rz_th_queue_free(addr_queue); rz_th_queue_free(il_queue); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index b7bbf402a56..b8c31cf7f36 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -8,7 +8,9 @@ #include #include #include +#include #include +#include RZ_API void rz_interpreter_il_queue_free(RZ_OWN RZ_NULLABLE RzThreadQueue /**/ *q) { if (!q) { @@ -93,6 +95,47 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre return yield_queue; } +/** + * \brief Initializes an abstract state for specified abstract kinds. Optionally with a list of registers. + * The register name list should always be given if the architecture has some. + */ +RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new(RzInterpreterAbstraction kinds, RZ_NULLABLE const RzPVector *reg_names) { + RzInterpreterAbstrState *state = RZ_NEW0(RzInterpreterAbstrState); + if (!state) { + return NULL; + } + state->kinds = kinds; + if (!reg_names) { + return state; + } + // Initialize the register file with uninitialized abstract values. + state->reg_map = ht_sp_new(HT_STR_DUP, NULL, free); + void **it; + rz_pvector_foreach (reg_names, it) { + const char *rname = *it; + RzInterpreterAbstrVal *aval = RZ_NEW0(RzInterpreterAbstrVal); + if (!aval) { + ht_sp_free(state->reg_map); + free(state); + return NULL; + } + + aval->kind = RZ_INTERPRETER_ABSTRACTION_UNDEF; + ht_sp_insert(state->reg_map, rname, aval); + } + return state; +} + +RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbstrState *state) { + if (!state) { + return; + } + if (state->reg_map) { + ht_sp_free(state->reg_map); + } + free(state); +} + /** * \brief Initializes a new RzInterpreterSet and returns it. * If it fails, all arguments are freed. @@ -104,18 +147,25 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre */ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, + RZ_NONNULL RZ_OWN RzInterpreterAbstrState *state, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *addr_queue, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag) { + rz_return_val_if_fail(plugin && state && plugin && addr_queue && il_queue && yield_queues, NULL); + RzInterpreterSet *set = RZ_NEW0(RzInterpreterSet); - if (!set) { + if (!set || (state->kinds != (plugin->supported_abstractions & state->kinds))) { + if ((state->kinds != (plugin->supported_abstractions & state->kinds))) { + RZ_LOG_ERROR("Abstract state doesn't fit to interpreter.\n"); + } rz_th_queue_free(addr_queue); rz_th_queue_free(il_queue); ht_up_free(yield_queues); rz_atomic_bool_free(is_running_flag); return NULL; } + set->state = state; set->il_queue = il_queue; set->addr_queue = addr_queue; set->yield_queues = yield_queues; @@ -127,6 +177,9 @@ RZ_API void rz_interpreter_queue_set_free(RZ_NULLABLE RZ_OWN RzInterpreterSet *i if (!iset) { return; } + if (iset->state) { + rz_interpreter_abstr_state_free(iset->state); + } if (iset->addr_queue) { rz_th_queue_free(iset->addr_queue); } @@ -143,30 +196,30 @@ RZ_API void rz_interpreter_queue_set_free(RZ_NULLABLE RZ_OWN RzInterpreterSet *i } /** - * \brief Runs the interpretation with a single interpreter plugin. + * Main interpretation. */ -static bool perform_interpretation(RzInterpreterPlugin *plugin, RZ_BORROW RzInterpreterSet *iset) { - RzThreadQueue *il_queue = iset->il_queue; - RzThreadQueue *addr_queue = iset->addr_queue; - - // The effect is owned by the cache. So it is constant. - const RzILOpEffect *effect = rz_th_queue_pop(il_queue, false); - RZ_LOG_WARN("INTERPRETER Instance: Got IL op: %p\n", effect); - if (effect) { - // No entry point given. - RZ_LOG_WARN("INTERPRETER Instance: No entry point.\n"); - return false; - } - return true; -} - -/** - * Main thread of the interpretation. - */ -RZ_API bool rz_interpreter_run(RZ_BORROW RzInterpreterSet *iset) { +RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_BORROW RzInterpreterSet *iset) { + rz_return_val_if_fail(iset && + iset->addr_queue && + iset->il_queue && + iset->yield_queues && + iset->is_running_flag && + iset->plugin && + iset->plugin->init_state && + iset->plugin->eval && + iset->plugin->hash_state, + false); RZ_LOG_WARN("INTERPRETER Main: Hello.\n"); - // This can be the place to spawn multiple interpreters in threads. - bool result = perform_interpretation(iset->plugin, iset); + + bool result = false; + void **plugin_data = NULL; + if (iset->plugin->init) { + iset->plugin->init(plugin_data); + } + rz_atomic_bool_set(iset->is_running_flag, true); rz_atomic_bool_set(iset->is_running_flag, false); + if (iset->plugin->fini) { + iset->plugin->fini(plugin_data); + } return result; } diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index bab59e46846..b6f84bf78f6 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -21,7 +21,7 @@ static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .supported_yields = RZ_INTERPRETER_YIELD_KIND_XREF, .init = NULL, .fini = NULL, - .eval = eval + .eval = eval, }; RZ_API RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype = { diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index ff1a2f9b4b7..0aeef3b7b77 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -31,6 +31,7 @@ rz_inquiry = library('rz_inquiry', rz_inquiry_sources, rz_interpreter_dep, rz_io_dep, rz_magic_dep, + rz_reg_dep, rz_search_dep, rz_syscall_dep, rz_type_dep, @@ -60,6 +61,7 @@ modules += { 'rz_inquiry': { 'rz_interpreter', 'rz_io', 'rz_magic', + 'rz_reg', 'rz_search', 'rz_syscall', 'rz_type', From 386a24573838d24cf2d3747daf93127aeebb8f2d Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 18 Nov 2025 18:45:40 +0100 Subject: [PATCH 021/334] Unfinished interpreter loop. --- librz/include/rz_inquiry/rz_interpreter.h | 11 ++- librz/inquiry/interpreter/interpreter.c | 78 ++++++++++++++++--- .../interpreter/p/interpreter_prototype.c | 39 ++++++++++ 3 files changed, 114 insertions(+), 14 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 14c553b5576..cf45c0db85f 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -51,8 +51,9 @@ typedef struct { typedef struct { RzInterpreterAbstraction kinds; ///< The abstractions of the state. - HtSP /**/ *reg_map; ///< The register file. Currently a hash map. - void *prop; ///< Optional state properties. Managed by indiviual interpreters. + HtSP /**/ *reg_file; ///< The register file. Currently a hash map. + RzInterpreterAbstrVal *pc; ///< In our RzIL implementation, the PC is not part of the register file. + void *prop; ///< Optional state properties. Managed by individual interpreters. } RzInterpreterAbstrState; typedef enum { @@ -123,10 +124,12 @@ typedef struct { void *plugin_data); /** * \brief Determines the next successor addresses from state. + * + * \return Returns false in case of error. The interpretation must abort. + * True otherwise. */ bool (*successors)(RZ_NONNULL const RzInterpreterAbstrState *state, - RZ_OUT ut64 *addr_arr, - size_t addr_array_size, + RZ_NONNULL RZ_OUT RzThreadQueue /**/ *addr_queue, void *plugin_data); } RzInterpreterPlugin; diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index b8c31cf7f36..d9f1c089a47 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -5,6 +5,9 @@ * \file The API implementation for all analysis interpreters. */ +#include "rz_util/ht_up.h" +#include "rz_util/rz_assert.h" +#include "rz_util/rz_set.h" #include #include #include @@ -109,19 +112,19 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new(RzInterpre return state; } // Initialize the register file with uninitialized abstract values. - state->reg_map = ht_sp_new(HT_STR_DUP, NULL, free); + state->reg_file = ht_sp_new(HT_STR_DUP, NULL, free); void **it; rz_pvector_foreach (reg_names, it) { const char *rname = *it; RzInterpreterAbstrVal *aval = RZ_NEW0(RzInterpreterAbstrVal); if (!aval) { - ht_sp_free(state->reg_map); + ht_sp_free(state->reg_file); free(state); return NULL; } aval->kind = RZ_INTERPRETER_ABSTRACTION_UNDEF; - ht_sp_insert(state->reg_map, rname, aval); + ht_sp_insert(state->reg_file, rname, aval); } return state; } @@ -130,8 +133,8 @@ RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbst if (!state) { return; } - if (state->reg_map) { - ht_sp_free(state->reg_map); + if (state->reg_file) { + ht_sp_free(state->reg_file); } free(state); } @@ -209,17 +212,72 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_BORROW RzInterpreterSet *iset) { iset->plugin->eval && iset->plugin->hash_state, false); + bool success = false; + RZ_LOG_WARN("INTERPRETER Main: Hello.\n"); + RzInterpreterPlugin *plugin = iset->plugin; - bool result = false; - void **plugin_data = NULL; + void **priv_ptr = NULL; if (iset->plugin->init) { - iset->plugin->init(plugin_data); + iset->plugin->init(priv_ptr); + } + void *plugin_data = priv_ptr ? *priv_ptr : NULL; + + plugin->init_state(iset->state, plugin_data); + + HtUP *state_map = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_abstr_state_free); + + + RzSetU *reachable_states = rz_set_u_new(); + if (!reachable_states) { + rz_warn_if_reached(); + goto error; } - rz_atomic_bool_set(iset->is_running_flag, true); + ut64 n_states = 0; + ut64 prev_n_states = 0; + while (true) { + const RzILOpEffect *eff = rz_th_queue_wait_pop(iset->il_queue, false); + if (!eff) { + rz_warn_if_reached(); + goto error; + } + + RzInterpreterAbstrState *state = ht_up_find(state_map, plugin->hash_state(state, plugin_data), NULL); + if (!state || !plugin->eval(state, eff, iset->yield_queues, plugin_data)) { + RZ_LOG_ERROR("Interpreter failed to evaluate an effect or state was NULL. Abort.\n"); + RZ_LOG_ERROR("n_states = %" PFMT64d ".\n", n_states); + goto error; + } + + rz_set_u_add(reachable_states, plugin->hash_state(state, plugin_data)); + n_states = rz_set_u_size(reachable_states); + bool reached_new_state = n_states > prev_n_states; + prev_n_states = n_states; + + if (rz_th_queue_size(iset->il_queue) == 0) { + // No effect left to evaluate. + break; + } + if (reached_new_state) { + // Only new states are allowed to add to the request queue. + // Otherwise we might loop indefinitely. + size_t prev_addr_size = rz_th_queue_size(iset->addr_queue); + if (!plugin->successors(state, iset->addr_queue, plugin_data)) { + RZ_LOG_ERROR("Error during PC concretization. Abort.\n"); + goto error; + } + } + } + // while true { + // get new PCs + // request effects + // push effects + // } + +error: rz_atomic_bool_set(iset->is_running_flag, false); if (iset->plugin->fini) { iset->plugin->fini(plugin_data); } - return result; + return success; } diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index b6f84bf78f6..93cc82664db 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -2,6 +2,13 @@ // SPDX-License-Identifier: LGPL-3.0-only #include +#include +#include +#include + +typedef struct { + RzBitVector *concrete; ///< A concrete value. If NULL, it is considered bottom. +} ProtoIntrprAbstrData; bool eval(RZ_NONNULL RZ_BORROW RzInterpreterAbstrState *state, RZ_NONNULL const RzILOpEffect *effect, @@ -11,6 +18,37 @@ bool eval(RZ_NONNULL RZ_BORROW RzInterpreterAbstrState *state, return true; } +bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, + RZ_NONNULL RZ_OUT RzThreadQueue /**/ *addr_queue, + void *plugin_data) { + rz_return_val_if_fail(state && addr_queue, false); + + RzInterpreterAbstrVal *pc = state->pc; + if (!pc || pc->abstr_data) { + RZ_LOG_ERROR("No PC found.\n"); + return false; + } + ProtoIntrprAbstrData *adata = pc->abstr_data; + if (!adata->concrete) { + // The PC is not a concrete value. + // This prototype can't estimate a reasonable concretization for it. + return true; + } + if (rz_bv_len(adata->concrete) > 64) { + RZ_LOG_WARN("PC has a length of more than 64 bits!\n"); + return true; + } + + ut64 *next_pc = RZ_NEW(ut64); + if (!next_pc) { + rz_warn_if_reached(); + return false; + } + *next_pc = rz_bv_to_ut64(adata->concrete); + rz_th_queue_push(addr_queue, next_pc, true); + return true; +} + static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .name = "abstr_int_prototype", .author = "Rot127", @@ -22,6 +60,7 @@ static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .init = NULL, .fini = NULL, .eval = eval, + .successors = successors, }; RZ_API RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype = { From 15c72f34fd6bb58e5804c254bbc8163bf24e7b00 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 21 Nov 2025 17:27:46 +0100 Subject: [PATCH 022/334] Add untested interpreter loop --- librz/include/rz_inquiry/rz_interpreter.h | 20 ++- librz/inquiry/inquiry.c | 1 - librz/inquiry/interpreter/interpreter.c | 163 +++++++++++++----- .../interpreter/p/interpreter_prototype.c | 15 +- 4 files changed, 139 insertions(+), 60 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index cf45c0db85f..0fb418299ac 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -52,7 +52,7 @@ typedef struct { typedef struct { RzInterpreterAbstraction kinds; ///< The abstractions of the state. HtSP /**/ *reg_file; ///< The register file. Currently a hash map. - RzInterpreterAbstrVal *pc; ///< In our RzIL implementation, the PC is not part of the register file. + RzInterpreterAbstrVal *pc; ///< In our RzIL implementation the PC is not part of the register file. void *prop; ///< Optional state properties. Managed by individual interpreters. } RzInterpreterAbstrState; @@ -111,17 +111,21 @@ typedef struct { */ bool (*fini_state)(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data); /** - * \brief Evaluates an effect with the mutable state. + * \brief Clones the abstract state. */ - bool (*eval)(RZ_NONNULL RZ_BORROW RzInterpreterAbstrState *state, - RZ_NONNULL const RzILOpEffect *effect, - RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, - void *plugin_data); + RZ_OWN RzInterpreterAbstrState *(*clone_state)(const RzInterpreterAbstrState *state, void *plugin_data); /** * \brief Hashes the state. */ ut64 (*hash_state)(RZ_NONNULL const RzInterpreterAbstrState *state, void *plugin_data); + /** + * \brief Evaluates an effect with the mutable state. + */ + bool (*eval)(RZ_NONNULL const RzInterpreterAbstrState *state, + RZ_NONNULL const RzILOpEffect *effect, + RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, + void *plugin_data); /** * \brief Determines the next successor addresses from state. * @@ -129,7 +133,7 @@ typedef struct { * True otherwise. */ bool (*successors)(RZ_NONNULL const RzInterpreterAbstrState *state, - RZ_NONNULL RZ_OUT RzThreadQueue /**/ *addr_queue, + RZ_NONNULL RZ_OUT RzVector /**/ *successors, void *plugin_data); } RzInterpreterPlugin; @@ -166,6 +170,6 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag); RZ_API void rz_interpreter_queue_set_free(RZ_NULLABLE RZ_OWN RzInterpreterSet *qset); -RZ_API bool rz_interpreter_run(RZ_BORROW RZ_NONNULL RzInterpreterSet *queue_set); +RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *queue_set); #endif // RZ_INTERPRETER diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 4359ca383f9..e7f711b5343 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -238,7 +238,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // Wait for thread to finish before cleaning. rz_th_wait(iterpr_th); rz_th_free(iterpr_th); - rz_interpreter_queue_set_free(iset); // Empty pseudo-cache. rz_pvector_free(il_cache); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index d9f1c089a47..8952f659e5f 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -6,8 +6,10 @@ */ #include "rz_util/ht_up.h" -#include "rz_util/rz_assert.h" +#include "rz_util/rz_iterator.h" #include "rz_util/rz_set.h" +#include "rz_util/rz_th_ht.h" +#include "subprojects/rzheap/rz_jemalloc/internal/jemalloc_internal.h" #include #include #include @@ -111,6 +113,10 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new(RzInterpre if (!reg_names) { return state; } + state->pc = RZ_NEW0(RzInterpreterAbstrVal); + if (!state->pc) { + return NULL; + } // Initialize the register file with uninitialized abstract values. state->reg_file = ht_sp_new(HT_STR_DUP, NULL, free); void **it; @@ -198,11 +204,17 @@ RZ_API void rz_interpreter_queue_set_free(RZ_NULLABLE RZ_OWN RzInterpreterSet *i free(iset); } +typedef struct { + ut64 addr; + ut64 in_state_hash; +} SuccessorState; + /** * Main interpretation. */ -RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_BORROW RzInterpreterSet *iset) { +RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { rz_return_val_if_fail(iset && + iset->state && iset->addr_queue && iset->il_queue && iset->yield_queues && @@ -223,61 +235,130 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_BORROW RzInterpreterSet *iset) { } void *plugin_data = priv_ptr ? *priv_ptr : NULL; - plugin->init_state(iset->state, plugin_data); + // + // Start interpretation + // - HtUP *state_map = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_abstr_state_free); + // Cache of the currently used states. + HtUP *state_cache = NULL; + // A vector for the plugin to push the determined successors into. + RzVector *tmp_succ_addr = NULL; + // The set of reachable states. + RzSetU *reachable_states = NULL; + // The successor states to evaluate. + // This vector must have the same order as the elements pushed into addr_queue. + RzVector *succ_states = NULL; + if (!plugin->init_state(iset->state, plugin_data)) { + goto pre_loop_error; + } + RzInterpreterAbstrState *in_state = iset->state; + ut64 in_hash = plugin->hash_state(in_state, plugin_data); + RzInterpreterAbstrState *out_state = NULL; + ut64 out_hash = 0; - RzSetU *reachable_states = rz_set_u_new(); - if (!reachable_states) { - rz_warn_if_reached(); - goto error; + const RzILOpEffect *eff = rz_th_queue_pop(iset->il_queue, false); + state_cache = ht_up_new_rc(NULL, NULL); + tmp_succ_addr = rz_vector_new(sizeof(ut64), NULL, NULL); + succ_states = rz_vector_new(sizeof(SuccessorState), NULL, NULL); + reachable_states = rz_set_u_new(); + if (!state_cache || !tmp_succ_addr || !succ_states || !eff || !reachable_states) { + goto pre_loop_error; } - ut64 n_states = 0; - ut64 prev_n_states = 0; + while (true) { - const RzILOpEffect *eff = rz_th_queue_wait_pop(iset->il_queue, false); - if (!eff) { - rz_warn_if_reached(); - goto error; + // Evaluate the effect on the input state. + if (!plugin->eval(in_state, eff, iset->yield_queues, plugin_data)) { + goto loop_error; } + // Decrease the reference count to the input state by one. + ht_up_delete_rc(state_cache, in_hash); + // The input state was (almost always) manipulated by eval(). Rename to clarify. + out_state = in_state; + out_hash = plugin->hash_state(out_state, plugin_data); - RzInterpreterAbstrState *state = ht_up_find(state_map, plugin->hash_state(state, plugin_data), NULL); - if (!state || !plugin->eval(state, eff, iset->yield_queues, plugin_data)) { - RZ_LOG_ERROR("Interpreter failed to evaluate an effect or state was NULL. Abort.\n"); - RZ_LOG_ERROR("n_states = %" PFMT64d ".\n", n_states); - goto error; - } + // Add out_state hash to the reachable states and + // set a flag if it was a new state. + size_t psize = rz_set_u_size(reachable_states); + rz_set_u_add(reachable_states, out_hash); + bool new_state_reached = psize < rz_set_u_size(reachable_states); - rz_set_u_add(reachable_states, plugin->hash_state(state, plugin_data)); - n_states = rz_set_u_size(reachable_states); - bool reached_new_state = n_states > prev_n_states; - prev_n_states = n_states; + // Determine the successor effects to evaluate. + // Only newly reached states are allowed to add successors. + if (new_state_reached) { + // Determine successors and increase the reference counts for the current out state. + if (!plugin->successors(out_state, tmp_succ_addr, plugin_data)) { + goto loop_error; + } + if (rz_vector_len(tmp_succ_addr) > 0) { + // The output state was new and there are successors from it. + // Add it to the cache and increase the reference count of it to the number + // of successors which have it as an input state. + ht_up_insert(state_cache, out_hash, out_state); + ht_up_inc_rc(state_cache, out_hash, rz_vector_len(tmp_succ_addr)); + } + // Request the successor effects over the queue. + while (!rz_vector_empty(tmp_succ_addr)) { + ut64 *addr = RZ_NEW(ut64); + rz_vector_pop_front(tmp_succ_addr, addr); + SuccessorState ss = { .addr = *addr, .in_state_hash = out_hash }; + // The successors are pushed in the same order into the succ_states + // vector, as they are requested over the addr_queue. + rz_vector_push(succ_states, &ss); + rz_th_queue_push(iset->addr_queue, addr, true); + } + } + if (ht_up_get_rc(state_cache, out_hash) == 0) { + // There are no references to the current out_state. + // Free it for resources. + bool found; + RzInterpreterAbstrState *tmp = ht_up_find(state_cache, out_hash, &found); + if (found) { + plugin->fini_state(tmp, plugin_data); + rz_interpreter_abstr_state_free(tmp); + } + ht_up_delete(state_cache, out_hash); + } - if (rz_th_queue_size(iset->il_queue) == 0) { - // No effect left to evaluate. + if (ht_up_size(state_cache) == 0) { + // No state in the cache left. + // This means we can stop interpreting. + // Note, that we can't use the queues as cancel condition because they + // are asynchronous and checking them would introduces race conditions. break; } - if (reached_new_state) { - // Only new states are allowed to add to the request queue. - // Otherwise we might loop indefinitely. - size_t prev_addr_size = rz_th_queue_size(iset->addr_queue); - if (!plugin->successors(state, iset->addr_queue, plugin_data)) { - RZ_LOG_ERROR("Error during PC concretization. Abort.\n"); - goto error; - } + + // Set effect and state for next evaluation. + SuccessorState next = { 0 }; + rz_vector_pop_front(succ_states, &next); + in_hash = next.in_state_hash; + in_state = ht_up_find(state_cache, in_hash, NULL); + if (plugin->clone_state && ht_up_get_rc(state_cache, in_hash) > 1) { + // There is more than one effect using this state as input. + // All except one need a clone of it for evaluation because + // they perform different changes on it. + in_state = plugin->clone_state(in_state, plugin_data); + } else if (ht_up_get_rc(state_cache, in_hash) > 1) { + RZ_LOG_ERROR("If the plugin can produce multiple successors for a single state, " + "it must implement the clone_state() callback.\n"); + rz_warn_if_reached(); + break; } + eff = rz_th_queue_wait_pop(iset->il_queue, false); } - // while true { - // get new PCs - // request effects - // push effects - // } -error: - rz_atomic_bool_set(iset->is_running_flag, false); +loop_error: + ht_up_free(state_cache); + rz_vector_free(tmp_succ_addr); + rz_vector_free(succ_states); + rz_set_u_free(reachable_states); if (iset->plugin->fini) { iset->plugin->fini(plugin_data); } + rz_atomic_bool_set(iset->is_running_flag, false); return success; + +pre_loop_error: + iset->plugin->fini_state(iset->state, plugin_data); + goto loop_error; } diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 93cc82664db..d25ccf5f16b 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -10,7 +10,7 @@ typedef struct { RzBitVector *concrete; ///< A concrete value. If NULL, it is considered bottom. } ProtoIntrprAbstrData; -bool eval(RZ_NONNULL RZ_BORROW RzInterpreterAbstrState *state, +bool eval(RZ_NONNULL const RzInterpreterAbstrState *state, RZ_NONNULL const RzILOpEffect *effect, RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, void *plugin_data) { @@ -19,9 +19,9 @@ bool eval(RZ_NONNULL RZ_BORROW RzInterpreterAbstrState *state, } bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, - RZ_NONNULL RZ_OUT RzThreadQueue /**/ *addr_queue, + RZ_NONNULL RZ_OUT RzVector /**/ *successors, void *plugin_data) { - rz_return_val_if_fail(state && addr_queue, false); + rz_return_val_if_fail(state && successors, false); RzInterpreterAbstrVal *pc = state->pc; if (!pc || pc->abstr_data) { @@ -39,13 +39,8 @@ bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, return true; } - ut64 *next_pc = RZ_NEW(ut64); - if (!next_pc) { - rz_warn_if_reached(); - return false; - } - *next_pc = rz_bv_to_ut64(adata->concrete); - rz_th_queue_push(addr_queue, next_pc, true); + ut64 next_pc = rz_bv_to_ut64(adata->concrete); + rz_vector_push(successors, &next_pc); return true; } From 87d41879a008fd9ea1f4e738d9256e9dbde02ed3 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 24 Nov 2025 14:31:33 +0100 Subject: [PATCH 023/334] Add init and fini state --- .../interpreter/p/interpreter_prototype.c | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index d25ccf5f16b..c3c7153d3ed 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2025 RizinOrg // SPDX-License-Identifier: LGPL-3.0-only +#include "rz_util/rz_iterator.h" #include #include #include @@ -44,6 +45,38 @@ bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, return true; } +static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data) { + state->pc->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); + RzIterator *it = ht_sp_as_iter(state->reg_file); + RzInterpreterAbstrVal **v; + rz_iterator_foreach(it, v) { + RzInterpreterAbstrVal *av = *v; + av->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); + } + rz_iterator_free(it); + return true; +} + +static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data) { + ProtoIntrprAbstrData *ad = state->pc->abstr_data; + if (ad->concrete) { + rz_bv_free(ad->concrete); + } + free(ad); + RzIterator *it = ht_sp_as_iter(state->reg_file); + RzInterpreterAbstrVal **v; + rz_iterator_foreach(it, v) { + RzInterpreterAbstrVal *av = *v; + ProtoIntrprAbstrData *ad = av->abstr_data; + if (ad->concrete) { + rz_bv_free(ad->concrete); + } + free(ad); + } + rz_iterator_free(it); + return true; +} + static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .name = "abstr_int_prototype", .author = "Rot127", @@ -56,6 +89,8 @@ static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .fini = NULL, .eval = eval, .successors = successors, + .init_state = init_state, + .fini_state = fini_state, }; RZ_API RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype = { From 6c5cca3d92fdcd23b570f7acb2b3877f868ce267 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 24 Nov 2025 14:47:32 +0100 Subject: [PATCH 024/334] Add hash_state --- .../interpreter/p/interpreter_prototype.c | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index c3c7153d3ed..859390e0db3 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -77,6 +77,30 @@ static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da return true; } +/** + * \brief This hash function is just an example implementation. + * It is likely not sufficient to prevent collisions. + * It is also slow. + */ +static ut64 hash_state(RZ_NONNULL const RzInterpreterAbstrState *state, void *plugin_data) { + ut64 hash = 0; + ProtoIntrprAbstrData *ad = state->pc->abstr_data; + if (ad->concrete) { + hash ^= rz_bv_to_ut64(ad->concrete); + } + RzIterator *it = ht_sp_as_iter(state->reg_file); + RzInterpreterAbstrVal **v; + rz_iterator_foreach(it, v) { + RzInterpreterAbstrVal *av = *v; + ProtoIntrprAbstrData *ad = av->abstr_data; + if (ad->concrete) { + hash ^= rz_bv_to_ut64(ad->concrete); + } + } + rz_iterator_free(it); + return hash; +} + static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .name = "abstr_int_prototype", .author = "Rot127", @@ -91,6 +115,7 @@ static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .successors = successors, .init_state = init_state, .fini_state = fini_state, + .hash_state = hash_state, }; RZ_API RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype = { From 5929a65cc66a59431b20782ce15107b3373f9872 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 24 Nov 2025 15:01:06 +0100 Subject: [PATCH 025/334] Cleanup --- librz/inquiry/interpreter/interpreter.c | 5 ----- librz/inquiry/interpreter/p/interpreter_prototype.c | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 8952f659e5f..4736f91530b 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -5,11 +5,6 @@ * \file The API implementation for all analysis interpreters. */ -#include "rz_util/ht_up.h" -#include "rz_util/rz_iterator.h" -#include "rz_util/rz_set.h" -#include "rz_util/rz_th_ht.h" -#include "subprojects/rzheap/rz_jemalloc/internal/jemalloc_internal.h" #include #include #include diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 859390e0db3..c00505f5196 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: 2025 RizinOrg // SPDX-License-Identifier: LGPL-3.0-only -#include "rz_util/rz_iterator.h" #include #include #include @@ -116,6 +115,7 @@ static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .init_state = init_state, .fini_state = fini_state, .hash_state = hash_state, + .clone_state = NULL, }; RZ_API RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype = { From f12cbf52691e247f558102dcfc3a9877a878bd04 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 24 Nov 2025 15:59:02 +0100 Subject: [PATCH 026/334] Start with prototype eval() --- librz/include/rz_inquiry/rz_interpreter.h | 12 +++++- librz/inquiry/inquiry.c | 13 +++++- librz/inquiry/interpreter/interpreter.c | 3 +- librz/inquiry/interpreter/meson.build | 1 + .../interpreter/p/interpreter_prototype.c | 31 +++++++------- librz/inquiry/interpreter/prototype/eval.c | 41 +++++++++++++++++++ librz/inquiry/interpreter/prototype/eval.h | 32 +++++++++++++++ 7 files changed, 112 insertions(+), 21 deletions(-) create mode 100644 librz/inquiry/interpreter/prototype/eval.c create mode 100644 librz/inquiry/interpreter/prototype/eval.h diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 0fb418299ac..4ceb32bc7bd 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -53,6 +53,14 @@ typedef struct { RzInterpreterAbstraction kinds; ///< The abstractions of the state. HtSP /**/ *reg_file; ///< The register file. Currently a hash map. RzInterpreterAbstrVal *pc; ///< In our RzIL implementation the PC is not part of the register file. + /** + * \brief The number by which the PC is incremented for a NOP instruction. + * Usually this is simply the instruction width. But it can be 0 + * if the architecture doesn't have a fixed increment (e.g. VLIW processors). + * If 0 the RzArch plugin is expected to always return an effect with a + * (conditional) JUMP at the end of each effect. + */ + ut64 nop_pc_inc; void *prop; ///< Optional state properties. Managed by individual interpreters. } RzInterpreterAbstrState; @@ -122,7 +130,7 @@ typedef struct { /** * \brief Evaluates an effect with the mutable state. */ - bool (*eval)(RZ_NONNULL const RzInterpreterAbstrState *state, + bool (*eval)(RZ_NONNULL RzInterpreterAbstrState *state, RZ_NONNULL const RzILOpEffect *effect, RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, void *plugin_data); @@ -154,7 +162,7 @@ RZ_API void rz_interpreter_il_queue_free(RZ_OWN RZ_NULLABLE RzThreadQueue /**/ *q); RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldQueue *yield_queue); -RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new(RzInterpreterAbstraction kinds, RZ_NULLABLE const RzPVector *reg_names); +RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new(RzInterpreterAbstraction kinds, RZ_NULLABLE const RzPVector *reg_names, ut64 nop_pc_increment); RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbstrState *state); RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpreterYieldKind kind, diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index e7f711b5343..919ce6d7518 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -66,6 +66,16 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(const RzAnalysisXRef *xref, const return false; } +static ut64 get_nop_pc_increment(RzAnalysis *analysis) { + if (RZ_STR_EQ(analysis->cur->arch, "hexagon")) { + // Hexagon has variable instruction lengths. + // It manages the JUMPs to the next packets on its own. + // So it always has a JUMP effect at the end of the effect. + return 0; + } + return analysis->cur->bits / 8; +} + static RzPVector *get_reg_names(RzAnalysis *analysis) { RzPVector *reg_names = rz_pvector_new(free); if (analysis->cur->il_config) { @@ -191,7 +201,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // Initialize the abstract state with the architecture's registers. RzPVector *reg_names = get_reg_names(core->analysis); - RzInterpreterAbstrState *abstr_state = rz_interpreter_abstr_state_new(RZ_INTERPRETER_ABSTRACTION_CONST, reg_names); + ut64 nop_pc_increment = get_nop_pc_increment(core->analysis); + RzInterpreterAbstrState *abstr_state = rz_interpreter_abstr_state_new(RZ_INTERPRETER_ABSTRACTION_CONST, reg_names, nop_pc_increment); rz_pvector_free(reg_names); // Bundle all the queues into one object to pass it to the thread. diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 4736f91530b..4c2c72dbf11 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -99,11 +99,12 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre * \brief Initializes an abstract state for specified abstract kinds. Optionally with a list of registers. * The register name list should always be given if the architecture has some. */ -RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new(RzInterpreterAbstraction kinds, RZ_NULLABLE const RzPVector *reg_names) { +RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new(RzInterpreterAbstraction kinds, RZ_NULLABLE const RzPVector *reg_names, ut64 nop_pc_increment) { RzInterpreterAbstrState *state = RZ_NEW0(RzInterpreterAbstrState); if (!state) { return NULL; } + state->nop_pc_inc = nop_pc_increment; state->kinds = kinds; if (!reg_names) { return state; diff --git a/librz/inquiry/interpreter/meson.build b/librz/inquiry/interpreter/meson.build index 972da4a8edc..cb3d481ef80 100644 --- a/librz/inquiry/interpreter/meson.build +++ b/librz/inquiry/interpreter/meson.build @@ -14,6 +14,7 @@ interpreter_plugins = { rz_interpreter_sources = [ 'interpreter.c', 'p/interpreter_prototype.c', + 'prototype/eval.c', ] rz_interpreter_inc = [platform_inc] diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index c00505f5196..88edfcb3091 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -6,16 +6,13 @@ #include #include -typedef struct { - RzBitVector *concrete; ///< A concrete value. If NULL, it is considered bottom. -} ProtoIntrprAbstrData; +#include "../prototype/eval.h" -bool eval(RZ_NONNULL const RzInterpreterAbstrState *state, +static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, RZ_NONNULL const RzILOpEffect *effect, RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, void *plugin_data) { - RZ_LOG_WARN("Hello from Protoype eval.\n"); - return true; + return interpreter_prototype_eval_effect(state, effect, yield_queues, plugin_data); } bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, @@ -29,17 +26,17 @@ bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, return false; } ProtoIntrprAbstrData *adata = pc->abstr_data; - if (!adata->concrete) { + if (adata->is_concrete) { // The PC is not a concrete value. // This prototype can't estimate a reasonable concretization for it. return true; } - if (rz_bv_len(adata->concrete) > 64) { + if (rz_bv_len(adata->bv) > 64) { RZ_LOG_WARN("PC has a length of more than 64 bits!\n"); return true; } - ut64 next_pc = rz_bv_to_ut64(adata->concrete); + ut64 next_pc = rz_bv_to_ut64(adata->bv); rz_vector_push(successors, &next_pc); return true; } @@ -58,8 +55,8 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data) { ProtoIntrprAbstrData *ad = state->pc->abstr_data; - if (ad->concrete) { - rz_bv_free(ad->concrete); + if (ad->bv) { + rz_bv_free(ad->bv); } free(ad); RzIterator *it = ht_sp_as_iter(state->reg_file); @@ -67,8 +64,8 @@ static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da rz_iterator_foreach(it, v) { RzInterpreterAbstrVal *av = *v; ProtoIntrprAbstrData *ad = av->abstr_data; - if (ad->concrete) { - rz_bv_free(ad->concrete); + if (ad->bv) { + rz_bv_free(ad->bv); } free(ad); } @@ -84,16 +81,16 @@ static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da static ut64 hash_state(RZ_NONNULL const RzInterpreterAbstrState *state, void *plugin_data) { ut64 hash = 0; ProtoIntrprAbstrData *ad = state->pc->abstr_data; - if (ad->concrete) { - hash ^= rz_bv_to_ut64(ad->concrete); + if (ad->bv) { + hash ^= rz_bv_to_ut64(ad->bv); } RzIterator *it = ht_sp_as_iter(state->reg_file); RzInterpreterAbstrVal **v; rz_iterator_foreach(it, v) { RzInterpreterAbstrVal *av = *v; ProtoIntrprAbstrData *ad = av->abstr_data; - if (ad->concrete) { - hash ^= rz_bv_to_ut64(ad->concrete); + if (ad->bv) { + hash ^= rz_bv_to_ut64(ad->bv); } } rz_iterator_free(it); diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c new file mode 100644 index 00000000000..44da965987b --- /dev/null +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2025 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#include "eval.h" +#include "rz_util/rz_bitvector.h" + +RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, + const RzILOpEffect *effect, + HtUP /**/ *yield_queues, + void *plugin_data) { + switch (effect->code) { + default: + case RZ_IL_OP_EMPTY: + break; + case RZ_IL_OP_NOP: + if (AD(state->pc)->is_concrete) { + // The PC is no longer a concrete value. + // This plugin has no addition for it defined. + break; + } + RzBitVector *new_pc = rz_bv_add(AD(state->pc)->bv, rz_bv_new(state->nop_pc_inc), NULL); + if (!AD(state->pc)->bv) { + goto error; + } + rz_bv_free(AD(state->pc)->bv); + AD(state->pc)->bv = new_pc; + break; + case RZ_IL_OP_STORE: + case RZ_IL_OP_STOREW: + case RZ_IL_OP_SET: + case RZ_IL_OP_JMP: + case RZ_IL_OP_GOTO: + case RZ_IL_OP_SEQ: + case RZ_IL_OP_BLK: + case RZ_IL_OP_REPEAT: + case RZ_IL_OP_BRANCH: + } + return true; +error: + return false; +} diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h new file mode 100644 index 00000000000..8b519db94c7 --- /dev/null +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2025 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef PROTOYPE_EVAL_H +#define PROTOYPE_EVAL_H + +#include +#include + +/** + * \brief Abstract data getter from the RzInterpreterAbstrVal + */ +#define AD(av) (((ProtoIntrprAbstrData *)av)) + +typedef struct { + /** + * \brief Set if the bit vector below is a valid concrete value. + * If unset it is a bottom value. + */ + bool is_concrete; + /** + * \brief The concrete value. If is_concrete is unset this might hold garbage. + */ + RzBitVector *bv; +} ProtoIntrprAbstrData; + +RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, + const RzILOpEffect *effect, + HtUP /**/ *yield_queues, + void *plugin_data); + +#endif // PROTOYPE_EVAL_H From 81fa3df66c2fd1eea148fcde980ebdbb27d1e26a Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 25 Nov 2025 15:52:02 +0100 Subject: [PATCH 027/334] Split eval source files. --- librz/inquiry/interpreter/meson.build | 3 +- librz/inquiry/interpreter/prototype/eval.h | 6 ++ .../prototype/{eval.c => eval_effect.c} | 2 + .../inquiry/interpreter/prototype/eval_pure.c | 89 +++++++++++++++++++ 4 files changed, 99 insertions(+), 1 deletion(-) rename librz/inquiry/interpreter/prototype/{eval.c => eval_effect.c} (96%) create mode 100644 librz/inquiry/interpreter/prototype/eval_pure.c diff --git a/librz/inquiry/interpreter/meson.build b/librz/inquiry/interpreter/meson.build index cb3d481ef80..118339a09c6 100644 --- a/librz/inquiry/interpreter/meson.build +++ b/librz/inquiry/interpreter/meson.build @@ -14,7 +14,8 @@ interpreter_plugins = { rz_interpreter_sources = [ 'interpreter.c', 'p/interpreter_prototype.c', - 'prototype/eval.c', + 'prototype/eval_effect.c', + 'prototype/eval_pure.c', ] rz_interpreter_inc = [platform_inc] diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 8b519db94c7..22cdad77e22 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -28,5 +28,11 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, HtUP /**/ *yield_queues, void *plugin_data); +RZ_IPI bool interpreter_prototype_eval_pure( + RzInterpreterAbstrState *state, + const RzILOpPure *pure, + RZ_OUT ProtoIntrprAbstrData *out, + HtUP /**/ *yield_queues, + void *plugin_data); #endif // PROTOYPE_EVAL_H diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval_effect.c similarity index 96% rename from librz/inquiry/interpreter/prototype/eval.c rename to librz/inquiry/interpreter/prototype/eval_effect.c index 44da965987b..e8377294a31 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -8,6 +8,8 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, HtUP /**/ *yield_queues, void *plugin_data) { + RzILOpPure out_pure_I = { 0 }; + switch (effect->code) { default: case RZ_IL_OP_EMPTY: diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c new file mode 100644 index 00000000000..5fe82a3036a --- /dev/null +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -0,0 +1,89 @@ +// SPDX-FileCopyrightText: 2025 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#include "eval.h" +#include "rz_il/rz_il_opcodes.h" +#include "rz_util/rz_bitvector.h" + +RZ_IPI bool interpreter_prototype_eval_pure( + RzInterpreterAbstrState *state, + const RzILOpPure *pure, + RZ_OUT ProtoIntrprAbstrData *out, + HtUP /**/ *yield_queues, + void *plugin_data) { + + switch (pure->code) { + default: + case RZ_IL_OP_VAR: + case RZ_IL_OP_ITE: + case RZ_IL_OP_LET: + case RZ_IL_OP_B0: + case RZ_IL_OP_B1: + case RZ_IL_OP_INV: + case RZ_IL_OP_AND: + case RZ_IL_OP_OR: + case RZ_IL_OP_XOR: + case RZ_IL_OP_BITV: + case RZ_IL_OP_MSB: + case RZ_IL_OP_LSB: + case RZ_IL_OP_IS_ZERO: + case RZ_IL_OP_NEG: + case RZ_IL_OP_LOGNOT: + case RZ_IL_OP_ADD: + case RZ_IL_OP_SUB: + case RZ_IL_OP_MUL: + case RZ_IL_OP_DIV: + case RZ_IL_OP_SDIV: + case RZ_IL_OP_MOD: + case RZ_IL_OP_SMOD: + case RZ_IL_OP_LOGAND: + case RZ_IL_OP_LOGOR: + case RZ_IL_OP_LOGXOR: + case RZ_IL_OP_SHIFTR: + case RZ_IL_OP_SHIFTL: + case RZ_IL_OP_EQ: + case RZ_IL_OP_SLE: + case RZ_IL_OP_ULE: + case RZ_IL_OP_CAST: + case RZ_IL_OP_APPEND: + case RZ_IL_OP_FLOAT: + case RZ_IL_OP_FBITS: + case RZ_IL_OP_IS_FINITE: + case RZ_IL_OP_IS_NAN: + case RZ_IL_OP_IS_INF: + case RZ_IL_OP_IS_FZERO: + case RZ_IL_OP_IS_FNEG: + case RZ_IL_OP_IS_FPOS: + case RZ_IL_OP_FNEG: + case RZ_IL_OP_FABS: + case RZ_IL_OP_FCAST_INT: + case RZ_IL_OP_FCAST_SINT: + case RZ_IL_OP_FCAST_FLOAT: + case RZ_IL_OP_FCAST_SFLOAT: + case RZ_IL_OP_FCONVERT: + case RZ_IL_OP_FREQUAL: + case RZ_IL_OP_FSUCC: + case RZ_IL_OP_FPRED: + case RZ_IL_OP_FORDER: + case RZ_IL_OP_FROUND: + case RZ_IL_OP_FSQRT: + case RZ_IL_OP_FRSQRT: + case RZ_IL_OP_FADD: + case RZ_IL_OP_FSUB: + case RZ_IL_OP_FMUL: + case RZ_IL_OP_FDIV: + case RZ_IL_OP_FMOD: + case RZ_IL_OP_FHYPOT: + case RZ_IL_OP_FPOW: + case RZ_IL_OP_FMAD: + case RZ_IL_OP_FROOTN: + case RZ_IL_OP_FPOWN: + case RZ_IL_OP_FCOMPOUND: + case RZ_IL_OP_FEXCEPT: + case RZ_IL_OP_LOAD: + case RZ_IL_OP_LOADW: + // Not implemented. + break; + } + return true; +} From 590130abdea604e8be7833b70eee239c58441a81 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 25 Nov 2025 16:43:32 +0100 Subject: [PATCH 028/334] Simplify NOP --- librz/inquiry/interpreter/prototype/eval_effect.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index e8377294a31..fd324d4af39 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -14,18 +14,19 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, default: case RZ_IL_OP_EMPTY: break; - case RZ_IL_OP_NOP: - if (AD(state->pc)->is_concrete) { + case RZ_IL_OP_NOP: { + ProtoIntrprAbstrData *pc = AD(state->pc); + if (pc->is_concrete) { // The PC is no longer a concrete value. // This plugin has no addition for it defined. break; } - RzBitVector *new_pc = rz_bv_add(AD(state->pc)->bv, rz_bv_new(state->nop_pc_inc), NULL); - if (!AD(state->pc)->bv) { + RzBitVector *new_pc = rz_bv_add(pc->bv, rz_bv_new(state->nop_pc_inc), NULL); + if (!pc->bv) { goto error; } - rz_bv_free(AD(state->pc)->bv); - AD(state->pc)->bv = new_pc; + rz_bv_free(pc->bv); + pc->bv = new_pc; break; case RZ_IL_OP_STORE: case RZ_IL_OP_STOREW: From 0af736d9f9a86c32d21b30d0a92392d9a0846324 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 25 Nov 2025 16:46:14 +0100 Subject: [PATCH 029/334] Reorganize into operations to implement and to ignore for now. --- .../interpreter/prototype/eval_effect.c | 13 ++++++--- .../inquiry/interpreter/prototype/eval_pure.c | 27 ++++++++++++------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index fd324d4af39..886f11652ae 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -28,15 +28,20 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, rz_bv_free(pc->bv); pc->bv = new_pc; break; - case RZ_IL_OP_STORE: - case RZ_IL_OP_STOREW: + } case RZ_IL_OP_SET: + case RZ_IL_OP_BRANCH: case RZ_IL_OP_JMP: - case RZ_IL_OP_GOTO: case RZ_IL_OP_SEQ: + // Essential for basic functioning. + // TODO + case RZ_IL_OP_STORE: + case RZ_IL_OP_STOREW: + case RZ_IL_OP_GOTO: case RZ_IL_OP_BLK: case RZ_IL_OP_REPEAT: - case RZ_IL_OP_BRANCH: + // Ignore for now. + break; } return true; error: diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 5fe82a3036a..7eb9ae1d5ae 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -19,11 +19,14 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_LET: case RZ_IL_OP_B0: case RZ_IL_OP_B1: + case RZ_IL_OP_BITV: + case RZ_IL_OP_CAST: + case RZ_IL_OP_APPEND: + // TODO case RZ_IL_OP_INV: case RZ_IL_OP_AND: case RZ_IL_OP_OR: case RZ_IL_OP_XOR: - case RZ_IL_OP_BITV: case RZ_IL_OP_MSB: case RZ_IL_OP_LSB: case RZ_IL_OP_IS_ZERO: @@ -31,11 +34,6 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_LOGNOT: case RZ_IL_OP_ADD: case RZ_IL_OP_SUB: - case RZ_IL_OP_MUL: - case RZ_IL_OP_DIV: - case RZ_IL_OP_SDIV: - case RZ_IL_OP_MOD: - case RZ_IL_OP_SMOD: case RZ_IL_OP_LOGAND: case RZ_IL_OP_LOGOR: case RZ_IL_OP_LOGXOR: @@ -44,8 +42,12 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_EQ: case RZ_IL_OP_SLE: case RZ_IL_OP_ULE: - case RZ_IL_OP_CAST: - case RZ_IL_OP_APPEND: + // TODO + case RZ_IL_OP_MUL: + case RZ_IL_OP_DIV: + case RZ_IL_OP_SDIV: + case RZ_IL_OP_MOD: + case RZ_IL_OP_SMOD: case RZ_IL_OP_FLOAT: case RZ_IL_OP_FBITS: case RZ_IL_OP_IS_FINITE: @@ -83,7 +85,14 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_LOAD: case RZ_IL_OP_LOADW: // Not implemented. - break; + goto map_to_bottom; } + + // TODO: Check filter if the values should be reported/pushed into the yield queue. + + return true; + +map_to_bottom: + out->is_concrete = false; return true; } From e234bf9dd8700a9aed6b3fbfa6d6b28852176790 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 25 Nov 2025 16:54:36 +0100 Subject: [PATCH 030/334] Implement SEQ --- librz/inquiry/interpreter/prototype/eval_effect.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 886f11652ae..5b118dec614 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -29,10 +29,18 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, pc->bv = new_pc; break; } + case RZ_IL_OP_SEQ: { + const RzILOpEffect *next = effect->op.seq.x; + while (next) { + if (!interpreter_prototype_eval_effect(state, next, yield_queues, plugin_data)) { + goto error; + } + next = effect->op.seq.y; + } + } case RZ_IL_OP_SET: case RZ_IL_OP_BRANCH: case RZ_IL_OP_JMP: - case RZ_IL_OP_SEQ: // Essential for basic functioning. // TODO case RZ_IL_OP_STORE: From 8343f8977fa2e0f6129a50af4c3ebf9731388ac8 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 25 Nov 2025 17:35:42 +0100 Subject: [PATCH 031/334] Add global/local variable maps instead of reg_map and access them with DJB2 keys. --- librz/include/rz_inquiry/rz_interpreter.h | 3 ++- librz/inquiry/interpreter/interpreter.c | 20 ++++++++++++++----- .../interpreter/p/interpreter_prototype.c | 17 +++++++++++++--- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 4ceb32bc7bd..621fe678403 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -51,7 +51,8 @@ typedef struct { typedef struct { RzInterpreterAbstraction kinds; ///< The abstractions of the state. - HtSP /**/ *reg_file; ///< The register file. Currently a hash map. + HtUP /**/ *globals; ///< Global variables (mostly registers). Indexed by DJB2 hash of global name. + HtUP /**/ *locals; ///< Local variables. Indexed by DJB2 hash of the local name. RzInterpreterAbstrVal *pc; ///< In our RzIL implementation the PC is not part of the register file. /** * \brief The number by which the PC is incremented for a NOP instruction. diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 4c2c72dbf11..593d83cdbde 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -114,20 +114,27 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new(RzInterpre return NULL; } // Initialize the register file with uninitialized abstract values. - state->reg_file = ht_sp_new(HT_STR_DUP, NULL, free); + state->globals = ht_up_new(NULL, free); void **it; rz_pvector_foreach (reg_names, it) { const char *rname = *it; RzInterpreterAbstrVal *aval = RZ_NEW0(RzInterpreterAbstrVal); if (!aval) { - ht_sp_free(state->reg_file); + ht_up_free(state->globals); free(state); return NULL; } aval->kind = RZ_INTERPRETER_ABSTRACTION_UNDEF; - ht_sp_insert(state->reg_file, rname, aval); + ut64 djb2_reg_hash = rz_str_djb2_hash(rname); + if (!ht_up_insert(state->globals, djb2_reg_hash, aval)) { + RZ_LOG_ERROR("Failed to add %s to the global variable map. " + "DJB2 hash collision of the register name. DJB2 hash = 0x%" PFMT64x "\n", + rname, djb2_reg_hash); + return NULL; + } } + state->locals = ht_up_new(NULL, NULL); return state; } @@ -135,8 +142,11 @@ RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbst if (!state) { return; } - if (state->reg_file) { - ht_sp_free(state->reg_file); + if (state->globals) { + ht_up_free(state->globals); + } + if (state->locals) { + ht_up_free(state->locals); } free(state); } diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 88edfcb3091..83a9462db71 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -43,7 +43,7 @@ bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data) { state->pc->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); - RzIterator *it = ht_sp_as_iter(state->reg_file); + RzIterator *it = ht_up_as_iter(state->globals); RzInterpreterAbstrVal **v; rz_iterator_foreach(it, v) { RzInterpreterAbstrVal *av = *v; @@ -59,7 +59,7 @@ static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da rz_bv_free(ad->bv); } free(ad); - RzIterator *it = ht_sp_as_iter(state->reg_file); + RzIterator *it = ht_up_as_iter(state->globals); RzInterpreterAbstrVal **v; rz_iterator_foreach(it, v) { RzInterpreterAbstrVal *av = *v; @@ -70,6 +70,17 @@ static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da free(ad); } rz_iterator_free(it); + + it = ht_up_as_iter(state->locals); + rz_iterator_foreach(it, v) { + RzInterpreterAbstrVal *av = *v; + ProtoIntrprAbstrData *ad = av->abstr_data; + if (ad->bv) { + rz_bv_free(ad->bv); + } + free(ad); + } + rz_iterator_free(it); return true; } @@ -84,7 +95,7 @@ static ut64 hash_state(RZ_NONNULL const RzInterpreterAbstrState *state, void *pl if (ad->bv) { hash ^= rz_bv_to_ut64(ad->bv); } - RzIterator *it = ht_sp_as_iter(state->reg_file); + RzIterator *it = ht_up_as_iter(state->globals); RzInterpreterAbstrVal **v; rz_iterator_foreach(it, v) { RzInterpreterAbstrVal *av = *v; From 5623c3526bf9c24e545b3582549dcb33904d2878 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 25 Nov 2025 17:36:23 +0100 Subject: [PATCH 032/334] Add stack based initializers for abstract data objects. --- librz/inquiry/interpreter/prototype/eval.h | 15 +++++++++++++++ librz/inquiry/interpreter/prototype/eval_effect.c | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 22cdad77e22..04fbabde513 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -24,6 +24,21 @@ typedef struct { RzBitVector *bv; } ProtoIntrprAbstrData; +/** + * \brief Up to 64bit-bitvector. + */ +#define STACK_ABSTR_DATA_SMALL_BV(name, bit_len) \ + RzBitVector _##name##_bv = { .len = bit_len, ._elem_len = bit_len / 8, .bits.small_u = 0 }; \ + ProtoIntrprAbstrData name = { .is_concrete = false, .bv = &_##name##_bv }; + +/** + * \brief Larger than 64bit-bitvector. + */ +#define STACK_ABSTR_DATA_LARGE_BV(name, bit_len) \ + ut8 _##name##_bv_buf[bit_len / 8] = { 0 }; \ + RzBitVector _##name##_bv = { .len = bit_len, ._elem_len = bit_len / 8, .bits.large_a = _##name##_bv_buf }; \ + ProtoIntrprAbstrData name = { .is_concrete = false, .bv = &_##name##_bv }; + RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, HtUP /**/ *yield_queues, diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 5b118dec614..8e07097b733 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -8,7 +8,8 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, HtUP /**/ *yield_queues, void *plugin_data) { - RzILOpPure out_pure_I = { 0 }; + // STACK_ABSTR_DATA_SMALL_BV(p_out_32, 32); + // STACK_ABSTR_DATA_LARGE_BV(p_out_128, 128); switch (effect->code) { default: From 4f65a2ee76d1770246c68bc20745e35e410fbac0 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 27 Nov 2025 16:32:02 +0100 Subject: [PATCH 033/334] Use in-place addition for update PC in NOP --- librz/inquiry/interpreter/prototype/eval_effect.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 8e07097b733..edde4ebaa1e 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -22,12 +22,9 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, // This plugin has no addition for it defined. break; } - RzBitVector *new_pc = rz_bv_add(pc->bv, rz_bv_new(state->nop_pc_inc), NULL); - if (!pc->bv) { + if (!rz_bv_add_inplace(pc->bv, rz_bv_new(state->nop_pc_inc), NULL)) { goto error; } - rz_bv_free(pc->bv); - pc->bv = new_pc; break; } case RZ_IL_OP_SEQ: { From f96338b6268b9ac2f0c25b8a02e9858d6817d76d Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 25 Nov 2025 19:06:43 +0100 Subject: [PATCH 034/334] Implement SET --- librz/inquiry/interpreter/interpreter.c | 2 +- librz/inquiry/interpreter/meson.build | 1 + .../interpreter/p/interpreter_prototype.c | 4 ++- librz/inquiry/interpreter/prototype/eval.c | 15 ++++++++++ librz/inquiry/interpreter/prototype/eval.h | 28 +++++++++++++------ .../interpreter/prototype/eval_effect.c | 21 ++++++++++++-- .../inquiry/interpreter/prototype/eval_pure.c | 3 ++ 7 files changed, 60 insertions(+), 14 deletions(-) create mode 100644 librz/inquiry/interpreter/prototype/eval.c diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 593d83cdbde..17fa58a0e71 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -346,7 +346,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { in_state = plugin->clone_state(in_state, plugin_data); } else if (ht_up_get_rc(state_cache, in_hash) > 1) { RZ_LOG_ERROR("If the plugin can produce multiple successors for a single state, " - "it must implement the clone_state() callback.\n"); + "it must implement the clone_state() callback.\n"); rz_warn_if_reached(); break; } diff --git a/librz/inquiry/interpreter/meson.build b/librz/inquiry/interpreter/meson.build index 118339a09c6..34deb8c33da 100644 --- a/librz/inquiry/interpreter/meson.build +++ b/librz/inquiry/interpreter/meson.build @@ -16,6 +16,7 @@ rz_interpreter_sources = [ 'p/interpreter_prototype.c', 'prototype/eval_effect.c', 'prototype/eval_pure.c', + 'prototype/eval.c', ] rz_interpreter_inc = [platform_inc] diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 83a9462db71..9cbf713d103 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -12,7 +12,9 @@ static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, RZ_NONNULL const RzILOpEffect *effect, RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, void *plugin_data) { - return interpreter_prototype_eval_effect(state, effect, yield_queues, plugin_data); + bool result = interpreter_prototype_eval_effect(state, effect, yield_queues, plugin_data); + // TODO: Clean up local variables. + return result; } bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c new file mode 100644 index 00000000000..3339838e980 --- /dev/null +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2025 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#include "eval.h" +#include "rz_util/rz_bitvector.h" + +void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) { + // TODO: For performance it should really just copy the data between bit vectors + // Not freeing the old one and allocating a new one with rz_bv_dup(). + // But this has to wait until we can copy bit vectors between on using an array + // and one using an ut64 to store its bits. + rz_bv_free(dst->bv); + dst->bv = rz_bv_dup(src->bv); + dst->is_concrete = src->is_concrete; +} diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 04fbabde513..b1dc7417adb 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -4,6 +4,8 @@ #ifndef PROTOYPE_EVAL_H #define PROTOYPE_EVAL_H +#include "rz_types.h" +#include "rz_util/rz_bitvector.h" #include #include @@ -25,19 +27,27 @@ typedef struct { } ProtoIntrprAbstrData; /** - * \brief Up to 64bit-bitvector. + * \brief Initializes an AbstractData object on the stack. + * The bit vector in it is for now hard coded to 0x2000 bytes. + * TODO: This won't matter anymore, when in-place casting + * and appending of bit vectors is implemented. */ -#define STACK_ABSTR_DATA_SMALL_BV(name, bit_len) \ - RzBitVector _##name##_bv = { .len = bit_len, ._elem_len = bit_len / 8, .bits.small_u = 0 }; \ - ProtoIntrprAbstrData name = { .is_concrete = false, .bv = &_##name##_bv }; +#define STACK_ABSTR_DATA_OUT(name) \ + ut8 _##name##_bv_large_buf[0x2000 / 8] = { 0 }; \ + RzBitVector _##name##_bv_large = { .len = 0x2000, ._elem_len = 0x2000 / 8, .bits.large_a = _##name##_bv_large_buf }; \ + ProtoIntrprAbstrData name = { .is_concrete = false, .bv = &_##name##_bv_large }; /** - * \brief Larger than 64bit-bitvector. + * \brief Creates abstract data on the heap with the given bit vector. */ -#define STACK_ABSTR_DATA_LARGE_BV(name, bit_len) \ - ut8 _##name##_bv_buf[bit_len / 8] = { 0 }; \ - RzBitVector _##name##_bv = { .len = bit_len, ._elem_len = bit_len / 8, .bits.large_a = _##name##_bv_buf }; \ - ProtoIntrprAbstrData name = { .is_concrete = false, .bv = &_##name##_bv }; +static inline RZ_OWN ProtoIntrprAbstrData *adata_from_bv(const RzBitVector *bv) { + ProtoIntrprAbstrData *ad = RZ_NEW(ProtoIntrprAbstrData); + ad->is_concrete = true; + ad->bv = rz_bv_dup(bv); + return ad; +} + +void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src); RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index edde4ebaa1e..ed667bfa4c2 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -8,8 +8,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, HtUP /**/ *yield_queues, void *plugin_data) { - // STACK_ABSTR_DATA_SMALL_BV(p_out_32, 32); - // STACK_ABSTR_DATA_LARGE_BV(p_out_128, 128); + STACK_ABSTR_DATA_OUT(eval_out); switch (effect->code) { default: @@ -35,8 +34,24 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, } next = effect->op.seq.y; } + break; + } + case RZ_IL_OP_SET: { + ut64 vhash = effect->op.set.hash; + if (!interpreter_prototype_eval_pure(state, effect->op.set.x, &eval_out, yield_queues, plugin_data)) { + goto error; + } + HtUP *ht_vals = effect->op.set.is_local ? state->locals : state->globals; + RzInterpreterAbstrVal *av = ht_up_find(ht_vals, vhash, NULL); + if (!av) { + av = RZ_NEW(RzInterpreterAbstrVal); + av->kind = RZ_INTERPRETER_ABSTRACTION_CONST; + av->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); + ht_up_insert(ht_vals, vhash, av); + } + copy_abstr_data(av->abstr_data, &eval_out); + break; } - case RZ_IL_OP_SET: case RZ_IL_OP_BRANCH: case RZ_IL_OP_JMP: // Essential for basic functioning. diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 7eb9ae1d5ae..a1d3b14b81b 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -5,6 +5,9 @@ #include "rz_il/rz_il_opcodes.h" #include "rz_util/rz_bitvector.h" +/** + * \brief Evaluate a pure. + */ RZ_IPI bool interpreter_prototype_eval_pure( RzInterpreterAbstrState *state, const RzILOpPure *pure, From 8f11fa8f386485620b7d8687d190c6aa9dc73c42 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 27 Nov 2025 20:22:54 +0100 Subject: [PATCH 035/334] Fix tests by adding new command --- test/db/cmd/cmd_help | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/test/db/cmd/cmd_help b/test/db/cmd/cmd_help index 0c15c9b3f69..183f4578c37 100644 --- a/test/db/cmd/cmd_help +++ b/test/db/cmd/cmd_help @@ -18,7 +18,8 @@ EOF EXPECT=< ...] # Abstract Interpreter Prototype | aac # Analyze function calls | aaci # Analyze all function calls to imports | aaC # Analysis classes from RzBin @@ -64,7 +65,7 @@ CMDS=<]","args":[{"type":"expression","name":"len","is_last":true}],"description":"","summary":"Analyze references with ESIL","details":[{"name":"Examples","entries":[{"text":"aae","comment":"analyze ranges given by analysis.in","arg_str":""},{"text":"aae","comment":"analyze the whole section","arg_str":" $SS @ $S"}]}]},"aaef":{"cmd":"aaef","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze references with ESIL in all functions","details":[]},"aaf":{"cmd":"aaf","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions","details":[]},"aafe":{"cmd":"aafe","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions using ESIL","details":[]},"aafr":{"cmd":"aafr","type":"argv","args_str":" ","args":[{"type":"number","name":"length","required":true}],"description":"","summary":"Analyze all consecutive functions in section","details":[]},"aaft":{"cmd":"aaft","type":"argv","args_str":"","args":[],"description":"","summary":"Performs recursive type matching in all functions","details":[]},"aai":{"cmd":"aai","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details","details":[]},"aaij":{"cmd":"aaij","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details (JSON mode)","details":[]},"aaj":{"cmd":"aaj","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all unresolved jumps","details":[]},"aalg":{"cmd":"aalg","type":"argv","args_str":"","args":[],"description":"","summary":"Recover and analyze all Golang functions and strings","details":[]},"aalor":{"cmd":"aalor","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all Objective-C references from selector usages to their implementations","details":[]},"aalos":{"cmd":"aalos","type":"argv","args_str":"","args":[],"description":"","summary":"Recover all Objective-C selector stub names (__objc_stubs section contents)","details":[]},"aan":{"cmd":"aan","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions based on their strings or calls","details":[]},"aanr":{"cmd":"aanr","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions which does not return","details":[]},"aap":{"cmd":"aap","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all preludes","details":[{"name":"Search a custom prelude","entries":[{"text":"e analysis.prelude='90AEF630'","comment":"Set new prelude","arg_str":""},{"text":"aap","comment":"Search for 90AEF630 and create a new function","arg_str":""}]}]},"aar":{"cmd":"aar","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Analyze xrefs in current section or by n_bytes","details":[]},"aas":{"cmd":"aas","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the symbols","details":[]},"aaS":{"cmd":"aaS","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the flags starting as sym.* and entry*","details":[]},"aat":{"cmd":"aat","type":"argv","args_str":" []","args":[{"type":"function","name":"func_name"}],"description":"","summary":"Analyze all/given function to convert immediate to linked structure offsets","details":[]},"aaT":{"cmd":"aaT","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Prints commands to create functions after a trap call","details":[]},"aau":{"cmd":"aau","type":"argv","args_str":" []","args":[{"type":"number","name":"min_len"}],"description":"","summary":"Print memory areas not covered by functions","details":[]},"aav":{"cmd":"aav","type":"argv_state","args_str":"","args":[],"description":"","summary":"Analyze values referencing a specific section or map","details":[]}} +{"aa":{"cmd":"aa","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all flags starting with sym. and entry","details":[]},"aaa":{"cmd":"aaa","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all calls, references, emulation and applies signatures","details":[]},"aaaa":{"cmd":"aaaa","type":"argv","args_str":"","args":[],"description":"","summary":"Legacy experimental analysis","details":[]},"aaaaIp":{"cmd":"aaaaIp","type":"argv","args_str":" [ ...]","args":[{"type":"expression","name":"entry_points","is_array":true}],"description":"","summary":"Abstract Interpreter Prototype","details":[]},"aac":{"cmd":"aac","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze function calls","details":[]},"aaci":{"cmd":"aaci","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all function calls to imports","details":[]},"aaC":{"cmd":"aaC","type":"argv","args_str":"","args":[],"description":"","summary":"Analysis classes from RzBin","details":[]},"aad":{"cmd":"aad","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze data references to code","details":[]},"aae":{"cmd":"aae","type":"argv","args_str":" []","args":[{"type":"expression","name":"len","is_last":true}],"description":"","summary":"Analyze references with ESIL","details":[{"name":"Examples","entries":[{"text":"aae","comment":"analyze ranges given by analysis.in","arg_str":""},{"text":"aae","comment":"analyze the whole section","arg_str":" $SS @ $S"}]}]},"aaef":{"cmd":"aaef","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze references with ESIL in all functions","details":[]},"aaf":{"cmd":"aaf","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions","details":[]},"aafe":{"cmd":"aafe","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions using ESIL","details":[]},"aafr":{"cmd":"aafr","type":"argv","args_str":" ","args":[{"type":"number","name":"length","required":true}],"description":"","summary":"Analyze all consecutive functions in section","details":[]},"aaft":{"cmd":"aaft","type":"argv","args_str":"","args":[],"description":"","summary":"Performs recursive type matching in all functions","details":[]},"aai":{"cmd":"aai","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details","details":[]},"aaij":{"cmd":"aaij","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details (JSON mode)","details":[]},"aaj":{"cmd":"aaj","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all unresolved jumps","details":[]},"aalg":{"cmd":"aalg","type":"argv","args_str":"","args":[],"description":"","summary":"Recover and analyze all Golang functions and strings","details":[]},"aalor":{"cmd":"aalor","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all Objective-C references from selector usages to their implementations","details":[]},"aalos":{"cmd":"aalos","type":"argv","args_str":"","args":[],"description":"","summary":"Recover all Objective-C selector stub names (__objc_stubs section contents)","details":[]},"aan":{"cmd":"aan","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions based on their strings or calls","details":[]},"aanr":{"cmd":"aanr","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions which does not return","details":[]},"aap":{"cmd":"aap","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all preludes","details":[{"name":"Search a custom prelude","entries":[{"text":"e analysis.prelude='90AEF630'","comment":"Set new prelude","arg_str":""},{"text":"aap","comment":"Search for 90AEF630 and create a new function","arg_str":""}]}]},"aar":{"cmd":"aar","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Analyze xrefs in current section or by n_bytes","details":[]},"aas":{"cmd":"aas","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the symbols","details":[]},"aaS":{"cmd":"aaS","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the flags starting as sym.* and entry*","details":[]},"aat":{"cmd":"aat","type":"argv","args_str":" []","args":[{"type":"function","name":"func_name"}],"description":"","summary":"Analyze all/given function to convert immediate to linked structure offsets","details":[]},"aaT":{"cmd":"aaT","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Prints commands to create functions after a trap call","details":[]},"aau":{"cmd":"aau","type":"argv","args_str":" []","args":[{"type":"number","name":"min_len"}],"description":"","summary":"Print memory areas not covered by functions","details":[]},"aav":{"cmd":"aav","type":"argv_state","args_str":"","args":[],"description":"","summary":"Analyze values referencing a specific section or map","details":[]},"aav*":{"cmd":"aav*","type":"argv_state","args_str":"","args":[],"description":"","summary":"Analyze values referencing a specific section or map (rizin mode)","details":[]}} EOF RUN @@ -76,7 +77,8 @@ EOF EXPECT=< ...] # Abstract Interpreter Prototype | aac # Analyze function calls | aaci # Analyze all function calls to imports | aaC # Analysis classes from RzBin @@ -113,7 +115,7 @@ EXPECT=<]","args":[{"type":"expression","name":"len","is_last":true}],"description":"","summary":"Analyze references with ESIL","details":[{"name":"Examples","entries":[{"text":"aae","comment":"analyze ranges given by analysis.in","arg_str":""},{"text":"aae","comment":"analyze the whole section","arg_str":" $SS @ $S"}]}]},"aaef":{"cmd":"aaef","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze references with ESIL in all functions","details":[]},"aaf":{"cmd":"aaf","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions","details":[]},"aafe":{"cmd":"aafe","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions using ESIL","details":[]},"aafr":{"cmd":"aafr","type":"argv","args_str":" ","args":[{"type":"number","name":"length","required":true}],"description":"","summary":"Analyze all consecutive functions in section","details":[]},"aaft":{"cmd":"aaft","type":"argv","args_str":"","args":[],"description":"","summary":"Performs recursive type matching in all functions","details":[]},"aai":{"cmd":"aai","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details","details":[]},"aaij":{"cmd":"aaij","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details (JSON mode)","details":[]},"aaj":{"cmd":"aaj","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all unresolved jumps","details":[]},"aalg":{"cmd":"aalg","type":"argv","args_str":"","args":[],"description":"","summary":"Recover and analyze all Golang functions and strings","details":[]},"aalor":{"cmd":"aalor","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all Objective-C references from selector usages to their implementations","details":[]},"aalos":{"cmd":"aalos","type":"argv","args_str":"","args":[],"description":"","summary":"Recover all Objective-C selector stub names (__objc_stubs section contents)","details":[]},"aan":{"cmd":"aan","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions based on their strings or calls","details":[]},"aanr":{"cmd":"aanr","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions which does not return","details":[]},"aap":{"cmd":"aap","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all preludes","details":[{"name":"Search a custom prelude","entries":[{"text":"e analysis.prelude='90AEF630'","comment":"Set new prelude","arg_str":""},{"text":"aap","comment":"Search for 90AEF630 and create a new function","arg_str":""}]}]},"aar":{"cmd":"aar","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Analyze xrefs in current section or by n_bytes","details":[]},"aas":{"cmd":"aas","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the symbols","details":[]},"aaS":{"cmd":"aaS","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the flags starting as sym.* and entry*","details":[]},"aat":{"cmd":"aat","type":"argv","args_str":" []","args":[{"type":"function","name":"func_name"}],"description":"","summary":"Analyze all/given function to convert immediate to linked structure offsets","details":[]},"aaT":{"cmd":"aaT","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Prints commands to create functions after a trap call","details":[]},"aau":{"cmd":"aau","type":"argv","args_str":" []","args":[{"type":"number","name":"min_len"}],"description":"","summary":"Print memory areas not covered by functions","details":[]},"aav":{"cmd":"aav","type":"argv_state","args_str":"","args":[],"description":"","summary":"Analyze values referencing a specific section or map","details":[]}} +{"aa":{"cmd":"aa","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all flags starting with sym. and entry","details":[]},"aaa":{"cmd":"aaa","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all calls, references, emulation and applies signatures","details":[]},"aaaa":{"cmd":"aaaa","type":"argv","args_str":"","args":[],"description":"","summary":"Legacy experimental analysis","details":[]},"aaaaIp":{"cmd":"aaaaIp","type":"argv","args_str":" [ ...]","args":[{"type":"expression","name":"entry_points","is_array":true}],"description":"","summary":"Abstract Interpreter Prototype","details":[]},"aac":{"cmd":"aac","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze function calls","details":[]},"aaci":{"cmd":"aaci","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all function calls to imports","details":[]},"aaC":{"cmd":"aaC","type":"argv","args_str":"","args":[],"description":"","summary":"Analysis classes from RzBin","details":[]},"aad":{"cmd":"aad","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze data references to code","details":[]},"aae":{"cmd":"aae","type":"argv","args_str":" []","args":[{"type":"expression","name":"len","is_last":true}],"description":"","summary":"Analyze references with ESIL","details":[{"name":"Examples","entries":[{"text":"aae","comment":"analyze ranges given by analysis.in","arg_str":""},{"text":"aae","comment":"analyze the whole section","arg_str":" $SS @ $S"}]}]},"aaef":{"cmd":"aaef","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze references with ESIL in all functions","details":[]},"aaf":{"cmd":"aaf","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions","details":[]},"aafe":{"cmd":"aafe","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions using ESIL","details":[]},"aafr":{"cmd":"aafr","type":"argv","args_str":" ","args":[{"type":"number","name":"length","required":true}],"description":"","summary":"Analyze all consecutive functions in section","details":[]},"aaft":{"cmd":"aaft","type":"argv","args_str":"","args":[],"description":"","summary":"Performs recursive type matching in all functions","details":[]},"aai":{"cmd":"aai","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details","details":[]},"aaij":{"cmd":"aaij","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details (JSON mode)","details":[]},"aaj":{"cmd":"aaj","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all unresolved jumps","details":[]},"aalg":{"cmd":"aalg","type":"argv","args_str":"","args":[],"description":"","summary":"Recover and analyze all Golang functions and strings","details":[]},"aalor":{"cmd":"aalor","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all Objective-C references from selector usages to their implementations","details":[]},"aalos":{"cmd":"aalos","type":"argv","args_str":"","args":[],"description":"","summary":"Recover all Objective-C selector stub names (__objc_stubs section contents)","details":[]},"aan":{"cmd":"aan","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions based on their strings or calls","details":[]},"aanr":{"cmd":"aanr","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions which does not return","details":[]},"aap":{"cmd":"aap","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all preludes","details":[{"name":"Search a custom prelude","entries":[{"text":"e analysis.prelude='90AEF630'","comment":"Set new prelude","arg_str":""},{"text":"aap","comment":"Search for 90AEF630 and create a new function","arg_str":""}]}]},"aar":{"cmd":"aar","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Analyze xrefs in current section or by n_bytes","details":[]},"aas":{"cmd":"aas","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the symbols","details":[]},"aaS":{"cmd":"aaS","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the flags starting as sym.* and entry*","details":[]},"aat":{"cmd":"aat","type":"argv","args_str":" []","args":[{"type":"function","name":"func_name"}],"description":"","summary":"Analyze all/given function to convert immediate to linked structure offsets","details":[]},"aaT":{"cmd":"aaT","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Prints commands to create functions after a trap call","details":[]},"aau":{"cmd":"aau","type":"argv","args_str":" []","args":[{"type":"number","name":"min_len"}],"description":"","summary":"Print memory areas not covered by functions","details":[]},"aav":{"cmd":"aav","type":"argv_state","args_str":"","args":[],"description":"","summary":"Analyze values referencing a specific section or map","details":[]},"aav*":{"cmd":"aav*","type":"argv_state","args_str":"","args":[],"description":"","summary":"Analyze values referencing a specific section or map (rizin mode)","details":[]}} EOF RUN From 3e9a4cb1a127b74e39ea4abd6f58e4b6503ee148 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 27 Nov 2025 20:27:40 +0100 Subject: [PATCH 036/334] Implement JMP --- librz/inquiry/interpreter/prototype/eval_effect.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index ed667bfa4c2..8514012b665 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -52,10 +52,17 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, copy_abstr_data(av->abstr_data, &eval_out); break; } + case RZ_IL_OP_JMP: { + if (!interpreter_prototype_eval_pure(state, effect->op.jmp.dst, &eval_out, yield_queues, plugin_data)) { + goto error; + } + copy_abstr_data(state->pc->abstr_data, &eval_out); + break; + } case RZ_IL_OP_BRANCH: - case RZ_IL_OP_JMP: - // Essential for basic functioning. // TODO + // If the condition is bottom, both branch targets (if concrete) should + // be returned as possible successor. case RZ_IL_OP_STORE: case RZ_IL_OP_STOREW: case RZ_IL_OP_GOTO: From 2ecb3495fed82953d9cef9dc093d4bacdf5a0738 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 27 Nov 2025 20:51:12 +0100 Subject: [PATCH 037/334] Fix inverted if --- librz/inquiry/interpreter/p/interpreter_prototype.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 9cbf713d103..80efe2028a7 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -23,12 +23,12 @@ bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, rz_return_val_if_fail(state && successors, false); RzInterpreterAbstrVal *pc = state->pc; - if (!pc || pc->abstr_data) { + if (!pc || !pc->abstr_data) { RZ_LOG_ERROR("No PC found.\n"); return false; } ProtoIntrprAbstrData *adata = pc->abstr_data; - if (adata->is_concrete) { + if (!adata->is_concrete) { // The PC is not a concrete value. // This prototype can't estimate a reasonable concretization for it. return true; From a38854cc3de6d184938d04e8cb081843365d0d40 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 27 Nov 2025 20:51:32 +0100 Subject: [PATCH 038/334] Implement BRANCH --- .../interpreter/prototype/eval_effect.c | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 8514012b665..1e844ac6f7b 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -59,10 +59,29 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, copy_abstr_data(state->pc->abstr_data, &eval_out); break; } - case RZ_IL_OP_BRANCH: - // TODO - // If the condition is bottom, both branch targets (if concrete) should - // be returned as possible successor. + case RZ_IL_OP_BRANCH: { + if (!interpreter_prototype_eval_pure(state, effect->op.branch.condition, &eval_out, yield_queues, plugin_data)) { + goto error; + } + if (!eval_out.is_concrete) { + // Bottom values means we can't make a + // decision (in this prototype implementation). + break; + } + + // TODO: The assumption that 0 == false is invalid. + // It depends on the architecture and must be decided by the RzArch plugin. + if (rz_bv_is_zero_vector(eval_out.bv)) { + if (!interpreter_prototype_eval_effect(state, effect->op.branch.false_eff, yield_queues, plugin_data)) { + goto error; + } + } else { + if (!interpreter_prototype_eval_effect(state, effect->op.branch.false_eff, yield_queues, plugin_data)) { + goto error; + } + } + break; + } case RZ_IL_OP_STORE: case RZ_IL_OP_STOREW: case RZ_IL_OP_GOTO: From b1276efacbe4296431f9a3fd50c0711ac6f6b559 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 27 Nov 2025 20:54:08 +0100 Subject: [PATCH 039/334] Rename and notes --- librz/include/rz_inquiry/rz_interpreter.h | 2 +- librz/inquiry/interpreter/p/interpreter_prototype.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 621fe678403..cf6ef765c06 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -62,7 +62,7 @@ typedef struct { * (conditional) JUMP at the end of each effect. */ ut64 nop_pc_inc; - void *prop; ///< Optional state properties. Managed by individual interpreters. + void *ext; ///< Optional state extensions. Managed by individual interpreters. } RzInterpreterAbstrState; typedef enum { diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 80efe2028a7..241aa46b218 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -14,6 +14,8 @@ static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, void *plugin_data) { bool result = interpreter_prototype_eval_effect(state, effect, yield_queues, plugin_data); // TODO: Clean up local variables. + // Or maybe not? Just costs performance. And the uplifted instructions should + // always set it before, otherwise the tests don't pass. return result; } From 62381375fc9ec22adc24eec77f2cd15507276153 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 28 Nov 2025 16:46:05 +0100 Subject: [PATCH 040/334] Implement VAR --- librz/include/rz_inquiry/rz_interpreter.h | 1 + librz/inquiry/interpreter/interpreter.c | 4 +++ .../inquiry/interpreter/prototype/eval_pure.c | 26 ++++++++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index cf6ef765c06..917d50114e5 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -53,6 +53,7 @@ typedef struct { RzInterpreterAbstraction kinds; ///< The abstractions of the state. HtUP /**/ *globals; ///< Global variables (mostly registers). Indexed by DJB2 hash of global name. HtUP /**/ *locals; ///< Local variables. Indexed by DJB2 hash of the local name. + HtUP /**/ *lets; ///< Let variables. Indexed by DJB2 hash of the let name. RzInterpreterAbstrVal *pc; ///< In our RzIL implementation the PC is not part of the register file. /** * \brief The number by which the PC is incremented for a NOP instruction. diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 17fa58a0e71..16289bd3638 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -135,6 +135,7 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new(RzInterpre } } state->locals = ht_up_new(NULL, NULL); + state->lets = ht_up_new(NULL, NULL); return state; } @@ -148,6 +149,9 @@ RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbst if (state->locals) { ht_up_free(state->locals); } + if (state->lets) { + ht_up_free(state->lets); + } free(state); } diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index a1d3b14b81b..76850cb7c88 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -17,12 +17,36 @@ RZ_IPI bool interpreter_prototype_eval_pure( switch (pure->code) { default: - case RZ_IL_OP_VAR: + case RZ_IL_OP_VAR: { + ut64 vhash = pure->op.var.hash; + HtUP *ht_vals; + switch (pure->op.var.kind) { + case RZ_IL_VAR_KIND_GLOBAL: + ht_vals = state->globals; + break; + case RZ_IL_VAR_KIND_LOCAL: + ht_vals = state->locals; + break; + case RZ_IL_VAR_KIND_LOCAL_PURE: + ht_vals = state->lets; + break; + } + RzInterpreterAbstrVal *av = ht_up_find(ht_vals, vhash, NULL); + if (!av) { + RZ_LOG_ERROR("prototype: VAR failed to evaluate. The %s '%s' doesn't exist.\n", + rz_il_var_kind_name(pure->op.var.kind), + pure->op.var.v); + goto map_to_bottom; + } + copy_abstr_data(out, av->abstr_data); + break; + } case RZ_IL_OP_ITE: case RZ_IL_OP_LET: case RZ_IL_OP_B0: case RZ_IL_OP_B1: case RZ_IL_OP_BITV: + // TODO case RZ_IL_OP_CAST: case RZ_IL_OP_APPEND: // TODO From ea6daa617370d9381fdc100a3df7e55d881c868e Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 28 Nov 2025 17:00:55 +0100 Subject: [PATCH 041/334] Implement LET --- .../inquiry/interpreter/prototype/eval_pure.c | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 76850cb7c88..ed5dfe3e98c 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -41,8 +41,30 @@ RZ_IPI bool interpreter_prototype_eval_pure( copy_abstr_data(out, av->abstr_data); break; } + case RZ_IL_OP_LET: { + ut64 vhash = pure->op.let.hash; + if (!interpreter_prototype_eval_pure(state, pure->op.let.exp, out, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: LET expression failed to evaluate.\n"); + goto map_to_bottom; + } + RzInterpreterAbstrVal *av = ht_up_find(state->lets, vhash, NULL); + if (!av) { + av = RZ_NEW(RzInterpreterAbstrVal); + av->kind = RZ_INTERPRETER_ABSTRACTION_CONST; + av->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); + ht_up_insert(state->lets, vhash, av); + } + copy_abstr_data(av->abstr_data, out); + // Evaluate body + if (!interpreter_prototype_eval_pure(state, pure->op.let.body, out, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: LET body failed to evaluate.\n"); + goto map_to_bottom; + } + // No need to free the LET variable. + // It is simply overwritten next time. + break; + } case RZ_IL_OP_ITE: - case RZ_IL_OP_LET: case RZ_IL_OP_B0: case RZ_IL_OP_B1: case RZ_IL_OP_BITV: From fb933e2c47ae1ad053ed2341290427545ada46db Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 28 Nov 2025 17:11:56 +0100 Subject: [PATCH 042/334] Add helpers to read and write variables to/from state. --- librz/inquiry/interpreter/prototype/eval.c | 53 +++++++++++++++++++ librz/inquiry/interpreter/prototype/eval.h | 2 + .../interpreter/prototype/eval_effect.c | 13 ++--- .../inquiry/interpreter/prototype/eval_pure.c | 26 +-------- 4 files changed, 61 insertions(+), 33 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 3339838e980..100ea8d3f3c 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -13,3 +13,56 @@ void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) dst->bv = rz_bv_dup(src->bv); dst->is_concrete = src->is_concrete; } + +void write_var_to_state(RzInterpreterAbstrState *state, + RzILVarKind kind, + ut64 var_id, + const ProtoIntrprAbstrData *data) { + HtUP *ht_vals; + switch (kind) { + case RZ_IL_VAR_KIND_GLOBAL: + ht_vals = state->globals; + break; + case RZ_IL_VAR_KIND_LOCAL: + ht_vals = state->locals; + break; + case RZ_IL_VAR_KIND_LOCAL_PURE: + ht_vals = state->lets; + break; + } + RzInterpreterAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); + if (!av) { + av = RZ_NEW(RzInterpreterAbstrVal); + av->kind = RZ_INTERPRETER_ABSTRACTION_CONST; + av->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); + ht_up_insert(ht_vals, var_id, av); + } + copy_abstr_data(av->abstr_data, data); +} + +bool read_var_from_state(RzInterpreterAbstrState *state, + RzILVarKind kind, + ut64 var_id, + RZ_OUT ProtoIntrprAbstrData *data) { + HtUP *ht_vals; + switch (kind) { + case RZ_IL_VAR_KIND_GLOBAL: + ht_vals = state->globals; + break; + case RZ_IL_VAR_KIND_LOCAL: + ht_vals = state->locals; + break; + case RZ_IL_VAR_KIND_LOCAL_PURE: + ht_vals = state->lets; + break; + } + RzInterpreterAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); + if (!av) { + // Variable doesn't exist. + // This should never happen and is a bug. + rz_warn_if_reached(); + return false; + } + copy_abstr_data(data, av->abstr_data); + return true; +} diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index b1dc7417adb..fcf992c3ecb 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -48,6 +48,8 @@ static inline RZ_OWN ProtoIntrprAbstrData *adata_from_bv(const RzBitVector *bv) } void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src); +void write_var_to_state(RzInterpreterAbstrState *state, RzILVarKind kind, ut64 var_id, const ProtoIntrprAbstrData *data); +bool read_var_from_state(RzInterpreterAbstrState *state, RzILVarKind kind, ut64 var_id, RZ_OUT ProtoIntrprAbstrData *data); RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 1e844ac6f7b..387bfcea359 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -41,15 +41,10 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, if (!interpreter_prototype_eval_pure(state, effect->op.set.x, &eval_out, yield_queues, plugin_data)) { goto error; } - HtUP *ht_vals = effect->op.set.is_local ? state->locals : state->globals; - RzInterpreterAbstrVal *av = ht_up_find(ht_vals, vhash, NULL); - if (!av) { - av = RZ_NEW(RzInterpreterAbstrVal); - av->kind = RZ_INTERPRETER_ABSTRACTION_CONST; - av->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); - ht_up_insert(ht_vals, vhash, av); - } - copy_abstr_data(av->abstr_data, &eval_out); + write_var_to_state(state, + effect->op.set.is_local ? RZ_IL_VAR_KIND_LOCAL : RZ_IL_VAR_KIND_GLOBAL, + vhash, + &eval_out); break; } case RZ_IL_OP_JMP: { diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index ed5dfe3e98c..322c4d2bdfe 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -18,27 +18,12 @@ RZ_IPI bool interpreter_prototype_eval_pure( switch (pure->code) { default: case RZ_IL_OP_VAR: { - ut64 vhash = pure->op.var.hash; - HtUP *ht_vals; - switch (pure->op.var.kind) { - case RZ_IL_VAR_KIND_GLOBAL: - ht_vals = state->globals; - break; - case RZ_IL_VAR_KIND_LOCAL: - ht_vals = state->locals; - break; - case RZ_IL_VAR_KIND_LOCAL_PURE: - ht_vals = state->lets; - break; - } - RzInterpreterAbstrVal *av = ht_up_find(ht_vals, vhash, NULL); - if (!av) { + if (!read_var_from_state(state, pure->op.var.kind, pure->op.var.hash, out)) { RZ_LOG_ERROR("prototype: VAR failed to evaluate. The %s '%s' doesn't exist.\n", rz_il_var_kind_name(pure->op.var.kind), pure->op.var.v); goto map_to_bottom; } - copy_abstr_data(out, av->abstr_data); break; } case RZ_IL_OP_LET: { @@ -47,14 +32,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RZ_LOG_ERROR("prototype: LET expression failed to evaluate.\n"); goto map_to_bottom; } - RzInterpreterAbstrVal *av = ht_up_find(state->lets, vhash, NULL); - if (!av) { - av = RZ_NEW(RzInterpreterAbstrVal); - av->kind = RZ_INTERPRETER_ABSTRACTION_CONST; - av->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); - ht_up_insert(state->lets, vhash, av); - } - copy_abstr_data(av->abstr_data, out); + write_var_to_state(state, RZ_IL_VAR_KIND_LOCAL_PURE, vhash, out); // Evaluate body if (!interpreter_prototype_eval_pure(state, pure->op.let.body, out, yield_queues, plugin_data)) { RZ_LOG_ERROR("prototype: LET body failed to evaluate.\n"); From 03e7ae517dad53d2ba8b230dc9e9aface54071ab Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 28 Nov 2025 17:13:12 +0100 Subject: [PATCH 043/334] include cleanup --- librz/inquiry/interpreter/prototype/eval.c | 1 - librz/inquiry/interpreter/prototype/eval.h | 6 +++--- librz/inquiry/interpreter/prototype/eval_effect.c | 1 - librz/inquiry/interpreter/prototype/eval_pure.c | 2 -- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 100ea8d3f3c..5d26f3e9535 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only #include "eval.h" -#include "rz_util/rz_bitvector.h" void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) { // TODO: For performance it should really just copy the data between bit vectors diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index fcf992c3ecb..dbb5301b510 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -4,10 +4,10 @@ #ifndef PROTOYPE_EVAL_H #define PROTOYPE_EVAL_H -#include "rz_types.h" -#include "rz_util/rz_bitvector.h" -#include +#include +#include #include +#include /** * \brief Abstract data getter from the RzInterpreterAbstrVal diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 387bfcea359..1876ec27eb7 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only #include "eval.h" -#include "rz_util/rz_bitvector.h" RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 322c4d2bdfe..ba86ff0e211 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -2,8 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only #include "eval.h" -#include "rz_il/rz_il_opcodes.h" -#include "rz_util/rz_bitvector.h" /** * \brief Evaluate a pure. From 77bf2f6c4730aadcf2acc48ef55e6e081a3e9ded Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 28 Nov 2025 17:56:24 +0100 Subject: [PATCH 044/334] Add helper to determine truthiness of abstract value. --- librz/inquiry/interpreter/prototype/eval.c | 13 +++++++++++++ librz/inquiry/interpreter/prototype/eval.h | 1 + librz/inquiry/interpreter/prototype/eval_effect.c | 4 +--- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 5d26f3e9535..0f27d17f74d 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -65,3 +65,16 @@ bool read_var_from_state(RzInterpreterAbstrState *state, copy_abstr_data(data, av->abstr_data); return true; } + +// Returns true if the bit vector in \p data is not zero. If it is zero or +// the abstract data is not concrete it returns false. +// +// TODO: The assumption that true != 0 is invalid. +// It depends on the architecture and must be decided by the RzArch plugin. +// State is passed due to this here as well. To make later refactoring easier. +bool abstr_is_true(const RzInterpreterAbstrState *state, const ProtoIntrprAbstrData *data) { + if (!data->is_concrete) { + return false; + } + return rz_bv_is_zero_vector(data->bv); +} diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index dbb5301b510..02ea2467126 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -50,6 +50,7 @@ static inline RZ_OWN ProtoIntrprAbstrData *adata_from_bv(const RzBitVector *bv) void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src); void write_var_to_state(RzInterpreterAbstrState *state, RzILVarKind kind, ut64 var_id, const ProtoIntrprAbstrData *data); bool read_var_from_state(RzInterpreterAbstrState *state, RzILVarKind kind, ut64 var_id, RZ_OUT ProtoIntrprAbstrData *data); +bool abstr_is_true(const RzInterpreterAbstrState *state, const ProtoIntrprAbstrData *data); RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 1876ec27eb7..9473331c08b 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -63,9 +63,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, break; } - // TODO: The assumption that 0 == false is invalid. - // It depends on the architecture and must be decided by the RzArch plugin. - if (rz_bv_is_zero_vector(eval_out.bv)) { + if (abstr_is_true(state, &eval_out)) { if (!interpreter_prototype_eval_effect(state, effect->op.branch.false_eff, yield_queues, plugin_data)) { goto error; } From d38e777dbf7c23382beb997beae34a6842db984b Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 28 Nov 2025 17:50:40 +0100 Subject: [PATCH 045/334] Implement ITE --- .../inquiry/interpreter/prototype/eval_pure.c | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index ba86ff0e211..480ccb44795 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -40,7 +40,29 @@ RZ_IPI bool interpreter_prototype_eval_pure( // It is simply overwritten next time. break; } - case RZ_IL_OP_ITE: + case RZ_IL_OP_ITE: { + if (!interpreter_prototype_eval_pure(state, pure->op.ite.condition, out, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: ITE condition failed to evaluate.\n"); + goto map_to_bottom; + } + if (!out->is_concrete) { + // Can't decide which pure to evaluate. + goto map_to_bottom; + } + + if (abstr_is_true(state, out)) { + if (!interpreter_prototype_eval_pure(state, pure->op.ite.x, out, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: ITE x failed to evaluate.\n"); + goto map_to_bottom; + } + } else { + if (!interpreter_prototype_eval_pure(state, pure->op.ite.y, out, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: ITE y failed to evaluate.\n"); + goto map_to_bottom; + } + } + break; + } case RZ_IL_OP_B0: case RZ_IL_OP_B1: case RZ_IL_OP_BITV: From 18716c9ec7395407aa60d76fc1559934f9557eef Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 28 Nov 2025 18:03:57 +0100 Subject: [PATCH 046/334] Implement B0, B1 --- librz/inquiry/interpreter/prototype/eval_pure.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 480ccb44795..f0a425ec2f7 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -64,7 +64,14 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } case RZ_IL_OP_B0: + rz_bv_set_all(out->bv, false); + out->is_concrete = true; + break; case RZ_IL_OP_B1: + rz_bv_set_all(out->bv, false); + rz_bv_set(out->bv, 0, true); + out->is_concrete = true; + break; case RZ_IL_OP_BITV: // TODO case RZ_IL_OP_CAST: From e18a4654122c567abc177a84cd6c34f69bf3f785 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 1 Dec 2025 15:38:35 +0100 Subject: [PATCH 047/334] Implement CAST --- .../inquiry/interpreter/prototype/eval_pure.c | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index f0a425ec2f7..ba6e2988f16 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -72,9 +72,27 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_set(out->bv, 0, true); out->is_concrete = true; break; + case RZ_IL_OP_CAST: { + if (!interpreter_prototype_eval_pure(state, pure->op.cast.val, out, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: CAST val failed to evaluate.\n"); + goto map_to_bottom; + } + if (!out->is_concrete) { + break; + } + STACK_ABSTR_DATA_OUT(fill_bit); + if (!interpreter_prototype_eval_pure(state, pure->op.cast.fill, &fill_bit, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: CAST fill failed to evaluate.\n"); + goto map_to_bottom; + } + if (!fill_bit.is_concrete) { + break; + } + rz_bv_cast_inplace(out->bv, pure->op.cast.length, abstr_is_true(state, &fill_bit)); + break; + } case RZ_IL_OP_BITV: // TODO - case RZ_IL_OP_CAST: case RZ_IL_OP_APPEND: // TODO case RZ_IL_OP_INV: From 8abdb0cf2c3aa9df409f83d01606bc7bbb37a0d2 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 2 Dec 2025 17:12:42 +0100 Subject: [PATCH 048/334] Stack allocated bv --- librz/inquiry/interpreter/prototype/eval.h | 11 ++++++----- librz/inquiry/interpreter/prototype/eval_effect.c | 2 ++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 02ea2467126..fa40352c28c 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -28,13 +28,14 @@ typedef struct { /** * \brief Initializes an AbstractData object on the stack. - * The bit vector in it is for now hard coded to 0x2000 bytes. - * TODO: This won't matter anymore, when in-place casting - * and appending of bit vectors is implemented. + * The bitvector pre-allocates 0x1000 bytes on the stack for large bit vectors + * (0x1000 * 8 = 32768 bits). + * Any value larger than these bits will be stored in heap allocated memory. + * Because of this the bit vector should always be passed to rz_bv_fini() after usage. */ #define STACK_ABSTR_DATA_OUT(name) \ - ut8 _##name##_bv_large_buf[0x2000 / 8] = { 0 }; \ - RzBitVector _##name##_bv_large = { .len = 0x2000, ._elem_len = 0x2000 / 8, .bits.large_a = _##name##_bv_large_buf }; \ + ut8 _##name##_bv_large_buf[0x1000] = { 0 }; \ + RzBitVector _##name##_bv_large = { .len = 0x1000, ._elem_len = 0x1000, .bits.large_a = _##name##_bv_large_buf, .stack_alloc = true }; \ ProtoIntrprAbstrData name = { .is_concrete = false, .bv = &_##name##_bv_large }; /** diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 9473331c08b..d18b0e0c18f 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -82,7 +82,9 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, // Ignore for now. break; } + rz_bv_fini(eval_out.bv); return true; error: + rz_bv_fini(eval_out.bv); return false; } From 6cba7a59b4a7ed38620fc211c93d03c4af9bbd84 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 2 Dec 2025 17:16:28 +0100 Subject: [PATCH 049/334] Implement BITV --- librz/inquiry/interpreter/prototype/eval_pure.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index ba6e2988f16..5c84c07825d 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -92,7 +92,10 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } case RZ_IL_OP_BITV: - // TODO + rz_bv_cast_inplace(out->bv, rz_bv_len(pure->op.bitv.value), false); + rz_bv_copy(out->bv, pure->op.bitv.value); + out->is_concrete = true; + break; case RZ_IL_OP_APPEND: // TODO case RZ_IL_OP_INV: From 6f68fde291e2f7d5c35e095d800d3dcc5900fb35 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 2 Dec 2025 17:19:34 +0100 Subject: [PATCH 050/334] Copy bit vectors when copying abstract data. --- librz/inquiry/interpreter/prototype/eval.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 0f27d17f74d..e2811c58088 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -2,14 +2,11 @@ // SPDX-License-Identifier: LGPL-3.0-only #include "eval.h" +#include void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) { - // TODO: For performance it should really just copy the data between bit vectors - // Not freeing the old one and allocating a new one with rz_bv_dup(). - // But this has to wait until we can copy bit vectors between on using an array - // and one using an ut64 to store its bits. - rz_bv_free(dst->bv); - dst->bv = rz_bv_dup(src->bv); + rz_bv_cast_inplace(dst->bv, rz_bv_len(src->bv), false); + rz_bv_copy(dst->bv, src->bv); dst->is_concrete = src->is_concrete; } From 28118eae5015da9b96ad4a39540e2c807d911b9b Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 2 Dec 2025 17:26:38 +0100 Subject: [PATCH 051/334] Implement APPEND --- .../inquiry/interpreter/prototype/eval_pure.c | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 5c84c07825d..3c1c6dc0cb8 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -96,8 +96,30 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_copy(out->bv, pure->op.bitv.value); out->is_concrete = true; break; - case RZ_IL_OP_APPEND: - // TODO + case RZ_IL_OP_APPEND: { + STACK_ABSTR_DATA_OUT(high); + if (!interpreter_prototype_eval_pure(state, pure->op.append.high, &high, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: APPEND high failed to evaluate.\n"); + goto map_to_bottom; + } + if (!high.is_concrete) { + rz_bv_fini(high.bv); + goto map_to_bottom; + } + if (!interpreter_prototype_eval_pure(state, pure->op.append.low, out, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: APPEND low failed to evaluate.\n"); + goto map_to_bottom; + } + if (!out->is_concrete) { + rz_bv_fini(high.bv); + goto map_to_bottom; + } + rz_bv_cast_inplace(out->bv, rz_bv_len(out->bv) + rz_bv_len(high.bv), false); + rz_bv_copy_nbits(high.bv, 0, out->bv, rz_bv_len(out->bv), rz_bv_len(high.bv)); + out->is_concrete = true; + rz_bv_fini(high.bv); + break; + } case RZ_IL_OP_INV: case RZ_IL_OP_AND: case RZ_IL_OP_OR: From e58ea9ac5a62581c1c2d3a737b91a8cbcb4b846e Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 2 Dec 2025 17:49:13 +0100 Subject: [PATCH 052/334] Implement INV --- librz/inquiry/interpreter/prototype/eval_pure.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 3c1c6dc0cb8..973f260d55b 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -121,6 +121,14 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } case RZ_IL_OP_INV: + if (!interpreter_prototype_eval_pure(state, pure->op.boolinv.x, out, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: INV x failed to evaluate.\n"); + goto map_to_bottom; + } + if (out->is_concrete) { + rz_bv_not_inplace(out->bv); + } + break; case RZ_IL_OP_AND: case RZ_IL_OP_OR: case RZ_IL_OP_XOR: From b4b8909167472df5d311a754a1b70ba276a4a688 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 2 Dec 2025 17:54:28 +0100 Subject: [PATCH 053/334] Enforce B0 and B1 to be 1bit wide. --- librz/inquiry/interpreter/prototype/eval_pure.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 973f260d55b..a3c27eeb520 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only #include "eval.h" +#include /** * \brief Evaluate a pure. @@ -64,11 +65,16 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } case RZ_IL_OP_B0: - rz_bv_set_all(out->bv, false); + if (rz_bv_len(out->bv) != 1) { + rz_bv_cast_inplace(out->bv, 1, false); + } + rz_bv_set(out->bv, 0, false); out->is_concrete = true; break; case RZ_IL_OP_B1: - rz_bv_set_all(out->bv, false); + if (rz_bv_len(out->bv) != 1) { + rz_bv_cast_inplace(out->bv, 1, false); + } rz_bv_set(out->bv, 0, true); out->is_concrete = true; break; From 1062a5e0ac8d805a297c469d8bdc3e9f6a3e0db1 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 2 Dec 2025 17:55:13 +0100 Subject: [PATCH 054/334] Add missing fini --- librz/inquiry/interpreter/prototype/eval_pure.c | 1 + 1 file changed, 1 insertion(+) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index a3c27eeb520..06828ce1827 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -114,6 +114,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( } if (!interpreter_prototype_eval_pure(state, pure->op.append.low, out, yield_queues, plugin_data)) { RZ_LOG_ERROR("prototype: APPEND low failed to evaluate.\n"); + rz_bv_fini(high.bv); goto map_to_bottom; } if (!out->is_concrete) { From 4887012f49a2aba48a87c41bb00e58075a1f02f1 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 2 Dec 2025 18:38:44 +0100 Subject: [PATCH 055/334] Implement AND --- .../inquiry/interpreter/prototype/eval_pure.c | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 06828ce1827..76822a594f2 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -137,6 +137,28 @@ RZ_IPI bool interpreter_prototype_eval_pure( } break; case RZ_IL_OP_AND: + if (!interpreter_prototype_eval_pure(state, pure->op.booland.x, out, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: AND x failed to evaluate.\n"); + goto map_to_bottom; + } + if (!out->is_concrete) { + goto map_to_bottom; + } + STACK_ABSTR_DATA_OUT(y); + if (!interpreter_prototype_eval_pure(state, pure->op.booland.y, &y, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: AND y failed to evaluate.\n"); + goto map_to_bottom; + } + if (!y.is_concrete) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + if (!rz_bv_and_inplace(out->bv, y.bv)) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + rz_bv_fini(y.bv); + break; case RZ_IL_OP_OR: case RZ_IL_OP_XOR: case RZ_IL_OP_MSB: From 8f685ee44220ffe07d043f5ae49f97f0ccd17299 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 2 Dec 2025 18:52:14 +0100 Subject: [PATCH 056/334] Implement OR --- .../inquiry/interpreter/prototype/eval_pure.c | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 76822a594f2..e3be3e33ac1 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -136,7 +136,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_not_inplace(out->bv); } break; - case RZ_IL_OP_AND: + case RZ_IL_OP_AND: { if (!interpreter_prototype_eval_pure(state, pure->op.booland.x, out, yield_queues, plugin_data)) { RZ_LOG_ERROR("prototype: AND x failed to evaluate.\n"); goto map_to_bottom; @@ -159,7 +159,31 @@ RZ_IPI bool interpreter_prototype_eval_pure( } rz_bv_fini(y.bv); break; - case RZ_IL_OP_OR: + } + case RZ_IL_OP_OR: { + if (!interpreter_prototype_eval_pure(state, pure->op.boolor.x, out, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: OR x failed to evaluate.\n"); + goto map_to_bottom; + } + if (!out->is_concrete) { + goto map_to_bottom; + } + STACK_ABSTR_DATA_OUT(y); + if (!interpreter_prototype_eval_pure(state, pure->op.boolor.y, &y, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: OR y failed to evaluate.\n"); + goto map_to_bottom; + } + if (!y.is_concrete) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + if (!rz_bv_or_inplace(out->bv, y.bv)) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + rz_bv_fini(y.bv); + break; + } case RZ_IL_OP_XOR: case RZ_IL_OP_MSB: case RZ_IL_OP_LSB: From bdcc0bbb6ee6db2ef0f0c83e94978e87919e0cd7 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 2 Dec 2025 19:07:27 +0100 Subject: [PATCH 057/334] Implement XOR --- .../inquiry/interpreter/prototype/eval_pure.c | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index e3be3e33ac1..d103146ab64 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -184,7 +184,30 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(y.bv); break; } - case RZ_IL_OP_XOR: + case RZ_IL_OP_XOR: { + if (!interpreter_prototype_eval_pure(state, pure->op.boolxor.x, out, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: XOR x failed to evaluate.\n"); + goto map_to_bottom; + } + if (!out->is_concrete) { + goto map_to_bottom; + } + STACK_ABSTR_DATA_OUT(y); + if (!interpreter_prototype_eval_pure(state, pure->op.boolxor.y, &y, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: XOR y failed to evaluate.\n"); + goto map_to_bottom; + } + if (!y.is_concrete) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + if (!rz_bv_xor_inplace(out->bv, y.bv)) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + rz_bv_fini(y.bv); + break; + } case RZ_IL_OP_MSB: case RZ_IL_OP_LSB: case RZ_IL_OP_IS_ZERO: From 8bdea41886135c529ebd3ed73da1bc6f1c42d018 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 2 Dec 2025 20:00:55 +0100 Subject: [PATCH 058/334] Implement LOGAND, LOGOR, LOGNOT, LOGXOR --- librz/inquiry/interpreter/prototype/eval_pure.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index d103146ab64..53220375b82 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -127,6 +127,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(high.bv); break; } + case RZ_IL_OP_LOGNOT: case RZ_IL_OP_INV: if (!interpreter_prototype_eval_pure(state, pure->op.boolinv.x, out, yield_queues, plugin_data)) { RZ_LOG_ERROR("prototype: INV x failed to evaluate.\n"); @@ -136,6 +137,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_not_inplace(out->bv); } break; + case RZ_IL_OP_LOGAND: case RZ_IL_OP_AND: { if (!interpreter_prototype_eval_pure(state, pure->op.booland.x, out, yield_queues, plugin_data)) { RZ_LOG_ERROR("prototype: AND x failed to evaluate.\n"); @@ -160,6 +162,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(y.bv); break; } + case RZ_IL_OP_LOGOR: case RZ_IL_OP_OR: { if (!interpreter_prototype_eval_pure(state, pure->op.boolor.x, out, yield_queues, plugin_data)) { RZ_LOG_ERROR("prototype: OR x failed to evaluate.\n"); @@ -184,6 +187,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(y.bv); break; } + case RZ_IL_OP_LOGXOR: case RZ_IL_OP_XOR: { if (!interpreter_prototype_eval_pure(state, pure->op.boolxor.x, out, yield_queues, plugin_data)) { RZ_LOG_ERROR("prototype: XOR x failed to evaluate.\n"); @@ -212,12 +216,8 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_LSB: case RZ_IL_OP_IS_ZERO: case RZ_IL_OP_NEG: - case RZ_IL_OP_LOGNOT: case RZ_IL_OP_ADD: case RZ_IL_OP_SUB: - case RZ_IL_OP_LOGAND: - case RZ_IL_OP_LOGOR: - case RZ_IL_OP_LOGXOR: case RZ_IL_OP_SHIFTR: case RZ_IL_OP_SHIFTL: case RZ_IL_OP_EQ: From 5bb76f6daac9b0dbfea9d9d7265705360ca8960a Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 2 Dec 2025 20:16:07 +0100 Subject: [PATCH 059/334] Implement MSB, LSB, IS_ZERO --- .../inquiry/interpreter/prototype/eval_pure.c | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 53220375b82..af6dc88fc59 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -212,9 +212,40 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(y.bv); break; } - case RZ_IL_OP_MSB: - case RZ_IL_OP_LSB: case RZ_IL_OP_IS_ZERO: + case RZ_IL_OP_LSB: + case RZ_IL_OP_MSB: { + bool (*truth_test)(const RzBitVector *bv); + RzILOpBitVector *bv; + switch (pure->code) { + default: + rz_warn_if_reached(); + goto map_to_bottom; + case RZ_IL_OP_IS_ZERO: + bv = pure->op.is_zero.bv; + truth_test = rz_bv_is_zero_vector; + break; + case RZ_IL_OP_LSB: + bv = pure->op.lsb.bv; + truth_test = rz_bv_lsb; + break; + case RZ_IL_OP_MSB: + bv = pure->op.msb.bv; + truth_test = rz_bv_msb; + break; + } + if (!interpreter_prototype_eval_pure(state, bv, out, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: MSB bv failed to evaluate.\n"); + goto map_to_bottom; + } + if (!out->is_concrete) { + goto map_to_bottom; + } + rz_bv_cast_inplace(out->bv, 1, false); + // TODO: Truth bit. + rz_bv_set(out->bv, 0, truth_test(out->bv)); + break; + } case RZ_IL_OP_NEG: case RZ_IL_OP_ADD: case RZ_IL_OP_SUB: From bca1c732da0fba904748c4b14aedd464e4f57c3c Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 2 Dec 2025 20:36:20 +0100 Subject: [PATCH 060/334] Fix access of two operand bit functions. --- .../inquiry/interpreter/prototype/eval_pure.c | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index af6dc88fc59..88c9ea09162 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -128,8 +128,9 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } case RZ_IL_OP_LOGNOT: - case RZ_IL_OP_INV: - if (!interpreter_prototype_eval_pure(state, pure->op.boolinv.x, out, yield_queues, plugin_data)) { + case RZ_IL_OP_INV: { + RzILOpPure *x = pure->code == RZ_IL_OP_INV ? pure->op.boolinv.x : pure->op.lognot.bv; + if (!interpreter_prototype_eval_pure(state, x, out, yield_queues, plugin_data)) { RZ_LOG_ERROR("prototype: INV x failed to evaluate.\n"); goto map_to_bottom; } @@ -137,9 +138,12 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_not_inplace(out->bv); } break; + } case RZ_IL_OP_LOGAND: case RZ_IL_OP_AND: { - if (!interpreter_prototype_eval_pure(state, pure->op.booland.x, out, yield_queues, plugin_data)) { + RzILOpPure *px = pure->code == RZ_IL_OP_AND ? pure->op.booland.x : pure->op.logand.x; + RzILOpPure *py = pure->code == RZ_IL_OP_AND ? pure->op.booland.y : pure->op.logand.y; + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data)) { RZ_LOG_ERROR("prototype: AND x failed to evaluate.\n"); goto map_to_bottom; } @@ -147,7 +151,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, pure->op.booland.y, &y, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data)) { RZ_LOG_ERROR("prototype: AND y failed to evaluate.\n"); goto map_to_bottom; } @@ -164,7 +168,9 @@ RZ_IPI bool interpreter_prototype_eval_pure( } case RZ_IL_OP_LOGOR: case RZ_IL_OP_OR: { - if (!interpreter_prototype_eval_pure(state, pure->op.boolor.x, out, yield_queues, plugin_data)) { + RzILOpPure *px = pure->code == RZ_IL_OP_OR ? pure->op.boolor.x : pure->op.logor.x; + RzILOpPure *py = pure->code == RZ_IL_OP_OR ? pure->op.boolor.y : pure->op.logor.y; + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data)) { RZ_LOG_ERROR("prototype: OR x failed to evaluate.\n"); goto map_to_bottom; } @@ -172,7 +178,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, pure->op.boolor.y, &y, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data)) { RZ_LOG_ERROR("prototype: OR y failed to evaluate.\n"); goto map_to_bottom; } @@ -189,7 +195,9 @@ RZ_IPI bool interpreter_prototype_eval_pure( } case RZ_IL_OP_LOGXOR: case RZ_IL_OP_XOR: { - if (!interpreter_prototype_eval_pure(state, pure->op.boolxor.x, out, yield_queues, plugin_data)) { + RzILOpPure *px = pure->code == RZ_IL_OP_XOR ? pure->op.boolxor.x : pure->op.logxor.x; + RzILOpPure *py = pure->code == RZ_IL_OP_XOR ? pure->op.boolxor.y : pure->op.logxor.y; + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data)) { RZ_LOG_ERROR("prototype: XOR x failed to evaluate.\n"); goto map_to_bottom; } @@ -197,7 +205,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, pure->op.boolxor.y, &y, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data)) { RZ_LOG_ERROR("prototype: XOR y failed to evaluate.\n"); goto map_to_bottom; } From a0db0f96d6807a354fa454e082014a37606fb07a Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 2 Dec 2025 20:37:36 +0100 Subject: [PATCH 061/334] Implement NEG --- librz/inquiry/interpreter/prototype/eval_pure.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 88c9ea09162..469c248a0b7 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -254,7 +254,16 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_set(out->bv, 0, truth_test(out->bv)); break; } - case RZ_IL_OP_NEG: + case RZ_IL_OP_NEG: { + if (!interpreter_prototype_eval_pure(state, pure->op.neg.bv, out, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: NEG bv failed to evaluate.\n"); + goto map_to_bottom; + } + if (out->is_concrete) { + rz_bv_neg_inplace(out->bv); + } + break; + } case RZ_IL_OP_ADD: case RZ_IL_OP_SUB: case RZ_IL_OP_SHIFTR: From 8819e0320760cc0f7b42f872d8945a20ab56b2c0 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 4 Dec 2025 15:17:26 +0100 Subject: [PATCH 062/334] Implement ADD --- .../inquiry/interpreter/prototype/eval_pure.c | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 469c248a0b7..addffb0f02c 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -264,7 +264,32 @@ RZ_IPI bool interpreter_prototype_eval_pure( } break; } - case RZ_IL_OP_ADD: + case RZ_IL_OP_ADD: { + RzILOpPure *px = pure->op.add.x; + RzILOpPure *py = pure->op.add.y; + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: ADD x failed to evaluate.\n"); + goto map_to_bottom; + } + if (!out->is_concrete) { + goto map_to_bottom; + } + STACK_ABSTR_DATA_OUT(y); + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: ADD y failed to evaluate.\n"); + goto map_to_bottom; + } + if (!y.is_concrete) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + if (!rz_bv_add_inplace(out->bv, y.bv, NULL)) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + rz_bv_fini(y.bv); + break; + } case RZ_IL_OP_SUB: case RZ_IL_OP_SHIFTR: case RZ_IL_OP_SHIFTL: From e29da7db395e9bf8263db2dbf69822074edfaf76 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 4 Dec 2025 16:14:49 +0100 Subject: [PATCH 063/334] Implement SUB --- .../inquiry/interpreter/prototype/eval_pure.c | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index addffb0f02c..df949db94e4 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -290,7 +290,32 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(y.bv); break; } - case RZ_IL_OP_SUB: + case RZ_IL_OP_SUB: { + RzILOpPure *px = pure->op.sub.x; + RzILOpPure *py = pure->op.sub.y; + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); + goto map_to_bottom; + } + if (!out->is_concrete) { + goto map_to_bottom; + } + STACK_ABSTR_DATA_OUT(y); + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: SUB y failed to evaluate.\n"); + goto map_to_bottom; + } + if (!y.is_concrete) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + if (!rz_bv_sub_inplace(out->bv, y.bv, NULL)) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + rz_bv_fini(y.bv); + break; + } case RZ_IL_OP_SHIFTR: case RZ_IL_OP_SHIFTL: case RZ_IL_OP_EQ: From 7d0f37a31699be0e14a08fe55fbb46d73790a22c Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 4 Dec 2025 16:26:34 +0100 Subject: [PATCH 064/334] Implement SHIFTR SHIFTL --- .../inquiry/interpreter/prototype/eval_pure.c | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index df949db94e4..8237fe3ab5e 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -316,8 +316,48 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(y.bv); break; } - case RZ_IL_OP_SHIFTR: case RZ_IL_OP_SHIFTL: + case RZ_IL_OP_SHIFTR: { + RzILOpPure *px = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.x : pure->op.shiftl.x; + RzILOpPure *py = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.y : pure->op.shiftl.y; + RzILOpPure *pfill_bit = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.fill_bit : pure->op.shiftl.fill_bit; + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: SHIFT(L/R) x failed to evaluate.\n"); + goto map_to_bottom; + } + if (!out->is_concrete) { + goto map_to_bottom; + } + STACK_ABSTR_DATA_OUT(y); + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: SHIFT(L/R) y failed to evaluate.\n"); + goto map_to_bottom; + } + if (!y.is_concrete) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + STACK_ABSTR_DATA_OUT(fill_bit); + if (!interpreter_prototype_eval_pure(state, pfill_bit, &fill_bit, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: SHIFT(L/R) fill_bit failed to evaluate.\n"); + goto map_to_bottom; + } + if (!fill_bit.is_concrete) { + rz_bv_fini(fill_bit.bv); + rz_bv_fini(y.bv); + goto map_to_bottom; + } + bool (*shift)(RzBitVector *bv, ut32 size, bool fill_bit); + shift = pure->code == RZ_IL_OP_SHIFTR ? rz_bv_rshift_fill : rz_bv_lshift_fill; + if (!shift(out->bv, rz_bv_to_ut64(y.bv), abstr_is_true(state, &fill_bit))) { + rz_bv_fini(fill_bit.bv); + rz_bv_fini(y.bv); + goto map_to_bottom; + } + rz_bv_fini(fill_bit.bv); + rz_bv_fini(y.bv); + break; + } case RZ_IL_OP_EQ: case RZ_IL_OP_SLE: case RZ_IL_OP_ULE: From 572263ee821443ca2341ed555ce576551363a0cd Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 4 Dec 2025 16:41:53 +0100 Subject: [PATCH 065/334] Implement EQ, ULE, SLE --- .../inquiry/interpreter/prototype/eval_pure.c | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 8237fe3ab5e..cd1af187518 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -358,10 +358,54 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(y.bv); break; } - case RZ_IL_OP_EQ: case RZ_IL_OP_SLE: case RZ_IL_OP_ULE: - // TODO + case RZ_IL_OP_EQ: { + bool (*cmp)(RzBitVector *x, RzBitVector *y); + RzILOpPure *px; + RzILOpPure *py; + switch (pure->code) { + default: + goto map_to_bottom; + case RZ_IL_OP_SLE: + px = pure->op.sle.x; + py = pure->op.sle.y; + cmp = rz_bv_sle; + break; + case RZ_IL_OP_ULE: + px = pure->op.ule.x; + py = pure->op.ule.y; + cmp = rz_bv_ule; + break; + case RZ_IL_OP_EQ: + px = pure->op.eq.x; + py = pure->op.eq.y; + cmp = rz_bv_eq; + break; + } + + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: CMP x failed to evaluate.\n"); + goto map_to_bottom; + } + if (!out->is_concrete) { + goto map_to_bottom; + } + STACK_ABSTR_DATA_OUT(y); + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data)) { + RZ_LOG_ERROR("prototype: CMP y failed to evaluate.\n"); + goto map_to_bottom; + } + if (!y.is_concrete) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + bool cmp_is_true = cmp(out->bv, y.bv); + rz_bv_cast_inplace(out->bv, 1, false); + rz_bv_set(out->bv, 0, cmp_is_true); + rz_bv_fini(y.bv); + break; + } case RZ_IL_OP_MUL: case RZ_IL_OP_DIV: case RZ_IL_OP_SDIV: From 2111a1f2063c6018f06abea647634eea29deaf44 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 6 Dec 2025 20:27:28 +0100 Subject: [PATCH 066/334] Implement store and load functionality. --- librz/include/rz_inquiry/rz_interpreter.h | 37 +++++- librz/inquiry/inquiry.c | 105 +++++++++++++++--- librz/inquiry/interpreter/interpreter.c | 13 ++- .../interpreter/p/interpreter_prototype.c | 3 + librz/inquiry/interpreter/prototype/eval.c | 65 +++++++++++ librz/inquiry/interpreter/prototype/eval.h | 25 ++++- librz/inquiry/meson.build | 2 + librz/util/bitvector.c | 2 + 8 files changed, 227 insertions(+), 25 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 917d50114e5..6dc0a736ccb 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -15,6 +15,10 @@ #include #include +/** + * \brief Only one IO request at a time is possible (currently). + */ +#define RZ_INTERPRETER_IO_QUEUE_SIZE 1 #define RZ_INTERPRETER_IL_QUEUE_SIZE 128 #define RZ_INTERPRETER_ADDR_QUEUE_SIZE 1024 #define RZ_INTERPRETER_YIELD_QUEUE_SIZE 4096 @@ -135,6 +139,8 @@ typedef struct { bool (*eval)(RZ_NONNULL RzInterpreterAbstrState *state, RZ_NONNULL const RzILOpEffect *effect, RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_request, + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_result, void *plugin_data); /** * \brief Determines the next successor addresses from state. @@ -147,15 +153,40 @@ typedef struct { void *plugin_data); } RzInterpreterPlugin; +typedef enum { + RZ_INTERPRETER_IO_READ, + RZ_INTERPRETER_IO_WRITE, +} RzInterpreterIOReqType; + +typedef struct { + RzInterpreterIOReqType type; + ut64 addr; ///< The address to read/write. + size_t n_bytes; ///< The number of bytes to read/write. + const ut8 *data; ///< The data to write. +} RzInterpreterIORequest; + +typedef struct { + const ut8 *data; ///< The data read. NULL in case of failed read. + ut64 n_bytes; ///< The number of bytes to read. +} RzInterpreterIOResR; + +typedef struct { + RzInterpreterIOReqType type; + bool req_ok; ///< Set to true if IO request succeeded. + RzInterpreterIOResR read; +} RzInterpreterIOResult; + /** * \brief The set of required queues for an interpreter to run. */ typedef struct { RzInterpreterAbstrState *state; ///< The abstract state of the interpreter. - RzThreadQueue /**/ *addr_queue; ///< The queue to send requests to the cache what address to get the next IL op from. + RzThreadQueue /**/ *addr_queue; ///< The queue to send requests to the cache what address to get the next IL op from. RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. // TODO: We need to decide how to distribute the yield. HtUP /**/ *yield_queues; ///< The queues to push the yield of interpretation into. + RzThreadQueue /**/ *io_request; ///< The queue for read/write requests to the IO layer. + RzThreadQueue /**/ *io_result; ///< The queue for the read/write requests' answers. RzAtomicBool *is_running_flag; ///< Flag for the interpreter thread to toggle when done. RzInterpreterPlugin *plugin; } RzInterpreterSet; @@ -174,9 +205,11 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, RZ_NONNULL RZ_OWN RzInterpreterAbstrState *state, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *addr_queue, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *addr_queue, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_request, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag); RZ_API void rz_interpreter_queue_set_free(RZ_NULLABLE RZ_OWN RzInterpreterSet *qset); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 919ce6d7518..71b10cb5ddc 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -6,9 +6,12 @@ #include #include "rz_analysis.h" +#include "rz_config.h" #include "rz_inquiry/rz_interpreter.h" #include "rz_inquiry_plugins.h" +#include "rz_io.h" #include "rz_reg.h" +#include "rz_th.h" #include "rz_vector.h" #include #include @@ -118,6 +121,18 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { return false; } + // Setup the IO queues. Each interpreter instance needs it's own queue at + // for writing IO. Because the writing is done on the IO cache, and each + // instance needs its own cache. + RzThreadQueue *io_request_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); + RzThreadQueue *io_result_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); + if (!io_request_q || io_result_q) { + rz_th_queue_free(il_queue); + rz_th_queue_free(io_request_q); + rz_th_queue_free(io_result_q); + return false; + } + // Add the Effect for each entry point. RzILOpEffect *eff = NULL; if (argc == 1) { @@ -125,6 +140,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { eff = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); if (!eff) { rz_th_queue_free(il_queue); + rz_th_queue_free(io_request_q); + rz_th_queue_free(io_result_q); RZ_LOG_WARN("Could not get entry point IL operation at 0x%" PFMT64x "\n", (ut64)entry_point); return false; } @@ -137,6 +154,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { eff = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); if (!eff) { rz_th_queue_free(il_queue); + rz_th_queue_free(io_request_q); + rz_th_queue_free(io_result_q); return false; } } @@ -150,6 +169,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { RzThreadQueue *addr_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, (RzListFree)rz_interpreter_addr_queue_free); if (!addr_queue) { rz_th_queue_free(il_queue); + rz_th_queue_free(io_request_q); + rz_th_queue_free(io_result_q); rz_pvector_free(il_cache); return false; } @@ -163,6 +184,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { if (!boundaries) { rz_th_queue_free(addr_queue); rz_th_queue_free(il_queue); + rz_th_queue_free(io_request_q); + rz_th_queue_free(io_result_q); rz_pvector_free(il_cache); return false; } @@ -178,6 +201,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { if (!yield_queue) { rz_th_queue_free(addr_queue); rz_th_queue_free(il_queue); + rz_th_queue_free(io_request_q); + rz_th_queue_free(io_result_q); rz_pvector_free(il_cache); return false; } @@ -190,6 +215,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { if (!yield_queue || !yield_queues) { rz_th_queue_free(addr_queue); rz_th_queue_free(il_queue); + rz_th_queue_free(io_request_q); + rz_th_queue_free(io_result_q); rz_pvector_free(il_cache); rz_interpreter_yield_queue_free(yield_queue); ht_up_free(yield_queues); @@ -209,10 +236,20 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // Later we would pass a unique iset to each interpreter with // the required queues only. // But for the prototype we have only one iset with all queues. - RzInterpreterSet *iset = rz_interpreter_set_new(rz_inquiry_plugin_interpreter_prototype.p_interpreter, abstr_state, addr_queue, il_queue, yield_queues, is_running); + RzInterpreterSet *iset = rz_interpreter_set_new( + rz_inquiry_plugin_interpreter_prototype.p_interpreter, + abstr_state, + addr_queue, + il_queue, + yield_queues, + io_request_q, + io_result_q, + is_running); if (!iset) { rz_th_queue_free(addr_queue); rz_th_queue_free(il_queue); + rz_th_queue_free(io_request_q); + rz_th_queue_free(io_result_q); rz_pvector_free(il_cache); rz_interpreter_yield_queue_free(yield_queue); ht_up_free(yield_queues); @@ -225,27 +262,65 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // Waiting for new Effects to be requested and sending them. RZ_LOG_WARN("INQUIRY: Start main interpretation thread.\n"); RzThread *iterpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); + RZ_LOG_WARN("INQUIRY: Enforce enabling IO cache.\n"); + const char *io_cache_opt = rz_config_get(core->config, "io.cache"); + rz_config_set(core->config, "io.cache", "true"); RZ_LOG_WARN("INQUIRY: Start IL providing loop.\n"); + + // Poor man's shared memory. + RzInterpreterIOResult _io_res = { 0 }; + RzInterpreterIOResult *io_res = &_io_res; + while (rz_atomic_bool_get(is_running)) { ut64 *addr = rz_th_queue_pop(addr_queue, false); - if (!addr) { - // Some artificial lag for testing. - rz_sys_usleep(rz_num_rand32(1000)); - RZ_LOG_WARN("INQUIRY: Sleep over.\n"); - continue; + if (addr) { + // This if() emulates the IL cache. + RZ_LOG_WARN("INQUIRY: Received IL request: %" PFMT64x ".\n", (*addr)); + RzILOpEffect *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); + if (!bb) { + // Stop interpreter. + rz_atomic_bool_set(is_running, false); + continue; + } + RZ_LOG_WARN("INQUIRY: Send IL result: %p.\n", bb); + rz_pvector_push(il_cache, bb); + // TODO: Free unused if too big. + rz_th_queue_push(il_queue, bb, true); } - RZ_LOG_WARN("INQUIRY: Received %" PFMT64x ".\n", (*addr)); - RzILOpEffect *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); - free(addr); - if (!bb) { - // Stop interpreter. - rz_atomic_bool_set(is_running, false); + + // This emulates the IO handler for a single(!) interpreter instances. + // In the future we should have only one IO handler + // but this requires that it can handle multiple IO write caches + // (one for each interpreter instance). + // Because this is not implemented there is only one interpreter thread for now. + RzInterpreterIORequest *io_req = rz_th_queue_pop(io_request_q, false); + if (!io_req) { continue; } - RZ_LOG_WARN("INQUIRY: Send %p.\n", bb); - rz_pvector_push(il_cache, bb); - rz_th_queue_push(il_queue, bb, true); + + RZ_LOG_WARN("INQUIRY: Received IO %s request: %" PFMT64x ".\n", + io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", + io_req->addr); + if (io_req->type == RZ_INTERPRETER_IO_READ) { + // TODO: Don't allocate here if not necessary. + ut8 *buf = RZ_NEWS0(ut8, io_req->n_bytes); + int n_read = rz_io_nread_at(core->io, io_req->addr, buf, io_req->n_bytes); + io_res->req_ok = n_read >= 0; + if (io_res->req_ok) { + io_res->read.data = buf; + io_res->read.n_bytes = n_read; + } + } else { + io_res->req_ok = rz_io_write_at(core->io, io_req->addr, io_req->data, io_req->n_bytes); + } + RZ_LOG_WARN("INQUIRY: Sent IO %s result. Success = %s.\n", + io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", + io_res->req_ok ? "true" : "false"); + rz_th_queue_push(io_result_q, io_res, true); } + + rz_config_set(core->config, "io.cache", io_cache_opt); + // Wait for thread to finish before cleaning. rz_th_wait(iterpr_th); rz_th_free(iterpr_th); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 16289bd3638..5a9dd4a3ac6 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -167,11 +167,13 @@ RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbst RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, RZ_NONNULL RZ_OWN RzInterpreterAbstrState *state, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *addr_queue, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *addr_queue, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_request, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag) { - rz_return_val_if_fail(plugin && state && plugin && addr_queue && il_queue && yield_queues, NULL); + rz_return_val_if_fail(plugin && state && plugin && addr_queue && il_queue && yield_queues && io_request && io_result && is_running_flag, NULL); RzInterpreterSet *set = RZ_NEW0(RzInterpreterSet); if (!set || (state->kinds != (plugin->supported_abstractions & state->kinds))) { @@ -188,6 +190,8 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( set->il_queue = il_queue; set->addr_queue = addr_queue; set->yield_queues = yield_queues; + set->io_request = io_request; + set->io_result = io_result; set->is_running_flag = is_running_flag; return set; } @@ -275,10 +279,12 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { if (!state_cache || !tmp_succ_addr || !succ_states || !eff || !reachable_states) { goto pre_loop_error; } + ut64 _addr = 0; + ut64 *addr = &_addr; while (true) { // Evaluate the effect on the input state. - if (!plugin->eval(in_state, eff, iset->yield_queues, plugin_data)) { + if (!plugin->eval(in_state, eff, iset->yield_queues, plugin_data, iset->io_request, iset->io_result)) { goto loop_error; } // Decrease the reference count to the input state by one. @@ -309,7 +315,6 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { } // Request the successor effects over the queue. while (!rz_vector_empty(tmp_succ_addr)) { - ut64 *addr = RZ_NEW(ut64); rz_vector_pop_front(tmp_succ_addr, addr); SuccessorState ss = { .addr = *addr, .in_state_hash = out_hash }; // The successors are pushed in the same order into the succ_states diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 241aa46b218..ec6df525b29 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -11,6 +11,8 @@ static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, RZ_NONNULL const RzILOpEffect *effect, RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_request, + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_result, void *plugin_data) { bool result = interpreter_prototype_eval_effect(state, effect, yield_queues, plugin_data); // TODO: Clean up local variables. @@ -54,6 +56,7 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da av->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); } rz_iterator_free(it); + state->ext = RZ_NEW0(RzInterpreterIORequest); return true; } diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index e2811c58088..776052fc3f7 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -2,6 +2,10 @@ // SPDX-License-Identifier: LGPL-3.0-only #include "eval.h" +#include "rz_inquiry/rz_interpreter.h" +#include "rz_th.h" +#include "rz_types.h" +#include "rz_util/rz_log.h" #include void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) { @@ -75,3 +79,64 @@ bool abstr_is_true(const RzInterpreterAbstrState *state, const ProtoIntrprAbstrD } return rz_bv_is_zero_vector(data->bv); } + +bool store_abstr_data( + RzInterpreterAbstrState *state, + ut64 addr, + const ProtoIntrprAbstrData *src, + RzThreadQueue /**/ *io_request, + RzThreadQueue /**/ *io_result) { + if (!src->is_concrete) { + // Really don't write? + return true; + } + ut8 *buf; + ut8 buf_stack[BV_STACK_MAX_SIZE] = { 0 }; + if (rz_bv_len(src->bv) > BV_STACK_MAX_SIZE * 8) { + buf = RZ_NEWS(ut8, rz_bv_len_bytes(src->bv)); + } else { + buf = buf_stack; + } + rz_bv_set_to_bytes_be(src->bv, buf); + RzInterpreterIORequest *io_req = state->ext; + io_req->type = RZ_INTERPRETER_IO_WRITE; + io_req->addr = addr; + io_req->data = buf; + + RZ_LOG_WARN("Prototype: Send store request"); + rz_th_queue_push(io_request, io_req, true); + // Wait for write being done. + RzInterpreterIOResult *io_res = rz_th_queue_wait_pop(io_result, false); + if (rz_bv_len(src->bv) > BV_STACK_MAX_SIZE * 8) { + free(buf); + } + return io_res->req_ok; +} + +bool load_abstr_data( + RzInterpreterAbstrState *state, + ut64 addr, + size_t size, + RZ_OUT ProtoIntrprAbstrData *out, + RzThreadQueue /**/ *io_request, + RzThreadQueue /**/ *io_result) { + RzInterpreterIORequest *io_req = state->ext; + io_req->type = RZ_INTERPRETER_IO_READ; + io_req->addr = addr; + io_req->n_bytes = size; + RZ_LOG_WARN("Prototype: Send load request"); + rz_th_queue_push(io_request, io_req, true); + // Wait for load being done. + RzInterpreterIOResult *io_res = rz_th_queue_wait_pop(io_result, false); + if (!io_res->req_ok) { + return false; + } + if (io_res->read.n_bytes != size) { + RZ_LOG_WARN("Prototype: Failed to read correct number of bytes."); + return false; + } + out->is_concrete = true; + rz_bv_cast_inplace(out->bv, size, 0); + rz_bv_set_from_bytes_be(out->bv, io_res->read.data, 0, io_res->read.n_bytes); + return false; +} diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index fa40352c28c..ae1b31a98d8 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -26,16 +26,20 @@ typedef struct { RzBitVector *bv; } ProtoIntrprAbstrData; +/** + * \brief In bytes + */ +#define BV_STACK_MAX_SIZE 0x1000 + /** * \brief Initializes an AbstractData object on the stack. - * The bitvector pre-allocates 0x1000 bytes on the stack for large bit vectors - * (0x1000 * 8 = 32768 bits). + * The bitvector pre-allocates BV_STACK_MAX_SIZE bytes on the stack for large bit vectors. * Any value larger than these bits will be stored in heap allocated memory. * Because of this the bit vector should always be passed to rz_bv_fini() after usage. */ #define STACK_ABSTR_DATA_OUT(name) \ - ut8 _##name##_bv_large_buf[0x1000] = { 0 }; \ - RzBitVector _##name##_bv_large = { .len = 0x1000, ._elem_len = 0x1000, .bits.large_a = _##name##_bv_large_buf, .stack_alloc = true }; \ + ut8 _##name##_bv_large_buf[BV_STACK_MAX_SIZE] = { 0 }; \ + RzBitVector _##name##_bv_large = { .len = BV_STACK_MAX_SIZE, ._elem_len = BV_STACK_MAX_SIZE, .bits.large_a = _##name##_bv_large_buf, .stack_alloc = true }; \ ProtoIntrprAbstrData name = { .is_concrete = false, .bv = &_##name##_bv_large }; /** @@ -52,6 +56,19 @@ void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) void write_var_to_state(RzInterpreterAbstrState *state, RzILVarKind kind, ut64 var_id, const ProtoIntrprAbstrData *data); bool read_var_from_state(RzInterpreterAbstrState *state, RzILVarKind kind, ut64 var_id, RZ_OUT ProtoIntrprAbstrData *data); bool abstr_is_true(const RzInterpreterAbstrState *state, const ProtoIntrprAbstrData *data); +bool store_abstr_data( + RzInterpreterAbstrState *state, + ut64 addr, + const ProtoIntrprAbstrData *src, + RzThreadQueue /**/ *io_request, + RzThreadQueue /**/ *io_result); +bool load_abstr_data( + RzInterpreterAbstrState *state, + ut64 addr, + size_t size, + RZ_OUT ProtoIntrprAbstrData *out, + RzThreadQueue /**/ *io_request, + RzThreadQueue /**/ *io_result); RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index 0aeef3b7b77..b340a2c4714 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -25,6 +25,7 @@ rz_inquiry = library('rz_inquiry', rz_inquiry_sources, dependencies: [ rz_arch_dep, # Legacy analysis module rz_bin_dep, + rz_config_dep, rz_cons_dep, rz_hash_dep, rz_il_dep, @@ -55,6 +56,7 @@ modules += { 'rz_inquiry': { 'dependencies': [ 'rz_arch', 'rz_bin', + 'rz_config', 'rz_cons', 'rz_hash', 'rz_il', diff --git a/librz/util/bitvector.c b/librz/util/bitvector.c index 15eb49e003e..015c6630f04 100644 --- a/librz/util/bitvector.c +++ b/librz/util/bitvector.c @@ -1,7 +1,9 @@ // SPDX-FileCopyrightText: 2021 heersin // SPDX-License-Identifier: LGPL-3.0-only +#include "rz_types.h" #include "rz_util.h" +#include "rz_util/rz_assert.h" #include #include From 9bca758883d885685f365a1100f16c7cf8aad3c0 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 6 Dec 2025 20:43:58 +0100 Subject: [PATCH 067/334] Pass IO queues to evaluators --- librz/inquiry/interpreter/prototype/eval.h | 4 ++ .../interpreter/prototype/eval_effect.c | 14 +++-- .../inquiry/interpreter/prototype/eval_pure.c | 56 ++++++++++--------- 3 files changed, 41 insertions(+), 33 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index ae1b31a98d8..8a1dd72676f 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -73,12 +73,16 @@ bool load_abstr_data( RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, HtUP /**/ *yield_queues, + RzThreadQueue /**/ *io_request, + RzThreadQueue /**/ *io_result, void *plugin_data); RZ_IPI bool interpreter_prototype_eval_pure( RzInterpreterAbstrState *state, const RzILOpPure *pure, RZ_OUT ProtoIntrprAbstrData *out, HtUP /**/ *yield_queues, + RzThreadQueue /**/ *io_request, + RzThreadQueue /**/ *io_result, void *plugin_data); #endif // PROTOYPE_EVAL_H diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index d18b0e0c18f..09bc16871c0 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -6,6 +6,8 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, HtUP /**/ *yield_queues, + RzThreadQueue /**/ *io_request, + RzThreadQueue /**/ *io_result, void *plugin_data) { STACK_ABSTR_DATA_OUT(eval_out); @@ -28,7 +30,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, case RZ_IL_OP_SEQ: { const RzILOpEffect *next = effect->op.seq.x; while (next) { - if (!interpreter_prototype_eval_effect(state, next, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_effect(state, next, yield_queues, plugin_data, io_request, io_result)) { goto error; } next = effect->op.seq.y; @@ -37,7 +39,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, } case RZ_IL_OP_SET: { ut64 vhash = effect->op.set.hash; - if (!interpreter_prototype_eval_pure(state, effect->op.set.x, &eval_out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, effect->op.set.x, &eval_out, yield_queues, plugin_data, io_request, io_result)) { goto error; } write_var_to_state(state, @@ -47,14 +49,14 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, break; } case RZ_IL_OP_JMP: { - if (!interpreter_prototype_eval_pure(state, effect->op.jmp.dst, &eval_out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, effect->op.jmp.dst, &eval_out, yield_queues, plugin_data, io_request, io_result)) { goto error; } copy_abstr_data(state->pc->abstr_data, &eval_out); break; } case RZ_IL_OP_BRANCH: { - if (!interpreter_prototype_eval_pure(state, effect->op.branch.condition, &eval_out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, effect->op.branch.condition, &eval_out, yield_queues, plugin_data, io_request, io_result)) { goto error; } if (!eval_out.is_concrete) { @@ -64,11 +66,11 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, } if (abstr_is_true(state, &eval_out)) { - if (!interpreter_prototype_eval_effect(state, effect->op.branch.false_eff, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_effect(state, effect->op.branch.false_eff, yield_queues, plugin_data, io_request, io_result)) { goto error; } } else { - if (!interpreter_prototype_eval_effect(state, effect->op.branch.false_eff, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_effect(state, effect->op.branch.false_eff, yield_queues, plugin_data, io_request, io_result)) { goto error; } } diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index cd1af187518..b44d4b2035d 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -12,6 +12,8 @@ RZ_IPI bool interpreter_prototype_eval_pure( const RzILOpPure *pure, RZ_OUT ProtoIntrprAbstrData *out, HtUP /**/ *yield_queues, + RzThreadQueue /**/ *io_request, + RzThreadQueue /**/ *io_result, void *plugin_data) { switch (pure->code) { @@ -27,13 +29,13 @@ RZ_IPI bool interpreter_prototype_eval_pure( } case RZ_IL_OP_LET: { ut64 vhash = pure->op.let.hash; - if (!interpreter_prototype_eval_pure(state, pure->op.let.exp, out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, pure->op.let.exp, out, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: LET expression failed to evaluate.\n"); goto map_to_bottom; } write_var_to_state(state, RZ_IL_VAR_KIND_LOCAL_PURE, vhash, out); // Evaluate body - if (!interpreter_prototype_eval_pure(state, pure->op.let.body, out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, pure->op.let.body, out, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: LET body failed to evaluate.\n"); goto map_to_bottom; } @@ -42,7 +44,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } case RZ_IL_OP_ITE: { - if (!interpreter_prototype_eval_pure(state, pure->op.ite.condition, out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, pure->op.ite.condition, out, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: ITE condition failed to evaluate.\n"); goto map_to_bottom; } @@ -52,12 +54,12 @@ RZ_IPI bool interpreter_prototype_eval_pure( } if (abstr_is_true(state, out)) { - if (!interpreter_prototype_eval_pure(state, pure->op.ite.x, out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, pure->op.ite.x, out, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: ITE x failed to evaluate.\n"); goto map_to_bottom; } } else { - if (!interpreter_prototype_eval_pure(state, pure->op.ite.y, out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, pure->op.ite.y, out, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: ITE y failed to evaluate.\n"); goto map_to_bottom; } @@ -79,7 +81,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( out->is_concrete = true; break; case RZ_IL_OP_CAST: { - if (!interpreter_prototype_eval_pure(state, pure->op.cast.val, out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, pure->op.cast.val, out, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: CAST val failed to evaluate.\n"); goto map_to_bottom; } @@ -87,7 +89,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } STACK_ABSTR_DATA_OUT(fill_bit); - if (!interpreter_prototype_eval_pure(state, pure->op.cast.fill, &fill_bit, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, pure->op.cast.fill, &fill_bit, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: CAST fill failed to evaluate.\n"); goto map_to_bottom; } @@ -104,7 +106,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; case RZ_IL_OP_APPEND: { STACK_ABSTR_DATA_OUT(high); - if (!interpreter_prototype_eval_pure(state, pure->op.append.high, &high, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, pure->op.append.high, &high, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: APPEND high failed to evaluate.\n"); goto map_to_bottom; } @@ -112,7 +114,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(high.bv); goto map_to_bottom; } - if (!interpreter_prototype_eval_pure(state, pure->op.append.low, out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, pure->op.append.low, out, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: APPEND low failed to evaluate.\n"); rz_bv_fini(high.bv); goto map_to_bottom; @@ -130,7 +132,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_LOGNOT: case RZ_IL_OP_INV: { RzILOpPure *x = pure->code == RZ_IL_OP_INV ? pure->op.boolinv.x : pure->op.lognot.bv; - if (!interpreter_prototype_eval_pure(state, x, out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, x, out, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: INV x failed to evaluate.\n"); goto map_to_bottom; } @@ -143,7 +145,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_AND: { RzILOpPure *px = pure->code == RZ_IL_OP_AND ? pure->op.booland.x : pure->op.logand.x; RzILOpPure *py = pure->code == RZ_IL_OP_AND ? pure->op.booland.y : pure->op.logand.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: AND x failed to evaluate.\n"); goto map_to_bottom; } @@ -151,7 +153,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: AND y failed to evaluate.\n"); goto map_to_bottom; } @@ -170,7 +172,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_OR: { RzILOpPure *px = pure->code == RZ_IL_OP_OR ? pure->op.boolor.x : pure->op.logor.x; RzILOpPure *py = pure->code == RZ_IL_OP_OR ? pure->op.boolor.y : pure->op.logor.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: OR x failed to evaluate.\n"); goto map_to_bottom; } @@ -178,7 +180,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: OR y failed to evaluate.\n"); goto map_to_bottom; } @@ -197,7 +199,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_XOR: { RzILOpPure *px = pure->code == RZ_IL_OP_XOR ? pure->op.boolxor.x : pure->op.logxor.x; RzILOpPure *py = pure->code == RZ_IL_OP_XOR ? pure->op.boolxor.y : pure->op.logxor.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: XOR x failed to evaluate.\n"); goto map_to_bottom; } @@ -205,7 +207,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: XOR y failed to evaluate.\n"); goto map_to_bottom; } @@ -242,7 +244,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( truth_test = rz_bv_msb; break; } - if (!interpreter_prototype_eval_pure(state, bv, out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, bv, out, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: MSB bv failed to evaluate.\n"); goto map_to_bottom; } @@ -255,7 +257,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } case RZ_IL_OP_NEG: { - if (!interpreter_prototype_eval_pure(state, pure->op.neg.bv, out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, pure->op.neg.bv, out, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: NEG bv failed to evaluate.\n"); goto map_to_bottom; } @@ -267,7 +269,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_ADD: { RzILOpPure *px = pure->op.add.x; RzILOpPure *py = pure->op.add.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: ADD x failed to evaluate.\n"); goto map_to_bottom; } @@ -275,7 +277,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: ADD y failed to evaluate.\n"); goto map_to_bottom; } @@ -293,7 +295,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_SUB: { RzILOpPure *px = pure->op.sub.x; RzILOpPure *py = pure->op.sub.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); goto map_to_bottom; } @@ -301,7 +303,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: SUB y failed to evaluate.\n"); goto map_to_bottom; } @@ -321,7 +323,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzILOpPure *px = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.x : pure->op.shiftl.x; RzILOpPure *py = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.y : pure->op.shiftl.y; RzILOpPure *pfill_bit = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.fill_bit : pure->op.shiftl.fill_bit; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) x failed to evaluate.\n"); goto map_to_bottom; } @@ -329,7 +331,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) y failed to evaluate.\n"); goto map_to_bottom; } @@ -338,7 +340,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(fill_bit); - if (!interpreter_prototype_eval_pure(state, pfill_bit, &fill_bit, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, pfill_bit, &fill_bit, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) fill_bit failed to evaluate.\n"); goto map_to_bottom; } @@ -384,7 +386,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: CMP x failed to evaluate.\n"); goto map_to_bottom; } @@ -392,7 +394,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data, io_request, io_result)) { RZ_LOG_ERROR("prototype: CMP y failed to evaluate.\n"); goto map_to_bottom; } From 1db405e7bc9244845d22d77f517c4c68008f409c Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 6 Dec 2025 20:44:11 +0100 Subject: [PATCH 068/334] Implement LOAD/LOADW --- librz/include/rz_inquiry/rz_interpreter.h | 7 ++++++- librz/inquiry/inquiry.c | 15 ++++++++++++++- librz/inquiry/interpreter/interpreter.c | 6 +++++- .../inquiry/interpreter/prototype/eval_pure.c | 19 +++++++++++++++++-- 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 6dc0a736ccb..646d611b893 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -67,6 +67,7 @@ typedef struct { * (conditional) JUMP at the end of each effect. */ ut64 nop_pc_inc; + size_t addr_bits; ///< Number of bits of a memory address. void *ext; ///< Optional state extensions. Managed by individual interpreters. } RzInterpreterAbstrState; @@ -195,7 +196,11 @@ RZ_API void rz_interpreter_il_queue_free(RZ_OWN RZ_NULLABLE RzThreadQueue /**/ *q); RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldQueue *yield_queue); -RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new(RzInterpreterAbstraction kinds, RZ_NULLABLE const RzPVector *reg_names, ut64 nop_pc_increment); +RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( + RzInterpreterAbstraction kinds, + RZ_NULLABLE const RzPVector *reg_names, + ut64 nop_pc_increment, + size_t addr_bits); RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbstrState *state); RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpreterYieldKind kind, diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 71b10cb5ddc..4d6b641c7f1 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -79,6 +79,14 @@ static ut64 get_nop_pc_increment(RzAnalysis *analysis) { return analysis->cur->bits / 8; } +static ut64 get_mem_addr_bits(RzAnalysis *analysis) { + if (analysis->cur->il_config) { + const RzAnalysisILConfig *config = analysis->cur->il_config(analysis); + return config->mem_key_size; + } + return analysis->cur->bits; +} + static RzPVector *get_reg_names(RzAnalysis *analysis) { RzPVector *reg_names = rz_pvector_new(free); if (analysis->cur->il_config) { @@ -227,9 +235,14 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { RzAtomicBool *is_running = rz_atomic_bool_new(true); // Initialize the abstract state with the architecture's registers. + size_t addr_bits = get_mem_addr_bits(core->analysis); RzPVector *reg_names = get_reg_names(core->analysis); ut64 nop_pc_increment = get_nop_pc_increment(core->analysis); - RzInterpreterAbstrState *abstr_state = rz_interpreter_abstr_state_new(RZ_INTERPRETER_ABSTRACTION_CONST, reg_names, nop_pc_increment); + RzInterpreterAbstrState *abstr_state = rz_interpreter_abstr_state_new( + RZ_INTERPRETER_ABSTRACTION_CONST, + reg_names, + nop_pc_increment, + addr_bits); rz_pvector_free(reg_names); // Bundle all the queues into one object to pass it to the thread. diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 5a9dd4a3ac6..752f7810e7e 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -99,7 +99,11 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre * \brief Initializes an abstract state for specified abstract kinds. Optionally with a list of registers. * The register name list should always be given if the architecture has some. */ -RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new(RzInterpreterAbstraction kinds, RZ_NULLABLE const RzPVector *reg_names, ut64 nop_pc_increment) { +RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( + RzInterpreterAbstraction kinds, + RZ_NULLABLE const RzPVector *reg_names, + ut64 nop_pc_increment, + size_t addr_bits) { RzInterpreterAbstrState *state = RZ_NEW0(RzInterpreterAbstrState); if (!state) { return NULL; diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index b44d4b2035d..78e99e16e1a 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -408,6 +408,23 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(y.bv); break; } + case RZ_IL_OP_LOADW: + case RZ_IL_OP_LOAD: { + RzILOpPure *key = pure->code == RZ_IL_OP_LOAD ? pure->op.load.key : pure->op.loadw.key; + if (!interpreter_prototype_eval_pure(state, key, out, yield_queues, plugin_data, io_request, io_result)) { + RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); + goto map_to_bottom; + } + if (!out->is_concrete) { + goto map_to_bottom; + } + ut64 addr = rz_bv_to_ut64(out->bv); + size_t addr_bits = pure->code == RZ_IL_OP_LOAD ? state->addr_bits : pure->op.loadw.n_bits; + if (!load_abstr_data(state, addr, addr_bits, out, io_request, io_result)) { + goto map_to_bottom; + } + break; + } case RZ_IL_OP_MUL: case RZ_IL_OP_DIV: case RZ_IL_OP_SDIV: @@ -447,8 +464,6 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_FPOWN: case RZ_IL_OP_FCOMPOUND: case RZ_IL_OP_FEXCEPT: - case RZ_IL_OP_LOAD: - case RZ_IL_OP_LOADW: // Not implemented. goto map_to_bottom; } From 22635b14dc31fd0a155e206314d153a5e96863d0 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 6 Dec 2025 20:55:27 +0100 Subject: [PATCH 069/334] Fix argument order --- .../interpreter/p/interpreter_prototype.c | 2 +- .../interpreter/prototype/eval_effect.c | 12 ++-- .../inquiry/interpreter/prototype/eval_pure.c | 56 +++++++++---------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index ec6df525b29..8659a1814af 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -14,7 +14,7 @@ static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_request, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_result, void *plugin_data) { - bool result = interpreter_prototype_eval_effect(state, effect, yield_queues, plugin_data); + bool result = interpreter_prototype_eval_effect(state, effect, yield_queues, io_request, io_result, plugin_data); // TODO: Clean up local variables. // Or maybe not? Just costs performance. And the uplifted instructions should // always set it before, otherwise the tests don't pass. diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 09bc16871c0..cc40dd7f271 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -30,7 +30,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, case RZ_IL_OP_SEQ: { const RzILOpEffect *next = effect->op.seq.x; while (next) { - if (!interpreter_prototype_eval_effect(state, next, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_effect(state, next, yield_queues, io_request, io_result, plugin_data)) { goto error; } next = effect->op.seq.y; @@ -39,7 +39,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, } case RZ_IL_OP_SET: { ut64 vhash = effect->op.set.hash; - if (!interpreter_prototype_eval_pure(state, effect->op.set.x, &eval_out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, effect->op.set.x, &eval_out, yield_queues, io_request, io_result, plugin_data)) { goto error; } write_var_to_state(state, @@ -49,14 +49,14 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, break; } case RZ_IL_OP_JMP: { - if (!interpreter_prototype_eval_pure(state, effect->op.jmp.dst, &eval_out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, effect->op.jmp.dst, &eval_out, yield_queues, io_request, io_result, plugin_data)) { goto error; } copy_abstr_data(state->pc->abstr_data, &eval_out); break; } case RZ_IL_OP_BRANCH: { - if (!interpreter_prototype_eval_pure(state, effect->op.branch.condition, &eval_out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, effect->op.branch.condition, &eval_out, yield_queues, io_request, io_result, plugin_data)) { goto error; } if (!eval_out.is_concrete) { @@ -66,11 +66,11 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, } if (abstr_is_true(state, &eval_out)) { - if (!interpreter_prototype_eval_effect(state, effect->op.branch.false_eff, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_effect(state, effect->op.branch.false_eff, yield_queues, io_request, io_result, plugin_data)) { goto error; } } else { - if (!interpreter_prototype_eval_effect(state, effect->op.branch.false_eff, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_effect(state, effect->op.branch.false_eff, yield_queues, io_request, io_result, plugin_data)) { goto error; } } diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 78e99e16e1a..d37504fb405 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -29,13 +29,13 @@ RZ_IPI bool interpreter_prototype_eval_pure( } case RZ_IL_OP_LET: { ut64 vhash = pure->op.let.hash; - if (!interpreter_prototype_eval_pure(state, pure->op.let.exp, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, pure->op.let.exp, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: LET expression failed to evaluate.\n"); goto map_to_bottom; } write_var_to_state(state, RZ_IL_VAR_KIND_LOCAL_PURE, vhash, out); // Evaluate body - if (!interpreter_prototype_eval_pure(state, pure->op.let.body, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, pure->op.let.body, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: LET body failed to evaluate.\n"); goto map_to_bottom; } @@ -44,7 +44,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } case RZ_IL_OP_ITE: { - if (!interpreter_prototype_eval_pure(state, pure->op.ite.condition, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, pure->op.ite.condition, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: ITE condition failed to evaluate.\n"); goto map_to_bottom; } @@ -54,12 +54,12 @@ RZ_IPI bool interpreter_prototype_eval_pure( } if (abstr_is_true(state, out)) { - if (!interpreter_prototype_eval_pure(state, pure->op.ite.x, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, pure->op.ite.x, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: ITE x failed to evaluate.\n"); goto map_to_bottom; } } else { - if (!interpreter_prototype_eval_pure(state, pure->op.ite.y, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, pure->op.ite.y, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: ITE y failed to evaluate.\n"); goto map_to_bottom; } @@ -81,7 +81,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( out->is_concrete = true; break; case RZ_IL_OP_CAST: { - if (!interpreter_prototype_eval_pure(state, pure->op.cast.val, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, pure->op.cast.val, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: CAST val failed to evaluate.\n"); goto map_to_bottom; } @@ -89,7 +89,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } STACK_ABSTR_DATA_OUT(fill_bit); - if (!interpreter_prototype_eval_pure(state, pure->op.cast.fill, &fill_bit, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, pure->op.cast.fill, &fill_bit, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: CAST fill failed to evaluate.\n"); goto map_to_bottom; } @@ -106,7 +106,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; case RZ_IL_OP_APPEND: { STACK_ABSTR_DATA_OUT(high); - if (!interpreter_prototype_eval_pure(state, pure->op.append.high, &high, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, pure->op.append.high, &high, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: APPEND high failed to evaluate.\n"); goto map_to_bottom; } @@ -114,7 +114,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(high.bv); goto map_to_bottom; } - if (!interpreter_prototype_eval_pure(state, pure->op.append.low, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, pure->op.append.low, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: APPEND low failed to evaluate.\n"); rz_bv_fini(high.bv); goto map_to_bottom; @@ -132,7 +132,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_LOGNOT: case RZ_IL_OP_INV: { RzILOpPure *x = pure->code == RZ_IL_OP_INV ? pure->op.boolinv.x : pure->op.lognot.bv; - if (!interpreter_prototype_eval_pure(state, x, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, x, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: INV x failed to evaluate.\n"); goto map_to_bottom; } @@ -145,7 +145,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_AND: { RzILOpPure *px = pure->code == RZ_IL_OP_AND ? pure->op.booland.x : pure->op.logand.x; RzILOpPure *py = pure->code == RZ_IL_OP_AND ? pure->op.booland.y : pure->op.logand.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: AND x failed to evaluate.\n"); goto map_to_bottom; } @@ -153,7 +153,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: AND y failed to evaluate.\n"); goto map_to_bottom; } @@ -172,7 +172,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_OR: { RzILOpPure *px = pure->code == RZ_IL_OP_OR ? pure->op.boolor.x : pure->op.logor.x; RzILOpPure *py = pure->code == RZ_IL_OP_OR ? pure->op.boolor.y : pure->op.logor.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: OR x failed to evaluate.\n"); goto map_to_bottom; } @@ -180,7 +180,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: OR y failed to evaluate.\n"); goto map_to_bottom; } @@ -199,7 +199,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_XOR: { RzILOpPure *px = pure->code == RZ_IL_OP_XOR ? pure->op.boolxor.x : pure->op.logxor.x; RzILOpPure *py = pure->code == RZ_IL_OP_XOR ? pure->op.boolxor.y : pure->op.logxor.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: XOR x failed to evaluate.\n"); goto map_to_bottom; } @@ -207,7 +207,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: XOR y failed to evaluate.\n"); goto map_to_bottom; } @@ -244,7 +244,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( truth_test = rz_bv_msb; break; } - if (!interpreter_prototype_eval_pure(state, bv, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, bv, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: MSB bv failed to evaluate.\n"); goto map_to_bottom; } @@ -257,7 +257,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } case RZ_IL_OP_NEG: { - if (!interpreter_prototype_eval_pure(state, pure->op.neg.bv, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, pure->op.neg.bv, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: NEG bv failed to evaluate.\n"); goto map_to_bottom; } @@ -269,7 +269,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_ADD: { RzILOpPure *px = pure->op.add.x; RzILOpPure *py = pure->op.add.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: ADD x failed to evaluate.\n"); goto map_to_bottom; } @@ -277,7 +277,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: ADD y failed to evaluate.\n"); goto map_to_bottom; } @@ -295,7 +295,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_SUB: { RzILOpPure *px = pure->op.sub.x; RzILOpPure *py = pure->op.sub.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); goto map_to_bottom; } @@ -303,7 +303,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: SUB y failed to evaluate.\n"); goto map_to_bottom; } @@ -323,7 +323,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzILOpPure *px = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.x : pure->op.shiftl.x; RzILOpPure *py = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.y : pure->op.shiftl.y; RzILOpPure *pfill_bit = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.fill_bit : pure->op.shiftl.fill_bit; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) x failed to evaluate.\n"); goto map_to_bottom; } @@ -331,7 +331,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) y failed to evaluate.\n"); goto map_to_bottom; } @@ -340,7 +340,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(fill_bit); - if (!interpreter_prototype_eval_pure(state, pfill_bit, &fill_bit, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, pfill_bit, &fill_bit, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) fill_bit failed to evaluate.\n"); goto map_to_bottom; } @@ -386,7 +386,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: CMP x failed to evaluate.\n"); goto map_to_bottom; } @@ -394,7 +394,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: CMP y failed to evaluate.\n"); goto map_to_bottom; } @@ -411,7 +411,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_LOADW: case RZ_IL_OP_LOAD: { RzILOpPure *key = pure->code == RZ_IL_OP_LOAD ? pure->op.load.key : pure->op.loadw.key; - if (!interpreter_prototype_eval_pure(state, key, out, yield_queues, plugin_data, io_request, io_result)) { + if (!interpreter_prototype_eval_pure(state, key, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); goto map_to_bottom; } From 89e46f5e279abfab173cbe52c7e7308de176d13a Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 6 Dec 2025 20:55:40 +0100 Subject: [PATCH 070/334] Implement STORE/STOREW --- .../interpreter/prototype/eval_effect.c | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index cc40dd7f271..d29a7de1748 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -77,7 +77,36 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, break; } case RZ_IL_OP_STORE: - case RZ_IL_OP_STOREW: + case RZ_IL_OP_STOREW: { + STACK_ABSTR_DATA_OUT(tmp); + RzILOpPure *key = effect->code == RZ_IL_OP_STORE ? effect->op.store.key : effect->op.storew.key; + if (!interpreter_prototype_eval_pure(state, key, &tmp, yield_queues, io_request, io_result, plugin_data)) { + RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); + rz_bv_fini(tmp.bv); + goto error; + } + if (!tmp.is_concrete) { + rz_bv_fini(tmp.bv); + break; + } + ut64 addr = rz_bv_to_ut64(tmp.bv); + RzILOpPure *pval = effect->code == RZ_IL_OP_STORE ? effect->op.store.value : effect->op.storew.value; + if (!interpreter_prototype_eval_pure(state, pval, &tmp, yield_queues, io_request, io_result, plugin_data)) { + RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); + rz_bv_fini(tmp.bv); + goto error; + } + if (!tmp.is_concrete) { + rz_bv_fini(tmp.bv); + break; + } + if (!store_abstr_data(state, addr, &tmp, io_request, io_result)) { + rz_bv_fini(tmp.bv); + goto error; + } + rz_bv_fini(tmp.bv); + break; + } case RZ_IL_OP_GOTO: case RZ_IL_OP_BLK: case RZ_IL_OP_REPEAT: From 40ae29ed03fe260a5cf636022771a20d9fd1d30f Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sun, 7 Dec 2025 19:54:29 +0100 Subject: [PATCH 071/334] Fix a bunch of leaks --- librz/include/rz_inquiry/rz_interpreter.h | 2 +- librz/inquiry/inquiry.c | 136 +++++++++++----------- librz/inquiry/interpreter/interpreter.c | 53 +++++---- 3 files changed, 100 insertions(+), 91 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 646d611b893..c9bbe718fd7 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -216,7 +216,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_request, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag); -RZ_API void rz_interpreter_queue_set_free(RZ_NULLABLE RZ_OWN RzInterpreterSet *qset); +RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset); RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *queue_set); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 4d6b641c7f1..c0488eec440 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -81,8 +81,10 @@ static ut64 get_nop_pc_increment(RzAnalysis *analysis) { static ut64 get_mem_addr_bits(RzAnalysis *analysis) { if (analysis->cur->il_config) { - const RzAnalysisILConfig *config = analysis->cur->il_config(analysis); - return config->mem_key_size; + RzAnalysisILConfig *config = analysis->cur->il_config(analysis); + size_t key_size = config->mem_key_size; + rz_analysis_il_config_free(config); + return key_size; } return analysis->cur->bits; } @@ -117,41 +119,53 @@ static RzPVector *get_reg_names(RzAnalysis *analysis) { * Usually these tasks will be split between different caches and yield consumers. */ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { + // All the things we need + bool return_code = true; + RzThreadQueue *io_request_q = NULL; + RzThreadQueue *io_result_q = NULL; + RzILOpEffect *eff = NULL; + RzThreadQueue *addr_queue = NULL; + RzList *boundaries = NULL; + RzInterpreterYieldQueue *yield_queue = NULL; + HtUP *yield_queues = NULL; + RzAtomicBool *is_running = rz_atomic_bool_new(true); + RzInterpreterAbstrState *abstr_state = NULL; + RzInterpreterSet *iset = NULL; + RzPVector *il_cache = NULL; + RzThreadQueue *il_queue = NULL; + // The pseudo cache of IL effects. // This is only a vector so we can simulate the ownership separation // of the pointers. - RzPVector *il_cache = rz_pvector_new((RzPVectorFree)rz_il_op_effect_free); + il_cache = rz_pvector_new((RzPVectorFree)rz_il_op_effect_free); // The queue to pass the Effects to the interpreter. // This is only one queue for the prototype. // In practice it would be one for each interpreter. - RzThreadQueue *il_queue = rz_th_queue_new(RZ_INTERPRETER_IL_QUEUE_SIZE, NULL); + il_queue = rz_th_queue_new(RZ_INTERPRETER_IL_QUEUE_SIZE, NULL); if (!il_queue) { - return false; + return_code = false; + goto error_free; } // Setup the IO queues. Each interpreter instance needs it's own queue at // for writing IO. Because the writing is done on the IO cache, and each // instance needs its own cache. - RzThreadQueue *io_request_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); - RzThreadQueue *io_result_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); - if (!io_request_q || io_result_q) { - rz_th_queue_free(il_queue); - rz_th_queue_free(io_request_q); - rz_th_queue_free(io_result_q); - return false; + io_request_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); + io_result_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); + if (!io_request_q || !io_result_q) { + return_code = false; + goto error_free; } // Add the Effect for each entry point. - RzILOpEffect *eff = NULL; + eff = NULL; if (argc == 1) { ut64 entry_point = rz_bin_get_first_entrypoint(core->bin->cur->o); eff = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); if (!eff) { - rz_th_queue_free(il_queue); - rz_th_queue_free(io_request_q); - rz_th_queue_free(io_result_q); RZ_LOG_WARN("Could not get entry point IL operation at 0x%" PFMT64x "\n", (ut64)entry_point); - return false; + return_code = false; + goto error_free; } rz_th_queue_push(il_queue, eff, true); rz_pvector_push(il_cache, eff); @@ -161,10 +175,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { ut64 entry_point = rz_num_get(core->num, argv[i]); eff = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); if (!eff) { - rz_th_queue_free(il_queue); - rz_th_queue_free(io_request_q); - rz_th_queue_free(io_result_q); - return false; + return_code = false; + goto error_free; } } rz_th_queue_push(il_queue, eff, true); @@ -174,13 +186,10 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // The address queue. It is the queue the interpreter can request new Effects. // Of course, currently there is only a single one for the prototype. // In practice there would be one for each interpreter instance. - RzThreadQueue *addr_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, (RzListFree)rz_interpreter_addr_queue_free); + addr_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, (RzListFree)rz_interpreter_addr_queue_free); if (!addr_queue) { - rz_th_queue_free(il_queue); - rz_th_queue_free(io_request_q); - rz_th_queue_free(io_result_q); - rz_pvector_free(il_cache); - return false; + return_code = false; + goto error_free; } // Here we build the filter for the yield queue. @@ -188,57 +197,41 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // So the filter checks the generated xrefs, if they are within the IO map // boundaries. RzInterval iv = { .addr = 0, .size = UT64_MAX }; - RzList *boundaries = rz_io_get_boundaries_all_io_maps(core->io, iv); + boundaries = rz_io_get_boundaries_all_io_maps(core->io, iv); if (!boundaries) { - rz_th_queue_free(addr_queue); - rz_th_queue_free(il_queue); - rz_th_queue_free(io_request_q); - rz_th_queue_free(io_result_q); - rz_pvector_free(il_cache); - return false; + return_code = false; + goto error_free; } // Now create a set of yield queue(s). // These yield queues can be shared between different interpreters. // So we have one yield queue for each yield type. RzInterpreterYieldKind yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; - RzInterpreterYieldQueue *yield_queue = rz_interpreter_yield_queue_new( + yield_queue = rz_interpreter_yield_queue_new( yield_kind, (RzInterpreterYieldFilter *)rz_inquiry_xref_interpreter_filter, boundaries); if (!yield_queue) { - rz_th_queue_free(addr_queue); - rz_th_queue_free(il_queue); - rz_th_queue_free(io_request_q); - rz_th_queue_free(io_result_q); - rz_pvector_free(il_cache); - return false; + return_code = false; + goto error_free; } // Multiple yield queues can be used by a single interpreter. // E.g. if the interpreter has a complex abstract memory model // for stack, heap and constant values. // Then it can produce three kind of yields. - HtUP *yield_queues = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_yield_queue_free); + yield_queues = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_yield_queue_free); if (!yield_queue || !yield_queues) { - rz_th_queue_free(addr_queue); - rz_th_queue_free(il_queue); - rz_th_queue_free(io_request_q); - rz_th_queue_free(io_result_q); - rz_pvector_free(il_cache); - rz_interpreter_yield_queue_free(yield_queue); - ht_up_free(yield_queues); - return false; + return_code = false; + goto error_free; } ht_up_insert(yield_queues, yield_kind, yield_queue); - // Create the running flag. - RzAtomicBool *is_running = rz_atomic_bool_new(true); // Initialize the abstract state with the architecture's registers. size_t addr_bits = get_mem_addr_bits(core->analysis); RzPVector *reg_names = get_reg_names(core->analysis); ut64 nop_pc_increment = get_nop_pc_increment(core->analysis); - RzInterpreterAbstrState *abstr_state = rz_interpreter_abstr_state_new( + abstr_state = rz_interpreter_abstr_state_new( RZ_INTERPRETER_ABSTRACTION_CONST, reg_names, nop_pc_increment, @@ -249,7 +242,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // Later we would pass a unique iset to each interpreter with // the required queues only. // But for the prototype we have only one iset with all queues. - RzInterpreterSet *iset = rz_interpreter_set_new( + iset = rz_interpreter_set_new( rz_inquiry_plugin_interpreter_prototype.p_interpreter, abstr_state, addr_queue, @@ -259,22 +252,15 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { io_result_q, is_running); if (!iset) { - rz_th_queue_free(addr_queue); - rz_th_queue_free(il_queue); - rz_th_queue_free(io_request_q); - rz_th_queue_free(io_result_q); - rz_pvector_free(il_cache); - rz_interpreter_yield_queue_free(yield_queue); - ht_up_free(yield_queues); - rz_atomic_bool_free(is_running); - return false; + return_code = false; + goto error_free; } // Dispatch prototype interpreter into a thread. // This part plays the role of the cache now. // Waiting for new Effects to be requested and sending them. RZ_LOG_WARN("INQUIRY: Start main interpretation thread.\n"); - RzThread *iterpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); + RzThread *interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); RZ_LOG_WARN("INQUIRY: Enforce enabling IO cache.\n"); const char *io_cache_opt = rz_config_get(core->config, "io.cache"); rz_config_set(core->config, "io.cache", "true"); @@ -335,10 +321,24 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { rz_config_set(core->config, "io.cache", io_cache_opt); // Wait for thread to finish before cleaning. - rz_th_wait(iterpr_th); - rz_th_free(iterpr_th); - // Empty pseudo-cache. + rz_th_wait(interpr_th); + rz_th_free(interpr_th); + +error_free: + if (!iset) { + rz_th_queue_free(addr_queue); + rz_th_queue_free(il_queue); + rz_th_queue_free(io_request_q); + rz_th_queue_free(io_result_q); + // yield_queues frees each individual queue as well. + ht_up_free(yield_queues); + rz_atomic_bool_free(is_running); + rz_interpreter_abstr_state_free(abstr_state); + } else { + // Ownership was passed to iset + rz_interpreter_set_free(iset); + } rz_pvector_free(il_cache); - return true; + return return_code; } diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 752f7810e7e..c5e3dd61550 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -156,9 +156,40 @@ RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbst if (state->lets) { ht_up_free(state->lets); } + if (state->pc) { + free(state->pc); + } free(state); } +RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { + if (!iset) { + return; + } + if (iset->addr_queue) { + rz_th_queue_free(iset->addr_queue); + } + if (iset->il_queue) { + rz_th_queue_free(iset->il_queue); + } + if (iset->io_request) { + rz_th_queue_free(iset->io_request); + } + if (iset->io_result) { + rz_th_queue_free(iset->io_result); + } + if (iset->is_running_flag) { + rz_atomic_bool_free(iset->is_running_flag); + } + if (iset->state) { + rz_interpreter_abstr_state_free(iset->state); + } + if (iset->yield_queues) { + ht_up_free(iset->yield_queues); + } + free(iset); +} + /** * \brief Initializes a new RzInterpreterSet and returns it. * If it fails, all arguments are freed. @@ -200,28 +231,6 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( return set; } -RZ_API void rz_interpreter_queue_set_free(RZ_NULLABLE RZ_OWN RzInterpreterSet *iset) { - if (!iset) { - return; - } - if (iset->state) { - rz_interpreter_abstr_state_free(iset->state); - } - if (iset->addr_queue) { - rz_th_queue_free(iset->addr_queue); - } - if (iset->il_queue) { - rz_th_queue_free(iset->il_queue); - } - if (iset->yield_queues) { - ht_up_free(iset->yield_queues); - } - if (iset->is_running_flag) { - rz_atomic_bool_free(iset->is_running_flag); - } - free(iset); -} - typedef struct { ut64 addr; ut64 in_state_hash; From 4758aa5485ba3008d861c1e77bdb75c4981eaccb Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sun, 7 Dec 2025 19:55:19 +0100 Subject: [PATCH 072/334] Fail more gracefully inside of the thread. --- librz/inquiry/inquiry.c | 5 +++++ librz/inquiry/interpreter/interpreter.c | 21 +++++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index c0488eec440..619b60ad9f8 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -271,6 +271,11 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { RzInterpreterIOResult *io_res = &_io_res; while (rz_atomic_bool_get(is_running)) { + if (rz_th_terminated(interpr_th)) { + rz_atomic_bool_set(is_running, false); + return_code = rz_th_get_retv(interpr_th); + break; + } ut64 *addr = rz_th_queue_pop(addr_queue, false); if (addr) { // This if() emulates the IL cache. diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index c5e3dd61550..61cfb67d53c 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -240,7 +240,7 @@ typedef struct { * Main interpretation. */ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { - rz_return_val_if_fail(iset && + rz_goto_if_fail(iset && iset->state && iset->addr_queue && iset->il_queue && @@ -250,7 +250,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { iset->plugin->init_state && iset->plugin->eval && iset->plugin->hash_state, - false); + entry_assert_error); bool success = false; RZ_LOG_WARN("INTERPRETER Main: Hello.\n"); @@ -298,7 +298,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { while (true) { // Evaluate the effect on the input state. if (!plugin->eval(in_state, eff, iset->yield_queues, plugin_data, iset->io_request, iset->io_result)) { - goto loop_error; + goto in_loop_error; } // Decrease the reference count to the input state by one. ht_up_delete_rc(state_cache, in_hash); @@ -317,7 +317,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { if (new_state_reached) { // Determine successors and increase the reference counts for the current out state. if (!plugin->successors(out_state, tmp_succ_addr, plugin_data)) { - goto loop_error; + goto in_loop_error; } if (rz_vector_len(tmp_succ_addr) > 0) { // The output state was new and there are successors from it. @@ -375,7 +375,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { eff = rz_th_queue_wait_pop(iset->il_queue, false); } -loop_error: +loop_cleanup: ht_up_free(state_cache); rz_vector_free(tmp_succ_addr); rz_vector_free(succ_states); @@ -386,7 +386,16 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { rz_atomic_bool_set(iset->is_running_flag, false); return success; +in_loop_error: + success = false; + goto loop_cleanup; + pre_loop_error: + success = false; iset->plugin->fini_state(iset->state, plugin_data); - goto loop_error; + goto loop_cleanup; + +entry_assert_error: + rz_atomic_bool_set(iset->is_running_flag, false); + return false; } From af800c4b3e6ecd019bcc39f950c665e2ceb92ad7 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sun, 7 Dec 2025 20:04:08 +0100 Subject: [PATCH 073/334] Prevent more leaks --- librz/inquiry/interpreter/interpreter.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 61cfb67d53c..43f1ac67a11 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -208,18 +208,11 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_request, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag) { - rz_return_val_if_fail(plugin && state && plugin && addr_queue && il_queue && yield_queues && io_request && io_result && is_running_flag, NULL); + rz_return_val_if_fail(plugin && state && addr_queue && il_queue && yield_queues && io_request && io_result && is_running_flag, NULL); RzInterpreterSet *set = RZ_NEW0(RzInterpreterSet); - if (!set || (state->kinds != (plugin->supported_abstractions & state->kinds))) { - if ((state->kinds != (plugin->supported_abstractions & state->kinds))) { - RZ_LOG_ERROR("Abstract state doesn't fit to interpreter.\n"); - } - rz_th_queue_free(addr_queue); - rz_th_queue_free(il_queue); - ht_up_free(yield_queues); - rz_atomic_bool_free(is_running_flag); - return NULL; + if (!set) { + return false; } set->state = state; set->il_queue = il_queue; @@ -228,6 +221,11 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( set->io_request = io_request; set->io_result = io_result; set->is_running_flag = is_running_flag; + if (state->kinds != (plugin->supported_abstractions & state->kinds)) { + RZ_LOG_ERROR("Abstract state doesn't fit to interpreter.\n"); + rz_interpreter_set_free(set); + return NULL; + } return set; } From df79a4092dcd64b0c1a3217f9270dbd56e58eb48 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sun, 7 Dec 2025 20:07:59 +0100 Subject: [PATCH 074/334] Missing plugin assignment to iset and NULL checks --- librz/inquiry/interpreter/interpreter.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 43f1ac67a11..fcae5be12ee 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -214,6 +214,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( if (!set) { return false; } + set->plugin = plugin; set->state = state; set->il_queue = il_queue; set->addr_queue = addr_queue; @@ -245,8 +246,10 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { iset->yield_queues && iset->is_running_flag && iset->plugin && - iset->plugin->init_state && iset->plugin->eval && + iset->plugin->successors && + iset->plugin->init_state && + iset->plugin->fini_state && iset->plugin->hash_state, entry_assert_error); bool success = false; From 41cf371468d2edf28ff89b5f73015436b084b57f Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sun, 7 Dec 2025 20:17:33 +0100 Subject: [PATCH 075/334] Leaks leaks leaks --- librz/inquiry/interpreter/interpreter.c | 2 +- librz/inquiry/interpreter/p/interpreter_prototype.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index fcae5be12ee..c880f015d89 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -381,6 +381,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { rz_vector_free(tmp_succ_addr); rz_vector_free(succ_states); rz_set_u_free(reachable_states); + iset->plugin->fini_state(iset->state, plugin_data); if (iset->plugin->fini) { iset->plugin->fini(plugin_data); } @@ -393,7 +394,6 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { pre_loop_error: success = false; - iset->plugin->fini_state(iset->state, plugin_data); goto loop_cleanup; entry_assert_error: diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 8659a1814af..50fed169789 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -88,6 +88,7 @@ static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da free(ad); } rz_iterator_free(it); + free(state->ext); return true; } From c81de07ec466de72908d2d55cbc9a4ddba82b6b6 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sun, 7 Dec 2025 20:18:02 +0100 Subject: [PATCH 076/334] Error out properly --- .../inquiry/interpreter/prototype/eval_pure.c | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index d37504fb405..1c81aa8ae88 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -23,7 +23,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RZ_LOG_ERROR("prototype: VAR failed to evaluate. The %s '%s' doesn't exist.\n", rz_il_var_kind_name(pure->op.var.kind), pure->op.var.v); - goto map_to_bottom; + return false; } break; } @@ -31,13 +31,13 @@ RZ_IPI bool interpreter_prototype_eval_pure( ut64 vhash = pure->op.let.hash; if (!interpreter_prototype_eval_pure(state, pure->op.let.exp, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: LET expression failed to evaluate.\n"); - goto map_to_bottom; + return false; } write_var_to_state(state, RZ_IL_VAR_KIND_LOCAL_PURE, vhash, out); // Evaluate body if (!interpreter_prototype_eval_pure(state, pure->op.let.body, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: LET body failed to evaluate.\n"); - goto map_to_bottom; + return false; } // No need to free the LET variable. // It is simply overwritten next time. @@ -46,22 +46,22 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_ITE: { if (!interpreter_prototype_eval_pure(state, pure->op.ite.condition, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: ITE condition failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!out->is_concrete) { // Can't decide which pure to evaluate. - goto map_to_bottom; + return false; } if (abstr_is_true(state, out)) { if (!interpreter_prototype_eval_pure(state, pure->op.ite.x, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: ITE x failed to evaluate.\n"); - goto map_to_bottom; + return false; } } else { if (!interpreter_prototype_eval_pure(state, pure->op.ite.y, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: ITE y failed to evaluate.\n"); - goto map_to_bottom; + return false; } } break; @@ -83,7 +83,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_CAST: { if (!interpreter_prototype_eval_pure(state, pure->op.cast.val, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: CAST val failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!out->is_concrete) { break; @@ -91,7 +91,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( STACK_ABSTR_DATA_OUT(fill_bit); if (!interpreter_prototype_eval_pure(state, pure->op.cast.fill, &fill_bit, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: CAST fill failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!fill_bit.is_concrete) { break; @@ -108,7 +108,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( STACK_ABSTR_DATA_OUT(high); if (!interpreter_prototype_eval_pure(state, pure->op.append.high, &high, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: APPEND high failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!high.is_concrete) { rz_bv_fini(high.bv); @@ -117,7 +117,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( if (!interpreter_prototype_eval_pure(state, pure->op.append.low, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: APPEND low failed to evaluate.\n"); rz_bv_fini(high.bv); - goto map_to_bottom; + return false; } if (!out->is_concrete) { rz_bv_fini(high.bv); @@ -134,7 +134,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzILOpPure *x = pure->code == RZ_IL_OP_INV ? pure->op.boolinv.x : pure->op.lognot.bv; if (!interpreter_prototype_eval_pure(state, x, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: INV x failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (out->is_concrete) { rz_bv_not_inplace(out->bv); @@ -147,7 +147,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzILOpPure *py = pure->code == RZ_IL_OP_AND ? pure->op.booland.y : pure->op.logand.y; if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: AND x failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!out->is_concrete) { goto map_to_bottom; @@ -155,7 +155,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( STACK_ABSTR_DATA_OUT(y); if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: AND y failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!y.is_concrete) { rz_bv_fini(y.bv); @@ -174,7 +174,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzILOpPure *py = pure->code == RZ_IL_OP_OR ? pure->op.boolor.y : pure->op.logor.y; if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: OR x failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!out->is_concrete) { goto map_to_bottom; @@ -182,7 +182,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( STACK_ABSTR_DATA_OUT(y); if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: OR y failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!y.is_concrete) { rz_bv_fini(y.bv); @@ -201,7 +201,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzILOpPure *py = pure->code == RZ_IL_OP_XOR ? pure->op.boolxor.y : pure->op.logxor.y; if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: XOR x failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!out->is_concrete) { goto map_to_bottom; @@ -209,7 +209,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( STACK_ABSTR_DATA_OUT(y); if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: XOR y failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!y.is_concrete) { rz_bv_fini(y.bv); @@ -246,7 +246,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( } if (!interpreter_prototype_eval_pure(state, bv, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: MSB bv failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!out->is_concrete) { goto map_to_bottom; @@ -259,7 +259,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_NEG: { if (!interpreter_prototype_eval_pure(state, pure->op.neg.bv, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: NEG bv failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (out->is_concrete) { rz_bv_neg_inplace(out->bv); @@ -271,7 +271,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzILOpPure *py = pure->op.add.y; if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: ADD x failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!out->is_concrete) { goto map_to_bottom; @@ -279,7 +279,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( STACK_ABSTR_DATA_OUT(y); if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: ADD y failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!y.is_concrete) { rz_bv_fini(y.bv); @@ -297,7 +297,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzILOpPure *py = pure->op.sub.y; if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!out->is_concrete) { goto map_to_bottom; @@ -305,7 +305,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( STACK_ABSTR_DATA_OUT(y); if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: SUB y failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!y.is_concrete) { rz_bv_fini(y.bv); @@ -325,7 +325,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzILOpPure *pfill_bit = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.fill_bit : pure->op.shiftl.fill_bit; if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) x failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!out->is_concrete) { goto map_to_bottom; @@ -333,7 +333,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( STACK_ABSTR_DATA_OUT(y); if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) y failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!y.is_concrete) { rz_bv_fini(y.bv); @@ -342,7 +342,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( STACK_ABSTR_DATA_OUT(fill_bit); if (!interpreter_prototype_eval_pure(state, pfill_bit, &fill_bit, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) fill_bit failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!fill_bit.is_concrete) { rz_bv_fini(fill_bit.bv); @@ -388,7 +388,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: CMP x failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!out->is_concrete) { goto map_to_bottom; @@ -396,7 +396,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( STACK_ABSTR_DATA_OUT(y); if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: CMP y failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!y.is_concrete) { rz_bv_fini(y.bv); @@ -413,7 +413,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzILOpPure *key = pure->code == RZ_IL_OP_LOAD ? pure->op.load.key : pure->op.loadw.key; if (!interpreter_prototype_eval_pure(state, key, out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); - goto map_to_bottom; + return false; } if (!out->is_concrete) { goto map_to_bottom; From fe52770407c9ec862bfda70843f1a353d929ea10 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sun, 7 Dec 2025 20:42:18 +0100 Subject: [PATCH 077/334] Fix initialization of local vars --- librz/inquiry/interpreter/prototype/eval.c | 2 +- librz/inquiry/interpreter/prototype/eval.h | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 776052fc3f7..07e29232dd5 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -34,7 +34,7 @@ void write_var_to_state(RzInterpreterAbstrState *state, if (!av) { av = RZ_NEW(RzInterpreterAbstrVal); av->kind = RZ_INTERPRETER_ABSTRACTION_CONST; - av->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); + av->abstr_data = adata_new(); ht_up_insert(ht_vals, var_id, av); } copy_abstr_data(av->abstr_data, data); diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 8a1dd72676f..93b57ef86d1 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -52,6 +52,13 @@ static inline RZ_OWN ProtoIntrprAbstrData *adata_from_bv(const RzBitVector *bv) return ad; } +static inline RZ_OWN ProtoIntrprAbstrData *adata_new() { + ProtoIntrprAbstrData *ad = RZ_NEW(ProtoIntrprAbstrData); + ad->is_concrete = false; + ad->bv = rz_bv_new(BV_STACK_MAX_SIZE); + return ad; +} + void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src); void write_var_to_state(RzInterpreterAbstrState *state, RzILVarKind kind, ut64 var_id, const ProtoIntrprAbstrData *data); bool read_var_from_state(RzInterpreterAbstrState *state, RzILVarKind kind, ut64 var_id, RZ_OUT ProtoIntrprAbstrData *data); From 817dba3ca70555853d9f3a940e30175ef85b78d3 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 8 Dec 2025 14:03:00 +0100 Subject: [PATCH 078/334] Leaks --- librz/inquiry/interpreter/interpreter.c | 4 ++-- librz/inquiry/interpreter/p/interpreter_prototype.c | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index c880f015d89..66c37fef24a 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -138,8 +138,8 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( return NULL; } } - state->locals = ht_up_new(NULL, NULL); - state->lets = ht_up_new(NULL, NULL); + state->locals = ht_up_new(NULL, free); + state->lets = ht_up_new(NULL, free); return state; } diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 50fed169789..756c1ad71e5 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -88,6 +88,17 @@ static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da free(ad); } rz_iterator_free(it); + + it = ht_up_as_iter(state->lets); + rz_iterator_foreach(it, v) { + RzInterpreterAbstrVal *av = *v; + ProtoIntrprAbstrData *ad = av->abstr_data; + if (ad->bv) { + rz_bv_free(ad->bv); + } + free(ad); + } + rz_iterator_free(it); free(state->ext); return true; } From 1b22f0940288c4026f29365b0e616a0c1c490aa0 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 8 Dec 2025 14:06:29 +0100 Subject: [PATCH 079/334] Fix SEQ --- librz/inquiry/interpreter/prototype/eval_effect.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index d29a7de1748..eef9c7f4f06 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -28,12 +28,11 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, break; } case RZ_IL_OP_SEQ: { - const RzILOpEffect *next = effect->op.seq.x; - while (next) { - if (!interpreter_prototype_eval_effect(state, next, yield_queues, io_request, io_result, plugin_data)) { - goto error; - } - next = effect->op.seq.y; + if (!interpreter_prototype_eval_effect(state, effect->op.seq.x, yield_queues, io_request, io_result, plugin_data)) { + goto error; + } + if (!interpreter_prototype_eval_effect(state, effect->op.seq.y, yield_queues, io_request, io_result, plugin_data)) { + goto error; } break; } From fd00e769b133a812a6a6a6cd533009eaa61de464 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 8 Dec 2025 14:10:23 +0100 Subject: [PATCH 080/334] Add warnings --- librz/inquiry/inquiry.c | 1 + librz/inquiry/interpreter/prototype/eval.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 619b60ad9f8..5693d6f86a6 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -282,6 +282,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { RZ_LOG_WARN("INQUIRY: Received IL request: %" PFMT64x ".\n", (*addr)); RzILOpEffect *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); if (!bb) { + RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", *addr); // Stop interpreter. rz_atomic_bool_set(is_running, false); continue; diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 07e29232dd5..5597990d0e6 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -32,6 +32,9 @@ void write_var_to_state(RzInterpreterAbstrState *state, } RzInterpreterAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); if (!av) { + if (kind == RZ_IL_VAR_KIND_GLOBAL) { + RZ_LOG_WARN("New global variable created: 0x%" PFMT64x "\n", var_id) + } av = RZ_NEW(RzInterpreterAbstrVal); av->kind = RZ_INTERPRETER_ABSTRACTION_CONST; av->abstr_data = adata_new(); From f714e335b48df63e8eac32e28f7c9d209f052750 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 8 Dec 2025 14:42:03 +0100 Subject: [PATCH 081/334] Signal interpreter that the lifting failed. --- librz/inquiry/inquiry.c | 3 ++- librz/inquiry/interpreter/interpreter.c | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 5693d6f86a6..79617aaf785 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -283,7 +283,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { RzILOpEffect *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); if (!bb) { RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", *addr); - // Stop interpreter. + // Signal interpreter the lifting failed. + rz_th_cond_signal_all(rz_th_queue_get_cond(iset->il_queue)); rz_atomic_bool_set(is_running, false); continue; } diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 66c37fef24a..e7a88f49e13 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -374,6 +374,10 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { break; } eff = rz_th_queue_wait_pop(iset->il_queue, false); + if (!eff) { + // Some error occurred lifting this basic block. Abort execution. + goto in_loop_error; + } } loop_cleanup: From ee055d0cc0a7c8f3cb4ef2b40f677a2ef33f99c6 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 8 Dec 2025 15:09:13 +0100 Subject: [PATCH 082/334] Set entry point in PC --- librz/include/rz_inquiry/rz_interpreter.h | 10 ++++++++-- librz/inquiry/inquiry.c | 8 +++++++- librz/inquiry/interpreter/interpreter.c | 20 +++++++++++++++++-- .../interpreter/p/interpreter_prototype.c | 3 ++- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index c9bbe718fd7..aaca80398f9 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -120,7 +120,7 @@ typedef struct { /** * \brief Initializes the abstract state. */ - bool (*init_state)(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data); + bool (*init_state)(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_point, void *plugin_data); /** * \brief Closes the abstract state and frees all its abstract data. */ @@ -189,6 +189,11 @@ typedef struct { RzThreadQueue /**/ *io_request; ///< The queue for read/write requests to the IO layer. RzThreadQueue /**/ *io_result; ///< The queue for the read/write requests' answers. RzAtomicBool *is_running_flag; ///< Flag for the interpreter thread to toggle when done. + /** + * \brief The entry points for the interpreters. + * Each address has its lifted IL op in the il_queue at the same index. + */ + RzVector /**/ *entry_points; RzInterpreterPlugin *plugin; } RzInterpreterSet; @@ -215,7 +220,8 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_request, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, - RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag); + RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag, + RZ_NONNULL RZ_OWN RzVector /**/ *entry_points); RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset); RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *queue_set); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 79617aaf785..a379144fe1e 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -133,6 +133,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { RzInterpreterSet *iset = NULL; RzPVector *il_cache = NULL; RzThreadQueue *il_queue = NULL; + RzVector *entry_points = NULL; // The pseudo cache of IL effects. // This is only a vector so we can simulate the ownership separation @@ -158,6 +159,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { } // Add the Effect for each entry point. + entry_points = rz_vector_new(sizeof(ut64), NULL, NULL); eff = NULL; if (argc == 1) { ut64 entry_point = rz_bin_get_first_entrypoint(core->bin->cur->o); @@ -167,6 +169,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { return_code = false; goto error_free; } + rz_vector_push(entry_points, &entry_point); rz_th_queue_push(il_queue, eff, true); rz_pvector_push(il_cache, eff); } else { @@ -178,6 +181,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { return_code = false; goto error_free; } + rz_vector_push(entry_points, &entry_point); } rz_th_queue_push(il_queue, eff, true); rz_pvector_push(il_cache, eff); @@ -250,7 +254,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { yield_queues, io_request_q, io_result_q, - is_running); + is_running, + entry_points); if (!iset) { return_code = false; goto error_free; @@ -341,6 +346,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { ht_up_free(yield_queues); rz_atomic_bool_free(is_running); rz_interpreter_abstr_state_free(abstr_state); + rz_vector_free(entry_points); } else { // Ownership was passed to iset rz_interpreter_set_free(iset); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index e7a88f49e13..1cda3b8a0ac 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -140,6 +140,7 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( } state->locals = ht_up_new(NULL, free); state->lets = ht_up_new(NULL, free); + state->addr_bits = addr_bits; return state; } @@ -187,6 +188,9 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { if (iset->yield_queues) { ht_up_free(iset->yield_queues); } + if (iset->entry_points) { + rz_vector_free(iset->entry_points); + } free(iset); } @@ -207,7 +211,8 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_request, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, - RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag) { + RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag, + RZ_NONNULL RZ_OWN RzVector /**/ *entry_points) { rz_return_val_if_fail(plugin && state && addr_queue && il_queue && yield_queues && io_request && io_result && is_running_flag, NULL); RzInterpreterSet *set = RZ_NEW0(RzInterpreterSet); @@ -222,6 +227,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( set->io_request = io_request; set->io_result = io_result; set->is_running_flag = is_running_flag; + set->entry_points = entry_points; if (state->kinds != (plugin->supported_abstractions & state->kinds)) { RZ_LOG_ERROR("Abstract state doesn't fit to interpreter.\n"); rz_interpreter_set_free(set); @@ -277,7 +283,9 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // This vector must have the same order as the elements pushed into addr_queue. RzVector *succ_states = NULL; - if (!plugin->init_state(iset->state, plugin_data)) { + ut64 entry_point; + rz_vector_pop_front(iset->entry_points, &entry_point); + if (!plugin->init_state(iset->state, entry_point, plugin_data)) { goto pre_loop_error; } RzInterpreterAbstrState *in_state = iset->state; @@ -286,6 +294,14 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { ut64 out_hash = 0; const RzILOpEffect *eff = rz_th_queue_pop(iset->il_queue, false); + // TODO: Add support for multiple entry points by spawning an interpreter for each of them. + // For now let's just drop them. + RzList *additional_entries = rz_th_queue_pop_all(iset->il_queue); + if (rz_list_length(additional_entries) > 0) { + RZ_LOG_WARN("More than one entry point is not yet supported by the prototype.\n"); + } + rz_list_free(additional_entries); + state_cache = ht_up_new_rc(NULL, NULL); tmp_succ_addr = rz_vector_new(sizeof(ut64), NULL, NULL); succ_states = rz_vector_new(sizeof(SuccessorState), NULL, NULL); diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 756c1ad71e5..18e9d7ed709 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -47,8 +47,9 @@ bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, return true; } -static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data) { +static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_point, void *plugin_data) { state->pc->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); + AD(state->pc->abstr_data)->bv = rz_bv_new_from_ut64(state->addr_bits, entry_point); RzIterator *it = ht_up_as_iter(state->globals); RzInterpreterAbstrVal **v; rz_iterator_foreach(it, v) { From 0f8b8aaa57f349ab6552f6cdf00d9f5aa132d329 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 8 Dec 2025 15:58:32 +0100 Subject: [PATCH 083/334] Remove state cleanup for now. Currently one exists anyways --- librz/inquiry/interpreter/interpreter.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 1cda3b8a0ac..e662004eee5 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -353,17 +353,6 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { rz_th_queue_push(iset->addr_queue, addr, true); } } - if (ht_up_get_rc(state_cache, out_hash) == 0) { - // There are no references to the current out_state. - // Free it for resources. - bool found; - RzInterpreterAbstrState *tmp = ht_up_find(state_cache, out_hash, &found); - if (found) { - plugin->fini_state(tmp, plugin_data); - rz_interpreter_abstr_state_free(tmp); - } - ht_up_delete(state_cache, out_hash); - } if (ht_up_size(state_cache) == 0) { // No state in the cache left. From e7186f08d5cc087d06f65ad04085ab669cbb8df2 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 8 Dec 2025 15:59:14 +0100 Subject: [PATCH 084/334] Fix argument order --- librz/inquiry/interpreter/prototype/eval.c | 2 +- librz/inquiry/interpreter/prototype/eval_pure.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 5597990d0e6..986befb9846 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -10,7 +10,7 @@ void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) { rz_bv_cast_inplace(dst->bv, rz_bv_len(src->bv), false); - rz_bv_copy(dst->bv, src->bv); + rz_bv_copy(src->bv, dst->bv); dst->is_concrete = src->is_concrete; } diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 1c81aa8ae88..18bf97d7016 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -101,7 +101,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( } case RZ_IL_OP_BITV: rz_bv_cast_inplace(out->bv, rz_bv_len(pure->op.bitv.value), false); - rz_bv_copy(out->bv, pure->op.bitv.value); + rz_bv_copy(pure->op.bitv.value, out->bv); out->is_concrete = true; break; case RZ_IL_OP_APPEND: { From 9f9f98d4f973298c3d78959527bd8ea4e05ca1bd Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 17:01:33 +0100 Subject: [PATCH 085/334] Init bitvector for globals. --- librz/inquiry/interpreter/p/interpreter_prototype.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 18e9d7ed709..ca2638c45fe 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -55,6 +55,11 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_poin rz_iterator_foreach(it, v) { RzInterpreterAbstrVal *av = *v; av->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); + // Length doesn't matter here. When written the destination is always + // set to the length of the length of the src. + // TODO: Really a good idea to be so liberal? + // Or should the length of the globals be enforced? + AD(av->abstr_data)->bv = rz_bv_new(state->addr_bits); } rz_iterator_free(it); state->ext = RZ_NEW0(RzInterpreterIORequest); From 79bbd79b2ec65e35839688ab999e4241ac11a1a3 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 17:10:10 +0100 Subject: [PATCH 086/334] Add assert --- librz/inquiry/interpreter/prototype/eval.c | 1 + 1 file changed, 1 insertion(+) diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 986befb9846..0e719096f93 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -9,6 +9,7 @@ #include void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) { + rz_return_if_fail(dst->bv && src->bv); rz_bv_cast_inplace(dst->bv, rz_bv_len(src->bv), false); rz_bv_copy(src->bv, dst->bv); dst->is_concrete = src->is_concrete; From 4d34d5d480dea34e3d176de253017adede876202 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 17:39:21 +0100 Subject: [PATCH 087/334] Fix truthy check function --- librz/inquiry/interpreter/prototype/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 0e719096f93..605b9066566 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -81,7 +81,7 @@ bool abstr_is_true(const RzInterpreterAbstrState *state, const ProtoIntrprAbstrD if (!data->is_concrete) { return false; } - return rz_bv_is_zero_vector(data->bv); + return !rz_bv_is_zero_vector(data->bv); } bool store_abstr_data( From 58cd4ae8e1d210b33838a21a115a5fc2dc06a0a7 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 17:39:28 +0100 Subject: [PATCH 088/334] Fix BRANCH --- librz/inquiry/interpreter/prototype/eval_effect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index eef9c7f4f06..b69630c3f7a 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -65,7 +65,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, } if (abstr_is_true(state, &eval_out)) { - if (!interpreter_prototype_eval_effect(state, effect->op.branch.false_eff, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_effect(state, effect->op.branch.true_eff, yield_queues, io_request, io_result, plugin_data)) { goto error; } } else { From 384525c66d55dce6b22c5beb1fe273e5638951aa Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 17:52:21 +0100 Subject: [PATCH 089/334] Read with rz_io_read_at_mapped() because it can read over map boundaries. --- librz/inquiry/inquiry_helpers.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c index ac5a3b2c1a3..2401504c55e 100644 --- a/librz/inquiry/inquiry_helpers.c +++ b/librz/inquiry/inquiry_helpers.c @@ -52,19 +52,17 @@ RZ_API RZ_OWN RzILOpEffect *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis rz_analysis_op_init(&op); // Estimate a reasonable number of bytes to read. int max_read_size = (analysis->cur->bits / 8) * 16; - int actual_size = max_read_size; - ut8 *buf = RZ_NEWS0(ut8, actual_size); - if (!actual_size || !buf) { + ut8 *buf = RZ_NEWS0(ut8, max_read_size); + if (!max_read_size || !buf) { goto fail; } bool changes_cf = true; do { - actual_size = rz_io_nread_at(io, addr, buf, max_read_size); - if (actual_size < 0) { + if (!rz_io_read_at_mapped(io, addr, buf, max_read_size)) { RZ_LOG_WARN("inquiry: Failed to read memory for IL basic block generation.\n"); goto fail; } - if (rz_analysis_op(analysis, &op, addr, buf, actual_size, RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_BASIC) <= 0 || !op.il_op) { + if (rz_analysis_op(analysis, &op, addr, buf, max_read_size, RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_BASIC) <= 0 || !op.il_op) { rz_analysis_op_fini(&op); break; } From ed2f821d82502de6d4560fd2893ff522f1a0f8ef Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 17:52:36 +0100 Subject: [PATCH 090/334] Enhance logs --- librz/inquiry/inquiry.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index a379144fe1e..cd71f809d0b 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -284,7 +284,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { ut64 *addr = rz_th_queue_pop(addr_queue, false); if (addr) { // This if() emulates the IL cache. - RZ_LOG_WARN("INQUIRY: Received IL request: %" PFMT64x ".\n", (*addr)); + RZ_LOG_WARN("INQUIRY: Received IL request: 0x%" PFMT64x "\n", (*addr)); RzILOpEffect *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); if (!bb) { RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", *addr); @@ -309,7 +309,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { continue; } - RZ_LOG_WARN("INQUIRY: Received IO %s request: %" PFMT64x ".\n", + RZ_LOG_WARN("INQUIRY: Received IO %s request: 0x%" PFMT64x "\n", io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", io_req->addr); if (io_req->type == RZ_INTERPRETER_IO_READ) { @@ -329,6 +329,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { io_res->req_ok ? "true" : "false"); rz_th_queue_push(io_result_q, io_res, true); } + RZ_LOG_WARN("INQUIRY: Done\n"); rz_config_set(core->config, "io.cache", io_cache_opt); From 115cf10bb758d1d9aa364c36c8e2aef6e10ae89f Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 18:32:56 +0100 Subject: [PATCH 091/334] Map not lifted instructions to NOP --- librz/inquiry/inquiry_helpers.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c index 2401504c55e..58e653d273e 100644 --- a/librz/inquiry/inquiry_helpers.c +++ b/librz/inquiry/inquiry_helpers.c @@ -62,14 +62,24 @@ RZ_API RZ_OWN RzILOpEffect *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis RZ_LOG_WARN("inquiry: Failed to read memory for IL basic block generation.\n"); goto fail; } - if (rz_analysis_op(analysis, &op, addr, buf, max_read_size, RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_BASIC) <= 0 || !op.il_op) { + if (rz_analysis_op(analysis, &op, addr, buf, max_read_size, RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_BASIC) <= 0) { + RZ_LOG_ERROR("Failed to decode IL op\n"); rz_analysis_op_fini(&op); break; } + bool lifted = true; + if (!op.il_op) { + // Not lifted. Map to NOP + lifted = false; + op.il_op = rz_il_op_new_nop(); + } + bb = bb ? rz_il_op_new_seq(bb, op.il_op) : op.il_op; // Take ownership of IL op pointer. op.il_op = NULL; - changes_cf = rz_analysis_op_changes_control_flow(&op); + if (lifted) { + changes_cf = rz_analysis_op_changes_control_flow(&op); + } rz_analysis_op_fini(&op); addr += op.size; rz_mem_memzero(buf, max_read_size); From c13471b531110644b6b7387f890182a0d709e9a6 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 18:41:02 +0100 Subject: [PATCH 092/334] Spelling --- librz/inquiry/interpreter/p/interpreter_prototype.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index ca2638c45fe..66058f186d2 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -55,8 +55,8 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_poin rz_iterator_foreach(it, v) { RzInterpreterAbstrVal *av = *v; av->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); - // Length doesn't matter here. When written the destination is always - // set to the length of the length of the src. + // Length doesn't matter here. Because the destination is always + // set to the length of the src. // TODO: Really a good idea to be so liberal? // Or should the length of the globals be enforced? AD(av->abstr_data)->bv = rz_bv_new(state->addr_bits); From 2a5cdaf48b40d3f2bb42f9e9dbcedcd3ddcd0fbc Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 18:43:57 +0100 Subject: [PATCH 093/334] Fix NOP --- librz/inquiry/inquiry.c | 6 ------ librz/inquiry/interpreter/prototype/eval_effect.c | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index cd71f809d0b..6d5217339a9 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -70,12 +70,6 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(const RzAnalysisXRef *xref, const } static ut64 get_nop_pc_increment(RzAnalysis *analysis) { - if (RZ_STR_EQ(analysis->cur->arch, "hexagon")) { - // Hexagon has variable instruction lengths. - // It manages the JUMPs to the next packets on its own. - // So it always has a JUMP effect at the end of the effect. - return 0; - } return analysis->cur->bits / 8; } diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index b69630c3f7a..1baa9c50431 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -17,7 +17,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, break; case RZ_IL_OP_NOP: { ProtoIntrprAbstrData *pc = AD(state->pc); - if (pc->is_concrete) { + if (!pc->is_concrete) { // The PC is no longer a concrete value. // This plugin has no addition for it defined. break; From 5d185e5c5b30afcfe17bc5ad4cd7b0989026d61f Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 18:54:57 +0100 Subject: [PATCH 094/334] Fix arguemnt order --- librz/inquiry/interpreter/interpreter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index e662004eee5..4e14865a76d 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -314,7 +314,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { while (true) { // Evaluate the effect on the input state. - if (!plugin->eval(in_state, eff, iset->yield_queues, plugin_data, iset->io_request, iset->io_result)) { + if (!plugin->eval(in_state, eff, iset->yield_queues, iset->io_request, iset->io_result, plugin_data)) { goto in_loop_error; } // Decrease the reference count to the input state by one. From aaf228f60501c66d91dbcedaf407a31582688866 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 19:10:59 +0100 Subject: [PATCH 095/334] Fix termination condition in interpreter --- librz/inquiry/interpreter/interpreter.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 4e14865a76d..8b670c9211b 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -354,11 +354,12 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { } } - if (ht_up_size(state_cache) == 0) { - // No state in the cache left. + if (ht_up_get_rc(state_cache, in_hash) == 0) { + // No reference to the current in_state. // This means we can stop interpreting. // Note, that we can't use the queues as cancel condition because they // are asynchronous and checking them would introduces race conditions. + // TODO: This doesn't work if the interpreter can produce multiple out states. break; } From 6c92f8be8dd6d9ab08815d502680cecfd8c9b3d6 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 19:28:01 +0100 Subject: [PATCH 096/334] Remove state cache for simplicity --- librz/inquiry/interpreter/interpreter.c | 33 +++------------------- librz/inquiry/interpreter/prototype/eval.c | 4 +-- 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 8b670c9211b..5e8a28ca47a 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -273,8 +273,6 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // Start interpretation // - // Cache of the currently used states. - HtUP *state_cache = NULL; // A vector for the plugin to push the determined successors into. RzVector *tmp_succ_addr = NULL; // The set of reachable states. @@ -302,11 +300,10 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { } rz_list_free(additional_entries); - state_cache = ht_up_new_rc(NULL, NULL); tmp_succ_addr = rz_vector_new(sizeof(ut64), NULL, NULL); succ_states = rz_vector_new(sizeof(SuccessorState), NULL, NULL); reachable_states = rz_set_u_new(); - if (!state_cache || !tmp_succ_addr || !succ_states || !eff || !reachable_states) { + if (!tmp_succ_addr || !succ_states || !eff || !reachable_states) { goto pre_loop_error; } ut64 _addr = 0; @@ -317,11 +314,10 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { if (!plugin->eval(in_state, eff, iset->yield_queues, iset->io_request, iset->io_result, plugin_data)) { goto in_loop_error; } - // Decrease the reference count to the input state by one. - ht_up_delete_rc(state_cache, in_hash); // The input state was (almost always) manipulated by eval(). Rename to clarify. out_state = in_state; out_hash = plugin->hash_state(out_state, plugin_data); + printf("in_hash = 0x%llx, out_hash = 0x%llx\n", in_hash, out_hash); // Add out_state hash to the reachable states and // set a flag if it was a new state. @@ -336,13 +332,6 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { if (!plugin->successors(out_state, tmp_succ_addr, plugin_data)) { goto in_loop_error; } - if (rz_vector_len(tmp_succ_addr) > 0) { - // The output state was new and there are successors from it. - // Add it to the cache and increase the reference count of it to the number - // of successors which have it as an input state. - ht_up_insert(state_cache, out_hash, out_state); - ht_up_inc_rc(state_cache, out_hash, rz_vector_len(tmp_succ_addr)); - } // Request the successor effects over the queue. while (!rz_vector_empty(tmp_succ_addr)) { rz_vector_pop_front(tmp_succ_addr, addr); @@ -354,9 +343,8 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { } } - if (ht_up_get_rc(state_cache, in_hash) == 0) { - // No reference to the current in_state. - // This means we can stop interpreting. + if (!new_state_reached) { + // No new state, means we can stop interpreting. // Note, that we can't use the queues as cancel condition because they // are asynchronous and checking them would introduces race conditions. // TODO: This doesn't work if the interpreter can produce multiple out states. @@ -367,18 +355,6 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { SuccessorState next = { 0 }; rz_vector_pop_front(succ_states, &next); in_hash = next.in_state_hash; - in_state = ht_up_find(state_cache, in_hash, NULL); - if (plugin->clone_state && ht_up_get_rc(state_cache, in_hash) > 1) { - // There is more than one effect using this state as input. - // All except one need a clone of it for evaluation because - // they perform different changes on it. - in_state = plugin->clone_state(in_state, plugin_data); - } else if (ht_up_get_rc(state_cache, in_hash) > 1) { - RZ_LOG_ERROR("If the plugin can produce multiple successors for a single state, " - "it must implement the clone_state() callback.\n"); - rz_warn_if_reached(); - break; - } eff = rz_th_queue_wait_pop(iset->il_queue, false); if (!eff) { // Some error occurred lifting this basic block. Abort execution. @@ -387,7 +363,6 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { } loop_cleanup: - ht_up_free(state_cache); rz_vector_free(tmp_succ_addr); rz_vector_free(succ_states); rz_set_u_free(reachable_states); diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 605b9066566..ac13e2d4333 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -107,7 +107,7 @@ bool store_abstr_data( io_req->addr = addr; io_req->data = buf; - RZ_LOG_WARN("Prototype: Send store request"); + RZ_LOG_WARN("Prototype: Send store request\n"); rz_th_queue_push(io_request, io_req, true); // Wait for write being done. RzInterpreterIOResult *io_res = rz_th_queue_wait_pop(io_result, false); @@ -128,7 +128,7 @@ bool load_abstr_data( io_req->type = RZ_INTERPRETER_IO_READ; io_req->addr = addr; io_req->n_bytes = size; - RZ_LOG_WARN("Prototype: Send load request"); + RZ_LOG_WARN("Prototype: Send load request\n"); rz_th_queue_push(io_request, io_req, true); // Wait for load being done. RzInterpreterIOResult *io_res = rz_th_queue_wait_pop(io_result, false); From 66f086f76880b367c4e94e37f779afb463ea4727 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 19:39:08 +0100 Subject: [PATCH 097/334] Fix NOP --- librz/inquiry/interpreter/prototype/eval_effect.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 1baa9c50431..4f9300bd067 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -16,13 +16,13 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, case RZ_IL_OP_EMPTY: break; case RZ_IL_OP_NOP: { - ProtoIntrprAbstrData *pc = AD(state->pc); + ProtoIntrprAbstrData *pc = AD(state->pc->abstr_data); if (!pc->is_concrete) { // The PC is no longer a concrete value. // This plugin has no addition for it defined. break; } - if (!rz_bv_add_inplace(pc->bv, rz_bv_new(state->nop_pc_inc), NULL)) { + if (!rz_bv_add_inplace(pc->bv, rz_bv_new_from_ut64(rz_bv_len(pc->bv), state->nop_pc_inc), NULL)) { goto error; } break; From f5d2ddaea75c07795c1c823d27668a5765c3b284 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 19:47:31 +0100 Subject: [PATCH 098/334] Fix leaks in NOP --- librz/inquiry/interpreter/prototype/eval_effect.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 4f9300bd067..047aa6298a9 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -22,7 +22,10 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, // This plugin has no addition for it defined. break; } - if (!rz_bv_add_inplace(pc->bv, rz_bv_new_from_ut64(rz_bv_len(pc->bv), state->nop_pc_inc), NULL)) { + STACK_ABSTR_DATA_OUT(inc); + rz_bv_set_from_ut64(inc.bv, state->nop_pc_inc); + rz_bv_cast_inplace(inc.bv, rz_bv_len(pc->bv), false); + if (!rz_bv_add_inplace(pc->bv, inc.bv, NULL)) { goto error; } break; From 212fab4071442d618939e8dc54ad0fb66cc849da Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 20:00:36 +0100 Subject: [PATCH 099/334] Read IO loads into stack buffer not heap ones. --- librz/inquiry/inquiry.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 6d5217339a9..f242ac7fa17 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -19,6 +19,8 @@ RZ_LIB_VERSION(rz_inquiry); +#define MAX_IO_DATA_READ 0x1000 + static RzInquiryPlugin *inquiry_static_plugins[] = { RZ_INQUIRY_STATIC_PLUGINS }; RZ_API const size_t rz_inquiry_get_n_plugins() { @@ -268,6 +270,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // Poor man's shared memory. RzInterpreterIOResult _io_res = { 0 }; RzInterpreterIOResult *io_res = &_io_res; + ut8 io_res_buf[MAX_IO_DATA_READ] = { 0 }; + io_res->read.data = io_res_buf; while (rz_atomic_bool_get(is_running)) { if (rz_th_terminated(interpr_th)) { @@ -307,14 +311,14 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", io_req->addr); if (io_req->type == RZ_INTERPRETER_IO_READ) { - // TODO: Don't allocate here if not necessary. - ut8 *buf = RZ_NEWS0(ut8, io_req->n_bytes); - int n_read = rz_io_nread_at(core->io, io_req->addr, buf, io_req->n_bytes); - io_res->req_ok = n_read >= 0; - if (io_res->req_ok) { - io_res->read.data = buf; - io_res->read.n_bytes = n_read; + if (io_req->n_bytes > MAX_IO_DATA_READ) { + RZ_LOG_ERROR("Plugin tried to read more than 0x%" PFMT32x " bytes.\n" + "This is more than configured. It will only read MAX_IO_DATA_READ bytes.\nPlease set MAX_IO_DATA_READ to a larger value and rebuild Rizin.\n", MAX_IO_DATA_READ); } + // Cast to ut8* here. The constant is only there so interpreter plugins don't free it by accident. + int n_read = rz_io_nread_at(core->io, io_req->addr, (ut8 *) io_res->read.data, io_req->n_bytes > MAX_IO_DATA_READ ? MAX_IO_DATA_READ : io_req->n_bytes); + io_res->req_ok = n_read >= 0; + io_res->read.n_bytes = n_read; } else { io_res->req_ok = rz_io_write_at(core->io, io_req->addr, io_req->data, io_req->n_bytes); } From ddefea5b84f664a0853dd70d70bf6dd8323054fa Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 20:08:31 +0100 Subject: [PATCH 100/334] Fix return values and show more errors. --- librz/inquiry/interpreter/prototype/eval.c | 3 ++- librz/inquiry/interpreter/prototype/eval_pure.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index ac13e2d4333..2f4384ca090 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -133,6 +133,7 @@ bool load_abstr_data( // Wait for load being done. RzInterpreterIOResult *io_res = rz_th_queue_wait_pop(io_result, false); if (!io_res->req_ok) { + RZ_LOG_WARN("Prototype: IO read failed."); return false; } if (io_res->read.n_bytes != size) { @@ -142,5 +143,5 @@ bool load_abstr_data( out->is_concrete = true; rz_bv_cast_inplace(out->bv, size, 0); rz_bv_set_from_bytes_be(out->bv, io_res->read.data, 0, io_res->read.n_bytes); - return false; + return true; } diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 18bf97d7016..76669c41793 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -50,7 +50,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( } if (!out->is_concrete) { // Can't decide which pure to evaluate. - return false; + goto map_to_bottom; } if (abstr_is_true(state, out)) { From 28cdf69bfd590d6f5408189f28a788ba32518384 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 20:14:24 +0100 Subject: [PATCH 101/334] Make all globals concrete by default to reach more states. --- librz/inquiry/interpreter/p/interpreter_prototype.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 66058f186d2..c50e667433b 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -60,6 +60,9 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_poin // TODO: Really a good idea to be so liberal? // Or should the length of the globals be enforced? AD(av->abstr_data)->bv = rz_bv_new(state->addr_bits); + // TODO: This is debatable. It depends on the ABI what the default values are. + // Some values must be concrete, otherwise the interpretation of the prototype end too early. + AD(av->abstr_data)->is_concrete = true; } rz_iterator_free(it); state->ext = RZ_NEW0(RzInterpreterIORequest); From 91f705c5fbd900a3161fbbf982adec8c46126f9e Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 9 Dec 2025 20:14:35 +0100 Subject: [PATCH 102/334] Improve hash function a little. --- librz/inquiry/interpreter/p/interpreter_prototype.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index c50e667433b..98b3568d407 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -118,10 +118,10 @@ static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da * It is also slow. */ static ut64 hash_state(RZ_NONNULL const RzInterpreterAbstrState *state, void *plugin_data) { - ut64 hash = 0; + ut64 h = 5381; ProtoIntrprAbstrData *ad = state->pc->abstr_data; if (ad->bv) { - hash ^= rz_bv_to_ut64(ad->bv); + h = (h ^ (h << 5)) ^ rz_bv_to_ut64(ad->bv); } RzIterator *it = ht_up_as_iter(state->globals); RzInterpreterAbstrVal **v; @@ -129,11 +129,11 @@ static ut64 hash_state(RZ_NONNULL const RzInterpreterAbstrState *state, void *pl RzInterpreterAbstrVal *av = *v; ProtoIntrprAbstrData *ad = av->abstr_data; if (ad->bv) { - hash ^= rz_bv_to_ut64(ad->bv); + h = (h ^ (h << 5)) ^ rz_bv_to_ut64(ad->bv); } } rz_iterator_free(it); - return hash; + return h; } static RzInterpreterPlugin rz_interpreter_plugin_prototype = { From aeed7640adebb2fdba4c1700f88946b4f9db7d91 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 10 Dec 2025 16:11:23 +0100 Subject: [PATCH 103/334] Report xrefs of LOAD/STORE/JMP. --- librz/include/rz_inquiry.h | 2 +- librz/include/rz_inquiry/rz_interpreter.h | 10 +++--- librz/inquiry/inquiry.c | 7 ++-- librz/inquiry/interpreter/interpreter.c | 23 ++++-------- librz/inquiry/interpreter/prototype/eval.c | 22 ++++++++++++ librz/inquiry/interpreter/prototype/eval.h | 2 ++ .../interpreter/prototype/eval_effect.c | 36 ++++++++++++------- .../inquiry/interpreter/prototype/eval_pure.c | 4 +-- 8 files changed, 66 insertions(+), 40 deletions(-) diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 885aaf91909..c5ab65b1d64 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -36,7 +36,7 @@ RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *a); RZ_API bool rz_inquiry_op_type_is_eob(_RzAnalysisOpType type); RZ_API RZ_OWN RzILOpEffect *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr); -RZ_API bool rz_inquiry_xref_interpreter_filter(const RzAnalysisXRef *xref, const RzList /**/ *allowed_io_maps); +RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzList /**/ *allowed_io_maps); RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv); diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index aaca80398f9..f05b524ec7c 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -89,15 +89,17 @@ typedef union { */ typedef bool (*RzInterpreterYieldFilter)(const void *element, const void *filter_data); +typedef struct { + RzList /**/ *io_boundaries; +} RzInterpreterYieldFilterData; + /** * \brief A queue to push interpretation yields into. */ typedef struct { RzInterpreterYieldKind kind; - const RzInterpreterYieldFilter *filter; - union { - RzList /**/ *io_boundaries; - } filter_data; + const RzInterpreterYieldFilter filter; + RzInterpreterYieldFilterData *filter_data; RzThreadQueue /**/ *yield_queue; } RzInterpreterYieldQueue; diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index f242ac7fa17..5476e738894 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -12,6 +12,7 @@ #include "rz_io.h" #include "rz_reg.h" #include "rz_th.h" +#include "rz_util/rz_bitvector.h" #include "rz_vector.h" #include #include @@ -57,14 +58,14 @@ RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OW return false; } -RZ_API bool rz_inquiry_xref_interpreter_filter(const RzAnalysisXRef *xref, const RzList /**/ *allowed_io_maps) { - rz_return_val_if_fail(xref && allowed_io_maps, false); +RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzList /**/ *allowed_io_maps) { + rz_return_val_if_fail(xref_to_addr && allowed_io_maps, false); const RzIOMap *map; RzListIter *it; rz_list_foreach (allowed_io_maps, it, map) { ut64 start = map->itv.addr; ut64 end = map->itv.addr + map->itv.size; - if (RZ_BETWEEN(start, xref->to, end)) { + if (RZ_BETWEEN(start, *xref_to_addr, end)) { return true; } } diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 5e8a28ca47a..facede7d5ec 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -52,20 +52,16 @@ RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYiel if (yield_queue->yield_queue) { rz_th_queue_free(yield_queue->yield_queue); } - switch (yield_queue->kind) { - default: - break; - case RZ_INTERPRETER_YIELD_KIND_XREF: - // Free the RzIOMap list. - rz_list_free(yield_queue->filter_data.io_boundaries); - break; + if (yield_queue->filter_data) { + rz_list_free(yield_queue->filter_data->io_boundaries); } + free(yield_queue->filter_data); free(yield_queue); } RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpreterYieldKind kind, const RzInterpreterYieldFilter *filter, - RZ_OWN RZ_NULLABLE void *filter_data) { + RZ_OWN RZ_NULLABLE void *filter_data_io_boundaries) { RzInterpreterYieldQueue *yield_queue = RZ_NEW0(RzInterpreterYieldQueue); if (!yield_queue) { return NULL; @@ -83,14 +79,9 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre yield_queue->kind = kind; yield_queue->yield_queue = queue; yield_queue->filter = filter; - // TODO Doesn't look good. Void pointers are not nice. - switch (kind) { - default: - rz_return_val_if_fail(!filter_data, NULL); - break; - case RZ_INTERPRETER_YIELD_KIND_XREF: - yield_queue->filter_data.io_boundaries = filter_data; - break; + yield_queue->filter_data = RZ_NEW0(RzInterpreterYieldFilterData); + if (filter_data_io_boundaries) { + yield_queue->filter_data->io_boundaries = filter_data_io_boundaries; } return yield_queue; } diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 2f4384ca090..b27e4d7cd5a 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -2,12 +2,34 @@ // SPDX-License-Identifier: LGPL-3.0-only #include "eval.h" +#include "rz_analysis.h" #include "rz_inquiry/rz_interpreter.h" #include "rz_th.h" #include "rz_types.h" #include "rz_util/rz_log.h" #include +bool report_xref_yield(HtUP /**/ *yield_queues, ut64 from, const ProtoIntrprAbstrData *to, RzAnalysisXRefType type) { + RzInterpreterYieldQueue *queue = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); + if (!queue) { + rz_warn_if_reached(); + return false; + } + if (!to->is_concrete || rz_bv_len(to->bv) > 64) { + // Isn't reported + return true; + } + ut64 to_addr = rz_bv_to_ut64(to->bv); + if (queue->filter(&to_addr, queue->filter_data)) { + RzAnalysisXRef *xref = RZ_NEW0(RzAnalysisXRef); + xref->from = from; + xref->to = to_addr; + xref->type = type; + rz_th_queue_push(queue->yield_queue, xref, true); + } + return true; +} + void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) { rz_return_if_fail(dst->bv && src->bv); rz_bv_cast_inplace(dst->bv, rz_bv_len(src->bv), false); diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 93b57ef86d1..5e963dc238c 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -92,4 +92,6 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzThreadQueue /**/ *io_result, void *plugin_data); +bool report_xref_yield(HtUP /**/ *yield_queues, ut64 from, const ProtoIntrprAbstrData *to, RzAnalysisXRefType type); + #endif // PROTOYPE_EVAL_H diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 047aa6298a9..b66100313af 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -2,6 +2,8 @@ // SPDX-License-Identifier: LGPL-3.0-only #include "eval.h" +#include "rz_analysis.h" +#include "rz_util/rz_bitvector.h" RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, @@ -54,7 +56,14 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, if (!interpreter_prototype_eval_pure(state, effect->op.jmp.dst, &eval_out, yield_queues, io_request, io_result, plugin_data)) { goto error; } + // Setting the PC to a bottom value is allowed here! + // The successor function will handle this case. copy_abstr_data(state->pc->abstr_data, &eval_out); + if (eval_out.is_concrete) { + // NOTE: This prototype can't classify into call or jump. + // Everything is just a jump for it at this point. + report_xref_yield(yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &eval_out, RZ_ANALYSIS_XREF_TYPE_CODE); + } break; } case RZ_IL_OP_BRANCH: { @@ -80,33 +89,34 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, } case RZ_IL_OP_STORE: case RZ_IL_OP_STOREW: { - STACK_ABSTR_DATA_OUT(tmp); + STACK_ABSTR_DATA_OUT(st_addr); RzILOpPure *key = effect->code == RZ_IL_OP_STORE ? effect->op.store.key : effect->op.storew.key; - if (!interpreter_prototype_eval_pure(state, key, &tmp, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, key, &st_addr, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); - rz_bv_fini(tmp.bv); + rz_bv_fini(st_addr.bv); goto error; } - if (!tmp.is_concrete) { - rz_bv_fini(tmp.bv); + if (!st_addr.is_concrete) { + rz_bv_fini(st_addr.bv); break; } - ut64 addr = rz_bv_to_ut64(tmp.bv); + ut64 addr = rz_bv_to_ut64(st_addr.bv); RzILOpPure *pval = effect->code == RZ_IL_OP_STORE ? effect->op.store.value : effect->op.storew.value; - if (!interpreter_prototype_eval_pure(state, pval, &tmp, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, pval, &st_addr, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); - rz_bv_fini(tmp.bv); + rz_bv_fini(st_addr.bv); goto error; } - if (!tmp.is_concrete) { - rz_bv_fini(tmp.bv); + if (!st_addr.is_concrete) { + rz_bv_fini(st_addr.bv); break; } - if (!store_abstr_data(state, addr, &tmp, io_request, io_result)) { - rz_bv_fini(tmp.bv); + report_xref_yield(yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &st_addr, RZ_ANALYSIS_XREF_TYPE_DATA); + if (!store_abstr_data(state, addr, &st_addr, io_request, io_result)) { + rz_bv_fini(st_addr.bv); goto error; } - rz_bv_fini(tmp.bv); + rz_bv_fini(st_addr.bv); break; } case RZ_IL_OP_GOTO: diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 76669c41793..efa3ff77ffb 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -418,6 +418,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( if (!out->is_concrete) { goto map_to_bottom; } + report_xref_yield(yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), out, RZ_ANALYSIS_XREF_TYPE_DATA); ut64 addr = rz_bv_to_ut64(out->bv); size_t addr_bits = pure->code == RZ_IL_OP_LOAD ? state->addr_bits : pure->op.loadw.n_bits; if (!load_abstr_data(state, addr, addr_bits, out, io_request, io_result)) { @@ -467,9 +468,6 @@ RZ_IPI bool interpreter_prototype_eval_pure( // Not implemented. goto map_to_bottom; } - - // TODO: Check filter if the values should be reported/pushed into the yield queue. - return true; map_to_bottom: From 2a2413d9467f3090b7a06e6748945e07c793e872 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 10 Dec 2025 18:52:00 +0100 Subject: [PATCH 104/334] Move different roles the inquiry function plays into threads. --- librz/inquiry/inquiry.c | 106 +++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 44 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 5476e738894..800fd941cc9 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -151,8 +151,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { io_request_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); io_result_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); if (!io_request_q || !io_result_q) { - return_code = false; - goto error_free; + return_code = false; + goto error_free; } // Add the Effect for each entry point. @@ -259,10 +259,16 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { } // Dispatch prototype interpreter into a thread. - // This part plays the role of the cache now. - // Waiting for new Effects to be requested and sending them. RZ_LOG_WARN("INQUIRY: Start main interpretation thread.\n"); RzThread *interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); + + // From here on, the code plays the role of the cache, IO handler, + // and yield consumer. + // - Waiting for new Effects to be requested and sending them. + // - Handling IO requests. + // - Receiving and adding the found xrefs to RzAnalysis. + // In the final implementation each of those roles would be split into + // two or more separated modules running in parallel. RZ_LOG_WARN("INQUIRY: Enforce enabling IO cache.\n"); const char *io_cache_opt = rz_config_get(core->config, "io.cache"); rz_config_set(core->config, "io.cache", "true"); @@ -280,53 +286,65 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { return_code = rz_th_get_retv(interpr_th); break; } - ut64 *addr = rz_th_queue_pop(addr_queue, false); - if (addr) { - // This if() emulates the IL cache. - RZ_LOG_WARN("INQUIRY: Received IL request: 0x%" PFMT64x "\n", (*addr)); - RzILOpEffect *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); - if (!bb) { - RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", *addr); - // Signal interpreter the lifting failed. - rz_th_cond_signal_all(rz_th_queue_get_cond(iset->il_queue)); - rz_atomic_bool_set(is_running, false); - continue; + + // This block mimics the IL cache. + { + ut64 *addr = rz_th_queue_pop(addr_queue, false); + if (addr) { + RZ_LOG_WARN("INQUIRY: Received IL request: 0x%" PFMT64x "\n", (*addr)); + RzILOpEffect *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); + if (!bb) { + RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", *addr); + // Signal interpreter the lifting failed. + rz_th_cond_signal_all(rz_th_queue_get_cond(iset->il_queue)); + rz_atomic_bool_set(is_running, false); + continue; + } + RZ_LOG_WARN("INQUIRY: Send IL result: %p.\n", bb); + rz_pvector_push(il_cache, bb); + // TODO: Free unused if too big. + rz_th_queue_push(il_queue, bb, true); } - RZ_LOG_WARN("INQUIRY: Send IL result: %p.\n", bb); - rz_pvector_push(il_cache, bb); - // TODO: Free unused if too big. - rz_th_queue_push(il_queue, bb, true); } - // This emulates the IO handler for a single(!) interpreter instances. - // In the future we should have only one IO handler - // but this requires that it can handle multiple IO write caches + // This plays the IO handler for a single(!) interpreter instances. + // In the future we should have only one IO handler for multiple interpreters. + // But this requires multiple IO write caches // (one for each interpreter instance). - // Because this is not implemented there is only one interpreter thread for now. - RzInterpreterIORequest *io_req = rz_th_queue_pop(io_request_q, false); - if (!io_req) { - continue; - } + // Because this is not yet implemented, there is only one interpreter thread for now. + { + RzInterpreterIORequest *io_req = rz_th_queue_pop(io_request_q, false); + if (!io_req) { + continue; + } - RZ_LOG_WARN("INQUIRY: Received IO %s request: 0x%" PFMT64x "\n", - io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", - io_req->addr); - if (io_req->type == RZ_INTERPRETER_IO_READ) { - if (io_req->n_bytes > MAX_IO_DATA_READ) { - RZ_LOG_ERROR("Plugin tried to read more than 0x%" PFMT32x " bytes.\n" - "This is more than configured. It will only read MAX_IO_DATA_READ bytes.\nPlease set MAX_IO_DATA_READ to a larger value and rebuild Rizin.\n", MAX_IO_DATA_READ); + RZ_LOG_WARN("INQUIRY: Received IO %s request: 0x%" PFMT64x "\n", + io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", + io_req->addr); + if (io_req->type == RZ_INTERPRETER_IO_READ) { + if (io_req->n_bytes > MAX_IO_DATA_READ) { + RZ_LOG_ERROR("Plugin tried to read more than 0x%" PFMT32x " bytes.\n" + "This is more than configured. It will only read MAX_IO_DATA_READ bytes.\nPlease set MAX_IO_DATA_READ to a larger value and rebuild Rizin.\n", + MAX_IO_DATA_READ); + } + // Cast to ut8* here. The constant is only there so interpreter plugins don't free it by accident. + int n_read = rz_io_nread_at(core->io, io_req->addr, (ut8 *)io_res->read.data, io_req->n_bytes > MAX_IO_DATA_READ ? MAX_IO_DATA_READ : io_req->n_bytes); + io_res->req_ok = n_read >= 0; + io_res->read.n_bytes = n_read; + } else { + io_res->req_ok = rz_io_write_at(core->io, io_req->addr, io_req->data, io_req->n_bytes); } - // Cast to ut8* here. The constant is only there so interpreter plugins don't free it by accident. - int n_read = rz_io_nread_at(core->io, io_req->addr, (ut8 *) io_res->read.data, io_req->n_bytes > MAX_IO_DATA_READ ? MAX_IO_DATA_READ : io_req->n_bytes); - io_res->req_ok = n_read >= 0; - io_res->read.n_bytes = n_read; - } else { - io_res->req_ok = rz_io_write_at(core->io, io_req->addr, io_req->data, io_req->n_bytes); + RZ_LOG_WARN("INQUIRY: Sent IO %s result. Success = %s.\n", + io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", + io_res->req_ok ? "true" : "false"); + rz_th_queue_push(io_result_q, io_res, true); + } + + // This part plays the role of a yield consumer. + // In our prototype it inly receives xrefs and stores them in RzAnalysis. + { + } - RZ_LOG_WARN("INQUIRY: Sent IO %s result. Success = %s.\n", - io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", - io_res->req_ok ? "true" : "false"); - rz_th_queue_push(io_result_q, io_res, true); } RZ_LOG_WARN("INQUIRY: Done\n"); From 6449cbb42c5d6525bb8aca6f75e63f6e7d860bb5 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 11 Dec 2025 16:13:23 +0100 Subject: [PATCH 105/334] Add xrefs to RzAnalysis --- librz/include/rz_inquiry/rz_interpreter.h | 4 ++-- librz/inquiry/inquiry.c | 11 ++++++++--- librz/inquiry/interpreter/interpreter.c | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index f05b524ec7c..2cef226d2e9 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -98,7 +98,7 @@ typedef struct { */ typedef struct { RzInterpreterYieldKind kind; - const RzInterpreterYieldFilter filter; + RzInterpreterYieldFilter filter; RzInterpreterYieldFilterData *filter_data; RzThreadQueue /**/ *yield_queue; } RzInterpreterYieldQueue; @@ -211,7 +211,7 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbstrState *state); RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpreterYieldKind kind, - const RzInterpreterYieldFilter *filter, + RzInterpreterYieldFilter filter, RZ_OWN RZ_NULLABLE void *filter_data); RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 800fd941cc9..cc91c879d28 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -12,7 +12,6 @@ #include "rz_io.h" #include "rz_reg.h" #include "rz_th.h" -#include "rz_util/rz_bitvector.h" #include "rz_vector.h" #include #include @@ -210,7 +209,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { RzInterpreterYieldKind yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; yield_queue = rz_interpreter_yield_queue_new( yield_kind, - (RzInterpreterYieldFilter *)rz_inquiry_xref_interpreter_filter, + (RzInterpreterYieldFilter)rz_inquiry_xref_interpreter_filter, boundaries); if (!yield_queue) { return_code = false; @@ -343,7 +342,13 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // This part plays the role of a yield consumer. // In our prototype it inly receives xrefs and stores them in RzAnalysis. { - + RzThreadQueue *q = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, false); + RzAnalysisXRef *xref = rz_th_queue_pop(q, false); + if (!xref) { + continue; + } + // TODO: Currently we can't classify calls as such. + rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, xref->type); } } RZ_LOG_WARN("INQUIRY: Done\n"); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index facede7d5ec..1fb9d4b09d6 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -60,7 +60,7 @@ RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYiel } RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpreterYieldKind kind, - const RzInterpreterYieldFilter *filter, + RzInterpreterYieldFilter filter, RZ_OWN RZ_NULLABLE void *filter_data_io_boundaries) { RzInterpreterYieldQueue *yield_queue = RZ_NEW0(RzInterpreterYieldQueue); if (!yield_queue) { From 50548ab94dd6912d2a7b35e334f7cba2be716073 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 13 Dec 2025 12:19:47 +0100 Subject: [PATCH 106/334] Make analysis abortable with ctrl+c and don't allocate xrefs shared over queue --- librz/include/rz_inquiry/rz_interpreter.h | 4 +- librz/inquiry/inquiry.c | 29 +++++++++----- librz/inquiry/interpreter/interpreter.c | 39 ++----------------- .../interpreter/p/interpreter_prototype.c | 2 +- librz/inquiry/interpreter/prototype/eval.c | 22 ++++++++--- librz/inquiry/interpreter/prototype/eval.h | 13 ++++++- .../interpreter/prototype/eval_effect.c | 4 +- .../inquiry/interpreter/prototype/eval_pure.c | 2 +- 8 files changed, 57 insertions(+), 58 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 2cef226d2e9..1182816b61f 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -100,7 +100,7 @@ typedef struct { RzInterpreterYieldKind kind; RzInterpreterYieldFilter filter; RzInterpreterYieldFilterData *filter_data; - RzThreadQueue /**/ *yield_queue; + RzThreadQueue /**/ *yield_queue; } RzInterpreterYieldQueue; typedef struct { @@ -199,8 +199,6 @@ typedef struct { RzInterpreterPlugin *plugin; } RzInterpreterSet; -RZ_API void rz_interpreter_il_queue_free(RZ_OWN RZ_NULLABLE RzThreadQueue /**/ *q); -RZ_API void rz_interpreter_addr_queue_free(RZ_OWN RZ_NULLABLE RzThreadQueue /**/ *q); RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldQueue *yield_queue); RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index cc91c879d28..3b497d3656c 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -7,6 +7,7 @@ #include "rz_analysis.h" #include "rz_config.h" +#include "rz_cons.h" #include "rz_inquiry/rz_interpreter.h" #include "rz_inquiry_plugins.h" #include "rz_io.h" @@ -130,6 +131,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { RzPVector *il_cache = NULL; RzThreadQueue *il_queue = NULL; RzVector *entry_points = NULL; + RzThread *interpr_th = NULL; + + rz_cons_push(); // The pseudo cache of IL effects. // This is only a vector so we can simulate the ownership separation @@ -186,7 +190,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // The address queue. It is the queue the interpreter can request new Effects. // Of course, currently there is only a single one for the prototype. // In practice there would be one for each interpreter instance. - addr_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, (RzListFree)rz_interpreter_addr_queue_free); + addr_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, NULL); if (!addr_queue) { return_code = false; goto error_free; @@ -259,7 +263,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // Dispatch prototype interpreter into a thread. RZ_LOG_WARN("INQUIRY: Start main interpretation thread.\n"); - RzThread *interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); + interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); // From here on, the code plays the role of the cache, IO handler, // and yield consumer. @@ -280,9 +284,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { io_res->read.data = io_res_buf; while (rz_atomic_bool_get(is_running)) { - if (rz_th_terminated(interpr_th)) { + if (rz_th_terminated(interpr_th) || rz_cons_is_breaked()) { rz_atomic_bool_set(is_running, false); - return_code = rz_th_get_retv(interpr_th); break; } @@ -342,8 +345,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // This part plays the role of a yield consumer. // In our prototype it inly receives xrefs and stores them in RzAnalysis. { - RzThreadQueue *q = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, false); - RzAnalysisXRef *xref = rz_th_queue_pop(q, false); + RzInterpreterYieldQueue *q = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); + RzAnalysisXRef *xref = rz_th_queue_pop(q->yield_queue, false); if (!xref) { continue; } @@ -356,10 +359,17 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { rz_config_set(core->config, "io.cache", io_cache_opt); // Wait for thread to finish before cleaning. - rz_th_wait(interpr_th); - rz_th_free(interpr_th); - error_free: + RZ_LOG_WARN("INQUIRY: Close queues\n"); + rz_th_cond_signal_all(rz_th_queue_get_cond(iset->il_queue)); + rz_th_cond_signal_all(rz_th_queue_get_cond(iset->io_result)); + if (interpr_th){ + RZ_LOG_WARN("INQUIRY: Wait for join\n"); + rz_th_wait(interpr_th); + return_code = rz_th_get_retv(interpr_th); + rz_th_free(interpr_th); + } + if (!iset) { rz_th_queue_free(addr_queue); rz_th_queue_free(il_queue); @@ -376,5 +386,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { } rz_pvector_free(il_cache); + rz_cons_pop(); return return_code; } diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 1fb9d4b09d6..0df5a4c8c28 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -12,39 +12,6 @@ #include #include -RZ_API void rz_interpreter_il_queue_free(RZ_OWN RZ_NULLABLE RzThreadQueue /**/ *q) { - if (!q) { - return; - } - RzILOpEffect *eff = rz_th_queue_pop(q, false); - while (eff) { - rz_il_op_effect_free(eff); - eff = rz_th_queue_pop(q, false); - } -} - -RZ_API void rz_interpreter_addr_queue_free(RZ_OWN RZ_NULLABLE RzThreadQueue /**/ *q) { - if (!q) { - return; - } - ut64 *addr = rz_th_queue_pop(q, false); - while (addr) { - free(addr); - addr = rz_th_queue_pop(q, false); - } -} - -static void const_yield_queue_free(RZ_OWN RZ_NULLABLE RzThreadQueue /**/ *q) { - if (!q) { - return; - } - RzInterpreterYield *yield = rz_th_queue_pop(q, false); - while (yield) { - free(yield->abstr_const); - yield = rz_th_queue_pop(q, false); - } -} - RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldQueue *yield_queue) { if (!yield_queue) { return; @@ -52,7 +19,7 @@ RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYiel if (yield_queue->yield_queue) { rz_th_queue_free(yield_queue->yield_queue); } - if (yield_queue->filter_data) { + if (yield_queue->filter_data && yield_queue->filter_data->io_boundaries) { rz_list_free(yield_queue->filter_data->io_boundaries); } free(yield_queue->filter_data); @@ -69,7 +36,7 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre RzThreadQueue *queue = NULL; switch (kind) { case RZ_INTERPRETER_YIELD_KIND_XREF: - queue = rz_th_queue_new(RZ_INTERPRETER_YIELD_QUEUE_SIZE, (RzListFree)const_yield_queue_free); + queue = rz_th_queue_new(RZ_INTERPRETER_YIELD_QUEUE_SIZE, NULL); break; } if (!queue) { @@ -300,7 +267,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { ut64 _addr = 0; ut64 *addr = &_addr; - while (true) { + while (rz_atomic_bool_get(iset->is_running_flag)) { // Evaluate the effect on the input state. if (!plugin->eval(in_state, eff, iset->yield_queues, iset->io_request, iset->io_result, plugin_data)) { goto in_loop_error; diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 98b3568d407..c28d65746b5 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -65,7 +65,7 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_poin AD(av->abstr_data)->is_concrete = true; } rz_iterator_free(it); - state->ext = RZ_NEW0(RzInterpreterIORequest); + state->ext = RZ_NEW0(ProtoInterprSharedObjects); return true; } diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index b27e4d7cd5a..d8169f255bd 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -9,7 +9,7 @@ #include "rz_util/rz_log.h" #include -bool report_xref_yield(HtUP /**/ *yield_queues, ut64 from, const ProtoIntrprAbstrData *to, RzAnalysisXRefType type) { +bool report_xref_yield(RzInterpreterAbstrState *state, HtUP /**/ *yield_queues, ut64 from, const ProtoIntrprAbstrData *to, RzAnalysisXRefType type) { RzInterpreterYieldQueue *queue = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); if (!queue) { rz_warn_if_reached(); @@ -19,9 +19,11 @@ bool report_xref_yield(HtUP /**/ *yield_queues, ut64 // Isn't reported return true; } + ProtoInterprSharedObjects *sobj = state->ext; + ut64 to_addr = rz_bv_to_ut64(to->bv); - if (queue->filter(&to_addr, queue->filter_data)) { - RzAnalysisXRef *xref = RZ_NEW0(RzAnalysisXRef); + if (queue->filter(&to_addr, queue->filter_data->io_boundaries)) { + RzAnalysisXRef *xref = &sobj->xref; xref->from = from; xref->to = to_addr; xref->type = type; @@ -124,7 +126,8 @@ bool store_abstr_data( buf = buf_stack; } rz_bv_set_to_bytes_be(src->bv, buf); - RzInterpreterIORequest *io_req = state->ext; + ProtoInterprSharedObjects *sobj = state->ext; + RzInterpreterIORequest *io_req = &sobj->io_req; io_req->type = RZ_INTERPRETER_IO_WRITE; io_req->addr = addr; io_req->data = buf; @@ -133,6 +136,10 @@ bool store_abstr_data( rz_th_queue_push(io_request, io_req, true); // Wait for write being done. RzInterpreterIOResult *io_res = rz_th_queue_wait_pop(io_result, false); + if (!io_res) { + // Abort of interpretation. + return false; + } if (rz_bv_len(src->bv) > BV_STACK_MAX_SIZE * 8) { free(buf); } @@ -146,7 +153,8 @@ bool load_abstr_data( RZ_OUT ProtoIntrprAbstrData *out, RzThreadQueue /**/ *io_request, RzThreadQueue /**/ *io_result) { - RzInterpreterIORequest *io_req = state->ext; + ProtoInterprSharedObjects *sobj = state->ext; + RzInterpreterIORequest *io_req = &sobj->io_req; io_req->type = RZ_INTERPRETER_IO_READ; io_req->addr = addr; io_req->n_bytes = size; @@ -154,6 +162,10 @@ bool load_abstr_data( rz_th_queue_push(io_request, io_req, true); // Wait for load being done. RzInterpreterIOResult *io_res = rz_th_queue_wait_pop(io_result, false); + if (!io_res) { + // Abort of interpretation. + return false; + } if (!io_res->req_ok) { RZ_LOG_WARN("Prototype: IO read failed."); return false; diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 5e963dc238c..5fe5a3c059c 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -4,6 +4,7 @@ #ifndef PROTOYPE_EVAL_H #define PROTOYPE_EVAL_H +#include "rz_analysis.h" #include #include #include @@ -26,6 +27,11 @@ typedef struct { RzBitVector *bv; } ProtoIntrprAbstrData; +typedef struct { + RzInterpreterIORequest io_req; + RzAnalysisXRef xref; +} ProtoInterprSharedObjects; + /** * \brief In bytes */ @@ -92,6 +98,11 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzThreadQueue /**/ *io_result, void *plugin_data); -bool report_xref_yield(HtUP /**/ *yield_queues, ut64 from, const ProtoIntrprAbstrData *to, RzAnalysisXRefType type); +bool report_xref_yield( + RzInterpreterAbstrState *state, + HtUP /**/ *yield_queues, + ut64 from, + const ProtoIntrprAbstrData *to, + RzAnalysisXRefType type); #endif // PROTOYPE_EVAL_H diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index b66100313af..c3ba0b82ab1 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -62,7 +62,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, if (eval_out.is_concrete) { // NOTE: This prototype can't classify into call or jump. // Everything is just a jump for it at this point. - report_xref_yield(yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &eval_out, RZ_ANALYSIS_XREF_TYPE_CODE); + report_xref_yield(state, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &eval_out, RZ_ANALYSIS_XREF_TYPE_CODE); } break; } @@ -111,7 +111,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, rz_bv_fini(st_addr.bv); break; } - report_xref_yield(yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &st_addr, RZ_ANALYSIS_XREF_TYPE_DATA); + report_xref_yield(state, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &st_addr, RZ_ANALYSIS_XREF_TYPE_DATA); if (!store_abstr_data(state, addr, &st_addr, io_request, io_result)) { rz_bv_fini(st_addr.bv); goto error; diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index efa3ff77ffb..85d4aca9075 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -418,7 +418,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( if (!out->is_concrete) { goto map_to_bottom; } - report_xref_yield(yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), out, RZ_ANALYSIS_XREF_TYPE_DATA); + report_xref_yield(state, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), out, RZ_ANALYSIS_XREF_TYPE_DATA); ut64 addr = rz_bv_to_ut64(out->bv); size_t addr_bits = pure->code == RZ_IL_OP_LOAD ? state->addr_bits : pure->op.loadw.n_bits; if (!load_abstr_data(state, addr, addr_bits, out, io_request, io_result)) { From aab627041e1433e32bdab1178cbec91b817b3c3d Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 13 Dec 2025 12:40:32 +0100 Subject: [PATCH 107/334] Logs and docs --- librz/inquiry/inquiry.c | 1 + librz/inquiry/interpreter/prototype/eval.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 3b497d3656c..67656958692 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -352,6 +352,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { } // TODO: Currently we can't classify calls as such. rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, xref->type); + RZ_LOG_WARN("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)) } } RZ_LOG_WARN("INQUIRY: Done\n"); diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index d8169f255bd..0d0645f1406 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -27,6 +27,10 @@ bool report_xref_yield(RzInterpreterAbstrState *state, HtUP /*from = from; xref->to = to_addr; xref->type = type; + // TODO: Possible race condition here, if the interpreter pushes a new xref + // before the previous one was handled. + // But this is fine for the prototype. Real implementation needs some kind + // of shared memory anyways. rz_th_queue_push(queue->yield_queue, xref, true); } return true; @@ -171,7 +175,7 @@ bool load_abstr_data( return false; } if (io_res->read.n_bytes != size) { - RZ_LOG_WARN("Prototype: Failed to read correct number of bytes."); + RZ_LOG_WARN("Prototype: Failed to read correct number of bytes.\n"); return false; } out->is_concrete = true; From 16a0dcf7a60f67cb2b4012b9d3efcf0097225f31 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 13 Dec 2025 15:45:52 +0100 Subject: [PATCH 108/334] Set PC on new effect selection --- librz/include/rz_inquiry/rz_interpreter.h | 6 ++++++ librz/inquiry/interpreter/interpreter.c | 5 +++-- librz/inquiry/interpreter/p/interpreter_prototype.c | 9 +++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 1182816b61f..5aadda44661 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -154,6 +154,12 @@ typedef struct { bool (*successors)(RZ_NONNULL const RzInterpreterAbstrState *state, RZ_NONNULL RZ_OUT RzVector /**/ *successors, void *plugin_data); + /** + * \brief Set the abstract PC to the given address. + */ + bool (*set_pc)(RZ_NONNULL RzInterpreterAbstrState *state, + ut64 pc, + void *plugin_data); } RzInterpreterPlugin; typedef enum { diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 0df5a4c8c28..741f6caf85b 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -314,8 +314,9 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { rz_vector_pop_front(succ_states, &next); in_hash = next.in_state_hash; eff = rz_th_queue_wait_pop(iset->il_queue, false); - if (!eff) { - // Some error occurred lifting this basic block. Abort execution. + if (!eff || !plugin->set_pc(in_state, next.addr, plugin_data)) { + // Some error occurred lifting this basic block. Or updating the PC. + // Abort execution. goto in_loop_error; } } diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index c28d65746b5..4b65c1154e5 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -14,6 +14,7 @@ static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_request, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_result, void *plugin_data) { + RZ_LOG_WARN("Eval PC = 0x%" PFMT64x "\n", rz_bv_to_ut64(AD(state->pc->abstr_data)->bv)); bool result = interpreter_prototype_eval_effect(state, effect, yield_queues, io_request, io_result, plugin_data); // TODO: Clean up local variables. // Or maybe not? Just costs performance. And the uplifted instructions should @@ -136,6 +137,13 @@ static ut64 hash_state(RZ_NONNULL const RzInterpreterAbstrState *state, void *pl return h; } +static bool set_pc(RzInterpreterAbstrState *state, ut64 pc, + void *plugin_data) { + rz_return_val_if_fail(state, false); + AD(state->pc->abstr_data)->is_concrete = true; + return rz_bv_set_from_ut64(AD(state->pc->abstr_data)->bv, pc); +} + static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .name = "abstr_int_prototype", .author = "Rot127", @@ -151,6 +159,7 @@ static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .init_state = init_state, .fini_state = fini_state, .hash_state = hash_state, + .set_pc = set_pc, .clone_state = NULL, }; From a5c22bb07bc0757c42803ecbed5fbe05ca790419 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 13 Dec 2025 15:46:03 +0100 Subject: [PATCH 109/334] Fix storing of memory. --- librz/inquiry/interpreter/prototype/eval.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 0d0645f1406..97bd5772fbd 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -122,16 +122,18 @@ bool store_abstr_data( // Really don't write? return true; } + ProtoInterprSharedObjects *sobj = state->ext; + RzInterpreterIORequest *io_req = &sobj->io_req; + io_req->n_bytes = rz_bv_len_bytes(src->bv); + ut8 *buf; ut8 buf_stack[BV_STACK_MAX_SIZE] = { 0 }; - if (rz_bv_len(src->bv) > BV_STACK_MAX_SIZE * 8) { - buf = RZ_NEWS(ut8, rz_bv_len_bytes(src->bv)); + if (io_req->n_bytes > BV_STACK_MAX_SIZE) { + buf = RZ_NEWS(ut8, io_req->n_bytes); } else { buf = buf_stack; } rz_bv_set_to_bytes_be(src->bv, buf); - ProtoInterprSharedObjects *sobj = state->ext; - RzInterpreterIORequest *io_req = &sobj->io_req; io_req->type = RZ_INTERPRETER_IO_WRITE; io_req->addr = addr; io_req->data = buf; @@ -140,13 +142,14 @@ bool store_abstr_data( rz_th_queue_push(io_request, io_req, true); // Wait for write being done. RzInterpreterIOResult *io_res = rz_th_queue_wait_pop(io_result, false); + if (io_req->n_bytes > BV_STACK_MAX_SIZE) { + free(buf); + } + if (!io_res) { // Abort of interpretation. return false; } - if (rz_bv_len(src->bv) > BV_STACK_MAX_SIZE * 8) { - free(buf); - } return io_res->req_ok; } From 70332bd3640ea31d57b3b86ca979bf59714952db Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 13 Dec 2025 15:50:43 +0100 Subject: [PATCH 110/334] Abort on failed memory read. --- librz/inquiry/interpreter/prototype/eval_pure.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 85d4aca9075..31f21bf0941 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -422,7 +422,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( ut64 addr = rz_bv_to_ut64(out->bv); size_t addr_bits = pure->code == RZ_IL_OP_LOAD ? state->addr_bits : pure->op.loadw.n_bits; if (!load_abstr_data(state, addr, addr_bits, out, io_request, io_result)) { - goto map_to_bottom; + return false; } break; } From 0b09efacde8d1a8b180045a93fc83fcdf8b116f2 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 13 Dec 2025 16:24:34 +0100 Subject: [PATCH 111/334] Log failed reads in more detail. --- librz/inquiry/interpreter/prototype/eval.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 97bd5772fbd..178a30e1f4c 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -178,7 +178,8 @@ bool load_abstr_data( return false; } if (io_res->read.n_bytes != size) { - RZ_LOG_WARN("Prototype: Failed to read correct number of bytes.\n"); + RZ_LOG_WARN("Prototype: Failed to read correct number of bytes. Requested: 0x%" PFMTSZx + " Received: 0x%" PFMT64x "\n", size, io_res->read.n_bytes); return false; } out->is_concrete = true; From 490412de77a3d7f87e86d1e488a4ae643dd58cf8 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 13 Dec 2025 17:10:37 +0100 Subject: [PATCH 112/334] Fix incorrect handling of PC increment of effects without jumps. --- librz/include/rz_inquiry.h | 2 +- librz/include/rz_inquiry/rz_interpreter.h | 20 ++++++------- librz/inquiry/inquiry.c | 30 ++++++++----------- librz/inquiry/inquiry_helpers.c | 16 +++++++--- librz/inquiry/interpreter/interpreter.c | 22 +++++++++----- .../interpreter/p/interpreter_prototype.c | 4 +-- librz/inquiry/interpreter/prototype/eval.h | 1 + .../interpreter/prototype/eval_effect.c | 11 +++---- .../inquiry/interpreter/prototype/eval_pure.c | 2 +- 9 files changed, 59 insertions(+), 49 deletions(-) diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index c5ab65b1d64..073c34c4834 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -34,7 +34,7 @@ RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void); RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *a); RZ_API bool rz_inquiry_op_type_is_eob(_RzAnalysisOpType type); -RZ_API RZ_OWN RzILOpEffect *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr); +RZ_API RZ_OWN RzInterpreterILOp *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr); RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzList /**/ *allowed_io_maps); diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 5aadda44661..548e4a20ceb 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -59,14 +59,6 @@ typedef struct { HtUP /**/ *locals; ///< Local variables. Indexed by DJB2 hash of the local name. HtUP /**/ *lets; ///< Let variables. Indexed by DJB2 hash of the let name. RzInterpreterAbstrVal *pc; ///< In our RzIL implementation the PC is not part of the register file. - /** - * \brief The number by which the PC is incremented for a NOP instruction. - * Usually this is simply the instruction width. But it can be 0 - * if the architecture doesn't have a fixed increment (e.g. VLIW processors). - * If 0 the RzArch plugin is expected to always return an effect with a - * (conditional) JUMP at the end of each effect. - */ - ut64 nop_pc_inc; size_t addr_bits; ///< Number of bits of a memory address. void *ext; ///< Optional state extensions. Managed by individual interpreters. } RzInterpreterAbstrState; @@ -103,6 +95,11 @@ typedef struct { RzThreadQueue /**/ *yield_queue; } RzInterpreterYieldQueue; +typedef struct { + RzILOpEffect *effect; ///< The effect to evaluate. + size_t asm_op_size; ///< The size of the instruction packet. Used to increment the PC if no JMP occurred. +} RzInterpreterILOp; + typedef struct { const char *name; const char *author; @@ -140,7 +137,7 @@ typedef struct { * \brief Evaluates an effect with the mutable state. */ bool (*eval)(RZ_NONNULL RzInterpreterAbstrState *state, - RZ_NONNULL const RzILOpEffect *effect, + RZ_NONNULL const RzInterpreterILOp *il_op, RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_request, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_result, @@ -191,7 +188,7 @@ typedef struct { typedef struct { RzInterpreterAbstrState *state; ///< The abstract state of the interpreter. RzThreadQueue /**/ *addr_queue; ///< The queue to send requests to the cache what address to get the next IL op from. - RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. + RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. // TODO: We need to decide how to distribute the yield. HtUP /**/ *yield_queues; ///< The queues to push the yield of interpretation into. RzThreadQueue /**/ *io_request; ///< The queue for read/write requests to the IO layer. @@ -205,12 +202,13 @@ typedef struct { RzInterpreterPlugin *plugin; } RzInterpreterSet; +RZ_API void rz_interpreter_il_op_free(RZ_NULLABLE RZ_OWN RzInterpreterILOp *il_op); + RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldQueue *yield_queue); RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( RzInterpreterAbstraction kinds, RZ_NULLABLE const RzPVector *reg_names, - ut64 nop_pc_increment, size_t addr_bits); RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbstrState *state); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 67656958692..8c68fb9097a 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -72,10 +72,6 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL co return false; } -static ut64 get_nop_pc_increment(RzAnalysis *analysis) { - return analysis->cur->bits / 8; -} - static ut64 get_mem_addr_bits(RzAnalysis *analysis) { if (analysis->cur->il_config) { RzAnalysisILConfig *config = analysis->cur->il_config(analysis); @@ -120,7 +116,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { bool return_code = true; RzThreadQueue *io_request_q = NULL; RzThreadQueue *io_result_q = NULL; - RzILOpEffect *eff = NULL; + RzInterpreterILOp *il_op = NULL; RzThreadQueue *addr_queue = NULL; RzList *boundaries = NULL; RzInterpreterYieldQueue *yield_queue = NULL; @@ -138,7 +134,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // The pseudo cache of IL effects. // This is only a vector so we can simulate the ownership separation // of the pointers. - il_cache = rz_pvector_new((RzPVectorFree)rz_il_op_effect_free); + il_cache = rz_pvector_new((RzPVectorFree)rz_interpreter_il_op_free); // The queue to pass the Effects to the interpreter. // This is only one queue for the prototype. // In practice it would be one for each interpreter. @@ -160,31 +156,31 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // Add the Effect for each entry point. entry_points = rz_vector_new(sizeof(ut64), NULL, NULL); - eff = NULL; + il_op = NULL; if (argc == 1) { ut64 entry_point = rz_bin_get_first_entrypoint(core->bin->cur->o); - eff = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); - if (!eff) { + il_op = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); + if (!il_op) { RZ_LOG_WARN("Could not get entry point IL operation at 0x%" PFMT64x "\n", (ut64)entry_point); return_code = false; goto error_free; } rz_vector_push(entry_points, &entry_point); - rz_th_queue_push(il_queue, eff, true); - rz_pvector_push(il_cache, eff); + rz_th_queue_push(il_queue, il_op, true); + rz_pvector_push(il_cache, il_op); } else { // Add all entry points given as arguments. for (size_t i = 1; i < argc; i++) { ut64 entry_point = rz_num_get(core->num, argv[i]); - eff = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); - if (!eff) { + il_op = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); + if (!il_op) { return_code = false; goto error_free; } rz_vector_push(entry_points, &entry_point); } - rz_th_queue_push(il_queue, eff, true); - rz_pvector_push(il_cache, eff); + rz_th_queue_push(il_queue, il_op, true); + rz_pvector_push(il_cache, il_op); } // The address queue. It is the queue the interpreter can request new Effects. @@ -234,11 +230,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // Initialize the abstract state with the architecture's registers. size_t addr_bits = get_mem_addr_bits(core->analysis); RzPVector *reg_names = get_reg_names(core->analysis); - ut64 nop_pc_increment = get_nop_pc_increment(core->analysis); abstr_state = rz_interpreter_abstr_state_new( RZ_INTERPRETER_ABSTRACTION_CONST, reg_names, - nop_pc_increment, addr_bits); rz_pvector_free(reg_names); @@ -294,7 +288,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { ut64 *addr = rz_th_queue_pop(addr_queue, false); if (addr) { RZ_LOG_WARN("INQUIRY: Received IL request: 0x%" PFMT64x "\n", (*addr)); - RzILOpEffect *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); + RzInterpreterILOp *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); if (!bb) { RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", *addr); // Signal interpreter the lifting failed. diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c index 58e653d273e..c3eefd4a4b9 100644 --- a/librz/inquiry/inquiry_helpers.c +++ b/librz/inquiry/inquiry_helpers.c @@ -5,6 +5,7 @@ * \file Helper functions for the new analysis. Some of them not yet temporarily. */ +#include #include #include #include @@ -45,8 +46,9 @@ RZ_API bool rz_inquiry_op_type_is_eob(_RzAnalysisOpType type) { } } -RZ_API RZ_OWN RzILOpEffect *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr) { +RZ_API RZ_OWN RzInterpreterILOp *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr) { rz_return_val_if_fail(analysis && analysis->cur && io, NULL); + size_t size_bb = 0; RzILOpEffect *bb = NULL; RzAnalysisOp op = { 0 }; rz_analysis_op_init(&op); @@ -64,8 +66,7 @@ RZ_API RZ_OWN RzILOpEffect *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis } if (rz_analysis_op(analysis, &op, addr, buf, max_read_size, RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_BASIC) <= 0) { RZ_LOG_ERROR("Failed to decode IL op\n"); - rz_analysis_op_fini(&op); - break; + goto fail; } bool lifted = true; if (!op.il_op) { @@ -75,6 +76,7 @@ RZ_API RZ_OWN RzILOpEffect *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis } bb = bb ? rz_il_op_new_seq(bb, op.il_op) : op.il_op; + size_bb += op.size; // Take ownership of IL op pointer. op.il_op = NULL; if (lifted) { @@ -86,7 +88,13 @@ RZ_API RZ_OWN RzILOpEffect *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis } while (!changes_cf); free(buf); - return bb; + RzInterpreterILOp *il_op = RZ_NEW(RzInterpreterILOp); + if (!il_op) { + goto fail; + } + il_op->asm_op_size = size_bb; + il_op->effect = bb; + return il_op; fail: free(buf); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 741f6caf85b..f92ab8e5cd1 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -12,6 +12,16 @@ #include #include +RZ_API void rz_interpreter_il_op_free(RZ_NULLABLE RZ_OWN RzInterpreterILOp *il_op) { + if (!il_op) { + return; + } + if (il_op->effect) { + rz_il_op_effect_free(il_op->effect); + } + free(il_op); +} + RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldQueue *yield_queue) { if (!yield_queue) { return; @@ -60,13 +70,11 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( RzInterpreterAbstraction kinds, RZ_NULLABLE const RzPVector *reg_names, - ut64 nop_pc_increment, size_t addr_bits) { RzInterpreterAbstrState *state = RZ_NEW0(RzInterpreterAbstrState); if (!state) { return NULL; } - state->nop_pc_inc = nop_pc_increment; state->kinds = kinds; if (!reg_names) { return state; @@ -249,7 +257,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { RzInterpreterAbstrState *out_state = NULL; ut64 out_hash = 0; - const RzILOpEffect *eff = rz_th_queue_pop(iset->il_queue, false); + const RzInterpreterILOp *il_op = rz_th_queue_pop(iset->il_queue, false); // TODO: Add support for multiple entry points by spawning an interpreter for each of them. // For now let's just drop them. RzList *additional_entries = rz_th_queue_pop_all(iset->il_queue); @@ -261,7 +269,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { tmp_succ_addr = rz_vector_new(sizeof(ut64), NULL, NULL); succ_states = rz_vector_new(sizeof(SuccessorState), NULL, NULL); reachable_states = rz_set_u_new(); - if (!tmp_succ_addr || !succ_states || !eff || !reachable_states) { + if (!tmp_succ_addr || !succ_states || !il_op || !reachable_states) { goto pre_loop_error; } ut64 _addr = 0; @@ -269,7 +277,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { while (rz_atomic_bool_get(iset->is_running_flag)) { // Evaluate the effect on the input state. - if (!plugin->eval(in_state, eff, iset->yield_queues, iset->io_request, iset->io_result, plugin_data)) { + if (!plugin->eval(in_state, il_op, iset->yield_queues, iset->io_request, iset->io_result, plugin_data)) { goto in_loop_error; } // The input state was (almost always) manipulated by eval(). Rename to clarify. @@ -313,8 +321,8 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { SuccessorState next = { 0 }; rz_vector_pop_front(succ_states, &next); in_hash = next.in_state_hash; - eff = rz_th_queue_wait_pop(iset->il_queue, false); - if (!eff || !plugin->set_pc(in_state, next.addr, plugin_data)) { + il_op = rz_th_queue_wait_pop(iset->il_queue, false); + if (!il_op || !plugin->set_pc(in_state, next.addr, plugin_data)) { // Some error occurred lifting this basic block. Or updating the PC. // Abort execution. goto in_loop_error; diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 4b65c1154e5..6be8366c930 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -9,13 +9,13 @@ #include "../prototype/eval.h" static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, - RZ_NONNULL const RzILOpEffect *effect, + RZ_NONNULL const RzInterpreterILOp *il_op, RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_request, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_result, void *plugin_data) { RZ_LOG_WARN("Eval PC = 0x%" PFMT64x "\n", rz_bv_to_ut64(AD(state->pc->abstr_data)->bv)); - bool result = interpreter_prototype_eval_effect(state, effect, yield_queues, io_request, io_result, plugin_data); + bool result = interpreter_prototype_eval_effect(state, il_op->effect, il_op->asm_op_size, yield_queues, io_request, io_result, plugin_data); // TODO: Clean up local variables. // Or maybe not? Just costs performance. And the uplifted instructions should // always set it before, otherwise the tests don't pass. diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 5fe5a3c059c..fcfe2201863 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -85,6 +85,7 @@ bool load_abstr_data( RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, + size_t nop_pc_inc, HtUP /**/ *yield_queues, RzThreadQueue /**/ *io_request, RzThreadQueue /**/ *io_result, diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index c3ba0b82ab1..69c11baabe5 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -7,6 +7,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, + size_t nop_pc_inc, HtUP /**/ *yield_queues, RzThreadQueue /**/ *io_request, RzThreadQueue /**/ *io_result, @@ -25,7 +26,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, break; } STACK_ABSTR_DATA_OUT(inc); - rz_bv_set_from_ut64(inc.bv, state->nop_pc_inc); + rz_bv_set_from_ut64(inc.bv, nop_pc_inc); rz_bv_cast_inplace(inc.bv, rz_bv_len(pc->bv), false); if (!rz_bv_add_inplace(pc->bv, inc.bv, NULL)) { goto error; @@ -33,10 +34,10 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, break; } case RZ_IL_OP_SEQ: { - if (!interpreter_prototype_eval_effect(state, effect->op.seq.x, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_effect(state, effect->op.seq.x, nop_pc_inc, yield_queues, io_request, io_result, plugin_data)) { goto error; } - if (!interpreter_prototype_eval_effect(state, effect->op.seq.y, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_effect(state, effect->op.seq.y, nop_pc_inc, yield_queues, io_request, io_result, plugin_data)) { goto error; } break; @@ -77,11 +78,11 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, } if (abstr_is_true(state, &eval_out)) { - if (!interpreter_prototype_eval_effect(state, effect->op.branch.true_eff, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_effect(state, effect->op.branch.true_eff, nop_pc_inc, yield_queues, io_request, io_result, plugin_data)) { goto error; } } else { - if (!interpreter_prototype_eval_effect(state, effect->op.branch.false_eff, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_effect(state, effect->op.branch.false_eff, nop_pc_inc, yield_queues, io_request, io_result, plugin_data)) { goto error; } } diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 31f21bf0941..85d4aca9075 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -422,7 +422,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( ut64 addr = rz_bv_to_ut64(out->bv); size_t addr_bits = pure->code == RZ_IL_OP_LOAD ? state->addr_bits : pure->op.loadw.n_bits; if (!load_abstr_data(state, addr, addr_bits, out, io_request, io_result)) { - return false; + goto map_to_bottom; } break; } From 630386a1c5eb3dbfa76d0ce75da5d39246486a9d Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 15 Dec 2025 01:18:34 +0100 Subject: [PATCH 113/334] Always assume the read succeeded. --- librz/inquiry/inquiry.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 8c68fb9097a..8fc721b12b1 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -323,10 +323,15 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { "This is more than configured. It will only read MAX_IO_DATA_READ bytes.\nPlease set MAX_IO_DATA_READ to a larger value and rebuild Rizin.\n", MAX_IO_DATA_READ); } - // Cast to ut8* here. The constant is only there so interpreter plugins don't free it by accident. - int n_read = rz_io_nread_at(core->io, io_req->addr, (ut8 *)io_res->read.data, io_req->n_bytes > MAX_IO_DATA_READ ? MAX_IO_DATA_READ : io_req->n_bytes); - io_res->req_ok = n_read >= 0; - io_res->read.n_bytes = n_read; + // Cast to constant ut8* here. The constant is only there so interpreter plugins don't free it by accident. + (void)rz_io_read_at_mapped(core->io, io_req->addr, (ut8 *)io_res->read.data, io_req->n_bytes > MAX_IO_DATA_READ ? MAX_IO_DATA_READ : io_req->n_bytes); + // The IO API doesn't have a function which can: + // - read beyond mapped regions and from cached data. + // - return the total number of bytes it read. + // + // So for this prototype we just have to assume it always succeeds :( + io_res->req_ok = true; + io_res->read.n_bytes = io_req->n_bytes; } else { io_res->req_ok = rz_io_write_at(core->io, io_req->addr, io_req->data, io_req->n_bytes); } From 460086a8e70ced0dcdb0911bc6034edc4474b084 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 15 Dec 2025 01:19:10 +0100 Subject: [PATCH 114/334] Terminate interpreter if it couldn't find successors. --- librz/inquiry/interpreter/interpreter.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index f92ab8e5cd1..4c1ae7ec94b 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -298,6 +298,10 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { if (!plugin->successors(out_state, tmp_succ_addr, plugin_data)) { goto in_loop_error; } + // It is possible that the successor function doesn't add successors. + // E.g. because the PC is an abstract value. + // In this case the state counts as invalid. + new_state_reached = !rz_vector_empty(tmp_succ_addr); // Request the successor effects over the queue. while (!rz_vector_empty(tmp_succ_addr)) { rz_vector_pop_front(tmp_succ_addr, addr); From ed4326c3237cc618fe0695fb953e6d98027db149 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 15 Dec 2025 15:22:21 +0100 Subject: [PATCH 115/334] Warn if PC is set to an abstract value --- librz/inquiry/interpreter/prototype/eval_effect.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 69c11baabe5..f6088ffe32d 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -57,6 +57,9 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, if (!interpreter_prototype_eval_pure(state, effect->op.jmp.dst, &eval_out, yield_queues, io_request, io_result, plugin_data)) { goto error; } + if (!eval_out.is_concrete) { + RZ_LOG_WARN("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", rz_bv_to_ut64(AD(state->pc->abstr_data)->bv)); + } // Setting the PC to a bottom value is allowed here! // The successor function will handle this case. copy_abstr_data(state->pc->abstr_data, &eval_out); From b9fff21ae239b3bdc2a3a5058affed38e1646e99 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 15 Dec 2025 15:30:46 +0100 Subject: [PATCH 116/334] Print packet address of instruction with too few ops. --- librz/arch/isa/hexagon/hexagon_il.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librz/arch/isa/hexagon/hexagon_il.c b/librz/arch/isa/hexagon/hexagon_il.c index 47defc91d56..30b159abf2c 100644 --- a/librz/arch/isa/hexagon/hexagon_il.c +++ b/librz/arch/isa/hexagon/hexagon_il.c @@ -228,7 +228,7 @@ static RZ_OWN RzILOpEffect *hex_pkt_to_il_seq(HexPkt *pkt) { rz_pvector_clear(pkt->il_ops); // We need at least the instruction op and the packet commit. // So if there aren't at least two ops something went wrong. - RZ_LOG_WARN("Invalid il ops sequence! There should be at least two il ops per packet.\n"); + RZ_LOG_WARN("Invalid il ops sequence! There should be at least two il ops per packet. pkt_addr = 0x%" PFMT32x "\n", pkt->pkt_addr); return NULL; } RzILOpEffect *complete_seq = EMPTY(); From 373875f97b81e4408ff4b37ab434f458c27fa4ea Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 15 Dec 2025 15:57:48 +0100 Subject: [PATCH 117/334] Move and log set pc actions --- .../interpreter/p/interpreter_prototype.c | 7 ------- librz/inquiry/interpreter/prototype/eval.c | 16 ++++++++++++++++ librz/inquiry/interpreter/prototype/eval.h | 3 +++ .../inquiry/interpreter/prototype/eval_effect.c | 9 +++++++++ 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 6be8366c930..dcba60463d5 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -137,13 +137,6 @@ static ut64 hash_state(RZ_NONNULL const RzInterpreterAbstrState *state, void *pl return h; } -static bool set_pc(RzInterpreterAbstrState *state, ut64 pc, - void *plugin_data) { - rz_return_val_if_fail(state, false); - AD(state->pc->abstr_data)->is_concrete = true; - return rz_bv_set_from_ut64(AD(state->pc->abstr_data)->bv, pc); -} - static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .name = "abstr_int_prototype", .author = "Rot127", diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 178a30e1f4c..60ada517297 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -133,6 +133,9 @@ bool store_abstr_data( } else { buf = buf_stack; } + char *bytes = rz_bv_as_hex_string(src->bv, true); + RZ_LOG_WARN("Prototype: STORE @ 0x%" PFMT64x " : %s\n", io_req->addr, bytes); + free(bytes); rz_bv_set_to_bytes_be(src->bv, buf); io_req->type = RZ_INTERPRETER_IO_WRITE; io_req->addr = addr; @@ -185,5 +188,18 @@ bool load_abstr_data( out->is_concrete = true; rz_bv_cast_inplace(out->bv, size, 0); rz_bv_set_from_bytes_be(out->bv, io_res->read.data, 0, io_res->read.n_bytes); + char *bytes = rz_bv_as_hex_string(out->bv, true); + RZ_LOG_WARN("Prototype: READ @ 0x%" PFMT64x " : %s\n", io_req->addr, bytes); + free(bytes); return true; } + +bool set_pc(RzInterpreterAbstrState *state, ut64 pc, + void *plugin_data) { + rz_return_val_if_fail(state, false); + AD(state->pc->abstr_data)->is_concrete = true; + RZ_LOG_WARN("Prototype: set_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x "(Concrete)\n", + rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), + pc); + return rz_bv_set_from_ut64(AD(state->pc->abstr_data)->bv, pc); +} diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index fcfe2201863..abfc20ce0c4 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -106,4 +106,7 @@ bool report_xref_yield( const ProtoIntrprAbstrData *to, RzAnalysisXRefType type); +bool set_pc(RzInterpreterAbstrState *state, ut64 pc, + void *plugin_data); + #endif // PROTOYPE_EVAL_H diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index f6088ffe32d..e0784586e91 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -28,9 +28,14 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, STACK_ABSTR_DATA_OUT(inc); rz_bv_set_from_ut64(inc.bv, nop_pc_inc); rz_bv_cast_inplace(inc.bv, rz_bv_len(pc->bv), false); + ut64 old_pc = rz_bv_to_ut64(pc->bv); if (!rz_bv_add_inplace(pc->bv, inc.bv, NULL)) { goto error; } + RZ_LOG_WARN("Prototype: NOP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", + old_pc, + rz_bv_to_ut64(pc->bv), + pc->is_concrete ? "Concrete" : "Abstract"); break; } case RZ_IL_OP_SEQ: { @@ -60,6 +65,10 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, if (!eval_out.is_concrete) { RZ_LOG_WARN("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", rz_bv_to_ut64(AD(state->pc->abstr_data)->bv)); } + RZ_LOG_WARN("Prototype: JMP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", + rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), + rz_bv_to_ut64(eval_out.bv), + eval_out.is_concrete ? "Concrete" : "Abstract"); // Setting the PC to a bottom value is allowed here! // The successor function will handle this case. copy_abstr_data(state->pc->abstr_data, &eval_out); From cc9d627401d9e8098458d5ca0979f0fd53a3251e Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 15 Dec 2025 16:08:41 +0100 Subject: [PATCH 118/334] Fix MSB/LSB/IS_ZERO --- librz/inquiry/interpreter/prototype/eval_pure.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 85d4aca9075..5d3ad2400bf 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -245,15 +245,16 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } if (!interpreter_prototype_eval_pure(state, bv, out, yield_queues, io_request, io_result, plugin_data)) { - RZ_LOG_ERROR("prototype: MSB bv failed to evaluate.\n"); + RZ_LOG_ERROR("prototype: MSB/LSB/IS_ZERO bv failed to evaluate.\n"); return false; } if (!out->is_concrete) { goto map_to_bottom; } + bool truth = truth_test(out->bv); rz_bv_cast_inplace(out->bv, 1, false); // TODO: Truth bit. - rz_bv_set(out->bv, 0, truth_test(out->bv)); + rz_bv_set(out->bv, 0, truth); break; } case RZ_IL_OP_NEG: { From 7992cd374c9a2a4da7d9e7eb6e92c683df9592f3 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 15 Dec 2025 17:50:03 +0100 Subject: [PATCH 119/334] Init PC as concrete values --- librz/inquiry/interpreter/p/interpreter_prototype.c | 1 + 1 file changed, 1 insertion(+) diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index dcba60463d5..09271fcccdb 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -51,6 +51,7 @@ bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_point, void *plugin_data) { state->pc->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); AD(state->pc->abstr_data)->bv = rz_bv_new_from_ut64(state->addr_bits, entry_point); + AD(state->pc->abstr_data)->is_concrete = true; RzIterator *it = ht_up_as_iter(state->globals); RzInterpreterAbstrVal **v; rz_iterator_foreach(it, v) { From 344234f2bb46f3c3facfbc369f0d3296f7f2eb3e Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 15 Dec 2025 17:50:20 +0100 Subject: [PATCH 120/334] Implement MUL --- .../inquiry/interpreter/prototype/eval_pure.c | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 5d3ad2400bf..6e5363c60f1 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -427,7 +427,32 @@ RZ_IPI bool interpreter_prototype_eval_pure( } break; } - case RZ_IL_OP_MUL: + case RZ_IL_OP_MUL: { + RzILOpPure *px = pure->op.mul.x; + RzILOpPure *py = pure->op.mul.y; + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { + RZ_LOG_ERROR("prototype: MUL x failed to evaluate.\n"); + return false; + } + if (!out->is_concrete) { + goto map_to_bottom; + } + STACK_ABSTR_DATA_OUT(y); + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { + RZ_LOG_ERROR("prototype: MUL y failed to evaluate.\n"); + return false; + } + if (!y.is_concrete) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + if (!rz_bv_mul_inplace(out->bv, y.bv)) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + rz_bv_fini(y.bv); + break; + } case RZ_IL_OP_DIV: case RZ_IL_OP_SDIV: case RZ_IL_OP_MOD: From b331c6145756b6a41731a1af90cf068efa63c2d9 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 15 Dec 2025 19:02:16 +0100 Subject: [PATCH 121/334] Hexagon: Allow to set instruction packet size in RzAnalysisOp --- librz/arch/p/analysis/analysis_hexagon.c | 4 ++++ librz/include/rz_analysis.h | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/librz/arch/p/analysis/analysis_hexagon.c b/librz/arch/p/analysis/analysis_hexagon.c index 4409ff951a2..72f9d85cc04 100644 --- a/librz/arch/p/analysis/analysis_hexagon.c +++ b/librz/arch/p/analysis/analysis_hexagon.c @@ -35,6 +35,10 @@ RZ_API int hexagon_v6_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, cons if (mask & RZ_ANALYSIS_OP_MASK_IL) { op->il_op = hex_get_il_op(addr, rev.pkt_fully_decoded, rev.state); } + HexPkt *p = hex_get_pkt(rev.state, addr); + if (p && (mask & RZ_ANALYSIS_OP_MASK_INSN_PKT)) { + op->size = rz_list_length(p->bin) * HEX_INSN_SIZE; + } return op->size; } diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index af18f77ff6c..52f9dfb314d 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -357,7 +357,8 @@ typedef enum { RZ_ANALYSIS_OP_MASK_OPEX = (1 << 3), // It fills RzAnalysisop->opex info RZ_ANALYSIS_OP_MASK_DISASM = (1 << 4), // It fills RzAnalysisop->mnemonic // should be RzAnalysisOp->disasm // only from rz_core_analysis_op() RZ_ANALYSIS_OP_MASK_IL = (1 << 5), // It fills RzAnalysisop->il_op - RZ_ANALYSIS_OP_MASK_ALL = RZ_ANALYSIS_OP_MASK_ESIL | RZ_ANALYSIS_OP_MASK_VAL | RZ_ANALYSIS_OP_MASK_HINT | RZ_ANALYSIS_OP_MASK_OPEX | RZ_ANALYSIS_OP_MASK_DISASM | RZ_ANALYSIS_OP_MASK_IL + RZ_ANALYSIS_OP_MASK_INSN_PKT = (1 << 6), // Set op.size to the size of the instruction packet, not the single instruction. + RZ_ANALYSIS_OP_MASK_ALL = RZ_ANALYSIS_OP_MASK_ESIL | RZ_ANALYSIS_OP_MASK_VAL | RZ_ANALYSIS_OP_MASK_HINT | RZ_ANALYSIS_OP_MASK_OPEX | RZ_ANALYSIS_OP_MASK_DISASM | RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_INSN_PKT } RzAnalysisOpMask; typedef enum { From 661998423f582784c899414d56304a98cd54aaf9 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 15 Dec 2025 19:03:37 +0100 Subject: [PATCH 122/334] Refactor to make IL basic block a vector of instruction packets. --- librz/include/rz_inquiry.h | 2 +- librz/include/rz_inquiry/rz_interpreter.h | 13 +++++---- librz/inquiry/inquiry.c | 8 +++--- librz/inquiry/inquiry_helpers.c | 28 +++++++++---------- librz/inquiry/interpreter/interpreter.c | 27 +++++++++++------- .../interpreter/p/interpreter_prototype.c | 18 +++++++++--- .../interpreter/prototype/eval_effect.c | 20 ++++++------- 7 files changed, 67 insertions(+), 49 deletions(-) diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 073c34c4834..c30a8c95acc 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -34,7 +34,7 @@ RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void); RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *a); RZ_API bool rz_inquiry_op_type_is_eob(_RzAnalysisOpType type); -RZ_API RZ_OWN RzInterpreterILOp *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr); +RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr); RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzList /**/ *allowed_io_maps); diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 548e4a20ceb..b1f23c6854b 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -95,10 +95,12 @@ typedef struct { RzThreadQueue /**/ *yield_queue; } RzInterpreterYieldQueue; +typedef RzPVector RzInterpreterILBB; + typedef struct { - RzILOpEffect *effect; ///< The effect to evaluate. - size_t asm_op_size; ///< The size of the instruction packet. Used to increment the PC if no JMP occurred. -} RzInterpreterILOp; + RzILOpEffect *effect; ///< Vector with all instruction packets of a basic block. + size_t insn_pkt_size; ///< The size of the instruction packet. Used to increment the PC if no JMP occurred. +} RzInterpreterInsnPkt; typedef struct { const char *name; @@ -137,7 +139,7 @@ typedef struct { * \brief Evaluates an effect with the mutable state. */ bool (*eval)(RZ_NONNULL RzInterpreterAbstrState *state, - RZ_NONNULL const RzInterpreterILOp *il_op, + RZ_NONNULL const RzInterpreterILBB *il_op, RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_request, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_result, @@ -202,7 +204,8 @@ typedef struct { RzInterpreterPlugin *plugin; } RzInterpreterSet; -RZ_API void rz_interpreter_il_op_free(RZ_NULLABLE RZ_OWN RzInterpreterILOp *il_op); +RZ_API void rz_interpreter_il_bb_free(RZ_NULLABLE RZ_OWN RzInterpreterILBB *il_op); +RZ_API void rz_interpreter_insn_pkt_free(RZ_NULLABLE RZ_OWN RzInterpreterInsnPkt *pkt); RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldQueue *yield_queue); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 8fc721b12b1..a0e30ec7e13 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -116,7 +116,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { bool return_code = true; RzThreadQueue *io_request_q = NULL; RzThreadQueue *io_result_q = NULL; - RzInterpreterILOp *il_op = NULL; + RzInterpreterILBB *il_op = NULL; RzThreadQueue *addr_queue = NULL; RzList *boundaries = NULL; RzInterpreterYieldQueue *yield_queue = NULL; @@ -134,7 +134,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // The pseudo cache of IL effects. // This is only a vector so we can simulate the ownership separation // of the pointers. - il_cache = rz_pvector_new((RzPVectorFree)rz_interpreter_il_op_free); + il_cache = rz_pvector_new((RzPVectorFree)rz_interpreter_il_bb_free); // The queue to pass the Effects to the interpreter. // This is only one queue for the prototype. // In practice it would be one for each interpreter. @@ -287,8 +287,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { { ut64 *addr = rz_th_queue_pop(addr_queue, false); if (addr) { - RZ_LOG_WARN("INQUIRY: Received IL request: 0x%" PFMT64x "\n", (*addr)); - RzInterpreterILOp *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); + printf("INQUIRY: Received IL request: 0x%" PFMT64x "\n", (*addr)); + RzInterpreterILBB *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); if (!bb) { RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", *addr); // Signal interpreter the lifting failed. diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c index c3eefd4a4b9..438c409fd11 100644 --- a/librz/inquiry/inquiry_helpers.c +++ b/librz/inquiry/inquiry_helpers.c @@ -46,10 +46,9 @@ RZ_API bool rz_inquiry_op_type_is_eob(_RzAnalysisOpType type) { } } -RZ_API RZ_OWN RzInterpreterILOp *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr) { +RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr) { rz_return_val_if_fail(analysis && analysis->cur && io, NULL); - size_t size_bb = 0; - RzILOpEffect *bb = NULL; + RzInterpreterILBB *il_bb = NULL; RzAnalysisOp op = { 0 }; rz_analysis_op_init(&op); // Estimate a reasonable number of bytes to read. @@ -58,13 +57,17 @@ RZ_API RZ_OWN RzInterpreterILOp *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana if (!max_read_size || !buf) { goto fail; } + il_bb = rz_pvector_new((RzPVectorFree)rz_interpreter_insn_pkt_free); + if (!il_bb) { + goto fail; + } bool changes_cf = true; do { if (!rz_io_read_at_mapped(io, addr, buf, max_read_size)) { RZ_LOG_WARN("inquiry: Failed to read memory for IL basic block generation.\n"); goto fail; } - if (rz_analysis_op(analysis, &op, addr, buf, max_read_size, RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_BASIC) <= 0) { + if (rz_analysis_op(analysis, &op, addr, buf, max_read_size, RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_INSN_PKT) <= 0) { RZ_LOG_ERROR("Failed to decode IL op\n"); goto fail; } @@ -74,9 +77,10 @@ RZ_API RZ_OWN RzInterpreterILOp *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana lifted = false; op.il_op = rz_il_op_new_nop(); } - - bb = bb ? rz_il_op_new_seq(bb, op.il_op) : op.il_op; - size_bb += op.size; + RzInterpreterInsnPkt *pkt = RZ_NEW0(RzInterpreterInsnPkt); + pkt->effect = op.il_op; + pkt->insn_pkt_size = op.size; + rz_pvector_push(il_bb, pkt); // Take ownership of IL op pointer. op.il_op = NULL; if (lifted) { @@ -88,17 +92,11 @@ RZ_API RZ_OWN RzInterpreterILOp *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana } while (!changes_cf); free(buf); - RzInterpreterILOp *il_op = RZ_NEW(RzInterpreterILOp); - if (!il_op) { - goto fail; - } - il_op->asm_op_size = size_bb; - il_op->effect = bb; - return il_op; + return il_bb; fail: free(buf); rz_analysis_op_fini(&op); - rz_il_op_effect_free(bb); + rz_pvector_free(il_bb); return NULL; } diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 4c1ae7ec94b..e47ad07cded 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -12,14 +12,21 @@ #include #include -RZ_API void rz_interpreter_il_op_free(RZ_NULLABLE RZ_OWN RzInterpreterILOp *il_op) { - if (!il_op) { +RZ_API void rz_interpreter_insn_pkt_free(RZ_NULLABLE RZ_OWN RzInterpreterInsnPkt *pkt) { + if (!pkt) { return; } - if (il_op->effect) { - rz_il_op_effect_free(il_op->effect); + if (pkt->effect) { + rz_il_op_effect_free(pkt->effect); + } + free(pkt); +} + +RZ_API void rz_interpreter_il_bb_free(RZ_NULLABLE RZ_OWN RzInterpreterILBB *il_op) { + if (!il_op) { + return; } - free(il_op); + rz_pvector_free(il_op); } RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldQueue *yield_queue) { @@ -257,7 +264,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { RzInterpreterAbstrState *out_state = NULL; ut64 out_hash = 0; - const RzInterpreterILOp *il_op = rz_th_queue_pop(iset->il_queue, false); + const RzInterpreterILBB *il_bb = rz_th_queue_pop(iset->il_queue, false); // TODO: Add support for multiple entry points by spawning an interpreter for each of them. // For now let's just drop them. RzList *additional_entries = rz_th_queue_pop_all(iset->il_queue); @@ -269,7 +276,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { tmp_succ_addr = rz_vector_new(sizeof(ut64), NULL, NULL); succ_states = rz_vector_new(sizeof(SuccessorState), NULL, NULL); reachable_states = rz_set_u_new(); - if (!tmp_succ_addr || !succ_states || !il_op || !reachable_states) { + if (!tmp_succ_addr || !succ_states || !il_bb || !reachable_states) { goto pre_loop_error; } ut64 _addr = 0; @@ -277,7 +284,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { while (rz_atomic_bool_get(iset->is_running_flag)) { // Evaluate the effect on the input state. - if (!plugin->eval(in_state, il_op, iset->yield_queues, iset->io_request, iset->io_result, plugin_data)) { + if (!plugin->eval(in_state, il_bb, iset->yield_queues, iset->io_request, iset->io_result, plugin_data)) { goto in_loop_error; } // The input state was (almost always) manipulated by eval(). Rename to clarify. @@ -325,8 +332,8 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { SuccessorState next = { 0 }; rz_vector_pop_front(succ_states, &next); in_hash = next.in_state_hash; - il_op = rz_th_queue_wait_pop(iset->il_queue, false); - if (!il_op || !plugin->set_pc(in_state, next.addr, plugin_data)) { + il_bb = rz_th_queue_wait_pop(iset->il_queue, false); + if (!il_bb || !plugin->set_pc(in_state, next.addr, plugin_data)) { // Some error occurred lifting this basic block. Or updating the PC. // Abort execution. goto in_loop_error; diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 09271fcccdb..b2e51659a4d 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -9,17 +9,27 @@ #include "../prototype/eval.h" static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, - RZ_NONNULL const RzInterpreterILOp *il_op, + RZ_NONNULL const RzInterpreterILBB *il_op, RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_request, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_result, void *plugin_data) { - RZ_LOG_WARN("Eval PC = 0x%" PFMT64x "\n", rz_bv_to_ut64(AD(state->pc->abstr_data)->bv)); - bool result = interpreter_prototype_eval_effect(state, il_op->effect, il_op->asm_op_size, yield_queues, io_request, io_result, plugin_data); + void **it; + rz_pvector_foreach(il_op, it) { + ut64 pc = rz_bv_to_ut64(AD(state->pc->abstr_data)->bv); + printf("Eval PC = 0x%" PFMT64x "\n", pc); + RzInterpreterInsnPkt *pkt = *it; + if (!interpreter_prototype_eval_effect(state, pkt->effect, pkt->insn_pkt_size, yield_queues, io_request, io_result, plugin_data)) { + return false; + } + if (pc == rz_bv_to_ut64(AD(state->pc->abstr_data)->bv)) { + set_pc(state, pc + pkt->insn_pkt_size, plugin_data); + } + } // TODO: Clean up local variables. // Or maybe not? Just costs performance. And the uplifted instructions should // always set it before, otherwise the tests don't pass. - return result; + return true; } bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index e0784586e91..94de86ab771 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -7,7 +7,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, - size_t nop_pc_inc, + size_t insn_pkt_size, HtUP /**/ *yield_queues, RzThreadQueue /**/ *io_request, RzThreadQueue /**/ *io_result, @@ -26,23 +26,23 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, break; } STACK_ABSTR_DATA_OUT(inc); - rz_bv_set_from_ut64(inc.bv, nop_pc_inc); + rz_bv_set_from_ut64(inc.bv, insn_pkt_size); rz_bv_cast_inplace(inc.bv, rz_bv_len(pc->bv), false); ut64 old_pc = rz_bv_to_ut64(pc->bv); if (!rz_bv_add_inplace(pc->bv, inc.bv, NULL)) { goto error; } - RZ_LOG_WARN("Prototype: NOP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", + printf("Prototype: NOP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", old_pc, rz_bv_to_ut64(pc->bv), pc->is_concrete ? "Concrete" : "Abstract"); break; } case RZ_IL_OP_SEQ: { - if (!interpreter_prototype_eval_effect(state, effect->op.seq.x, nop_pc_inc, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_effect(state, effect->op.seq.x, insn_pkt_size, yield_queues, io_request, io_result, plugin_data)) { goto error; } - if (!interpreter_prototype_eval_effect(state, effect->op.seq.y, nop_pc_inc, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_effect(state, effect->op.seq.y, insn_pkt_size, yield_queues, io_request, io_result, plugin_data)) { goto error; } break; @@ -63,20 +63,20 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, goto error; } if (!eval_out.is_concrete) { - RZ_LOG_WARN("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", rz_bv_to_ut64(AD(state->pc->abstr_data)->bv)); + printf("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", rz_bv_to_ut64(AD(state->pc->abstr_data)->bv)); } - RZ_LOG_WARN("Prototype: JMP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", + printf("Prototype: JMP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), rz_bv_to_ut64(eval_out.bv), eval_out.is_concrete ? "Concrete" : "Abstract"); // Setting the PC to a bottom value is allowed here! // The successor function will handle this case. - copy_abstr_data(state->pc->abstr_data, &eval_out); if (eval_out.is_concrete) { // NOTE: This prototype can't classify into call or jump. // Everything is just a jump for it at this point. report_xref_yield(state, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &eval_out, RZ_ANALYSIS_XREF_TYPE_CODE); } + copy_abstr_data(state->pc->abstr_data, &eval_out); break; } case RZ_IL_OP_BRANCH: { @@ -90,11 +90,11 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, } if (abstr_is_true(state, &eval_out)) { - if (!interpreter_prototype_eval_effect(state, effect->op.branch.true_eff, nop_pc_inc, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_effect(state, effect->op.branch.true_eff, insn_pkt_size, yield_queues, io_request, io_result, plugin_data)) { goto error; } } else { - if (!interpreter_prototype_eval_effect(state, effect->op.branch.false_eff, nop_pc_inc, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_effect(state, effect->op.branch.false_eff, insn_pkt_size, yield_queues, io_request, io_result, plugin_data)) { goto error; } } From 446f801dca48f3bb888d918a4f4e1acfde48deda Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 15 Dec 2025 19:04:10 +0100 Subject: [PATCH 123/334] Use printf for debug printing to make it usable with grep --- librz/inquiry/inquiry.c | 20 +++++++++---------- librz/inquiry/interpreter/interpreter.c | 2 +- librz/inquiry/interpreter/prototype/eval.c | 8 +++----- .../inquiry/interpreter/prototype/eval_pure.c | 1 + 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index a0e30ec7e13..7c7af886bbf 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -256,7 +256,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { } // Dispatch prototype interpreter into a thread. - RZ_LOG_WARN("INQUIRY: Start main interpretation thread.\n"); + printf("INQUIRY: Start main interpretation thread.\n"); interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); // From here on, the code plays the role of the cache, IO handler, @@ -266,10 +266,10 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // - Receiving and adding the found xrefs to RzAnalysis. // In the final implementation each of those roles would be split into // two or more separated modules running in parallel. - RZ_LOG_WARN("INQUIRY: Enforce enabling IO cache.\n"); + printf("INQUIRY: Enforce enabling IO cache.\n"); const char *io_cache_opt = rz_config_get(core->config, "io.cache"); rz_config_set(core->config, "io.cache", "true"); - RZ_LOG_WARN("INQUIRY: Start IL providing loop.\n"); + printf("INQUIRY: Start IL providing loop.\n"); // Poor man's shared memory. RzInterpreterIOResult _io_res = { 0 }; @@ -296,7 +296,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { rz_atomic_bool_set(is_running, false); continue; } - RZ_LOG_WARN("INQUIRY: Send IL result: %p.\n", bb); + printf("INQUIRY: Send IL result: %p.\n", bb); rz_pvector_push(il_cache, bb); // TODO: Free unused if too big. rz_th_queue_push(il_queue, bb, true); @@ -314,7 +314,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { continue; } - RZ_LOG_WARN("INQUIRY: Received IO %s request: 0x%" PFMT64x "\n", + printf("INQUIRY: Received IO %s request: 0x%" PFMT64x "\n", io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", io_req->addr); if (io_req->type == RZ_INTERPRETER_IO_READ) { @@ -335,7 +335,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { } else { io_res->req_ok = rz_io_write_at(core->io, io_req->addr, io_req->data, io_req->n_bytes); } - RZ_LOG_WARN("INQUIRY: Sent IO %s result. Success = %s.\n", + printf("INQUIRY: Sent IO %s result. Success = %s.\n", io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", io_res->req_ok ? "true" : "false"); rz_th_queue_push(io_result_q, io_res, true); @@ -351,20 +351,20 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { } // TODO: Currently we can't classify calls as such. rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, xref->type); - RZ_LOG_WARN("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)) + printf("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)); } } - RZ_LOG_WARN("INQUIRY: Done\n"); + printf("INQUIRY: Done\n"); rz_config_set(core->config, "io.cache", io_cache_opt); // Wait for thread to finish before cleaning. error_free: - RZ_LOG_WARN("INQUIRY: Close queues\n"); + printf("INQUIRY: Close queues\n"); rz_th_cond_signal_all(rz_th_queue_get_cond(iset->il_queue)); rz_th_cond_signal_all(rz_th_queue_get_cond(iset->io_result)); if (interpr_th){ - RZ_LOG_WARN("INQUIRY: Wait for join\n"); + printf("INQUIRY: Wait for join\n"); rz_th_wait(interpr_th); return_code = rz_th_get_retv(interpr_th); rz_th_free(interpr_th); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index e47ad07cded..68df9665924 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -233,7 +233,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { entry_assert_error); bool success = false; - RZ_LOG_WARN("INTERPRETER Main: Hello.\n"); + printf("INTERPRETER Main: Hello.\n"); RzInterpreterPlugin *plugin = iset->plugin; void **priv_ptr = NULL; diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 60ada517297..f21f1bbdc18 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -134,14 +134,13 @@ bool store_abstr_data( buf = buf_stack; } char *bytes = rz_bv_as_hex_string(src->bv, true); - RZ_LOG_WARN("Prototype: STORE @ 0x%" PFMT64x " : %s\n", io_req->addr, bytes); + printf("Prototype: STORE @ 0x%" PFMT64x " : %s\n", io_req->addr, bytes); free(bytes); rz_bv_set_to_bytes_be(src->bv, buf); io_req->type = RZ_INTERPRETER_IO_WRITE; io_req->addr = addr; io_req->data = buf; - RZ_LOG_WARN("Prototype: Send store request\n"); rz_th_queue_push(io_request, io_req, true); // Wait for write being done. RzInterpreterIOResult *io_res = rz_th_queue_wait_pop(io_result, false); @@ -168,7 +167,6 @@ bool load_abstr_data( io_req->type = RZ_INTERPRETER_IO_READ; io_req->addr = addr; io_req->n_bytes = size; - RZ_LOG_WARN("Prototype: Send load request\n"); rz_th_queue_push(io_request, io_req, true); // Wait for load being done. RzInterpreterIOResult *io_res = rz_th_queue_wait_pop(io_result, false); @@ -189,7 +187,7 @@ bool load_abstr_data( rz_bv_cast_inplace(out->bv, size, 0); rz_bv_set_from_bytes_be(out->bv, io_res->read.data, 0, io_res->read.n_bytes); char *bytes = rz_bv_as_hex_string(out->bv, true); - RZ_LOG_WARN("Prototype: READ @ 0x%" PFMT64x " : %s\n", io_req->addr, bytes); + printf("Prototype: READ @ 0x%" PFMT64x " : %s\n", io_req->addr, bytes); free(bytes); return true; } @@ -198,7 +196,7 @@ bool set_pc(RzInterpreterAbstrState *state, ut64 pc, void *plugin_data) { rz_return_val_if_fail(state, false); AD(state->pc->abstr_data)->is_concrete = true; - RZ_LOG_WARN("Prototype: set_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x "(Concrete)\n", + printf("Prototype: set_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x "(Concrete)\n", rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), pc); return rz_bv_set_from_ut64(AD(state->pc->abstr_data)->bv, pc); diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 6e5363c60f1..ac08170b26d 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -491,6 +491,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_FPOWN: case RZ_IL_OP_FCOMPOUND: case RZ_IL_OP_FEXCEPT: + printf("\nUnhandled op\n"); // Not implemented. goto map_to_bottom; } From f5a7bdf669dbd39195a026f93afd93f58bf16252 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 16 Dec 2025 16:29:18 +0100 Subject: [PATCH 124/334] Keep the IL config in the state --- librz/include/rz_inquiry/rz_interpreter.h | 6 +++--- librz/inquiry/inquiry.c | 20 +++++++------------ librz/inquiry/interpreter/interpreter.c | 10 +++++++--- librz/inquiry/interpreter/meson.build | 2 ++ .../interpreter/p/interpreter_prototype.c | 4 ++-- librz/inquiry/interpreter/prototype/eval.c | 4 ++-- .../inquiry/interpreter/prototype/eval_pure.c | 4 ++-- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index b1f23c6854b..851ca701887 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -59,7 +59,7 @@ typedef struct { HtUP /**/ *locals; ///< Local variables. Indexed by DJB2 hash of the local name. HtUP /**/ *lets; ///< Let variables. Indexed by DJB2 hash of the let name. RzInterpreterAbstrVal *pc; ///< In our RzIL implementation the PC is not part of the register file. - size_t addr_bits; ///< Number of bits of a memory address. + RzAnalysisILConfig *il_config; ///< The IL configuration of the RzArch plugin. void *ext; ///< Optional state extensions. Managed by individual interpreters. } RzInterpreterAbstrState; @@ -211,8 +211,8 @@ RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYiel RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( RzInterpreterAbstraction kinds, - RZ_NULLABLE const RzPVector *reg_names, - size_t addr_bits); + RZ_OWN RZ_NONNULL RzAnalysisILConfig *il_config, + RZ_NULLABLE const RzPVector *reg_names); RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbstrState *state); RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpreterYieldKind kind, diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 7c7af886bbf..1e5f7cc0eef 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -72,16 +72,6 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL co return false; } -static ut64 get_mem_addr_bits(RzAnalysis *analysis) { - if (analysis->cur->il_config) { - RzAnalysisILConfig *config = analysis->cur->il_config(analysis); - size_t key_size = config->mem_key_size; - rz_analysis_il_config_free(config); - return key_size; - } - return analysis->cur->bits; -} - static RzPVector *get_reg_names(RzAnalysis *analysis) { RzPVector *reg_names = rz_pvector_new(free); if (analysis->cur->il_config) { @@ -228,12 +218,16 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { ht_up_insert(yield_queues, yield_kind, yield_queue); // Initialize the abstract state with the architecture's registers. - size_t addr_bits = get_mem_addr_bits(core->analysis); + if (!core->analysis->cur->il_config) { + RZ_LOG_ERROR("The RzArch plugin doesn't have il_config() implemented.\n"); + goto error_free; + } + RzAnalysisILConfig *config = core->analysis->cur->il_config(core->analysis); RzPVector *reg_names = get_reg_names(core->analysis); abstr_state = rz_interpreter_abstr_state_new( RZ_INTERPRETER_ABSTRACTION_CONST, - reg_names, - addr_bits); + config, + reg_names); rz_pvector_free(reg_names); // Bundle all the queues into one object to pass it to the thread. diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 68df9665924..a30652d4587 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -76,8 +76,9 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre */ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( RzInterpreterAbstraction kinds, - RZ_NULLABLE const RzPVector *reg_names, - size_t addr_bits) { + RZ_OWN RZ_NONNULL RzAnalysisILConfig *il_config, + RZ_NULLABLE const RzPVector *reg_names) { + rz_return_val_if_fail(il_config, NULL); RzInterpreterAbstrState *state = RZ_NEW0(RzInterpreterAbstrState); if (!state) { return NULL; @@ -113,7 +114,7 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( } state->locals = ht_up_new(NULL, free); state->lets = ht_up_new(NULL, free); - state->addr_bits = addr_bits; + state->il_config = il_config; return state; } @@ -133,6 +134,9 @@ RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbst if (state->pc) { free(state->pc); } + if (state->il_config) { + rz_analysis_il_config_free(state->il_config); + } free(state); } diff --git a/librz/inquiry/interpreter/meson.build b/librz/inquiry/interpreter/meson.build index 34deb8c33da..2001f212842 100644 --- a/librz/inquiry/interpreter/meson.build +++ b/librz/inquiry/interpreter/meson.build @@ -24,6 +24,7 @@ rz_interpreter_inc = [platform_inc] rz_interpreter = library('rz_interpreter', rz_interpreter_sources, include_directories: rz_interpreter_inc, dependencies: [ + rz_arch_dep, rz_cons_dep, rz_hash_dep, rz_il_dep, @@ -46,6 +47,7 @@ meson.override_dependency('rz_interpreter', rz_interpreter_dep) modules += { 'rz_interpreter': { 'target': rz_interpreter, 'dependencies': [ + 'rz_arch', 'rz_cons', 'rz_hash', 'rz_il', diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index b2e51659a4d..aced4e3905b 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -60,7 +60,7 @@ bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_point, void *plugin_data) { state->pc->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); - AD(state->pc->abstr_data)->bv = rz_bv_new_from_ut64(state->addr_bits, entry_point); + AD(state->pc->abstr_data)->bv = rz_bv_new_from_ut64(state->il_config->mem_key_size, entry_point); AD(state->pc->abstr_data)->is_concrete = true; RzIterator *it = ht_up_as_iter(state->globals); RzInterpreterAbstrVal **v; @@ -71,7 +71,7 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_poin // set to the length of the src. // TODO: Really a good idea to be so liberal? // Or should the length of the globals be enforced? - AD(av->abstr_data)->bv = rz_bv_new(state->addr_bits); + AD(av->abstr_data)->bv = rz_bv_new(state->il_config->mem_key_size); // TODO: This is debatable. It depends on the ABI what the default values are. // Some values must be concrete, otherwise the interpretation of the prototype end too early. AD(av->abstr_data)->is_concrete = true; diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index f21f1bbdc18..626e5c6032f 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -136,7 +136,7 @@ bool store_abstr_data( char *bytes = rz_bv_as_hex_string(src->bv, true); printf("Prototype: STORE @ 0x%" PFMT64x " : %s\n", io_req->addr, bytes); free(bytes); - rz_bv_set_to_bytes_be(src->bv, buf); + rz_bv_set_to_bytes_ble(src->bv, buf, state->il_config->big_endian); io_req->type = RZ_INTERPRETER_IO_WRITE; io_req->addr = addr; io_req->data = buf; @@ -185,7 +185,7 @@ bool load_abstr_data( } out->is_concrete = true; rz_bv_cast_inplace(out->bv, size, 0); - rz_bv_set_from_bytes_be(out->bv, io_res->read.data, 0, io_res->read.n_bytes); + rz_bv_set_from_bytes_ble(out->bv, io_res->read.data, 0, io_res->read.n_bytes, state->il_config->big_endian); char *bytes = rz_bv_as_hex_string(out->bv, true); printf("Prototype: READ @ 0x%" PFMT64x " : %s\n", io_req->addr, bytes); free(bytes); diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index ac08170b26d..49bd287dddb 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -413,7 +413,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_LOAD: { RzILOpPure *key = pure->code == RZ_IL_OP_LOAD ? pure->op.load.key : pure->op.loadw.key; if (!interpreter_prototype_eval_pure(state, key, out, yield_queues, io_request, io_result, plugin_data)) { - RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); + RZ_LOG_ERROR("prototype: LOAD/LOADW key failed to evaluate.\n"); return false; } if (!out->is_concrete) { @@ -421,7 +421,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( } report_xref_yield(state, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), out, RZ_ANALYSIS_XREF_TYPE_DATA); ut64 addr = rz_bv_to_ut64(out->bv); - size_t addr_bits = pure->code == RZ_IL_OP_LOAD ? state->addr_bits : pure->op.loadw.n_bits; + size_t addr_bits = pure->code == RZ_IL_OP_LOAD ? state->il_config->mem_key_size : pure->op.loadw.n_bits; if (!load_abstr_data(state, addr, addr_bits, out, io_request, io_result)) { goto map_to_bottom; } From de2538eb8a0ecea36d9c63d493abeb27aee19845 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 16 Dec 2025 16:53:23 +0100 Subject: [PATCH 125/334] Fix detection of basic block boundaries --- librz/arch/analysis.c | 19 ++++++++++++++---- librz/include/rz_inquiry.h | 1 - librz/inquiry/inquiry_helpers.c | 34 --------------------------------- 3 files changed, 15 insertions(+), 39 deletions(-) diff --git a/librz/arch/analysis.c b/librz/arch/analysis.c index fec232bf7a0..87435060945 100644 --- a/librz/arch/analysis.c +++ b/librz/arch/analysis.c @@ -996,10 +996,19 @@ RZ_API bool rz_analysis_op_is_call(RZ_NONNULL const RzAnalysisOp *op) { */ RZ_API bool rz_analysis_op_changes_control_flow(RZ_NONNULL const RzAnalysisOp *op) { rz_return_val_if_fail(op, false); - if (rz_analysis_op_is_eob(op)) { - return true; - } switch (op->type & RZ_ANALYSIS_OP_TYPE_MASK) { + case RZ_ANALYSIS_OP_TYPE_JMP: + case RZ_ANALYSIS_OP_TYPE_UJMP: + case RZ_ANALYSIS_OP_TYPE_RJMP: + case RZ_ANALYSIS_OP_TYPE_IJMP: + case RZ_ANALYSIS_OP_TYPE_IRJMP: + case RZ_ANALYSIS_OP_TYPE_CJMP: + case RZ_ANALYSIS_OP_TYPE_RCJMP: + case RZ_ANALYSIS_OP_TYPE_MJMP: + case RZ_ANALYSIS_OP_TYPE_MCJMP: + case RZ_ANALYSIS_OP_TYPE_UCJMP: + case RZ_ANALYSIS_OP_TYPE_CALL: + case RZ_ANALYSIS_OP_TYPE_UCALL: case RZ_ANALYSIS_OP_TYPE_RCALL: case RZ_ANALYSIS_OP_TYPE_ICALL: case RZ_ANALYSIS_OP_TYPE_IRCALL: @@ -1009,9 +1018,11 @@ RZ_API bool rz_analysis_op_changes_control_flow(RZ_NONNULL const RzAnalysisOp *o case RZ_ANALYSIS_OP_TYPE_CRET: case RZ_ANALYSIS_OP_TYPE_ILL: case RZ_ANALYSIS_OP_TYPE_UNK: + case RZ_ANALYSIS_OP_TYPE_TRAP: case RZ_ANALYSIS_OP_TYPE_SWI: case RZ_ANALYSIS_OP_TYPE_CSWI: - case RZ_ANALYSIS_OP_TYPE_TRAP: + case RZ_ANALYSIS_OP_TYPE_LEAVE: + case RZ_ANALYSIS_OP_TYPE_SWITCH: return true; default: return false; diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index c30a8c95acc..b21365ea944 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -33,7 +33,6 @@ RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NO RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void); RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *a); -RZ_API bool rz_inquiry_op_type_is_eob(_RzAnalysisOpType type); RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr); RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzList /**/ *allowed_io_maps); diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c index 438c409fd11..c7a518718dd 100644 --- a/librz/inquiry/inquiry_helpers.c +++ b/librz/inquiry/inquiry_helpers.c @@ -12,40 +12,6 @@ #include #include -RZ_API bool rz_inquiry_op_type_is_eob(_RzAnalysisOpType type) { - switch (type) { - default: - return false; - case RZ_ANALYSIS_OP_TYPE_JMP: - case RZ_ANALYSIS_OP_TYPE_UJMP: - case RZ_ANALYSIS_OP_TYPE_RJMP: - case RZ_ANALYSIS_OP_TYPE_IJMP: - case RZ_ANALYSIS_OP_TYPE_IRJMP: - case RZ_ANALYSIS_OP_TYPE_CJMP: - case RZ_ANALYSIS_OP_TYPE_RCJMP: - case RZ_ANALYSIS_OP_TYPE_MJMP: - case RZ_ANALYSIS_OP_TYPE_MCJMP: - case RZ_ANALYSIS_OP_TYPE_UCJMP: - case RZ_ANALYSIS_OP_TYPE_CALL: - case RZ_ANALYSIS_OP_TYPE_UCALL: - case RZ_ANALYSIS_OP_TYPE_RCALL: - case RZ_ANALYSIS_OP_TYPE_ICALL: - case RZ_ANALYSIS_OP_TYPE_IRCALL: - case RZ_ANALYSIS_OP_TYPE_CCALL: - case RZ_ANALYSIS_OP_TYPE_UCCALL: - case RZ_ANALYSIS_OP_TYPE_RET: - case RZ_ANALYSIS_OP_TYPE_CRET: - case RZ_ANALYSIS_OP_TYPE_ILL: - case RZ_ANALYSIS_OP_TYPE_UNK: - case RZ_ANALYSIS_OP_TYPE_TRAP: - case RZ_ANALYSIS_OP_TYPE_SWI: - case RZ_ANALYSIS_OP_TYPE_CSWI: - case RZ_ANALYSIS_OP_TYPE_LEAVE: - case RZ_ANALYSIS_OP_TYPE_SWITCH: - return true; - } -} - RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr) { rz_return_val_if_fail(analysis && analysis->cur && io, NULL); RzInterpreterILBB *il_bb = NULL; From 10f4cdf76bb3f8534403530576413dd931f68bfa Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 16 Dec 2025 16:53:35 +0100 Subject: [PATCH 126/334] Check if set PC is concrete --- librz/inquiry/interpreter/p/interpreter_prototype.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index aced4e3905b..85aa97bf229 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -22,7 +22,7 @@ static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, if (!interpreter_prototype_eval_effect(state, pkt->effect, pkt->insn_pkt_size, yield_queues, io_request, io_result, plugin_data)) { return false; } - if (pc == rz_bv_to_ut64(AD(state->pc->abstr_data)->bv)) { + if (pc == rz_bv_to_ut64(AD(state->pc->abstr_data)->bv) && AD(state->pc->abstr_data)->is_concrete) { set_pc(state, pc + pkt->insn_pkt_size, plugin_data); } } From 25a2feaf54668ba930abbf4ab7a8630d29f4c82b Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 16 Dec 2025 17:20:17 +0100 Subject: [PATCH 127/334] Use RZ_LOG_DEBUG instead of printf --- librz/inquiry/inquiry.c | 22 +++++++++---------- librz/inquiry/interpreter/interpreter.c | 4 ++-- .../interpreter/p/interpreter_prototype.c | 2 +- librz/inquiry/interpreter/prototype/eval.c | 6 ++--- .../interpreter/prototype/eval_effect.c | 6 ++--- .../inquiry/interpreter/prototype/eval_pure.c | 2 +- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 1e5f7cc0eef..a7f8d6b824b 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -250,7 +250,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { } // Dispatch prototype interpreter into a thread. - printf("INQUIRY: Start main interpretation thread.\n"); + RZ_LOG_DEBUG("INQUIRY: Start main interpretation thread.\n"); interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); // From here on, the code plays the role of the cache, IO handler, @@ -260,10 +260,10 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // - Receiving and adding the found xrefs to RzAnalysis. // In the final implementation each of those roles would be split into // two or more separated modules running in parallel. - printf("INQUIRY: Enforce enabling IO cache.\n"); + RZ_LOG_DEBUG("INQUIRY: Enforce enabling IO cache.\n"); const char *io_cache_opt = rz_config_get(core->config, "io.cache"); rz_config_set(core->config, "io.cache", "true"); - printf("INQUIRY: Start IL providing loop.\n"); + RZ_LOG_DEBUG("INQUIRY: Start IL providing loop.\n"); // Poor man's shared memory. RzInterpreterIOResult _io_res = { 0 }; @@ -281,7 +281,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { { ut64 *addr = rz_th_queue_pop(addr_queue, false); if (addr) { - printf("INQUIRY: Received IL request: 0x%" PFMT64x "\n", (*addr)); + RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x "\n", (*addr)); RzInterpreterILBB *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); if (!bb) { RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", *addr); @@ -290,7 +290,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { rz_atomic_bool_set(is_running, false); continue; } - printf("INQUIRY: Send IL result: %p.\n", bb); + RZ_LOG_DEBUG("INQUIRY: Send IL result: %p.\n", bb); rz_pvector_push(il_cache, bb); // TODO: Free unused if too big. rz_th_queue_push(il_queue, bb, true); @@ -308,7 +308,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { continue; } - printf("INQUIRY: Received IO %s request: 0x%" PFMT64x "\n", + RZ_LOG_DEBUG("INQUIRY: Received IO %s request: 0x%" PFMT64x "\n", io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", io_req->addr); if (io_req->type == RZ_INTERPRETER_IO_READ) { @@ -329,7 +329,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { } else { io_res->req_ok = rz_io_write_at(core->io, io_req->addr, io_req->data, io_req->n_bytes); } - printf("INQUIRY: Sent IO %s result. Success = %s.\n", + RZ_LOG_DEBUG("INQUIRY: Sent IO %s result. Success = %s.\n", io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", io_res->req_ok ? "true" : "false"); rz_th_queue_push(io_result_q, io_res, true); @@ -345,20 +345,20 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { } // TODO: Currently we can't classify calls as such. rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, xref->type); - printf("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)); + RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)); } } - printf("INQUIRY: Done\n"); + RZ_LOG_DEBUG("INQUIRY: Done\n"); rz_config_set(core->config, "io.cache", io_cache_opt); // Wait for thread to finish before cleaning. error_free: - printf("INQUIRY: Close queues\n"); + RZ_LOG_DEBUG("INQUIRY: Close queues\n"); rz_th_cond_signal_all(rz_th_queue_get_cond(iset->il_queue)); rz_th_cond_signal_all(rz_th_queue_get_cond(iset->io_result)); if (interpr_th){ - printf("INQUIRY: Wait for join\n"); + RZ_LOG_DEBUG("INQUIRY: Wait for join\n"); rz_th_wait(interpr_th); return_code = rz_th_get_retv(interpr_th); rz_th_free(interpr_th); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index a30652d4587..de5eca9bef8 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -237,7 +237,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { entry_assert_error); bool success = false; - printf("INTERPRETER Main: Hello.\n"); + RZ_LOG_DEBUG("INTERPRETER Main: Hello.\n"); RzInterpreterPlugin *plugin = iset->plugin; void **priv_ptr = NULL; @@ -294,7 +294,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // The input state was (almost always) manipulated by eval(). Rename to clarify. out_state = in_state; out_hash = plugin->hash_state(out_state, plugin_data); - printf("in_hash = 0x%llx, out_hash = 0x%llx\n", in_hash, out_hash); + RZ_LOG_DEBUG("in_hash = 0x%llx, out_hash = 0x%llx\n", in_hash, out_hash); // Add out_state hash to the reachable states and // set a flag if it was a new state. diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 85aa97bf229..cbe6db9ea5c 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -17,7 +17,7 @@ static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, void **it; rz_pvector_foreach(il_op, it) { ut64 pc = rz_bv_to_ut64(AD(state->pc->abstr_data)->bv); - printf("Eval PC = 0x%" PFMT64x "\n", pc); + RZ_LOG_DEBUG("Eval PC = 0x%" PFMT64x "\n", pc); RzInterpreterInsnPkt *pkt = *it; if (!interpreter_prototype_eval_effect(state, pkt->effect, pkt->insn_pkt_size, yield_queues, io_request, io_result, plugin_data)) { return false; diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 626e5c6032f..54ace5b3e22 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -134,7 +134,7 @@ bool store_abstr_data( buf = buf_stack; } char *bytes = rz_bv_as_hex_string(src->bv, true); - printf("Prototype: STORE @ 0x%" PFMT64x " : %s\n", io_req->addr, bytes); + RZ_LOG_DEBUG("Prototype: STORE @ 0x%" PFMT64x " : %s\n", io_req->addr, bytes); free(bytes); rz_bv_set_to_bytes_ble(src->bv, buf, state->il_config->big_endian); io_req->type = RZ_INTERPRETER_IO_WRITE; @@ -187,7 +187,7 @@ bool load_abstr_data( rz_bv_cast_inplace(out->bv, size, 0); rz_bv_set_from_bytes_ble(out->bv, io_res->read.data, 0, io_res->read.n_bytes, state->il_config->big_endian); char *bytes = rz_bv_as_hex_string(out->bv, true); - printf("Prototype: READ @ 0x%" PFMT64x " : %s\n", io_req->addr, bytes); + RZ_LOG_DEBUG("Prototype: READ @ 0x%" PFMT64x " : %s\n", io_req->addr, bytes); free(bytes); return true; } @@ -196,7 +196,7 @@ bool set_pc(RzInterpreterAbstrState *state, ut64 pc, void *plugin_data) { rz_return_val_if_fail(state, false); AD(state->pc->abstr_data)->is_concrete = true; - printf("Prototype: set_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x "(Concrete)\n", + RZ_LOG_DEBUG("Prototype: set_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x "(Concrete)\n", rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), pc); return rz_bv_set_from_ut64(AD(state->pc->abstr_data)->bv, pc); diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 94de86ab771..b4dfe2c444d 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -32,7 +32,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, if (!rz_bv_add_inplace(pc->bv, inc.bv, NULL)) { goto error; } - printf("Prototype: NOP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", + RZ_LOG_DEBUG("Prototype: NOP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", old_pc, rz_bv_to_ut64(pc->bv), pc->is_concrete ? "Concrete" : "Abstract"); @@ -63,9 +63,9 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, goto error; } if (!eval_out.is_concrete) { - printf("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", rz_bv_to_ut64(AD(state->pc->abstr_data)->bv)); + RZ_LOG_DEBUG("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", rz_bv_to_ut64(AD(state->pc->abstr_data)->bv)); } - printf("Prototype: JMP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", + RZ_LOG_DEBUG("Prototype: JMP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), rz_bv_to_ut64(eval_out.bv), eval_out.is_concrete ? "Concrete" : "Abstract"); diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 49bd287dddb..7375eb6bd78 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -491,7 +491,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_FPOWN: case RZ_IL_OP_FCOMPOUND: case RZ_IL_OP_FEXCEPT: - printf("\nUnhandled op\n"); + RZ_LOG_DEBUG("\nUnhandled op\n"); // Not implemented. goto map_to_bottom; } From 1705f7da1f6c833a255cacc990c6c9d24e8dd984 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 16 Dec 2025 17:20:33 +0100 Subject: [PATCH 128/334] Add xref test --- test/db/inquiry/interpreter/xrefs | 103 ++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 test/db/inquiry/interpreter/xrefs diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs new file mode 100644 index 00000000000..744f035928f --- /dev/null +++ b/test/db/inquiry/interpreter/xrefs @@ -0,0 +1,103 @@ +NAME=Indirect call x86 +FILE=bins/rzil_analysis/interpreter/prototype/x86_icall_malloc +CMDS=< 0x00401a8f cmp qword [rbp-0x18], 0x03 + ,==< 0x00401a94 jnb 0x401ac4 + |: 0x00401a96 mov rcx, qword [rbp-0x18] + |: 0x00401a9a mov al, 0x00 + |: 0x00401a9c call qword [rcx*8+obj.fcn_arr] ; 0x4a80d0 + |: ; DATA XREF from main @ +0x2c + |: 0x00401aa3 mov qword [rbp-0x20], rax ; [0x4a9b50:8]=0 + |: ; obj.z + |: 0x00401aa7 mov rax, qword [rbp-0x20] + |: 0x00401aab movzx eax, byte [rax] ; [0x4a9b50:1]=0 + |: 0x00401aae add rax, qword [rbp-0x10] + |: 0x00401ab2 mov qword [rbp-0x10], rax + |: 0x00401ab6 mov rax, qword [rbp-0x18] + |: 0x00401aba add rax, 0x01 + |: 0x00401abe mov qword [rbp-0x18], rax + |`=< 0x00401ac2 jmp 0x401a8f + | ; CODE XREF from main @ +0x24 + `--> 0x00401ac4 mov rax, qword [rbp-0x10] + 0x00401ac8 add rsp, 0x20 + 0x00401acc pop rbp + 0x00401acd ret + 0x00401ace nop + ; CODE XREF from main @ +0x2c + ;-- function_0: + 0x00401ad0 push rbp + 0x00401ad1 mov rbp, rsp + 0x00401ad4 call sym.some_ptr + ; DATA XREF from sym.function_0 @ +0x4 + ; CODE XREF from sym.some_ptr @ +0xf + 0x00401ad9 pop rbp + 0x00401ada ret + 0x00401adb nop dword [rax+rax*1], eax + ; CODE XREF from sym.function_0 @ +0x4 + ; CODE XREF from sym.function_1 @ +0x4 + ; CODE XREF from sym.function_2 @ +0x4 + ;-- some_ptr: + 0x00401ae0 push rbp + 0x00401ae1 mov rbp, rsp + 0x00401ae4 mov rax, obj.z ; 0x4a9b50 + 0x00401aee pop rbp + 0x00401aef ret + ; CODE XREF from main @ +0x2c + ;-- function_1: + 0x00401af0 push rbp + 0x00401af1 mov rbp, rsp + 0x00401af4 call sym.some_ptr + ; CODE XREF from sym.some_ptr @ +0xf + ; DATA XREF from sym.function_1 @ +0x4 + 0x00401af9 pop rbp + 0x00401afa ret + 0x00401afb nop dword [rax+rax*1], eax + ; CODE XREF from main @ +0x2c + ;-- function_2: + 0x00401b00 push rbp + 0x00401b01 mov rbp, rsp + 0x00401b04 call sym.some_ptr + ; CODE XREF from sym.some_ptr @ +0xf + ; DATA XREF from sym.function_2 @ +0x4 + 0x00401b09 pop rbp + 0x00401b0a ret + main+36 0x401a94 -> CODE -> 0x401ac4 main+84 + main+44 0x401a9c -> DATA -> 0x401aa3 main+51 + main+44 0x401a9c -> CODE -> 0x401ad0 sym.function_0 + main+44 0x401a9c -> CODE -> 0x401af0 sym.function_1 + main+44 0x401a9c -> CODE -> 0x401b00 sym.function_2 + main+44 0x401a9c -> DATA -> 0x4a80d0 obj.fcn_arr + main+44 0x401a9c -> DATA -> 0x4a80d8 obj.fcn_arr+8 + main+44 0x401a9c -> DATA -> 0x4a80e0 obj.fcn_arr+16 + main+51 0x401aa3 -> DATA -> 0x4a9b50 obj.z + main+59 0x401aab -> DATA -> 0x4a9b50 obj.z + main+82 0x401ac2 -> CODE -> 0x401a8f main+31 + sym.function_0+4 0x401ad4 -> DATA -> 0x401ad9 sym.function_0+9 + sym.function_0+4 0x401ad4 -> CODE -> 0x401ae0 sym.some_ptr + sym.some_ptr+15 0x401aef -> CODE -> 0x401ad9 sym.function_0+9 + sym.some_ptr+15 0x401aef -> CODE -> 0x401af9 sym.function_1+9 + sym.some_ptr+15 0x401aef -> CODE -> 0x401b09 sym.function_2+9 + sym.function_1+4 0x401af4 -> CODE -> 0x401ae0 sym.some_ptr + sym.function_1+4 0x401af4 -> DATA -> 0x401af9 sym.function_1+9 + sym.function_2+4 0x401b04 -> CODE -> 0x401ae0 sym.some_ptr + sym.function_2+4 0x401b04 -> DATA -> 0x401b09 sym.function_2+9 +EOF +EXPECT_ERR=< Date: Wed, 17 Dec 2025 18:31:58 +0100 Subject: [PATCH 129/334] Use latest queue modernizations. --- librz/inquiry/inquiry.c | 88 ++++++++++++---------- librz/inquiry/interpreter/interpreter.c | 11 ++- librz/inquiry/interpreter/prototype/eval.c | 15 ++-- 3 files changed, 62 insertions(+), 52 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index a7f8d6b824b..6ca887e994f 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -279,16 +279,20 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // This block mimics the IL cache. { - ut64 *addr = rz_th_queue_pop(addr_queue, false); + ut64 *addr = NULL; + if (!rz_th_queue_pop(addr_queue, false, (void **)&addr)) { + rz_warn_if_reached(); + break; + } if (addr) { RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x "\n", (*addr)); RzInterpreterILBB *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); if (!bb) { RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", *addr); // Signal interpreter the lifting failed. - rz_th_cond_signal_all(rz_th_queue_get_cond(iset->il_queue)); rz_atomic_bool_set(is_running, false); - continue; + rz_th_queue_close(iset->il_queue); + break; } RZ_LOG_DEBUG("INQUIRY: Send IL result: %p.\n", bb); rz_pvector_push(il_cache, bb); @@ -303,49 +307,54 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // (one for each interpreter instance). // Because this is not yet implemented, there is only one interpreter thread for now. { - RzInterpreterIORequest *io_req = rz_th_queue_pop(io_request_q, false); - if (!io_req) { - continue; + RzInterpreterIORequest *io_req = NULL; + if (!rz_th_queue_pop(io_request_q, false, (void **)&io_req)) { + rz_atomic_bool_set(is_running, false); + break; } - - RZ_LOG_DEBUG("INQUIRY: Received IO %s request: 0x%" PFMT64x "\n", - io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", - io_req->addr); - if (io_req->type == RZ_INTERPRETER_IO_READ) { - if (io_req->n_bytes > MAX_IO_DATA_READ) { - RZ_LOG_ERROR("Plugin tried to read more than 0x%" PFMT32x " bytes.\n" - "This is more than configured. It will only read MAX_IO_DATA_READ bytes.\nPlease set MAX_IO_DATA_READ to a larger value and rebuild Rizin.\n", - MAX_IO_DATA_READ); + if (io_req) { + RZ_LOG_DEBUG("INQUIRY: Received IO %s request: 0x%" PFMT64x "\n", + io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", + io_req->addr); + if (io_req->type == RZ_INTERPRETER_IO_READ) { + if (io_req->n_bytes > MAX_IO_DATA_READ) { + RZ_LOG_ERROR("Plugin tried to read more than 0x%" PFMT32x " bytes.\n" + "This is more than configured. It will only read MAX_IO_DATA_READ bytes.\nPlease set MAX_IO_DATA_READ to a larger value and rebuild Rizin.\n", + MAX_IO_DATA_READ); + } + // Cast to constant ut8* here. The constant is only there so interpreter plugins don't free it by accident. + (void)rz_io_read_at_mapped(core->io, io_req->addr, (ut8 *)io_res->read.data, io_req->n_bytes > MAX_IO_DATA_READ ? MAX_IO_DATA_READ : io_req->n_bytes); + // The IO API doesn't have a function which can: + // - read beyond mapped regions and from cached data. + // - return the total number of bytes it read. + // + // So for this prototype we just have to assume it always succeeds :( + io_res->req_ok = true; + io_res->read.n_bytes = io_req->n_bytes; + } else { + io_res->req_ok = rz_io_write_at(core->io, io_req->addr, io_req->data, io_req->n_bytes); } - // Cast to constant ut8* here. The constant is only there so interpreter plugins don't free it by accident. - (void)rz_io_read_at_mapped(core->io, io_req->addr, (ut8 *)io_res->read.data, io_req->n_bytes > MAX_IO_DATA_READ ? MAX_IO_DATA_READ : io_req->n_bytes); - // The IO API doesn't have a function which can: - // - read beyond mapped regions and from cached data. - // - return the total number of bytes it read. - // - // So for this prototype we just have to assume it always succeeds :( - io_res->req_ok = true; - io_res->read.n_bytes = io_req->n_bytes; - } else { - io_res->req_ok = rz_io_write_at(core->io, io_req->addr, io_req->data, io_req->n_bytes); + RZ_LOG_DEBUG("INQUIRY: Sent IO %s result. Success = %s.\n", + io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", + io_res->req_ok ? "true" : "false"); + rz_th_queue_push(io_result_q, io_res, true); } - RZ_LOG_DEBUG("INQUIRY: Sent IO %s result. Success = %s.\n", - io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", - io_res->req_ok ? "true" : "false"); - rz_th_queue_push(io_result_q, io_res, true); } // This part plays the role of a yield consumer. // In our prototype it inly receives xrefs and stores them in RzAnalysis. { RzInterpreterYieldQueue *q = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); - RzAnalysisXRef *xref = rz_th_queue_pop(q->yield_queue, false); - if (!xref) { - continue; + RzAnalysisXRef *xref = NULL; + if (!rz_th_queue_pop(q->yield_queue, false, (void **)&xref)) { + rz_atomic_bool_set(is_running, false); + break; + } + if (xref) { + // TODO: Currently we can't classify calls as such. + rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, xref->type); + RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)); } - // TODO: Currently we can't classify calls as such. - rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, xref->type); - RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)); } } RZ_LOG_DEBUG("INQUIRY: Done\n"); @@ -355,9 +364,10 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // Wait for thread to finish before cleaning. error_free: RZ_LOG_DEBUG("INQUIRY: Close queues\n"); - rz_th_cond_signal_all(rz_th_queue_get_cond(iset->il_queue)); - rz_th_cond_signal_all(rz_th_queue_get_cond(iset->io_result)); - if (interpr_th){ + rz_th_queue_close(il_queue); + rz_th_queue_close(io_request_q); + rz_th_queue_close(io_result_q); + if (interpr_th) { RZ_LOG_DEBUG("INQUIRY: Wait for join\n"); rz_th_wait(interpr_th); return_code = rz_th_get_retv(interpr_th); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index de5eca9bef8..f83023af8a3 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -268,7 +268,10 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { RzInterpreterAbstrState *out_state = NULL; ut64 out_hash = 0; - const RzInterpreterILBB *il_bb = rz_th_queue_pop(iset->il_queue, false); + const RzInterpreterILBB *il_bb = NULL; + if (!rz_th_queue_pop_wait(iset->il_queue, false, (void **)&il_bb) || !il_bb) { + goto pre_loop_error; + } // TODO: Add support for multiple entry points by spawning an interpreter for each of them. // For now let's just drop them. RzList *additional_entries = rz_th_queue_pop_all(iset->il_queue); @@ -336,8 +339,10 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { SuccessorState next = { 0 }; rz_vector_pop_front(succ_states, &next); in_hash = next.in_state_hash; - il_bb = rz_th_queue_wait_pop(iset->il_queue, false); - if (!il_bb || !plugin->set_pc(in_state, next.addr, plugin_data)) { + if (!rz_th_queue_pop_wait(iset->il_queue, false, (void **)&il_bb) || !il_bb) { + goto in_loop_error; + } + if (!plugin->set_pc(in_state, next.addr, plugin_data)) { // Some error occurred lifting this basic block. Or updating the PC. // Abort execution. goto in_loop_error; diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 54ace5b3e22..ed06d089c56 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -143,16 +143,12 @@ bool store_abstr_data( rz_th_queue_push(io_request, io_req, true); // Wait for write being done. - RzInterpreterIOResult *io_res = rz_th_queue_wait_pop(io_result, false); + RzInterpreterIOResult *io_res = NULL; + rz_th_queue_pop_wait(io_result, false, (void **)&io_res); if (io_req->n_bytes > BV_STACK_MAX_SIZE) { free(buf); } - - if (!io_res) { - // Abort of interpretation. - return false; - } - return io_res->req_ok; + return io_res ? io_res->req_ok : false; } bool load_abstr_data( @@ -169,9 +165,8 @@ bool load_abstr_data( io_req->n_bytes = size; rz_th_queue_push(io_request, io_req, true); // Wait for load being done. - RzInterpreterIOResult *io_res = rz_th_queue_wait_pop(io_result, false); - if (!io_res) { - // Abort of interpretation. + RzInterpreterIOResult *io_res = NULL; + if (!rz_th_queue_pop_wait(io_result, false, (void **)&io_res) || !io_res) { return false; } if (!io_res->req_ok) { From f1220a5103d10a56220c09b53b4f702015602b8a Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 17 Dec 2025 18:32:48 +0100 Subject: [PATCH 130/334] Update test with return xrefs from functions. --- test/db/inquiry/interpreter/xrefs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs index 744f035928f..98af7404daf 100644 --- a/test/db/inquiry/interpreter/xrefs +++ b/test/db/inquiry/interpreter/xrefs @@ -21,6 +21,9 @@ EXPECT=< CODE -> 0x401a8f main+31 sym.function_0+4 0x401ad4 -> DATA -> 0x401ad9 sym.function_0+9 sym.function_0+4 0x401ad4 -> CODE -> 0x401ae0 sym.some_ptr + sym.function_0+10 0x401ada -> CODE -> 0x401aa3 main+51 sym.some_ptr+15 0x401aef -> CODE -> 0x401ad9 sym.function_0+9 sym.some_ptr+15 0x401aef -> CODE -> 0x401af9 sym.function_1+9 sym.some_ptr+15 0x401aef -> CODE -> 0x401b09 sym.function_2+9 sym.function_1+4 0x401af4 -> CODE -> 0x401ae0 sym.some_ptr sym.function_1+4 0x401af4 -> DATA -> 0x401af9 sym.function_1+9 + sym.function_1+10 0x401afa -> CODE -> 0x401aa3 main+51 sym.function_2+4 0x401b04 -> CODE -> 0x401ae0 sym.some_ptr sym.function_2+4 0x401b04 -> DATA -> 0x401b09 sym.function_2+9 + sym.function_2+10 0x401b0a -> CODE -> 0x401aa3 main+51 EOF EXPECT_ERR=< Date: Wed, 17 Dec 2025 19:02:27 +0100 Subject: [PATCH 131/334] Use rz_io_nread_at() to allow reading at map boundaries without failure. --- librz/inquiry/inquiry_helpers.c | 8 ++++++-- test/db/inquiry/interpreter/xrefs | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c index c7a518718dd..96af12306f8 100644 --- a/librz/inquiry/inquiry_helpers.c +++ b/librz/inquiry/inquiry_helpers.c @@ -29,8 +29,12 @@ RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana } bool changes_cf = true; do { - if (!rz_io_read_at_mapped(io, addr, buf, max_read_size)) { - RZ_LOG_WARN("inquiry: Failed to read memory for IL basic block generation.\n"); + // Don't use rz_io_read_at_mapped() here. + // It fails if it reads beyond a mapped memory region. + // Although this is expected here. rz_io_nread_at() on the other hand just + // reads less bytes. + if (rz_io_nread_at(io, addr, buf, max_read_size) == 0) { + RZ_LOG_WARN("inquiry: Failed to read memory for IL basic block generation at 0x%" PFMT64x " size: %u.\n", addr, max_read_size); goto fail; } if (rz_analysis_op(analysis, &op, addr, buf, max_read_size, RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_INSN_PKT) <= 0) { diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs index 98af7404daf..013d2032153 100644 --- a/test/db/inquiry/interpreter/xrefs +++ b/test/db/inquiry/interpreter/xrefs @@ -103,7 +103,7 @@ EXPECT=< CODE -> 0x401aa3 main+51 EOF EXPECT_ERR=< Date: Wed, 17 Dec 2025 19:06:24 +0100 Subject: [PATCH 132/334] Rename rzil_analysis -> inquiry --- test/db/inquiry/interpreter/xrefs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs index 013d2032153..048c35f25ec 100644 --- a/test/db/inquiry/interpreter/xrefs +++ b/test/db/inquiry/interpreter/xrefs @@ -1,5 +1,5 @@ NAME=Indirect call x86 -FILE=bins/rzil_analysis/interpreter/prototype/x86_icall_malloc +FILE=bins/inquiry/interpreter/prototype/x86_icall_malloc CMDS=< Date: Wed, 17 Dec 2025 19:22:16 +0100 Subject: [PATCH 133/334] Add work-around for Hexagon, so 'next packet' jumps are not reported as xrefs. --- librz/include/rz_inquiry/rz_interpreter.h | 2 ++ librz/inquiry/inquiry.c | 1 + librz/inquiry/interpreter/interpreter.c | 2 ++ librz/inquiry/interpreter/prototype/eval.c | 12 ++++++++++++ 4 files changed, 17 insertions(+) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 851ca701887..9c6106b9da9 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -60,6 +60,7 @@ typedef struct { HtUP /**/ *lets; ///< Let variables. Indexed by DJB2 hash of the let name. RzInterpreterAbstrVal *pc; ///< In our RzIL implementation the PC is not part of the register file. RzAnalysisILConfig *il_config; ///< The IL configuration of the RzArch plugin. + const char *arch_name; ///< Name of architecture. Used by work-arounds until we have RzArch. void *ext; ///< Optional state extensions. Managed by individual interpreters. } RzInterpreterAbstrState; @@ -210,6 +211,7 @@ RZ_API void rz_interpreter_insn_pkt_free(RZ_NULLABLE RZ_OWN RzInterpreterInsnPkt RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldQueue *yield_queue); RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( + const char *arch_name, RzInterpreterAbstraction kinds, RZ_OWN RZ_NONNULL RzAnalysisILConfig *il_config, RZ_NULLABLE const RzPVector *reg_names); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 6ca887e994f..96c2fe1574f 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -225,6 +225,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { RzAnalysisILConfig *config = core->analysis->cur->il_config(core->analysis); RzPVector *reg_names = get_reg_names(core->analysis); abstr_state = rz_interpreter_abstr_state_new( + core->analysis->cur->arch, RZ_INTERPRETER_ABSTRACTION_CONST, config, reg_names); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index f83023af8a3..309a191a79d 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -75,6 +75,7 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre * The register name list should always be given if the architecture has some. */ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( + const char *arch_name, RzInterpreterAbstraction kinds, RZ_OWN RZ_NONNULL RzAnalysisILConfig *il_config, RZ_NULLABLE const RzPVector *reg_names) { @@ -83,6 +84,7 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( if (!state) { return NULL; } + state->arch_name = arch_name; state->kinds = kinds; if (!reg_names) { return state; diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index ed06d089c56..d253c8e24dc 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -19,6 +19,18 @@ bool report_xref_yield(RzInterpreterAbstrState *state, HtUP /*arch_name, "hexagon") && + from + 4 == rz_bv_to_ut64(to->bv)) { + // Ugly work around. + // Because we don't have RzArch yet the Hexagon plugin adds a JUMP at the + // end of each and every instruction packet. + // This is necessary because the RzIL VM would otherwise just add 4 to the PC, + // which is too little for a packet with 2+ instructions. + // We don't want to report the code references to the next instruction + // packet. So skip them here. + return true; + } ProtoInterprSharedObjects *sobj = state->ext; ut64 to_addr = rz_bv_to_ut64(to->bv); From f59f953e0f36a7bd4481819793bc38e358035e03 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 18 Dec 2025 16:55:32 +0100 Subject: [PATCH 134/334] Revert to only blocking variant of queue_pop --- librz/inquiry/inquiry.c | 36 +++++++++++----------- librz/inquiry/interpreter/interpreter.c | 4 +-- librz/inquiry/interpreter/prototype/eval.c | 4 +-- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 96c2fe1574f..3aee56f9d5a 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -280,12 +280,12 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // This block mimics the IL cache. { - ut64 *addr = NULL; - if (!rz_th_queue_pop(addr_queue, false, (void **)&addr)) { - rz_warn_if_reached(); - break; - } - if (addr) { + if (!rz_th_queue_is_empty(addr_queue)) { + ut64 *addr = NULL; + if (!rz_th_queue_pop(addr_queue, false, (void **)&addr) || !addr) { + rz_warn_if_reached(); + break; + } RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x "\n", (*addr)); RzInterpreterILBB *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); if (!bb) { @@ -308,12 +308,12 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // (one for each interpreter instance). // Because this is not yet implemented, there is only one interpreter thread for now. { - RzInterpreterIORequest *io_req = NULL; - if (!rz_th_queue_pop(io_request_q, false, (void **)&io_req)) { - rz_atomic_bool_set(is_running, false); - break; - } - if (io_req) { + if (!rz_th_queue_is_empty(io_request_q)) { + RzInterpreterIORequest *io_req = NULL; + if (!rz_th_queue_pop(io_request_q, false, (void **)&io_req) || !io_req) { + rz_atomic_bool_set(is_running, false); + break; + } RZ_LOG_DEBUG("INQUIRY: Received IO %s request: 0x%" PFMT64x "\n", io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", io_req->addr); @@ -346,12 +346,12 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // In our prototype it inly receives xrefs and stores them in RzAnalysis. { RzInterpreterYieldQueue *q = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); - RzAnalysisXRef *xref = NULL; - if (!rz_th_queue_pop(q->yield_queue, false, (void **)&xref)) { - rz_atomic_bool_set(is_running, false); - break; - } - if (xref) { + if (!rz_th_queue_is_empty(q->yield_queue)) { + RzAnalysisXRef *xref = NULL; + if (!rz_th_queue_pop(q->yield_queue, false, (void **)&xref) || !xref) { + rz_atomic_bool_set(is_running, false); + break; + } // TODO: Currently we can't classify calls as such. rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, xref->type); RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 309a191a79d..fe4eedd061f 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -271,7 +271,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { ut64 out_hash = 0; const RzInterpreterILBB *il_bb = NULL; - if (!rz_th_queue_pop_wait(iset->il_queue, false, (void **)&il_bb) || !il_bb) { + if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { goto pre_loop_error; } // TODO: Add support for multiple entry points by spawning an interpreter for each of them. @@ -341,7 +341,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { SuccessorState next = { 0 }; rz_vector_pop_front(succ_states, &next); in_hash = next.in_state_hash; - if (!rz_th_queue_pop_wait(iset->il_queue, false, (void **)&il_bb) || !il_bb) { + if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { goto in_loop_error; } if (!plugin->set_pc(in_state, next.addr, plugin_data)) { diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index d253c8e24dc..f7f13ccfe3c 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -156,7 +156,7 @@ bool store_abstr_data( rz_th_queue_push(io_request, io_req, true); // Wait for write being done. RzInterpreterIOResult *io_res = NULL; - rz_th_queue_pop_wait(io_result, false, (void **)&io_res); + rz_th_queue_pop(io_result, false, (void **)&io_res); if (io_req->n_bytes > BV_STACK_MAX_SIZE) { free(buf); } @@ -178,7 +178,7 @@ bool load_abstr_data( rz_th_queue_push(io_request, io_req, true); // Wait for load being done. RzInterpreterIOResult *io_res = NULL; - if (!rz_th_queue_pop_wait(io_result, false, (void **)&io_res) || !io_res) { + if (!rz_th_queue_pop(io_result, false, (void **)&io_res) || !io_res) { return false; } if (!io_res->req_ok) { From a62c3dc1ebc19073bfab486e788668efd37eca49 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 18 Dec 2025 20:39:57 +0100 Subject: [PATCH 135/334] Fix another edge case of Hexagon IL lifting. If a packet was pushed out of the cache, it can be decoded as invalid, although it is valid. Then it is marked as such according to some heuristic. But the get_pkt_op parameter was not set in this case, which further down let the plugin return an EMPTY() effect. --- librz/arch/p/analysis/analysis_hexagon.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/librz/arch/p/analysis/analysis_hexagon.c b/librz/arch/p/analysis/analysis_hexagon.c index 72f9d85cc04..1f7e50527f2 100644 --- a/librz/arch/p/analysis/analysis_hexagon.c +++ b/librz/arch/p/analysis/analysis_hexagon.c @@ -31,14 +31,13 @@ RZ_API int hexagon_v6_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, cons HexPkt *p = hex_get_pkt(rev.state, addr); if (p) { rev.pkt_fully_decoded = p->is_valid; + if (mask & RZ_ANALYSIS_OP_MASK_INSN_PKT) { + op->size = rz_list_length(p->bin) * HEX_INSN_SIZE; + } } if (mask & RZ_ANALYSIS_OP_MASK_IL) { op->il_op = hex_get_il_op(addr, rev.pkt_fully_decoded, rev.state); } - HexPkt *p = hex_get_pkt(rev.state, addr); - if (p && (mask & RZ_ANALYSIS_OP_MASK_INSN_PKT)) { - op->size = rz_list_length(p->bin) * HEX_INSN_SIZE; - } return op->size; } From bb3373116caf7397b769cebd79bfffdb8a571101 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 18 Dec 2025 21:03:22 +0100 Subject: [PATCH 136/334] Several fixes for interpretation. --- librz/inquiry/inquiry.c | 4 +- librz/inquiry/interpreter/prototype/eval.c | 25 ++-- librz/inquiry/interpreter/prototype/eval.h | 1 + .../interpreter/prototype/eval_effect.c | 14 +- .../inquiry/interpreter/prototype/eval_pure.c | 2 +- test/db/inquiry/interpreter/xrefs | 140 ++++++++++++++++-- 6 files changed, 155 insertions(+), 31 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 3aee56f9d5a..216b0c17ae5 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -280,9 +280,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // This block mimics the IL cache. { - if (!rz_th_queue_is_empty(addr_queue)) { + if (!rz_th_queue_is_empty(iset->addr_queue)) { ut64 *addr = NULL; - if (!rz_th_queue_pop(addr_queue, false, (void **)&addr) || !addr) { + if (!rz_th_queue_pop(iset->addr_queue, false, (void **)&addr) || !addr) { rz_warn_if_reached(); break; } diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index f7f13ccfe3c..9cac454496f 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -9,7 +9,13 @@ #include "rz_util/rz_log.h" #include -bool report_xref_yield(RzInterpreterAbstrState *state, HtUP /**/ *yield_queues, ut64 from, const ProtoIntrprAbstrData *to, RzAnalysisXRefType type) { +bool report_xref_yield( + RzInterpreterAbstrState *state, + size_t insn_pkt_size, + HtUP /**/ *yield_queues, + ut64 from, + const ProtoIntrprAbstrData *to, + RzAnalysisXRefType type) { RzInterpreterYieldQueue *queue = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); if (!queue) { rz_warn_if_reached(); @@ -20,8 +26,8 @@ bool report_xref_yield(RzInterpreterAbstrState *state, HtUP /*arch_name, "hexagon") && - from + 4 == rz_bv_to_ut64(to->bv)) { + RZ_STR_EQ(state->arch_name, "hexagon") && + from + insn_pkt_size == rz_bv_to_ut64(to->bv)) { // Ugly work around. // Because we don't have RzArch yet the Hexagon plugin adds a JUMP at the // end of each and every instruction packet. @@ -145,13 +151,13 @@ bool store_abstr_data( } else { buf = buf_stack; } - char *bytes = rz_bv_as_hex_string(src->bv, true); - RZ_LOG_DEBUG("Prototype: STORE @ 0x%" PFMT64x " : %s\n", io_req->addr, bytes); - free(bytes); rz_bv_set_to_bytes_ble(src->bv, buf, state->il_config->big_endian); io_req->type = RZ_INTERPRETER_IO_WRITE; io_req->addr = addr; io_req->data = buf; + char *bytes = rz_bv_as_hex_string(src->bv, true); + RZ_LOG_DEBUG("Prototype: STORE @ 0x%" PFMT64x " : %s\n", io_req->addr, bytes); + free(bytes); rz_th_queue_push(io_request, io_req, true); // Wait for write being done. @@ -187,7 +193,8 @@ bool load_abstr_data( } if (io_res->read.n_bytes != size) { RZ_LOG_WARN("Prototype: Failed to read correct number of bytes. Requested: 0x%" PFMTSZx - " Received: 0x%" PFMT64x "\n", size, io_res->read.n_bytes); + " Received: 0x%" PFMT64x "\n", + size, io_res->read.n_bytes); return false; } out->is_concrete = true; @@ -204,7 +211,7 @@ bool set_pc(RzInterpreterAbstrState *state, ut64 pc, rz_return_val_if_fail(state, false); AD(state->pc->abstr_data)->is_concrete = true; RZ_LOG_DEBUG("Prototype: set_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x "(Concrete)\n", - rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), - pc); + rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), + pc); return rz_bv_set_from_ut64(AD(state->pc->abstr_data)->bv, pc); } diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index abfc20ce0c4..a8df3255d20 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -101,6 +101,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( bool report_xref_yield( RzInterpreterAbstrState *state, + size_t insn_pkt_size, HtUP /**/ *yield_queues, ut64 from, const ProtoIntrprAbstrData *to, diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index b4dfe2c444d..e98938fd080 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -74,7 +74,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, if (eval_out.is_concrete) { // NOTE: This prototype can't classify into call or jump. // Everything is just a jump for it at this point. - report_xref_yield(state, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &eval_out, RZ_ANALYSIS_XREF_TYPE_CODE); + report_xref_yield(state, insn_pkt_size, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &eval_out, RZ_ANALYSIS_XREF_TYPE_CODE); } copy_abstr_data(state->pc->abstr_data, &eval_out); break; @@ -105,7 +105,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, STACK_ABSTR_DATA_OUT(st_addr); RzILOpPure *key = effect->code == RZ_IL_OP_STORE ? effect->op.store.key : effect->op.storew.key; if (!interpreter_prototype_eval_pure(state, key, &st_addr, yield_queues, io_request, io_result, plugin_data)) { - RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); + RZ_LOG_ERROR("prototype: STORE/STOREW key failed to evaluate.\n"); rz_bv_fini(st_addr.bv); goto error; } @@ -113,19 +113,19 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, rz_bv_fini(st_addr.bv); break; } - ut64 addr = rz_bv_to_ut64(st_addr.bv); RzILOpPure *pval = effect->code == RZ_IL_OP_STORE ? effect->op.store.value : effect->op.storew.value; - if (!interpreter_prototype_eval_pure(state, pval, &st_addr, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(state, pval, &eval_out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); rz_bv_fini(st_addr.bv); goto error; } - if (!st_addr.is_concrete) { + if (!eval_out.is_concrete) { rz_bv_fini(st_addr.bv); break; } - report_xref_yield(state, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &st_addr, RZ_ANALYSIS_XREF_TYPE_DATA); - if (!store_abstr_data(state, addr, &st_addr, io_request, io_result)) { + report_xref_yield(state, insn_pkt_size, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &st_addr, RZ_ANALYSIS_XREF_TYPE_DATA); + ut64 addr = rz_bv_to_ut64(st_addr.bv); + if (!store_abstr_data(state, addr, &eval_out, io_request, io_result)) { rz_bv_fini(st_addr.bv); goto error; } diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 7375eb6bd78..d9fd5b0edbf 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -419,7 +419,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( if (!out->is_concrete) { goto map_to_bottom; } - report_xref_yield(state, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), out, RZ_ANALYSIS_XREF_TYPE_DATA); + report_xref_yield(state, 0, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), out, RZ_ANALYSIS_XREF_TYPE_DATA); ut64 addr = rz_bv_to_ut64(out->bv); size_t addr_bits = pure->code == RZ_IL_OP_LOAD ? state->il_config->mem_key_size : pure->op.loadw.n_bits; if (!load_abstr_data(state, addr, addr_bits, out, io_request, io_result)) { diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs index 048c35f25ec..6fb73579cd9 100644 --- a/test/db/inquiry/interpreter/xrefs +++ b/test/db/inquiry/interpreter/xrefs @@ -1,7 +1,6 @@ NAME=Indirect call x86 FILE=bins/inquiry/interpreter/prototype/x86_icall_malloc CMDS=< CODE -> 0x401ac4 main+84 - main+44 0x401a9c -> DATA -> 0x401aa3 main+51 main+44 0x401a9c -> CODE -> 0x401ad0 sym.function_0 main+44 0x401a9c -> CODE -> 0x401af0 sym.function_1 main+44 0x401a9c -> CODE -> 0x401b00 sym.function_2 main+44 0x401a9c -> DATA -> 0x4a80d0 obj.fcn_arr main+44 0x401a9c -> DATA -> 0x4a80d8 obj.fcn_arr+8 main+44 0x401a9c -> DATA -> 0x4a80e0 obj.fcn_arr+16 - main+51 0x401aa3 -> DATA -> 0x4a9b50 obj.z main+59 0x401aab -> DATA -> 0x4a9b50 obj.z main+82 0x401ac2 -> CODE -> 0x401a8f main+31 - sym.function_0+4 0x401ad4 -> DATA -> 0x401ad9 sym.function_0+9 sym.function_0+4 0x401ad4 -> CODE -> 0x401ae0 sym.some_ptr sym.function_0+10 0x401ada -> CODE -> 0x401aa3 main+51 sym.some_ptr+15 0x401aef -> CODE -> 0x401ad9 sym.function_0+9 sym.some_ptr+15 0x401aef -> CODE -> 0x401af9 sym.function_1+9 sym.some_ptr+15 0x401aef -> CODE -> 0x401b09 sym.function_2+9 sym.function_1+4 0x401af4 -> CODE -> 0x401ae0 sym.some_ptr - sym.function_1+4 0x401af4 -> DATA -> 0x401af9 sym.function_1+9 sym.function_1+10 0x401afa -> CODE -> 0x401aa3 main+51 sym.function_2+4 0x401b04 -> CODE -> 0x401ae0 sym.some_ptr - sym.function_2+4 0x401b04 -> DATA -> 0x401b09 sym.function_2+9 sym.function_2+10 0x401b0a -> CODE -> 0x401aa3 main+51 EOF EXPECT_ERR=< 0x08000060 [ R2 = memw(FP+##-0xc) + : 0x08000064 [ P0 = cmp.gtu(R2,##0x2) + :,=< 0x08000068 [ if (P0) jump:nt 0x80000ac + ,===< 0x0800006c [ jump 0x8000070 + `---> 0x08000070 [ R2 = memw(FP+##-0xc) + :| ;-- .data: + :| 0x08000074 / immext(##sym.some_ptr) ; 0x80000e8 + :| ; sym..data; RELOC 32 .data @ 0x080000e8 + :| ;-- .data: + :| 0x08000078 \ R2 = memw(R2<<#0x2+##0x80000e8) ; RELOC 32 .data @ 0x080000e8 + :| 0x0800007c [ callr R2 + :| ; CODE XREF from sym.function_0 @ +0x8 + :| ; CODE XREF from sym.function_1 @ +0x8 + :| ; CODE XREF from sym.function_2 @ +0x8 + :| 0x08000080 [ memw(FP+##-0x10) = R0 + :| 0x08000084 [ R2 = memw(FP+##-0x10) + :| 0x08000088 [ R3 = memub(R2+##0x0) + :| 0x0800008c [ R2 = memw(FP+##-0x8) + :| 0x08000090 [ R2 = add(R2,R3) + :| 0x08000094 [ memw(FP+##-0x8) = R2 + ,===< 0x08000098 [ jump 0x800009c + `---> 0x0800009c [ R2 = memw(FP+##-0xc) + :| 0x080000a0 [ R2 = add(R2,##0x1) + :| 0x080000a4 [ memw(FP+##-0xc) = R2 + `==< 0x080000a8 [ jump 0x8000060 + | ; CODE XREF from section..text @ +0x28 + `-> 0x080000ac [ R0 = memw(FP+##-0x8) + 0x080000b0 [ LR:FP = dealloc_return(FP):raw + ; CODE XREF from reloc..data.8000078 @ +0x4 + ;-- function_0: + 0x080000b4 [ allocframe(SP,#0x0):raw + 0x080000b8 [ call sym.some_ptr + ; CODE XREF from reloc..bss.80000c8 @ +0x4 + 0x080000bc [ LR:FP = dealloc_return(FP):raw + ; CODE XREF from sym.function_0 @ +0x4 + ; CODE XREF from sym.function_1 @ +0x4 + ; CODE XREF from sym.function_2 @ +0x4 + ;-- some_ptr: + 0x080000c0 [ allocframe(SP,#0x0):raw + ;-- .bss: + 0x080000c4 / immext(##sym.some_ptr) ; RELOC 32 .bss @ 0x080000f8 + ;-- .bss: + 0x080000c8 \ R0 = ##obj.z ; RELOC 32 .bss @ 0x080000f8 + 0x080000cc [ LR:FP = dealloc_return(FP):raw + ; CODE XREF from reloc..data.8000078 @ +0x4 + ;-- function_1: + 0x080000d0 [ allocframe(SP,#0x0):raw + 0x080000d4 [ call sym.some_ptr + ; CODE XREF from reloc..bss.80000c8 @ +0x4 + 0x080000d8 [ LR:FP = dealloc_return(FP):raw + ; CODE XREF from reloc..data.8000078 @ +0x4 + ;-- function_2: + 0x080000dc [ allocframe(SP,#0x0):raw + 0x080000e0 [ call sym.some_ptr + ; CODE XREF from reloc..bss.80000c8 @ +0x4 + 0x080000e4 [ LR:FP = dealloc_return(FP):raw + ; DATA XREF from reloc..data @ + ;-- section..data: + ;-- .data: + ;-- .text: + ;-- fcn_arr: + 0x080000e8 .dword 0x080000b4 ; sym.function_0 ; RELOC 32 .text @ 0x08000040 + 0x74 ; [04] -rw- section size 12 named .data + ; DATA XREF from reloc..data @ + ;-- .text: + 0x080000ec .dword 0x080000d0 ; sym.function_1 ; RELOC 32 .text @ 0x08000040 + 0x90 + ; DATA XREF from reloc..data @ + ;-- .text: + 0x080000f0 .dword 0x080000dc ; sym.function_2 ; RELOC 32 .text @ 0x08000040 + 0x9c + 0x080000f4 + ;-- section..bss: + ;-- section..comment: + ;-- .bss: + ;-- z: + 0x080000f8 .dword 0xffffffff ; RELOC TARGET 32 .bss @ 0x080000f8 ; [07] ---- section size 41 named .comment + section..text+40 0x8000068 -> CODE -> 0x80000ac reloc..data.8000078+52 + reloc..data 0x8000074 -> DATA -> 0x80000e8 section..data + reloc..data 0x8000074 -> DATA -> 0x80000ec reloc..text.80000ec + reloc..data 0x8000074 -> DATA -> 0x80000f0 reloc..text.80000f0 + reloc..data.8000078+4 0x800007c -> CODE -> 0x80000b4 sym.function_0 + reloc..data.8000078+4 0x800007c -> CODE -> 0x80000d0 sym.function_1 + reloc..data.8000078+4 0x800007c -> CODE -> 0x80000dc sym.function_2 + reloc..data.8000078+48 0x80000a8 -> CODE -> 0x8000060 section..text+32 + sym.function_0+4 0x80000b8 -> CODE -> 0x80000c0 sym.some_ptr + sym.function_0+8 0x80000bc -> CODE -> 0x8000080 reloc..data.8000078+8 + reloc..bss.80000c8+4 0x80000cc -> CODE -> 0x80000bc sym.function_0+8 + reloc..bss.80000c8+4 0x80000cc -> CODE -> 0x80000d8 sym.function_1+8 + reloc..bss.80000c8+4 0x80000cc -> CODE -> 0x80000e4 sym.function_2+8 + sym.function_1+4 0x80000d4 -> CODE -> 0x80000c0 sym.some_ptr + sym.function_1+8 0x80000d8 -> CODE -> 0x8000080 reloc..data.8000078+8 + sym.function_2+4 0x80000e0 -> CODE -> 0x80000c0 sym.some_ptr + sym.function_2+8 0x80000e4 -> CODE -> 0x8000080 reloc..data.8000078+8 +EOF +EXPECT_ERR=< Date: Fri, 19 Dec 2025 17:42:18 +0100 Subject: [PATCH 137/334] Terminate interpretation at obviously wrong address. --- librz/inquiry/interpreter/interpreter.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index fe4eedd061f..9edd596bb4e 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -321,6 +321,10 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // Request the successor effects over the queue. while (!rz_vector_empty(tmp_succ_addr)) { rz_vector_pop_front(tmp_succ_addr, addr); + if (*addr == UT64_MAX || *addr == 0) { + // Obviously wrong address. + goto loop_cleanup; + } SuccessorState ss = { .addr = *addr, .in_state_hash = out_hash }; // The successors are pushed in the same order into the succ_states // vector, as they are requested over the addr_queue. From 6bdc88d81f6a0d35dea8f70356d145e0318cae7a Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 20 Dec 2025 22:21:30 +0100 Subject: [PATCH 138/334] Add a il memory load function which reads into a given output bit vector. --- librz/il/definitions/mem.c | 39 +++++++++++++++++++++++++-- librz/include/rz_il/definitions/mem.h | 7 ++++- test/unit/test_il_definitions.c | 7 +++++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/librz/il/definitions/mem.c b/librz/il/definitions/mem.c index d23a693b81b..b2ade7ae0ff 100644 --- a/librz/il/definitions/mem.c +++ b/librz/il/definitions/mem.c @@ -154,7 +154,24 @@ static RzBitVector *read_n_bits(RzBuffer *buf, ut32 n_bits, RzBitVector *key, bo return value; } -static bool write_n_bits(RzBuffer *buf, RzBitVector *key, RzBitVector *value, bool big_endian) { +static bool read_n_bits_into(RzBuffer *mem_buf, RZ_OUT RzBitVector *out_bv, ut32 n_bits, const RzBitVector *key, bool big_endian) { + ut64 address = rz_bv_to_ut64(key); + size_t n_bytes = rz_bv_len_bytes(out_bv); + ut8 *data = calloc(n_bytes, 1); + if (!data) { + return false; + } + // we ignore bad reads. RzBuffer fills up with its "overflow byte" on failure. + rz_buf_read_at(mem_buf, address, data, n_bytes); + if (big_endian) { + rz_bv_set_from_bytes_be(out_bv, data, 0, n_bits); + } else { + rz_bv_set_from_bytes_le(out_bv, data, 0, n_bits); + } + free(data); + return true; +} +static bool write_n_bits(RzBuffer *buf, const RzBitVector *key, const RzBitVector *value, bool big_endian) { ut64 address = rz_bv_to_ut64(key); ut32 n_bytes = rz_bv_len_bytes(value); @@ -186,13 +203,31 @@ RZ_API RzBitVector *rz_il_mem_loadw(RzILMem *mem, RzBitVector *key, ut32 n_bits, return read_n_bits(mem->buf, n_bits, key, big_endian); } +/** + * Load data of the given size from the given address into the bitvector \p out_bv. + * + * \param out_bv The bitvector to write the loaded data into. + * \param key address (bitvector) + * \param n_bits How many bits to read. This also determines the size of the returned bitvector + * \return True on success, false on failure. + */ +RZ_API bool rz_il_mem_loadw_into(RZ_NONNULL RzILMem *mem, + RZ_OUT RZ_NONNULL RzBitVector *out_bv, + RZ_NONNULL const RzBitVector *key, + ut32 n_bits, + bool big_endian) { + rz_return_val_if_fail(mem && key && n_bits, false); + return_val_if_key_len_wrong(mem, key, NULL); + return read_n_bits_into(mem->buf, out_bv, n_bits, key, big_endian); +} + /** * Store an entire word or arbitrary size at an address * \param key address * \param value data * \return whether the store succeeded */ -RZ_API bool rz_il_mem_storew(RzILMem *mem, RzBitVector *key, RzBitVector *value, bool big_endian) { +RZ_API bool rz_il_mem_storew(RzILMem *mem, const RzBitVector *key, const RzBitVector *value, bool big_endian) { rz_return_val_if_fail(mem && key && value, false); return_val_if_key_len_wrong(mem, key, false); return write_n_bits(mem->buf, key, value, big_endian); diff --git a/librz/include/rz_il/definitions/mem.h b/librz/include/rz_il/definitions/mem.h index ab541284e51..cbd0a357d74 100644 --- a/librz/include/rz_il/definitions/mem.h +++ b/librz/include/rz_il/definitions/mem.h @@ -36,7 +36,12 @@ RZ_API ut32 rz_il_mem_value_len(RzILMem *mem); RZ_API RzBitVector *rz_il_mem_load(RzILMem *mem, RzBitVector *key); RZ_API bool rz_il_mem_store(RzILMem *mem, RzBitVector *key, RzBitVector *value); RZ_API RzBitVector *rz_il_mem_loadw(RzILMem *mem, RzBitVector *key, ut32 n_bits, bool big_endian); -RZ_API bool rz_il_mem_storew(RzILMem *mem, RzBitVector *key, RzBitVector *value, bool big_endian); +RZ_API bool rz_il_mem_loadw_into(RZ_NONNULL RzILMem *mem, + RZ_OUT RZ_NONNULL RzBitVector *out_bv, + RZ_NONNULL const RzBitVector *key, + ut32 n_bits, + bool big_endian); +RZ_API bool rz_il_mem_storew(RzILMem *mem, const RzBitVector *key, const RzBitVector *value, bool big_endian); #ifdef __cplusplus } diff --git a/test/unit/test_il_definitions.c b/test/unit/test_il_definitions.c index 806a86862f7..ab26f3578c5 100644 --- a/test/unit/test_il_definitions.c +++ b/test/unit/test_il_definitions.c @@ -196,6 +196,13 @@ static bool test_il_mem_loadw() { mu_assert_eq(rz_bv_to_ut64(val), 0xaaaa, "load val"); rz_bv_free(val); + // Load into existing bitvector. + val = rz_bv_new_from_ut64(16, 0); + mu_assert_true(rz_il_mem_loadw_into(mem, val, addr, 16, false), "Load success"); + mu_assert_eq(rz_bv_len(val), 16, "load size"); + mu_assert_eq(rz_bv_to_ut64(val), 0xaaaa, "load val"); + rz_bv_free(val); + rz_bv_free(addr); rz_il_mem_free(mem); mu_end; From 0f8ad54cd3a02e0280d70e37f70ae587ddae07e2 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 20 Dec 2025 23:28:50 +0100 Subject: [PATCH 139/334] Refactor to use RzILMem for IO and RzAnalysisILVM for setup. This change enables the RzArch plugin to run their init() ILVM procedures. E.g. to setup extra memory regions or modify the register bindings. But sadly it also enforces addresses in the positive st64 range. This happens because RzILMem is used now and it uses RzBuffer, which uses st64 as type for addresses. --- librz/include/rz_inquiry/rz_interpreter.h | 19 ++- librz/inquiry/inquiry.c | 118 +++++++++--------- librz/inquiry/interpreter/interpreter.c | 13 +- librz/inquiry/interpreter/prototype/eval.c | 66 +++++----- librz/inquiry/interpreter/prototype/eval.h | 11 +- .../interpreter/prototype/eval_effect.c | 14 ++- .../inquiry/interpreter/prototype/eval_pure.c | 28 ++++- librz/util/buf.c | 1 + test/db/inquiry/interpreter/xrefs | 86 ++++++------- 9 files changed, 185 insertions(+), 171 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 9c6106b9da9..41096693544 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -9,6 +9,7 @@ #ifndef RZ_INTERPRETER #define RZ_INTERPRETER +#include "rz_util/rz_bitvector.h" #include #include #include @@ -169,20 +170,16 @@ typedef enum { typedef struct { RzInterpreterIOReqType type; - ut64 addr; ///< The address to read/write. - size_t n_bytes; ///< The number of bytes to read/write. - const ut8 *data; ///< The data to write. + size_t mem_idx; ///< The memory space to read/write. + bool big_endian; ///< Set if the data is big endian ordered. + const RzBitVector *addr; ///< The address to read/write. + const RzBitVector *st_data; ///< The data to store. + RzBitVector *ld_data; ///< The bit vector to load into. It is BORROWED. + size_t n_bits; ///< The number of bits to read/write. } RzInterpreterIORequest; typedef struct { - const ut8 *data; ///< The data read. NULL in case of failed read. - ut64 n_bytes; ///< The number of bytes to read. -} RzInterpreterIOResR; - -typedef struct { - RzInterpreterIOReqType type; bool req_ok; ///< Set to true if IO request succeeded. - RzInterpreterIOResR read; } RzInterpreterIOResult; /** @@ -214,7 +211,7 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( const char *arch_name, RzInterpreterAbstraction kinds, RZ_OWN RZ_NONNULL RzAnalysisILConfig *il_config, - RZ_NULLABLE const RzPVector *reg_names); + RZ_NULLABLE const RzILRegBinding *reg_bindings); RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbstrState *state); RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpreterYieldKind kind, diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 216b0c17ae5..e1f765d7098 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -8,20 +8,22 @@ #include "rz_analysis.h" #include "rz_config.h" #include "rz_cons.h" +#include "rz_il/definitions/mem.h" +#include "rz_il/rz_il_vm.h" #include "rz_inquiry/rz_interpreter.h" #include "rz_inquiry_plugins.h" #include "rz_io.h" -#include "rz_reg.h" #include "rz_th.h" +#include "rz_util/rz_bitvector.h" +#include "rz_util/rz_buf.h" #include "rz_vector.h" +#include #include #include #include RZ_LIB_VERSION(rz_inquiry); -#define MAX_IO_DATA_READ 0x1000 - static RzInquiryPlugin *inquiry_static_plugins[] = { RZ_INQUIRY_STATIC_PLUGINS }; RZ_API const size_t rz_inquiry_get_n_plugins() { @@ -72,29 +74,30 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL co return false; } -static RzPVector *get_reg_names(RzAnalysis *analysis) { - RzPVector *reg_names = rz_pvector_new(free); - if (analysis->cur->il_config) { - RzAnalysisILConfig *config = analysis->cur->il_config(analysis); - if (config->reg_bindings) { - for (size_t i = 0; config->reg_bindings[i]; i++) { - rz_pvector_push(reg_names, rz_str_dup(config->reg_bindings[i])); - } - rz_analysis_il_config_free(config); - return reg_names; - } - rz_analysis_il_config_free(config); +static void handle_io_request(RzCore *core, RzPVector /**/ *il_mems, RzInterpreterIORequest *io_req, RZ_OUT RzInterpreterIOResult *io_res) { + RZ_LOG_DEBUG("INQUIRY: Received IO %s request: 0x%" PFMT64x "\n", + io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", + rz_bv_to_ut64(io_req->addr)); + io_res->req_ok = false; + RzILMemIndex mem_idx = io_req->mem_idx; + if (rz_pvector_empty(il_mems) || rz_pvector_len(il_mems) <= mem_idx) { + rz_warn_if_reached(); + return; } - const RzList *regs = rz_reg_get_list(analysis->reg, RZ_REG_TYPE_ANY); - if (!regs) { - return NULL; + if (rz_bv_len(io_req->addr) == 64 && rz_bv_msb(io_req->addr)) { + RZ_LOG_ERROR("Due to the Unix seek() implementation, addresses with the " + "63 bit set can't be addresses.\n"); + return; } - RzRegItem *reg; - RzListIter *iter; - rz_list_foreach (regs, iter, reg) { - rz_pvector_push(reg_names, rz_str_dup(reg->name)); + RzILMem *mem = rz_pvector_at(il_mems, mem_idx); + if (io_req->type == RZ_INTERPRETER_IO_READ) { + io_res->req_ok = rz_il_mem_loadw_into(mem, io_req->ld_data, io_req->addr, io_req->n_bits, io_req->big_endian); + } else { + io_res->req_ok = rz_il_mem_storew(mem, io_req->addr, io_req->st_data, io_req->big_endian); } - return reg_names; + RZ_LOG_DEBUG("INQUIRY: Sent IO %s result. Success = %s.\n", + io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", + rz_str_bool(io_res->req_ok)); } /** @@ -118,6 +121,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { RzThreadQueue *il_queue = NULL; RzVector *entry_points = NULL; RzThread *interpr_th = NULL; + RzBuffer *io_buf = rz_buf_new_with_io(&core->analysis->iob); + RzAnalysisILVM *analysis_vm = NULL; rz_cons_push(); @@ -220,16 +225,36 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // Initialize the abstract state with the architecture's registers. if (!core->analysis->cur->il_config) { RZ_LOG_ERROR("The RzArch plugin doesn't have il_config() implemented.\n"); + return_code = false; goto error_free; } - RzAnalysisILConfig *config = core->analysis->cur->il_config(core->analysis); - RzPVector *reg_names = get_reg_names(core->analysis); - abstr_state = rz_interpreter_abstr_state_new( - core->analysis->cur->arch, - RZ_INTERPRETER_ABSTRACTION_CONST, - config, - reg_names); - rz_pvector_free(reg_names); + + // Perform the RzAnalysisILVM and abstract state setup procedure. + // This prototype won't use the RzAnalysisILVM directly but its components. + // That is because the prototypes doesn't handle the VM tasks (track PC, handle IO) + // in one VM object, but in separated modules. + // So analysis_vm->vm->vm_memorys is used for handling IO requests and + // analysis_vm->reg_binding is used for the abstract state setup. + // + // TODO: Is it a good idea to separate these tasks into different modules? + // It allows the IO handler to buffer reads in r-- sections for multiple interpreters. + // Possibly allows to optimize the IO access, because there is only module accessing it (not every interpreter). + // But is there any other advantage? + { + analysis_vm = rz_analysis_il_vm_new(core->analysis, core->analysis->reg); + if (!analysis_vm) { + RZ_LOG_ERROR("Failed during RzAnalysisILVM setup.\n"); + return_code = false; + goto error_free; + } + + RzAnalysisILConfig *config = core->analysis->cur->il_config(core->analysis); + abstr_state = rz_interpreter_abstr_state_new( + core->analysis->cur->arch, + RZ_INTERPRETER_ABSTRACTION_CONST, + config, + analysis_vm->reg_binding); + } // Bundle all the queues into one object to pass it to the thread. // Later we would pass a unique iset to each interpreter with @@ -247,6 +272,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { entry_points); if (!iset) { return_code = false; + rz_warn_if_reached(); goto error_free; } @@ -269,8 +295,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // Poor man's shared memory. RzInterpreterIOResult _io_res = { 0 }; RzInterpreterIOResult *io_res = &_io_res; - ut8 io_res_buf[MAX_IO_DATA_READ] = { 0 }; - io_res->read.data = io_res_buf; while (rz_atomic_bool_get(is_running)) { if (rz_th_terminated(interpr_th) || rz_cons_is_breaked()) { @@ -312,32 +336,10 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { RzInterpreterIORequest *io_req = NULL; if (!rz_th_queue_pop(io_request_q, false, (void **)&io_req) || !io_req) { rz_atomic_bool_set(is_running, false); + rz_warn_if_reached(); break; } - RZ_LOG_DEBUG("INQUIRY: Received IO %s request: 0x%" PFMT64x "\n", - io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", - io_req->addr); - if (io_req->type == RZ_INTERPRETER_IO_READ) { - if (io_req->n_bytes > MAX_IO_DATA_READ) { - RZ_LOG_ERROR("Plugin tried to read more than 0x%" PFMT32x " bytes.\n" - "This is more than configured. It will only read MAX_IO_DATA_READ bytes.\nPlease set MAX_IO_DATA_READ to a larger value and rebuild Rizin.\n", - MAX_IO_DATA_READ); - } - // Cast to constant ut8* here. The constant is only there so interpreter plugins don't free it by accident. - (void)rz_io_read_at_mapped(core->io, io_req->addr, (ut8 *)io_res->read.data, io_req->n_bytes > MAX_IO_DATA_READ ? MAX_IO_DATA_READ : io_req->n_bytes); - // The IO API doesn't have a function which can: - // - read beyond mapped regions and from cached data. - // - return the total number of bytes it read. - // - // So for this prototype we just have to assume it always succeeds :( - io_res->req_ok = true; - io_res->read.n_bytes = io_req->n_bytes; - } else { - io_res->req_ok = rz_io_write_at(core->io, io_req->addr, io_req->data, io_req->n_bytes); - } - RZ_LOG_DEBUG("INQUIRY: Sent IO %s result. Success = %s.\n", - io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", - io_res->req_ok ? "true" : "false"); + handle_io_request(core, &analysis_vm->vm->vm_memory, io_req, io_res); rz_th_queue_push(io_result_q, io_res, true); } } @@ -365,6 +367,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { // Wait for thread to finish before cleaning. error_free: RZ_LOG_DEBUG("INQUIRY: Close queues\n"); + rz_buf_free(io_buf); + rz_analysis_il_vm_free(analysis_vm); rz_th_queue_close(il_queue); rz_th_queue_close(io_request_q); rz_th_queue_close(io_result_q); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 9edd596bb4e..ebbbbd31e84 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -5,6 +5,7 @@ * \file The API implementation for all analysis interpreters. */ +#include "rz_cons.h" #include #include #include @@ -78,26 +79,22 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( const char *arch_name, RzInterpreterAbstraction kinds, RZ_OWN RZ_NONNULL RzAnalysisILConfig *il_config, - RZ_NULLABLE const RzPVector *reg_names) { - rz_return_val_if_fail(il_config, NULL); + RZ_NULLABLE const RzILRegBinding *reg_bindings) { + rz_return_val_if_fail(il_config && reg_bindings, NULL); RzInterpreterAbstrState *state = RZ_NEW0(RzInterpreterAbstrState); if (!state) { return NULL; } state->arch_name = arch_name; state->kinds = kinds; - if (!reg_names) { - return state; - } state->pc = RZ_NEW0(RzInterpreterAbstrVal); if (!state->pc) { return NULL; } // Initialize the register file with uninitialized abstract values. state->globals = ht_up_new(NULL, free); - void **it; - rz_pvector_foreach (reg_names, it) { - const char *rname = *it; + for (size_t i = 0; i < reg_bindings->regs_count; i++) { + const char *rname = reg_bindings->regs[i].name; RzInterpreterAbstrVal *aval = RZ_NEW0(RzInterpreterAbstrVal); if (!aval) { ht_up_free(state->globals); diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 9cac454496f..f63ec8b9d90 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -6,6 +6,7 @@ #include "rz_inquiry/rz_interpreter.h" #include "rz_th.h" #include "rz_types.h" +#include "rz_util/rz_assert.h" #include "rz_util/rz_log.h" #include @@ -132,7 +133,8 @@ bool abstr_is_true(const RzInterpreterAbstrState *state, const ProtoIntrprAbstrD bool store_abstr_data( RzInterpreterAbstrState *state, - ut64 addr, + RzILMemIndex mem_idx, + const ProtoIntrprAbstrData *addr, const ProtoIntrprAbstrData *src, RzThreadQueue /**/ *io_request, RzThreadQueue /**/ *io_result) { @@ -140,68 +142,58 @@ bool store_abstr_data( // Really don't write? return true; } - ProtoInterprSharedObjects *sobj = state->ext; - RzInterpreterIORequest *io_req = &sobj->io_req; - io_req->n_bytes = rz_bv_len_bytes(src->bv); + RzInterpreterIORequest io_req = { 0 }; + io_req.n_bits = rz_bv_len(src->bv); + io_req.mem_idx = mem_idx; + io_req.big_endian = state->il_config->big_endian; + + io_req.type = RZ_INTERPRETER_IO_WRITE; + io_req.addr = addr->bv; + io_req.st_data = src->bv; - ut8 *buf; - ut8 buf_stack[BV_STACK_MAX_SIZE] = { 0 }; - if (io_req->n_bytes > BV_STACK_MAX_SIZE) { - buf = RZ_NEWS(ut8, io_req->n_bytes); - } else { - buf = buf_stack; - } - rz_bv_set_to_bytes_ble(src->bv, buf, state->il_config->big_endian); - io_req->type = RZ_INTERPRETER_IO_WRITE; - io_req->addr = addr; - io_req->data = buf; char *bytes = rz_bv_as_hex_string(src->bv, true); - RZ_LOG_DEBUG("Prototype: STORE @ 0x%" PFMT64x " : %s\n", io_req->addr, bytes); + RZ_LOG_DEBUG("Prototype: STORE @ 0x%" PFMT64x " : %s\n", rz_bv_to_ut64(io_req.addr), bytes); free(bytes); - rz_th_queue_push(io_request, io_req, true); + rz_th_queue_push(io_request, &io_req, true); // Wait for write being done. RzInterpreterIOResult *io_res = NULL; rz_th_queue_pop(io_result, false, (void **)&io_res); - if (io_req->n_bytes > BV_STACK_MAX_SIZE) { - free(buf); - } return io_res ? io_res->req_ok : false; } bool load_abstr_data( RzInterpreterAbstrState *state, - ut64 addr, - size_t size, + RzILMemIndex mem_idx, + const ProtoIntrprAbstrData *addr, + size_t n_bits, RZ_OUT ProtoIntrprAbstrData *out, RzThreadQueue /**/ *io_request, RzThreadQueue /**/ *io_result) { - ProtoInterprSharedObjects *sobj = state->ext; - RzInterpreterIORequest *io_req = &sobj->io_req; - io_req->type = RZ_INTERPRETER_IO_READ; - io_req->addr = addr; - io_req->n_bytes = size; - rz_th_queue_push(io_request, io_req, true); + RzInterpreterIORequest io_req = { 0 }; + rz_bv_cast_inplace(out->bv, n_bits, 0); + io_req.type = RZ_INTERPRETER_IO_READ; + io_req.addr = addr->bv; + io_req.ld_data = out->bv; + io_req.mem_idx = mem_idx; + io_req.n_bits = n_bits; + io_req.big_endian = state->il_config->big_endian; + rz_th_queue_push(io_request, &io_req, true); // Wait for load being done. RzInterpreterIOResult *io_res = NULL; if (!rz_th_queue_pop(io_result, false, (void **)&io_res) || !io_res) { return false; } if (!io_res->req_ok) { - RZ_LOG_WARN("Prototype: IO read failed."); - return false; - } - if (io_res->read.n_bytes != size) { RZ_LOG_WARN("Prototype: Failed to read correct number of bytes. Requested: 0x%" PFMTSZx - " Received: 0x%" PFMT64x "\n", - size, io_res->read.n_bytes); + " Received: 0x%" PFMT32x " bits.\n", + n_bits, rz_bv_len(out->bv)); return false; } out->is_concrete = true; - rz_bv_cast_inplace(out->bv, size, 0); - rz_bv_set_from_bytes_ble(out->bv, io_res->read.data, 0, io_res->read.n_bytes, state->il_config->big_endian); + char *bytes = rz_bv_as_hex_string(out->bv, true); - RZ_LOG_DEBUG("Prototype: READ @ 0x%" PFMT64x " : %s\n", io_req->addr, bytes); + RZ_LOG_DEBUG("Prototype: READ @ 0x%" PFMT64x " : %s\n", rz_bv_to_ut64(io_req.addr), bytes); free(bytes); return true; } diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index a8df3255d20..0eb75d83151 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -28,8 +28,7 @@ typedef struct { } ProtoIntrprAbstrData; typedef struct { - RzInterpreterIORequest io_req; - RzAnalysisXRef xref; + RzAnalysisXRef xref; } ProtoInterprSharedObjects; /** @@ -71,14 +70,16 @@ bool read_var_from_state(RzInterpreterAbstrState *state, RzILVarKind kind, ut64 bool abstr_is_true(const RzInterpreterAbstrState *state, const ProtoIntrprAbstrData *data); bool store_abstr_data( RzInterpreterAbstrState *state, - ut64 addr, + RzILMemIndex mem_idx, + const ProtoIntrprAbstrData *addr, const ProtoIntrprAbstrData *src, RzThreadQueue /**/ *io_request, RzThreadQueue /**/ *io_result); bool load_abstr_data( RzInterpreterAbstrState *state, - ut64 addr, - size_t size, + RzILMemIndex mem_idx, + const ProtoIntrprAbstrData *addr, + size_t n_bits, RZ_OUT ProtoIntrprAbstrData *out, RzThreadQueue /**/ *io_request, RzThreadQueue /**/ *io_result); diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index e98938fd080..8aaa7c7ab4c 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -104,6 +104,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, case RZ_IL_OP_STOREW: { STACK_ABSTR_DATA_OUT(st_addr); RzILOpPure *key = effect->code == RZ_IL_OP_STORE ? effect->op.store.key : effect->op.storew.key; + RzILMemIndex mem_idx = effect->code == RZ_IL_OP_STORE ? 0 : effect->op.storew.mem; if (!interpreter_prototype_eval_pure(state, key, &st_addr, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: STORE/STOREW key failed to evaluate.\n"); rz_bv_fini(st_addr.bv); @@ -113,6 +114,16 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, rz_bv_fini(st_addr.bv); break; } + if (rz_bv_len(st_addr.bv) == 64) { + // TODO: Remove normalization. + // Unset bit 63 is required, because the RzBuffer API only supports + // st64 addresses. + RzBitVector mask = { 0 }; + rz_bv_init(&mask, 64); + rz_bv_set_from_ut64(&mask, 0x7fffffffffffffff); + rz_bv_and_inplace(st_addr.bv, &mask); + } + RzILOpPure *pval = effect->code == RZ_IL_OP_STORE ? effect->op.store.value : effect->op.storew.value; if (!interpreter_prototype_eval_pure(state, pval, &eval_out, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); @@ -124,8 +135,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, break; } report_xref_yield(state, insn_pkt_size, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &st_addr, RZ_ANALYSIS_XREF_TYPE_DATA); - ut64 addr = rz_bv_to_ut64(st_addr.bv); - if (!store_abstr_data(state, addr, &eval_out, io_request, io_result)) { + if (!store_abstr_data(state, mem_idx, &st_addr, &eval_out, io_request, io_result)) { rz_bv_fini(st_addr.bv); goto error; } diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index d9fd5b0edbf..90c90e70a55 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only #include "eval.h" +#include "rz_util/rz_assert.h" #include /** @@ -411,20 +412,35 @@ RZ_IPI bool interpreter_prototype_eval_pure( } case RZ_IL_OP_LOADW: case RZ_IL_OP_LOAD: { + STACK_ABSTR_DATA_OUT(ld_addr); RzILOpPure *key = pure->code == RZ_IL_OP_LOAD ? pure->op.load.key : pure->op.loadw.key; - if (!interpreter_prototype_eval_pure(state, key, out, yield_queues, io_request, io_result, plugin_data)) { + RzILMemIndex mem_idx = pure->code == RZ_IL_OP_LOAD ? 0 : pure->op.loadw.mem; + if (!interpreter_prototype_eval_pure(state, key, &ld_addr, yield_queues, io_request, io_result, plugin_data)) { RZ_LOG_ERROR("prototype: LOAD/LOADW key failed to evaluate.\n"); + rz_bv_fini(ld_addr.bv); return false; } - if (!out->is_concrete) { + if (!ld_addr.is_concrete) { + rz_bv_fini(ld_addr.bv); goto map_to_bottom; } - report_xref_yield(state, 0, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), out, RZ_ANALYSIS_XREF_TYPE_DATA); - ut64 addr = rz_bv_to_ut64(out->bv); - size_t addr_bits = pure->code == RZ_IL_OP_LOAD ? state->il_config->mem_key_size : pure->op.loadw.n_bits; - if (!load_abstr_data(state, addr, addr_bits, out, io_request, io_result)) { + if (rz_bv_len(ld_addr.bv) == 64) { + // TODO: Remove normalization. + // Unset bit 63 is required, because the RzBuffer API only supports + // st64 addresses. + RzBitVector mask = { 0 }; + rz_bv_init(&mask, 64); + rz_bv_set_from_ut64(&mask, 0x7fffffffffffffff); + rz_bv_and_inplace(ld_addr.bv, &mask); + } + + report_xref_yield(state, 0, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &ld_addr, RZ_ANALYSIS_XREF_TYPE_DATA); + size_t n_bits = pure->code == RZ_IL_OP_LOAD ? state->il_config->mem_key_size : pure->op.loadw.n_bits; + if (!load_abstr_data(state, mem_idx, &ld_addr, n_bits, out, io_request, io_result)) { + rz_bv_fini(ld_addr.bv); goto map_to_bottom; } + rz_bv_fini(ld_addr.bv); break; } case RZ_IL_OP_MUL: { diff --git a/librz/util/buf.c b/librz/util/buf.c index 40824e2a81c..fbcd8179f9c 100644 --- a/librz/util/buf.c +++ b/librz/util/buf.c @@ -13,6 +13,7 @@ #include "buf_io_fd.c" #include "buf_io.c" #include "buf_ref.c" +#include "rz_util/rz_assert.h" #define GET_STRING_BUFFER_SIZE 32 diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs index 6fb73579cd9..dbaa3ca8e90 100644 --- a/test/db/inquiry/interpreter/xrefs +++ b/test/db/inquiry/interpreter/xrefs @@ -2,7 +2,7 @@ NAME=Indirect call x86 FILE=bins/inquiry/interpreter/prototype/x86_icall_malloc CMDS=< 0x00401a8f cmp qword [rbp-0x18], 0x03 - ,==< 0x00401a94 jnb 0x401ac4 - |: 0x00401a96 mov rcx, qword [rbp-0x18] - |: 0x00401a9a mov al, 0x00 - |: 0x00401a9c call qword [rcx*8+obj.fcn_arr] ; 0x4a80d0 + 0x00401a7f mov dword [rbp-0x08], 0x00 + 0x00401a86 mov dword [rbp-0x0c], 0x00 + ; CODE XREF from main @ +0x4b + .-> 0x00401a8d cmp dword [rbp-0x0c], 0x03 + ,==< 0x00401a91 jnb 0x401abd + |: 0x00401a93 mov eax, dword [rbp-0x0c] + |: 0x00401a96 mov ecx, eax + |: 0x00401a98 mov al, 0x00 + |: 0x00401a9a call qword [rcx*8+obj.fcn_arr] ; 0x4a80d0 |: ; CODE XREF from sym.function_0 @ +0xa |: ; CODE XREF from sym.function_1 @ +0xa |: ; CODE XREF from sym.function_2 @ +0xa - |: 0x00401aa3 mov qword [rbp-0x20], rax - |: 0x00401aa7 mov rax, qword [rbp-0x20] - |: 0x00401aab movzx eax, byte [rax] ; [0x4a9b50:1]=0 - |: 0x00401aae add rax, qword [rbp-0x10] - |: 0x00401ab2 mov qword [rbp-0x10], rax - |: 0x00401ab6 mov rax, qword [rbp-0x18] - |: 0x00401aba add rax, 0x01 - |: 0x00401abe mov qword [rbp-0x18], rax - |`=< 0x00401ac2 jmp 0x401a8f - | ; CODE XREF from main @ +0x24 - `--> 0x00401ac4 mov rax, qword [rbp-0x10] - 0x00401ac8 add rsp, 0x20 - 0x00401acc pop rbp - 0x00401acd ret - 0x00401ace nop - ; CODE XREF from main @ +0x2c + |: 0x00401aa1 mov qword [rbp-0x18], rax + |: 0x00401aa5 mov rax, qword [rbp-0x18] + |: 0x00401aa9 movzx eax, byte [rax] ; [0x4a9b50:1]=0 + |: 0x00401aac add eax, dword [rbp-0x08] + |: 0x00401aaf mov dword [rbp-0x08], eax + |: 0x00401ab2 mov eax, dword [rbp-0x0c] + |: 0x00401ab5 add eax, 0x01 + |: 0x00401ab8 mov dword [rbp-0x0c], eax + |`=< 0x00401abb jmp 0x401a8d + | ; CODE XREF from main @ +0x21 + `--> 0x00401abd mov eax, dword [rbp-0x08] + 0x00401ac0 add rsp, 0x20 + 0x00401ac4 pop rbp + 0x00401ac5 ret + 0x00401ac6 nop word [rax+rax*1], ax + ; CODE XREF from main @ +0x2a ;-- function_0: 0x00401ad0 push rbp 0x00401ad1 mov rbp, rsp @@ -55,7 +56,7 @@ EXPECT=< CODE -> 0x401ac4 main+84 - main+44 0x401a9c -> CODE -> 0x401ad0 sym.function_0 - main+44 0x401a9c -> CODE -> 0x401af0 sym.function_1 - main+44 0x401a9c -> CODE -> 0x401b00 sym.function_2 - main+44 0x401a9c -> DATA -> 0x4a80d0 obj.fcn_arr - main+44 0x401a9c -> DATA -> 0x4a80d8 obj.fcn_arr+8 - main+44 0x401a9c -> DATA -> 0x4a80e0 obj.fcn_arr+16 - main+59 0x401aab -> DATA -> 0x4a9b50 obj.z - main+82 0x401ac2 -> CODE -> 0x401a8f main+31 + main+33 0x401a91 -> CODE -> 0x401abd main+77 + main+42 0x401a9a -> CODE -> 0x401ad0 sym.function_0 + main+42 0x401a9a -> CODE -> 0x401af0 sym.function_1 + main+42 0x401a9a -> CODE -> 0x401b00 sym.function_2 + main+42 0x401a9a -> DATA -> 0x4a80d0 obj.fcn_arr + main+42 0x401a9a -> DATA -> 0x4a80d8 obj.fcn_arr+8 + main+42 0x401a9a -> DATA -> 0x4a80e0 obj.fcn_arr+16 + main+57 0x401aa9 -> DATA -> 0x4a9b50 obj.z + main+75 0x401abb -> CODE -> 0x401a8d main+29 sym.function_0+4 0x401ad4 -> CODE -> 0x401ae0 sym.some_ptr - sym.function_0+10 0x401ada -> CODE -> 0x401aa3 main+51 + sym.function_0+10 0x401ada -> CODE -> 0x401aa1 main+49 sym.some_ptr+15 0x401aef -> CODE -> 0x401ad9 sym.function_0+9 sym.some_ptr+15 0x401aef -> CODE -> 0x401af9 sym.function_1+9 sym.some_ptr+15 0x401aef -> CODE -> 0x401b09 sym.function_2+9 sym.function_1+4 0x401af4 -> CODE -> 0x401ae0 sym.some_ptr - sym.function_1+10 0x401afa -> CODE -> 0x401aa3 main+51 + sym.function_1+10 0x401afa -> CODE -> 0x401aa1 main+49 sym.function_2+4 0x401b04 -> CODE -> 0x401ae0 sym.some_ptr - sym.function_2+10 0x401b0a -> CODE -> 0x401aa3 main+51 -EOF -EXPECT_ERR=< CODE -> 0x401aa1 main+49 EOF +EXPECT_ERR= RUN NAME=Indirect call hexagon @@ -218,8 +216,6 @@ WARNING: Skip adding map 'fmap..note.llvm.cgmdinfo' to boundaries, because it is WARNING: Skip adding map 'fmap..hexagon.attributes' to boundaries, because it is not readable. WARNING: Skip adding map 'fmap..llvm_addrsig' to boundaries, because it is not readable. WARNING: Skip adding map 'fmap..symtab' to boundaries, because it is not readable. -WARNING: inquiry: Failed to read memory for IL basic block generation at 0x0 size: 64. -ERROR: Failed to lift basic block at 0x0 ERROR: Failed to read chunk of size 0x101 at 0x8000040 for disassembly. EOF RUN From 30a266de98bd4b4bd4165452361d81d7574280fd Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 6 Jan 2026 18:57:36 +0100 Subject: [PATCH 140/334] Init state with default values. --- librz/inquiry/inquiry.c | 3 ++- .../interpreter/p/interpreter_prototype.c | 22 +++++++++++++++---- librz/inquiry/interpreter/prototype/eval.c | 4 ++-- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index e1f765d7098..610fa869142 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -75,8 +75,9 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL co } static void handle_io_request(RzCore *core, RzPVector /**/ *il_mems, RzInterpreterIORequest *io_req, RZ_OUT RzInterpreterIOResult *io_res) { - RZ_LOG_DEBUG("INQUIRY: Received IO %s request: 0x%" PFMT64x "\n", + RZ_LOG_DEBUG("INQUIRY: Received IO %s request: mem:%" PFMTSZd " 0x%" PFMT64x "\n", io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", + io_req->mem_idx, rz_bv_to_ut64(io_req->addr)); io_res->req_ok = false; RzILMemIndex mem_idx = io_req->mem_idx; diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index cbe6db9ea5c..e792014a52f 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -62,10 +62,12 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_poin state->pc->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); AD(state->pc->abstr_data)->bv = rz_bv_new_from_ut64(state->il_config->mem_key_size, entry_point); AD(state->pc->abstr_data)->is_concrete = true; - RzIterator *it = ht_up_as_iter(state->globals); - RzInterpreterAbstrVal **v; - rz_iterator_foreach(it, v) { - RzInterpreterAbstrVal *av = *v; + RzIterator *it = ht_up_as_iter_keys(state->globals); + ut64 *k; + rz_iterator_foreach(it, k) { + ut64 djb2_reg_name = *k; + RzInterpreterAbstrVal *av = ht_up_find(state->globals, djb2_reg_name, NULL); + rz_return_val_if_fail(av, false); av->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); // Length doesn't matter here. Because the destination is always // set to the length of the src. @@ -75,6 +77,18 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_poin // TODO: This is debatable. It depends on the ABI what the default values are. // Some values must be concrete, otherwise the interpretation of the prototype end too early. AD(av->abstr_data)->is_concrete = true; + if (state->il_config->init_state) { + RzAnalysisILInitStateVar *il_var; + rz_vector_foreach(&state->il_config->init_state->vars, il_var) { + if (rz_str_djb2_hash(il_var->name) != djb2_reg_name) { + continue; + } + // The RzArch plugin defined a default value for this global. + RzBitVector *default_val = rz_il_value_to_bv(il_var->val); + rz_bv_copy(default_val, AD(av->abstr_data)->bv); + rz_bv_free(default_val); + } + } } rz_iterator_free(it); state->ext = RZ_NEW0(ProtoInterprSharedObjects); diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index f63ec8b9d90..93df8a72559 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -152,7 +152,7 @@ bool store_abstr_data( io_req.st_data = src->bv; char *bytes = rz_bv_as_hex_string(src->bv, true); - RZ_LOG_DEBUG("Prototype: STORE @ 0x%" PFMT64x " : %s\n", rz_bv_to_ut64(io_req.addr), bytes); + RZ_LOG_DEBUG("Prototype: STORE @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); free(bytes); rz_th_queue_push(io_request, &io_req, true); @@ -193,7 +193,7 @@ bool load_abstr_data( out->is_concrete = true; char *bytes = rz_bv_as_hex_string(out->bv, true); - RZ_LOG_DEBUG("Prototype: READ @ 0x%" PFMT64x " : %s\n", rz_bv_to_ut64(io_req.addr), bytes); + RZ_LOG_DEBUG("Prototype: READ @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); free(bytes); return true; } From 4652c193e950ee1d1c2209f7f32f8c45275cf076 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 6 Jan 2026 23:58:16 +0100 Subject: [PATCH 141/334] Implement DIV and MOD --- .../interpreter/prototype/eval_effect.c | 1 + .../inquiry/interpreter/prototype/eval_pure.c | 56 ++++++++++++++++++- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 8aaa7c7ab4c..63048b2431f 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -145,6 +145,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, case RZ_IL_OP_GOTO: case RZ_IL_OP_BLK: case RZ_IL_OP_REPEAT: + RZ_LOG_ERROR("Unhandled effect %" PFMT32d "\n", effect->code); // Ignore for now. break; } diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 90c90e70a55..605b790751b 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -469,9 +469,59 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(y.bv); break; } - case RZ_IL_OP_DIV: + case RZ_IL_OP_MOD: { + RzILOpPure *px = pure->op.mod.x; + RzILOpPure *py = pure->op.mod.y; + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { + RZ_LOG_ERROR("prototype: MOD x failed to evaluate.\n"); + return false; + } + if (!out->is_concrete) { + goto map_to_bottom; + } + STACK_ABSTR_DATA_OUT(y); + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { + RZ_LOG_ERROR("prototype: MOD y failed to evaluate.\n"); + return false; + } + if (!y.is_concrete) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + if (!rz_bv_mod_inplace(out->bv, y.bv)) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + rz_bv_fini(y.bv); + break; + } + case RZ_IL_OP_DIV: { + RzILOpPure *px = pure->op.div.x; + RzILOpPure *py = pure->op.div.y; + if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { + RZ_LOG_ERROR("prototype: DIV x failed to evaluate.\n"); + return false; + } + if (!out->is_concrete) { + goto map_to_bottom; + } + STACK_ABSTR_DATA_OUT(y); + if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { + RZ_LOG_ERROR("prototype: DIV y failed to evaluate.\n"); + return false; + } + if (!y.is_concrete) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + if (!rz_bv_div_inplace(out->bv, y.bv)) { + rz_bv_fini(y.bv); + goto map_to_bottom; + } + rz_bv_fini(y.bv); + break; + } case RZ_IL_OP_SDIV: - case RZ_IL_OP_MOD: case RZ_IL_OP_SMOD: case RZ_IL_OP_FLOAT: case RZ_IL_OP_FBITS: @@ -507,7 +557,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_FPOWN: case RZ_IL_OP_FCOMPOUND: case RZ_IL_OP_FEXCEPT: - RZ_LOG_DEBUG("\nUnhandled op\n"); + RZ_LOG_ERROR("Unhandled pure %" PFMT32d "\n", pure->code); // Not implemented. goto map_to_bottom; } From 7d81a2b49a67660569753bb8c94ebcc6ff7080f8 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 7 Jan 2026 17:59:53 +0100 Subject: [PATCH 142/334] Fix Sparc BB generation --- librz/inquiry/inquiry_helpers.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c index 96af12306f8..acad19ddf21 100644 --- a/librz/inquiry/inquiry_helpers.c +++ b/librz/inquiry/inquiry_helpers.c @@ -27,6 +27,8 @@ RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana if (!il_bb) { goto fail; } + RZ_LOG_DEBUG("Gen BB:\n"); + bool sparc_add_delayed_insn = false; bool changes_cf = true; do { // Don't use rz_io_read_at_mapped() here. @@ -56,9 +58,20 @@ RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana if (lifted) { changes_cf = rz_analysis_op_changes_control_flow(&op); } + RZ_LOG_DEBUG("\t0x%" PFMT64x "\n", addr); rz_analysis_op_fini(&op); addr += op.size; rz_mem_memzero(buf, max_read_size); + if (sparc_add_delayed_insn) { + // Instruction was added, now the BB is complete. + break; + } + if (changes_cf && RZ_STR_EQ(analysis->cur->arch, "sparc")) { + // We need to add the instruction after the branch. + // So one more iteration is needed. + sparc_add_delayed_insn = true; + changes_cf = false; + } } while (!changes_cf); free(buf); From ce14958d6d62fa714915153b01758acb23e6c404 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 7 Jan 2026 18:34:28 +0100 Subject: [PATCH 143/334] Map not concrete shift results to bottom. --- librz/inquiry/interpreter/prototype/eval_pure.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 605b790751b..9057a5578ba 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -87,7 +87,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( return false; } if (!out->is_concrete) { - break; + goto map_to_bottom; } STACK_ABSTR_DATA_OUT(fill_bit); if (!interpreter_prototype_eval_pure(state, pure->op.cast.fill, &fill_bit, yield_queues, io_request, io_result, plugin_data)) { @@ -95,7 +95,8 @@ RZ_IPI bool interpreter_prototype_eval_pure( return false; } if (!fill_bit.is_concrete) { - break; + rz_bv_fini(fill_bit.bv); + goto map_to_bottom; } rz_bv_cast_inplace(out->bv, pure->op.cast.length, abstr_is_true(state, &fill_bit)); break; From 83a852cfd7ab144ff8ee8277a27c7d1ad760c12e Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 9 Jan 2026 21:38:44 +0100 Subject: [PATCH 144/334] Add callback to print current state. --- librz/include/rz_inquiry/rz_interpreter.h | 11 ++++++++ librz/inquiry/interpreter/interpreter.c | 15 ++++++++++- .../interpreter/p/interpreter_prototype.c | 27 +++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 41096693544..6867c799414 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -56,6 +56,7 @@ typedef struct { typedef struct { RzInterpreterAbstraction kinds; ///< The abstractions of the state. + HtUP *var_name_hashes; ///< Map of DJB2 hashes to variable names. HtUP /**/ *globals; ///< Global variables (mostly registers). Indexed by DJB2 hash of global name. HtUP /**/ *locals; ///< Local variables. Indexed by DJB2 hash of the local name. HtUP /**/ *lets; ///< Let variables. Indexed by DJB2 hash of the let name. @@ -155,6 +156,16 @@ typedef struct { bool (*successors)(RZ_NONNULL const RzInterpreterAbstrState *state, RZ_NONNULL RZ_OUT RzVector /**/ *successors, void *plugin_data); + + /** + * \brief Builds a string for printing the current state. + * + * \return Returns false in case of error. The interpretation must abort. + * True otherwise. + */ + bool (*state_as_str)(RZ_NONNULL const RzInterpreterAbstrState *state, + RZ_NONNULL RZ_OUT RzStrBuf *str_buf, + void *plugin_data); /** * \brief Set the abstract PC to the given address. */ diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index ebbbbd31e84..e0b3c914b1b 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -92,19 +92,22 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( return NULL; } // Initialize the register file with uninitialized abstract values. + state->var_name_hashes = ht_up_new(NULL, free); state->globals = ht_up_new(NULL, free); for (size_t i = 0; i < reg_bindings->regs_count; i++) { const char *rname = reg_bindings->regs[i].name; RzInterpreterAbstrVal *aval = RZ_NEW0(RzInterpreterAbstrVal); if (!aval) { ht_up_free(state->globals); + ht_up_free(state->var_name_hashes); free(state); return NULL; } aval->kind = RZ_INTERPRETER_ABSTRACTION_UNDEF; ut64 djb2_reg_hash = rz_str_djb2_hash(rname); - if (!ht_up_insert(state->globals, djb2_reg_hash, aval)) { + if (!ht_up_insert(state->globals, djb2_reg_hash, aval) || + !ht_up_insert(state->var_name_hashes, djb2_reg_hash, rz_str_dup(rname))) { RZ_LOG_ERROR("Failed to add %s to the global variable map. " "DJB2 hash collision of the register name. DJB2 hash = 0x%" PFMT64x "\n", rname, djb2_reg_hash); @@ -121,6 +124,9 @@ RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbst if (!state) { return; } + if (state->var_name_hashes) { + ht_up_free(state->var_name_hashes); + } if (state->globals) { ht_up_free(state->globals); } @@ -249,6 +255,8 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // Start interpretation // + RzStrBuf *state_str = rz_strbuf_new(""); + // A vector for the plugin to push the determined successors into. RzVector *tmp_succ_addr = NULL; // The set of reachable states. @@ -307,6 +315,10 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // Determine the successor effects to evaluate. // Only newly reached states are allowed to add successors. if (new_state_reached) { + plugin->state_as_str(out_state, state_str, plugin_data); + char *s = rz_strbuf_drain_nofree(state_str); + RZ_LOG_DEBUG("%s", s); + free(s); // Determine successors and increase the reference counts for the current out state. if (!plugin->successors(out_state, tmp_succ_addr, plugin_data)) { goto in_loop_error; @@ -353,6 +365,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { } loop_cleanup: + rz_strbuf_free(state_str); rz_vector_free(tmp_succ_addr); rz_vector_free(succ_states); rz_set_u_free(reachable_states); diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index e792014a52f..528b33016e5 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -162,6 +162,32 @@ static ut64 hash_state(RZ_NONNULL const RzInterpreterAbstrState *state, void *pl return h; } +bool state_as_str(RZ_NONNULL const RzInterpreterAbstrState *state, + RZ_NONNULL RZ_OUT RzStrBuf *sb, + void *plugin_data) { + rz_return_val_if_fail(state && sb, false); + + ut64 hash = hash_state(state, plugin_data); + rz_strbuf_appendf(sb, "hash = 0x%" PFMT64x "\n\n", hash); + rz_strbuf_append(sb, "Globals\n\n"); + char *value = AD(state->pc->abstr_data)->is_concrete ? rz_bv_as_hex_string(AD(state->pc->abstr_data)->bv, true) : rz_str_dup("⊥"); + rz_strbuf_appendf(sb, "\tpc = %s\n\n", value); + free(value); + + RzIterator *it = ht_up_as_iter_keys(state->globals); + ut64 *k; + rz_iterator_foreach(it, k) { + const char *gname = ht_up_find(state->var_name_hashes, *k, NULL); + RzInterpreterAbstrVal *av = ht_up_find(state->globals, *k, NULL); + ProtoIntrprAbstrData *ad = av->abstr_data; + value = ad->is_concrete ? rz_bv_as_hex_string(ad->bv, true) : rz_str_dup("⊥"); + rz_strbuf_appendf(sb, "\t%s = %s\n", gname, value); + free(value); + } + rz_iterator_free(it); + return true; +} + static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .name = "abstr_int_prototype", .author = "Rot127", @@ -178,6 +204,7 @@ static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .fini_state = fini_state, .hash_state = hash_state, .set_pc = set_pc, + .state_as_str = state_as_str, .clone_state = NULL, }; From 8825b00769f05b5279c0ed9e3599e2c414b7b317 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 12 Jan 2026 18:33:18 +0100 Subject: [PATCH 145/334] Add test for Sparc indirect call discovery. --- test/db/inquiry/interpreter/xrefs | 125 ++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs index dbaa3ca8e90..d708bd56640 100644 --- a/test/db/inquiry/interpreter/xrefs +++ b/test/db/inquiry/interpreter/xrefs @@ -219,3 +219,128 @@ WARNING: Skip adding map 'fmap..symtab' to boundaries, because it is not readabl ERROR: Failed to read chunk of size 0x101 at 0x8000040 for disassembly. EOF RUN + +NAME=Indirect call sparc +FILE=bins/inquiry/interpreter/prototype/sparc_icall_malloc +CMDS=< 0x080000c0 sethi 0x20000, g1 ; RELOC 0 .data @ 0x08000130 + :| ;-- .data: + :| 0x080000c4 or g1, 0x130, g2 ; RELOC 0 .data @ 0x08000130 + :| 0x080000c8 ld [fp+0x7f7], g1 + :| 0x080000cc srl g1, 0, g1 + :| 0x080000d0 sllx g1, 3, g1 + :| 0x080000d4 ldx [g2+g1], g1 ; 0x8000130 + :| ; obj.fcn_arr + :| 0x080000d8 call g1 + :| 0x080000dc nop + :| ; CODE XREF from sym.function_0 @ +0x18 + :| ; CODE XREF from sym.function_1 @ +0x18 + :| 0x080000e0 stx o0, [fp+0x7ef] + :| 0x080000e4 ldx [fp+0x7ef], g1 + :| 0x080000e8 ldub [g1], g1 + :| 0x080000ec and g1, 0xff, g1 + :| 0x080000f0 ld [fp+0x7fb], g2 + :| 0x080000f4 add g2, g1, g1 + :| 0x080000f8 st g1, [fp+0x7fb] + :| 0x080000fc ld [fp+0x7f7], g1 + :| 0x08000100 add g1, 1, g1 + :| 0x08000104 st g1, [fp+0x7f7] + :| ; CODE XREF from sym.main @ +0x10 + :`-> 0x08000108 ld [fp+0x7f7], g1 + : 0x0800010c cmp g1, 2 + `==< 0x08000110 bleu icc, reloc..data + 0x08000114 nop + 0x08000118 ld [fp+0x7fb], g1 + 0x0800011c sra g1, 0, g1 + 0x08000120 mov g1, i0 + 0x08000124 rett i7+8 + 0x08000128 nop + 0x0800012c invalid + reloc..bss.8000048+12 0x8000054 -> CODE -> 0x8000064 sym.function_0+12 + reloc..bss.8000048+12 0x8000054 -> CODE -> 0x8000080 sym.function_1+12 + sym.function_0+8 0x8000060 -> CODE -> 0x8000040 section..text + sym.function_0+24 0x8000070 -> CODE -> 0x80000e0 reloc..data.80000c4+28 + sym.function_1+8 0x800007c -> CODE -> 0x8000040 section..text + sym.function_1+24 0x800008c -> CODE -> 0x80000e0 reloc..data.80000c4+28 + sym.main+16 0x80000bc -> CODE -> 0x8000108 reloc..data.80000c4+68 + reloc..data.80000c4+16 0x80000d4 -> DATA -> 0x8000130 section..data + reloc..data.80000c4+16 0x80000d4 -> DATA -> 0x8000138 reloc..text.8000138 + reloc..data.80000c4+24 0x80000dc -> CODE -> 0x8000058 sym.function_0 + reloc..data.80000c4+24 0x80000dc -> CODE -> 0x8000074 sym.function_1 + reloc..data.80000c4+80 0x8000114 -> CODE -> 0x80000c0 reloc..data +EOF +EXPECT_ERR=< Date: Mon, 12 Jan 2026 18:59:24 +0100 Subject: [PATCH 146/334] Fix build for less smart CI build chains. --- librz/inquiry/interpreter/interpreter.c | 8 +++++++- librz/inquiry/interpreter/prototype/eval.c | 6 ++++++ .../inquiry/interpreter/prototype/eval_effect.c | 16 ++++++++++------ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index e0b3c914b1b..6bda32e7c61 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -107,7 +107,7 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( aval->kind = RZ_INTERPRETER_ABSTRACTION_UNDEF; ut64 djb2_reg_hash = rz_str_djb2_hash(rname); if (!ht_up_insert(state->globals, djb2_reg_hash, aval) || - !ht_up_insert(state->var_name_hashes, djb2_reg_hash, rz_str_dup(rname))) { + !ht_up_insert(state->var_name_hashes, djb2_reg_hash, rz_str_dup(rname))) { RZ_LOG_ERROR("Failed to add %s to the global variable map. " "DJB2 hash collision of the register name. DJB2 hash = 0x%" PFMT64x "\n", rname, djb2_reg_hash); @@ -271,7 +271,9 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { goto pre_loop_error; } RzInterpreterAbstrState *in_state = iset->state; +#if RZ_BUILD_DEBUG ut64 in_hash = plugin->hash_state(in_state, plugin_data); +#endif RzInterpreterAbstrState *out_state = NULL; ut64 out_hash = 0; @@ -304,7 +306,9 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // The input state was (almost always) manipulated by eval(). Rename to clarify. out_state = in_state; out_hash = plugin->hash_state(out_state, plugin_data); +#if RZ_BUILD_DEBUG RZ_LOG_DEBUG("in_hash = 0x%llx, out_hash = 0x%llx\n", in_hash, out_hash); +#endif // Add out_state hash to the reachable states and // set a flag if it was a new state. @@ -353,7 +357,9 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // Set effect and state for next evaluation. SuccessorState next = { 0 }; rz_vector_pop_front(succ_states, &next); +#if RZ_BUILD_DEBUG in_hash = next.in_state_hash; +#endif if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { goto in_loop_error; } diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 93df8a72559..add164f96f5 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -68,6 +68,9 @@ void write_var_to_state(RzInterpreterAbstrState *state, const ProtoIntrprAbstrData *data) { HtUP *ht_vals; switch (kind) { + default: + rz_warn_if_reached(); + return; case RZ_IL_VAR_KIND_GLOBAL: ht_vals = state->globals; break; @@ -97,6 +100,9 @@ bool read_var_from_state(RzInterpreterAbstrState *state, RZ_OUT ProtoIntrprAbstrData *data) { HtUP *ht_vals; switch (kind) { + default: + rz_warn_if_reached(); + return false; case RZ_IL_VAR_KIND_GLOBAL: ht_vals = state->globals; break; diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 63048b2431f..b5a4570bef3 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -28,14 +28,18 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, STACK_ABSTR_DATA_OUT(inc); rz_bv_set_from_ut64(inc.bv, insn_pkt_size); rz_bv_cast_inplace(inc.bv, rz_bv_len(pc->bv), false); +#if RZ_BUILD_DEBUG ut64 old_pc = rz_bv_to_ut64(pc->bv); +#endif if (!rz_bv_add_inplace(pc->bv, inc.bv, NULL)) { goto error; } +#if RZ_BUILD_DEBUG RZ_LOG_DEBUG("Prototype: NOP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", - old_pc, - rz_bv_to_ut64(pc->bv), - pc->is_concrete ? "Concrete" : "Abstract"); + old_pc, + rz_bv_to_ut64(pc->bv), + pc->is_concrete ? "Concrete" : "Abstract"); +#endif break; } case RZ_IL_OP_SEQ: { @@ -66,9 +70,9 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, RZ_LOG_DEBUG("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", rz_bv_to_ut64(AD(state->pc->abstr_data)->bv)); } RZ_LOG_DEBUG("Prototype: JMP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", - rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), - rz_bv_to_ut64(eval_out.bv), - eval_out.is_concrete ? "Concrete" : "Abstract"); + rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), + rz_bv_to_ut64(eval_out.bv), + eval_out.is_concrete ? "Concrete" : "Abstract"); // Setting the PC to a bottom value is allowed here! // The successor function will handle this case. if (eval_out.is_concrete) { From 0b4aa04446a3b1c7a03b02e24fb3323b4a24a418 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 12 Jan 2026 19:07:04 +0100 Subject: [PATCH 147/334] Move entry-points init to command handler --- librz/core/cmd/cmd_inquiry.c | 21 +++++++++++++++- librz/include/rz_inquiry.h | 2 +- librz/inquiry/inquiry.c | 32 ++++++------------------- librz/inquiry/interpreter/interpreter.c | 2 +- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 5711cd0235c..5e5013f22c6 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -12,5 +12,24 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int argc, const char **argv) { rz_return_val_if_fail(core->analysis && core->io, RZ_CMD_STATUS_ERROR); - return rz_inquiry_interpreter(core, argc, argv) ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; + RzVector *entry_points = rz_vector_new(sizeof(ut64), NULL, NULL); + if (!entry_points) { + return RZ_CMD_STATUS_ERROR; + } + + ut64 entry_point; + if (argc == 1) { + // No specific entry point given. Pick the one provided by the bin plugin. + entry_point = rz_bin_get_first_entrypoint(core->bin->cur->o); + rz_vector_push(entry_points, &entry_point); + } else { + // Add all entry points given as arguments. + for (size_t i = 1; i < argc; i++) { + ut64 entry_point = rz_num_get(core->num, argv[i]); + rz_vector_push(entry_points, &entry_point); + } + } + bool success = rz_inquiry_interpreter(core, entry_points); + rz_vector_free(entry_points); + return success ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; } diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index b21365ea944..8f139c90358 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -37,7 +37,7 @@ RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzList /**/ *allowed_io_maps); -RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv); +RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entry_points); #ifdef __cplusplus } diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 610fa869142..e104d480305 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -105,7 +105,7 @@ static void handle_io_request(RzCore *core, RzPVector /**/ *il_mems, * A function to call the prototype interpreter. * Usually these tasks will be split between different caches and yield consumers. */ -RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { +RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entry_points) { // All the things we need bool return_code = true; RzThreadQueue *io_request_q = NULL; @@ -120,7 +120,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { RzInterpreterSet *iset = NULL; RzPVector *il_cache = NULL; RzThreadQueue *il_queue = NULL; - RzVector *entry_points = NULL; RzThread *interpr_th = NULL; RzBuffer *io_buf = rz_buf_new_with_io(&core->analysis->iob); RzAnalysisILVM *analysis_vm = NULL; @@ -151,30 +150,14 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { } // Add the Effect for each entry point. - entry_points = rz_vector_new(sizeof(ut64), NULL, NULL); - il_op = NULL; - if (argc == 1) { - ut64 entry_point = rz_bin_get_first_entrypoint(core->bin->cur->o); - il_op = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); + ut64 *ep; + rz_vector_foreach (entry_points, ep) { + il_op = rz_inquiry_gen_il_bb(core->analysis, core->io, *ep); if (!il_op) { - RZ_LOG_WARN("Could not get entry point IL operation at 0x%" PFMT64x "\n", (ut64)entry_point); + RZ_LOG_WARN("Could not get entry point IL operation at 0x%" PFMT64x "\n", *ep); return_code = false; goto error_free; } - rz_vector_push(entry_points, &entry_point); - rz_th_queue_push(il_queue, il_op, true); - rz_pvector_push(il_cache, il_op); - } else { - // Add all entry points given as arguments. - for (size_t i = 1; i < argc; i++) { - ut64 entry_point = rz_num_get(core->num, argv[i]); - il_op = rz_inquiry_gen_il_bb(core->analysis, core->io, entry_point); - if (!il_op) { - return_code = false; - goto error_free; - } - rz_vector_push(entry_points, &entry_point); - } rz_th_queue_push(il_queue, il_op, true); rz_pvector_push(il_cache, il_op); } @@ -270,7 +253,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { io_request_q, io_result_q, is_running, - entry_points); + rz_vector_clone(entry_points)); if (!iset) { return_code = false; rz_warn_if_reached(); @@ -346,7 +329,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { } // This part plays the role of a yield consumer. - // In our prototype it inly receives xrefs and stores them in RzAnalysis. + // In our prototype it only receives xrefs and stores them in RzAnalysis. { RzInterpreterYieldQueue *q = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); if (!rz_th_queue_is_empty(q->yield_queue)) { @@ -389,7 +372,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, int argc, const char **argv) { ht_up_free(yield_queues); rz_atomic_bool_free(is_running); rz_interpreter_abstr_state_free(abstr_state); - rz_vector_free(entry_points); } else { // Ownership was passed to iset rz_interpreter_set_free(iset); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 6bda32e7c61..9cbe1dc661c 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -195,7 +195,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag, RZ_NONNULL RZ_OWN RzVector /**/ *entry_points) { - rz_return_val_if_fail(plugin && state && addr_queue && il_queue && yield_queues && io_request && io_result && is_running_flag, NULL); + rz_return_val_if_fail(plugin && state && addr_queue && il_queue && yield_queues && io_request && io_result && is_running_flag && entry_points, NULL); RzInterpreterSet *set = RZ_NEW0(RzInterpreterSet); if (!set) { From 77221b70f21a9aa134fa891c472aeff5e36c0141 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 12 Jan 2026 19:10:36 +0100 Subject: [PATCH 148/334] Move io.cache toggle to before iset for loop preparation. --- librz/inquiry/inquiry.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index e104d480305..b5b1adb323e 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -217,7 +217,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entr // This prototype won't use the RzAnalysisILVM directly but its components. // That is because the prototypes doesn't handle the VM tasks (track PC, handle IO) // in one VM object, but in separated modules. - // So analysis_vm->vm->vm_memorys is used for handling IO requests and + // So analysis_vm->vm->vm_memorys is used for handling IO requests and // analysis_vm->reg_binding is used for the abstract state setup. // // TODO: Is it a good idea to separate these tasks into different modules? @@ -240,6 +240,10 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entr analysis_vm->reg_binding); } + RZ_LOG_DEBUG("INQUIRY: Enforce enabling IO cache.\n"); + const char *io_cache_opt = rz_config_get(core->config, "io.cache"); + rz_config_set(core->config, "io.cache", "true"); + // Bundle all the queues into one object to pass it to the thread. // Later we would pass a unique iset to each interpreter with // the required queues only. @@ -271,9 +275,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entr // - Receiving and adding the found xrefs to RzAnalysis. // In the final implementation each of those roles would be split into // two or more separated modules running in parallel. - RZ_LOG_DEBUG("INQUIRY: Enforce enabling IO cache.\n"); - const char *io_cache_opt = rz_config_get(core->config, "io.cache"); - rz_config_set(core->config, "io.cache", "true"); RZ_LOG_DEBUG("INQUIRY: Start IL providing loop.\n"); // Poor man's shared memory. From 13ff2d90b1eb270e2ac1970c9c09e1926429d273 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 12 Jan 2026 20:53:34 +0100 Subject: [PATCH 149/334] Add two more reference types. --- librz/arch/xrefs.c | 4 ++++ librz/include/rz_analysis.h | 14 +++++++++++--- librz/inquiry/interpreter/prototype/eval_effect.c | 2 +- librz/inquiry/interpreter/prototype/eval_pure.c | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/librz/arch/xrefs.c b/librz/arch/xrefs.c index 9b339583d9a..c50af829164 100644 --- a/librz/arch/xrefs.c +++ b/librz/arch/xrefs.c @@ -214,6 +214,8 @@ RZ_API const char *rz_analysis_xrefs_type_tostring(RzAnalysisXRefType type) { return "DATA"; case RZ_ANALYSIS_XREF_TYPE_STRING: return "STRING"; + case RZ_ANALYSIS_XREF_TYPE_MEM_WRITE: + return "MEM_WRITE"; case RZ_ANALYSIS_XREF_TYPE_NULL: default: return "UNKNOWN"; @@ -306,6 +308,8 @@ RZ_API const char *rz_analysis_ref_type_tostring(RzAnalysisXRefType t) { return "data"; case RZ_ANALYSIS_XREF_TYPE_STRING: return "string"; + case RZ_ANALYSIS_XREF_TYPE_MEM_WRITE: + return "write"; } return "unknown"; } diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 52f9dfb314d..d67c1fcf0bd 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -956,10 +956,18 @@ typedef struct rz_analysis_task_item { typedef enum { RZ_ANALYSIS_XREF_TYPE_NULL = 0, - RZ_ANALYSIS_XREF_TYPE_CODE = 'c', // code ref + RZ_ANALYSIS_XREF_TYPE_CODE = 'c', ///< Unspecified code reference. RZ_ANALYSIS_XREF_TYPE_CALL = 'C', // code ref (call) - RZ_ANALYSIS_XREF_TYPE_DATA = 'd', // mem ref - RZ_ANALYSIS_XREF_TYPE_STRING = 's' // string ref + RZ_ANALYSIS_XREF_TYPE_DATA = 'd', ///< Unspecified data memory access. + RZ_ANALYSIS_XREF_TYPE_STRING = 's', ///< String reference + /** + * \brief Memory read reference. + * Probably not the same as RZ_ANALYSIS_XREF_TYPE_DATA, but the data + * reference is used everywhere and changes the output of the disassembly. + * So keep it for now like that to have better test output. + */ + RZ_ANALYSIS_XREF_TYPE_MEM_READ = RZ_ANALYSIS_XREF_TYPE_DATA, + RZ_ANALYSIS_XREF_TYPE_MEM_WRITE = 'w', ///< Memory write reference } RzAnalysisXRefType; typedef struct rz_analysis_ref_t { diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index b5a4570bef3..dffdcd68389 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -138,7 +138,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, rz_bv_fini(st_addr.bv); break; } - report_xref_yield(state, insn_pkt_size, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &st_addr, RZ_ANALYSIS_XREF_TYPE_DATA); + report_xref_yield(state, insn_pkt_size, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); if (!store_abstr_data(state, mem_idx, &st_addr, &eval_out, io_request, io_result)) { rz_bv_fini(st_addr.bv); goto error; diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 9057a5578ba..d412d67df57 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -435,7 +435,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_and_inplace(ld_addr.bv, &mask); } - report_xref_yield(state, 0, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &ld_addr, RZ_ANALYSIS_XREF_TYPE_DATA); + report_xref_yield(state, 0, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &ld_addr, RZ_ANALYSIS_XREF_TYPE_MEM_READ); size_t n_bits = pure->code == RZ_IL_OP_LOAD ? state->il_config->mem_key_size : pure->op.loadw.n_bits; if (!load_abstr_data(state, mem_idx, &ld_addr, n_bits, out, io_request, io_result)) { rz_bv_fini(ld_addr.bv); From de9516ae00f90900e91a23d0083a67dd26f3574a Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 12 Jan 2026 21:40:37 +0100 Subject: [PATCH 150/334] Start implementing BB tracking. --- librz/arch/fcn.c | 22 ++++++++++++++++++++++ librz/include/rz_analysis.h | 2 ++ librz/include/rz_inquiry.h | 2 +- librz/inquiry/inquiry.c | 8 ++++++-- librz/inquiry/inquiry_helpers.c | 5 ++++- 5 files changed, 35 insertions(+), 4 deletions(-) diff --git a/librz/arch/fcn.c b/librz/arch/fcn.c index fa4eeb3108b..87aaa764a98 100644 --- a/librz/arch/fcn.c +++ b/librz/arch/fcn.c @@ -1854,6 +1854,28 @@ RZ_API RzAnalysisFunction *rz_analysis_get_function_byname(RzAnalysis *a, const return NULL; } +/** + * \brief Adds a basic block to the RzAnalysis. + */ +RZ_API bool rz_analysis_add_bb(RzAnalysis *a, ut64 addr, ut64 size) { + rz_return_val_if_fail(a && size > 0, false); + if (size > a->opt.bb_max_size) { + RZ_LOG_ERROR("Cannot allocate such big bb of %" PFMT64d " bytes at 0x%08" PFMT64x "\n", (st64)size, addr); + rz_warn_if_reached(); + return false; + } + RzAnalysisBlock *block = rz_analysis_get_block_at(a, addr); + if (block) { + rz_analysis_delete_block(block); + } + + block = rz_analysis_create_block(a, addr, size); + if (!block) { + return false; + } + return true; +} + /* rename RzAnalysisFunctionBB.add() */ RZ_API bool rz_analysis_fcn_add_bb(RzAnalysis *a, RzAnalysisFunction *fcn, ut64 addr, ut64 size, ut64 jump, ut64 fail) { if (size == 0) { diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index d67c1fcf0bd..39b08290533 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -975,6 +975,7 @@ typedef struct rz_analysis_ref_t { ut64 to; RzAnalysisXRefType type; } RzAnalysisXRef; + RZ_API const char *rz_analysis_ref_type_tostring(RzAnalysisXRefType t); /* represents a reference line from one address (from) to another (to) */ @@ -1577,6 +1578,7 @@ RZ_API int rz_analysis_fcn(RzAnalysis *analysis, RzAnalysisFunction *fcn, ut64 a RZ_API int rz_analysis_fcn_del(RzAnalysis *analysis, ut64 addr); RZ_API int rz_analysis_fcn_del_locs(RzAnalysis *analysis, ut64 addr); RZ_API bool rz_analysis_fcn_add_bb(RzAnalysis *analysis, RzAnalysisFunction *fcn, ut64 addr, ut64 size, ut64 jump, ut64 fail); +RZ_API bool rz_analysis_add_bb(RzAnalysis *a, ut64 addr, ut64 size); RZ_API bool rz_analysis_check_fcn(RzAnalysis *analysis, ut8 *buf, ut16 bufsz, ut64 addr, ut64 low, ut64 high); RZ_API void rz_analysis_function_check_bp_use(RzAnalysisFunction *fcn); diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 8f139c90358..46126bd72d9 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -33,7 +33,7 @@ RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NO RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void); RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *a); -RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr); +RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr, RZ_NULLABLE RZ_OUT size_t *bb_size); RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzList /**/ *allowed_io_maps); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index b5b1adb323e..7d958533022 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -152,12 +152,14 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entr // Add the Effect for each entry point. ut64 *ep; rz_vector_foreach (entry_points, ep) { - il_op = rz_inquiry_gen_il_bb(core->analysis, core->io, *ep); + size_t bb_size = 0; + il_op = rz_inquiry_gen_il_bb(core->analysis, core->io, *ep, &bb_size); if (!il_op) { RZ_LOG_WARN("Could not get entry point IL operation at 0x%" PFMT64x "\n", *ep); return_code = false; goto error_free; } + rz_analysis_add_bb(core->analysis, *ep, bb_size); rz_th_queue_push(il_queue, il_op, true); rz_pvector_push(il_cache, il_op); } @@ -296,7 +298,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entr break; } RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x "\n", (*addr)); - RzInterpreterILBB *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr); + size_t bb_size = 0; + RzInterpreterILBB *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr, &bb_size); if (!bb) { RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", *addr); // Signal interpreter the lifting failed. @@ -304,6 +307,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entr rz_th_queue_close(iset->il_queue); break; } + rz_analysis_add_bb(core->analysis, *addr, bb_size); RZ_LOG_DEBUG("INQUIRY: Send IL result: %p.\n", bb); rz_pvector_push(il_cache, bb); // TODO: Free unused if too big. diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c index acad19ddf21..95c80c86932 100644 --- a/librz/inquiry/inquiry_helpers.c +++ b/librz/inquiry/inquiry_helpers.c @@ -12,7 +12,7 @@ #include #include -RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr) { +RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr, RZ_NULLABLE RZ_OUT size_t *bb_size) { rz_return_val_if_fail(analysis && analysis->cur && io, NULL); RzInterpreterILBB *il_bb = NULL; RzAnalysisOp op = { 0 }; @@ -61,6 +61,9 @@ RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana RZ_LOG_DEBUG("\t0x%" PFMT64x "\n", addr); rz_analysis_op_fini(&op); addr += op.size; + if (bb_size) { + *bb_size += op.size; + } rz_mem_memzero(buf, max_read_size); if (sparc_add_delayed_insn) { // Instruction was added, now the BB is complete. From d87fbbf39efe5fea41174dbb2db7088b380f8b2d Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 14 Jan 2026 15:08:46 +0100 Subject: [PATCH 151/334] Move all the queue init code into a static function. --- librz/inquiry/inquiry.c | 120 ++++++++++++++++++++++------------------ 1 file changed, 66 insertions(+), 54 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 7d958533022..b767c111088 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -101,75 +101,36 @@ static void handle_io_request(RzCore *core, RzPVector /**/ *il_mems, rz_str_bool(io_res->req_ok)); } -/** - * A function to call the prototype interpreter. - * Usually these tasks will be split between different caches and yield consumers. - */ -RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entry_points) { - // All the things we need - bool return_code = true; - RzThreadQueue *io_request_q = NULL; - RzThreadQueue *io_result_q = NULL; - RzInterpreterILBB *il_op = NULL; - RzThreadQueue *addr_queue = NULL; +static bool setup_queues(RzIO *io, + RZ_OUT RzThreadQueue **il_queue, + RZ_OUT RzThreadQueue **io_request_q, + RZ_OUT RzThreadQueue **io_result_q, + RZ_OUT RzThreadQueue **addr_queue, + RZ_OUT HtUP **yield_queues) { RzList *boundaries = NULL; RzInterpreterYieldQueue *yield_queue = NULL; - HtUP *yield_queues = NULL; - RzAtomicBool *is_running = rz_atomic_bool_new(true); - RzInterpreterAbstrState *abstr_state = NULL; - RzInterpreterSet *iset = NULL; - RzPVector *il_cache = NULL; - RzThreadQueue *il_queue = NULL; - RzThread *interpr_th = NULL; - RzBuffer *io_buf = rz_buf_new_with_io(&core->analysis->iob); - RzAnalysisILVM *analysis_vm = NULL; - - rz_cons_push(); - - // The pseudo cache of IL effects. - // This is only a vector so we can simulate the ownership separation - // of the pointers. - il_cache = rz_pvector_new((RzPVectorFree)rz_interpreter_il_bb_free); // The queue to pass the Effects to the interpreter. // This is only one queue for the prototype. // In practice it would be one for each interpreter. - il_queue = rz_th_queue_new(RZ_INTERPRETER_IL_QUEUE_SIZE, NULL); + *il_queue = rz_th_queue_new(RZ_INTERPRETER_IL_QUEUE_SIZE, NULL); if (!il_queue) { - return_code = false; goto error_free; } // Setup the IO queues. Each interpreter instance needs it's own queue at // for writing IO. Because the writing is done on the IO cache, and each // instance needs its own cache. - io_request_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); - io_result_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); + *io_request_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); + *io_result_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); if (!io_request_q || !io_result_q) { - return_code = false; goto error_free; } - // Add the Effect for each entry point. - ut64 *ep; - rz_vector_foreach (entry_points, ep) { - size_t bb_size = 0; - il_op = rz_inquiry_gen_il_bb(core->analysis, core->io, *ep, &bb_size); - if (!il_op) { - RZ_LOG_WARN("Could not get entry point IL operation at 0x%" PFMT64x "\n", *ep); - return_code = false; - goto error_free; - } - rz_analysis_add_bb(core->analysis, *ep, bb_size); - rz_th_queue_push(il_queue, il_op, true); - rz_pvector_push(il_cache, il_op); - } - // The address queue. It is the queue the interpreter can request new Effects. // Of course, currently there is only a single one for the prototype. // In practice there would be one for each interpreter instance. - addr_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, NULL); + *addr_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, NULL); if (!addr_queue) { - return_code = false; goto error_free; } @@ -178,9 +139,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entr // So the filter checks the generated xrefs, if they are within the IO map // boundaries. RzInterval iv = { .addr = 0, .size = UT64_MAX }; - boundaries = rz_io_get_boundaries_all_io_maps(core->io, iv); + boundaries = rz_io_get_boundaries_all_io_maps(io, iv); if (!boundaries) { - return_code = false; goto error_free; } @@ -193,7 +153,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entr (RzInterpreterYieldFilter)rz_inquiry_xref_interpreter_filter, boundaries); if (!yield_queue) { - return_code = false; goto error_free; } @@ -201,12 +160,65 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entr // E.g. if the interpreter has a complex abstract memory model // for stack, heap and constant values. // Then it can produce three kind of yields. - yield_queues = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_yield_queue_free); + *yield_queues = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_yield_queue_free); if (!yield_queue || !yield_queues) { + goto error_free; + } + ht_up_insert(*yield_queues, yield_kind, yield_queue); + + return true; + +error_free: + return false; +} + +/** + * A function to call the prototype interpreter. + * Usually these tasks will be split between different caches and yield consumers. + */ +RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entry_points) { + // All the things we need + bool return_code = true; + RzThreadQueue *io_request_q = NULL; + RzThreadQueue *io_result_q = NULL; + RzInterpreterILBB *il_op = NULL; + RzThreadQueue *addr_queue = NULL; + HtUP *yield_queues = NULL; + RzAtomicBool *is_running = rz_atomic_bool_new(true); + RzInterpreterAbstrState *abstr_state = NULL; + RzInterpreterSet *iset = NULL; + RzPVector *il_cache = NULL; + RzThreadQueue *il_queue = NULL; + RzThread *interpr_th = NULL; + RzBuffer *io_buf = rz_buf_new_with_io(&core->analysis->iob); + RzAnalysisILVM *analysis_vm = NULL; + + rz_cons_push(); + + if (!setup_queues(core->io, &il_queue, &io_request_q, &io_result_q, &addr_queue, &yield_queues)) { return_code = false; goto error_free; } - ht_up_insert(yield_queues, yield_kind, yield_queue); + + // The pseudo cache of IL effects. + // This is only a vector so we can simulate the ownership separation + // of the pointers. + il_cache = rz_pvector_new((RzPVectorFree)rz_interpreter_il_bb_free); + + // Add the Effect for each entry point. + ut64 *ep; + rz_vector_foreach (entry_points, ep) { + size_t bb_size = 0; + il_op = rz_inquiry_gen_il_bb(core->analysis, core->io, *ep, &bb_size); + if (!il_op) { + RZ_LOG_WARN("Could not get entry point IL operation at 0x%" PFMT64x "\n", *ep); + return_code = false; + goto error_free; + } + rz_analysis_add_bb(core->analysis, *ep, bb_size); + rz_th_queue_push(il_queue, il_op, true); + rz_pvector_push(il_cache, il_op); + } // Initialize the abstract state with the architecture's registers. if (!core->analysis->cur->il_config) { From a50434be78a4e721c2079881769bccfb3641e0d6 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 14 Jan 2026 21:30:23 +0100 Subject: [PATCH 152/334] Second part implementing BB and coverage tracking. --- librz/arch/analysis.c | 14 + librz/arch/xrefs.c | 53 ++++ librz/core/cmd/cmd_analysis.c | 1 + librz/core/cmd/cmd_inquiry.c | 1 - librz/include/rz_analysis.h | 3 + librz/include/rz_inquiry.h | 2 +- librz/include/rz_inquiry/rz_interpreter.h | 3 +- librz/inquiry/inquiry.c | 241 +++++++++++------- librz/inquiry/interpreter/interpreter.c | 24 +- .../interpreter/p/interpreter_prototype.c | 14 +- test/db/inquiry/interpreter/xrefs | 70 ++++- 11 files changed, 320 insertions(+), 106 deletions(-) diff --git a/librz/arch/analysis.c b/librz/arch/analysis.c index 87435060945..d7ffde76af2 100644 --- a/librz/arch/analysis.c +++ b/librz/arch/analysis.c @@ -1029,6 +1029,20 @@ RZ_API bool rz_analysis_op_changes_control_flow(RZ_NONNULL const RzAnalysisOp *o } } +/** + * \brief Returns true if the \p op is a direct call. + */ +RZ_API bool rz_analysis_op_is_direct_call(RZ_NONNULL const RzAnalysisOp *op) { + rz_return_val_if_fail(op, false); + switch (op->type & RZ_ANALYSIS_OP_TYPE_MASK) { + case RZ_ANALYSIS_OP_TYPE_CALL: + case RZ_ANALYSIS_OP_TYPE_CCALL: + return true; + default: + return false; + } +} + RZ_API void rz_analysis_purge(RzAnalysis *analysis) { rz_analysis_hint_clear(analysis); rz_interval_tree_fini(&analysis->meta); diff --git a/librz/arch/xrefs.c b/librz/arch/xrefs.c index c50af829164..8935c6992bf 100644 --- a/librz/arch/xrefs.c +++ b/librz/arch/xrefs.c @@ -313,3 +313,56 @@ RZ_API const char *rz_analysis_ref_type_tostring(RzAnalysisXRefType t) { } return "unknown"; } + +int is_in_x_map(const ut64 *call_target, const RzIOMap *map, void *user) { + return (map->perm & RZ_PERM_X && rz_itv_contain(map->itv, *call_target)) ? 0 : -1; +} + +/** + * \brief Returns all targets of call instructions within the \p boundaries. + * NOTE: This function disassembles all instructions within the boundaries and checks for calls. + * It DOES NOT use the existing xrefs. + * + * \param analysis The analysis plugin. + * \param maps The IO maps to disassemble to find call instructions. + * \param call_targets The found call targets of all disassemble calls. + * NOTE: They can point outside of \p maps! + * + * \return True in case of success, false otherwise. + */ +RZ_API bool rz_analysis_get_all_call_targets(RzAnalysis *analysis, const RzList /**/ *maps, RZ_NONNULL RZ_OUT RzSetU *call_targets) { + rz_return_val_if_fail(analysis && analysis->cur && maps && call_targets, false); + int buf_size = (analysis->cur->bits / 8) * 16; + ut8 *buf = RZ_NEWS0(ut8, buf_size); + if (!buf_size || !buf) { + return false; + } + + RzAnalysisOp op = { 0 }; + RzIOMap *map; + RzListIter *it; + rz_list_foreach (maps, it, map) { + ut64 off = 0; + ut64 addr = map->itv.addr + off; + while (map->itv.addr + off < map->itv.addr + map->itv.size) { + if (analysis->iob.read_at(analysis->iob.io, addr, buf, buf_size) == 0) { + RZ_LOG_WARN("Failed to read memory at 0x%" PFMT64x " size: %u.\n", addr, buf_size); + rz_analysis_op_fini(&op); + break; + } + if (rz_analysis_op(analysis, &op, addr, buf, buf_size, RZ_ANALYSIS_OP_MASK_BASIC) <= 0) { + rz_analysis_op_fini(&op); + break; + } + // Only add call targets going to executable regions. + if (rz_analysis_op_is_direct_call(&op) && rz_list_find(maps, &op.jump, (RzListComparator)is_in_x_map, NULL)) { + RZ_LOG_DEBUG("Add call target 0x%" PFMT64x " -> 0x%" PFMT64x "\n", op.addr, op.jump); + rz_set_u_add(call_targets, op.jump); + } + addr += op.size; + rz_analysis_op_fini(&op); + } + } + free(buf); + return true; +} diff --git a/librz/core/cmd/cmd_analysis.c b/librz/core/cmd/cmd_analysis.c index 1eca5574951..c903ecc7b84 100644 --- a/librz/core/cmd/cmd_analysis.c +++ b/librz/core/cmd/cmd_analysis.c @@ -2125,6 +2125,7 @@ RZ_IPI RzCmdStatus rz_analysis_function_xrefs_handler(RzCore *core, int argc, co case RZ_ANALYSIS_XREF_TYPE_NULL: rz_cons_printf("0x%08" PFMT64x " ", xref->to); break; + case RZ_ANALYSIS_XREF_TYPE_MEM_WRITE: case RZ_ANALYSIS_XREF_TYPE_CODE: case RZ_ANALYSIS_XREF_TYPE_CALL: case RZ_ANALYSIS_XREF_TYPE_DATA: diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 5e5013f22c6..9056f48d232 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -30,6 +30,5 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar } } bool success = rz_inquiry_interpreter(core, entry_points); - rz_vector_free(entry_points); return success ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; } diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 39b08290533..948080b8025 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -1511,6 +1511,7 @@ RZ_API int rz_analysis_op_reg_delta(RzAnalysis *analysis, ut64 addr, const char RZ_API bool rz_analysis_op_is_eob(const RzAnalysisOp *op); RZ_API bool rz_analysis_op_is_call(RZ_NONNULL const RzAnalysisOp *op); RZ_API bool rz_analysis_op_changes_control_flow(RZ_NONNULL const RzAnalysisOp *op); +RZ_API bool rz_analysis_op_is_direct_call(RZ_NONNULL const RzAnalysisOp *op); RZ_API RzList /**/ *rz_analysis_op_list_new(void); RZ_API int rz_analysis_op(RZ_NONNULL RzAnalysis *analysis, RZ_OUT RzAnalysisOp *op, ut64 addr, const ut8 *data, ut64 len, RzAnalysisOpMask mask); RZ_API RzAnalysisOp *rz_analysis_op_hexstr(RzAnalysis *analysis, ut64 addr, const char *hexstr); @@ -1618,6 +1619,8 @@ RZ_API bool rz_analysis_xrefs_set(RzAnalysis *analysis, ut64 from, ut64 to, RzAn RZ_API bool rz_analysis_xrefs_deln(RzAnalysis *analysis, ut64 from, ut64 to, RzAnalysisXRefType type); RZ_API bool rz_analysis_xref_del(RzAnalysis *analysis, ut64 from, ut64 to); +RZ_API bool rz_analysis_get_all_call_targets(RzAnalysis *analysis, const RzList /**/ *maps, RZ_NONNULL RZ_OUT RzSetU *call_targets); + /* var.c */ RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_set_var( RzAnalysisFunction *fcn, diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 46126bd72d9..eb1134f55b5 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -37,7 +37,7 @@ RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzList /**/ *allowed_io_maps); -RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entry_points); +RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *entry_points); #ifdef __cplusplus } diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 6867c799414..8b4c38cb759 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -126,7 +126,7 @@ typedef struct { */ bool (*init_state)(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_point, void *plugin_data); /** - * \brief Closes the abstract state and frees all its abstract data. + * \brief Closes the abstract state and frees all its abstract data and sets the pointers to NULL. */ bool (*fini_state)(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data); /** @@ -240,6 +240,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag, RZ_NONNULL RZ_OWN RzVector /**/ *entry_points); RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset); +RZ_API void rz_interpreter_set_add_entry_points(RZ_NONNULL RzInterpreterSet *iset, const RzVector /**/ *entry_points); RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *queue_set); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index b767c111088..8ddbf2a22f7 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -16,6 +16,7 @@ #include "rz_th.h" #include "rz_util/rz_bitvector.h" #include "rz_util/rz_buf.h" +#include "rz_util/rz_set.h" #include "rz_vector.h" #include #include @@ -176,12 +177,11 @@ static bool setup_queues(RzIO *io, * A function to call the prototype interpreter. * Usually these tasks will be split between different caches and yield consumers. */ -RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entry_points) { +RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *entry_points) { // All the things we need bool return_code = true; RzThreadQueue *io_request_q = NULL; RzThreadQueue *io_result_q = NULL; - RzInterpreterILBB *il_op = NULL; RzThreadQueue *addr_queue = NULL; HtUP *yield_queues = NULL; RzAtomicBool *is_running = rz_atomic_bool_new(true); @@ -192,6 +192,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entr RzThread *interpr_th = NULL; RzBuffer *io_buf = rz_buf_new_with_io(&core->analysis->iob); RzAnalysisILVM *analysis_vm = NULL; + RzSetU *call_targets = rz_set_u_new(); rz_cons_push(); @@ -205,20 +206,18 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entr // of the pointers. il_cache = rz_pvector_new((RzPVectorFree)rz_interpreter_il_bb_free); - // Add the Effect for each entry point. - ut64 *ep; - rz_vector_foreach (entry_points, ep) { - size_t bb_size = 0; - il_op = rz_inquiry_gen_il_bb(core->analysis, core->io, *ep, &bb_size); - if (!il_op) { - RZ_LOG_WARN("Could not get entry point IL operation at 0x%" PFMT64x "\n", *ep); - return_code = false; - goto error_free; - } - rz_analysis_add_bb(core->analysis, *ep, bb_size); - rz_th_queue_push(il_queue, il_op, true); - rz_pvector_push(il_cache, il_op); + RzInterval iv = { .addr = 0, .size = UT64_MAX }; + RzList *boundaries = rz_io_get_boundaries_all_io_maps(core->io, iv); + if (!boundaries) { + goto error_free; } + if (!rz_analysis_get_all_call_targets(core->analysis, boundaries, call_targets)) { + RZ_LOG_ERROR("Failed to get call targets.\n"); + return_code = false; + goto error_free; + } + rz_list_free(boundaries); + RZ_LOG_DEBUG("Total call targets in binary: %" PFMT32d "\n", rz_set_u_size(call_targets)); // Initialize the abstract state with the architecture's registers. if (!core->analysis->cur->il_config) { @@ -278,89 +277,155 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entr goto error_free; } - // Dispatch prototype interpreter into a thread. - RZ_LOG_DEBUG("INQUIRY: Start main interpretation thread.\n"); - interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); - - // From here on, the code plays the role of the cache, IO handler, - // and yield consumer. - // - Waiting for new Effects to be requested and sending them. - // - Handling IO requests. - // - Receiving and adding the found xrefs to RzAnalysis. - // In the final implementation each of those roles would be split into - // two or more separated modules running in parallel. - RZ_LOG_DEBUG("INQUIRY: Start IL providing loop.\n"); - - // Poor man's shared memory. - RzInterpreterIOResult _io_res = { 0 }; - RzInterpreterIOResult *io_res = &_io_res; - - while (rz_atomic_bool_get(is_running)) { - if (rz_th_terminated(interpr_th) || rz_cons_is_breaked()) { - rz_atomic_bool_set(is_running, false); - break; - } + do { + // Dispatch prototype interpreter into a thread. + RZ_LOG_DEBUG("INQUIRY: Start main interpretation thread.\n"); + interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); + + // Poor man's shared memory. + RzInterpreterIOResult _io_res = { 0 }; + RzInterpreterIOResult *io_res = &_io_res; + + // From here on, the code plays the role of the cache, IO handler, + // and yield consumer. + // - Waiting for new Effects to be requested and sending them. + // - Handling IO requests. + // - Receiving and adding the found xrefs to RzAnalysis. + // In the final implementation each of those roles would be split into + // two or more separated modules running in parallel. + RZ_LOG_DEBUG("INQUIRY: Start IL providing loop.\n"); + rz_atomic_bool_set(is_running, true); + + while (rz_atomic_bool_get(is_running)) { + if (rz_th_terminated(interpr_th) || rz_cons_is_breaked()) { + rz_atomic_bool_set(is_running, false); + break; + } - // This block mimics the IL cache. - { - if (!rz_th_queue_is_empty(iset->addr_queue)) { - ut64 *addr = NULL; - if (!rz_th_queue_pop(iset->addr_queue, false, (void **)&addr) || !addr) { - rz_warn_if_reached(); - break; + // ========= + // IL CACHE + // ========= + // + // This block mimics the IL cache. + { + if (!rz_th_queue_is_empty(iset->addr_queue)) { + ut64 *addr = NULL; + if (!rz_th_queue_pop(iset->addr_queue, false, (void **)&addr) || !addr) { + rz_warn_if_reached(); + break; + } + RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x "\n", (*addr)); + size_t bb_size = 0; + RzInterpreterILBB *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr, &bb_size); + if (!bb) { + RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", *addr); + // Signal interpreter the lifting failed. + rz_atomic_bool_set(is_running, false); + rz_th_queue_close(iset->il_queue); + break; + } + rz_analysis_add_bb(core->analysis, *addr, bb_size); + RZ_LOG_DEBUG("INQUIRY: Send IL result: %p.\n", bb); + rz_pvector_push(il_cache, bb); + // TODO: Free unused if too big. + rz_th_queue_push(il_queue, bb, true); } - RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x "\n", (*addr)); - size_t bb_size = 0; - RzInterpreterILBB *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr, &bb_size); - if (!bb) { - RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", *addr); - // Signal interpreter the lifting failed. - rz_atomic_bool_set(is_running, false); - rz_th_queue_close(iset->il_queue); - break; + } + + // ========== + // IO HANDLER + // ========== + // + // This plays the IO handler for a single(!) interpreter instances. + // In the future we should have only one IO handler for multiple interpreters. + // But this requires multiple IO write caches + // (one for each interpreter instance). + // Because this is not yet implemented, there is only one interpreter thread for now. + { + if (!rz_th_queue_is_empty(io_request_q)) { + RzInterpreterIORequest *io_req = NULL; + if (!rz_th_queue_pop(io_request_q, false, (void **)&io_req) || !io_req) { + rz_atomic_bool_set(is_running, false); + rz_warn_if_reached(); + break; + } + handle_io_request(core, &analysis_vm->vm->vm_memory, io_req, io_res); + rz_th_queue_push(io_result_q, io_res, true); } - rz_analysis_add_bb(core->analysis, *addr, bb_size); - RZ_LOG_DEBUG("INQUIRY: Send IL result: %p.\n", bb); - rz_pvector_push(il_cache, bb); - // TODO: Free unused if too big. - rz_th_queue_push(il_queue, bb, true); } - } - // This plays the IO handler for a single(!) interpreter instances. - // In the future we should have only one IO handler for multiple interpreters. - // But this requires multiple IO write caches - // (one for each interpreter instance). - // Because this is not yet implemented, there is only one interpreter thread for now. - { - if (!rz_th_queue_is_empty(io_request_q)) { - RzInterpreterIORequest *io_req = NULL; - if (!rz_th_queue_pop(io_request_q, false, (void **)&io_req) || !io_req) { - rz_atomic_bool_set(is_running, false); - rz_warn_if_reached(); - break; + // ============== + // YIELD CONSUMER + // ============== + // + // This part plays the role of a yield consumer. + // In our prototype it only receives xrefs and stores them in RzAnalysis. + { + RzInterpreterYieldQueue *q = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); + if (!rz_th_queue_is_empty(q->yield_queue)) { + RzAnalysisXRef *xref = NULL; + if (!rz_th_queue_pop(q->yield_queue, false, (void **)&xref) || !xref) { + rz_atomic_bool_set(is_running, false); + break; + } + // TODO: Currently we can't classify calls as such. + rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, xref->type); + RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)); } - handle_io_request(core, &analysis_vm->vm->vm_memory, io_req, io_res); - rz_th_queue_push(io_result_q, io_res, true); } } - // This part plays the role of a yield consumer. - // In our prototype it only receives xrefs and stores them in RzAnalysis. + RZ_LOG_DEBUG("INQUIRY: Wait for join\n"); + rz_th_wait(interpr_th); + return_code = rz_th_get_retv(interpr_th); + rz_th_free(interpr_th); + if (!return_code) { + RZ_LOG_ERROR("Interpreter failed with an error. Abort.\n"); + break; + } + + // At this point the interpreter is finished and returned. + // Now we need to check for executable regions it did not cover. + // For this we simply delete all call targets from our set, which point + // into the already handled basic blocks. + // Then add a few addresses as new entry points. + // The addresses we add are call targets from call instructions in the binary. { - RzInterpreterYieldQueue *q = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); - if (!rz_th_queue_is_empty(q->yield_queue)) { - RzAnalysisXRef *xref = NULL; - if (!rz_th_queue_pop(q->yield_queue, false, (void **)&xref) || !xref) { - rz_atomic_bool_set(is_running, false); + rz_vector_clear(entry_points); + RzVector *covered_call_targets = rz_vector_new(sizeof(ut64), NULL, NULL); + RzIterator *ct_iter = rz_set_u_as_iter(call_targets); + size_t x = 0; + ut64 *ct; + rz_iterator_foreach(ct_iter, ct) { + RzList *containing_blocks = rz_analysis_get_blocks_in(core->analysis, *ct); + if (rz_list_length(containing_blocks) != 0) { + rz_vector_push(covered_call_targets, ct); + rz_list_free(containing_blocks); + continue; + } + rz_list_free(containing_blocks); + x++; + rz_vector_push(entry_points, ct); + // Experiment how many new entry points we add. + if (x >= 1) { break; } - // TODO: Currently we can't classify calls as such. - rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, xref->type); - RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)); } + rz_interpreter_set_add_entry_points(iset, entry_points); + + rz_iterator_free(ct_iter); + ut64 *ep; + rz_vector_foreach (covered_call_targets, ep) { + // Delete the selected ones from the call target set. + // So they are not requested again. + rz_set_u_delete(call_targets, *ep); + } + rz_vector_free(covered_call_targets); + + RZ_LOG_DEBUG("Call targets left: %" PFMT32d "\n", rz_set_u_size(call_targets)); } - } + } while (!rz_vector_empty(entry_points)); + RZ_LOG_DEBUG("INQUIRY: Done\n"); rz_config_set(core->config, "io.cache", io_cache_opt); @@ -368,17 +433,13 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, const RzVector /**/ *entr // Wait for thread to finish before cleaning. error_free: RZ_LOG_DEBUG("INQUIRY: Close queues\n"); + rz_vector_free(entry_points); + rz_set_u_free(call_targets); rz_buf_free(io_buf); rz_analysis_il_vm_free(analysis_vm); rz_th_queue_close(il_queue); rz_th_queue_close(io_request_q); rz_th_queue_close(io_result_q); - if (interpr_th) { - RZ_LOG_DEBUG("INQUIRY: Wait for join\n"); - rz_th_wait(interpr_th); - return_code = rz_th_get_retv(interpr_th); - rz_th_free(interpr_th); - } if (!iset) { rz_th_queue_free(addr_queue); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 9cbe1dc661c..e9ecfdfc087 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -6,6 +6,7 @@ */ #include "rz_cons.h" +#include "rz_util/rz_assert.h" #include #include #include @@ -218,6 +219,12 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( return set; } +RZ_API void rz_interpreter_set_add_entry_points(RZ_NONNULL RzInterpreterSet *iset, const RzVector /**/ *entry_points) { + rz_return_if_fail(iset && entry_points); + rz_vector_clear(iset->entry_points); + rz_vector_clone_intof(iset->entry_points, entry_points, NULL); +} + typedef struct { ut64 addr; ut64 in_state_hash; @@ -240,7 +247,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { iset->plugin->fini_state && iset->plugin->hash_state, entry_assert_error); - bool success = false; + bool success = true; RZ_LOG_DEBUG("INTERPRETER Main: Hello.\n"); RzInterpreterPlugin *plugin = iset->plugin; @@ -265,6 +272,13 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // This vector must have the same order as the elements pushed into addr_queue. RzVector *succ_states = NULL; + // TODO: Add support for multiple entry points by spawning an interpreter for each of them. + // For now let's just drop them. + if (rz_vector_len(iset->entry_points) > 1) { + RZ_LOG_ERROR("More than one entry point is not yet supported by the prototype.\n"); + goto pre_loop_error; + } + ut64 entry_point; rz_vector_pop_front(iset->entry_points, &entry_point); if (!plugin->init_state(iset->state, entry_point, plugin_data)) { @@ -277,17 +291,11 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { RzInterpreterAbstrState *out_state = NULL; ut64 out_hash = 0; + rz_th_queue_push(iset->addr_queue, &entry_point, true); const RzInterpreterILBB *il_bb = NULL; if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { goto pre_loop_error; } - // TODO: Add support for multiple entry points by spawning an interpreter for each of them. - // For now let's just drop them. - RzList *additional_entries = rz_th_queue_pop_all(iset->il_queue); - if (rz_list_length(additional_entries) > 0) { - RZ_LOG_WARN("More than one entry point is not yet supported by the prototype.\n"); - } - rz_list_free(additional_entries); tmp_succ_addr = rz_vector_new(sizeof(ut64), NULL, NULL); succ_states = rz_vector_new(sizeof(SuccessorState), NULL, NULL); diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 528b33016e5..d98b21ca47d 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -97,19 +97,22 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_poin static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data) { ProtoIntrprAbstrData *ad = state->pc->abstr_data; - if (ad->bv) { + if (ad && ad->bv) { rz_bv_free(ad->bv); } free(ad); + state->pc->abstr_data = NULL; + RzIterator *it = ht_up_as_iter(state->globals); RzInterpreterAbstrVal **v; rz_iterator_foreach(it, v) { RzInterpreterAbstrVal *av = *v; ProtoIntrprAbstrData *ad = av->abstr_data; - if (ad->bv) { + if (ad && ad->bv) { rz_bv_free(ad->bv); } free(ad); + av->abstr_data = NULL; } rz_iterator_free(it); @@ -117,10 +120,11 @@ static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da rz_iterator_foreach(it, v) { RzInterpreterAbstrVal *av = *v; ProtoIntrprAbstrData *ad = av->abstr_data; - if (ad->bv) { + if (ad && ad->bv) { rz_bv_free(ad->bv); } free(ad); + av->abstr_data = NULL; } rz_iterator_free(it); @@ -128,13 +132,15 @@ static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da rz_iterator_foreach(it, v) { RzInterpreterAbstrVal *av = *v; ProtoIntrprAbstrData *ad = av->abstr_data; - if (ad->bv) { + if (ad && ad->bv) { rz_bv_free(ad->bv); } free(ad); + av->abstr_data = NULL; } rz_iterator_free(it); free(state->ext); + state->ext = NULL; return true; } diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs index d708bd56640..019cc30c89d 100644 --- a/test/db/inquiry/interpreter/xrefs +++ b/test/db/inquiry/interpreter/xrefs @@ -4,6 +4,7 @@ CMDS=< CODE -> 0x401aa1 main+49 sym.function_2+4 0x401b04 -> CODE -> 0x401ae0 sym.some_ptr sym.function_2+10 0x401b0a -> CODE -> 0x401aa1 main+49 + addr size traced ninstr jump fail fcns calls xrefs +------------------------------------------------------------------- +0x401a70 35 0 0 +0x401a8d 6 0 0 +0x401a93 14 0 0 +0x401aa1 28 0 0 +0x401abd 9 0 0 +0x401ad0 9 0 0 0x00401ae0 0x00401ae0 +0x401ad9 2 0 0 +0x401ae0 16 0 0 +0x401af0 9 0 0 0x00401ae0 0x00401ae0 +0x401af9 2 0 0 +0x401b00 9 0 0 0x00401ae0 0x00401ae0 +0x401b09 2 0 0 +EOF +EXPECT_ERR=< CODE -> 0x8000080 reloc..data.8000078+8 sym.function_2+4 0x80000e0 -> CODE -> 0x80000c0 sym.some_ptr sym.function_2+8 0x80000e4 -> CODE -> 0x8000080 reloc..data.8000078+8 + addr size traced ninstr jump fail fcns calls xrefs +-------------------------------------------------------------------- +0x8000040 32 0 0 +0x8000060 12 0 0 +0x800006c 4 0 0 +0x8000070 16 0 0 +0x8000080 28 0 0 +0x800009c 16 0 0 +0x80000ac 8 0 0 +0x80000b4 8 0 0 0x080000c0 0x080000c0 +0x80000bc 4 0 0 +0x80000c0 16 0 0 +0x80000d0 8 0 0 0x080000c0 0x080000c0 +0x80000d8 4 0 0 +0x80000dc 8 0 0 0x080000c0 0x080000c0 +0x80000e4 4 0 0 EOF EXPECT_ERR=< CODE -> 0x8000058 sym.function_0 reloc..data.80000c4+24 0x80000dc -> CODE -> 0x8000074 sym.function_1 reloc..data.80000c4+80 0x8000114 -> CODE -> 0x80000c0 reloc..data + addr size traced ninstr jump fail fcns calls xrefs +------------------------------------------------------------------------------------ +0x8000040 24 0 0 +0x8000058 12 0 0 0x08000040 0x08000040 +0x8000064 16 0 0 +0x8000074 12 0 0 0x08000040 0x08000040 +0x8000080 16 0 0 +0x80000ac 20 0 0 +0x80000c0 32 0 0 0xffffffffffffffff 0xffffffffffffffff +0x80000e0 56 0 0 +0x8000108 16 0 0 EOF EXPECT_ERR=< Date: Thu, 15 Jan 2026 17:24:38 +0100 Subject: [PATCH 153/334] Continue interpretation after an invalid basic block --- librz/include/rz_th.h | 1 + librz/inquiry/inquiry.c | 16 ++++++++++++++-- librz/util/thread_queue.c | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/librz/include/rz_th.h b/librz/include/rz_th.h index aedbcf24a84..9a4b2109e49 100644 --- a/librz/include/rz_th.h +++ b/librz/include/rz_th.h @@ -104,6 +104,7 @@ RZ_API bool rz_th_queue_is_full(RZ_NONNULL RzThreadQueue *queue); RZ_API size_t rz_th_queue_size(RZ_NONNULL RzThreadQueue *queue); RZ_API void rz_th_queue_close_when_empty(RZ_NONNULL RzThreadQueue *queue); RZ_API void rz_th_queue_close(RZ_NONNULL RzThreadQueue *queue); +RZ_API void rz_th_queue_open(RZ_NONNULL RzThreadQueue *queue); RZ_API bool rz_th_queue_is_closed(RZ_NONNULL RzThreadQueue *queue); RZ_API RZ_OWN RzAtomicBool *rz_atomic_bool_new(bool value); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 8ddbf2a22f7..4da1543d85f 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -278,6 +278,11 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent } do { + bool bb_decode_failed = false; + // Clear queues from any left overs of previous runs. + rz_list_free(rz_th_queue_pop_all(iset->il_queue)); + rz_list_free(rz_th_queue_pop_all(iset->addr_queue)); + // Dispatch prototype interpreter into a thread. RZ_LOG_DEBUG("INQUIRY: Start main interpretation thread.\n"); interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); @@ -321,14 +326,16 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", *addr); // Signal interpreter the lifting failed. rz_atomic_bool_set(is_running, false); + rz_th_queue_close(iset->addr_queue); rz_th_queue_close(iset->il_queue); + bb_decode_failed = true; break; } rz_analysis_add_bb(core->analysis, *addr, bb_size); RZ_LOG_DEBUG("INQUIRY: Send IL result: %p.\n", bb); rz_pvector_push(il_cache, bb); // TODO: Free unused if too big. - rz_th_queue_push(il_queue, bb, true); + rz_th_queue_push(iset->il_queue, bb, true); } } @@ -379,9 +386,14 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent rz_th_wait(interpr_th); return_code = rz_th_get_retv(interpr_th); rz_th_free(interpr_th); - if (!return_code) { + if (!return_code && !bb_decode_failed) { RZ_LOG_ERROR("Interpreter failed with an error. Abort.\n"); break; + } else if (bb_decode_failed) { + // Open queue again, so the interpretation can start at another + // call target again. + rz_th_queue_open(iset->addr_queue); + rz_th_queue_open(iset->il_queue); } // At this point the interpreter is finished and returned. diff --git a/librz/util/thread_queue.c b/librz/util/thread_queue.c index 8034e38c701..1cb833acff7 100644 --- a/librz/util/thread_queue.c +++ b/librz/util/thread_queue.c @@ -143,6 +143,21 @@ RZ_API void rz_th_queue_close_when_empty(RZ_NONNULL RzThreadQueue *queue) { rz_th_lock_leave(queue->data_lock); } +/** + * \brief Opens a RzThreadQueue (allows to read/write data). + * + * \param queue The RzThreadQueue to close + */ +RZ_API void rz_th_queue_open(RZ_NONNULL RzThreadQueue *queue) { + rz_return_if_fail(queue); + + rz_th_lock_enter(queue->data_lock); + if (!queue->closed) { + queue->closed = false; + } + rz_th_lock_leave(queue->data_lock); +} + /** * \brief Closes a RzThreadQueue (once closed you cannot read/write data). * From 5dc206ee3e7d13fbdfd941ea23873967d34f9d01 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 15 Jan 2026 18:20:20 +0100 Subject: [PATCH 154/334] Scan sections for next targets. --- librz/arch/xrefs.c | 38 +++++++------- librz/include/rz_analysis.h | 2 +- librz/include/rz_inquiry.h | 2 +- librz/include/rz_inquiry/rz_interpreter.h | 2 +- librz/inquiry/inquiry.c | 61 +++++++++++------------ librz/inquiry/interpreter/interpreter.c | 2 +- 6 files changed, 55 insertions(+), 52 deletions(-) diff --git a/librz/arch/xrefs.c b/librz/arch/xrefs.c index 8935c6992bf..651c3075957 100644 --- a/librz/arch/xrefs.c +++ b/librz/arch/xrefs.c @@ -314,24 +314,25 @@ RZ_API const char *rz_analysis_ref_type_tostring(RzAnalysisXRefType t) { return "unknown"; } -int is_in_x_map(const ut64 *call_target, const RzIOMap *map, void *user) { - return (map->perm & RZ_PERM_X && rz_itv_contain(map->itv, *call_target)) ? 0 : -1; +int addr_at_aligned_x_addr(const ut64 *addr, const RzBinSection *sec, void *user) { + return ((*addr & 3) == 0 && RZ_BETWEEN(sec->vaddr, *addr, sec->vaddr + sec->size)) ? 0 : -1; } /** * \brief Returns all targets of call instructions within the \p boundaries. * NOTE: This function disassembles all instructions within the boundaries and checks for calls. * It DOES NOT use the existing xrefs. + * NOTE: Jumps includes calls. * * \param analysis The analysis plugin. - * \param maps The IO maps to disassemble to find call instructions. - * \param call_targets The found call targets of all disassemble calls. + * \param sections The RzBinSections to disassemble to find jump/call instructions. + * \param jump_targets The found jump targets of all disassemble jumps. * NOTE: They can point outside of \p maps! * * \return True in case of success, false otherwise. */ -RZ_API bool rz_analysis_get_all_call_targets(RzAnalysis *analysis, const RzList /**/ *maps, RZ_NONNULL RZ_OUT RzSetU *call_targets) { - rz_return_val_if_fail(analysis && analysis->cur && maps && call_targets, false); +RZ_API bool rz_analysis_get_all_jmp_targets(RzAnalysis *analysis, const RzPVector /**/ *sections, RZ_NONNULL RZ_OUT RzSetU *jump_targets) { + rz_return_val_if_fail(analysis && analysis->cur && sections && jump_targets, false); int buf_size = (analysis->cur->bits / 8) * 16; ut8 *buf = RZ_NEWS0(ut8, buf_size); if (!buf_size || !buf) { @@ -339,12 +340,14 @@ RZ_API bool rz_analysis_get_all_call_targets(RzAnalysis *analysis, const RzList } RzAnalysisOp op = { 0 }; - RzIOMap *map; - RzListIter *it; - rz_list_foreach (maps, it, map) { - ut64 off = 0; - ut64 addr = map->itv.addr + off; - while (map->itv.addr + off < map->itv.addr + map->itv.size) { + void **it; + rz_pvector_foreach (sections, it) { + RzBinSection *sec = *it; + if (!(sec->perm & RZ_PERM_X)) { + continue; + } + ut64 addr = sec->vaddr; + while (addr < sec->vaddr + sec->size) { if (analysis->iob.read_at(analysis->iob.io, addr, buf, buf_size) == 0) { RZ_LOG_WARN("Failed to read memory at 0x%" PFMT64x " size: %u.\n", addr, buf_size); rz_analysis_op_fini(&op); @@ -352,12 +355,13 @@ RZ_API bool rz_analysis_get_all_call_targets(RzAnalysis *analysis, const RzList } if (rz_analysis_op(analysis, &op, addr, buf, buf_size, RZ_ANALYSIS_OP_MASK_BASIC) <= 0) { rz_analysis_op_fini(&op); - break; + addr += op.size; + continue; } - // Only add call targets going to executable regions. - if (rz_analysis_op_is_direct_call(&op) && rz_list_find(maps, &op.jump, (RzListComparator)is_in_x_map, NULL)) { - RZ_LOG_DEBUG("Add call target 0x%" PFMT64x " -> 0x%" PFMT64x "\n", op.addr, op.jump); - rz_set_u_add(call_targets, op.jump); + // Only add jump targets going to executable regions. + if (rz_analysis_op_is_direct_call(&op) && op.jump != UT64_MAX && rz_pvector_find(sections, &op.jump, (RzListComparator)addr_at_aligned_x_addr, NULL)) { + RZ_LOG_DEBUG("Add jump target 0x%" PFMT64x " -> 0x%" PFMT64x "\n", op.addr, op.jump); + rz_set_u_add(jump_targets, op.jump); } addr += op.size; rz_analysis_op_fini(&op); diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 948080b8025..cb88e4ef6c4 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -1619,7 +1619,7 @@ RZ_API bool rz_analysis_xrefs_set(RzAnalysis *analysis, ut64 from, ut64 to, RzAn RZ_API bool rz_analysis_xrefs_deln(RzAnalysis *analysis, ut64 from, ut64 to, RzAnalysisXRefType type); RZ_API bool rz_analysis_xref_del(RzAnalysis *analysis, ut64 from, ut64 to); -RZ_API bool rz_analysis_get_all_call_targets(RzAnalysis *analysis, const RzList /**/ *maps, RZ_NONNULL RZ_OUT RzSetU *call_targets); +RZ_API bool rz_analysis_get_all_jmp_targets(RzAnalysis *analysis, const RzPVector /**/ *sections, RZ_NONNULL RZ_OUT RzSetU *jump_targets); /* var.c */ RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_set_var( diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index eb1134f55b5..1284cd08a1f 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -35,7 +35,7 @@ RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *a); RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr, RZ_NULLABLE RZ_OUT size_t *bb_size); -RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzList /**/ *allowed_io_maps); +RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzPVector /**/ *allowed_segments); RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *entry_points); diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 8b4c38cb759..a9b56828e77 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -85,7 +85,7 @@ typedef union { typedef bool (*RzInterpreterYieldFilter)(const void *element, const void *filter_data); typedef struct { - RzList /**/ *io_boundaries; + RzPVector /**/ *io_boundaries; } RzInterpreterYieldFilterData; /** diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 4da1543d85f..487880461eb 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -61,13 +61,14 @@ RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OW return false; } -RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzList /**/ *allowed_io_maps) { - rz_return_val_if_fail(xref_to_addr && allowed_io_maps, false); - const RzIOMap *map; - RzListIter *it; - rz_list_foreach (allowed_io_maps, it, map) { - ut64 start = map->itv.addr; - ut64 end = map->itv.addr + map->itv.size; +RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzPVector /**/ *allowed_segments) { + rz_return_val_if_fail(xref_to_addr && allowed_segments, false); + void **it; + rz_pvector_foreach (allowed_segments, it) { + const RzBinSection *sec = *it; + + ut64 start = sec->vaddr; + ut64 end = start + sec->size; if (RZ_BETWEEN(start, *xref_to_addr, end)) { return true; } @@ -102,13 +103,13 @@ static void handle_io_request(RzCore *core, RzPVector /**/ *il_mems, rz_str_bool(io_res->req_ok)); } -static bool setup_queues(RzIO *io, +static bool setup_queues(RzCore *core, RZ_OUT RzThreadQueue **il_queue, RZ_OUT RzThreadQueue **io_request_q, RZ_OUT RzThreadQueue **io_result_q, RZ_OUT RzThreadQueue **addr_queue, RZ_OUT HtUP **yield_queues) { - RzList *boundaries = NULL; + RzPVector /**/ *boundaries = NULL; RzInterpreterYieldQueue *yield_queue = NULL; // The queue to pass the Effects to the interpreter. // This is only one queue for the prototype. @@ -139,8 +140,7 @@ static bool setup_queues(RzIO *io, // The prototype generates constant xrefs. // So the filter checks the generated xrefs, if they are within the IO map // boundaries. - RzInterval iv = { .addr = 0, .size = UT64_MAX }; - boundaries = rz_io_get_boundaries_all_io_maps(io, iv); + boundaries = rz_bin_object_get_sections(core->bin->cur->o); if (!boundaries) { goto error_free; } @@ -192,11 +192,11 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent RzThread *interpr_th = NULL; RzBuffer *io_buf = rz_buf_new_with_io(&core->analysis->iob); RzAnalysisILVM *analysis_vm = NULL; - RzSetU *call_targets = rz_set_u_new(); + RzSetU *jmp_targets = rz_set_u_new(); rz_cons_push(); - if (!setup_queues(core->io, &il_queue, &io_request_q, &io_result_q, &addr_queue, &yield_queues)) { + if (!setup_queues(core, &il_queue, &io_request_q, &io_result_q, &addr_queue, &yield_queues)) { return_code = false; goto error_free; } @@ -206,18 +206,17 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent // of the pointers. il_cache = rz_pvector_new((RzPVectorFree)rz_interpreter_il_bb_free); - RzInterval iv = { .addr = 0, .size = UT64_MAX }; - RzList *boundaries = rz_io_get_boundaries_all_io_maps(core->io, iv); + RzPVector /**/ *boundaries = rz_bin_object_get_sections(core->bin->cur->o); if (!boundaries) { goto error_free; } - if (!rz_analysis_get_all_call_targets(core->analysis, boundaries, call_targets)) { + if (!rz_analysis_get_all_jmp_targets(core->analysis, boundaries, jmp_targets)) { RZ_LOG_ERROR("Failed to get call targets.\n"); return_code = false; goto error_free; } - rz_list_free(boundaries); - RZ_LOG_DEBUG("Total call targets in binary: %" PFMT32d "\n", rz_set_u_size(call_targets)); + rz_pvector_free(boundaries); + RZ_LOG_DEBUG("Total call targets in binary: %" PFMT32d "\n", rz_set_u_size(jmp_targets)); // Initialize the abstract state with the architecture's registers. if (!core->analysis->cur->il_config) { @@ -375,7 +374,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent rz_atomic_bool_set(is_running, false); break; } - // TODO: Currently we can't classify calls as such. + // TODO: Currently we can't classify jumps/calls as such. rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, xref->type); RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)); } @@ -391,27 +390,27 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent break; } else if (bb_decode_failed) { // Open queue again, so the interpretation can start at another - // call target again. + // jump target again. rz_th_queue_open(iset->addr_queue); rz_th_queue_open(iset->il_queue); } // At this point the interpreter is finished and returned. // Now we need to check for executable regions it did not cover. - // For this we simply delete all call targets from our set, which point + // For this we simply delete all jump targets from our set, which point // into the already handled basic blocks. // Then add a few addresses as new entry points. - // The addresses we add are call targets from call instructions in the binary. + // The addresses we add are jump targets from jump instructions in the binary. { rz_vector_clear(entry_points); - RzVector *covered_call_targets = rz_vector_new(sizeof(ut64), NULL, NULL); - RzIterator *ct_iter = rz_set_u_as_iter(call_targets); + RzVector *covered_jump_targets = rz_vector_new(sizeof(ut64), NULL, NULL); + RzIterator *ct_iter = rz_set_u_as_iter(jmp_targets); size_t x = 0; ut64 *ct; rz_iterator_foreach(ct_iter, ct) { RzList *containing_blocks = rz_analysis_get_blocks_in(core->analysis, *ct); if (rz_list_length(containing_blocks) != 0) { - rz_vector_push(covered_call_targets, ct); + rz_vector_push(covered_jump_targets, ct); rz_list_free(containing_blocks); continue; } @@ -427,14 +426,14 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent rz_iterator_free(ct_iter); ut64 *ep; - rz_vector_foreach (covered_call_targets, ep) { - // Delete the selected ones from the call target set. + rz_vector_foreach (covered_jump_targets, ep) { + // Delete the selected ones from the jump target set. // So they are not requested again. - rz_set_u_delete(call_targets, *ep); + rz_set_u_delete(jmp_targets, *ep); } - rz_vector_free(covered_call_targets); + rz_vector_free(covered_jump_targets); - RZ_LOG_DEBUG("Call targets left: %" PFMT32d "\n", rz_set_u_size(call_targets)); + RZ_LOG_DEBUG("jump targets left: %" PFMT32d "\n", rz_set_u_size(jmp_targets)); } } while (!rz_vector_empty(entry_points)); @@ -446,7 +445,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent error_free: RZ_LOG_DEBUG("INQUIRY: Close queues\n"); rz_vector_free(entry_points); - rz_set_u_free(call_targets); + rz_set_u_free(jmp_targets); rz_buf_free(io_buf); rz_analysis_il_vm_free(analysis_vm); rz_th_queue_close(il_queue); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index e9ecfdfc087..8203a796123 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -39,7 +39,7 @@ RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYiel rz_th_queue_free(yield_queue->yield_queue); } if (yield_queue->filter_data && yield_queue->filter_data->io_boundaries) { - rz_list_free(yield_queue->filter_data->io_boundaries); + rz_pvector_free(yield_queue->filter_data->io_boundaries); } free(yield_queue->filter_data); free(yield_queue); From 4319814ef04b295d45904d3f91ad57411d304e36 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 16 Jan 2026 17:36:39 +0100 Subject: [PATCH 155/334] Change order of arguments after rebase --- librz/inquiry/interpreter/p/interpreter_prototype.c | 2 +- librz/inquiry/interpreter/prototype/eval.c | 2 +- librz/inquiry/interpreter/prototype/eval_pure.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index d98b21ca47d..62f37ba9f2d 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -85,7 +85,7 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_poin } // The RzArch plugin defined a default value for this global. RzBitVector *default_val = rz_il_value_to_bv(il_var->val); - rz_bv_copy(default_val, AD(av->abstr_data)->bv); + rz_bv_copy(AD(av->abstr_data)->bv, default_val); rz_bv_free(default_val); } } diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index add164f96f5..780c669ef68 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -58,7 +58,7 @@ bool report_xref_yield( void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) { rz_return_if_fail(dst->bv && src->bv); rz_bv_cast_inplace(dst->bv, rz_bv_len(src->bv), false); - rz_bv_copy(src->bv, dst->bv); + rz_bv_copy(dst->bv, src->bv); dst->is_concrete = src->is_concrete; } diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index d412d67df57..8b79664e8ae 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -103,7 +103,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( } case RZ_IL_OP_BITV: rz_bv_cast_inplace(out->bv, rz_bv_len(pure->op.bitv.value), false); - rz_bv_copy(pure->op.bitv.value, out->bv); + rz_bv_copy(out->bv, pure->op.bitv.value); out->is_concrete = true; break; case RZ_IL_OP_APPEND: { From 979f98bce4558fb41376e93ac09820c51106235d Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 16 Jan 2026 20:46:09 +0100 Subject: [PATCH 156/334] Fix queue open --- librz/util/thread_queue.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/librz/util/thread_queue.c b/librz/util/thread_queue.c index 1cb833acff7..eeb6633dab7 100644 --- a/librz/util/thread_queue.c +++ b/librz/util/thread_queue.c @@ -152,9 +152,7 @@ RZ_API void rz_th_queue_open(RZ_NONNULL RzThreadQueue *queue) { rz_return_if_fail(queue); rz_th_lock_enter(queue->data_lock); - if (!queue->closed) { - queue->closed = false; - } + queue->closed = false; rz_th_lock_leave(queue->data_lock); } From 7549ace7905f82eafe22e38346327e0104c5d11d Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 16 Jan 2026 21:08:43 +0100 Subject: [PATCH 157/334] Bug fixes --- librz/inquiry/inquiry.c | 6 +++++- librz/inquiry/interpreter/interpreter.c | 20 ++++++++++++++++---- librz/inquiry/interpreter/prototype/eval.c | 12 +++++++----- librz/inquiry/interpreter/prototype/eval.h | 4 ++-- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 487880461eb..6e8fb2a23dd 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -381,6 +381,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent } } + rz_th_queue_close(iset->addr_queue); + rz_th_queue_close(iset->il_queue); + RZ_LOG_DEBUG("INQUIRY: Wait for join\n"); rz_th_wait(interpr_th); return_code = rz_th_get_retv(interpr_th); @@ -422,9 +425,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent break; } } + rz_iterator_free(ct_iter); rz_interpreter_set_add_entry_points(iset, entry_points); - rz_iterator_free(ct_iter); ut64 *ep; rz_vector_foreach (covered_jump_targets, ep) { // Delete the selected ones from the jump target set. @@ -453,6 +456,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent rz_th_queue_close(io_result_q); if (!iset) { + // Ownership of all those objects wasn't yet passed to the iset. rz_th_queue_free(addr_queue); rz_th_queue_free(il_queue); rz_th_queue_free(io_request_q); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 8203a796123..73b10598b16 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -282,6 +282,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { ut64 entry_point; rz_vector_pop_front(iset->entry_points, &entry_point); if (!plugin->init_state(iset->state, entry_point, plugin_data)) { + rz_warn_if_reached(); goto pre_loop_error; } RzInterpreterAbstrState *in_state = iset->state; @@ -294,6 +295,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { rz_th_queue_push(iset->addr_queue, &entry_point, true); const RzInterpreterILBB *il_bb = NULL; if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { + rz_warn_if_reached(); goto pre_loop_error; } @@ -301,6 +303,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { succ_states = rz_vector_new(sizeof(SuccessorState), NULL, NULL); reachable_states = rz_set_u_new(); if (!tmp_succ_addr || !succ_states || !il_bb || !reachable_states) { + rz_warn_if_reached(); goto pre_loop_error; } ut64 _addr = 0; @@ -309,6 +312,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { while (rz_atomic_bool_get(iset->is_running_flag)) { // Evaluate the effect on the input state. if (!plugin->eval(in_state, il_bb, iset->yield_queues, iset->io_request, iset->io_result, plugin_data)) { + RZ_LOG_DEBUG("Eval failed\n"); goto in_loop_error; } // The input state was (almost always) manipulated by eval(). Rename to clarify. @@ -327,12 +331,16 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // Determine the successor effects to evaluate. // Only newly reached states are allowed to add successors. if (new_state_reached) { - plugin->state_as_str(out_state, state_str, plugin_data); - char *s = rz_strbuf_drain_nofree(state_str); - RZ_LOG_DEBUG("%s", s); - free(s); + // Debug printing whole state of VM. + // + // plugin->state_as_str(out_state, state_str, plugin_data); + // char *s = rz_strbuf_drain_nofree(state_str); + // RZ_LOG_DEBUG("%s", s); + // free(s); + // Determine successors and increase the reference counts for the current out state. if (!plugin->successors(out_state, tmp_succ_addr, plugin_data)) { + rz_warn_if_reached(); goto in_loop_error; } // It is possible that the successor function doesn't add successors. @@ -369,9 +377,11 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { in_hash = next.in_state_hash; #endif if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { + rz_warn_if_reached(); goto in_loop_error; } if (!plugin->set_pc(in_state, next.addr, plugin_data)) { + rz_warn_if_reached(); // Some error occurred lifting this basic block. Or updating the PC. // Abort execution. goto in_loop_error; @@ -391,10 +401,12 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { return success; in_loop_error: + RZ_LOG_DEBUG("in_loop_error"); success = false; goto loop_cleanup; pre_loop_error: + RZ_LOG_DEBUG("pre_loop_error"); success = false; goto loop_cleanup; diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 780c669ef68..a09acaf9c04 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -56,7 +56,7 @@ bool report_xref_yield( } void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) { - rz_return_if_fail(dst->bv && src->bv); + rz_return_if_fail(dst && src && dst->bv && src->bv); rz_bv_cast_inplace(dst->bv, rz_bv_len(src->bv), false); rz_bv_copy(dst->bv, src->bv); dst->is_concrete = src->is_concrete; @@ -86,10 +86,12 @@ void write_var_to_state(RzInterpreterAbstrState *state, if (kind == RZ_IL_VAR_KIND_GLOBAL) { RZ_LOG_WARN("New global variable created: 0x%" PFMT64x "\n", var_id) } - av = RZ_NEW(RzInterpreterAbstrVal); + av = RZ_NEW0(RzInterpreterAbstrVal); + ht_up_insert(ht_vals, var_id, av); + } + if (!av->abstr_data) { av->kind = RZ_INTERPRETER_ABSTRACTION_CONST; av->abstr_data = adata_new(); - ht_up_insert(ht_vals, var_id, av); } copy_abstr_data(av->abstr_data, data); } @@ -114,7 +116,7 @@ bool read_var_from_state(RzInterpreterAbstrState *state, break; } RzInterpreterAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); - if (!av) { + if (!av || !av->abstr_data) { // Variable doesn't exist. // This should never happen and is a bug. rz_warn_if_reached(); @@ -208,7 +210,7 @@ bool set_pc(RzInterpreterAbstrState *state, ut64 pc, void *plugin_data) { rz_return_val_if_fail(state, false); AD(state->pc->abstr_data)->is_concrete = true; - RZ_LOG_DEBUG("Prototype: set_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x "(Concrete)\n", + RZ_LOG_DEBUG("Prototype: set_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (Concrete)\n", rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), pc); return rz_bv_set_from_ut64(AD(state->pc->abstr_data)->bv, pc); diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 0eb75d83151..b83b37f7e83 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -51,14 +51,14 @@ typedef struct { * \brief Creates abstract data on the heap with the given bit vector. */ static inline RZ_OWN ProtoIntrprAbstrData *adata_from_bv(const RzBitVector *bv) { - ProtoIntrprAbstrData *ad = RZ_NEW(ProtoIntrprAbstrData); + ProtoIntrprAbstrData *ad = RZ_NEW0(ProtoIntrprAbstrData); ad->is_concrete = true; ad->bv = rz_bv_dup(bv); return ad; } static inline RZ_OWN ProtoIntrprAbstrData *adata_new() { - ProtoIntrprAbstrData *ad = RZ_NEW(ProtoIntrprAbstrData); + ProtoIntrprAbstrData *ad = RZ_NEW0(ProtoIntrprAbstrData); ad->is_concrete = false; ad->bv = rz_bv_new(BV_STACK_MAX_SIZE); return ad; From 1721ecc10f2a0f4621a56cb59395e2bceca935d1 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 16 Jan 2026 23:01:18 +0100 Subject: [PATCH 158/334] More bugs fixes --- librz/arch/xrefs.c | 32 +++++++---- librz/include/rz_analysis.h | 2 +- librz/inquiry/inquiry.c | 74 +++++++++++++++++-------- librz/inquiry/interpreter/interpreter.c | 5 +- test/db/inquiry/interpreter/xrefs | 36 ------------ 5 files changed, 77 insertions(+), 72 deletions(-) diff --git a/librz/arch/xrefs.c b/librz/arch/xrefs.c index 651c3075957..5dd9fa957c3 100644 --- a/librz/arch/xrefs.c +++ b/librz/arch/xrefs.c @@ -318,22 +318,32 @@ int addr_at_aligned_x_addr(const ut64 *addr, const RzBinSection *sec, void *user return ((*addr & 3) == 0 && RZ_BETWEEN(sec->vaddr, *addr, sec->vaddr + sec->size)) ? 0 : -1; } +static bool read_up_to(RzAnalysis *analysis, ut64 addr, ut8 *buf, size_t buf_size) { + rz_mem_set_num(buf, buf_size, 0); + do { + if (analysis->iob.read_at(analysis->iob.io, addr, buf, buf_size) > 0) { + return true; + } + buf_size--; + } while (buf_size > 0); + return false; +} + /** - * \brief Returns all targets of call instructions within the \p boundaries. + * \brief Returns all targets of call instructions within the \p sections. * NOTE: This function disassembles all instructions within the boundaries and checks for calls. * It DOES NOT use the existing xrefs. - * NOTE: Jumps includes calls. * * \param analysis The analysis plugin. - * \param sections The RzBinSections to disassemble to find jump/call instructions. - * \param jump_targets The found jump targets of all disassemble jumps. + * \param sections The RzBinSections to disassemble to find call instructions. + * \param call_targets The found call targets of all disassemble calls. * NOTE: They can point outside of \p maps! * * \return True in case of success, false otherwise. */ -RZ_API bool rz_analysis_get_all_jmp_targets(RzAnalysis *analysis, const RzPVector /**/ *sections, RZ_NONNULL RZ_OUT RzSetU *jump_targets) { - rz_return_val_if_fail(analysis && analysis->cur && sections && jump_targets, false); - int buf_size = (analysis->cur->bits / 8) * 16; +RZ_API bool rz_analysis_get_all_call_targets(RzAnalysis *analysis, const RzPVector /**/ *sections, RZ_NONNULL RZ_OUT RzSetU *call_targets) { + rz_return_val_if_fail(analysis && analysis->cur && sections && call_targets, false); + size_t buf_size = (analysis->cur->bits / 8) * 16; ut8 *buf = RZ_NEWS0(ut8, buf_size); if (!buf_size || !buf) { return false; @@ -348,8 +358,8 @@ RZ_API bool rz_analysis_get_all_jmp_targets(RzAnalysis *analysis, const RzPVecto } ut64 addr = sec->vaddr; while (addr < sec->vaddr + sec->size) { - if (analysis->iob.read_at(analysis->iob.io, addr, buf, buf_size) == 0) { - RZ_LOG_WARN("Failed to read memory at 0x%" PFMT64x " size: %u.\n", addr, buf_size); + if (!read_up_to(analysis, addr, buf, buf_size)) { + RZ_LOG_WARN("Failed to read memory at 0x%" PFMT64x " size: %" PFMTSZu ".\n", addr, buf_size); rz_analysis_op_fini(&op); break; } @@ -360,8 +370,8 @@ RZ_API bool rz_analysis_get_all_jmp_targets(RzAnalysis *analysis, const RzPVecto } // Only add jump targets going to executable regions. if (rz_analysis_op_is_direct_call(&op) && op.jump != UT64_MAX && rz_pvector_find(sections, &op.jump, (RzListComparator)addr_at_aligned_x_addr, NULL)) { - RZ_LOG_DEBUG("Add jump target 0x%" PFMT64x " -> 0x%" PFMT64x "\n", op.addr, op.jump); - rz_set_u_add(jump_targets, op.jump); + RZ_LOG_DEBUG("Add call target 0x%" PFMT64x " -> 0x%" PFMT64x "\n", op.addr, op.jump); + rz_set_u_add(call_targets, op.jump); } addr += op.size; rz_analysis_op_fini(&op); diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index cb88e4ef6c4..2d9725a3029 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -1619,7 +1619,7 @@ RZ_API bool rz_analysis_xrefs_set(RzAnalysis *analysis, ut64 from, ut64 to, RzAn RZ_API bool rz_analysis_xrefs_deln(RzAnalysis *analysis, ut64 from, ut64 to, RzAnalysisXRefType type); RZ_API bool rz_analysis_xref_del(RzAnalysis *analysis, ut64 from, ut64 to); -RZ_API bool rz_analysis_get_all_jmp_targets(RzAnalysis *analysis, const RzPVector /**/ *sections, RZ_NONNULL RZ_OUT RzSetU *jump_targets); +RZ_API bool rz_analysis_get_all_call_targets(RzAnalysis *analysis, const RzPVector /**/ *sections, RZ_NONNULL RZ_OUT RzSetU *jump_targets); /* var.c */ RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_set_var( diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 6e8fb2a23dd..1ff7320c3da 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -68,7 +68,7 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL co const RzBinSection *sec = *it; ut64 start = sec->vaddr; - ut64 end = start + sec->size; + ut64 end = start + sec->vsize; if (RZ_BETWEEN(start, *xref_to_addr, end)) { return true; } @@ -173,6 +173,33 @@ static bool setup_queues(RzCore *core, return false; } +static bool get_call_targets(RzCore *core, RzSetU *call_targets) { + RzPVector /**/ *sections = rz_bin_object_get_sections(core->bin->cur->o); + if (!sections) { + return false; + } + RzVector *non_x_idx = rz_vector_new(sizeof(size_t), NULL, NULL); + void **it; + size_t i; + rz_pvector_enumerate (sections, it, i) { + RzBinSection *sec = *it; + if (!(sec->perm & RZ_PERM_X)) { + rz_vector_push(non_x_idx, &i); + } + } + size_t *j; + rz_vector_foreach_prev (non_x_idx, j) { + rz_pvector_remove_at(sections, *j); + } + rz_vector_free(non_x_idx); + if (!rz_analysis_get_all_call_targets(core->analysis, sections, call_targets)) { + RZ_LOG_ERROR("Failed to get call targets.\n"); + return false; + } + rz_pvector_free(sections); + return true; +} + /** * A function to call the prototype interpreter. * Usually these tasks will be split between different caches and yield consumers. @@ -192,7 +219,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent RzThread *interpr_th = NULL; RzBuffer *io_buf = rz_buf_new_with_io(&core->analysis->iob); RzAnalysisILVM *analysis_vm = NULL; - RzSetU *jmp_targets = rz_set_u_new(); + RzSetU *call_targets = rz_set_u_new(); rz_cons_push(); @@ -206,17 +233,12 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent // of the pointers. il_cache = rz_pvector_new((RzPVectorFree)rz_interpreter_il_bb_free); - RzPVector /**/ *boundaries = rz_bin_object_get_sections(core->bin->cur->o); - if (!boundaries) { - goto error_free; - } - if (!rz_analysis_get_all_jmp_targets(core->analysis, boundaries, jmp_targets)) { + if (!get_call_targets(core, call_targets)) { RZ_LOG_ERROR("Failed to get call targets.\n"); return_code = false; goto error_free; } - rz_pvector_free(boundaries); - RZ_LOG_DEBUG("Total call targets in binary: %" PFMT32d "\n", rz_set_u_size(jmp_targets)); + RZ_LOG_DEBUG("Total call targets in binary: %" PFMT32d "\n", rz_set_u_size(call_targets)); // Initialize the abstract state with the architecture's registers. if (!core->analysis->cur->il_config) { @@ -277,6 +299,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent } do { + bool user_sent_signal = false; bool bb_decode_failed = false; // Clear queues from any left overs of previous runs. rz_list_free(rz_th_queue_pop_all(iset->il_queue)); @@ -303,6 +326,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent while (rz_atomic_bool_get(is_running)) { if (rz_th_terminated(interpr_th) || rz_cons_is_breaked()) { rz_atomic_bool_set(is_running, false); + user_sent_signal = true; break; } @@ -325,6 +349,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", *addr); // Signal interpreter the lifting failed. rz_atomic_bool_set(is_running, false); + rz_th_queue_close(io_request_q); + rz_th_queue_close(io_result_q); rz_th_queue_close(iset->addr_queue); rz_th_queue_close(iset->il_queue); bb_decode_failed = true; @@ -381,6 +407,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent } } + rz_th_queue_close(io_request_q); + rz_th_queue_close(io_result_q); rz_th_queue_close(iset->addr_queue); rz_th_queue_close(iset->il_queue); @@ -388,15 +416,18 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent rz_th_wait(interpr_th); return_code = rz_th_get_retv(interpr_th); rz_th_free(interpr_th); - if (!return_code && !bb_decode_failed) { - RZ_LOG_ERROR("Interpreter failed with an error. Abort.\n"); + if ((!return_code && !bb_decode_failed) || user_sent_signal) { + if (!user_sent_signal) { + RZ_LOG_ERROR("Interpreter failed with an error. Abort.\n"); + } break; - } else if (bb_decode_failed) { - // Open queue again, so the interpretation can start at another - // jump target again. - rz_th_queue_open(iset->addr_queue); - rz_th_queue_open(iset->il_queue); } + // Open queue again, so the interpretation can start at another + // jump target again. + rz_th_queue_open(io_request_q); + rz_th_queue_open(io_result_q); + rz_th_queue_open(iset->addr_queue); + rz_th_queue_open(iset->il_queue); // At this point the interpreter is finished and returned. // Now we need to check for executable regions it did not cover. @@ -407,7 +438,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent { rz_vector_clear(entry_points); RzVector *covered_jump_targets = rz_vector_new(sizeof(ut64), NULL, NULL); - RzIterator *ct_iter = rz_set_u_as_iter(jmp_targets); + RzIterator *ct_iter = rz_set_u_as_iter(call_targets); size_t x = 0; ut64 *ct; rz_iterator_foreach(ct_iter, ct) { @@ -432,11 +463,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent rz_vector_foreach (covered_jump_targets, ep) { // Delete the selected ones from the jump target set. // So they are not requested again. - rz_set_u_delete(jmp_targets, *ep); + rz_set_u_delete(call_targets, *ep); } rz_vector_free(covered_jump_targets); - - RZ_LOG_DEBUG("jump targets left: %" PFMT32d "\n", rz_set_u_size(jmp_targets)); } } while (!rz_vector_empty(entry_points)); @@ -448,12 +477,13 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent error_free: RZ_LOG_DEBUG("INQUIRY: Close queues\n"); rz_vector_free(entry_points); - rz_set_u_free(jmp_targets); + rz_set_u_free(call_targets); rz_buf_free(io_buf); rz_analysis_il_vm_free(analysis_vm); - rz_th_queue_close(il_queue); rz_th_queue_close(io_request_q); rz_th_queue_close(io_result_q); + rz_th_queue_close(iset->addr_queue); + rz_th_queue_close(iset->il_queue); if (!iset) { // Ownership of all those objects wasn't yet passed to the iset. diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 73b10598b16..c5552c2eadd 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -351,6 +351,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { while (!rz_vector_empty(tmp_succ_addr)) { rz_vector_pop_front(tmp_succ_addr, addr); if (*addr == UT64_MAX || *addr == 0) { + RZ_LOG_DEBUG("interpreter: Quit due to invalid PC.\n"); // Obviously wrong address. goto loop_cleanup; } @@ -401,12 +402,12 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { return success; in_loop_error: - RZ_LOG_DEBUG("in_loop_error"); + RZ_LOG_DEBUG("in_loop_error\n"); success = false; goto loop_cleanup; pre_loop_error: - RZ_LOG_DEBUG("pre_loop_error"); + RZ_LOG_DEBUG("pre_loop_error\n"); success = false; goto loop_cleanup; diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs index 019cc30c89d..fff4f7587f5 100644 --- a/test/db/inquiry/interpreter/xrefs +++ b/test/db/inquiry/interpreter/xrefs @@ -242,26 +242,6 @@ EXPECT=< Date: Sun, 18 Jan 2026 20:59:08 +0100 Subject: [PATCH 159/334] Move 'choose successor' logic into own function. --- librz/include/rz_inquiry/rz_interpreter.h | 4 +- librz/inquiry/inquiry.c | 3 +- librz/inquiry/interpreter/interpreter.c | 51 ++++++++++++++++++++++- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index a9b56828e77..574f72210d6 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -205,6 +205,7 @@ typedef struct { RzThreadQueue /**/ *io_request; ///< The queue for read/write requests to the IO layer. RzThreadQueue /**/ *io_result; ///< The queue for the read/write requests' answers. RzAtomicBool *is_running_flag; ///< Flag for the interpreter thread to toggle when done. + const RzPVector *symbols; ///< Known symbols of a binary. /** * \brief The entry points for the interpreters. * Each address has its lifted IL op in the il_queue at the same index. @@ -238,7 +239,8 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_request, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag, - RZ_NONNULL RZ_OWN RzVector /**/ *entry_points); + RZ_NONNULL RZ_OWN RzVector /**/ *entry_points, + RZ_NONNULL const RzPVector /**/ *symbols); RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset); RZ_API void rz_interpreter_set_add_entry_points(RZ_NONNULL RzInterpreterSet *iset, const RzVector /**/ *entry_points); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 1ff7320c3da..b146e39cde9 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -291,7 +291,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent io_request_q, io_result_q, is_running, - rz_vector_clone(entry_points)); + rz_vector_clone(entry_points), + rz_bin_object_get_symbols(core->bin->cur->o)); if (!iset) { return_code = false; rz_warn_if_reached(); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index c5552c2eadd..1064bed9195 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -195,8 +195,9 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_request, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag, - RZ_NONNULL RZ_OWN RzVector /**/ *entry_points) { - rz_return_val_if_fail(plugin && state && addr_queue && il_queue && yield_queues && io_request && io_result && is_running_flag && entry_points, NULL); + RZ_NONNULL RZ_OWN RzVector /**/ *entry_points, + RZ_NONNULL const RzPVector /**/ *symbols) { + rz_return_val_if_fail(plugin && state && addr_queue && il_queue && yield_queues && io_request && io_result && is_running_flag && entry_points && symbols, NULL); RzInterpreterSet *set = RZ_NEW0(RzInterpreterSet); if (!set) { @@ -211,6 +212,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( set->io_result = io_result; set->is_running_flag = is_running_flag; set->entry_points = entry_points; + set->symbols = symbols; if (state->kinds != (plugin->supported_abstractions & state->kinds)) { RZ_LOG_ERROR("Abstract state doesn't fit to interpreter.\n"); rz_interpreter_set_free(set); @@ -230,6 +232,49 @@ typedef struct { ut64 in_state_hash; } SuccessorState; +static bool choose_next_pc(RzInterpreterSet *iset, + RzInterpreterAbstrState *out_state, + ut64 out_hash, + RzVector *tmp_succ_addr, + RzVector *succ_states, + ut64 *shared_addr_loc, + void *plugin_data) { + // Debug printing whole state of VM. + // + // plugin->state_as_str(out_state, state_str, plugin_data); + // char *s = rz_strbuf_drain_nofree(state_str); + // RZ_LOG_DEBUG("%s", s); + // free(s); + + bool has_succsessor = true; + + // Determine successors and increase the reference counts for the current out state. + if (!iset->plugin->successors(out_state, tmp_succ_addr, plugin_data)) { + rz_warn_if_reached(); + return false; + } + + // It is possible that the successor function doesn't add successors. + // E.g. because the PC is an abstract value. + // In this case the state counts as invalid. + has_succsessor = !rz_vector_empty(tmp_succ_addr); + // Request the successor effects over the queue. + while (!rz_vector_empty(tmp_succ_addr)) { + rz_vector_pop_front(tmp_succ_addr, shared_addr_loc); + if (*shared_addr_loc == UT64_MAX || *shared_addr_loc == 0) { + RZ_LOG_DEBUG("interpreter: Quit due to invalid PC.\n"); + // Obviously wrong address. + return false; + } + SuccessorState ss = { .addr = *shared_addr_loc, .in_state_hash = out_hash }; + // The successors are pushed in the same order into the succ_states + // vector, as they are requested over the addr_queue. + rz_vector_push(succ_states, &ss); + rz_th_queue_push(iset->addr_queue, shared_addr_loc, true); + } + return has_succsessor; +} + /** * Main interpretation. */ @@ -306,6 +351,8 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { rz_warn_if_reached(); goto pre_loop_error; } + + // Shared memory passed via the addr_queue. ut64 _addr = 0; ut64 *addr = &_addr; From 9b6046b8250151deff7d5e2338cc31f2906dd65a Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 20 Jan 2026 00:12:36 +0100 Subject: [PATCH 160/334] RzBinSymbol: Add role attribute to the layout. --- librz/bin/format/mach0/mach0.c | 10 ++++++++++ librz/bin/p/bin_elf.inc | 6 ++++++ librz/include/rz_bin.h | 6 ++++++ 3 files changed, 22 insertions(+) diff --git a/librz/bin/format/mach0/mach0.c b/librz/bin/format/mach0/mach0.c index 0375375e522..c5c6dd5e9ff 100644 --- a/librz/bin/format/mach0/mach0.c +++ b/librz/bin/format/mach0/mach0.c @@ -2028,6 +2028,13 @@ RzPVector /**/ *MACH0_(get_maps)(RzBinFile *bf) { return ret; } +static bool is_linking_related_section(ut64 sec_type) { + return sec_type & S_SYMBOL_STUBS || + sec_type & S_LAZY_SYMBOL_POINTERS || + sec_type & S_NON_LAZY_SYMBOL_POINTERS || + sec_type & S_LAZY_DYLIB_SYMBOL_POINTERS; +} + RzPVector /**/ *MACH0_(get_segments)(RzBinFile *bf) { struct MACH0_(obj_t) *bin = bf->o->bin_obj; if (bin->sections_cache) { @@ -2077,6 +2084,9 @@ RzPVector /**/ *MACH0_(get_segments)(RzBinFile *bf) { s->flags = bin->sects[i].flags & 0xFFFFFF00; // XXX flags s->paddr = (ut64)bin->sects[i].offset; + if (is_linking_related_section(s->type)) { + s->layout.role = RZ_BIN_SECTION_ROLE_LINKING; + } int segment_index = 0; // s->perm =prot2perm (bin->segs[j].initprot); for (j = 0; j < bin->nsegs; j++) { diff --git a/librz/bin/p/bin_elf.inc b/librz/bin/p/bin_elf.inc index 5f32a8132f4..99eb26f2c90 100644 --- a/librz/bin/p/bin_elf.inc +++ b/librz/bin/p/bin_elf.inc @@ -857,6 +857,12 @@ static RzPVector /**/ *sections_obj(ELFOBJ *obj, size_t psize) { ptr->layout.element_size = sizeof(Elf_(Addr)); ptr->layout.count = section->size / sizeof(Elf_(Addr)); } + if (section->type == SHT_RELA || + section->type == SHT_DYNAMIC || + section->type == SHT_DYNSYM || + (section->name && strstr(section->name, ".plt"))) { + ptr->layout.role = RZ_BIN_SECTION_ROLE_LINKING; + } ptr->size = section->type != SHT_NOBITS ? section->size : 0; ptr->vsize = section->size; ptr->paddr = section->offset; diff --git a/librz/include/rz_bin.h b/librz/include/rz_bin.h index 3350ed64bde..d240e0ec816 100644 --- a/librz/include/rz_bin.h +++ b/librz/include/rz_bin.h @@ -632,12 +632,18 @@ typedef struct rz_bin_map_t { #define RZ_BIN_MAX_HANDLED_LAYOUT_SIZE (1024 * 1024 * 2) +typedef enum { + RZ_BIN_SECTION_ROLE_UNSPECIFIED = 0, + RZ_BIN_SECTION_ROLE_LINKING, ///< Section is used for (dynamic) linking. +} RzBinSectionRole; + /** * \brief A repetitive data layout of a section. The type will be applied to * element_size * element_count. */ typedef struct rz_bin_section_format_t { int /* RzAnalysisMetaType */ type; ///< The meta type of the elements. + RzBinSectionRole role; ///< The role a section has. ut64 element_size; ///< Size of an element in this section. size_t count; ///< The number of consecutive elements in this section. } RzBinSectionLayout; From dd55a8b80c1ee442c003ed1cbf202b31c43b1b1c Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 20 Jan 2026 22:40:29 +0100 Subject: [PATCH 161/334] Allow to declare address intervals which should never be executed. This also moves the determination of the next PC into a helper function. --- librz/include/rz_inquiry/rz_interpreter.h | 14 +-- librz/inquiry/inquiry.c | 29 ++++++- librz/inquiry/inquiry_helpers.c | 13 ++- librz/inquiry/interpreter/interpreter.c | 87 +++++++++---------- .../interpreter/p/interpreter_prototype.c | 5 +- 5 files changed, 89 insertions(+), 59 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 574f72210d6..bc28a3b6877 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -98,7 +98,11 @@ typedef struct { RzThreadQueue /**/ *yield_queue; } RzInterpreterYieldQueue; -typedef RzPVector RzInterpreterILBB; +typedef struct { + RzPVector *il_ops; ///< The sequence of IL operations of this basic block. + size_t size; ///< The number of bytes the basic block has. + ut64 bb_addr; ///< The address where the basic block starts. +} RzInterpreterILBB; typedef struct { RzILOpEffect *effect; ///< Vector with all instruction packets of a basic block. @@ -142,7 +146,7 @@ typedef struct { * \brief Evaluates an effect with the mutable state. */ bool (*eval)(RZ_NONNULL RzInterpreterAbstrState *state, - RZ_NONNULL const RzInterpreterILBB *il_op, + RZ_NONNULL const RzInterpreterILBB *il_bb, RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_request, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_result, @@ -205,7 +209,7 @@ typedef struct { RzThreadQueue /**/ *io_request; ///< The queue for read/write requests to the IO layer. RzThreadQueue /**/ *io_result; ///< The queue for the read/write requests' answers. RzAtomicBool *is_running_flag; ///< Flag for the interpreter thread to toggle when done. - const RzPVector *symbols; ///< Known symbols of a binary. + RzVector /**/ *ignored_code; /** * \brief The entry points for the interpreters. * Each address has its lifted IL op in the il_queue at the same index. @@ -214,7 +218,7 @@ typedef struct { RzInterpreterPlugin *plugin; } RzInterpreterSet; -RZ_API void rz_interpreter_il_bb_free(RZ_NULLABLE RZ_OWN RzInterpreterILBB *il_op); +RZ_API void rz_interpreter_il_bb_free(RZ_NULLABLE RZ_OWN RzInterpreterILBB *il_bb); RZ_API void rz_interpreter_insn_pkt_free(RZ_NULLABLE RZ_OWN RzInterpreterInsnPkt *pkt); RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldQueue *yield_queue); @@ -240,7 +244,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag, RZ_NONNULL RZ_OWN RzVector /**/ *entry_points, - RZ_NONNULL const RzPVector /**/ *symbols); + RZ_NONNULL RZ_OWN RzVector /**/ *ignored_code); RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset); RZ_API void rz_interpreter_set_add_entry_points(RZ_NONNULL RzInterpreterSet *iset, const RzVector /**/ *entry_points); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index b146e39cde9..c601d3d2ea1 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -6,13 +6,13 @@ #include #include "rz_analysis.h" +#include "rz_bin.h" #include "rz_config.h" #include "rz_cons.h" #include "rz_il/definitions/mem.h" #include "rz_il/rz_il_vm.h" #include "rz_inquiry/rz_interpreter.h" #include "rz_inquiry_plugins.h" -#include "rz_io.h" #include "rz_th.h" #include "rz_util/rz_bitvector.h" #include "rz_util/rz_buf.h" @@ -200,6 +200,29 @@ static bool get_call_targets(RzCore *core, RzSetU *call_targets) { return true; } +static RzVector /**/ *get_ignored_code_regions( + const RzPVector /**/ *symbols, + RzPVector /**/ *sections) { + void **it; + RzVector *v = rz_vector_new(sizeof(RzInterval), NULL, NULL); + rz_pvector_foreach (sections, it) { + RzBinSection *sec = *it; + if (sec->layout.role == RZ_BIN_SECTION_ROLE_LINKING) { + RzInterval itv = { .addr = sec->vaddr, .size = sec->vsize }; + rz_vector_push(v, &itv); + } + } + rz_pvector_free(sections); + rz_pvector_foreach (symbols, it) { + RzBinSymbol *sym = *it; + if (sym->is_imported) { + RzInterval itv = { .addr = sym->vaddr, .size = sym->size }; + rz_vector_push(v, &itv); + } + } + return v; +} + /** * A function to call the prototype interpreter. * Usually these tasks will be split between different caches and yield consumers. @@ -292,7 +315,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent io_result_q, is_running, rz_vector_clone(entry_points), - rz_bin_object_get_symbols(core->bin->cur->o)); + get_ignored_code_regions( + rz_bin_object_get_symbols(core->bin->cur->o), + rz_bin_object_get_sections(core->bin->cur->o))); if (!iset) { return_code = false; rz_warn_if_reached(); diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c index 95c80c86932..49a184fed1f 100644 --- a/librz/inquiry/inquiry_helpers.c +++ b/librz/inquiry/inquiry_helpers.c @@ -23,10 +23,16 @@ RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana if (!max_read_size || !buf) { goto fail; } - il_bb = rz_pvector_new((RzPVectorFree)rz_interpreter_insn_pkt_free); + + il_bb = RZ_NEW0(RzInterpreterILBB); if (!il_bb) { goto fail; } + il_bb->il_ops = rz_pvector_new((RzPVectorFree)rz_interpreter_insn_pkt_free); + if (!il_bb->il_ops) { + goto fail; + } + il_bb->bb_addr = addr; RZ_LOG_DEBUG("Gen BB:\n"); bool sparc_add_delayed_insn = false; bool changes_cf = true; @@ -52,7 +58,8 @@ RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana RzInterpreterInsnPkt *pkt = RZ_NEW0(RzInterpreterInsnPkt); pkt->effect = op.il_op; pkt->insn_pkt_size = op.size; - rz_pvector_push(il_bb, pkt); + il_bb->size += op.size; + rz_pvector_push(il_bb->il_ops, pkt); // Take ownership of IL op pointer. op.il_op = NULL; if (lifted) { @@ -83,6 +90,6 @@ RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana fail: free(buf); rz_analysis_op_fini(&op); - rz_pvector_free(il_bb); + rz_interpreter_il_bb_free(il_bb); return NULL; } diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 1064bed9195..fdd14fe75ed 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -5,8 +5,8 @@ * \file The API implementation for all analysis interpreters. */ -#include "rz_cons.h" #include "rz_util/rz_assert.h" +#include "rz_util/rz_itv.h" #include #include #include @@ -24,11 +24,12 @@ RZ_API void rz_interpreter_insn_pkt_free(RZ_NULLABLE RZ_OWN RzInterpreterInsnPkt free(pkt); } -RZ_API void rz_interpreter_il_bb_free(RZ_NULLABLE RZ_OWN RzInterpreterILBB *il_op) { - if (!il_op) { +RZ_API void rz_interpreter_il_bb_free(RZ_NULLABLE RZ_OWN RzInterpreterILBB *il_bb) { + if (!il_bb) { return; } - rz_pvector_free(il_op); + rz_pvector_free(il_bb->il_ops); + free(il_bb); } RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldQueue *yield_queue) { @@ -165,6 +166,9 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { if (iset->is_running_flag) { rz_atomic_bool_free(iset->is_running_flag); } + if (iset->ignored_code) { + rz_vector_free(iset->ignored_code); + } if (iset->state) { rz_interpreter_abstr_state_free(iset->state); } @@ -196,8 +200,8 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag, RZ_NONNULL RZ_OWN RzVector /**/ *entry_points, - RZ_NONNULL const RzPVector /**/ *symbols) { - rz_return_val_if_fail(plugin && state && addr_queue && il_queue && yield_queues && io_request && io_result && is_running_flag && entry_points && symbols, NULL); + RZ_NONNULL RZ_OWN RzVector /**/ *ignored_code) { + rz_return_val_if_fail(plugin && state && addr_queue && il_queue && yield_queues && io_request && io_result && is_running_flag && entry_points && ignored_code, NULL); RzInterpreterSet *set = RZ_NEW0(RzInterpreterSet); if (!set) { @@ -212,7 +216,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( set->io_result = io_result; set->is_running_flag = is_running_flag; set->entry_points = entry_points; - set->symbols = symbols; + set->ignored_code = ignored_code; if (state->kinds != (plugin->supported_abstractions & state->kinds)) { RZ_LOG_ERROR("Abstract state doesn't fit to interpreter.\n"); rz_interpreter_set_free(set); @@ -221,6 +225,17 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( return set; } +static bool jumps_ignored_code(const RzVector *v, ut64 jump_target) { + void *it; + rz_vector_foreach (v, it) { + RzInterval *itv = it; + if (rz_itv_contain(*itv, jump_target)) { + return true; + } + } + return false; +} + RZ_API void rz_interpreter_set_add_entry_points(RZ_NONNULL RzInterpreterSet *iset, const RzVector /**/ *entry_points) { rz_return_if_fail(iset && entry_points); rz_vector_clear(iset->entry_points); @@ -237,7 +252,8 @@ static bool choose_next_pc(RzInterpreterSet *iset, ut64 out_hash, RzVector *tmp_succ_addr, RzVector *succ_states, - ut64 *shared_addr_loc, + ut64 *shared_addr, + const RzInterpreterILBB *il_bb, void *plugin_data) { // Debug printing whole state of VM. // @@ -260,17 +276,27 @@ static bool choose_next_pc(RzInterpreterSet *iset, has_succsessor = !rz_vector_empty(tmp_succ_addr); // Request the successor effects over the queue. while (!rz_vector_empty(tmp_succ_addr)) { - rz_vector_pop_front(tmp_succ_addr, shared_addr_loc); - if (*shared_addr_loc == UT64_MAX || *shared_addr_loc == 0) { + rz_vector_pop_front(tmp_succ_addr, shared_addr); + if (*shared_addr == UT64_MAX || *shared_addr == 0) { RZ_LOG_DEBUG("interpreter: Quit due to invalid PC.\n"); // Obviously wrong address. return false; } - SuccessorState ss = { .addr = *shared_addr_loc, .in_state_hash = out_hash }; + if (jumps_ignored_code(iset->ignored_code, *shared_addr)) { + RZ_LOG_DEBUG("interpreter: tried to jump to ignored code region at 0x%" PFMT64x "\n", *shared_addr); + // Ignored code is mostly dynamically linked functions. + // Skip to the next following address after the jump. + *shared_addr = il_bb->bb_addr + il_bb->size; + RZ_LOG_DEBUG("interpreter: Request instead 0x%" PFMT64x "\n", *shared_addr); + } + + SuccessorState ss = { .addr = *shared_addr, .in_state_hash = out_hash }; // The successors are pushed in the same order into the succ_states // vector, as they are requested over the addr_queue. rz_vector_push(succ_states, &ss); - rz_th_queue_push(iset->addr_queue, shared_addr_loc, true); + rz_th_queue_push(iset->addr_queue, shared_addr, true); + // TODO: Race condition: + // Multiple jump targets could overwrite the value in shared_addr before it is read. } return has_succsessor; } @@ -377,41 +403,8 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // Determine the successor effects to evaluate. // Only newly reached states are allowed to add successors. - if (new_state_reached) { - // Debug printing whole state of VM. - // - // plugin->state_as_str(out_state, state_str, plugin_data); - // char *s = rz_strbuf_drain_nofree(state_str); - // RZ_LOG_DEBUG("%s", s); - // free(s); - - // Determine successors and increase the reference counts for the current out state. - if (!plugin->successors(out_state, tmp_succ_addr, plugin_data)) { - rz_warn_if_reached(); - goto in_loop_error; - } - // It is possible that the successor function doesn't add successors. - // E.g. because the PC is an abstract value. - // In this case the state counts as invalid. - new_state_reached = !rz_vector_empty(tmp_succ_addr); - // Request the successor effects over the queue. - while (!rz_vector_empty(tmp_succ_addr)) { - rz_vector_pop_front(tmp_succ_addr, addr); - if (*addr == UT64_MAX || *addr == 0) { - RZ_LOG_DEBUG("interpreter: Quit due to invalid PC.\n"); - // Obviously wrong address. - goto loop_cleanup; - } - SuccessorState ss = { .addr = *addr, .in_state_hash = out_hash }; - // The successors are pushed in the same order into the succ_states - // vector, as they are requested over the addr_queue. - rz_vector_push(succ_states, &ss); - rz_th_queue_push(iset->addr_queue, addr, true); - } - } - - if (!new_state_reached) { - // No new state, means we can stop interpreting. + if (!(new_state_reached && choose_next_pc(iset, out_state, out_hash, tmp_succ_addr, succ_states, addr, il_bb, plugin_data))) { + // No new state or address means we can stop interpreting. // Note, that we can't use the queues as cancel condition because they // are asynchronous and checking them would introduces race conditions. // TODO: This doesn't work if the interpreter can produce multiple out states. diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 62f37ba9f2d..0b7db5ebe79 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -9,13 +9,13 @@ #include "../prototype/eval.h" static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, - RZ_NONNULL const RzInterpreterILBB *il_op, + RZ_NONNULL const RzInterpreterILBB *il_bb, RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_request, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_result, void *plugin_data) { void **it; - rz_pvector_foreach(il_op, it) { + rz_pvector_foreach (il_bb->il_ops, it) { ut64 pc = rz_bv_to_ut64(AD(state->pc->abstr_data)->bv); RZ_LOG_DEBUG("Eval PC = 0x%" PFMT64x "\n", pc); RzInterpreterInsnPkt *pkt = *it; @@ -23,6 +23,7 @@ static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, return false; } if (pc == rz_bv_to_ut64(AD(state->pc->abstr_data)->bv) && AD(state->pc->abstr_data)->is_concrete) { + // Instruction did not manipulate the PC. Set it to the next instruction (packet). set_pc(state, pc + pkt->insn_pkt_size, plugin_data); } } From fae7b5d94260ed8b778f19d64f02f3dcf04126dd Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 20 Jan 2026 23:20:19 +0100 Subject: [PATCH 162/334] Actualy Cache IL ops --- librz/inquiry/inquiry.c | 44 +++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index c601d3d2ea1..e01c89f7ddb 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -237,7 +237,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent RzAtomicBool *is_running = rz_atomic_bool_new(true); RzInterpreterAbstrState *abstr_state = NULL; RzInterpreterSet *iset = NULL; - RzPVector *il_cache = NULL; + HtUP *il_cache = NULL; RzThreadQueue *il_queue = NULL; RzThread *interpr_th = NULL; RzBuffer *io_buf = rz_buf_new_with_io(&core->analysis->iob); @@ -254,7 +254,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent // The pseudo cache of IL effects. // This is only a vector so we can simulate the ownership separation // of the pointers. - il_cache = rz_pvector_new((RzPVectorFree)rz_interpreter_il_bb_free); + il_cache = ht_up_new(NULL, (RzPVectorFree)rz_interpreter_il_bb_free); if (!get_call_targets(core, call_targets)) { RZ_LOG_ERROR("Failed to get call targets.\n"); @@ -369,23 +369,29 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent break; } RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x "\n", (*addr)); - size_t bb_size = 0; - RzInterpreterILBB *bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr, &bb_size); - if (!bb) { - RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", *addr); - // Signal interpreter the lifting failed. - rz_atomic_bool_set(is_running, false); - rz_th_queue_close(io_request_q); - rz_th_queue_close(io_result_q); - rz_th_queue_close(iset->addr_queue); - rz_th_queue_close(iset->il_queue); - bb_decode_failed = true; - break; + RzInterpreterILBB *bb; + if (!ht_up_find(il_cache, *addr, NULL)) { + RZ_LOG_DEBUG("INQUIRY: Lift new BB\n"); + size_t bb_size = 0; + bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr, &bb_size); + if (!bb) { + RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", *addr); + // Signal interpreter the lifting failed. + rz_atomic_bool_set(is_running, false); + rz_th_queue_close(io_request_q); + rz_th_queue_close(io_result_q); + rz_th_queue_close(iset->addr_queue); + rz_th_queue_close(iset->il_queue); + bb_decode_failed = true; + break; + } + rz_analysis_add_bb(core->analysis, *addr, bb_size); + RZ_LOG_DEBUG("INQUIRY: Send IL result: %p.\n", bb); + ht_up_insert(il_cache, bb->bb_addr, bb); + } else { + RZ_LOG_DEBUG("INQUIRY: Serve BB from cache\n"); + bb = ht_up_find(il_cache, *addr, NULL); } - rz_analysis_add_bb(core->analysis, *addr, bb_size); - RZ_LOG_DEBUG("INQUIRY: Send IL result: %p.\n", bb); - rz_pvector_push(il_cache, bb); - // TODO: Free unused if too big. rz_th_queue_push(iset->il_queue, bb, true); } } @@ -525,7 +531,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent // Ownership was passed to iset rz_interpreter_set_free(iset); } - rz_pvector_free(il_cache); + ht_up_free(il_cache); rz_cons_pop(); return return_code; From b67ebbb77575618216ee1f181928dba376a702db Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 21 Jan 2026 23:23:12 +0100 Subject: [PATCH 163/334] Move ownership of sent queue objects to RzInquiry. This prevents heap after use issues, when the interpreter quit, but RzInquiey still tries to read. --- librz/include/rz_inquiry/rz_interpreter.h | 19 ++++++++++++++++++- librz/inquiry/interpreter/interpreter.c | 12 ++++++------ .../interpreter/p/interpreter_prototype.c | 3 --- librz/inquiry/interpreter/prototype/eval.c | 3 +-- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index bc28a3b6877..1d0af6d717c 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -54,6 +54,19 @@ typedef struct { void *abstr_data; ///< The abstract data. It is managed by individual interpreter. } RzInterpreterAbstrVal; +/** + * objects the interpreter should use to send over the queues. + * + * TODO: Race conditions ahead, if one party is faster in overwriting one value + * than the other using it. + * Shouldn't happen though as long as there is only one interpreter + * and one RzInquiry managing everything. + */ +typedef struct { + RzAnalysisXRef xref; ///< OWNER: yield consumer - The xref object passed over the queue. + ut64 shared_addr; ///< OWNER: IL cache - The address object passed to an IL cache for BB requests. +} RzInterpreterSharedObjects; + typedef struct { RzInterpreterAbstraction kinds; ///< The abstractions of the state. HtUP *var_name_hashes; ///< Map of DJB2 hashes to variable names. @@ -63,7 +76,11 @@ typedef struct { RzInterpreterAbstrVal *pc; ///< In our RzIL implementation the PC is not part of the register file. RzAnalysisILConfig *il_config; ///< The IL configuration of the RzArch plugin. const char *arch_name; ///< Name of architecture. Used by work-arounds until we have RzArch. - void *ext; ///< Optional state extensions. Managed by individual interpreters. + /** + * \brief Shared objects. Pointers to the members are passed over the queue. + * TODO: This is obviously not the final solution. Just some poor man's shared memory. + */ + RzInterpreterSharedObjects *shared_obj; } RzInterpreterAbstrState; typedef enum { diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index fdd14fe75ed..1e716c31815 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -119,6 +119,7 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( state->locals = ht_up_new(NULL, free); state->lets = ht_up_new(NULL, free); state->il_config = il_config; + state->shared_obj = RZ_NEW0(RzInterpreterSharedObjects); return state; } @@ -144,6 +145,9 @@ RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbst if (state->il_config) { rz_analysis_il_config_free(state->il_config); } + if (state->shared_obj) { + free(state->shared_obj); + } free(state); } @@ -252,7 +256,6 @@ static bool choose_next_pc(RzInterpreterSet *iset, ut64 out_hash, RzVector *tmp_succ_addr, RzVector *succ_states, - ut64 *shared_addr, const RzInterpreterILBB *il_bb, void *plugin_data) { // Debug printing whole state of VM. @@ -262,6 +265,7 @@ static bool choose_next_pc(RzInterpreterSet *iset, // RZ_LOG_DEBUG("%s", s); // free(s); + ut64 *shared_addr = &iset->state->shared_obj->shared_addr; bool has_succsessor = true; // Determine successors and increase the reference counts for the current out state. @@ -378,10 +382,6 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { goto pre_loop_error; } - // Shared memory passed via the addr_queue. - ut64 _addr = 0; - ut64 *addr = &_addr; - while (rz_atomic_bool_get(iset->is_running_flag)) { // Evaluate the effect on the input state. if (!plugin->eval(in_state, il_bb, iset->yield_queues, iset->io_request, iset->io_result, plugin_data)) { @@ -403,7 +403,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // Determine the successor effects to evaluate. // Only newly reached states are allowed to add successors. - if (!(new_state_reached && choose_next_pc(iset, out_state, out_hash, tmp_succ_addr, succ_states, addr, il_bb, plugin_data))) { + if (!(new_state_reached && choose_next_pc(iset, out_state, out_hash, tmp_succ_addr, succ_states, il_bb, plugin_data))) { // No new state or address means we can stop interpreting. // Note, that we can't use the queues as cancel condition because they // are asynchronous and checking them would introduces race conditions. diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 0b7db5ebe79..49830f32569 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -92,7 +92,6 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_poin } } rz_iterator_free(it); - state->ext = RZ_NEW0(ProtoInterprSharedObjects); return true; } @@ -140,8 +139,6 @@ static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da av->abstr_data = NULL; } rz_iterator_free(it); - free(state->ext); - state->ext = NULL; return true; } diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index a09acaf9c04..5000c962fa5 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -38,11 +38,10 @@ bool report_xref_yield( // packet. So skip them here. return true; } - ProtoInterprSharedObjects *sobj = state->ext; ut64 to_addr = rz_bv_to_ut64(to->bv); if (queue->filter(&to_addr, queue->filter_data->io_boundaries)) { - RzAnalysisXRef *xref = &sobj->xref; + RzAnalysisXRef *xref = &state->shared_obj->xref; xref->from = from; xref->to = to_addr; xref->type = type; From 146c0413106b2b64bdb9c9ad7040a229d49127e0 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 21 Jan 2026 23:24:00 +0100 Subject: [PATCH 164/334] Track invocation of BBs and quit if they were executed more than 3 times. Prevents essentially endless loops. --- librz/inquiry/interpreter/interpreter.c | 6 +-- .../interpreter/p/interpreter_prototype.c | 47 ++++++++++++++++++- librz/inquiry/interpreter/prototype/eval.h | 4 +- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 1e716c31815..479a1ad9a16 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -327,11 +327,11 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { RZ_LOG_DEBUG("INTERPRETER Main: Hello.\n"); RzInterpreterPlugin *plugin = iset->plugin; - void **priv_ptr = NULL; + void *priv_ptr = NULL; if (iset->plugin->init) { - iset->plugin->init(priv_ptr); + iset->plugin->init(&priv_ptr); } - void *plugin_data = priv_ptr ? *priv_ptr : NULL; + void *plugin_data = priv_ptr ? priv_ptr : NULL; // // Start interpretation diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 49830f32569..7d73f44586a 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -7,6 +7,9 @@ #include #include "../prototype/eval.h" +#include "rz_util/ht_uu.h" + +#define MAX_INVOCATIONS_PER_BB 3 static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, RZ_NONNULL const RzInterpreterILBB *il_bb, @@ -14,6 +17,20 @@ static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_request, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_result, void *plugin_data) { + ProtoIntrprPluginData *pdata = plugin_data; + bool found = false; + ut64 ic_pc = ht_uu_find(pdata->bb_invocation_count, il_bb->bb_addr, &found); + if (!found) { + ic_pc = 0; + } else if (ic_pc > MAX_INVOCATIONS_PER_BB) { + // TODO: Make it configurable + RZ_LOG_DEBUG("Reached maximum number of invocations of basic block at 0x%" PFMT64x ". Skipping it.\n", il_bb->bb_addr) + set_pc(state, il_bb->bb_addr + il_bb->size, plugin_data); + return true; + } + ht_uu_update(pdata->bb_invocation_count, il_bb->bb_addr, ic_pc + 1); + RZ_LOG_DEBUG("Eval BB (ic: %" PFMT64d ") = 0x%" PFMT64x "\n", ic_pc, il_bb->bb_addr); + void **it; rz_pvector_foreach (il_bb->il_ops, it) { ut64 pc = rz_bv_to_ut64(AD(state->pc->abstr_data)->bv); @@ -192,6 +209,32 @@ bool state_as_str(RZ_NONNULL const RzInterpreterAbstrState *state, return true; } +bool init(void **plugin_data) { + ProtoIntrprPluginData *pdata = RZ_NEW0(ProtoIntrprPluginData); + if (!pdata) { + return NULL; + } + RZ_LOG_DEBUG("prototype: init()\n"); + pdata->bb_invocation_count = ht_uu_new(); + if (!pdata->bb_invocation_count) { + free(pdata); + return false; + } + *plugin_data = pdata; + return true; +} + +bool fini(void *plugin_data) { + if (!plugin_data) { + return true; + } + RZ_LOG_DEBUG("prototype: fini()\n"); + ProtoIntrprPluginData *pdata = plugin_data; + ht_uu_free(pdata->bb_invocation_count); + free(pdata); + return true; +} + static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .name = "abstr_int_prototype", .author = "Rot127", @@ -200,8 +243,8 @@ static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .license = "LGPL-3.0-only", .supported_abstractions = RZ_INTERPRETER_ABSTRACTION_CONST, .supported_yields = RZ_INTERPRETER_YIELD_KIND_XREF, - .init = NULL, - .fini = NULL, + .init = init, + .fini = fini, .eval = eval, .successors = successors, .init_state = init_state, diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index b83b37f7e83..c6b4185e4a2 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -28,8 +28,8 @@ typedef struct { } ProtoIntrprAbstrData; typedef struct { - RzAnalysisXRef xref; -} ProtoInterprSharedObjects; + HtUU *bb_invocation_count; +} ProtoIntrprPluginData; /** * \brief In bytes From af6f7565916cfbccb3a7cb5321847a540951f427 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 23 Jan 2026 01:34:30 +0100 Subject: [PATCH 165/334] Print some status update. --- librz/inquiry/inquiry.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index e01c89f7ddb..2757f81b01e 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -16,6 +16,7 @@ #include "rz_th.h" #include "rz_util/rz_bitvector.h" #include "rz_util/rz_buf.h" +#include "rz_util/rz_log.h" #include "rz_util/rz_set.h" #include "rz_vector.h" #include @@ -261,7 +262,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent return_code = false; goto error_free; } - RZ_LOG_DEBUG("Total call targets in binary: %" PFMT32d "\n", rz_set_u_size(call_targets)); + if (rz_log_get_level() > RZ_LOGLVL_INFO) { + printf("Total call targets in binary: %" PFMT32d "\n", rz_set_u_size(call_targets)); + } // Initialize the abstract state with the architecture's registers. if (!core->analysis->cur->il_config) { @@ -499,7 +502,12 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent } rz_vector_free(covered_jump_targets); } + if (rz_log_get_level() > RZ_LOGLVL_INFO) { + printf(RZ_CONS_CLEAR_LINE "\rCall targets left: %" PFMT32d, rz_set_u_size(call_targets)); + fflush(stdout); + } } while (!rz_vector_empty(entry_points)); + printf("\n"); RZ_LOG_DEBUG("INQUIRY: Done\n"); From 45f4a12ede54e4f6a1c9d2baf1d6c90e784c8844 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 23 Jan 2026 01:38:02 +0100 Subject: [PATCH 166/334] Add NULL check to not fail with -n flag --- librz/core/cmd/cmd_inquiry.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 9056f48d232..7b6730f3e2a 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -11,7 +11,7 @@ #include RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int argc, const char **argv) { - rz_return_val_if_fail(core->analysis && core->io, RZ_CMD_STATUS_ERROR); + rz_return_val_if_fail(core->analysis && core->io && core->bin->cur && core->bin->cur->o, RZ_CMD_STATUS_ERROR); RzVector *entry_points = rz_vector_new(sizeof(ut64), NULL, NULL); if (!entry_points) { return RZ_CMD_STATUS_ERROR; From 3404ba91010fbebc87a891f16542a054e9b0687b Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sun, 25 Jan 2026 01:08:12 +0100 Subject: [PATCH 167/334] Basic start for new yield types for function detection --- librz/include/rz_inquiry/rz_interpreter.h | 30 ++++++++++++- librz/include/rz_types.h | 2 + librz/inquiry/inquiry.c | 42 +++++++++++++++---- librz/inquiry/interpreter/interpreter.c | 14 ++++--- .../interpreter/p/interpreter_prototype.c | 2 +- 5 files changed, 75 insertions(+), 15 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 1d0af6d717c..599dd197cd0 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -63,8 +63,14 @@ typedef struct { * and one RzInquiry managing everything. */ typedef struct { - RzAnalysisXRef xref; ///< OWNER: yield consumer - The xref object passed over the queue. - ut64 shared_addr; ///< OWNER: IL cache - The address object passed to an IL cache for BB requests. + RZ_LIFETIME(RzInquiry) + ut64 shared_addr; ///< The address object passed to an IL cache for BB requests. + RZ_LIFETIME(RzInquiry) + RzAnalysisXRef xref; ///< The xref object passed over the queue. + RZ_LIFETIME(RzInquiry) + ut64 return_loc; ///< The return location passed over the queue. + RZ_LIFETIME(RzInquiry) + bool stores_npc; ///< The stores next pc flag passed over the queue. } RzInterpreterSharedObjects; typedef struct { @@ -84,7 +90,25 @@ typedef struct { } RzInterpreterAbstrState; typedef enum { + /** + * \brief The yield is an cross reference. + */ RZ_INTERPRETER_YIELD_KIND_XREF = 1 << 0, + + /** + * \brief This yield is a simple flag, signaling if the current basic block + * storing the next PC (address _after_ the basic block) to memory or an register. + * + * If the last branch instruction does not jump to the neighboring basic block + * it is a strong indicator that the jump is a call and the next address a return point. + */ + RZ_INTERPRETER_YIELD_KIND_ST_NPC = 1 << 1, + + /** + * \brief This yield is a simple flag, signaling for a given address that it + * a location where a functions' return instruction jumps to. + */ + RZ_INTERPRETER_YIELD_KIND_RET_LOC = 1 << 2, } RzInterpreterYieldKind; /** @@ -93,6 +117,8 @@ typedef enum { */ typedef union { RzAnalysisXRef *abstr_const; + bool backs_up_npc; + ut64 return_point; } RzInterpreterYield; /** diff --git a/librz/include/rz_types.h b/librz/include/rz_types.h index 1650376f017..e9865c15f86 100644 --- a/librz/include/rz_types.h +++ b/librz/include/rz_types.h @@ -52,6 +52,8 @@ extern "C" { #define RZ_DEPRECATE /* should not be used in new code and should/will be removed in the future */ #endif +#define RZ_LIFETIME(X) /* The lifetime of the object is as long as the given X. What X is depends on the context. */ + #define RZ_IFNULL(x) /* default value for the pointer when null */ #ifdef __GNUC__ #define RZ_UNUSED __attribute__((__unused__)) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 2757f81b01e..3d22b8a60a1 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -110,6 +110,12 @@ static bool setup_queues(RzCore *core, RZ_OUT RzThreadQueue **io_result_q, RZ_OUT RzThreadQueue **addr_queue, RZ_OUT HtUP **yield_queues) { + *il_queue = NULL; + *io_request_q = NULL; + *io_result_q = NULL; + *addr_queue = NULL; + *yield_queues = NULL; + RzPVector /**/ *boundaries = NULL; RzInterpreterYieldQueue *yield_queue = NULL; // The queue to pass the Effects to the interpreter. @@ -137,6 +143,15 @@ static bool setup_queues(RzCore *core, goto error_free; } + // Multiple yield queues can be used by a single interpreter. + // E.g. if the interpreter has a complex abstract memory model + // for stack, heap and constant values. + // Then it can produce three kind of yields. + *yield_queues = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_yield_queue_free); + if (!yield_queues) { + goto error_free; + } + // Here we build the filter for the yield queue. // The prototype generates constant xrefs. // So the filter checks the generated xrefs, if they are within the IO map @@ -146,9 +161,10 @@ static bool setup_queues(RzCore *core, goto error_free; } - // Now create a set of yield queue(s). // These yield queues can be shared between different interpreters. // So we have one yield queue for each yield type. + + // Xref yield queue. RzInterpreterYieldKind yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; yield_queue = rz_interpreter_yield_queue_new( yield_kind, @@ -157,13 +173,20 @@ static bool setup_queues(RzCore *core, if (!yield_queue) { goto error_free; } + ht_up_insert(*yield_queues, yield_kind, yield_queue); + + // stores npc yield queue. + yield_kind = RZ_INTERPRETER_YIELD_KIND_ST_NPC; + yield_queue = rz_interpreter_yield_queue_new(yield_kind, NULL, NULL); + if (!yield_queue) { + goto error_free; + } + ht_up_insert(*yield_queues, yield_kind, yield_queue); - // Multiple yield queues can be used by a single interpreter. - // E.g. if the interpreter has a complex abstract memory model - // for stack, heap and constant values. - // Then it can produce three kind of yields. - *yield_queues = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_yield_queue_free); - if (!yield_queue || !yield_queues) { + // return location yield queue. + yield_kind = RZ_INTERPRETER_YIELD_KIND_RET_LOC; + yield_queue = rz_interpreter_yield_queue_new(yield_kind, NULL, NULL); + if (!yield_queue) { goto error_free; } ht_up_insert(*yield_queues, yield_kind, yield_queue); @@ -171,6 +194,11 @@ static bool setup_queues(RzCore *core, return true; error_free: + ht_up_free(*yield_queues); + rz_th_queue_free(*il_queue); + rz_th_queue_free(*io_request_q); + rz_th_queue_free(*io_result_q); + rz_th_queue_free(*addr_queue); return false; } diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 479a1ad9a16..703fa7951ef 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -48,14 +48,22 @@ RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYiel RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpreterYieldKind kind, RzInterpreterYieldFilter filter, - RZ_OWN RZ_NULLABLE void *filter_data_io_boundaries) { + RZ_OWN RZ_NULLABLE void *filter_data) { RzInterpreterYieldQueue *yield_queue = RZ_NEW0(RzInterpreterYieldQueue); if (!yield_queue) { return NULL; } RzThreadQueue *queue = NULL; switch (kind) { + case RZ_INTERPRETER_YIELD_KIND_RET_LOC: + case RZ_INTERPRETER_YIELD_KIND_ST_NPC: + queue = rz_th_queue_new(RZ_INTERPRETER_YIELD_QUEUE_SIZE, NULL); + break; case RZ_INTERPRETER_YIELD_KIND_XREF: + if (filter_data) { + yield_queue->filter_data = RZ_NEW0(RzInterpreterYieldFilterData); + yield_queue->filter_data->io_boundaries = filter_data; + } queue = rz_th_queue_new(RZ_INTERPRETER_YIELD_QUEUE_SIZE, NULL); break; } @@ -66,10 +74,6 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre yield_queue->kind = kind; yield_queue->yield_queue = queue; yield_queue->filter = filter; - yield_queue->filter_data = RZ_NEW0(RzInterpreterYieldFilterData); - if (filter_data_io_boundaries) { - yield_queue->filter_data->io_boundaries = filter_data_io_boundaries; - } return yield_queue; } diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 7d73f44586a..b320c250083 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -242,7 +242,7 @@ static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .desc = "A prototype interpreter for constant/bottom abstractions.", .license = "LGPL-3.0-only", .supported_abstractions = RZ_INTERPRETER_ABSTRACTION_CONST, - .supported_yields = RZ_INTERPRETER_YIELD_KIND_XREF, + .supported_yields = RZ_INTERPRETER_YIELD_KIND_XREF | RZ_INTERPRETER_YIELD_KIND_ST_NPC | RZ_INTERPRETER_YIELD_KIND_RET_LOC, .init = init, .fini = fini, .eval = eval, From a2374c078a13aecd2c07d766abdb463d28fd2b52 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 27 Jan 2026 17:29:31 +0100 Subject: [PATCH 168/334] Fix test --- librz/inquiry/inquiry.c | 8 +++++--- test/db/inquiry/interpreter/xrefs | 18 +++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 3d22b8a60a1..16ccff437d0 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -290,7 +290,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent return_code = false; goto error_free; } - if (rz_log_get_level() > RZ_LOGLVL_INFO) { + if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { printf("Total call targets in binary: %" PFMT32d "\n", rz_set_u_size(call_targets)); } @@ -530,12 +530,14 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent } rz_vector_free(covered_jump_targets); } - if (rz_log_get_level() > RZ_LOGLVL_INFO) { + if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { printf(RZ_CONS_CLEAR_LINE "\rCall targets left: %" PFMT32d, rz_set_u_size(call_targets)); fflush(stdout); } } while (!rz_vector_empty(entry_points)); - printf("\n"); + if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { + printf("\n"); + } RZ_LOG_DEBUG("INQUIRY: Done\n"); diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs index fff4f7587f5..df0bc72ff62 100644 --- a/test/db/inquiry/interpreter/xrefs +++ b/test/db/inquiry/interpreter/xrefs @@ -3,8 +3,10 @@ FILE=bins/inquiry/interpreter/prototype/x86_icall_malloc CMDS=< CODE -> 0x401aa1 main+49 sym.function_2+4 0x401b04 -> CODE -> 0x401ae0 sym.some_ptr sym.function_2+10 0x401b0a -> CODE -> 0x401aa1 main+49 - addr size traced ninstr jump fail fcns calls xrefs -------------------------------------------------------------------- 0x401a70 35 0 0 0x401a8d 6 0 0 0x401a93 14 0 0 @@ -106,9 +106,13 @@ EXPECT=< Date: Tue, 27 Jan 2026 17:33:06 +0100 Subject: [PATCH 169/334] CLean up code --- librz/include/rz_inquiry/rz_interpreter.h | 5 +++++ librz/inquiry/interpreter/prototype/eval.c | 2 +- librz/inquiry/interpreter/prototype/eval.h | 2 +- librz/inquiry/interpreter/prototype/eval_effect.c | 8 ++++---- librz/inquiry/interpreter/prototype/eval_pure.c | 2 +- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 599dd197cd0..a5e3f947fff 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -107,6 +107,11 @@ typedef enum { /** * \brief This yield is a simple flag, signaling for a given address that it * a location where a functions' return instruction jumps to. + * + * TODO: This is one of the yields which has uncertainty attached to it. + * Currently a return point is defined as instruction after a call. + * But this is no given! If for example the call never returns (e.g. `call about()`). + * Of course we could change the definition. But for the prototype this is good enough. */ RZ_INTERPRETER_YIELD_KIND_RET_LOC = 1 << 2, } RzInterpreterYieldKind; diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 5000c962fa5..6110e2a096e 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -10,7 +10,7 @@ #include "rz_util/rz_log.h" #include -bool report_xref_yield( +bool report_yield_xref( RzInterpreterAbstrState *state, size_t insn_pkt_size, HtUP /**/ *yield_queues, diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index c6b4185e4a2..9b40a916c75 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -100,7 +100,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzThreadQueue /**/ *io_result, void *plugin_data); -bool report_xref_yield( +bool report_yield_xref( RzInterpreterAbstrState *state, size_t insn_pkt_size, HtUP /**/ *yield_queues, diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index dffdcd68389..12abd0bbc01 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -13,13 +13,13 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, RzThreadQueue /**/ *io_result, void *plugin_data) { STACK_ABSTR_DATA_OUT(eval_out); + ProtoIntrprAbstrData *pc = AD(state->pc->abstr_data); switch (effect->code) { default: case RZ_IL_OP_EMPTY: break; case RZ_IL_OP_NOP: { - ProtoIntrprAbstrData *pc = AD(state->pc->abstr_data); if (!pc->is_concrete) { // The PC is no longer a concrete value. // This plugin has no addition for it defined. @@ -67,10 +67,10 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, goto error; } if (!eval_out.is_concrete) { - RZ_LOG_DEBUG("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", rz_bv_to_ut64(AD(state->pc->abstr_data)->bv)); + RZ_LOG_DEBUG("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", rz_bv_to_ut64(pc->bv)); } RZ_LOG_DEBUG("Prototype: JMP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", - rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), + rz_bv_to_ut64(pc->bv), rz_bv_to_ut64(eval_out.bv), eval_out.is_concrete ? "Concrete" : "Abstract"); // Setting the PC to a bottom value is allowed here! @@ -78,7 +78,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, if (eval_out.is_concrete) { // NOTE: This prototype can't classify into call or jump. // Everything is just a jump for it at this point. - report_xref_yield(state, insn_pkt_size, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &eval_out, RZ_ANALYSIS_XREF_TYPE_CODE); + report_yield_xref(state, insn_pkt_size, yield_queues, rz_bv_to_ut64(pc->bv), &eval_out, RZ_ANALYSIS_XREF_TYPE_CODE); } copy_abstr_data(state->pc->abstr_data, &eval_out); break; diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 8b79664e8ae..ce97e1ebc39 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -435,7 +435,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_and_inplace(ld_addr.bv, &mask); } - report_xref_yield(state, 0, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &ld_addr, RZ_ANALYSIS_XREF_TYPE_MEM_READ); + report_yield_xref(state, 0, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &ld_addr, RZ_ANALYSIS_XREF_TYPE_MEM_READ); size_t n_bits = pure->code == RZ_IL_OP_LOAD ? state->il_config->mem_key_size : pure->op.loadw.n_bits; if (!load_abstr_data(state, mem_idx, &ld_addr, n_bits, out, io_request, io_result)) { rz_bv_fini(ld_addr.bv); From 4238d0167ac1324a468789fc2b32b9adbf9cf902 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 27 Jan 2026 17:33:39 +0100 Subject: [PATCH 170/334] Report yields: PC store and possible return points. --- librz/include/rz_analysis.h | 12 +++++++ librz/include/rz_inquiry/rz_interpreter.h | 8 ++++- librz/inquiry/interpreter/interpreter.c | 2 ++ .../interpreter/p/interpreter_prototype.c | 1 + librz/inquiry/interpreter/prototype/eval.c | 34 +++++++++++++++++++ librz/inquiry/interpreter/prototype/eval.h | 8 +++++ .../interpreter/prototype/eval_effect.c | 18 +++++++--- 7 files changed, 77 insertions(+), 6 deletions(-) diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 2d9725a3029..ae05b2e5400 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -976,6 +976,18 @@ typedef struct rz_analysis_ref_t { RzAnalysisXRefType type; } RzAnalysisXRef; +/** + * \brief A struct combining information about an instruction storing the + * a next instruction pointer of a basic block. + * Indicative for a call instruction. + */ +typedef struct { + ut64 bb_addr; ///< The address of the basic block which stores the NPC. + ut64 insn_pkt_addr; ///< The address of the instruction packet storing the NPC. + ut64 npc; ///< The NPC stored. + bool in_mem; ///< True if the NPC is written to memory. False if it is written to a register. +} RzAnalysisStNPCLoc; + RZ_API const char *rz_analysis_ref_type_tostring(RzAnalysisXRefType t); /* represents a reference line from one address (from) to another (to) */ diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index a5e3f947fff..50d1fd1a7f4 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -65,12 +65,15 @@ typedef struct { typedef struct { RZ_LIFETIME(RzInquiry) ut64 shared_addr; ///< The address object passed to an IL cache for BB requests. + RZ_LIFETIME(RzInquiry) RzAnalysisXRef xref; ///< The xref object passed over the queue. + RZ_LIFETIME(RzInquiry) ut64 return_loc; ///< The return location passed over the queue. + RZ_LIFETIME(RzInquiry) - bool stores_npc; ///< The stores next pc flag passed over the queue. + RzAnalysisStNPCLoc stores_npc; ///< The stores next pc info passed over the queue. } RzInterpreterSharedObjects; typedef struct { @@ -86,7 +89,10 @@ typedef struct { * \brief Shared objects. Pointers to the members are passed over the queue. * TODO: This is obviously not the final solution. Just some poor man's shared memory. */ + RZ_LIFETIME(RzInquiry) RzInterpreterSharedObjects *shared_obj; + ut64 bb_addr; + ut64 bb_size; } RzInterpreterAbstrState; typedef enum { diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 703fa7951ef..5dafeb2421d 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -387,6 +387,8 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { } while (rz_atomic_bool_get(iset->is_running_flag)) { + iset->state->bb_addr = il_bb->bb_addr; + iset->state->bb_size = il_bb->size; // Evaluate the effect on the input state. if (!plugin->eval(in_state, il_bb, iset->yield_queues, iset->io_request, iset->io_result, plugin_data)) { RZ_LOG_DEBUG("Eval failed\n"); diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index b320c250083..6799a2bfdb7 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -91,6 +91,7 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_poin // set to the length of the src. // TODO: Really a good idea to be so liberal? // Or should the length of the globals be enforced? + // The bitvector arithmetic does enforce the length. AD(av->abstr_data)->bv = rz_bv_new(state->il_config->mem_key_size); // TODO: This is debatable. It depends on the ABI what the default values are. // Some values must be concrete, otherwise the interpretation of the prototype end too early. diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 6110e2a096e..96c0d25671d 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -54,6 +54,40 @@ bool report_yield_xref( return true; } +/** + * \brief Report the store of the next PC and report it as possible return point. + */ +bool report_yield_str_pc_ret_loc( + RzInterpreterAbstrState *state, + HtUP /**/ *yield_queues, + ut64 bb_addr, + ut64 insn_pkt_addr, + const ProtoIntrprAbstrData *npc, + bool in_mem) { + if (!npc->is_concrete || rz_bv_len(npc->bv) > 64) { + // Isn't reported + return true; + } + RzInterpreterYieldQueue *st_queue = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_ST_NPC, NULL); + RzInterpreterYieldQueue *ret_queue = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_RET_LOC, NULL); + if (!st_queue || !ret_queue) { + rz_warn_if_reached(); + return false; + } + + RzAnalysisStNPCLoc *st_npc = &state->shared_obj->stores_npc; + st_npc->bb_addr = bb_addr; + st_npc->insn_pkt_addr = insn_pkt_addr; + st_npc->in_mem = in_mem; + st_npc->npc = rz_bv_to_ut64(npc->bv); + rz_th_queue_push(st_queue->yield_queue, st_npc, true); + + ut64 *ret_loc = &state->shared_obj->return_loc; + *ret_loc = rz_bv_to_ut64(npc->bv); + rz_th_queue_push(ret_queue->yield_queue, ret_loc, true); + return true; +} + void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) { rz_return_if_fail(dst && src && dst->bv && src->bv); rz_bv_cast_inplace(dst->bv, rz_bv_len(src->bv), false); diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 9b40a916c75..7494bf5f7d7 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -108,6 +108,14 @@ bool report_yield_xref( const ProtoIntrprAbstrData *to, RzAnalysisXRefType type); +bool report_yield_str_pc_ret_loc( + RzInterpreterAbstrState *state, + HtUP /**/ *yield_queues, + ut64 bb_addr, + ut64 insn_pkt_addr, + const ProtoIntrprAbstrData *npc, + bool in_mem); + bool set_pc(RzInterpreterAbstrState *state, ut64 pc, void *plugin_data); diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 12abd0bbc01..3af4975259e 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -3,6 +3,7 @@ #include "eval.h" #include "rz_analysis.h" +#include "rz_inquiry/rz_interpreter.h" #include "rz_util/rz_bitvector.h" RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, @@ -56,10 +57,13 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, if (!interpreter_prototype_eval_pure(state, effect->op.set.x, &eval_out, yield_queues, io_request, io_result, plugin_data)) { goto error; } - write_var_to_state(state, - effect->op.set.is_local ? RZ_IL_VAR_KIND_LOCAL : RZ_IL_VAR_KIND_GLOBAL, - vhash, - &eval_out); + RzILVarKind kind = effect->op.set.is_local ? RZ_IL_VAR_KIND_LOCAL : RZ_IL_VAR_KIND_GLOBAL; + write_var_to_state(state, kind, vhash, &eval_out); + if (eval_out.is_concrete && + kind == RZ_IL_VAR_KIND_GLOBAL && + rz_bv_to_ut64(eval_out.bv) == state->bb_addr + state->bb_size) { + report_yield_str_pc_ret_loc(state, yield_queues, state->bb_addr, rz_bv_to_ut64(pc->bv), &eval_out, false); + } break; } case RZ_IL_OP_JMP: { @@ -138,7 +142,11 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, rz_bv_fini(st_addr.bv); break; } - report_xref_yield(state, insn_pkt_size, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); + if (eval_out.is_concrete && + rz_bv_to_ut64(eval_out.bv) == state->bb_addr + state->bb_size) { + report_yield_str_pc_ret_loc(state, yield_queues, state->bb_addr, rz_bv_to_ut64(pc->bv), &eval_out, true); + } + report_yield_xref(state, insn_pkt_size, yield_queues, rz_bv_to_ut64(pc->bv), &st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); if (!store_abstr_data(state, mem_idx, &st_addr, &eval_out, io_request, io_result)) { rz_bv_fini(st_addr.bv); goto error; From 5fd75720a31e63b5f44fae0a25b58c96ddb623cb Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 27 Jan 2026 23:03:30 +0100 Subject: [PATCH 171/334] Move yield handling into own function. --- librz/inquiry/inquiry.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 16ccff437d0..1d92219a230 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -252,6 +252,19 @@ static RzVector /**/ *get_ignored_code_regions( return v; } +static bool handle_yields(RzCore *core, HtUP *yield_queues) { + RzInterpreterYieldQueue *q_xrefs = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); + if (!rz_th_queue_is_empty(q_xrefs->yield_queue)) { + RzAnalysisXRef *xref = NULL; + if (!rz_th_queue_pop(q_xrefs->yield_queue, false, (void **)&xref) || !xref) { + return false; + } + rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, xref->type); + RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)); + } + return true; +} + /** * A function to call the prototype interpreter. * Usually these tasks will be split between different caches and yield consumers. @@ -456,16 +469,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent // This part plays the role of a yield consumer. // In our prototype it only receives xrefs and stores them in RzAnalysis. { - RzInterpreterYieldQueue *q = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); - if (!rz_th_queue_is_empty(q->yield_queue)) { - RzAnalysisXRef *xref = NULL; - if (!rz_th_queue_pop(q->yield_queue, false, (void **)&xref) || !xref) { - rz_atomic_bool_set(is_running, false); - break; - } - // TODO: Currently we can't classify jumps/calls as such. - rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, xref->type); - RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)); + if (!handle_yields(core, yield_queues)) { + rz_atomic_bool_set(is_running, false); + break; } } } @@ -535,6 +541,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent fflush(stdout); } } while (!rz_vector_empty(entry_points)); + if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { printf("\n"); } From 75f7b964d8f6b83f567ed475f0e65a86e8e311cf Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 28 Jan 2026 00:00:44 +0100 Subject: [PATCH 172/334] Move the whole plugin setupt into rz_inquiry_new() and free() --- librz/core/cmd/cmd_inquiry.c | 1 + librz/core/core.c | 3 ++ librz/include/rz_inquiry.h | 3 +- librz/inquiry/inquiry.c | 72 ++++++++++++++++++++++++++++++++---- 4 files changed, 71 insertions(+), 8 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 7b6730f3e2a..9661a111764 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -30,5 +30,6 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar } } bool success = rz_inquiry_interpreter(core, entry_points); + return success ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; } diff --git a/librz/core/core.c b/librz/core/core.c index aa3918a55c1..9a82db448ff 100644 --- a/librz/core/core.c +++ b/librz/core/core.c @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2009-2020 pancake // SPDX-License-Identifier: LGPL-3.0-only +#include "rz_inquiry.h" #include #include #include @@ -1615,6 +1616,7 @@ RZ_API bool rz_core_init(RzCore *core) { /* XXX memory leak */ return false; } + core->inquiry = rz_inquiry_new(); core->ev = rz_event_new(core); core->max_cmd_depth = RZ_CONS_CMD_DEPTH + 1; core->sdb = sdb_new(NULL, "rzkv.sdb", 0); // XXX: path must be in home? @@ -1870,6 +1872,7 @@ RZ_API void rz_core_fini(RzCore *c) { RZ_FREE_CUSTOM(c->hash, rz_hash_free); RZ_FREE_CUSTOM(c->ropchain, rz_list_free); RZ_FREE_CUSTOM(c->ev, rz_event_free); + RZ_FREE_CUSTOM(c->inquiry, rz_inquiry_free); RZ_FREE(c->cmdlog); RZ_FREE(c->lastsearch); RZ_FREE(c->cons->pager); diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 1284cd08a1f..e8d47bddb80 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -25,13 +25,14 @@ typedef struct { * \brief RzInquiry interpreter plugins. Indexed by name. */ HtSP /**/ *plugins; + HtSP /**/ *plugins_data; } RzInquiry; RZ_API bool rz_inquiry_plugin_add(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NONNULL RzInquiryPlugin *plugin); RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NONNULL RzInquiryPlugin *plugin); RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void); -RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *a); +RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *q); RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr, RZ_NULLABLE RZ_OUT size_t *bb_size); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 1d92219a230..c4fbbce7c5b 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -14,8 +14,12 @@ #include "rz_inquiry/rz_interpreter.h" #include "rz_inquiry_plugins.h" #include "rz_th.h" +#include "rz_types.h" +#include "rz_util/ht_pp.h" +#include "rz_util/ht_sp.h" #include "rz_util/rz_bitvector.h" #include "rz_util/rz_buf.h" +#include "rz_util/rz_iterator.h" #include "rz_util/rz_log.h" #include "rz_util/rz_set.h" #include "rz_vector.h" @@ -41,20 +45,35 @@ RZ_API RZ_BORROW RzInquiryPlugin *rz_inquiry_get_plugin(size_t index) { RZ_API bool rz_inquiry_plugin_add(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OWN RZ_NONNULL RzInquiryPlugin *plugin) { rz_return_val_if_fail(inquiry && plugin, false); - if (plugin->p_interpreter) { - if (!ht_sp_insert(inquiry->plugins, plugin->p_interpreter->name, plugin)) { - RZ_LOG_WARN("Plugin '%s' was already added.\n", plugin->p_interpreter->name); - } + if (!plugin->p_interpreter) { + rz_warn_if_reached(); + return false; + } + + if (!ht_sp_insert(inquiry->plugins, plugin->p_interpreter->name, plugin)) { + RZ_LOG_WARN("Plugin '%s' was already added.\n", plugin->p_interpreter->name); return true; } - rz_warn_if_reached(); - return false; + void **p_data = RZ_NEW0(void *); + if (!ht_sp_insert(inquiry->plugins_data, plugin->p_interpreter->name, p_data)) { + rz_warn_if_reached(); + return false; + } + if (plugin->p_interpreter->init) { + plugin->p_interpreter->init(ht_sp_find(inquiry->plugins_data, plugin->p_interpreter->name, NULL)); + } + return true; } RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OWN RZ_NONNULL RzInquiryPlugin *plugin) { rz_return_val_if_fail(inquiry && plugin, false); + void **p_data = ht_sp_find(inquiry->plugins_data, plugin->p_interpreter->name, NULL); + if (plugin->p_interpreter->fini) { + plugin->p_interpreter->fini(p_data ? *p_data : NULL); + } + free(p_data); if (plugin->p_interpreter) { return ht_sp_delete(inquiry->plugins, plugin->p_interpreter->name); } @@ -62,6 +81,37 @@ RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OW return false; } +RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void) { + RzInquiry *iq = RZ_NEW0(RzInquiry); + if (!iq) { + return NULL; + } + iq->plugins = ht_sp_new(HT_STR_CONST, NULL, NULL); + iq->plugins_data = ht_sp_new(HT_STR_CONST, NULL, NULL); + if (!iq->plugins || !iq->plugins_data) { + ht_sp_free(iq->plugins); + ht_sp_free(iq->plugins_data); + free(iq); + return NULL; + } + for (size_t i = 0; i < RZ_ARRAY_SIZE(inquiry_static_plugins); ++i) { + rz_inquiry_plugin_add(iq, inquiry_static_plugins[i]); + } + return iq; +} + +RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *iq) { + if (!iq) { + return; + } + for (size_t i = 0; i < RZ_ARRAY_SIZE(inquiry_static_plugins); ++i) { + rz_inquiry_plugin_del(iq, inquiry_static_plugins[i]); + } + ht_sp_free(iq->plugins); + ht_sp_free(iq->plugins_data); + free(iq); +} + RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzPVector /**/ *allowed_segments) { rz_return_val_if_fail(xref_to_addr && allowed_segments, false); void **it; @@ -349,8 +399,16 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent // Later we would pass a unique iset to each interpreter with // the required queues only. // But for the prototype we have only one iset with all queues. + RzInquiryPlugin *prototype = ht_sp_find(core->inquiry->plugins, "abstr_int_prototype", NULL); + if (!prototype) { + return_code = false; + rz_warn_if_reached(); + goto error_free; + } iset = rz_interpreter_set_new( - rz_inquiry_plugin_interpreter_prototype.p_interpreter, + // TODO: Maybe use the pointer from RzCore. + // But in general the whole thing should run without RzCore. + prototype->p_interpreter, abstr_state, addr_queue, il_queue, From 31c4ffd74f4cb79dfd734fb4a73e9602ec594f6a Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 29 Jan 2026 21:26:38 +0100 Subject: [PATCH 173/334] Rewrite the whole call candidate detection. --- librz/include/rz_analysis.h | 10 +++-- librz/include/rz_inquiry.h | 3 +- librz/include/rz_inquiry/rz_interpreter.h | 28 +----------- librz/inquiry/inquiry.c | 45 ++++++++++++++----- librz/inquiry/interpreter/interpreter.c | 3 +- .../interpreter/p/interpreter_prototype.c | 2 +- librz/inquiry/interpreter/prototype/eval.c | 29 +++--------- librz/inquiry/interpreter/prototype/eval.h | 12 +++-- .../interpreter/prototype/eval_effect.c | 36 +++++++++++---- .../inquiry/interpreter/prototype/eval_pure.c | 3 +- 10 files changed, 87 insertions(+), 84 deletions(-) diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index ae05b2e5400..190d506abc8 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -979,14 +979,16 @@ typedef struct rz_analysis_ref_t { /** * \brief A struct combining information about an instruction storing the * a next instruction pointer of a basic block. - * Indicative for a call instruction. + * This indicates that the ending branch instruction in a basic block is + * a call instruction. */ typedef struct { ut64 bb_addr; ///< The address of the basic block which stores the NPC. - ut64 insn_pkt_addr; ///< The address of the instruction packet storing the NPC. - ut64 npc; ///< The NPC stored. + ut64 store_addr; ///< The address of the instruction packet storing the NPC. + ut64 jmp_addr; ///< Address of the call candidate instruction packet. + ut64 npc; ///< The NPC stored. Should point after a basic block. bool in_mem; ///< True if the NPC is written to memory. False if it is written to a register. -} RzAnalysisStNPCLoc; +} RzAnalysisCallCandidate; RZ_API const char *rz_analysis_ref_type_tostring(RzAnalysisXRefType t); diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index e8d47bddb80..f5933dbc144 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -17,7 +17,6 @@ extern "C" { typedef struct rz_inquiry_plugin_t { RzInterpreterPlugin *p_interpreter; - // RzInquiryAlgorithm *p_algorithm; } RzInquiryPlugin; typedef struct { @@ -26,6 +25,8 @@ typedef struct { */ HtSP /**/ *plugins; HtSP /**/ *plugins_data; + + HtUP /**/ *call_candidates; ///< Indexed by address of candidate instruction. } RzInquiry; RZ_API bool rz_inquiry_plugin_add(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NONNULL RzInquiryPlugin *plugin); diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 50d1fd1a7f4..4219b163682 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -70,10 +70,7 @@ typedef struct { RzAnalysisXRef xref; ///< The xref object passed over the queue. RZ_LIFETIME(RzInquiry) - ut64 return_loc; ///< The return location passed over the queue. - - RZ_LIFETIME(RzInquiry) - RzAnalysisStNPCLoc stores_npc; ///< The stores next pc info passed over the queue. + RzAnalysisCallCandidate call_cand; ///< The stores next pc info passed over the queue. } RzInterpreterSharedObjects; typedef struct { @@ -108,30 +105,9 @@ typedef enum { * If the last branch instruction does not jump to the neighboring basic block * it is a strong indicator that the jump is a call and the next address a return point. */ - RZ_INTERPRETER_YIELD_KIND_ST_NPC = 1 << 1, - - /** - * \brief This yield is a simple flag, signaling for a given address that it - * a location where a functions' return instruction jumps to. - * - * TODO: This is one of the yields which has uncertainty attached to it. - * Currently a return point is defined as instruction after a call. - * But this is no given! If for example the call never returns (e.g. `call about()`). - * Of course we could change the definition. But for the prototype this is good enough. - */ - RZ_INTERPRETER_YIELD_KIND_RET_LOC = 1 << 2, + RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE = 1 << 1, } RzInterpreterYieldKind; -/** - * \brief A yield of an interpreter. Type is implied by the queue. - * Object is a union so the elements pushed over the queue are small. - */ -typedef union { - RzAnalysisXRef *abstr_const; - bool backs_up_npc; - ut64 return_point; -} RzInterpreterYield; - /** * \brief A filter for abstract values to decide if they should be pushed into * the yield queue or not. diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index c4fbbce7c5b..6d3c616bebc 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -17,11 +17,13 @@ #include "rz_types.h" #include "rz_util/ht_pp.h" #include "rz_util/ht_sp.h" +#include "rz_util/ht_up.h" #include "rz_util/rz_bitvector.h" #include "rz_util/rz_buf.h" #include "rz_util/rz_iterator.h" #include "rz_util/rz_log.h" #include "rz_util/rz_set.h" +#include "rz_util/rz_str.h" #include "rz_vector.h" #include #include @@ -88,6 +90,7 @@ RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void) { } iq->plugins = ht_sp_new(HT_STR_CONST, NULL, NULL); iq->plugins_data = ht_sp_new(HT_STR_CONST, NULL, NULL); + iq->call_candidates = ht_up_new(NULL, free); if (!iq->plugins || !iq->plugins_data) { ht_sp_free(iq->plugins); ht_sp_free(iq->plugins_data); @@ -109,6 +112,7 @@ RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *iq) { } ht_sp_free(iq->plugins); ht_sp_free(iq->plugins_data); + ht_up_free(iq->call_candidates); free(iq); } @@ -225,22 +229,12 @@ static bool setup_queues(RzCore *core, } ht_up_insert(*yield_queues, yield_kind, yield_queue); - // stores npc yield queue. - yield_kind = RZ_INTERPRETER_YIELD_KIND_ST_NPC; + yield_kind = RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE; yield_queue = rz_interpreter_yield_queue_new(yield_kind, NULL, NULL); if (!yield_queue) { goto error_free; } ht_up_insert(*yield_queues, yield_kind, yield_queue); - - // return location yield queue. - yield_kind = RZ_INTERPRETER_YIELD_KIND_RET_LOC; - yield_queue = rz_interpreter_yield_queue_new(yield_kind, NULL, NULL); - if (!yield_queue) { - goto error_free; - } - ht_up_insert(*yield_queues, yield_kind, yield_queue); - return true; error_free: @@ -312,6 +306,21 @@ static bool handle_yields(RzCore *core, HtUP *yield_queues) { rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, xref->type); RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)); } + + RzInterpreterYieldQueue *q_calls = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, NULL); + if (!rz_th_queue_is_empty(q_calls->yield_queue)) { + RzAnalysisCallCandidate *cc = NULL; + if (!rz_th_queue_pop(q_calls->yield_queue, false, (void **)&cc) || !cc) { + return false; + } + RzAnalysisCallCandidate *cc_clone = RZ_NEW0(RzAnalysisCallCandidate); + memcpy(cc_clone, cc, sizeof(RzAnalysisCallCandidate)); + if (!ht_up_update(core->inquiry->call_candidates, cc_clone->jmp_addr, cc_clone)) { + RZ_LOG_DEBUG("Overwrote a call candidate located at 0x%" PFMT64x "\n", cc_clone->jmp_addr); + } else { + RZ_LOG_DEBUG("Added call candidate located at 0x%" PFMT64x "\n", cc_clone->jmp_addr); + } + } return true; } @@ -606,6 +615,20 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent RZ_LOG_DEBUG("INQUIRY: Done\n"); + printf("Found call candidates:\n"); + RzIterator *it = ht_up_as_iter(core->inquiry->call_candidates); + RzAnalysisCallCandidate **v; + rz_iterator_foreach(it, v) { + RzAnalysisCallCandidate *cc = *v; + printf("\n"); + printf("\tbb_addr = 0x%" PFMT64x "\n", cc->bb_addr); + printf("\tstore_addr = 0x%" PFMT64x "\n", cc->store_addr); + printf("\tjmp_addr = 0x%" PFMT64x "\n", cc->jmp_addr); + printf("\tnpc = 0x%" PFMT64x "\n", cc->npc); + printf("\tin_mem = %s\n", rz_str_bool(cc->in_mem)); + } + rz_iterator_free(it); + rz_config_set(core->config, "io.cache", io_cache_opt); // Wait for thread to finish before cleaning. diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 5dafeb2421d..f0af7a43f61 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -55,8 +55,7 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre } RzThreadQueue *queue = NULL; switch (kind) { - case RZ_INTERPRETER_YIELD_KIND_RET_LOC: - case RZ_INTERPRETER_YIELD_KIND_ST_NPC: + case RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE: queue = rz_th_queue_new(RZ_INTERPRETER_YIELD_QUEUE_SIZE, NULL); break; case RZ_INTERPRETER_YIELD_KIND_XREF: diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 6799a2bfdb7..63b477d3b8d 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -243,7 +243,7 @@ static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .desc = "A prototype interpreter for constant/bottom abstractions.", .license = "LGPL-3.0-only", .supported_abstractions = RZ_INTERPRETER_ABSTRACTION_CONST, - .supported_yields = RZ_INTERPRETER_YIELD_KIND_XREF | RZ_INTERPRETER_YIELD_KIND_ST_NPC | RZ_INTERPRETER_YIELD_KIND_RET_LOC, + .supported_yields = RZ_INTERPRETER_YIELD_KIND_XREF | RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, .init = init, .fini = fini, .eval = eval, diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 96c0d25671d..0798cb0134d 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -57,34 +57,19 @@ bool report_yield_xref( /** * \brief Report the store of the next PC and report it as possible return point. */ -bool report_yield_str_pc_ret_loc( +bool report_yield_call_candiate( RzInterpreterAbstrState *state, HtUP /**/ *yield_queues, - ut64 bb_addr, - ut64 insn_pkt_addr, - const ProtoIntrprAbstrData *npc, - bool in_mem) { - if (!npc->is_concrete || rz_bv_len(npc->bv) > 64) { - // Isn't reported - return true; - } - RzInterpreterYieldQueue *st_queue = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_ST_NPC, NULL); - RzInterpreterYieldQueue *ret_queue = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_RET_LOC, NULL); - if (!st_queue || !ret_queue) { + ProtoIntrprPluginData *plugin_data) { + RzInterpreterYieldQueue *cc_queue = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, NULL); + if (!cc_queue) { rz_warn_if_reached(); return false; } - RzAnalysisStNPCLoc *st_npc = &state->shared_obj->stores_npc; - st_npc->bb_addr = bb_addr; - st_npc->insn_pkt_addr = insn_pkt_addr; - st_npc->in_mem = in_mem; - st_npc->npc = rz_bv_to_ut64(npc->bv); - rz_th_queue_push(st_queue->yield_queue, st_npc, true); - - ut64 *ret_loc = &state->shared_obj->return_loc; - *ret_loc = rz_bv_to_ut64(npc->bv); - rz_th_queue_push(ret_queue->yield_queue, ret_loc, true); + RzAnalysisCallCandidate *cc = &state->shared_obj->call_cand; + memcpy(cc, &plugin_data->call_cand, sizeof(plugin_data->call_cand)); + rz_th_queue_push(cc_queue->yield_queue, cc, true); return true; } diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 7494bf5f7d7..5347090f48f 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -29,6 +29,7 @@ typedef struct { typedef struct { HtUU *bb_invocation_count; + RzAnalysisCallCandidate call_cand; ///< Data of a call candidate. } ProtoIntrprPluginData; /** @@ -90,7 +91,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, HtUP /**/ *yield_queues, RzThreadQueue /**/ *io_request, RzThreadQueue /**/ *io_result, - void *plugin_data); + ProtoIntrprPluginData *plugin_data); RZ_IPI bool interpreter_prototype_eval_pure( RzInterpreterAbstrState *state, const RzILOpPure *pure, @@ -98,7 +99,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( HtUP /**/ *yield_queues, RzThreadQueue /**/ *io_request, RzThreadQueue /**/ *io_result, - void *plugin_data); + ProtoIntrprPluginData *plugin_data); bool report_yield_xref( RzInterpreterAbstrState *state, @@ -108,13 +109,10 @@ bool report_yield_xref( const ProtoIntrprAbstrData *to, RzAnalysisXRefType type); -bool report_yield_str_pc_ret_loc( +bool report_yield_call_candiate( RzInterpreterAbstrState *state, HtUP /**/ *yield_queues, - ut64 bb_addr, - ut64 insn_pkt_addr, - const ProtoIntrprAbstrData *npc, - bool in_mem); + ProtoIntrprPluginData *plugin_data); bool set_pc(RzInterpreterAbstrState *state, ut64 pc, void *plugin_data); diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 3af4975259e..9c989127097 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -5,6 +5,15 @@ #include "rz_analysis.h" #include "rz_inquiry/rz_interpreter.h" #include "rz_util/rz_bitvector.h" +#include "rz_util/rz_str.h" + +static bool value_indicates_ret_addr_write(RzInterpreterAbstrState *state, ProtoIntrprAbstrData *val) { + return val->is_concrete && + (rz_bv_to_ut64(val->bv) == state->bb_addr + state->bb_size || + // Sparc stores the call instruction PC into o8. + // The return instruction jumps then to o7+8. + (rz_str_startswith(state->arch_name, "sparc") && rz_bv_to_ut64(val->bv) == rz_bv_to_ut64(AD(state->pc->abstr_data)->bv))); +} RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, @@ -12,7 +21,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, HtUP /**/ *yield_queues, RzThreadQueue /**/ *io_request, RzThreadQueue /**/ *io_result, - void *plugin_data) { + ProtoIntrprPluginData *plugin_data) { STACK_ABSTR_DATA_OUT(eval_out); ProtoIntrprAbstrData *pc = AD(state->pc->abstr_data); @@ -59,10 +68,12 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, } RzILVarKind kind = effect->op.set.is_local ? RZ_IL_VAR_KIND_LOCAL : RZ_IL_VAR_KIND_GLOBAL; write_var_to_state(state, kind, vhash, &eval_out); - if (eval_out.is_concrete && - kind == RZ_IL_VAR_KIND_GLOBAL && - rz_bv_to_ut64(eval_out.bv) == state->bb_addr + state->bb_size) { - report_yield_str_pc_ret_loc(state, yield_queues, state->bb_addr, rz_bv_to_ut64(pc->bv), &eval_out, false); + if (value_indicates_ret_addr_write(state, &eval_out) && + kind == RZ_IL_VAR_KIND_GLOBAL) { + plugin_data->call_cand.store_addr = rz_bv_to_ut64(pc->bv); + plugin_data->call_cand.npc = state->bb_addr + state->bb_size; + plugin_data->call_cand.bb_addr = state->bb_addr; + plugin_data->call_cand.in_mem = false; } break; } @@ -84,6 +95,13 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, // Everything is just a jump for it at this point. report_yield_xref(state, insn_pkt_size, yield_queues, rz_bv_to_ut64(pc->bv), &eval_out, RZ_ANALYSIS_XREF_TYPE_CODE); } + if (plugin_data->call_cand.store_addr) { + // An instruction in this basic block stored the next PC. + // Report a call candidate. + plugin_data->call_cand.jmp_addr = rz_bv_to_ut64(pc->bv); + report_yield_call_candiate(state, yield_queues, plugin_data); + memset(&plugin_data->call_cand, 0, sizeof(plugin_data->call_cand)); + } copy_abstr_data(state->pc->abstr_data, &eval_out); break; } @@ -142,9 +160,11 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, rz_bv_fini(st_addr.bv); break; } - if (eval_out.is_concrete && - rz_bv_to_ut64(eval_out.bv) == state->bb_addr + state->bb_size) { - report_yield_str_pc_ret_loc(state, yield_queues, state->bb_addr, rz_bv_to_ut64(pc->bv), &eval_out, true); + if (value_indicates_ret_addr_write(state, &eval_out)) { + plugin_data->call_cand.store_addr = rz_bv_to_ut64(pc->bv); + plugin_data->call_cand.npc = state->bb_addr + state->bb_size; + plugin_data->call_cand.bb_addr = state->bb_addr; + plugin_data->call_cand.in_mem = true; } report_yield_xref(state, insn_pkt_size, yield_queues, rz_bv_to_ut64(pc->bv), &st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); if (!store_abstr_data(state, mem_idx, &st_addr, &eval_out, io_request, io_result)) { diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index ce97e1ebc39..272d2262742 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -15,8 +15,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( HtUP /**/ *yield_queues, RzThreadQueue /**/ *io_request, RzThreadQueue /**/ *io_result, - void *plugin_data) { - + ProtoIntrprPluginData *plugin_data) { switch (pure->code) { default: case RZ_IL_OP_VAR: { From fb30512ca15c22166c5f38e43a8f37ec07d21ada Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 3 Feb 2026 19:46:52 +0100 Subject: [PATCH 174/334] Some clean ups --- librz/core/cmd/cmd_inquiry.c | 14 ++++++++++++++ librz/inquiry/inquiry.c | 22 ++++------------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 9661a111764..1c5a7cd1e10 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -31,5 +31,19 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar } bool success = rz_inquiry_interpreter(core, entry_points); + printf("Found call candidates:\n"); + RzIterator *it = ht_up_as_iter(core->inquiry->call_candidates); + RzAnalysisCallCandidate **v; + rz_iterator_foreach(it, v) { + RzAnalysisCallCandidate *cc = *v; + printf("\n"); + printf("\tbb_addr = 0x%" PFMT64x "\n", cc->bb_addr); + printf("\tstore_addr = 0x%" PFMT64x "\n", cc->store_addr); + printf("\tjmp_addr = 0x%" PFMT64x "\n", cc->jmp_addr); + printf("\tnpc = 0x%" PFMT64x "\n", cc->npc); + printf("\tin_mem = %s\n", rz_str_bool(cc->in_mem)); + } + rz_iterator_free(it); + return success ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; } diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 6d3c616bebc..3d24fca5c8f 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -471,7 +471,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent // IL CACHE // ========= // - // This block mimics the IL cache. + // This block mimics the IL cache. It uplifts basic blocks, + // caches them, logs them in RzAnalysis. { if (!rz_th_queue_is_empty(iset->addr_queue)) { ut64 *addr = NULL; @@ -480,8 +481,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent break; } RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x "\n", (*addr)); - RzInterpreterILBB *bb; - if (!ht_up_find(il_cache, *addr, NULL)) { + RzInterpreterILBB *bb = ht_up_find(il_cache, *addr, NULL); + if (!bb) { RZ_LOG_DEBUG("INQUIRY: Lift new BB\n"); size_t bb_size = 0; bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr, &bb_size); @@ -501,7 +502,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent ht_up_insert(il_cache, bb->bb_addr, bb); } else { RZ_LOG_DEBUG("INQUIRY: Serve BB from cache\n"); - bb = ht_up_find(il_cache, *addr, NULL); } rz_th_queue_push(iset->il_queue, bb, true); } @@ -615,20 +615,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent RZ_LOG_DEBUG("INQUIRY: Done\n"); - printf("Found call candidates:\n"); - RzIterator *it = ht_up_as_iter(core->inquiry->call_candidates); - RzAnalysisCallCandidate **v; - rz_iterator_foreach(it, v) { - RzAnalysisCallCandidate *cc = *v; - printf("\n"); - printf("\tbb_addr = 0x%" PFMT64x "\n", cc->bb_addr); - printf("\tstore_addr = 0x%" PFMT64x "\n", cc->store_addr); - printf("\tjmp_addr = 0x%" PFMT64x "\n", cc->jmp_addr); - printf("\tnpc = 0x%" PFMT64x "\n", cc->npc); - printf("\tin_mem = %s\n", rz_str_bool(cc->in_mem)); - } - rz_iterator_free(it); - rz_config_set(core->config, "io.cache", io_cache_opt); // Wait for thread to finish before cleaning. From 41e88a63b128df03d5b36b6626e89659447d18ad Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 5 Feb 2026 16:46:39 +0100 Subject: [PATCH 175/334] Add basic block CFG as wrapper around RzGraph. --- librz/include/rz_inquiry.h | 38 +++++++++++- librz/inquiry/bb_cfg.c | 124 +++++++++++++++++++++++++++++++++++++ librz/inquiry/inquiry.c | 16 ++++- librz/inquiry/meson.build | 1 + 4 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 librz/inquiry/bb_cfg.c diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index f5933dbc144..41a110e3444 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -19,6 +19,32 @@ typedef struct rz_inquiry_plugin_t { RzInterpreterPlugin *p_interpreter; } RzInquiryPlugin; +/** + * \brief A control flow graph with basic blocks as nodes. + */ +typedef struct { + /** + * \brief Indexed by start address of basic block. + */ + HtUP /**/ *basic_blocks; + + /** + * \brief Maps a basic block address to its node index in the RzGraph instance. + */ + HtUU *bb_gidx_map; + + /** + * \brief Maps a basic block address to the RzgraphNode pointer of the RzGraph instance. + */ + HtUP /**/ *bb_gnode_map; + + /** + * \brief The CFG discovered during interpretation. + * The node data are the addresses of basic blocks, cast to (void *). + */ + RzGraph *graph; +} RzInquiryBBCFG; + typedef struct { /** * \brief RzInquiry interpreter plugins. Indexed by name. @@ -26,9 +52,19 @@ typedef struct { HtSP /**/ *plugins; HtSP /**/ *plugins_data; - HtUP /**/ *call_candidates; ///< Indexed by address of candidate instruction. + HtUP /**/ *call_candidates; ///< Indexed by address of call candidate instruction. + RzVector /**/ *xrefs; ///< All xrefs it detected. + RzInquiryBBCFG *bb_cfg; ///< The control flow graph all the basic blocks build. } RzInquiry; +RZ_IPI RZ_OWN RzInquiryBBCFG *rz_inquiry_bb_cfg_new(); +RZ_IPI void rz_inquiry_bb_cfg_free(RZ_NULLABLE RZ_OWN RzInquiryBBCFG *bb_cfg); +RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size); + +RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref); + +RZ_IPI bool rz_inquiry_fill_bb_cfg(RzInquiry *iq); + RZ_API bool rz_inquiry_plugin_add(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NONNULL RzInquiryPlugin *plugin); RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NONNULL RzInquiryPlugin *plugin); diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c new file mode 100644 index 00000000000..e094f416e0f --- /dev/null +++ b/librz/inquiry/bb_cfg.c @@ -0,0 +1,124 @@ +// SPDX-FileCopyrightText: 2026 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#include "rz_util/ht_up.h" +#include "rz_util/ht_uu.h" +#include "rz_util/rz_assert.h" +#include "rz_util/rz_graph.h" +#include "rz_util/rz_log.h" +#include + +RZ_IPI RZ_OWN RzInquiryBBCFG *rz_inquiry_bb_cfg_new() { + RzInquiryBBCFG *bb_cfg = RZ_NEW0(RzInquiryBBCFG); + if (!bb_cfg) { + return NULL; + } + bb_cfg->basic_blocks = ht_up_new(NULL, free); + bb_cfg->bb_gnode_map = ht_up_new(NULL, NULL); + bb_cfg->bb_gidx_map = ht_uu_new(); + bb_cfg->graph = rz_graph_new(); + if (!bb_cfg->basic_blocks || + !bb_cfg->bb_gnode_map || + !bb_cfg->bb_gidx_map || + !bb_cfg->graph) { + rz_inquiry_bb_cfg_free(bb_cfg); + return NULL; + } + return bb_cfg; +} + +RZ_IPI void rz_inquiry_bb_cfg_free(RZ_NULLABLE RZ_OWN RzInquiryBBCFG *bb_cfg) { + if (!bb_cfg) { + return; + } + ht_uu_free(bb_cfg->bb_gidx_map); + ht_up_free(bb_cfg->basic_blocks); + ht_up_free(bb_cfg->bb_gnode_map); + rz_graph_free(bb_cfg->graph); + free(bb_cfg); +} + +RZ_IPI bool rz_inquiry_bb_cfg_add_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size) { + RzInterval *bb = ht_up_find(cfg->basic_blocks, addr, NULL); + if (bb && bb->size == size) { + return true; + } else if (bb && bb->size != size) { + RZ_LOG_ERROR("inquiry: Attempt to overwrite basic block size. Ignoring new version: " + "previous: (0x%" PFMT64x ", %" PFMT64d ") - new: (0x%" PFMT64x ", %" PFMT64d ")\n", + bb->addr, bb->size, addr, size); + return false; + } + bb = rz_itv_new(addr, size); + ht_up_insert(cfg->basic_blocks, addr, bb); + + return true; +} + +/** + * \brief Adds new node or returns existing one. + */ +static /*const*/ RZ_BORROW RzGraphNode *get_add_node_to_cfg(RzInquiryBBCFG *cfg, ut64 bb_addr) { + bool found = false; + RzGraphNode *n = ht_up_find(cfg->bb_gnode_map, bb_addr, &found); + if (found) { + return n; + } + n = rz_graph_add_node(cfg->graph, (void *)bb_addr); + if (!n) { + return NULL; + } + ht_uu_insert(cfg->bb_gidx_map, bb_addr, n->idx); + ht_up_insert(cfg->bb_gnode_map, bb_addr, n); + return n; +} + +static bool add_edge_to_cfg(RzInquiryBBCFG *cfg, ut64 from, ut64 to) { + RzGraphNode *f = get_add_node_to_cfg(cfg, from); + RzGraphNode *t = get_add_node_to_cfg(cfg, to); + if (!f || !t) { + rz_warn_if_reached(); + return false; + } + rz_graph_add_edge(cfg->graph, f, t); + return true; +} + +RZ_IPI bool rz_inquiry_fill_bb_cfg(RzInquiry *iq) { + if (ht_up_size(iq->bb_cfg->basic_blocks) == 0) { + RZ_LOG_WARN("No basic blocks present to fill CFG.\n"); + return false; + } + RzAnalysisXRef *xref; + rz_vector_foreach (iq->xrefs, xref) { + if (xref->type != RZ_ANALYSIS_XREF_TYPE_CODE) { + continue; + } + void **it; + RzIterator *bb_iter = ht_up_as_iter(iq->bb_cfg->basic_blocks); + rz_iterator_foreach(bb_iter, it) { + RzInterval *bb = *it; + if (!rz_itv_contain(*bb, xref->from)) { + continue; + } + add_edge_to_cfg(iq->bb_cfg, bb->addr, xref->to); + } + } + return true; +} + +RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size) { + if (ht_up_find(cfg->basic_blocks, addr, NULL)) { + return true; + } + + if (!get_add_node_to_cfg(cfg, addr)) { + rz_warn_if_reached(); + return false; + } + RzInterval *bb = rz_itv_new(addr, size); + if (!bb || !ht_up_insert(cfg->basic_blocks, addr, bb)) { + rz_warn_if_reached(); + return false; + } + return true; +} diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 3d24fca5c8f..229038b4a68 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -91,12 +91,16 @@ RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void) { iq->plugins = ht_sp_new(HT_STR_CONST, NULL, NULL); iq->plugins_data = ht_sp_new(HT_STR_CONST, NULL, NULL); iq->call_candidates = ht_up_new(NULL, free); - if (!iq->plugins || !iq->plugins_data) { + iq->xrefs = rz_vector_new(sizeof(RzAnalysisXRef), NULL, NULL); + iq->bb_cfg = rz_inquiry_bb_cfg_new(); + if (!iq->plugins || !iq->plugins_data || !iq->bb_cfg) { ht_sp_free(iq->plugins); ht_sp_free(iq->plugins_data); + rz_inquiry_bb_cfg_free(iq->bb_cfg); free(iq); return NULL; } + for (size_t i = 0; i < RZ_ARRAY_SIZE(inquiry_static_plugins); ++i) { rz_inquiry_plugin_add(iq, inquiry_static_plugins[i]); } @@ -113,9 +117,17 @@ RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *iq) { ht_sp_free(iq->plugins); ht_sp_free(iq->plugins_data); ht_up_free(iq->call_candidates); + rz_inquiry_bb_cfg_free(iq->bb_cfg); + rz_vector_free(iq->xrefs); free(iq); } +RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref) { + RzAnalysisXRef clone = { 0 }; + memcpy(&clone, xref, sizeof(RzAnalysisXRef)); + rz_vector_push(iq->xrefs, &clone); +} + RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzPVector /**/ *allowed_segments) { rz_return_val_if_fail(xref_to_addr && allowed_segments, false); void **it; @@ -303,6 +315,7 @@ static bool handle_yields(RzCore *core, HtUP *yield_queues) { if (!rz_th_queue_pop(q_xrefs->yield_queue, false, (void **)&xref) || !xref) { return false; } + rz_inquiry_add_xref(core->inquiry, xref); rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, xref->type); RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)); } @@ -500,6 +513,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent rz_analysis_add_bb(core->analysis, *addr, bb_size); RZ_LOG_DEBUG("INQUIRY: Send IL result: %p.\n", bb); ht_up_insert(il_cache, bb->bb_addr, bb); + rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, bb->bb_addr, bb->size); } else { RZ_LOG_DEBUG("INQUIRY: Serve BB from cache\n"); } diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index b340a2c4714..af785faa95c 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -13,6 +13,7 @@ inquiry_plugins = { rz_inquiry_sources = [ 'inquiry.c', + 'bb_cfg.c', 'inquiry_helpers.c', ] From 847bd7e6ceb094ee46d8a2db0e5f6cd931815280 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 5 Feb 2026 18:50:24 +0100 Subject: [PATCH 176/334] Unfinished function detection algorithm. --- librz/include/rz_inquiry.h | 23 +++ .../inquiry/algorithms/revng_fcn_detection.c | 173 ++++++++++++++++++ librz/inquiry/bb_cfg.c | 42 ++++- librz/inquiry/inquiry.c | 23 +++ librz/inquiry/meson.build | 3 +- 5 files changed, 259 insertions(+), 5 deletions(-) create mode 100644 librz/inquiry/algorithms/revng_fcn_detection.c diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 41a110e3444..49dc1e8d653 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -60,6 +60,9 @@ typedef struct { RZ_IPI RZ_OWN RzInquiryBBCFG *rz_inquiry_bb_cfg_new(); RZ_IPI void rz_inquiry_bb_cfg_free(RZ_NULLABLE RZ_OWN RzInquiryBBCFG *bb_cfg); RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size); +RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RzInterval *bb); +RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb); +RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours(const RzInquiryBBCFG *cfg, ut64 bb_addr); RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref); @@ -77,6 +80,26 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL co RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *entry_points); +//============ +// Algorithms +//============ + +/** + * \brief A simple function deduced by inquiry algorithms. + */ +typedef struct { + RzVector /**/ *entry_points; + RzInquiryBBCFG *bb_cfg; +} RzInquiryFunction; + +RZ_IPI RZ_OWN RzInquiryFunction *rz_inquiry_function_new(); +RZ_IPI void rz_inquiry_function_free(RZ_NULLABLE RZ_OWN RzInquiryFunction *fcn); + +RZ_API bool rz_inquiry_algo_revng_fcn_detection( + const HtUP /**/ *call_candidates, + const RzInquiryBBCFG *bb_cfg, + RZ_OUT RzPVector /**/ *fcns); + #ifdef __cplusplus } #endif diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c new file mode 100644 index 00000000000..7edd7991971 --- /dev/null +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -0,0 +1,173 @@ +// SPDX-FileCopyrightText: 2026 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +/** + * \file This is the function detection algorithm from the + * paper: "REV.NG: A Unified Binary Analysis Framework to Recover CFGs and Function Boundaries" + * chapter: "4.2 Function boundaries recovery" + * doi: 10.1145/3033019.3033028 + * + * The algorithm here differs in a few ways to fit the assumptions Rizin makes + * and simplify implementation. + * + * 1. Function Call: A branch instruction with any store of the next PC **in its basic block**. + * - That introduces somewhat more inaccuracy. + * But is easier to implement for a prototype. + * 2. Return: Any branch instruction whose destination is a return address. + * - Remove requirement for indirect branch and don't treat unknown targets as return. + * Easier to implement. + * 3. Syscalls are ignored. They are not supported by RzIL, yet. + * 4. longjmps/killer basic blocks are ignored. - Easier to implement. + */ + +#include "rz_analysis.h" +#include "rz_list.h" +#include "rz_types_base.h" +#include "rz_util/ht_up.h" +#include "rz_util/rz_assert.h" +#include "rz_util/rz_iterator.h" +#include "rz_util/rz_set.h" +#include +#include +#include + +static int cmp(const void *a, const void *b, void *user) { + if ((ut64)a > (ut64)b) { + return 1; + } else if ((ut64)a < (ut64)b) { + return -1; + } + return 0; +} + +static void recurse_into_fcn_bbs( + RzInquiryFunction *fcn, + ut64 predecessor_bb_addr, + ut64 this_bb_addr, + RzSetU *visited_fcn_bbs, + RZ_OUT RzSetU *tail_called_addr, + const RzList /**/ *cfep_addresses, + const RzInquiryBBCFG *binary_bb_cfg) { + if (rz_set_u_contains(visited_fcn_bbs, this_bb_addr)) { + return; + } + rz_set_u_add(visited_fcn_bbs, this_bb_addr); + + // + // Add edge + // + RzInterval this_bb = { 0 }; + if (!rz_inquiry_bb_cfg_get_basic_block(binary_bb_cfg, this_bb_addr, &this_bb)) { + goto warn_return; + } + if (!rz_inquiry_bb_cfg_add_basic_block(fcn->bb_cfg, this_bb.addr, this_bb.size)) { + goto warn_return; + } + + if (predecessor_bb_addr != UT64_MAX) { + RzInterval from_bb = { 0 }; + if (!rz_inquiry_bb_cfg_get_basic_block(binary_bb_cfg, predecessor_bb_addr, &from_bb)) { + goto warn_return; + } + if (!rz_inquiry_bb_cfg_add_basic_block(fcn->bb_cfg, from_bb.addr, from_bb.size)) { + goto warn_return; + } + if (!rz_inquiry_bb_cfg_add_edge(fcn->bb_cfg, predecessor_bb_addr, this_bb_addr)) { + goto warn_return; + } + } + + // + // Visit neighbors + // + const RzList *successors = rz_inquiry_bb_cfg_get_neighbours(binary_bb_cfg, this_bb_addr); + if (!successors) { + goto warn_return; + } + + RzListIter *lit; + const RzGraphNode *s; + rz_list_foreach (successors, lit, s) { + ut64 succ_addr = (ut64)s->data; + if (rz_list_contains(cfep_addresses, (utptr *)succ_addr) || + rz_set_u_contains(tail_called_addr, succ_addr)) { + continue; + } + // Check if the successor lies past a cfep candidate. + // That indicates it might be a tail jump. + + utptr *val; + RzListIter *it; + rz_list_foreach (cfep_addresses, it, val) { + ut64 cfep_addr = (ut64)val; + if (!(cfep_addr != this_bb_addr && + (RZ_BETWEEN_EXCL(this_bb_addr, cfep_addr, succ_addr) || + RZ_BETWEEN_EXCL(succ_addr, cfep_addr, this_bb_addr)))) { + // The jump goes not over cfep_addr + continue; + } + // The jump goes over a cfep candidate. + // Assume the succ_addr is a tail call target. + // Log it and don't recurse. + rz_set_u_add(tail_called_addr, succ_addr); + continue; + } + + recurse_into_fcn_bbs( + fcn, + this_bb_addr, + succ_addr, + visited_fcn_bbs, + tail_called_addr, + cfep_addresses, + binary_bb_cfg); + } + +warn_return: + rz_warn_if_reached(); + return; +} + +RZ_API bool rz_inquiry_algo_revng_fcn_detection( + RZ_NONNULL const HtUP /**/ *call_candidates, + RZ_NONNULL const RzInquiryBBCFG *binary_bb_cfg, + RZ_NONNULL RZ_OUT RzPVector /**/ *fcns) { + rz_return_val_if_fail(call_candidates && binary_bb_cfg && fcns, false); + + // Candidate function entry points + RzList *cfep_addresses = rz_list_new(); + void **it; + RzIterator *iter = ht_up_as_iter(call_candidates); + rz_iterator_foreach(iter, it) { + RzAnalysisCallCandidate *cc = *it; + rz_list_push(cfep_addresses, (utptr *)cc->jmp_addr); + } + rz_iterator_free(iter); + rz_list_sort(cfep_addresses, cmp, NULL); + + // Tail calls discovered by this algorithm. + RzSetU *tail_called_addr = rz_set_u_new(); + + utptr *data; + RzListIter *lit; + rz_list_foreach (cfep_addresses, lit, data) { + RzSetU *visited_bbs = rz_set_u_new(); + RzInquiryFunction *fcn = rz_inquiry_function_new(); + recurse_into_fcn_bbs(fcn, UT64_MAX, (ut64)data, visited_bbs, tail_called_addr, cfep_addresses, binary_bb_cfg); + rz_set_u_free(visited_bbs); + rz_pvector_push(fcns, fcn); + } + rz_iterator_free(iter); + + if (rz_set_u_size(tail_called_addr) == 0) { + rz_set_u_free(tail_called_addr); + rz_list_free(cfep_addresses); + return true; + } + + // Handle tail calls. + + rz_set_u_free(tail_called_addr); + rz_list_free(cfep_addresses); + return true; +} diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index e094f416e0f..2b349c965d3 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -5,6 +5,7 @@ #include "rz_util/ht_uu.h" #include "rz_util/rz_assert.h" #include "rz_util/rz_graph.h" +#include "rz_util/rz_itv.h" #include "rz_util/rz_log.h" #include @@ -72,9 +73,19 @@ static /*const*/ RZ_BORROW RzGraphNode *get_add_node_to_cfg(RzInquiryBBCFG *cfg, return n; } -static bool add_edge_to_cfg(RzInquiryBBCFG *cfg, ut64 from, ut64 to) { - RzGraphNode *f = get_add_node_to_cfg(cfg, from); - RzGraphNode *t = get_add_node_to_cfg(cfg, to); +/** + * \brief Adds an edge to the basic block CFG. + * + * \param cfg The basic block CFG to edit. + * \param from_bb The address of the basic block with the branch. + * Not the address of the branch instruction! + * \param to_bb The address of the basic block the branch leads to. + * + * \return False if an error occurred. True otherwise. + */ +RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb) { + RzGraphNode *f = get_add_node_to_cfg(cfg, from_bb); + RzGraphNode *t = get_add_node_to_cfg(cfg, to_bb); if (!f || !t) { rz_warn_if_reached(); return false; @@ -83,6 +94,29 @@ static bool add_edge_to_cfg(RzInquiryBBCFG *cfg, ut64 from, ut64 to) { return true; } +RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RzInterval *bb) { + rz_return_val_if_fail(cfg && bb, false); + const RzInterval *itv = ht_up_find(cfg->basic_blocks, bb_addr, NULL); + if (!itv) { + rz_warn_if_reached(); + return false; + } + bb->addr = itv->addr; + bb->size = itv->size; + return true; +} + +RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours(const RzInquiryBBCFG *cfg, ut64 bb_addr) { + rz_return_val_if_fail(cfg, NULL); + + const RzGraphNode *n = ht_up_find(cfg->bb_gnode_map, bb_addr, NULL); + if (!n) { + rz_warn_if_reached(); + return NULL; + } + return rz_graph_get_neighbours(cfg->graph, n); +} + RZ_IPI bool rz_inquiry_fill_bb_cfg(RzInquiry *iq) { if (ht_up_size(iq->bb_cfg->basic_blocks) == 0) { RZ_LOG_WARN("No basic blocks present to fill CFG.\n"); @@ -100,7 +134,7 @@ RZ_IPI bool rz_inquiry_fill_bb_cfg(RzInquiry *iq) { if (!rz_itv_contain(*bb, xref->from)) { continue; } - add_edge_to_cfg(iq->bb_cfg, bb->addr, xref->to); + rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, xref->to); } } return true; diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 229038b4a68..253f77b1133 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -83,6 +83,29 @@ RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OW return false; } +RZ_IPI void rz_inquiry_function_free(RZ_NULLABLE RZ_OWN RzInquiryFunction *fcn) { + if (!fcn) { + return; + } + rz_inquiry_bb_cfg_free(fcn->bb_cfg); + rz_vector_free(fcn->entry_points); + free(fcn); +} + +RZ_IPI RZ_OWN RzInquiryFunction *rz_inquiry_function_new() { + RzInquiryFunction *fcn = RZ_NEW0(RzInquiryFunction); + if (!fcn) { + return NULL; + } + fcn->bb_cfg = rz_inquiry_bb_cfg_new(); + fcn->entry_points = rz_vector_new(sizeof(ut64), NULL, NULL); + if (!fcn->bb_cfg || !fcn->entry_points) { + rz_inquiry_function_free(fcn); + return NULL; + } + return fcn; +} + RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void) { RzInquiry *iq = RZ_NEW0(RzInquiry); if (!iq) { diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index af785faa95c..9ef58b10e90 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -12,8 +12,9 @@ inquiry_plugins = { } rz_inquiry_sources = [ - 'inquiry.c', + 'algorithms/revng_fcn_detection.c', 'bb_cfg.c', + 'inquiry.c', 'inquiry_helpers.c', ] From ef9ca1bba7d19838d6fca8192f7d48d1f77262fd Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 7 Feb 2026 21:54:47 +0100 Subject: [PATCH 177/334] Finish function detection. --- .../inquiry/algorithms/revng_fcn_detection.c | 98 ++++++++++++------- 1 file changed, 60 insertions(+), 38 deletions(-) diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index 7edd7991971..db9aad78de7 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -46,6 +46,7 @@ static void recurse_into_fcn_bbs( ut64 this_bb_addr, RzSetU *visited_fcn_bbs, RZ_OUT RzSetU *tail_called_addr, + const RzSetU *return_addresses, const RzList /**/ *cfep_addresses, const RzInquiryBBCFG *binary_bb_cfg) { if (rz_set_u_contains(visited_fcn_bbs, this_bb_addr)) { @@ -89,38 +90,38 @@ static void recurse_into_fcn_bbs( const RzGraphNode *s; rz_list_foreach (successors, lit, s) { ut64 succ_addr = (ut64)s->data; - if (rz_list_contains(cfep_addresses, (utptr *)succ_addr) || + if (rz_set_u_contains((RzSetU *)return_addresses, succ_addr) || + rz_list_contains(cfep_addresses, (utptr *)succ_addr) || rz_set_u_contains(tail_called_addr, succ_addr)) { continue; } - // Check if the successor lies past a cfep candidate. - // That indicates it might be a tail jump. utptr *val; RzListIter *it; rz_list_foreach (cfep_addresses, it, val) { ut64 cfep_addr = (ut64)val; - if (!(cfep_addr != this_bb_addr && - (RZ_BETWEEN_EXCL(this_bb_addr, cfep_addr, succ_addr) || - RZ_BETWEEN_EXCL(succ_addr, cfep_addr, this_bb_addr)))) { - // The jump goes not over cfep_addr + if (cfep_addr != this_bb_addr && + (RZ_BETWEEN_EXCL(this_bb_addr, cfep_addr, succ_addr) || + RZ_BETWEEN_EXCL(succ_addr, cfep_addr, this_bb_addr))) { + // The jump goes over a cfep candidate. + // Assume the succ_addr is a tail call target. + // Log it and don't recurse. + rz_set_u_add(tail_called_addr, succ_addr); continue; } - // The jump goes over a cfep candidate. - // Assume the succ_addr is a tail call target. - // Log it and don't recurse. - rz_set_u_add(tail_called_addr, succ_addr); - continue; - } - recurse_into_fcn_bbs( - fcn, - this_bb_addr, - succ_addr, - visited_fcn_bbs, - tail_called_addr, - cfep_addresses, - binary_bb_cfg); + // The jump goes not over cfep so it should be a + // valid basic block of this function. + recurse_into_fcn_bbs( + fcn, + this_bb_addr, + succ_addr, + visited_fcn_bbs, + tail_called_addr, + return_addresses, + cfep_addresses, + binary_bb_cfg); + } } warn_return: @@ -135,11 +136,13 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( rz_return_val_if_fail(call_candidates && binary_bb_cfg && fcns, false); // Candidate function entry points + RzSetU *return_addresses = rz_set_u_new(); RzList *cfep_addresses = rz_list_new(); void **it; RzIterator *iter = ht_up_as_iter(call_candidates); rz_iterator_foreach(iter, it) { RzAnalysisCallCandidate *cc = *it; + rz_set_u_add(return_addresses, cc->npc); rz_list_push(cfep_addresses, (utptr *)cc->jmp_addr); } rz_iterator_free(iter); @@ -147,25 +150,44 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( // Tail calls discovered by this algorithm. RzSetU *tail_called_addr = rz_set_u_new(); + // Set of handled cfep + RzSetU *cfep_handled = rz_set_u_new(); + + do { + utptr *data; + RzListIter *lit; + rz_list_foreach (cfep_addresses, lit, data) { + if (rz_set_u_contains(cfep_handled, (ut64)data)) { + continue; + } - utptr *data; - RzListIter *lit; - rz_list_foreach (cfep_addresses, lit, data) { - RzSetU *visited_bbs = rz_set_u_new(); - RzInquiryFunction *fcn = rz_inquiry_function_new(); - recurse_into_fcn_bbs(fcn, UT64_MAX, (ut64)data, visited_bbs, tail_called_addr, cfep_addresses, binary_bb_cfg); - rz_set_u_free(visited_bbs); - rz_pvector_push(fcns, fcn); - } - rz_iterator_free(iter); - - if (rz_set_u_size(tail_called_addr) == 0) { - rz_set_u_free(tail_called_addr); - rz_list_free(cfep_addresses); - return true; - } + RzSetU *visited_bbs = rz_set_u_new(); + RzInquiryFunction *fcn = rz_inquiry_function_new(); + recurse_into_fcn_bbs(fcn, + UT64_MAX, + (ut64)data, + visited_bbs, + tail_called_addr, + return_addresses, + cfep_addresses, + binary_bb_cfg); + rz_set_u_free(visited_bbs); + rz_set_u_add(cfep_handled, (ut64)data); + rz_pvector_push(fcns, fcn); + } - // Handle tail calls. + iter = rz_set_u_as_iter(tail_called_addr); + ut64 *val; + rz_iterator_foreach(iter, val) { + ut64 tail_cfep = *val; + if (rz_list_find_val(cfep_addresses, (utptr *)tail_cfep)) { + continue; + } + rz_list_add_sorted(cfep_addresses, (utptr *)tail_cfep, cmp, NULL); + } + rz_iterator_free(iter); + rz_set_u_clean(tail_called_addr); + } while (rz_set_u_size(tail_called_addr) != 0); rz_set_u_free(tail_called_addr); rz_list_free(cfep_addresses); From 95b2e0dd97c0c3e83f52360d8da00f553c21390b Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 7 Feb 2026 22:13:10 +0100 Subject: [PATCH 178/334] Perform function detection in aaaaIp --- librz/core/cmd/cmd_inquiry.c | 39 +++++++++++++++++++++++++----------- librz/include/rz_inquiry.h | 3 ++- librz/inquiry/inquiry.c | 2 +- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 1c5a7cd1e10..1f74d92d8e8 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -31,19 +31,34 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar } bool success = rz_inquiry_interpreter(core, entry_points); - printf("Found call candidates:\n"); - RzIterator *it = ht_up_as_iter(core->inquiry->call_candidates); - RzAnalysisCallCandidate **v; - rz_iterator_foreach(it, v) { - RzAnalysisCallCandidate *cc = *v; - printf("\n"); - printf("\tbb_addr = 0x%" PFMT64x "\n", cc->bb_addr); - printf("\tstore_addr = 0x%" PFMT64x "\n", cc->store_addr); - printf("\tjmp_addr = 0x%" PFMT64x "\n", cc->jmp_addr); - printf("\tnpc = 0x%" PFMT64x "\n", cc->npc); - printf("\tin_mem = %s\n", rz_str_bool(cc->in_mem)); + RZ_LOG_INFO("Finished reference recovery."); + + RZ_LOG_INFO("Perform function deduction."); + + RzPVector *fcns = rz_pvector_new((RzPVectorFree)rz_inquiry_function_free); + rz_inquiry_algo_revng_fcn_detection( + core->inquiry->call_candidates, + core->inquiry->bb_cfg, + fcns); + + void **it; + rz_pvector_foreach (fcns, it) { + RzInquiryFunction *fcn = *it; + + char fcn_name[512] = { 0 }; + ut64 fcn_addr = *(ut64 *)rz_vector_head(fcn->entry_points); + rz_strf(fcn_name, "iq_fcn_0x%" PFMT64x, fcn_addr); + RzAnalysisFunction *afcn = rz_analysis_create_function(core->analysis, fcn_name, fcn_addr, RZ_ANALYSIS_FCN_TYPE_FCN); + rz_analysis_add_function(core->analysis, afcn); + + void **it2; + RzIterator *iter = ht_up_as_iter(fcn->bb_cfg->basic_blocks); + rz_iterator_foreach(iter, it2) { + RzInterval *bb = *it2; + RzAnalysisBlock *abb = rz_analysis_create_block(core->analysis, bb->addr, bb->size); + rz_analysis_function_add_block(afcn, abb); + } } - rz_iterator_free(it); return success ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; } diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 49dc1e8d653..e069adbd229 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -93,7 +93,8 @@ typedef struct { } RzInquiryFunction; RZ_IPI RZ_OWN RzInquiryFunction *rz_inquiry_function_new(); -RZ_IPI void rz_inquiry_function_free(RZ_NULLABLE RZ_OWN RzInquiryFunction *fcn); + +RZ_API void rz_inquiry_function_free(RZ_NULLABLE RZ_OWN RzInquiryFunction *fcn); RZ_API bool rz_inquiry_algo_revng_fcn_detection( const HtUP /**/ *call_candidates, diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 253f77b1133..5b3c275dadd 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -83,7 +83,7 @@ RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OW return false; } -RZ_IPI void rz_inquiry_function_free(RZ_NULLABLE RZ_OWN RzInquiryFunction *fcn) { +RZ_API void rz_inquiry_function_free(RZ_NULLABLE RZ_OWN RzInquiryFunction *fcn) { if (!fcn) { return; } From f6925461b2c26ae8bce8b4b575871816e4399df3 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sun, 8 Feb 2026 22:30:26 +0100 Subject: [PATCH 179/334] Renames, sharing settting og bb_cfg edge and more --- librz/core/cmd/cmd_inquiry.c | 2 + librz/include/rz_analysis.h | 2 +- librz/include/rz_inquiry/rz_interpreter.h | 9 ++++- .../inquiry/algorithms/revng_fcn_detection.c | 39 ++++++++++--------- librz/inquiry/bb_cfg.c | 2 +- librz/inquiry/inquiry.c | 33 ++++++++-------- librz/inquiry/interpreter/interpreter.c | 39 ++++++++++--------- .../interpreter/prototype/eval_effect.c | 2 +- 8 files changed, 70 insertions(+), 58 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 1f74d92d8e8..ceb6cd1bb68 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -58,7 +58,9 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar RzAnalysisBlock *abb = rz_analysis_create_block(core->analysis, bb->addr, bb->size); rz_analysis_function_add_block(afcn, abb); } + rz_iterator_free(iter); } + rz_pvector_free(fcns); return success ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; } diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 190d506abc8..955f2ce9925 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -985,7 +985,7 @@ typedef struct rz_analysis_ref_t { typedef struct { ut64 bb_addr; ///< The address of the basic block which stores the NPC. ut64 store_addr; ///< The address of the instruction packet storing the NPC. - ut64 jmp_addr; ///< Address of the call candidate instruction packet. + ut64 candidate_addr; ///< Address of the call candidate instruction packet. ut64 npc; ///< The NPC stored. Should point after a basic block. bool in_mem; ///< True if the NPC is written to memory. False if it is written to a register. } RzAnalysisCallCandidate; diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 4219b163682..a3683eff379 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -54,6 +54,11 @@ typedef struct { void *abstr_data; ///< The abstract data. It is managed by individual interpreter. } RzInterpreterAbstrVal; +typedef struct { + ut64 branching_bb_addr; ///< The address of the bb which branches. + ut64 target_addr; ///< The target address it branches to. +} RzInterpreterBranch; + /** * objects the interpreter should use to send over the queues. * @@ -64,7 +69,7 @@ typedef struct { */ typedef struct { RZ_LIFETIME(RzInquiry) - ut64 shared_addr; ///< The address object passed to an IL cache for BB requests. + RzInterpreterBranch branch; ///< The address object passed to an IL cache for BB requests. RZ_LIFETIME(RzInquiry) RzAnalysisXRef xref; ///< The xref object passed over the queue. @@ -232,7 +237,7 @@ typedef struct { */ typedef struct { RzInterpreterAbstrState *state; ///< The abstract state of the interpreter. - RzThreadQueue /**/ *addr_queue; ///< The queue to send requests to the cache what address to get the next IL op from. + RzThreadQueue /**/ *branch_queue; ///< The queue to send requests to the cache what address to get the next IL op from. RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. // TODO: We need to decide how to distribute the yield. HtUP /**/ *yield_queues; ///< The queues to push the yield of interpretation into. diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index db9aad78de7..2e7108b7279 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -47,7 +47,7 @@ static void recurse_into_fcn_bbs( RzSetU *visited_fcn_bbs, RZ_OUT RzSetU *tail_called_addr, const RzSetU *return_addresses, - const RzList /**/ *cfep_addresses, + const RzVector /**/ *cfep_addresses, const RzInquiryBBCFG *binary_bb_cfg) { if (rz_set_u_contains(visited_fcn_bbs, this_bb_addr)) { return; @@ -91,15 +91,14 @@ static void recurse_into_fcn_bbs( rz_list_foreach (successors, lit, s) { ut64 succ_addr = (ut64)s->data; if (rz_set_u_contains((RzSetU *)return_addresses, succ_addr) || - rz_list_contains(cfep_addresses, (utptr *)succ_addr) || + rz_vector_contains(cfep_addresses, &succ_addr) || rz_set_u_contains(tail_called_addr, succ_addr)) { continue; } - utptr *val; - RzListIter *it; - rz_list_foreach (cfep_addresses, it, val) { - ut64 cfep_addr = (ut64)val; + ut64 *val; + rz_vector_foreach (cfep_addresses, val) { + ut64 cfep_addr = *val; if (cfep_addr != this_bb_addr && (RZ_BETWEEN_EXCL(this_bb_addr, cfep_addr, succ_addr) || RZ_BETWEEN_EXCL(succ_addr, cfep_addr, this_bb_addr))) { @@ -123,6 +122,7 @@ static void recurse_into_fcn_bbs( binary_bb_cfg); } } + return; warn_return: rz_warn_if_reached(); @@ -137,16 +137,16 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( // Candidate function entry points RzSetU *return_addresses = rz_set_u_new(); - RzList *cfep_addresses = rz_list_new(); + RzVector *cfep_addresses = rz_vector_new(sizeof(ut64), NULL, NULL); void **it; RzIterator *iter = ht_up_as_iter(call_candidates); rz_iterator_foreach(iter, it) { RzAnalysisCallCandidate *cc = *it; rz_set_u_add(return_addresses, cc->npc); - rz_list_push(cfep_addresses, (utptr *)cc->jmp_addr); + rz_vector_push(cfep_addresses, &cc->candidate_addr); } rz_iterator_free(iter); - rz_list_sort(cfep_addresses, cmp, NULL); + rz_vector_sort(cfep_addresses, cmp, false, NULL); // Tail calls discovered by this algorithm. RzSetU *tail_called_addr = rz_set_u_new(); @@ -154,25 +154,26 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( RzSetU *cfep_handled = rz_set_u_new(); do { - utptr *data; - RzListIter *lit; - rz_list_foreach (cfep_addresses, lit, data) { - if (rz_set_u_contains(cfep_handled, (ut64)data)) { + ut64 *elem; + rz_vector_foreach (cfep_addresses, elem) { + ut64 cfep_addr = *elem; + if (rz_set_u_contains(cfep_handled, cfep_addr)) { continue; } RzSetU *visited_bbs = rz_set_u_new(); RzInquiryFunction *fcn = rz_inquiry_function_new(); + rz_vector_push(fcn->entry_points, &cfep_addr); recurse_into_fcn_bbs(fcn, UT64_MAX, - (ut64)data, + cfep_addr, visited_bbs, tail_called_addr, return_addresses, cfep_addresses, binary_bb_cfg); rz_set_u_free(visited_bbs); - rz_set_u_add(cfep_handled, (ut64)data); + rz_set_u_add(cfep_handled, cfep_addr); rz_pvector_push(fcns, fcn); } @@ -180,16 +181,18 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( ut64 *val; rz_iterator_foreach(iter, val) { ut64 tail_cfep = *val; - if (rz_list_find_val(cfep_addresses, (utptr *)tail_cfep)) { + if (rz_vector_contains(cfep_addresses, &tail_cfep)) { continue; } - rz_list_add_sorted(cfep_addresses, (utptr *)tail_cfep, cmp, NULL); + rz_vector_push_front(cfep_addresses, &tail_cfep); } rz_iterator_free(iter); rz_set_u_clean(tail_called_addr); } while (rz_set_u_size(tail_called_addr) != 0); + rz_set_u_free(cfep_handled); rz_set_u_free(tail_called_addr); - rz_list_free(cfep_addresses); + rz_set_u_free(return_addresses); + rz_vector_free(cfep_addresses); return true; } diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index 2b349c965d3..12612df76c6 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -98,7 +98,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb rz_return_val_if_fail(cfg && bb, false); const RzInterval *itv = ht_up_find(cfg->basic_blocks, bb_addr, NULL); if (!itv) { - rz_warn_if_reached(); + RZ_LOG_WARN("Could not find BB at 0x%" PFMT64x "\n", bb_addr); return false; } bb->addr = itv->addr; diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 5b3c275dadd..1af5509225c 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -351,10 +351,10 @@ static bool handle_yields(RzCore *core, HtUP *yield_queues) { } RzAnalysisCallCandidate *cc_clone = RZ_NEW0(RzAnalysisCallCandidate); memcpy(cc_clone, cc, sizeof(RzAnalysisCallCandidate)); - if (!ht_up_update(core->inquiry->call_candidates, cc_clone->jmp_addr, cc_clone)) { - RZ_LOG_DEBUG("Overwrote a call candidate located at 0x%" PFMT64x "\n", cc_clone->jmp_addr); + if (!ht_up_update(core->inquiry->call_candidates, cc_clone->candidate_addr, cc_clone)) { + RZ_LOG_DEBUG("Overwrote a call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); } else { - RZ_LOG_DEBUG("Added call candidate located at 0x%" PFMT64x "\n", cc_clone->jmp_addr); + RZ_LOG_DEBUG("Added call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); } } return true; @@ -476,7 +476,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent bool bb_decode_failed = false; // Clear queues from any left overs of previous runs. rz_list_free(rz_th_queue_pop_all(iset->il_queue)); - rz_list_free(rz_th_queue_pop_all(iset->addr_queue)); + rz_list_free(rz_th_queue_pop_all(iset->branch_queue)); // Dispatch prototype interpreter into a thread. RZ_LOG_DEBUG("INQUIRY: Start main interpretation thread.\n"); @@ -510,30 +510,31 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent // This block mimics the IL cache. It uplifts basic blocks, // caches them, logs them in RzAnalysis. { - if (!rz_th_queue_is_empty(iset->addr_queue)) { - ut64 *addr = NULL; - if (!rz_th_queue_pop(iset->addr_queue, false, (void **)&addr) || !addr) { + if (!rz_th_queue_is_empty(iset->branch_queue)) { + RzInterpreterBranch *branch = NULL; + if (!rz_th_queue_pop(iset->branch_queue, false, (void **)&branch) || !branch) { rz_warn_if_reached(); break; } - RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x "\n", (*addr)); - RzInterpreterILBB *bb = ht_up_find(il_cache, *addr, NULL); + RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x "\n", branch->target_addr); + RzInterpreterILBB *bb = ht_up_find(il_cache, branch->target_addr, NULL); if (!bb) { RZ_LOG_DEBUG("INQUIRY: Lift new BB\n"); size_t bb_size = 0; - bb = rz_inquiry_gen_il_bb(core->analysis, core->io, *addr, &bb_size); + bb = rz_inquiry_gen_il_bb(core->analysis, core->io, branch->target_addr, &bb_size); if (!bb) { - RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", *addr); + RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", branch->target_addr); // Signal interpreter the lifting failed. rz_atomic_bool_set(is_running, false); rz_th_queue_close(io_request_q); rz_th_queue_close(io_result_q); - rz_th_queue_close(iset->addr_queue); + rz_th_queue_close(iset->branch_queue); rz_th_queue_close(iset->il_queue); bb_decode_failed = true; break; } - rz_analysis_add_bb(core->analysis, *addr, bb_size); + rz_analysis_add_bb(core->analysis, branch->target_addr, bb_size); + rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); RZ_LOG_DEBUG("INQUIRY: Send IL result: %p.\n", bb); ht_up_insert(il_cache, bb->bb_addr, bb); rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, bb->bb_addr, bb->size); @@ -582,7 +583,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent rz_th_queue_close(io_request_q); rz_th_queue_close(io_result_q); - rz_th_queue_close(iset->addr_queue); + rz_th_queue_close(iset->branch_queue); rz_th_queue_close(iset->il_queue); RZ_LOG_DEBUG("INQUIRY: Wait for join\n"); @@ -599,7 +600,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent // jump target again. rz_th_queue_open(io_request_q); rz_th_queue_open(io_result_q); - rz_th_queue_open(iset->addr_queue); + rz_th_queue_open(iset->branch_queue); rz_th_queue_open(iset->il_queue); // At this point the interpreter is finished and returned. @@ -663,7 +664,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent rz_analysis_il_vm_free(analysis_vm); rz_th_queue_close(io_request_q); rz_th_queue_close(io_result_q); - rz_th_queue_close(iset->addr_queue); + rz_th_queue_close(iset->branch_queue); rz_th_queue_close(iset->il_queue); if (!iset) { diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index f0af7a43f61..684727d14e8 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -158,8 +158,8 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { if (!iset) { return; } - if (iset->addr_queue) { - rz_th_queue_free(iset->addr_queue); + if (iset->branch_queue) { + rz_th_queue_free(iset->branch_queue); } if (iset->il_queue) { rz_th_queue_free(iset->il_queue); @@ -200,7 +200,7 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, RZ_NONNULL RZ_OWN RzInterpreterAbstrState *state, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *addr_queue, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *branch_queue, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_request, @@ -208,7 +208,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag, RZ_NONNULL RZ_OWN RzVector /**/ *entry_points, RZ_NONNULL RZ_OWN RzVector /**/ *ignored_code) { - rz_return_val_if_fail(plugin && state && addr_queue && il_queue && yield_queues && io_request && io_result && is_running_flag && entry_points && ignored_code, NULL); + rz_return_val_if_fail(plugin && state && branch_queue && il_queue && yield_queues && io_request && io_result && is_running_flag && entry_points && ignored_code, NULL); RzInterpreterSet *set = RZ_NEW0(RzInterpreterSet); if (!set) { @@ -217,7 +217,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( set->plugin = plugin; set->state = state; set->il_queue = il_queue; - set->addr_queue = addr_queue; + set->branch_queue = branch_queue; set->yield_queues = yield_queues; set->io_request = io_request; set->io_result = io_result; @@ -268,7 +268,7 @@ static bool choose_next_pc(RzInterpreterSet *iset, // RZ_LOG_DEBUG("%s", s); // free(s); - ut64 *shared_addr = &iset->state->shared_obj->shared_addr; + RzInterpreterBranch *shared_branch = &iset->state->shared_obj->branch; bool has_succsessor = true; // Determine successors and increase the reference counts for the current out state. @@ -283,25 +283,26 @@ static bool choose_next_pc(RzInterpreterSet *iset, has_succsessor = !rz_vector_empty(tmp_succ_addr); // Request the successor effects over the queue. while (!rz_vector_empty(tmp_succ_addr)) { - rz_vector_pop_front(tmp_succ_addr, shared_addr); - if (*shared_addr == UT64_MAX || *shared_addr == 0) { + rz_vector_pop_front(tmp_succ_addr, &shared_branch->target_addr); + if (shared_branch->target_addr == UT64_MAX || shared_branch->target_addr == 0) { RZ_LOG_DEBUG("interpreter: Quit due to invalid PC.\n"); // Obviously wrong address. return false; } - if (jumps_ignored_code(iset->ignored_code, *shared_addr)) { - RZ_LOG_DEBUG("interpreter: tried to jump to ignored code region at 0x%" PFMT64x "\n", *shared_addr); + shared_branch->branching_bb_addr = il_bb->bb_addr; + if (jumps_ignored_code(iset->ignored_code, shared_branch->target_addr)) { + RZ_LOG_DEBUG("interpreter: tried to jump to ignored code region at 0x%" PFMT64x "\n", shared_branch->target_addr); // Ignored code is mostly dynamically linked functions. // Skip to the next following address after the jump. - *shared_addr = il_bb->bb_addr + il_bb->size; - RZ_LOG_DEBUG("interpreter: Request instead 0x%" PFMT64x "\n", *shared_addr); + shared_branch->target_addr = il_bb->bb_addr + il_bb->size; + RZ_LOG_DEBUG("interpreter: Request instead 0x%" PFMT64x "\n", shared_branch->target_addr); } - SuccessorState ss = { .addr = *shared_addr, .in_state_hash = out_hash }; + SuccessorState ss = { .addr = shared_branch->target_addr, .in_state_hash = out_hash }; // The successors are pushed in the same order into the succ_states // vector, as they are requested over the addr_queue. rz_vector_push(succ_states, &ss); - rz_th_queue_push(iset->addr_queue, shared_addr, true); + rz_th_queue_push(iset->branch_queue, shared_branch, true); // TODO: Race condition: // Multiple jump targets could overwrite the value in shared_addr before it is read. } @@ -314,7 +315,7 @@ static bool choose_next_pc(RzInterpreterSet *iset, RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { rz_goto_if_fail(iset && iset->state && - iset->addr_queue && + iset->branch_queue && iset->il_queue && iset->yield_queues && iset->is_running_flag && @@ -357,9 +358,9 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { goto pre_loop_error; } - ut64 entry_point; - rz_vector_pop_front(iset->entry_points, &entry_point); - if (!plugin->init_state(iset->state, entry_point, plugin_data)) { + RzInterpreterBranch *shared_branch = &iset->state->shared_obj->branch; + rz_vector_pop_front(iset->entry_points, &shared_branch->target_addr); + if (!plugin->init_state(iset->state, shared_branch->target_addr, plugin_data)) { rz_warn_if_reached(); goto pre_loop_error; } @@ -370,7 +371,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { RzInterpreterAbstrState *out_state = NULL; ut64 out_hash = 0; - rz_th_queue_push(iset->addr_queue, &entry_point, true); + rz_th_queue_push(iset->branch_queue, shared_branch, true); const RzInterpreterILBB *il_bb = NULL; if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { rz_warn_if_reached(); diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 9c989127097..2669d643bcb 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -98,7 +98,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, if (plugin_data->call_cand.store_addr) { // An instruction in this basic block stored the next PC. // Report a call candidate. - plugin_data->call_cand.jmp_addr = rz_bv_to_ut64(pc->bv); + plugin_data->call_cand.candidate_addr = rz_bv_to_ut64(pc->bv); report_yield_call_candiate(state, yield_queues, plugin_data); memset(&plugin_data->call_cand, 0, sizeof(plugin_data->call_cand)); } From f13fbb3270e2c5240b11266dcfc3a66d9f7e4166 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 9 Feb 2026 18:18:07 +0100 Subject: [PATCH 180/334] Fix incorrect bb edges --- librz/core/cmd/cmd_inquiry.c | 7 +++++++ librz/include/rz_inquiry/rz_interpreter.h | 2 +- librz/inquiry/algorithms/revng_fcn_detection.c | 7 ++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index ceb6cd1bb68..89def15fd94 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -55,7 +55,14 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar RzIterator *iter = ht_up_as_iter(fcn->bb_cfg->basic_blocks); rz_iterator_foreach(iter, it2) { RzInterval *bb = *it2; + if (rz_analysis_get_block_at(core->analysis, bb->addr)) { + continue; + } RzAnalysisBlock *abb = rz_analysis_create_block(core->analysis, bb->addr, bb->size); + if (!abb) { + rz_warn_if_reached(); + break; + } rz_analysis_function_add_block(afcn, abb); } rz_iterator_free(iter); diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index a3683eff379..86726d03b49 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -69,7 +69,7 @@ typedef struct { */ typedef struct { RZ_LIFETIME(RzInquiry) - RzInterpreterBranch branch; ///< The address object passed to an IL cache for BB requests. + RzInterpreterBranch branch; ///< The branch object passed to an IL cache for BB requests. RZ_LIFETIME(RzInquiry) RzAnalysisXRef xref; ///< The xref object passed over the queue. diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index 2e7108b7279..6117f62c935 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -143,7 +143,12 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( rz_iterator_foreach(iter, it) { RzAnalysisCallCandidate *cc = *it; rz_set_u_add(return_addresses, cc->npc); - rz_vector_push(cfep_addresses, &cc->candidate_addr); + const RzList *successors = rz_inquiry_bb_cfg_get_neighbours(binary_bb_cfg, cc->bb_addr); + RzGraphNode *gnode; + RzListIter *lit; + rz_list_foreach(successors, lit, gnode) { + rz_vector_push(cfep_addresses, &gnode->data); + } } rz_iterator_free(iter); rz_vector_sort(cfep_addresses, cmp, false, NULL); From da70cf56a94dea8ada3f53e7fb9e35cbb57a4380 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 9 Feb 2026 18:53:17 +0100 Subject: [PATCH 181/334] Enable formatted inquiry function printing. --- librz/include/rz_inquiry.h | 1 + librz/inquiry/inquiry.c | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index e069adbd229..7955ef1a1b0 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -93,6 +93,7 @@ typedef struct { } RzInquiryFunction; RZ_IPI RZ_OWN RzInquiryFunction *rz_inquiry_function_new(); +RZ_API RZ_OWN char *rz_inquiry_function_str(const RzInquiryFunction *fcn); RZ_API void rz_inquiry_function_free(RZ_NULLABLE RZ_OWN RzInquiryFunction *fcn); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 1af5509225c..79eb9851d53 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -106,6 +106,25 @@ RZ_IPI RZ_OWN RzInquiryFunction *rz_inquiry_function_new() { return fcn; } +RZ_API RZ_OWN char *rz_inquiry_function_str(const RzInquiryFunction *fcn) { + RzStrBuf *buf = rz_strbuf_new(""); + rz_strbuf_append(buf, "ifcn: ["); + ut64 *it; + size_t i = 0; + rz_vector_foreach(fcn->entry_points, it) { + rz_strbuf_appendf(buf, "%s0x%" PFMT64x, (i++ > 0 ? ", " : " "), *it); + } + rz_strbuf_append(buf, " ]\n"); + RzIterator *iter = ht_up_as_iter(fcn->bb_cfg->basic_blocks); + RzInterval **itv; + rz_iterator_foreach(iter, itv) { + RzInterval *bb = *itv; + rz_strbuf_appendf(buf, "\t0x%" PFMT64x ":0x%" PFMT64x "\n", bb->addr, bb->size); + } + rz_iterator_free(iter); + return rz_strbuf_drain(buf); +} + RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void) { RzInquiry *iq = RZ_NEW0(RzInquiry); if (!iq) { From 8da3acf5afb45f3e2462d20f982523e138de322c Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 9 Feb 2026 18:59:10 +0100 Subject: [PATCH 182/334] Fix bb assignment to functions --- librz/core/cmd/cmd_inquiry.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 89def15fd94..a5f5250d1d4 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -31,9 +31,8 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar } bool success = rz_inquiry_interpreter(core, entry_points); - RZ_LOG_INFO("Finished reference recovery."); - - RZ_LOG_INFO("Perform function deduction."); + RZ_LOG_INFO("Finished reference recovery.\n"); + RZ_LOG_INFO("Perform function deduction.\n"); RzPVector *fcns = rz_pvector_new((RzPVectorFree)rz_inquiry_function_free); rz_inquiry_algo_revng_fcn_detection( @@ -41,33 +40,39 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar core->inquiry->bb_cfg, fcns); + // Create analysis function and add their blocks to them. void **it; rz_pvector_foreach (fcns, it) { RzInquiryFunction *fcn = *it; + char *fcn_desc = rz_inquiry_function_str(fcn); + printf("%s", fcn_desc); + free(fcn_desc); - char fcn_name[512] = { 0 }; ut64 fcn_addr = *(ut64 *)rz_vector_head(fcn->entry_points); + char fcn_name[512] = { 0 }; rz_strf(fcn_name, "iq_fcn_0x%" PFMT64x, fcn_addr); RzAnalysisFunction *afcn = rz_analysis_create_function(core->analysis, fcn_name, fcn_addr, RZ_ANALYSIS_FCN_TYPE_FCN); - rz_analysis_add_function(core->analysis, afcn); void **it2; RzIterator *iter = ht_up_as_iter(fcn->bb_cfg->basic_blocks); rz_iterator_foreach(iter, it2) { RzInterval *bb = *it2; - if (rz_analysis_get_block_at(core->analysis, bb->addr)) { - continue; - } - RzAnalysisBlock *abb = rz_analysis_create_block(core->analysis, bb->addr, bb->size); + RzAnalysisBlock *abb = rz_analysis_get_block_at(core->analysis, bb->addr); if (!abb) { - rz_warn_if_reached(); - break; + rz_analysis_create_block(core->analysis, bb->addr, bb->size); + if (!abb) { + rz_warn_if_reached(); + break; + } } rz_analysis_function_add_block(afcn, abb); + rz_analysis_block_update_hash(abb); } rz_iterator_free(iter); } rz_pvector_free(fcns); + // Add edges between blocks + return success ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; } From 392637088c2f0c9b86730f09d85823e7f519e45e Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 9 Feb 2026 20:38:35 +0100 Subject: [PATCH 183/334] Fix adding of basic blocks following calls --- librz/include/rz_inquiry.h | 5 +- .../inquiry/algorithms/revng_fcn_detection.c | 73 ++++++++++++------- librz/inquiry/bb_cfg.c | 13 +++- librz/inquiry/inquiry.c | 2 +- 4 files changed, 61 insertions(+), 32 deletions(-) diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 7955ef1a1b0..8114bdde9b7 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -52,7 +52,7 @@ typedef struct { HtSP /**/ *plugins; HtSP /**/ *plugins_data; - HtUP /**/ *call_candidates; ///< Indexed by address of call candidate instruction. + HtUP /**/ *call_candidates; ///< Indexed by address of basic block with the call candidate. RzVector /**/ *xrefs; ///< All xrefs it detected. RzInquiryBBCFG *bb_cfg; ///< The control flow graph all the basic blocks build. } RzInquiry; @@ -62,7 +62,8 @@ RZ_IPI void rz_inquiry_bb_cfg_free(RZ_NULLABLE RZ_OWN RzInquiryBBCFG *bb_cfg); RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size); RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RzInterval *bb); RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb); -RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours(const RzInquiryBBCFG *cfg, ut64 bb_addr); +RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_from(const RzInquiryBBCFG *cfg, ut64 bb_addr); +RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_to(const RzInquiryBBCFG *cfg, ut64 bb_addr); RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref); diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index 6117f62c935..918e953f0ec 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -2,22 +2,10 @@ // SPDX-License-Identifier: LGPL-3.0-only /** - * \file This is the function detection algorithm from the + * \file This is the function detection algorithm inspired by * paper: "REV.NG: A Unified Binary Analysis Framework to Recover CFGs and Function Boundaries" * chapter: "4.2 Function boundaries recovery" * doi: 10.1145/3033019.3033028 - * - * The algorithm here differs in a few ways to fit the assumptions Rizin makes - * and simplify implementation. - * - * 1. Function Call: A branch instruction with any store of the next PC **in its basic block**. - * - That introduces somewhat more inaccuracy. - * But is easier to implement for a prototype. - * 2. Return: Any branch instruction whose destination is a return address. - * - Remove requirement for indirect branch and don't treat unknown targets as return. - * Easier to implement. - * 3. Syscalls are ignored. They are not supported by RzIL, yet. - * 4. longjmps/killer basic blocks are ignored. - Easier to implement. */ #include "rz_analysis.h" @@ -48,6 +36,7 @@ static void recurse_into_fcn_bbs( RZ_OUT RzSetU *tail_called_addr, const RzSetU *return_addresses, const RzVector /**/ *cfep_addresses, + const HtUP /**/ *call_candidates, const RzInquiryBBCFG *binary_bb_cfg) { if (rz_set_u_contains(visited_fcn_bbs, this_bb_addr)) { return; @@ -81,7 +70,7 @@ static void recurse_into_fcn_bbs( // // Visit neighbors // - const RzList *successors = rz_inquiry_bb_cfg_get_neighbours(binary_bb_cfg, this_bb_addr); + const RzList *successors = rz_inquiry_bb_cfg_get_neighbours_from(binary_bb_cfg, this_bb_addr); if (!successors) { goto warn_return; } @@ -91,10 +80,23 @@ static void recurse_into_fcn_bbs( rz_list_foreach (successors, lit, s) { ut64 succ_addr = (ut64)s->data; if (rz_set_u_contains((RzSetU *)return_addresses, succ_addr) || - rz_vector_contains(cfep_addresses, &succ_addr) || rz_set_u_contains(tail_called_addr, succ_addr)) { + // Ignore tail called functions and return points + // because they belong to a different function. continue; } + if (rz_vector_contains(cfep_addresses, &succ_addr)) { + // The successor is another function. + // If address after the branch is a return point we choose it as + // successor. + // If it isn't, then the call at this basic block is likely a tail call. + const RzAnalysisCallCandidate *cc = ht_up_find((HtUP *)call_candidates, this_bb_addr, NULL); + if (cc && rz_set_u_contains((RzSetU *)return_addresses, cc->npc)) { + succ_addr = cc->npc; + } else { + continue; + } + } ut64 *val; rz_vector_foreach (cfep_addresses, val) { @@ -119,6 +121,7 @@ static void recurse_into_fcn_bbs( tail_called_addr, return_addresses, cfep_addresses, + call_candidates, binary_bb_cfg); } } @@ -129,29 +132,42 @@ static void recurse_into_fcn_bbs( return; } -RZ_API bool rz_inquiry_algo_revng_fcn_detection( - RZ_NONNULL const HtUP /**/ *call_candidates, - RZ_NONNULL const RzInquiryBBCFG *binary_bb_cfg, - RZ_NONNULL RZ_OUT RzPVector /**/ *fcns) { - rz_return_val_if_fail(call_candidates && binary_bb_cfg && fcns, false); - - // Candidate function entry points - RzSetU *return_addresses = rz_set_u_new(); - RzVector *cfep_addresses = rz_vector_new(sizeof(ut64), NULL, NULL); +static void fill_cfep_and_ret_addresses( + const RzInquiryBBCFG *binary_bb_cfg, + const HtUP /**/ *call_candidates, + RzSetU *return_addresses, + RzVector *cfep_addresses) { void **it; RzIterator *iter = ht_up_as_iter(call_candidates); rz_iterator_foreach(iter, it) { RzAnalysisCallCandidate *cc = *it; - rz_set_u_add(return_addresses, cc->npc); - const RzList *successors = rz_inquiry_bb_cfg_get_neighbours(binary_bb_cfg, cc->bb_addr); + ut64 ret_addr = cc->npc; + const RzList *predecessor = rz_inquiry_bb_cfg_get_neighbours_to(binary_bb_cfg, ret_addr); + if (rz_list_length(predecessor) > 0) { + rz_set_u_add(return_addresses, ret_addr); + } + + const RzList *successors = rz_inquiry_bb_cfg_get_neighbours_from(binary_bb_cfg, cc->bb_addr); RzGraphNode *gnode; RzListIter *lit; - rz_list_foreach(successors, lit, gnode) { + rz_list_foreach (successors, lit, gnode) { rz_vector_push(cfep_addresses, &gnode->data); } } rz_iterator_free(iter); rz_vector_sort(cfep_addresses, cmp, false, NULL); +} + +RZ_API bool rz_inquiry_algo_revng_fcn_detection( + RZ_NONNULL const HtUP /**/ *call_candidates, + RZ_NONNULL const RzInquiryBBCFG *binary_bb_cfg, + RZ_NONNULL RZ_OUT RzPVector /**/ *fcns) { + rz_return_val_if_fail(call_candidates && binary_bb_cfg && fcns, false); + + // Candidate function entry points + RzSetU *return_addresses = rz_set_u_new(); + RzVector *cfep_addresses = rz_vector_new(sizeof(ut64), NULL, NULL); + fill_cfep_and_ret_addresses(binary_bb_cfg, call_candidates, return_addresses, cfep_addresses); // Tail calls discovered by this algorithm. RzSetU *tail_called_addr = rz_set_u_new(); @@ -176,13 +192,14 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( tail_called_addr, return_addresses, cfep_addresses, + call_candidates, binary_bb_cfg); rz_set_u_free(visited_bbs); rz_set_u_add(cfep_handled, cfep_addr); rz_pvector_push(fcns, fcn); } - iter = rz_set_u_as_iter(tail_called_addr); + RzIterator *iter = rz_set_u_as_iter(tail_called_addr); ut64 *val; rz_iterator_foreach(iter, val) { ut64 tail_cfep = *val; diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index 12612df76c6..d35eaa89217 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -106,7 +106,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb return true; } -RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours(const RzInquiryBBCFG *cfg, ut64 bb_addr) { +RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_from(const RzInquiryBBCFG *cfg, ut64 bb_addr) { rz_return_val_if_fail(cfg, NULL); const RzGraphNode *n = ht_up_find(cfg->bb_gnode_map, bb_addr, NULL); @@ -117,6 +117,17 @@ RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours(const return rz_graph_get_neighbours(cfg->graph, n); } +RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_to(const RzInquiryBBCFG *cfg, ut64 bb_addr) { + rz_return_val_if_fail(cfg, NULL); + + const RzGraphNode *n = ht_up_find(cfg->bb_gnode_map, bb_addr, NULL); + if (!n) { + rz_warn_if_reached(); + return NULL; + } + return rz_graph_innodes(cfg->graph, n); +} + RZ_IPI bool rz_inquiry_fill_bb_cfg(RzInquiry *iq) { if (ht_up_size(iq->bb_cfg->basic_blocks) == 0) { RZ_LOG_WARN("No basic blocks present to fill CFG.\n"); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 79eb9851d53..8fbc6da9898 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -370,7 +370,7 @@ static bool handle_yields(RzCore *core, HtUP *yield_queues) { } RzAnalysisCallCandidate *cc_clone = RZ_NEW0(RzAnalysisCallCandidate); memcpy(cc_clone, cc, sizeof(RzAnalysisCallCandidate)); - if (!ht_up_update(core->inquiry->call_candidates, cc_clone->candidate_addr, cc_clone)) { + if (!ht_up_update(core->inquiry->call_candidates, cc_clone->bb_addr, cc_clone)) { RZ_LOG_DEBUG("Overwrote a call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); } else { RZ_LOG_DEBUG("Added call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); From 6e2b7cd598a9fa18aed9a1ef0d7a4eb9d9bf36aa Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 9 Feb 2026 21:35:35 +0100 Subject: [PATCH 184/334] Move function detection into own function --- librz/core/cmd/cmd_inquiry.c | 46 ++++-------------------------------- librz/include/rz_inquiry.h | 2 ++ librz/inquiry/inquiry.c | 40 +++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 41 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index a5f5250d1d4..6c7f02d68f7 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -30,49 +30,13 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar } } bool success = rz_inquiry_interpreter(core, entry_points); - - RZ_LOG_INFO("Finished reference recovery.\n"); - RZ_LOG_INFO("Perform function deduction.\n"); - - RzPVector *fcns = rz_pvector_new((RzPVectorFree)rz_inquiry_function_free); - rz_inquiry_algo_revng_fcn_detection( - core->inquiry->call_candidates, - core->inquiry->bb_cfg, - fcns); - - // Create analysis function and add their blocks to them. - void **it; - rz_pvector_foreach (fcns, it) { - RzInquiryFunction *fcn = *it; - char *fcn_desc = rz_inquiry_function_str(fcn); - printf("%s", fcn_desc); - free(fcn_desc); - - ut64 fcn_addr = *(ut64 *)rz_vector_head(fcn->entry_points); - char fcn_name[512] = { 0 }; - rz_strf(fcn_name, "iq_fcn_0x%" PFMT64x, fcn_addr); - RzAnalysisFunction *afcn = rz_analysis_create_function(core->analysis, fcn_name, fcn_addr, RZ_ANALYSIS_FCN_TYPE_FCN); - - void **it2; - RzIterator *iter = ht_up_as_iter(fcn->bb_cfg->basic_blocks); - rz_iterator_foreach(iter, it2) { - RzInterval *bb = *it2; - RzAnalysisBlock *abb = rz_analysis_get_block_at(core->analysis, bb->addr); - if (!abb) { - rz_analysis_create_block(core->analysis, bb->addr, bb->size); - if (!abb) { - rz_warn_if_reached(); - break; - } - } - rz_analysis_function_add_block(afcn, abb); - rz_analysis_block_update_hash(abb); - } - rz_iterator_free(iter); + RZ_LOG_INFO("Finished reference recovery: %s\n", success ? "OK" : "FAIL"); + if (!success) { + return RZ_CMD_STATUS_ERROR; } - rz_pvector_free(fcns); - // Add edges between blocks + success = rz_inquiry_function_deduction(core->analysis, core->inquiry); + RZ_LOG_INFO("Perform function deduction: %s\n", success ? "OK" : "FAIL"); return success ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; } diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 8114bdde9b7..71db854626f 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -81,6 +81,8 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL co RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *entry_points); +RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry); + //============ // Algorithms //============ diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 8fbc6da9898..7003459c424 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -705,3 +705,43 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent rz_cons_pop(); return return_code; } + +RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry) { + RzPVector *fcns = rz_pvector_new((RzPVectorFree)rz_inquiry_function_free); + rz_inquiry_algo_revng_fcn_detection( + inquiry->call_candidates, + inquiry->bb_cfg, + fcns); + + // Create analysis function and add their blocks to them. + void **it; + rz_pvector_foreach (fcns, it) { + RzInquiryFunction *fcn = *it; + + ut64 fcn_addr = *(ut64 *)rz_vector_head(fcn->entry_points); + char fcn_name[64] = { 0 }; + rz_strf(fcn_name, "iq_fcn_0x%" PFMT64x, fcn_addr); + RzAnalysisFunction *afcn = rz_analysis_create_function(analysis, fcn_name, fcn_addr, RZ_ANALYSIS_FCN_TYPE_FCN); + + void **it2; + RzIterator *iter = ht_up_as_iter(fcn->bb_cfg->basic_blocks); + rz_iterator_foreach(iter, it2) { + RzInterval *bb = *it2; + RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, bb->addr); + if (!abb) { + rz_analysis_create_block(analysis, bb->addr, bb->size); + if (!abb) { + rz_warn_if_reached(); + break; + } + } + rz_analysis_function_add_block(afcn, abb); + } + rz_iterator_free(iter); + } + rz_pvector_free(fcns); + + // Add edges between blocks + + return true; +} From 29290a4ceb5ddf07f63acc16439a6fca06be8373 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 10 Feb 2026 16:14:55 +0100 Subject: [PATCH 185/334] Remove the whole tail call logic for simplicity. --- .../inquiry/algorithms/revng_fcn_detection.c | 97 ++++++------------- 1 file changed, 30 insertions(+), 67 deletions(-) diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index 918e953f0ec..1fa12c9594c 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -33,7 +33,6 @@ static void recurse_into_fcn_bbs( ut64 predecessor_bb_addr, ut64 this_bb_addr, RzSetU *visited_fcn_bbs, - RZ_OUT RzSetU *tail_called_addr, const RzSetU *return_addresses, const RzVector /**/ *cfep_addresses, const HtUP /**/ *call_candidates, @@ -79,8 +78,7 @@ static void recurse_into_fcn_bbs( const RzGraphNode *s; rz_list_foreach (successors, lit, s) { ut64 succ_addr = (ut64)s->data; - if (rz_set_u_contains((RzSetU *)return_addresses, succ_addr) || - rz_set_u_contains(tail_called_addr, succ_addr)) { + if (rz_set_u_contains((RzSetU *)return_addresses, succ_addr)) { // Ignore tail called functions and return points // because they belong to a different function. continue; @@ -98,32 +96,15 @@ static void recurse_into_fcn_bbs( } } - ut64 *val; - rz_vector_foreach (cfep_addresses, val) { - ut64 cfep_addr = *val; - if (cfep_addr != this_bb_addr && - (RZ_BETWEEN_EXCL(this_bb_addr, cfep_addr, succ_addr) || - RZ_BETWEEN_EXCL(succ_addr, cfep_addr, this_bb_addr))) { - // The jump goes over a cfep candidate. - // Assume the succ_addr is a tail call target. - // Log it and don't recurse. - rz_set_u_add(tail_called_addr, succ_addr); - continue; - } - - // The jump goes not over cfep so it should be a - // valid basic block of this function. - recurse_into_fcn_bbs( - fcn, - this_bb_addr, - succ_addr, - visited_fcn_bbs, - tail_called_addr, - return_addresses, - cfep_addresses, - call_candidates, - binary_bb_cfg); - } + recurse_into_fcn_bbs( + fcn, + this_bb_addr, + succ_addr, + visited_fcn_bbs, + return_addresses, + cfep_addresses, + call_candidates, + binary_bb_cfg); } return; @@ -169,51 +150,33 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( RzVector *cfep_addresses = rz_vector_new(sizeof(ut64), NULL, NULL); fill_cfep_and_ret_addresses(binary_bb_cfg, call_candidates, return_addresses, cfep_addresses); - // Tail calls discovered by this algorithm. - RzSetU *tail_called_addr = rz_set_u_new(); // Set of handled cfep RzSetU *cfep_handled = rz_set_u_new(); - do { - ut64 *elem; - rz_vector_foreach (cfep_addresses, elem) { - ut64 cfep_addr = *elem; - if (rz_set_u_contains(cfep_handled, cfep_addr)) { - continue; - } - - RzSetU *visited_bbs = rz_set_u_new(); - RzInquiryFunction *fcn = rz_inquiry_function_new(); - rz_vector_push(fcn->entry_points, &cfep_addr); - recurse_into_fcn_bbs(fcn, - UT64_MAX, - cfep_addr, - visited_bbs, - tail_called_addr, - return_addresses, - cfep_addresses, - call_candidates, - binary_bb_cfg); - rz_set_u_free(visited_bbs); - rz_set_u_add(cfep_handled, cfep_addr); - rz_pvector_push(fcns, fcn); + ut64 *elem; + rz_vector_foreach (cfep_addresses, elem) { + ut64 cfep_addr = *elem; + if (rz_set_u_contains(cfep_handled, cfep_addr)) { + continue; } - RzIterator *iter = rz_set_u_as_iter(tail_called_addr); - ut64 *val; - rz_iterator_foreach(iter, val) { - ut64 tail_cfep = *val; - if (rz_vector_contains(cfep_addresses, &tail_cfep)) { - continue; - } - rz_vector_push_front(cfep_addresses, &tail_cfep); - } - rz_iterator_free(iter); - rz_set_u_clean(tail_called_addr); - } while (rz_set_u_size(tail_called_addr) != 0); + RzSetU *visited_bbs = rz_set_u_new(); + RzInquiryFunction *fcn = rz_inquiry_function_new(); + rz_vector_push(fcn->entry_points, &cfep_addr); + recurse_into_fcn_bbs(fcn, + UT64_MAX, + cfep_addr, + visited_bbs, + return_addresses, + cfep_addresses, + call_candidates, + binary_bb_cfg); + rz_set_u_free(visited_bbs); + rz_set_u_add(cfep_handled, cfep_addr); + rz_pvector_push(fcns, fcn); + } rz_set_u_free(cfep_handled); - rz_set_u_free(tail_called_addr); rz_set_u_free(return_addresses); rz_vector_free(cfep_addresses); return true; From d9e4eaf71c62e8313629cebf7f19bb4b6163a6c5 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 10 Feb 2026 19:00:49 +0100 Subject: [PATCH 186/334] Move il_bb lifting into own function. --- librz/include/rz_inquiry.h | 2 +- librz/inquiry/inquiry.c | 53 +++++++++++++++++++-------------- librz/inquiry/inquiry_helpers.c | 5 +--- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 71db854626f..bb0a06b5ac1 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -75,7 +75,7 @@ RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NO RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void); RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *q); -RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr, RZ_NULLABLE RZ_OUT size_t *bb_size); +RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr); RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzPVector /**/ *allowed_segments); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 7003459c424..ed3251169b1 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -379,6 +379,25 @@ static bool handle_yields(RzCore *core, HtUP *yield_queues) { return true; } +static const RzInterpreterILBB *get_il_bb(RzCore *core, HtUP *il_cache, ut64 addr) { + RzInterpreterILBB *bb = ht_up_find(il_cache, addr, NULL); + if (!bb) { + RZ_LOG_DEBUG("INQUIRY: Lift new BB\n"); + bb = rz_inquiry_gen_il_bb(core->analysis, core->io, addr); + if (!bb) { + RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", addr); + return NULL; + } + rz_analysis_add_bb(core->analysis, bb->bb_addr, bb->size); + RZ_LOG_DEBUG("INQUIRY: Send IL result: %p.\n", bb); + ht_up_insert(il_cache, bb->bb_addr, bb); + rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, bb->bb_addr, bb->size); + } else { + RZ_LOG_DEBUG("INQUIRY: Serve BB from cache\n"); + } + return bb; +} + /** * A function to call the prototype interpreter. * Usually these tasks will be split between different caches and yield consumers. @@ -536,31 +555,19 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent break; } RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x "\n", branch->target_addr); - RzInterpreterILBB *bb = ht_up_find(il_cache, branch->target_addr, NULL); + const RzInterpreterILBB *bb = get_il_bb(core, il_cache, branch->target_addr); if (!bb) { - RZ_LOG_DEBUG("INQUIRY: Lift new BB\n"); - size_t bb_size = 0; - bb = rz_inquiry_gen_il_bb(core->analysis, core->io, branch->target_addr, &bb_size); - if (!bb) { - RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", branch->target_addr); - // Signal interpreter the lifting failed. - rz_atomic_bool_set(is_running, false); - rz_th_queue_close(io_request_q); - rz_th_queue_close(io_result_q); - rz_th_queue_close(iset->branch_queue); - rz_th_queue_close(iset->il_queue); - bb_decode_failed = true; - break; - } - rz_analysis_add_bb(core->analysis, branch->target_addr, bb_size); - rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); - RZ_LOG_DEBUG("INQUIRY: Send IL result: %p.\n", bb); - ht_up_insert(il_cache, bb->bb_addr, bb); - rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, bb->bb_addr, bb->size); - } else { - RZ_LOG_DEBUG("INQUIRY: Serve BB from cache\n"); + // Signal interpreter the lifting failed. + rz_atomic_bool_set(is_running, false); + rz_th_queue_close(io_request_q); + rz_th_queue_close(io_result_q); + rz_th_queue_close(iset->branch_queue); + rz_th_queue_close(iset->il_queue); + bb_decode_failed = true; + break; } - rz_th_queue_push(iset->il_queue, bb, true); + rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); + rz_th_queue_push(iset->il_queue, (void *)bb, true); } } diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c index 49a184fed1f..83d7678a2fc 100644 --- a/librz/inquiry/inquiry_helpers.c +++ b/librz/inquiry/inquiry_helpers.c @@ -12,7 +12,7 @@ #include #include -RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr, RZ_NULLABLE RZ_OUT size_t *bb_size) { +RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr) { rz_return_val_if_fail(analysis && analysis->cur && io, NULL); RzInterpreterILBB *il_bb = NULL; RzAnalysisOp op = { 0 }; @@ -68,9 +68,6 @@ RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana RZ_LOG_DEBUG("\t0x%" PFMT64x "\n", addr); rz_analysis_op_fini(&op); addr += op.size; - if (bb_size) { - *bb_size += op.size; - } rz_mem_memzero(buf, max_read_size); if (sparc_add_delayed_insn) { // Instruction was added, now the BB is complete. From 9e03f30a96de523a1aef59e56209d1d005060124 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 10 Feb 2026 20:06:44 +0100 Subject: [PATCH 187/334] Add entry point always as cfep --- librz/core/cmd/cmd_inquiry.c | 4 ++-- librz/include/rz_analysis.h | 4 ++-- librz/include/rz_inquiry.h | 3 ++- librz/inquiry/algorithms/revng_fcn_detection.c | 2 ++ librz/inquiry/inquiry.c | 3 ++- librz/inquiry/interpreter/interpreter.c | 4 ++-- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 6c7f02d68f7..3d205493baf 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -29,13 +29,13 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar rz_vector_push(entry_points, &entry_point); } } + entry_point = *(ut64 *)rz_vector_head(entry_points); bool success = rz_inquiry_interpreter(core, entry_points); RZ_LOG_INFO("Finished reference recovery: %s\n", success ? "OK" : "FAIL"); if (!success) { return RZ_CMD_STATUS_ERROR; } - - success = rz_inquiry_function_deduction(core->analysis, core->inquiry); + success = rz_inquiry_function_deduction(core->analysis, core->inquiry, entry_point); RZ_LOG_INFO("Perform function deduction: %s\n", success ? "OK" : "FAIL"); return success ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 955f2ce9925..0c69308d7b8 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -984,8 +984,8 @@ typedef struct rz_analysis_ref_t { */ typedef struct { ut64 bb_addr; ///< The address of the basic block which stores the NPC. - ut64 store_addr; ///< The address of the instruction packet storing the NPC. - ut64 candidate_addr; ///< Address of the call candidate instruction packet. + ut64 store_addr; ///< The address of the instruction packet storing the NPC. Might be 0, if it was not added by the interpreter. + ut64 candidate_addr; ///< Address of the call candidate instruction packet. Might be 0, if it was not added by the interpreter. ut64 npc; ///< The NPC stored. Should point after a basic block. bool in_mem; ///< True if the NPC is written to memory. False if it is written to a register. } RzAnalysisCallCandidate; diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index bb0a06b5ac1..2de2b355db3 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -81,7 +81,7 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL co RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *entry_points); -RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry); +RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry, ut64 entry_point); //============ // Algorithms @@ -101,6 +101,7 @@ RZ_API RZ_OWN char *rz_inquiry_function_str(const RzInquiryFunction *fcn); RZ_API void rz_inquiry_function_free(RZ_NULLABLE RZ_OWN RzInquiryFunction *fcn); RZ_API bool rz_inquiry_algo_revng_fcn_detection( + ut64 entry_point, const HtUP /**/ *call_candidates, const RzInquiryBBCFG *bb_cfg, RZ_OUT RzPVector /**/ *fcns); diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index 1fa12c9594c..5df45c6265d 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -140,6 +140,7 @@ static void fill_cfep_and_ret_addresses( } RZ_API bool rz_inquiry_algo_revng_fcn_detection( + ut64 entry_point, RZ_NONNULL const HtUP /**/ *call_candidates, RZ_NONNULL const RzInquiryBBCFG *binary_bb_cfg, RZ_NONNULL RZ_OUT RzPVector /**/ *fcns) { @@ -148,6 +149,7 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( // Candidate function entry points RzSetU *return_addresses = rz_set_u_new(); RzVector *cfep_addresses = rz_vector_new(sizeof(ut64), NULL, NULL); + rz_vector_push(cfep_addresses, &entry_point); fill_cfep_and_ret_addresses(binary_bb_cfg, call_candidates, return_addresses, cfep_addresses); // Set of handled cfep diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index ed3251169b1..ec5bd2fcdf3 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -713,9 +713,10 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent return return_code; } -RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry) { +RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry, ut64 entry_point) { RzPVector *fcns = rz_pvector_new((RzPVectorFree)rz_inquiry_function_free); rz_inquiry_algo_revng_fcn_detection( + entry_point, inquiry->call_candidates, inquiry->bb_cfg, fcns); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 684727d14e8..e9d14e2ebe0 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -348,11 +348,11 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // The set of reachable states. RzSetU *reachable_states = NULL; // The successor states to evaluate. - // This vector must have the same order as the elements pushed into addr_queue. + // This vector must have the same order as the elements pushed into branch_queue. RzVector *succ_states = NULL; // TODO: Add support for multiple entry points by spawning an interpreter for each of them. - // For now let's just drop them. + // For now let's just ignore them. if (rz_vector_len(iset->entry_points) > 1) { RZ_LOG_ERROR("More than one entry point is not yet supported by the prototype.\n"); goto pre_loop_error; From 174b2b1f99198e7bb12a0505473b512aee9c45b7 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 10 Feb 2026 20:33:22 +0100 Subject: [PATCH 188/334] Add edges to analysis basic blocks --- librz/inquiry/inquiry.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index ec5bd2fcdf3..2cc66561dda 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -736,12 +736,19 @@ RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inqui rz_iterator_foreach(iter, it2) { RzInterval *bb = *it2; RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, bb->addr); - if (!abb) { - rz_analysis_create_block(analysis, bb->addr, bb->size); - if (!abb) { - rz_warn_if_reached(); - break; - } + if (!abb && !(abb = rz_analysis_create_block(analysis, bb->addr, bb->size))) { + rz_warn_if_reached(); + break; + } + const RzList *successors = rz_inquiry_bb_cfg_get_neighbours_from(inquiry->bb_cfg, bb->addr); + RzGraphNode *n; + if (rz_list_length(successors) > 0) { + n = rz_list_get_n(successors, 0); + abb->jump = (ut64)n->data; + } + if (rz_list_length(successors) > 1) { + n = rz_list_get_n(successors, 1); + abb->fail = (ut64)n->data; } rz_analysis_function_add_block(afcn, abb); } @@ -749,7 +756,5 @@ RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inqui } rz_pvector_free(fcns); - // Add edges between blocks - return true; } From 7e152320ea46c74f3729b0b16bc0fab35c36a6c6 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 10 Feb 2026 20:38:01 +0100 Subject: [PATCH 189/334] Fix iq_fcn name --- librz/inquiry/inquiry.c | 1 + 1 file changed, 1 insertion(+) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 2cc66561dda..043260c1339 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -730,6 +730,7 @@ RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inqui char fcn_name[64] = { 0 }; rz_strf(fcn_name, "iq_fcn_0x%" PFMT64x, fcn_addr); RzAnalysisFunction *afcn = rz_analysis_create_function(analysis, fcn_name, fcn_addr, RZ_ANALYSIS_FCN_TYPE_FCN); + rz_mem_memzero(fcn_name, sizeof(fcn_name)); void **it2; RzIterator *iter = ht_up_as_iter(fcn->bb_cfg->basic_blocks); From f3f6026f25dfc43fae60d213a5473c5c5afb4578 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 10 Feb 2026 21:03:38 +0100 Subject: [PATCH 190/334] Extend initial set of addresses to interpret. --- librz/arch/analysis.c | 14 ++++++++++++++ librz/arch/xrefs.c | 30 +++++++++++++++++++++--------- librz/include/rz_analysis.h | 6 +++++- librz/inquiry/inquiry.c | 4 ++-- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/librz/arch/analysis.c b/librz/arch/analysis.c index d7ffde76af2..a695c8d8d8b 100644 --- a/librz/arch/analysis.c +++ b/librz/arch/analysis.c @@ -1043,6 +1043,20 @@ RZ_API bool rz_analysis_op_is_direct_call(RZ_NONNULL const RzAnalysisOp *op) { } } +/** + * \brief Returns true if the \p op is a direct call. + */ +RZ_API bool rz_analysis_op_is_direct_jump(RZ_NONNULL const RzAnalysisOp *op) { + rz_return_val_if_fail(op, false); + switch (op->type & RZ_ANALYSIS_OP_TYPE_MASK) { + case RZ_ANALYSIS_OP_TYPE_JMP: + case RZ_ANALYSIS_OP_TYPE_CJMP: + return true; + default: + return false; + } +} + RZ_API void rz_analysis_purge(RzAnalysis *analysis) { rz_analysis_hint_clear(analysis); rz_interval_tree_fini(&analysis->meta); diff --git a/librz/arch/xrefs.c b/librz/arch/xrefs.c index 5dd9fa957c3..b48a1b8b818 100644 --- a/librz/arch/xrefs.c +++ b/librz/arch/xrefs.c @@ -330,19 +330,23 @@ static bool read_up_to(RzAnalysis *analysis, ut64 addr, ut8 *buf, size_t buf_siz } /** - * \brief Returns all targets of call instructions within the \p sections. - * NOTE: This function disassembles all instructions within the boundaries and checks for calls. + * \brief Returns all targets of call/jmp instructions within the \p sections. + * NOTE: This function disassembles all instructions within the boundaries and checks for calls/jmps. * It DOES NOT use the existing xrefs. * * \param analysis The analysis plugin. - * \param sections The RzBinSections to disassemble to find call instructions. - * \param call_targets The found call targets of all disassemble calls. + * \param sections The RzBinSections to disassemble to find call/jmp instructions. + * \param branch_targets The found call targets of all disassemble calls. * NOTE: They can point outside of \p maps! + * \param include_call_return_pts If true, it will add addresses after a call instruction as well. * * \return True in case of success, false otherwise. */ -RZ_API bool rz_analysis_get_all_call_targets(RzAnalysis *analysis, const RzPVector /**/ *sections, RZ_NONNULL RZ_OUT RzSetU *call_targets) { - rz_return_val_if_fail(analysis && analysis->cur && sections && call_targets, false); +RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, + const RzPVector /**/ *sections, + bool include_call_return_pts, + RZ_NONNULL RZ_OUT RzSetU *branch_targets) { + rz_return_val_if_fail(analysis && analysis->cur && sections && branch_targets, false); size_t buf_size = (analysis->cur->bits / 8) * 16; ut8 *buf = RZ_NEWS0(ut8, buf_size); if (!buf_size || !buf) { @@ -363,15 +367,23 @@ RZ_API bool rz_analysis_get_all_call_targets(RzAnalysis *analysis, const RzPVect rz_analysis_op_fini(&op); break; } - if (rz_analysis_op(analysis, &op, addr, buf, buf_size, RZ_ANALYSIS_OP_MASK_BASIC) <= 0) { + if (rz_analysis_op(analysis, &op, addr, buf, buf_size, RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_INSN_PKT) <= 0) { rz_analysis_op_fini(&op); addr += op.size; continue; } // Only add jump targets going to executable regions. - if (rz_analysis_op_is_direct_call(&op) && op.jump != UT64_MAX && rz_pvector_find(sections, &op.jump, (RzListComparator)addr_at_aligned_x_addr, NULL)) { + if ((rz_analysis_op_is_direct_call(&op) || + rz_analysis_op_is_direct_jump(&op)) && + op.jump != UT64_MAX && + rz_pvector_find(sections, &op.jump, (RzListComparator)addr_at_aligned_x_addr, NULL)) { RZ_LOG_DEBUG("Add call target 0x%" PFMT64x " -> 0x%" PFMT64x "\n", op.addr, op.jump); - rz_set_u_add(call_targets, op.jump); + rz_set_u_add(branch_targets, op.jump); + if (include_call_return_pts && rz_analysis_op_is_direct_call(&op)) { + // If it is a call, also add the following instruction as reference. + // Because it is likely a return point. + rz_set_u_add(branch_targets, op.addr && op.size); + } } addr += op.size; rz_analysis_op_fini(&op); diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 0c69308d7b8..fd23db8d50e 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -1526,6 +1526,7 @@ RZ_API bool rz_analysis_op_is_eob(const RzAnalysisOp *op); RZ_API bool rz_analysis_op_is_call(RZ_NONNULL const RzAnalysisOp *op); RZ_API bool rz_analysis_op_changes_control_flow(RZ_NONNULL const RzAnalysisOp *op); RZ_API bool rz_analysis_op_is_direct_call(RZ_NONNULL const RzAnalysisOp *op); +RZ_API bool rz_analysis_op_is_direct_jump(RZ_NONNULL const RzAnalysisOp *op); RZ_API RzList /**/ *rz_analysis_op_list_new(void); RZ_API int rz_analysis_op(RZ_NONNULL RzAnalysis *analysis, RZ_OUT RzAnalysisOp *op, ut64 addr, const ut8 *data, ut64 len, RzAnalysisOpMask mask); RZ_API RzAnalysisOp *rz_analysis_op_hexstr(RzAnalysis *analysis, ut64 addr, const char *hexstr); @@ -1633,7 +1634,10 @@ RZ_API bool rz_analysis_xrefs_set(RzAnalysis *analysis, ut64 from, ut64 to, RzAn RZ_API bool rz_analysis_xrefs_deln(RzAnalysis *analysis, ut64 from, ut64 to, RzAnalysisXRefType type); RZ_API bool rz_analysis_xref_del(RzAnalysis *analysis, ut64 from, ut64 to); -RZ_API bool rz_analysis_get_all_call_targets(RzAnalysis *analysis, const RzPVector /**/ *sections, RZ_NONNULL RZ_OUT RzSetU *jump_targets); +RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, + const RzPVector /**/ *sections, + bool include_call_return_pts, + RZ_NONNULL RZ_OUT RzSetU *branch_targets); /* var.c */ RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_set_var( diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 043260c1339..b303d7c42cd 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -300,7 +300,7 @@ static bool setup_queues(RzCore *core, return false; } -static bool get_call_targets(RzCore *core, RzSetU *call_targets) { +static bool get_call_targets(RzCore *core, RzSetU *branch_targets) { RzPVector /**/ *sections = rz_bin_object_get_sections(core->bin->cur->o); if (!sections) { return false; @@ -319,7 +319,7 @@ static bool get_call_targets(RzCore *core, RzSetU *call_targets) { rz_pvector_remove_at(sections, *j); } rz_vector_free(non_x_idx); - if (!rz_analysis_get_all_call_targets(core->analysis, sections, call_targets)) { + if (!rz_analysis_get_all_branch_targets(core->analysis, sections, false, branch_targets)) { RZ_LOG_ERROR("Failed to get call targets.\n"); return false; } From 82033a351faff4b90a9186c4b468b0dc8f668b70 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 12 Feb 2026 14:46:15 +0100 Subject: [PATCH 191/334] Add edges between calls and their (guessed) return point. --- librz/include/rz_inquiry.h | 2 +- librz/inquiry/bb_cfg.c | 7 ++----- librz/inquiry/inquiry.c | 14 ++++++++++++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 2de2b355db3..dcd925059b1 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -67,7 +67,7 @@ RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_to(con RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref); -RZ_IPI bool rz_inquiry_fill_bb_cfg(RzInquiry *iq); +RZ_IPI bool rz_inquiry_complement_bb_cfg(RzInquiry *iq); RZ_API bool rz_inquiry_plugin_add(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NONNULL RzInquiryPlugin *plugin); RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NONNULL RzInquiryPlugin *plugin); diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index d35eaa89217..ac9a87eb98d 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -128,11 +128,7 @@ RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_to(con return rz_graph_innodes(cfg->graph, n); } -RZ_IPI bool rz_inquiry_fill_bb_cfg(RzInquiry *iq) { - if (ht_up_size(iq->bb_cfg->basic_blocks) == 0) { - RZ_LOG_WARN("No basic blocks present to fill CFG.\n"); - return false; - } +RZ_IPI bool rz_inquiry_complement_bb_cfg(RzInquiry *iq) { RzAnalysisXRef *xref; rz_vector_foreach (iq->xrefs, xref) { if (xref->type != RZ_ANALYSIS_XREF_TYPE_CODE) { @@ -147,6 +143,7 @@ RZ_IPI bool rz_inquiry_fill_bb_cfg(RzInquiry *iq) { } rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, xref->to); } + rz_iterator_free(bb_iter); } return true; } diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index b303d7c42cd..6001bb8b6fc 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -567,6 +567,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent break; } rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); + // Add or not add? rz_th_queue_push(iset->il_queue, (void *)bb, true); } } @@ -633,8 +634,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent // Now we need to check for executable regions it did not cover. // For this we simply delete all jump targets from our set, which point // into the already handled basic blocks. - // Then add a few addresses as new entry points. - // The addresses we add are jump targets from jump instructions in the binary. + // Then add a few addresses as new entry point. + // The addresses we add are jump targets from jump/call instructions in the binary. { rz_vector_clear(entry_points); RzVector *covered_jump_targets = rz_vector_new(sizeof(ut64), NULL, NULL); @@ -676,6 +677,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { printf("\n"); } + rz_inquiry_complement_bb_cfg(core->inquiry); RZ_LOG_DEBUG("INQUIRY: Done\n"); @@ -751,6 +753,14 @@ RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inqui n = rz_list_get_n(successors, 1); abb->fail = (ut64)n->data; } + RzAnalysisCallCandidate *cc; + if ((cc = ht_up_find(inquiry->call_candidates, bb->addr, NULL))) { + // Calls need an edge between the call instruction and its return address. + // That is technically wrong, because the call could be a tail call. + // But the prototype doesn't model this. + // So just add an edge. + abb->jump = cc->npc; + } rz_analysis_function_add_block(afcn, abb); } rz_iterator_free(iter); From 1e7b92fc850283181083746cbce53c1ce5eaf573 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 12 Feb 2026 14:47:08 +0100 Subject: [PATCH 192/334] Don't add duplicate edges to the bb cfg. --- librz/inquiry/bb_cfg.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index ac9a87eb98d..b37d44b7b89 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -90,6 +90,11 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 t rz_warn_if_reached(); return false; } + const RzList /**/ *neighs = rz_inquiry_bb_cfg_get_neighbours_from(cfg, from_bb); + if (rz_list_contains(neighs, t)) { + // Edge already added. + return true; + } rz_graph_add_edge(cfg->graph, f, t); return true; } From 4a4ec84ab0aa8ef45de478b163b0c9a13c0b06a6 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 12 Feb 2026 16:45:43 +0100 Subject: [PATCH 193/334] Log more warnings --- librz/inquiry/algorithms/revng_fcn_detection.c | 4 ++++ librz/inquiry/inquiry_helpers.c | 3 +++ 2 files changed, 7 insertions(+) diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index 5df45c6265d..e40742f0e0d 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -124,6 +124,10 @@ static void fill_cfep_and_ret_addresses( RzAnalysisCallCandidate *cc = *it; ut64 ret_addr = cc->npc; const RzList *predecessor = rz_inquiry_bb_cfg_get_neighbours_to(binary_bb_cfg, ret_addr); + if (!predecessor) { + RZ_LOG_WARN("The call candidate (0x%" PFMT64x ") NPC 0x%" PFMT64x " doesn't point to a BB.\n", cc->bb_addr, cc->npc); + continue; + } if (rz_list_length(predecessor) > 0) { rz_set_u_add(return_addresses, ret_addr); } diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c index 83d7678a2fc..be1c3be430d 100644 --- a/librz/inquiry/inquiry_helpers.c +++ b/librz/inquiry/inquiry_helpers.c @@ -21,15 +21,18 @@ RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana int max_read_size = (analysis->cur->bits / 8) * 16; ut8 *buf = RZ_NEWS0(ut8, max_read_size); if (!max_read_size || !buf) { + rz_warn_if_reached(); goto fail; } il_bb = RZ_NEW0(RzInterpreterILBB); if (!il_bb) { + rz_warn_if_reached(); goto fail; } il_bb->il_ops = rz_pvector_new((RzPVectorFree)rz_interpreter_insn_pkt_free); if (!il_bb->il_ops) { + rz_warn_if_reached(); goto fail; } il_bb->bb_addr = addr; From 4b54d17d1dc1581a74576d0678012c1e9e4757cc Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 12 Feb 2026 17:27:47 +0100 Subject: [PATCH 194/334] Fix possible endless loop if interpreter fails early. --- librz/inquiry/inquiry.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 6001bb8b6fc..d503d3dba11 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -512,9 +512,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent do { bool user_sent_signal = false; bool bb_decode_failed = false; - // Clear queues from any left overs of previous runs. - rz_list_free(rz_th_queue_pop_all(iset->il_queue)); - rz_list_free(rz_th_queue_pop_all(iset->branch_queue)); // Dispatch prototype interpreter into a thread. RZ_LOG_DEBUG("INQUIRY: Start main interpretation thread.\n"); @@ -557,6 +554,12 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x "\n", branch->target_addr); const RzInterpreterILBB *bb = get_il_bb(core, il_cache, branch->target_addr); if (!bb) { + // Delete the address from the call targets. + // This is currently necessary as a work around, because if the interpreter + // fails before interpreting the address, it is added again as next entry point. + // Giving an endless loop. + // One of the design thingies to fix in the proper implementation. + rz_set_u_delete(call_targets, branch->target_addr); // Signal interpreter the lifting failed. rz_atomic_bool_set(is_running, false); rz_th_queue_close(io_request_q); @@ -629,6 +632,11 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent rz_th_queue_open(io_result_q); rz_th_queue_open(iset->branch_queue); rz_th_queue_open(iset->il_queue); + // Clear queues from any left overs of previous runs. + rz_list_free(rz_th_queue_pop_all(io_result_q)); + rz_list_free(rz_th_queue_pop_all(io_request_q)); + rz_list_free(rz_th_queue_pop_all(iset->il_queue)); + rz_list_free(rz_th_queue_pop_all(iset->branch_queue)); // At this point the interpreter is finished and returned. // Now we need to check for executable regions it did not cover. From 7f24bd9f227d14ea4595dedca125e8d7807afba7 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 12 Feb 2026 21:22:56 +0100 Subject: [PATCH 195/334] Don't overwrite existing symbol names. --- librz/core/cmd/cmd_inquiry.c | 3 ++- librz/include/rz_inquiry.h | 5 ++++- librz/inquiry/inquiry.c | 26 +++++++++++++++++++++----- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 3d205493baf..cec45470abb 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -35,7 +35,8 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar if (!success) { return RZ_CMD_STATUS_ERROR; } - success = rz_inquiry_function_deduction(core->analysis, core->inquiry, entry_point); + const RzPVector *symbols = rz_bin_object_get_symbols(core->bin->cur->o); + success = rz_inquiry_function_deduction(core->analysis, core->inquiry, entry_point, symbols); RZ_LOG_INFO("Perform function deduction: %s\n", success ? "OK" : "FAIL"); return success ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index dcd925059b1..e5d191e40c6 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -81,7 +81,10 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL co RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *entry_points); -RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry, ut64 entry_point); +RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, + RzInquiry *inquiry, + ut64 entry_point, + const RzPVector /**/ *symbols); //============ // Algorithms diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index d503d3dba11..1cb394b8bea 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -723,7 +723,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent return return_code; } -RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry, ut64 entry_point) { +RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry, ut64 entry_point, + const RzPVector /**/ *symbols) { RzPVector *fcns = rz_pvector_new((RzPVectorFree)rz_inquiry_function_free); rz_inquiry_algo_revng_fcn_detection( entry_point, @@ -737,10 +738,25 @@ RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inqui RzInquiryFunction *fcn = *it; ut64 fcn_addr = *(ut64 *)rz_vector_head(fcn->entry_points); - char fcn_name[64] = { 0 }; - rz_strf(fcn_name, "iq_fcn_0x%" PFMT64x, fcn_addr); - RzAnalysisFunction *afcn = rz_analysis_create_function(analysis, fcn_name, fcn_addr, RZ_ANALYSIS_FCN_TYPE_FCN); - rz_mem_memzero(fcn_name, sizeof(fcn_name)); + const char *symb_name = NULL; + void **it; + rz_pvector_foreach(symbols, it) { + RzBinSymbol *s = *it; + if (s->vaddr == fcn_addr) { + symb_name = s->name; + break; + } + } + if (RZ_STR_ISEMPTY(symb_name)) { + char new_fcn_name[64] = { 0 }; + rz_strf(new_fcn_name, "fcn_0x%" PFMT64x, fcn_addr); + symb_name = new_fcn_name; + } + RzAnalysisFunction *afcn = rz_analysis_create_function(analysis, symb_name, fcn_addr, RZ_ANALYSIS_FCN_TYPE_FCN); + if (!afcn) { + rz_warn_if_reached(); + return false; + } void **it2; RzIterator *iter = ht_up_as_iter(fcn->bb_cfg->basic_blocks); From 9c1e9f7786dea2fa5e25ba76ef8b3a89a67a1043 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 12 Feb 2026 21:26:10 +0100 Subject: [PATCH 196/334] Rename call targets -> branch targets --- librz/inquiry/inquiry.c | 45 ++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 1cb394b8bea..add7e10836f 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -10,6 +10,7 @@ #include "rz_config.h" #include "rz_cons.h" #include "rz_il/definitions/mem.h" +#include "rz_il/rz_il_validate.h" #include "rz_il/rz_il_vm.h" #include "rz_inquiry/rz_interpreter.h" #include "rz_inquiry_plugins.h" @@ -300,7 +301,7 @@ static bool setup_queues(RzCore *core, return false; } -static bool get_call_targets(RzCore *core, RzSetU *branch_targets) { +static bool get_branch_targets(RzCore *core, RzSetU *branch_targets) { RzPVector /**/ *sections = rz_bin_object_get_sections(core->bin->cur->o); if (!sections) { return false; @@ -320,7 +321,7 @@ static bool get_call_targets(RzCore *core, RzSetU *branch_targets) { } rz_vector_free(non_x_idx); if (!rz_analysis_get_all_branch_targets(core->analysis, sections, false, branch_targets)) { - RZ_LOG_ERROR("Failed to get call targets.\n"); + RZ_LOG_ERROR("Failed to get branch targets.\n"); return false; } rz_pvector_free(sections); @@ -388,6 +389,26 @@ static const RzInterpreterILBB *get_il_bb(RzCore *core, HtUP *il_cache, ut64 add RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", addr); return NULL; } + +#if RZ_BUILD_DEBUG + RzAnalysisILVM *vm = rz_analysis_il_vm_new(core->analysis, NULL); + RzILValidateGlobalContext *ctx = rz_il_validate_global_context_new_from_vm(vm->vm); + void **it; + size_t i = 0; + rz_pvector_enumerate(bb->il_ops, it, i) { + char *report = NULL; + RzInterpreterInsnPkt *pkt = *it; + if (!rz_il_validate_effect(pkt->effect, ctx, NULL, NULL, &report)) { + RZ_LOG_ERROR("Validation failed for IL op %" PFMTSZu " in BB 0x%" PFMT64x " in insn packet:\n" + "\t'%s'\n", i, bb->bb_addr, report); + } + free(report); + } + rz_analysis_il_vm_free(vm); + rz_il_validate_global_context_free(ctx); + // Otherwise YOLO +#endif + rz_analysis_add_bb(core->analysis, bb->bb_addr, bb->size); RZ_LOG_DEBUG("INQUIRY: Send IL result: %p.\n", bb); ht_up_insert(il_cache, bb->bb_addr, bb); @@ -417,7 +438,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent RzThread *interpr_th = NULL; RzBuffer *io_buf = rz_buf_new_with_io(&core->analysis->iob); RzAnalysisILVM *analysis_vm = NULL; - RzSetU *call_targets = rz_set_u_new(); + RzSetU *branch_targets = rz_set_u_new(); rz_cons_push(); @@ -431,13 +452,13 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent // of the pointers. il_cache = ht_up_new(NULL, (RzPVectorFree)rz_interpreter_il_bb_free); - if (!get_call_targets(core, call_targets)) { - RZ_LOG_ERROR("Failed to get call targets.\n"); + if (!get_branch_targets(core, branch_targets)) { + RZ_LOG_ERROR("Failed to get branch targets.\n"); return_code = false; goto error_free; } if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { - printf("Total call targets in binary: %" PFMT32d "\n", rz_set_u_size(call_targets)); + printf("Total branch targets in binary: %" PFMT32d "\n", rz_set_u_size(branch_targets)); } // Initialize the abstract state with the architecture's registers. @@ -554,12 +575,12 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x "\n", branch->target_addr); const RzInterpreterILBB *bb = get_il_bb(core, il_cache, branch->target_addr); if (!bb) { - // Delete the address from the call targets. + // Delete the address from the branch targets. // This is currently necessary as a work around, because if the interpreter // fails before interpreting the address, it is added again as next entry point. // Giving an endless loop. // One of the design thingies to fix in the proper implementation. - rz_set_u_delete(call_targets, branch->target_addr); + rz_set_u_delete(branch_targets, branch->target_addr); // Signal interpreter the lifting failed. rz_atomic_bool_set(is_running, false); rz_th_queue_close(io_request_q); @@ -647,7 +668,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent { rz_vector_clear(entry_points); RzVector *covered_jump_targets = rz_vector_new(sizeof(ut64), NULL, NULL); - RzIterator *ct_iter = rz_set_u_as_iter(call_targets); + RzIterator *ct_iter = rz_set_u_as_iter(branch_targets); size_t x = 0; ut64 *ct; rz_iterator_foreach(ct_iter, ct) { @@ -672,12 +693,12 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent rz_vector_foreach (covered_jump_targets, ep) { // Delete the selected ones from the jump target set. // So they are not requested again. - rz_set_u_delete(call_targets, *ep); + rz_set_u_delete(branch_targets, *ep); } rz_vector_free(covered_jump_targets); } if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { - printf(RZ_CONS_CLEAR_LINE "\rCall targets left: %" PFMT32d, rz_set_u_size(call_targets)); + printf(RZ_CONS_CLEAR_LINE "\rBranch targets left: %" PFMT32d, rz_set_u_size(branch_targets)); fflush(stdout); } } while (!rz_vector_empty(entry_points)); @@ -695,7 +716,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent error_free: RZ_LOG_DEBUG("INQUIRY: Close queues\n"); rz_vector_free(entry_points); - rz_set_u_free(call_targets); + rz_set_u_free(branch_targets); rz_buf_free(io_buf); rz_analysis_il_vm_free(analysis_vm); rz_th_queue_close(io_request_q); From eae376f1cdfa2a9889235e5b41ea2ebd0843bb3c Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 14 Feb 2026 15:31:26 +0100 Subject: [PATCH 197/334] Fix function naming of existing symbols --- librz/inquiry/inquiry.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index add7e10836f..6bbba964b42 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -759,21 +759,19 @@ RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inqui RzInquiryFunction *fcn = *it; ut64 fcn_addr = *(ut64 *)rz_vector_head(fcn->entry_points); - const char *symb_name = NULL; + char new_fcn_name[64] = { 0 }; void **it; rz_pvector_foreach(symbols, it) { RzBinSymbol *s = *it; - if (s->vaddr == fcn_addr) { - symb_name = s->name; + if (s->vaddr == fcn_addr && RZ_STR_EQ(s->type, RZ_BIN_TYPE_FUNC_STR)) { + rz_strf(new_fcn_name, "sym.%s", s->name); break; } } - if (RZ_STR_ISEMPTY(symb_name)) { - char new_fcn_name[64] = { 0 }; + if (new_fcn_name[0] == '\0') { rz_strf(new_fcn_name, "fcn_0x%" PFMT64x, fcn_addr); - symb_name = new_fcn_name; } - RzAnalysisFunction *afcn = rz_analysis_create_function(analysis, symb_name, fcn_addr, RZ_ANALYSIS_FCN_TYPE_FCN); + RzAnalysisFunction *afcn = rz_analysis_create_function(analysis, new_fcn_name, fcn_addr, RZ_ANALYSIS_FCN_TYPE_FCN); if (!afcn) { rz_warn_if_reached(); return false; From 89de5dcce6c16ce623313a7ddb941886d09bf7b5 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 14 Feb 2026 15:44:46 +0100 Subject: [PATCH 198/334] Update tests with latest results. --- test/db/inquiry/interpreter/xrefs | 545 +++++++++++++++--------------- 1 file changed, 273 insertions(+), 272 deletions(-) diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs index df0bc72ff62..a35f2f3e55d 100644 --- a/test/db/inquiry/interpreter/xrefs +++ b/test/db/inquiry/interpreter/xrefs @@ -1,118 +1,119 @@ NAME=Indirect call x86 FILE=bins/inquiry/interpreter/prototype/x86_icall_malloc CMDS=< 0x00401a8d cmp dword [rbp-0x0c], 0x03 - ,==< 0x00401a91 jnb 0x401abd - |: 0x00401a93 mov eax, dword [rbp-0x0c] - |: 0x00401a96 mov ecx, eax - |: 0x00401a98 mov al, 0x00 - |: 0x00401a9a call qword [rcx*8+obj.fcn_arr] ; 0x4a80d0 - |: ; CODE XREF from sym.function_0 @ +0xa - |: ; CODE XREF from sym.function_1 @ +0xa - |: ; CODE XREF from sym.function_2 @ +0xa - |: 0x00401aa1 mov qword [rbp-0x18], rax - |: 0x00401aa5 mov rax, qword [rbp-0x18] - |: 0x00401aa9 movzx eax, byte [rax] ; [0x4a9b50:1]=0 - |: 0x00401aac add eax, dword [rbp-0x08] - |: 0x00401aaf mov dword [rbp-0x08], eax - |: 0x00401ab2 mov eax, dword [rbp-0x0c] - |: 0x00401ab5 add eax, 0x01 - |: 0x00401ab8 mov dword [rbp-0x0c], eax - |`=< 0x00401abb jmp 0x401a8d - | ; CODE XREF from main @ +0x21 - `--> 0x00401abd mov eax, dword [rbp-0x08] - 0x00401ac0 add rsp, 0x20 - 0x00401ac4 pop rbp - 0x00401ac5 ret - 0x00401ac6 nop word [rax+rax*1], ax - ; CODE XREF from main @ +0x2a - ;-- function_0: - 0x00401ad0 push rbp - 0x00401ad1 mov rbp, rsp - 0x00401ad4 call sym.some_ptr - ; CODE XREF from sym.some_ptr @ +0xf - 0x00401ad9 pop rbp - 0x00401ada ret - 0x00401adb nop dword [rax+rax*1], eax - ; CODE XREF from sym.function_0 @ +0x4 - ; CODE XREF from sym.function_1 @ +0x4 - ; CODE XREF from sym.function_2 @ +0x4 - ;-- some_ptr: - 0x00401ae0 push rbp - 0x00401ae1 mov rbp, rsp - 0x00401ae4 mov rax, obj.z ; 0x4a9b50 - 0x00401aee pop rbp - 0x00401aef ret - ; CODE XREF from main @ +0x2a - ;-- function_1: - 0x00401af0 push rbp - 0x00401af1 mov rbp, rsp - 0x00401af4 call sym.some_ptr - ; CODE XREF from sym.some_ptr @ +0xf - 0x00401af9 pop rbp - 0x00401afa ret - 0x00401afb nop dword [rax+rax*1], eax - ; CODE XREF from main @ +0x2a - ;-- function_2: - 0x00401b00 push rbp - 0x00401b01 mov rbp, rsp - 0x00401b04 call sym.some_ptr - ; CODE XREF from sym.some_ptr @ +0xf - 0x00401b09 pop rbp - 0x00401b0a ret - main+33 0x401a91 -> CODE -> 0x401abd main+77 - main+42 0x401a9a -> CODE -> 0x401ad0 sym.function_0 - main+42 0x401a9a -> CODE -> 0x401af0 sym.function_1 - main+42 0x401a9a -> CODE -> 0x401b00 sym.function_2 - main+42 0x401a9a -> DATA -> 0x4a80d0 obj.fcn_arr - main+42 0x401a9a -> DATA -> 0x4a80d8 obj.fcn_arr+8 - main+42 0x401a9a -> DATA -> 0x4a80e0 obj.fcn_arr+16 - main+57 0x401aa9 -> DATA -> 0x4a9b50 obj.z - main+75 0x401abb -> CODE -> 0x401a8d main+29 - sym.function_0+4 0x401ad4 -> CODE -> 0x401ae0 sym.some_ptr - sym.function_0+10 0x401ada -> CODE -> 0x401aa1 main+49 - sym.some_ptr+15 0x401aef -> CODE -> 0x401ad9 sym.function_0+9 - sym.some_ptr+15 0x401aef -> CODE -> 0x401af9 sym.function_1+9 - sym.some_ptr+15 0x401aef -> CODE -> 0x401b09 sym.function_2+9 - sym.function_1+4 0x401af4 -> CODE -> 0x401ae0 sym.some_ptr - sym.function_1+10 0x401afa -> CODE -> 0x401aa1 main+49 - sym.function_2+4 0x401b04 -> CODE -> 0x401ae0 sym.some_ptr - sym.function_2+10 0x401b0a -> CODE -> 0x401aa1 main+49 -0x401a70 35 0 0 -0x401a8d 6 0 0 -0x401a93 14 0 0 -0x401aa1 28 0 0 -0x401abd 9 0 0 -0x401ad0 9 0 0 0x00401ae0 0x00401ae0 -0x401ad9 2 0 0 -0x401ae0 16 0 0 -0x401af0 9 0 0 0x00401ae0 0x00401ae0 -0x401af9 2 0 0 -0x401b00 9 0 0 0x00401ae0 0x00401ae0 -0x401b09 2 0 0 -0x401b70 27 0 0 -0x401b8b 34 0 0 -0x401bb6 23 0 0 -0x401bcd 18 0 0 -0x401bdf 5 0 0 -0x401be4 9 0 0 -0x401bed 5 0 0 + ;-- section..text: + ;-- .text: +/ int sym.main(int argc, char **argv, char **envp); +| 0x08000040 push rbp ; RELOC TARGET 32 .text @ 0x08000040 - 0x8000138 ; [02] -r-x section size 155 named .text +| 0x08000041 mov rbp, rsp +| 0x08000044 sub rsp, 0x20 +| 0x08000048 mov dword [rbp-0x04], 0x00 +| 0x0800004f mov dword [rbp-0x08], 0x00 +| 0x08000056 mov dword [rbp-0x0c], 0x00 +| ; CODE XREF from sym.main @ 0x800008b +| .-> 0x0800005d cmp dword [rbp-0x0c], 0x03 +| ,==< 0x08000061 jnb 0x800008d +| |: 0x08000063 mov eax, dword [rbp-0x0c] +| |: 0x08000066 mov ecx, eax +| |: 0x08000068 mov al, 0x00 +| |: 0x0800006a call qword [rcx*8+obj.fcn_arr] ; sym..data +| |: ; 0x80000e0; RELOC 32 .data @ 0x080000e0 +| |: ; CODE XREF from sym.function_0 @ 0x80000aa +| |: ; CODE XREF from sym.function_1 @ 0x80000ca +| |: ; CODE XREF from sym.function_2 @ 0x80000da +| |: 0x08000071 mov qword [rbp-0x18], rax +| |: 0x08000075 mov rax, qword [rbp-0x18] +| |: 0x08000079 movzx eax, byte [rax] +| |: 0x0800007c add eax, dword [rbp-0x08] +| |: 0x0800007f mov dword [rbp-0x08], eax +| |: 0x08000082 mov eax, dword [rbp-0x0c] +| |: 0x08000085 add eax, 0x01 +| |: 0x08000088 mov dword [rbp-0x0c], eax +| |`=< 0x0800008b jmp 0x800005d +| | ; CODE XREF from sym.main @ 0x8000061 +| `--> 0x0800008d mov eax, dword [rbp-0x08] +| 0x08000090 add rsp, 0x20 +| 0x08000094 pop rbp +\ 0x08000095 ret + 0x08000096 nop word [rax+rax*1], ax + ; CODE XREF from sym.main @ 0x800006a +/ sym.function_0(); +| 0x080000a0 push rbp +| 0x080000a1 mov rbp, rsp +| 0x080000a4 call sym.some_ptr +| ; CODE XREF from sym.some_ptr @ 0x80000bf +| 0x080000a9 pop rbp +\ 0x080000aa ret + 0x080000ab nop dword [rax+rax*1], eax + ; CODE XREF from sym.function_0 @ 0x80000a4 + ; CODE XREF from sym.function_1 @ 0x80000c4 + ; CODE XREF from sym.function_2 @ 0x80000d4 +/ sym.some_ptr(); +| 0x080000b0 push rbp +| 0x080000b1 mov rbp, rsp +| 0x080000b4 mov rax, obj.z ; sym..bss +| ; 0x80000f8; RELOC 64 .bss @ 0x080000f8 +| 0x080000be pop rbp +\ 0x080000bf ret + ; CODE XREF from sym.main @ 0x800006a +/ sym.function_1(); +| 0x080000c0 push rbp +| 0x080000c1 mov rbp, rsp +| 0x080000c4 call sym.some_ptr +| ; CODE XREF from sym.some_ptr @ 0x80000bf +| 0x080000c9 pop rbp +\ 0x080000ca ret + 0x080000cb nop dword [rax+rax*1], eax + ; CODE XREF from sym.main @ 0x800006a +/ sym.function_2(); +| 0x080000d0 push rbp +| 0x080000d1 mov rbp, rsp +| 0x080000d4 call sym.some_ptr +| ; CODE XREF from sym.some_ptr @ 0x80000bf +| 0x080000d9 pop rbp +\ 0x080000da ret + section..text+33 0x8000061 -> CODE -> 0x800008d reloc..data+32 + section..text+42 0x800006a -> CODE -> 0x80000a0 sym.function_0 + section..text+42 0x800006a -> CODE -> 0x80000c0 sym.function_1 + section..text+42 0x800006a -> CODE -> 0x80000d0 sym.function_2 + section..text+42 0x800006a -> DATA -> 0x80000e0 section..data + section..text+42 0x800006a -> DATA -> 0x80000e8 reloc..text.80000e8 + section..text+42 0x800006a -> DATA -> 0x80000f0 reloc..text.80000f0 + reloc..data+30 0x800008b -> CODE -> 0x800005d section..text+29 + sym.function_0+4 0x80000a4 -> CODE -> 0x80000b0 sym.some_ptr + sym.function_0+10 0x80000aa -> CODE -> 0x8000071 reloc..data+4 + reloc..bss+9 0x80000bf -> CODE -> 0x80000a9 sym.function_0+9 + reloc..bss+9 0x80000bf -> CODE -> 0x80000c9 sym.function_1+9 + reloc..bss+9 0x80000bf -> CODE -> 0x80000d9 sym.function_2+9 + sym.function_1+4 0x80000c4 -> CODE -> 0x80000b0 sym.some_ptr + sym.function_1+10 0x80000ca -> CODE -> 0x8000071 reloc..data+4 + sym.function_2+4 0x80000d4 -> CODE -> 0x80000b0 sym.some_ptr + sym.function_2+10 0x80000da -> CODE -> 0x8000071 reloc..data+4 + addr size traced ninstr jump fail fcns calls xrefs +--------------------------------------------------------------------------------------- +0x8000040 35 0 0 0x08000063 0x0800008d 0x08000040 +0x800005d 6 0 0 0x08000063 0x0800008d 0x08000040 +0x8000063 14 0 0 0x08000071 0x080000c0 0x08000040 +0x8000071 28 0 0 0x0800005d 0x08000040 +0x800008d 9 0 0 0x08000040 +0x80000a0 9 0 0 0x080000a9 0x080000a0 0x080000b0 0x080000b0 +0x80000a9 2 0 0 0x08000071 0x080000a0 +0x80000b0 16 0 0 0x080000a9 0x080000c9 0x080000b0 +0x80000c0 9 0 0 0x080000c9 0x080000c0 0x080000b0 0x080000b0 +0x80000c9 2 0 0 0x08000071 0x080000c0 +0x80000d0 9 0 0 0x080000d9 0x080000d0 0x080000b0 0x080000b0 +0x80000d9 2 0 0 0x08000071 0x080000d0 EOF RUN @@ -127,82 +128,82 @@ EOF EXPECT=< 0x08000060 [ R2 = memw(FP+##-0xc) - : 0x08000064 [ P0 = cmp.gtu(R2,##0x2) - :,=< 0x08000068 [ if (P0) jump:nt 0x80000ac - ,===< 0x0800006c [ jump 0x8000070 - `---> 0x08000070 [ R2 = memw(FP+##-0xc) - :| ;-- .data: - :| 0x08000074 / immext(##sym.some_ptr) ; 0x80000e8 - :| ; sym..data; RELOC 32 .data @ 0x080000e8 - :| ;-- .data: - :| 0x08000078 \ R2 = memw(R2<<#0x2+##0x80000e8) ; RELOC 32 .data @ 0x080000e8 - :| 0x0800007c [ callr R2 - :| ; CODE XREF from sym.function_0 @ +0x8 - :| ; CODE XREF from sym.function_1 @ +0x8 - :| ; CODE XREF from sym.function_2 @ +0x8 - :| 0x08000080 [ memw(FP+##-0x10) = R0 - :| 0x08000084 [ R2 = memw(FP+##-0x10) - :| 0x08000088 [ R3 = memub(R2+##0x0) - :| 0x0800008c [ R2 = memw(FP+##-0x8) - :| 0x08000090 [ R2 = add(R2,R3) - :| 0x08000094 [ memw(FP+##-0x8) = R2 - ,===< 0x08000098 [ jump 0x800009c - `---> 0x0800009c [ R2 = memw(FP+##-0xc) - :| 0x080000a0 [ R2 = add(R2,##0x1) - :| 0x080000a4 [ memw(FP+##-0xc) = R2 - `==< 0x080000a8 [ jump 0x8000060 - | ; CODE XREF from section..text @ +0x28 - `-> 0x080000ac [ R0 = memw(FP+##-0x8) - 0x080000b0 [ LR:FP = dealloc_return(FP):raw - ; CODE XREF from reloc..data.8000078 @ +0x4 - ;-- function_0: - 0x080000b4 [ allocframe(SP,#0x0):raw - 0x080000b8 [ call sym.some_ptr - ; CODE XREF from reloc..bss.80000c8 @ +0x4 - 0x080000bc [ LR:FP = dealloc_return(FP):raw - ; CODE XREF from sym.function_0 @ +0x4 - ; CODE XREF from sym.function_1 @ +0x4 - ; CODE XREF from sym.function_2 @ +0x4 - ;-- some_ptr: - 0x080000c0 [ allocframe(SP,#0x0):raw - ;-- .bss: - 0x080000c4 / immext(##sym.some_ptr) ; RELOC 32 .bss @ 0x080000f8 - ;-- .bss: - 0x080000c8 \ R0 = ##obj.z ; RELOC 32 .bss @ 0x080000f8 - 0x080000cc [ LR:FP = dealloc_return(FP):raw - ; CODE XREF from reloc..data.8000078 @ +0x4 - ;-- function_1: - 0x080000d0 [ allocframe(SP,#0x0):raw - 0x080000d4 [ call sym.some_ptr - ; CODE XREF from reloc..bss.80000c8 @ +0x4 - 0x080000d8 [ LR:FP = dealloc_return(FP):raw - ; CODE XREF from reloc..data.8000078 @ +0x4 - ;-- function_2: - 0x080000dc [ allocframe(SP,#0x0):raw - 0x080000e0 [ call sym.some_ptr - ; CODE XREF from reloc..bss.80000c8 @ +0x4 - 0x080000e4 [ LR:FP = dealloc_return(FP):raw - ; DATA XREF from reloc..data @ +/ int sym.main(int argc, char **argv, char **envp); +| 0x08000040 [ allocframe(SP,#0x10):raw ; RELOC TARGET 32 .text @ 0x08000040 + 0x9c ; [02] -r-x section size 168 named .text +| 0x08000044 [ R2 = add(FP,##-0x4) +| 0x08000048 [ memw(R2+#0x0) = ##0x0 +| 0x0800004c [ R2 = add(FP,##-0x8) +| 0x08000050 [ memw(R2+#0x0) = ##0x0 +| 0x08000054 [ R2 = add(FP,##-0xc) +| 0x08000058 [ memw(R2+#0x0) = ##0x0 +| ,=< 0x0800005c [ jump 0x8000060 +| | ; CODE XREF from sym.main @ 0x80000a8 +| .`-> 0x08000060 [ R2 = memw(FP+##-0xc) +| : 0x08000064 [ P0 = cmp.gtu(R2,##0x2) +| :,=< 0x08000068 [ if (P0) jump:nt 0x80000ac +| ,===< 0x0800006c [ jump 0x8000070 +| `---> 0x08000070 [ R2 = memw(FP+##-0xc) +| :| ;-- .data: +| :| 0x08000074 / immext(##sym.some_ptr) ; 0x80000e8 +| :| ; sym..data; RELOC 32 .data @ 0x080000e8 +| :| ;-- .data: +| :| 0x08000078 \ R2 = memw(R2<<#0x2) ; RELOC 32 .data @ 0x080000e8 +| :| 0x0800007c [ callr R2 +| :| ; CODE XREF from sym.function_0 @ 0x80000bc +| :| ; CODE XREF from sym.function_1 @ 0x80000d8 +| :| ; CODE XREF from sym.function_2 @ 0x80000e4 +| :| 0x08000080 [ memw(FP+##-0x10) = R0 +| :| 0x08000084 [ R2 = memw(FP+##-0x10) +| :| 0x08000088 [ R3 = memub(R2+##0x0) +| :| 0x0800008c [ R2 = memw(FP+##-0x8) +| :| 0x08000090 [ R2 = add(R2,R3) +| :| 0x08000094 [ memw(FP+##-0x8) = R2 +| ,===< 0x08000098 [ jump 0x800009c +| `---> 0x0800009c [ R2 = memw(FP+##-0xc) +| :| 0x080000a0 [ R2 = add(R2,##0x1) +| :| 0x080000a4 [ memw(FP+##-0xc) = R2 +| `==< 0x080000a8 [ jump 0x8000060 +| | ; CODE XREF from sym.main @ 0x8000068 +| `-> 0x080000ac [ R0 = memw(FP+##-0x8) +\ 0x080000b0 [ LR:FP = dealloc_return(FP):raw + ; CODE XREF from sym.main @ 0x800007c +/ sym.function_0(); +| 0x080000b4 [ allocframe(SP,#0x0):raw +| 0x080000b8 [ call sym.some_ptr +| ; CODE XREF from sym.some_ptr @ 0x80000cc +\ 0x080000bc [ LR:FP = dealloc_return(FP):raw + ; CODE XREF from sym.function_0 @ 0x80000b8 + ; CODE XREF from sym.function_1 @ 0x80000d4 + ; CODE XREF from sym.function_2 @ 0x80000e0 +/ sym.some_ptr(); +| 0x080000c0 [ allocframe(SP,#0x0):raw +| ;-- .bss: +| 0x080000c4 / immext(##sym.some_ptr) ; RELOC 32 .bss @ 0x080000f8 +| ;-- .bss: +| 0x080000c8 \ R0 = ##obj.z ; RELOC 32 .bss @ 0x080000f8 +\ 0x080000cc [ LR:FP = dealloc_return(FP):raw + ; CODE XREF from sym.main @ 0x800007c +/ sym.function_1(); +| 0x080000d0 [ allocframe(SP,#0x0):raw +| 0x080000d4 [ call sym.some_ptr +| ; CODE XREF from sym.some_ptr @ 0x80000cc +\ 0x080000d8 [ LR:FP = dealloc_return(FP):raw + ; CODE XREF from sym.main @ 0x800007c +/ sym.function_2(); +| 0x080000dc [ allocframe(SP,#0x0):raw +| 0x080000e0 [ call sym.some_ptr +| ; CODE XREF from sym.some_ptr @ 0x80000cc +\ 0x080000e4 [ LR:FP = dealloc_return(FP):raw + ; DATA XREF from sym.main @ 0x8000074 ;-- section..data: ;-- .data: ;-- .text: ;-- fcn_arr: 0x080000e8 .dword 0x080000b4 ; sym.function_0 ; RELOC 32 .text @ 0x08000040 + 0x74 ; [04] -rw- section size 12 named .data - ; DATA XREF from reloc..data @ + ; DATA XREF from sym.main @ 0x8000074 ;-- .text: 0x080000ec .dword 0x080000d0 ; sym.function_1 ; RELOC 32 .text @ 0x08000040 + 0x90 - ; DATA XREF from reloc..data @ + ; DATA XREF from sym.main @ 0x8000074 ;-- .text: 0x080000f0 .dword 0x080000dc ; sym.function_2 ; RELOC 32 .text @ 0x08000040 + 0x9c 0x080000f4 @@ -228,22 +229,22 @@ EXPECT=< CODE -> 0x8000080 reloc..data.8000078+8 sym.function_2+4 0x80000e0 -> CODE -> 0x80000c0 sym.some_ptr sym.function_2+8 0x80000e4 -> CODE -> 0x8000080 reloc..data.8000078+8 - addr size traced ninstr jump fail fcns calls xrefs --------------------------------------------------------------------- -0x8000040 32 0 0 -0x8000060 12 0 0 -0x800006c 4 0 0 -0x8000070 16 0 0 -0x8000080 28 0 0 -0x800009c 16 0 0 -0x80000ac 8 0 0 -0x80000b4 8 0 0 0x080000c0 0x080000c0 -0x80000bc 4 0 0 -0x80000c0 16 0 0 -0x80000d0 8 0 0 0x080000c0 0x080000c0 -0x80000d8 4 0 0 -0x80000dc 8 0 0 0x080000c0 0x080000c0 -0x80000e4 4 0 0 + addr size traced ninstr jump fail fcns calls xrefs +--------------------------------------------------------------------------------------- +0x8000040 32 0 0 0x08000060 0x08000040 +0x8000060 12 0 0 0x0800006c 0x080000ac 0x08000040 +0x800006c 4 0 0 0x08000070 0x08000040 +0x8000070 16 0 0 0x08000080 0x080000d0 0x08000040 +0x8000080 28 0 0 0x0800009c 0x08000040 +0x800009c 16 0 0 0x08000060 0x08000040 +0x80000ac 8 0 0 0x08000040 +0x80000b4 8 0 0 0x080000bc 0x080000b4 0x080000c0 0x080000c0 +0x80000bc 4 0 0 0x08000080 0x080000b4 +0x80000c0 16 0 0 0x080000bc 0x080000d8 0x080000c0 +0x80000d0 8 0 0 0x080000d8 0x080000d0 0x080000c0 0x080000c0 +0x80000d8 4 0 0 0x08000080 0x080000d0 +0x80000dc 8 0 0 0x080000e4 0x080000dc 0x080000c0 0x080000c0 +0x80000e4 4 0 0 0x08000080 0x080000dc EOF EXPECT_ERR=< 0x080000c0 sethi 0x20000, g1 ; RELOC 0 .data @ 0x08000130 - :| ;-- .data: - :| 0x080000c4 or g1, 0x130, g2 ; RELOC 0 .data @ 0x08000130 - :| 0x080000c8 ld [fp+0x7f7], g1 - :| 0x080000cc srl g1, 0, g1 - :| 0x080000d0 sllx g1, 3, g1 - :| 0x080000d4 ldx [g2+g1], g1 ; 0x8000130 - :| ; obj.fcn_arr - :| 0x080000d8 call g1 - :| 0x080000dc nop - :| ; CODE XREF from sym.function_0 @ +0x18 - :| ; CODE XREF from sym.function_1 @ +0x18 - :| 0x080000e0 stx o0, [fp+0x7ef] - :| 0x080000e4 ldx [fp+0x7ef], g1 - :| 0x080000e8 ldub [g1], g1 - :| 0x080000ec and g1, 0xff, g1 - :| 0x080000f0 ld [fp+0x7fb], g2 - :| 0x080000f4 add g2, g1, g1 - :| 0x080000f8 st g1, [fp+0x7fb] - :| 0x080000fc ld [fp+0x7f7], g1 - :| 0x08000100 add g1, 1, g1 - :| 0x08000104 st g1, [fp+0x7f7] - :| ; CODE XREF from sym.main @ +0x10 - :`-> 0x08000108 ld [fp+0x7f7], g1 - : 0x0800010c cmp g1, 2 - `==< 0x08000110 bleu icc, reloc..data - 0x08000114 nop +/ int sym.main(int argc, char **argv, char **envp); +| 0x080000ac save sp, -0xc0, sp +| 0x080000b0 st g0, [fp+0x7fb] +| 0x080000b4 st g0, [fp+0x7f7] +| ,=< 0x080000b8 ba xcc, 0x8000108 +| | 0x080000bc nop +| | ; CODE XREF from sym.main @ 0x8000114 +| | ;-- .data: +| .--> 0x080000c0 sethi 0x20000, g1 ; RELOC 0 .data @ 0x08000130 +| :| ;-- .data: +| :| 0x080000c4 or g1, 0x130, g2 ; RELOC 0 .data @ 0x08000130 +| :| 0x080000c8 ld [fp+0x7f7], g1 +| :| 0x080000cc srl g1, 0, g1 +| :| 0x080000d0 sllx g1, 3, g1 +| :| 0x080000d4 ldx [g2+g1], g1 ; 0x8000130 +| :| ; obj.fcn_arr +| :| 0x080000d8 call g1 +| :| 0x080000dc nop +| :| ; CODE XREF from sym.function_0 @ 0x8000070 +| :| ; CODE XREF from sym.function_1 @ 0x800008c +| :| 0x080000e0 stx o0, [fp+0x7ef] +| :| 0x080000e4 ldx [fp+0x7ef], g1 +| :| 0x080000e8 ldub [g1], g1 +| :| 0x080000ec and g1, 0xff, g1 +| :| 0x080000f0 ld [fp+0x7fb], g2 +| :| 0x080000f4 add g2, g1, g1 +| :| 0x080000f8 st g1, [fp+0x7fb] +| :| 0x080000fc ld [fp+0x7f7], g1 +| :| 0x08000100 add g1, 1, g1 +| :| 0x08000104 st g1, [fp+0x7f7] +| :| ; CODE XREF from sym.main @ 0x80000bc +| :`-> 0x08000108 ld [fp+0x7f7], g1 +| : 0x0800010c cmp g1, 2 +| `==< 0x08000110 bleu icc, reloc..data +\ 0x08000114 nop 0x08000118 ld [fp+0x7fb], g1 0x0800011c sra g1, 0, g1 0x08000120 mov g1, i0 @@ -361,17 +362,17 @@ EXPECT=< CODE -> 0x8000058 sym.function_0 reloc..data.80000c4+24 0x80000dc -> CODE -> 0x8000074 sym.function_1 reloc..data.80000c4+80 0x8000114 -> CODE -> 0x80000c0 reloc..data - addr size traced ninstr jump fail fcns calls xrefs ------------------------------------------------------------------------------------- -0x8000040 24 0 0 -0x8000058 12 0 0 0x08000040 0x08000040 -0x8000064 16 0 0 -0x8000074 12 0 0 0x08000040 0x08000040 -0x8000080 16 0 0 -0x80000ac 20 0 0 -0x80000c0 32 0 0 0xffffffffffffffff 0xffffffffffffffff -0x80000e0 56 0 0 -0x8000108 16 0 0 + addr size traced ninstr jump fail fcns calls xrefs +------------------------------------------------------------------------------------------------------- +0x8000040 24 0 0 0x08000064 0x08000080 0x08000040 +0x8000058 12 0 0 0x08000064 0x08000058 0x08000040 0x08000040 +0x8000064 16 0 0 0x080000e0 0x08000058 +0x8000074 12 0 0 0x08000080 0x08000074 0x08000040 0x08000040 +0x8000080 16 0 0 0x080000e0 0x08000074 +0x80000ac 20 0 0 0x08000108 0x080000ac +0x80000c0 32 0 0 0x080000e0 0x08000074 0x080000ac 0xffffffffffffffff 0xffffffffffffffff +0x80000e0 56 0 0 0x080000c0 0x080000ac +0x8000108 16 0 0 0x080000c0 0x080000ac EOF EXPECT_ERR=< Date: Sat, 14 Feb 2026 17:07:16 +0100 Subject: [PATCH 199/334] Add ARM test --- test/db/inquiry/interpreter/xrefs | 154 ++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs index a35f2f3e55d..4ec4b9a2a0a 100644 --- a/test/db/inquiry/interpreter/xrefs +++ b/test/db/inquiry/interpreter/xrefs @@ -381,3 +381,157 @@ WARNING: Unhandled conversion of ELF reloc 9 (R_SPARC_HI22) WARNING: Unhandled conversion of ELF reloc 12 (R_SPARC_LO10) EOF RUN + +NAME=Indirect call arm +FILE=bins/inquiry/interpreter/prototype/arm_icall_malloc +CMDS=< 0x080000d4 ldr r2, obj.fcn_arr ; [reloc..data:4]=0x8000134 obj.fcn_arr ; "4\U00000001" +| :| 0x080000d8 ldr r3, [fp, -0xc] ; 12 +| :| 0x080000dc ldr r3, [r2, r3, lsl 2] ; 0x8000134 ; "T" +| :| 0x080000e0 mov lr, pc +| :| 0x080000e4 bx r3 ; RELOC 0 +| :| ; CODE XREF from sym.function_0 @ 0x8000070 +| :| ; CODE XREF from sym.function_1 @ 0x8000090 +| :| 0x080000e8 str r0, [fp, -0x10] ; 16 +| :| 0x080000ec ldr r3, [fp, -0x10] ; 16 +| :| 0x080000f0 ldrb r3, [r3] +| :| 0x080000f4 mov r2, r3 +| :| 0x080000f8 ldr r3, [fp, -8] ; 8 +| :| 0x080000fc add r3, r3, r2 +| :| 0x08000100 str r3, [fp, -8] ; 8 +| :| 0x08000104 ldr r3, [fp, -0xc] ; 12 +| :| 0x08000108 add r3, r3, 1 +| :| 0x0800010c str r3, [fp, -0xc] ; 12 +| :| ; CODE XREF from sym.main @ 0x80000d0 +| :`-> 0x08000110 ldr r3, [fp, -0xc] ; 12 +| : 0x08000114 cmp r3, 2 ; 2 +\ `==< 0x08000118 bls 0x80000d4 + 0x0800011c ldr r3, [fp, -8] ; 8 + 0x08000120 mov r0, r3 + 0x08000124 sub sp, fp, 4 + 0x08000128 pop {fp, lr} + 0x0800012c bx lr ; RELOC 0 + ; DATA XREF from sym.main @ 0x80000d4 + ;-- .data: + 0x08000130 stmdaeq r0, {r2, r4, r5, r8} ; RELOC 32 .data @ 0x08000134 + ; DATA XREF from sym.main @ 0x80000dc + ;-- section..data: + ;-- .data: + ;-- function_0: + ;-- fcn_arr: + 0x08000134 .dword 0x08000054 ; reloc.target.function_0 ; sym.function_0; RELOC 32 function_0 @ 0x08000054 ; [03] -rw- section size 12 named .data + ; DATA XREF from sym.main @ 0x80000dc + ;-- function_1: + 0x08000138 .dword 0x08000074 ; reloc.target.function_1 ; sym.function_1; RELOC 32 function_1 @ 0x08000074 + ;-- function_2: + 0x0800013c .dword 0x08000094 ; reloc.target.function_2 ; sym.function_2; RELOC 32 function_2 @ 0x08000094 + ;-- section..bss: + ;-- section..comment: + ;-- .bss: + ;-- z: + ;-- .comment: + 0x08000140 .dword 0xffffffff ; RELOC TARGET 32 .bss @ 0x08000140 ; [06] ---- section size 36 named .comment + 0x08000144 invalid + section..text+8 0x800003c -> DATA -> 0x8000050 reloc..bss + section..text+24 0x800004c -> CODE -> 0x8000060 reloc.target.function_0+12 + section..text+24 0x800004c -> CODE -> 0x8000080 reloc.target.function_1+12 + reloc.target.function_0+8 0x800005c -> CODE -> 0x8000034 section..text + reloc.target.function_0+28 0x8000070 -> CODE -> 0x80000e8 sym.main+52 + reloc.target.function_1+8 0x800007c -> CODE -> 0x8000034 section..text + reloc.target.function_1+28 0x8000090 -> CODE -> 0x80000e8 sym.main+52 + sym.main+28 0x80000d0 -> CODE -> 0x8000110 sym.main+92 + sym.main+32 0x80000d4 -> DATA -> 0x8000130 reloc..data + sym.main+40 0x80000dc -> DATA -> 0x8000134 section..data + sym.main+40 0x80000dc -> DATA -> 0x8000138 reloc.function_1 + sym.main+48 0x80000e4 -> CODE -> 0x8000054 reloc.target.function_0 + sym.main+48 0x80000e4 -> CODE -> 0x8000074 reloc.target.function_1 + sym.main+100 0x8000118 -> CODE -> 0x80000d4 sym.main+32 + addr size traced ninstr jump fail fcns calls xrefs +--------------------------------------------------------------------------------------- +0x8000034 28 0 0 0x08000060 0x08000080 0x08000034 +0x8000054 12 0 0 0x08000060 0x08000054 0x08000034 0x08000034 +0x8000060 20 0 0 0x080000e8 0x08000054 +0x8000074 12 0 0 0x08000080 0x08000074 0x08000034 0x08000034 +0x8000080 20 0 0 0x080000e8 0x08000074 +0x80000b4 32 0 0 0x08000110 0x080000b4 +0x80000d4 20 0 0 0x080000e8 0x08000074 0x080000b4 +0x80000e8 52 0 0 0x080000d4 0x080000b4 +0x8000110 12 0 0 0x080000d4 0x080000b4 +EOF +EXPECT_ERR=< Date: Sat, 14 Feb 2026 17:16:17 +0100 Subject: [PATCH 200/334] Add broken test for Mips. --- test/db/inquiry/interpreter/xrefs | 113 ++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs index 4ec4b9a2a0a..591ca91108f 100644 --- a/test/db/inquiry/interpreter/xrefs +++ b/test/db/inquiry/interpreter/xrefs @@ -535,3 +535,116 @@ WARNING: Unhandled conversion of ELF reloc 40 (R_ARM_V4BX) WARNING: Unhandled conversion of ELF reloc 40 (R_ARM_V4BX) EOF RUN + +NAME=Indirect call mips64 +FILE=bins/inquiry/interpreter/prototype/mips64_icall_malloc +CMDS=< 0x08000194 ld v0, 0(gp) ; RELOC 0 .data.rel.local @ 0x080002f0 +| :| 0x08000198 lwu v1, 4(fp) +| :| 0x0800019c dsll v1, v1, 3 +| :| ;-- .data.rel.local: +| :| 0x080001a0 daddiu v0, v0, 0 ; RELOC 0 .data.rel.local @ 0x080002f0 +| :| 0x080001a4 daddu v0, v1, v0 +| :| 0x080001a8 ld v0, 0(v0) +| :| 0x080001ac move t9, v0 +| :| 0x080001b0 jalr t9 + :| 0x080001b4 nop + :| 0x080001b8 sd v0, 8(fp) + :| 0x080001bc ld v0, 8(fp) + :| 0x080001c0 lbu v0, 0(v0) + :| 0x080001c4 lw v1, 0(fp) + :| 0x080001c8 addu v0, v1, v0 + :| 0x080001cc sw v0, 0(fp) + :| 0x080001d0 lw v0, 4(fp) + :| 0x080001d4 addiu v0, v0, 1 + :| 0x080001d8 sw v0, 4(fp) +| :| ; CODE XREF from fcn_0x8000178 @ 0x800018c +| :`-> 0x080001dc lw v0, 4(fp) +| : 0x080001e0 sltiu v0, v0, 3 +\ `==< 0x080001e4 bnez v0, reloc..data.rel.local + 0x080001e8 nop + 0x080001ec lw v0, 0(fp) + reloc.main.8000180+12 0x800018c -> CODE -> 0x80001dc reloc..data.rel.local.80001a0+60 + reloc..data.rel.local.80001a0+68 0x80001e4 -> CODE -> 0x8000194 reloc..data.rel.local + addr size traced ninstr jump fail fcns calls xrefs +--------------------------------------------------------------------- +0x8000178 24 0 0 0x080001dc 0x08000178 +0x8000194 32 0 0 0x08000178 +0x80001dc 12 0 0 0x08000194 0x08000178 +EOF +EXPECT_ERR=< Date: Sat, 14 Feb 2026 17:41:18 +0100 Subject: [PATCH 201/334] Add overview of basic function detection algo --- librz/include/rz_analysis.h | 2 +- .../inquiry/algorithms/revng_fcn_detection.c | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index fd23db8d50e..119d47432fd 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -978,7 +978,7 @@ typedef struct rz_analysis_ref_t { /** * \brief A struct combining information about an instruction storing the - * a next instruction pointer of a basic block. + * next instruction pointer of a basic block. * This indicates that the ending branch instruction in a basic block is * a call instruction. */ diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index e40742f0e0d..d7cc61584e8 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -6,6 +6,37 @@ * paper: "REV.NG: A Unified Binary Analysis Framework to Recover CFGs and Function Boundaries" * chapter: "4.2 Function boundaries recovery" * doi: 10.1145/3033019.3033028 + * + * The actualy algorithm is really simple. + * + * TERMS: + * + * Basic Block: A sequence of instructions ending with a branch of any kind. + * Call candidate: RzAnalysisCallCandidate + * Candidate function entry points (CFEP): Addresses of possible function entry point. + * Return Addresses: address after a call candidate BB, with an xref to it. + * Basic Block CFG: Control Flow Graph with basic blocks as nodes. + * + * IN: + * - Call candidates. + * - CFEPs + * - Return Addresses + * - Basic Block CFG (bb_cfg) + * + * ALGO: + * + * It simply iterates over all CFEPs. + * For each one it follows its edges in the bb_cfg. + * If an edge belongs to a call, it is NOT taken. + * Instead it continues with the basic block after the call, if it is a return address. + * + * Every walked edge and the basic blocks are added to the function. + * + * Tail calls are ignored. + * + * OUT: + * - List of functions + * - Each functions starts with one CFEP, and has a sub-graph in the bb_cfg. */ #include "rz_analysis.h" From 2de8e4e27aecd81f3f5746b193ffb8e4d01890b1 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 21 Feb 2026 20:25:27 +0100 Subject: [PATCH 202/334] Also add op.fail to branch targets --- librz/arch/xrefs.c | 5 +++- librz/inquiry/inquiry.c | 6 +++-- test/db/inquiry/interpreter/xrefs | 45 ++++++++++++++++++++++++------- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/librz/arch/xrefs.c b/librz/arch/xrefs.c index b48a1b8b818..6e4aa2d471e 100644 --- a/librz/arch/xrefs.c +++ b/librz/arch/xrefs.c @@ -337,7 +337,6 @@ static bool read_up_to(RzAnalysis *analysis, ut64 addr, ut8 *buf, size_t buf_siz * \param analysis The analysis plugin. * \param sections The RzBinSections to disassemble to find call/jmp instructions. * \param branch_targets The found call targets of all disassemble calls. - * NOTE: They can point outside of \p maps! * \param include_call_return_pts If true, it will add addresses after a call instruction as well. * * \return True in case of success, false otherwise. @@ -379,6 +378,10 @@ RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, rz_pvector_find(sections, &op.jump, (RzListComparator)addr_at_aligned_x_addr, NULL)) { RZ_LOG_DEBUG("Add call target 0x%" PFMT64x " -> 0x%" PFMT64x "\n", op.addr, op.jump); rz_set_u_add(branch_targets, op.jump); + if (op.fail != UT64_MAX) { + RZ_LOG_DEBUG("Add call target 0x%" PFMT64x " -> 0x%" PFMT64x "\n", op.addr, op.fail); + rz_set_u_add(branch_targets, op.fail); + } if (include_call_return_pts && rz_analysis_op_is_direct_call(&op)) { // If it is a call, also add the following instruction as reference. // Because it is likely a return point. diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 6bbba964b42..26ce33c5f70 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -639,14 +639,16 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent RZ_LOG_DEBUG("INQUIRY: Wait for join\n"); rz_th_wait(interpr_th); - return_code = rz_th_get_retv(interpr_th); + bool interpr_ret = rz_th_get_retv(interpr_th); rz_th_free(interpr_th); - if ((!return_code && !bb_decode_failed) || user_sent_signal) { + if ((!interpr_ret && !bb_decode_failed) || user_sent_signal) { if (!user_sent_signal) { RZ_LOG_ERROR("Interpreter failed with an error. Abort.\n"); } break; } + // Clear shared objects to not have any left overs in the next run. + memset((ut8 *)iset->state->shared_obj, 0, sizeof(RzInterpreterSharedObjects)); // Open queue again, so the interpretation can start at another // jump target again. rz_th_queue_open(io_request_q); diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs index 591ca91108f..33375914161 100644 --- a/test/db/inquiry/interpreter/xrefs +++ b/test/db/inquiry/interpreter/xrefs @@ -373,12 +373,16 @@ EXPECT=< 0x08000194 ld v0, 0(gp) ; RELOC 0 .data.rel.local @ 0x080002f0 | :| 0x08000198 lwu v1, 4(fp) @@ -578,19 +596,25 @@ EXPECT=< 0x080001dc lw v0, 4(fp) | : 0x080001e0 sltiu v0, v0, 3 \ `==< 0x080001e4 bnez v0, reloc..data.rel.local 0x080001e8 nop 0x080001ec lw v0, 0(fp) + 0x080001f0 move sp, fp + 0x080001f4 ld ra, 0x28(sp) + 0x080001f8 ld fp, 0x20(sp) + 0x080001fc ld gp, 0x18(sp) + 0x08000200 daddiu sp, sp, 0x30 reloc.main.8000180+12 0x800018c -> CODE -> 0x80001dc reloc..data.rel.local.80001a0+60 reloc..data.rel.local.80001a0+68 0x80001e4 -> CODE -> 0x8000194 reloc..data.rel.local addr size traced ninstr jump fail fcns calls xrefs --------------------------------------------------------------------- -0x8000178 24 0 0 0x080001dc 0x08000178 -0x8000194 32 0 0 0x08000178 -0x80001dc 12 0 0 0x08000194 0x08000178 +0x8000164 44 0 0 0x080001dc 0x08000164 +0x8000194 32 0 0 0x08000164 +0x80001dc 12 0 0 0x08000194 0x08000164 +0x80001e8 32 0 0 EOF EXPECT_ERR=< Date: Sat, 21 Feb 2026 21:25:44 +0100 Subject: [PATCH 203/334] clang-format --- librz/inquiry/inquiry.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 26ce33c5f70..66bcc64253f 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -112,8 +112,8 @@ RZ_API RZ_OWN char *rz_inquiry_function_str(const RzInquiryFunction *fcn) { rz_strbuf_append(buf, "ifcn: ["); ut64 *it; size_t i = 0; - rz_vector_foreach(fcn->entry_points, it) { - rz_strbuf_appendf(buf, "%s0x%" PFMT64x, (i++ > 0 ? ", " : " "), *it); + rz_vector_foreach (fcn->entry_points, it) { + rz_strbuf_appendf(buf, "%s0x%" PFMT64x, (i++ > 0 ? ", " : " "), *it); } rz_strbuf_append(buf, " ]\n"); RzIterator *iter = ht_up_as_iter(fcn->bb_cfg->basic_blocks); @@ -395,12 +395,13 @@ static const RzInterpreterILBB *get_il_bb(RzCore *core, HtUP *il_cache, ut64 add RzILValidateGlobalContext *ctx = rz_il_validate_global_context_new_from_vm(vm->vm); void **it; size_t i = 0; - rz_pvector_enumerate(bb->il_ops, it, i) { + rz_pvector_enumerate (bb->il_ops, it, i) { char *report = NULL; RzInterpreterInsnPkt *pkt = *it; if (!rz_il_validate_effect(pkt->effect, ctx, NULL, NULL, &report)) { RZ_LOG_ERROR("Validation failed for IL op %" PFMTSZu " in BB 0x%" PFMT64x " in insn packet:\n" - "\t'%s'\n", i, bb->bb_addr, report); + "\t'%s'\n", + i, bb->bb_addr, report); } free(report); } @@ -747,7 +748,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent } RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry, ut64 entry_point, - const RzPVector /**/ *symbols) { + const RzPVector /**/ *symbols) { RzPVector *fcns = rz_pvector_new((RzPVectorFree)rz_inquiry_function_free); rz_inquiry_algo_revng_fcn_detection( entry_point, @@ -763,7 +764,7 @@ RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inqui ut64 fcn_addr = *(ut64 *)rz_vector_head(fcn->entry_points); char new_fcn_name[64] = { 0 }; void **it; - rz_pvector_foreach(symbols, it) { + rz_pvector_foreach (symbols, it) { RzBinSymbol *s = *it; if (s->vaddr == fcn_addr && RZ_STR_EQ(s->type, RZ_BIN_TYPE_FUNC_STR)) { rz_strf(new_fcn_name, "sym.%s", s->name); From 1d02b893b2cba80c8e8afdca4b581354abfed2b4 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sun, 22 Feb 2026 21:12:25 +0100 Subject: [PATCH 204/334] Add statically known to the bb_cfg --- librz/arch/p/analysis/analysis_hexagon.c | 1 + librz/arch/xrefs.c | 13 ++++++++++--- librz/include/rz_analysis.h | 4 +++- librz/include/rz_inquiry.h | 2 +- librz/inquiry/bb_cfg.c | 17 ++++++++++++++++- librz/inquiry/inquiry.c | 11 +++++++---- 6 files changed, 38 insertions(+), 10 deletions(-) diff --git a/librz/arch/p/analysis/analysis_hexagon.c b/librz/arch/p/analysis/analysis_hexagon.c index 1f7e50527f2..fe8d9c17177 100644 --- a/librz/arch/p/analysis/analysis_hexagon.c +++ b/librz/arch/p/analysis/analysis_hexagon.c @@ -30,6 +30,7 @@ RZ_API int hexagon_v6_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, cons hexagon_reverse_opcode(&rev, addr, NULL, analysis); HexPkt *p = hex_get_pkt(rev.state, addr); if (p) { + op->hexagon_pkt_addr = p->pkt_addr; rev.pkt_fully_decoded = p->is_valid; if (mask & RZ_ANALYSIS_OP_MASK_INSN_PKT) { op->size = rz_list_length(p->bin) * HEX_INSN_SIZE; diff --git a/librz/arch/xrefs.c b/librz/arch/xrefs.c index 6e4aa2d471e..ff4232c2444 100644 --- a/librz/arch/xrefs.c +++ b/librz/arch/xrefs.c @@ -344,7 +344,8 @@ static bool read_up_to(RzAnalysis *analysis, ut64 addr, ut8 *buf, size_t buf_siz RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, const RzPVector /**/ *sections, bool include_call_return_pts, - RZ_NONNULL RZ_OUT RzSetU *branch_targets) { + RZ_NONNULL RZ_OUT RzSetU *branch_targets, + RZ_NONNULL RZ_OUT RzVector /**/ *insn_to_insn_edges) { rz_return_val_if_fail(analysis && analysis->cur && sections && branch_targets, false); size_t buf_size = (analysis->cur->bits / 8) * 16; ut8 *buf = RZ_NEWS0(ut8, buf_size); @@ -366,7 +367,7 @@ RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, rz_analysis_op_fini(&op); break; } - if (rz_analysis_op(analysis, &op, addr, buf, buf_size, RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_INSN_PKT) <= 0) { + if (rz_analysis_op(analysis, &op, addr, buf, buf_size, RZ_ANALYSIS_OP_MASK_BASIC) <= 0) { rz_analysis_op_fini(&op); addr += op.size; continue; @@ -378,9 +379,15 @@ RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, rz_pvector_find(sections, &op.jump, (RzListComparator)addr_at_aligned_x_addr, NULL)) { RZ_LOG_DEBUG("Add call target 0x%" PFMT64x " -> 0x%" PFMT64x "\n", op.addr, op.jump); rz_set_u_add(branch_targets, op.jump); + + RzAnalysisXRef edge = { .from = addr, .to = op.jump }; + rz_vector_push(insn_to_insn_edges, &edge); if (op.fail != UT64_MAX) { - RZ_LOG_DEBUG("Add call target 0x%" PFMT64x " -> 0x%" PFMT64x "\n", op.addr, op.fail); + RZ_LOG_DEBUG("Add branch target 0x%" PFMT64x " -> 0x%" PFMT64x "\n", op.addr, op.fail); rz_set_u_add(branch_targets, op.fail); + + RzAnalysisXRef edge = { .from = addr, .to = op.fail }; + rz_vector_push(insn_to_insn_edges, &edge); } if (include_call_return_pts && rz_analysis_op_is_direct_call(&op)) { // If it is a call, also add the following instruction as reference. diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 119d47432fd..a521d0822a7 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -889,6 +889,7 @@ typedef struct rz_analysis_op_t { RzAnalysisSwitchOp *switch_op; RzAnalysisHint hint; RzAnalysisDataType datatype; + ut64 hexagon_pkt_addr; } RzAnalysisOp; #define RZ_TYPE_COND_SINGLE(x) (!x->arg[1] || x->arg[0] == x->arg[1]) @@ -1637,7 +1638,8 @@ RZ_API bool rz_analysis_xref_del(RzAnalysis *analysis, ut64 from, ut64 to); RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, const RzPVector /**/ *sections, bool include_call_return_pts, - RZ_NONNULL RZ_OUT RzSetU *branch_targets); + RZ_NONNULL RZ_OUT RzSetU *branch_targets, + RZ_NONNULL RZ_OUT RzVector /**/ *edges); /* var.c */ RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_set_var( diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index e5d191e40c6..0917dedb159 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -67,7 +67,7 @@ RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_to(con RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref); -RZ_IPI bool rz_inquiry_complement_bb_cfg(RzInquiry *iq); +RZ_IPI bool rz_inquiry_complement_bb_cfg(RzInquiry *iq, RzVector /**/ *insn_to_insn_edges); RZ_API bool rz_inquiry_plugin_add(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NONNULL RzInquiryPlugin *plugin); RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NONNULL RzInquiryPlugin *plugin); diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index b37d44b7b89..2e96f4b29f1 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -133,7 +133,7 @@ RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_to(con return rz_graph_innodes(cfg->graph, n); } -RZ_IPI bool rz_inquiry_complement_bb_cfg(RzInquiry *iq) { +RZ_IPI bool rz_inquiry_complement_bb_cfg(RzInquiry *iq, RzVector /**/ *insn_to_insn_edges) { RzAnalysisXRef *xref; rz_vector_foreach (iq->xrefs, xref) { if (xref->type != RZ_ANALYSIS_XREF_TYPE_CODE) { @@ -150,6 +150,21 @@ RZ_IPI bool rz_inquiry_complement_bb_cfg(RzInquiry *iq) { } rz_iterator_free(bb_iter); } + + // Add the instruction to instruction edges. + RzAnalysisXRef *i2i_edge; + rz_vector_foreach(insn_to_insn_edges, i2i_edge) { + void **it; + RzIterator *bb_iter = ht_up_as_iter(iq->bb_cfg->basic_blocks); + rz_iterator_foreach(bb_iter, it) { + RzInterval *bb = *it; + if (!rz_itv_contain(*bb, i2i_edge->from)) { + continue; + } + rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, i2i_edge->to); + } + rz_iterator_free(bb_iter); + } return true; } diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 66bcc64253f..dc3f9464898 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -301,7 +301,7 @@ static bool setup_queues(RzCore *core, return false; } -static bool get_branch_targets(RzCore *core, RzSetU *branch_targets) { +static bool get_branch_targets(RzCore *core, RzSetU *branch_targets, RzVector /**/ *insn_to_insn_edges) { RzPVector /**/ *sections = rz_bin_object_get_sections(core->bin->cur->o); if (!sections) { return false; @@ -320,7 +320,7 @@ static bool get_branch_targets(RzCore *core, RzSetU *branch_targets) { rz_pvector_remove_at(sections, *j); } rz_vector_free(non_x_idx); - if (!rz_analysis_get_all_branch_targets(core->analysis, sections, false, branch_targets)) { + if (!rz_analysis_get_all_branch_targets(core->analysis, sections, false, branch_targets, insn_to_insn_edges)) { RZ_LOG_ERROR("Failed to get branch targets.\n"); return false; } @@ -453,11 +453,13 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent // of the pointers. il_cache = ht_up_new(NULL, (RzPVectorFree)rz_interpreter_il_bb_free); - if (!get_branch_targets(core, branch_targets)) { + RzVector /**/ *insn_to_insn_edges = rz_vector_new(sizeof(RzAnalysisXRef), NULL, NULL); + if (!get_branch_targets(core, branch_targets, insn_to_insn_edges)) { RZ_LOG_ERROR("Failed to get branch targets.\n"); return_code = false; goto error_free; } + if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { printf("Total branch targets in binary: %" PFMT32d "\n", rz_set_u_size(branch_targets)); } @@ -709,7 +711,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { printf("\n"); } - rz_inquiry_complement_bb_cfg(core->inquiry); + rz_inquiry_complement_bb_cfg(core->inquiry, insn_to_insn_edges); + rz_vector_free(insn_to_insn_edges); RZ_LOG_DEBUG("INQUIRY: Done\n"); From e512e4d6d6d3e0d734ffa1304c2c8723e4f4b318 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 23 Feb 2026 19:31:47 +0100 Subject: [PATCH 205/334] Some code clean up and separation --- librz/include/rz_inquiry.h | 4 +-- .../inquiry/algorithms/revng_fcn_detection.c | 2 +- librz/inquiry/bb_cfg.c | 15 ++++++--- librz/inquiry/inquiry.c | 31 ++++++++++++------- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 0917dedb159..edfd7003609 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -65,9 +65,9 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 t RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_from(const RzInquiryBBCFG *cfg, ut64 bb_addr); RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_to(const RzInquiryBBCFG *cfg, ut64 bb_addr); -RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref); +RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /**/ *insn_to_insn_edges); -RZ_IPI bool rz_inquiry_complement_bb_cfg(RzInquiry *iq, RzVector /**/ *insn_to_insn_edges); +RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref); RZ_API bool rz_inquiry_plugin_add(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NONNULL RzInquiryPlugin *plugin); RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NONNULL RzInquiryPlugin *plugin); diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index d7cc61584e8..5244e48db1e 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -11,7 +11,7 @@ * * TERMS: * - * Basic Block: A sequence of instructions ending with a branch of any kind. + * Basic Block: A sequence of instructions ending with exactly one known entry point and one branch at the end. * Call candidate: RzAnalysisCallCandidate * Candidate function entry points (CFEP): Addresses of possible function entry point. * Return Addresses: address after a call candidate BB, with an xref to it. diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index 2e96f4b29f1..3e4665f7b74 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -55,13 +55,16 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 siz return true; } +static /*const*/ RZ_BORROW RzGraphNode *get_node(RzInquiryBBCFG *cfg, ut64 bb_addr) { + return ht_up_find(cfg->bb_gnode_map, bb_addr, NULL); +} + /** * \brief Adds new node or returns existing one. */ static /*const*/ RZ_BORROW RzGraphNode *get_add_node_to_cfg(RzInquiryBBCFG *cfg, ut64 bb_addr) { - bool found = false; - RzGraphNode *n = ht_up_find(cfg->bb_gnode_map, bb_addr, &found); - if (found) { + RzGraphNode *n = get_node(cfg, bb_addr); + if (n) { return n; } n = rz_graph_add_node(cfg->graph, (void *)bb_addr); @@ -133,7 +136,11 @@ RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_to(con return rz_graph_innodes(cfg->graph, n); } -RZ_IPI bool rz_inquiry_complement_bb_cfg(RzInquiry *iq, RzVector /**/ *insn_to_insn_edges) { +/** + * \brief Add edges from iq->xrefs and the \p insn_to_insn_edges to the cfg. + */ +RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /**/ *insn_to_insn_edges) { + // Add all edges discovered by the interpreter RzAnalysisXRef *xref; rz_vector_foreach (iq->xrefs, xref) { if (xref->type != RZ_ANALYSIS_XREF_TYPE_CODE) { diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index dc3f9464898..99f36d1df02 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -413,7 +413,6 @@ static const RzInterpreterILBB *get_il_bb(RzCore *core, HtUP *il_cache, ut64 add rz_analysis_add_bb(core->analysis, bb->bb_addr, bb->size); RZ_LOG_DEBUG("INQUIRY: Send IL result: %p.\n", bb); ht_up_insert(il_cache, bb->bb_addr, bb); - rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, bb->bb_addr, bb->size); } else { RZ_LOG_DEBUG("INQUIRY: Serve BB from cache\n"); } @@ -593,6 +592,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent bb_decode_failed = true; break; } + rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, bb->bb_addr, bb->size); rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); // Add or not add? rz_th_queue_push(iset->il_queue, (void *)bb, true); @@ -711,7 +711,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { printf("\n"); } - rz_inquiry_complement_bb_cfg(core->inquiry, insn_to_insn_edges); + rz_inquiry_bb_cfg_complement(core->inquiry, insn_to_insn_edges); rz_vector_free(insn_to_insn_edges); RZ_LOG_DEBUG("INQUIRY: Done\n"); @@ -750,15 +750,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent return return_code; } -RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry, ut64 entry_point, +static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry, RzPVector *fcns, const RzPVector /**/ *symbols) { - RzPVector *fcns = rz_pvector_new((RzPVectorFree)rz_inquiry_function_free); - rz_inquiry_algo_revng_fcn_detection( - entry_point, - inquiry->call_candidates, - inquiry->bb_cfg, - fcns); - // Create analysis function and add their blocks to them. void **it; rz_pvector_foreach (fcns, it) { @@ -790,7 +783,7 @@ RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inqui RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, bb->addr); if (!abb && !(abb = rz_analysis_create_block(analysis, bb->addr, bb->size))) { rz_warn_if_reached(); - break; + return false; } const RzList *successors = rz_inquiry_bb_cfg_get_neighbours_from(inquiry->bb_cfg, bb->addr); RzGraphNode *n; @@ -815,6 +808,20 @@ RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inqui rz_iterator_free(iter); } rz_pvector_free(fcns); - return true; } + +RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry, ut64 entry_point, + const RzPVector /**/ *symbols) { + RzPVector *fcns = rz_pvector_new((RzPVectorFree)rz_inquiry_function_free); + if (!rz_inquiry_algo_revng_fcn_detection( + entry_point, + inquiry->call_candidates, + inquiry->bb_cfg, + fcns)) { + rz_warn_if_reached(); + return false; + } + + return convert_and_add_to_analysis(analysis, inquiry, fcns, symbols); +} From 21ecee30425ac174988c62fbbb4cbc2447e7ffe0 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 23 Feb 2026 22:29:20 +0100 Subject: [PATCH 206/334] Remove duplicate basic blocks and ensure each of them has exactly one entry point. --- librz/include/rz_inquiry.h | 2 + librz/inquiry/bb_cfg.c | 82 ++++++++++++++++++++++++++++++++++++++ librz/inquiry/inquiry.c | 28 +++++++++---- 3 files changed, 105 insertions(+), 7 deletions(-) diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index edfd7003609..54061000880 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -61,11 +61,13 @@ RZ_IPI RZ_OWN RzInquiryBBCFG *rz_inquiry_bb_cfg_new(); RZ_IPI void rz_inquiry_bb_cfg_free(RZ_NULLABLE RZ_OWN RzInquiryBBCFG *bb_cfg); RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size); RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RzInterval *bb); +RZ_IPI bool rz_inquiry_bb_cfg_del_out_edges(RzInquiryBBCFG *cfg, ut64 bb_addr); RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb); RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_from(const RzInquiryBBCFG *cfg, ut64 bb_addr); RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_to(const RzInquiryBBCFG *cfg, ut64 bb_addr); RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /**/ *insn_to_insn_edges); +RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg); RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref); diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index 3e4665f7b74..4b7fe2cff95 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2026 RizinOrg // SPDX-License-Identifier: LGPL-3.0-only +#include "rz_list.h" #include "rz_util/ht_up.h" #include "rz_util/ht_uu.h" #include "rz_util/rz_assert.h" @@ -76,6 +77,20 @@ static /*const*/ RZ_BORROW RzGraphNode *get_add_node_to_cfg(RzInquiryBBCFG *cfg, return n; } +RZ_IPI bool rz_inquiry_bb_cfg_del_out_edges(RzInquiryBBCFG *cfg, ut64 bb_addr) { + RzGraphNode *f = get_node(cfg, bb_addr); + rz_return_val_if_fail(f, false); + const RzList /**/ *neighs = rz_inquiry_bb_cfg_get_neighbours_from(cfg, bb_addr); + RzGraphNode *t; + RzListIter *it; + RzList *ptr_clones = rz_list_clone(neighs); + rz_list_foreach (ptr_clones, it, t) { + rz_graph_del_edge(cfg->graph, f, t); + } + rz_list_free(ptr_clones); + return true; +} + /** * \brief Adds an edge to the basic block CFG. * @@ -191,3 +206,70 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut } return true; } + +static int cmp(const ut64 *a, const ut64 *b, void *user) { + if (*a < *b) { + return -1; + } else if (*a > *b) { + return 1; + } + return 0; +} + +/** + * \brief Reduces all basic blocks in the cfg to their minimum size. + * Removing duplicates and overlapping basic blocks. + * This function makes each basic block just have a single entry point. + */ +RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg) { + // Index is end address of bb, values are starting address of bbs with that end address. + HtUP *overlapping_bbs = ht_up_new(NULL, (HtUPFreeValue)rz_vector_free); + + RzIterator *iter = ht_up_as_iter(cfg->basic_blocks); + void **it; + rz_iterator_foreach(iter, it) { + RzInterval *bb = *it; + ut64 end = bb->addr + bb->size; + RzVector *start_addresses = ht_up_find(overlapping_bbs, end, NULL); + if (!start_addresses) { + start_addresses = rz_vector_new(sizeof(ut64), NULL, NULL); + ht_up_insert(overlapping_bbs, end, start_addresses); + } + rz_vector_push(start_addresses, &bb->addr); + } + rz_iterator_free(iter); + + iter = ht_up_as_iter(overlapping_bbs); + rz_iterator_foreach(iter, it) { + RzVector *addrs = *it; + size_t i = rz_vector_len(addrs); + if (i == 1) { + continue; + } + rz_vector_sort(addrs, (RzVectorComparator)cmp, false, NULL); + for (i = i - 1; i > 0; i--) { + ut64 small_bb_addr = *((ut64 *)rz_vector_index_ptr(addrs, i)); + ut64 big_bb_addr = *((ut64 *)rz_vector_index_ptr(addrs, i - 1)); + rz_goto_if_fail(small_bb_addr > big_bb_addr, fail); + + // Change size of big bb + RzInterval *big_bb = ht_up_find(cfg->basic_blocks, big_bb_addr, NULL); + rz_goto_if_fail(big_bb, fail); + big_bb->size = small_bb_addr - big_bb_addr; + + // add edge between big to small bb, remove old edges. + rz_inquiry_bb_cfg_del_out_edges(cfg, big_bb_addr); + if (!rz_inquiry_bb_cfg_add_edge(cfg, big_bb_addr, small_bb_addr)) { + goto fail; + } + } + } + rz_iterator_free(iter); + ht_up_free(overlapping_bbs); + return true; + +fail: + ht_up_free(overlapping_bbs); + rz_warn_if_reached(); + return false; +} diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 99f36d1df02..fc92740da0e 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -410,7 +410,6 @@ static const RzInterpreterILBB *get_il_bb(RzCore *core, HtUP *il_cache, ut64 add // Otherwise YOLO #endif - rz_analysis_add_bb(core->analysis, bb->bb_addr, bb->size); RZ_LOG_DEBUG("INQUIRY: Send IL result: %p.\n", bb); ht_up_insert(il_cache, bb->bb_addr, bb); } else { @@ -677,13 +676,11 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent size_t x = 0; ut64 *ct; rz_iterator_foreach(ct_iter, ct) { - RzList *containing_blocks = rz_analysis_get_blocks_in(core->analysis, *ct); - if (rz_list_length(containing_blocks) != 0) { + if (ht_up_find(il_cache, *ct, NULL)) { + // This call target was interpreted before (hence is in the IL cache). rz_vector_push(covered_jump_targets, ct); - rz_list_free(containing_blocks); continue; } - rz_list_free(containing_blocks); x++; rz_vector_push(entry_points, ct); // Experiment how many new entry points we add. @@ -711,7 +708,14 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { printf("\n"); } - rz_inquiry_bb_cfg_complement(core->inquiry, insn_to_insn_edges); + if (!rz_inquiry_bb_cfg_complement(core->inquiry, insn_to_insn_edges)) { + rz_warn_if_reached(); + goto error_free; + } + if (!rz_inquiry_bb_cfg_reduce(core->inquiry->bb_cfg)) { + rz_warn_if_reached(); + goto error_free; + } rz_vector_free(insn_to_insn_edges); RZ_LOG_DEBUG("INQUIRY: Done\n"); @@ -752,7 +756,17 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry, RzPVector *fcns, const RzPVector /**/ *symbols) { - // Create analysis function and add their blocks to them. + // Add all discovered binary blocks to analysis + + RzIterator *iter = ht_up_as_iter(inquiry->bb_cfg->basic_blocks); + RzInterval **it_bb; + rz_iterator_foreach(iter, it_bb) { + RzInterval *bb = *it_bb; + rz_analysis_add_bb(analysis, bb->addr, bb->size); + } + rz_iterator_free(iter); + + // Convert the Inquiry functions to analysis function. void **it; rz_pvector_foreach (fcns, it) { RzInquiryFunction *fcn = *it; From 53649efd0b2d65336c0ff17f07eac2e6fdcc1132 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 23 Feb 2026 22:34:26 +0100 Subject: [PATCH 207/334] Demode super common logged errors --- librz/inquiry/inquiry.c | 2 +- librz/inquiry/inquiry_helpers.c | 1 - librz/inquiry/interpreter/interpreter.c | 3 ++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index fc92740da0e..bf2ef2a77f2 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -386,7 +386,7 @@ static const RzInterpreterILBB *get_il_bb(RzCore *core, HtUP *il_cache, ut64 add RZ_LOG_DEBUG("INQUIRY: Lift new BB\n"); bb = rz_inquiry_gen_il_bb(core->analysis, core->io, addr); if (!bb) { - RZ_LOG_ERROR("Failed to lift basic block at 0x%" PFMT64x "\n", addr); + RZ_LOG_DEBUG("Failed to lift basic block at 0x%" PFMT64x "\n", addr); return NULL; } diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c index be1c3be430d..edd5f28e3a0 100644 --- a/librz/inquiry/inquiry_helpers.c +++ b/librz/inquiry/inquiry_helpers.c @@ -45,7 +45,6 @@ RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana // Although this is expected here. rz_io_nread_at() on the other hand just // reads less bytes. if (rz_io_nread_at(io, addr, buf, max_read_size) == 0) { - RZ_LOG_WARN("inquiry: Failed to read memory for IL basic block generation at 0x%" PFMT64x " size: %u.\n", addr, max_read_size); goto fail; } if (rz_analysis_op(analysis, &op, addr, buf, max_read_size, RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_INSN_PKT) <= 0) { diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index e9d14e2ebe0..9c1e9842b9e 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -424,7 +424,8 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { in_hash = next.in_state_hash; #endif if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { - rz_warn_if_reached(); + // The il op lifting failed. Likely because the PC + // pointed to an unmapped region. goto in_loop_error; } if (!plugin->set_pc(in_state, next.addr, plugin_data)) { From 75aa4486e7477f470db7f5cb9ff2b0613aa04919 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 23 Feb 2026 22:56:04 +0100 Subject: [PATCH 208/334] Don't add call return points to branch target list. --- librz/arch/xrefs.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/librz/arch/xrefs.c b/librz/arch/xrefs.c index ff4232c2444..c3c6f1af52f 100644 --- a/librz/arch/xrefs.c +++ b/librz/arch/xrefs.c @@ -383,11 +383,16 @@ RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, RzAnalysisXRef edge = { .from = addr, .to = op.jump }; rz_vector_push(insn_to_insn_edges, &edge); if (op.fail != UT64_MAX) { - RZ_LOG_DEBUG("Add branch target 0x%" PFMT64x " -> 0x%" PFMT64x "\n", op.addr, op.fail); - rz_set_u_add(branch_targets, op.fail); - - RzAnalysisXRef edge = { .from = addr, .to = op.fail }; - rz_vector_push(insn_to_insn_edges, &edge); + if (rz_analysis_op_is_direct_jump(&op)) { + RZ_LOG_DEBUG("Add branch target 0x%" PFMT64x " -> 0x%" PFMT64x "\n", op.addr, op.fail); + rz_set_u_add(branch_targets, op.fail); + + RzAnalysisXRef edge = { .from = addr, .to = op.fail }; + rz_vector_push(insn_to_insn_edges, &edge); + } else if (include_call_return_pts && rz_analysis_op_is_direct_jump(&op)) { + rz_set_u_add(branch_targets, op.fail); + // Don't add to insn_to_ins_edges. Those are only real edges. + } } if (include_call_return_pts && rz_analysis_op_is_direct_call(&op)) { // If it is a call, also add the following instruction as reference. From ad69008682acfe24ed7f3f80ef64d346050053fe Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 23 Feb 2026 22:58:49 +0100 Subject: [PATCH 209/334] Fix tests with latest changes --- test/db/inquiry/interpreter/xrefs | 82 +++++++++++++------------------ 1 file changed, 35 insertions(+), 47 deletions(-) diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs index 33375914161..d506b518e6e 100644 --- a/test/db/inquiry/interpreter/xrefs +++ b/test/db/inquiry/interpreter/xrefs @@ -102,7 +102,7 @@ EXPECT=< CODE -> 0x8000071 reloc..data+4 addr size traced ninstr jump fail fcns calls xrefs --------------------------------------------------------------------------------------- -0x8000040 35 0 0 0x08000063 0x0800008d 0x08000040 +0x8000040 29 0 0 0x0800005d 0x08000040 0x800005d 6 0 0 0x08000063 0x0800008d 0x08000040 0x8000063 14 0 0 0x08000071 0x080000c0 0x08000040 0x8000071 28 0 0 0x0800005d 0x08000040 @@ -343,12 +343,12 @@ EXPECT=< 0x08000108 ld [fp+0x7f7], g1 | : 0x0800010c cmp g1, 2 | `==< 0x08000110 bleu icc, reloc..data -\ 0x08000114 nop - 0x08000118 ld [fp+0x7fb], g1 - 0x0800011c sra g1, 0, g1 - 0x08000120 mov g1, i0 - 0x08000124 rett i7+8 - 0x08000128 nop +| 0x08000114 nop +| 0x08000118 ld [fp+0x7fb], g1 +| 0x0800011c sra g1, 0, g1 +| 0x08000120 mov g1, i0 +| 0x08000124 rett i7+8 +\ 0x08000128 nop 0x0800012c invalid reloc..bss.8000048+12 0x8000054 -> CODE -> 0x8000064 sym.function_0+12 reloc..bss.8000048+12 0x8000054 -> CODE -> 0x8000080 sym.function_1+12 @@ -371,18 +371,15 @@ EXPECT=< 0x08000110 ldr r3, [fp, -0xc] ; 12 | : 0x08000114 cmp r3, 2 ; 2 -\ `==< 0x08000118 bls 0x80000d4 - 0x0800011c ldr r3, [fp, -8] ; 8 - 0x08000120 mov r0, r3 - 0x08000124 sub sp, fp, 4 - 0x08000128 pop {fp, lr} - 0x0800012c bx lr ; RELOC 0 +| `==< 0x08000118 bls 0x80000d4 +| 0x0800011c ldr r3, [fp, -8] ; 8 +| 0x08000120 mov r0, r3 +| 0x08000124 sub sp, fp, 4 +| 0x08000128 pop {fp, lr} +\ 0x0800012c bx lr ; RELOC 0 ; DATA XREF from sym.main @ 0x80000d4 ;-- .data: 0x08000130 stmdaeq r0, {r2, r4, r5, r8} ; RELOC 32 .data @ 0x08000134 @@ -525,12 +522,11 @@ EXPECT=< 0x080001dc lw v0, 4(fp) | : 0x080001e0 sltiu v0, v0, 3 -\ `==< 0x080001e4 bnez v0, reloc..data.rel.local - 0x080001e8 nop - 0x080001ec lw v0, 0(fp) - 0x080001f0 move sp, fp - 0x080001f4 ld ra, 0x28(sp) - 0x080001f8 ld fp, 0x20(sp) - 0x080001fc ld gp, 0x18(sp) - 0x08000200 daddiu sp, sp, 0x30 +| `==< 0x080001e4 bnez v0, reloc..data.rel.local +| 0x080001e8 nop +| 0x080001ec lw v0, 0(fp) +| 0x080001f0 move sp, fp +| 0x080001f4 ld ra, 0x28(sp) +| 0x080001f8 ld fp, 0x20(sp) +| 0x080001fc ld gp, 0x18(sp) +| 0x08000200 daddiu sp, sp, 0x30 +\ 0x08000204 jr ra reloc.main.8000180+12 0x800018c -> CODE -> 0x80001dc reloc..data.rel.local.80001a0+60 reloc..data.rel.local.80001a0+68 0x80001e4 -> CODE -> 0x8000194 reloc..data.rel.local - addr size traced ninstr jump fail fcns calls xrefs ---------------------------------------------------------------------- -0x8000164 44 0 0 0x080001dc 0x08000164 -0x8000194 32 0 0 0x08000164 -0x80001dc 12 0 0 0x08000194 0x08000164 -0x80001e8 32 0 0 + addr size traced ninstr jump fail fcns calls xrefs +--------------------------------------------------------------------------- +0x8000164 44 0 0 0x080001dc 0x08000164 +0x8000194 32 0 0 0x08000164 +0x80001dc 12 0 0 0x08000194 0x080001e8 0x08000164 +0x80001e8 32 0 0 0x08000164 EOF EXPECT_ERR=< Date: Mon, 23 Feb 2026 23:00:14 +0100 Subject: [PATCH 210/334] Update docs and logs --- librz/inquiry/inquiry.c | 7 +++---- librz/inquiry/interpreter/interpreter.c | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index bf2ef2a77f2..d4145925070 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -564,8 +564,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent // IL CACHE // ========= // - // This block mimics the IL cache. It uplifts basic blocks, - // caches them, logs them in RzAnalysis. + // This block mimics the IL cache. It uplifts basic blocks and + // caches them. { if (!rz_th_queue_is_empty(iset->branch_queue)) { RzInterpreterBranch *branch = NULL; @@ -593,7 +593,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent } rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, bb->bb_addr, bb->size); rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); - // Add or not add? rz_th_queue_push(iset->il_queue, (void *)bb, true); } } @@ -625,7 +624,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent // ============== // // This part plays the role of a yield consumer. - // In our prototype it only receives xrefs and stores them in RzAnalysis. + // In our prototype it only receives xrefs and call candidates. { if (!handle_yields(core, yield_queues)) { rz_atomic_bool_set(is_running, false); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 9c1e9842b9e..5b45dbc3735 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -374,7 +374,6 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { rz_th_queue_push(iset->branch_queue, shared_branch, true); const RzInterpreterILBB *il_bb = NULL; if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { - rz_warn_if_reached(); goto pre_loop_error; } From badbfeec88beda2ec8ab4ca691f8e7556d1b4310 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 25 Feb 2026 15:21:53 +0100 Subject: [PATCH 211/334] Speed up post processing of bb cfg --- librz/include/rz_analysis.h | 1 + librz/inquiry/bb_cfg.c | 19 +------------------ librz/inquiry/inquiry.c | 11 ++++++----- librz/inquiry/interpreter/prototype/eval.c | 1 + 4 files changed, 9 insertions(+), 23 deletions(-) diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index a521d0822a7..be8ba6e437b 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -972,6 +972,7 @@ typedef enum { } RzAnalysisXRefType; typedef struct rz_analysis_ref_t { + ut64 bb_addr; ///< Temporary member to make prototype function detection work. ut64 from; ut64 to; RzAnalysisXRefType type; diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index 4b7fe2cff95..2e0e182e485 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -153,26 +153,9 @@ RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_to(con /** * \brief Add edges from iq->xrefs and the \p insn_to_insn_edges to the cfg. + * TODO: Crazy inefficient. */ RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /**/ *insn_to_insn_edges) { - // Add all edges discovered by the interpreter - RzAnalysisXRef *xref; - rz_vector_foreach (iq->xrefs, xref) { - if (xref->type != RZ_ANALYSIS_XREF_TYPE_CODE) { - continue; - } - void **it; - RzIterator *bb_iter = ht_up_as_iter(iq->bb_cfg->basic_blocks); - rz_iterator_foreach(bb_iter, it) { - RzInterval *bb = *it; - if (!rz_itv_contain(*bb, xref->from)) { - continue; - } - rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, xref->to); - } - rz_iterator_free(bb_iter); - } - // Add the instruction to instruction edges. RzAnalysisXRef *i2i_edge; rz_vector_foreach(insn_to_insn_edges, i2i_edge) { diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index d4145925070..47c53da66ef 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -166,9 +166,10 @@ RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *iq) { } RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref) { - RzAnalysisXRef clone = { 0 }; - memcpy(&clone, xref, sizeof(RzAnalysisXRef)); - rz_vector_push(iq->xrefs, &clone); + rz_vector_push(iq->xrefs, (void *)xref); + if (xref->type == RZ_ANALYSIS_XREF_TYPE_CODE) { + rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, xref->bb_addr, xref->to); + } } RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzPVector /**/ *allowed_segments) { @@ -707,11 +708,11 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { printf("\n"); } - if (!rz_inquiry_bb_cfg_complement(core->inquiry, insn_to_insn_edges)) { + if (!rz_inquiry_bb_cfg_reduce(core->inquiry->bb_cfg)) { rz_warn_if_reached(); goto error_free; } - if (!rz_inquiry_bb_cfg_reduce(core->inquiry->bb_cfg)) { + if (!rz_inquiry_bb_cfg_complement(core->inquiry, insn_to_insn_edges)) { rz_warn_if_reached(); goto error_free; } diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 0798cb0134d..87b3942841a 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -42,6 +42,7 @@ bool report_yield_xref( ut64 to_addr = rz_bv_to_ut64(to->bv); if (queue->filter(&to_addr, queue->filter_data->io_boundaries)) { RzAnalysisXRef *xref = &state->shared_obj->xref; + xref->bb_addr = state->bb_addr; xref->from = from; xref->to = to_addr; xref->type = type; From b9869b2982c5e5c1b20d0eb338e796dca39eb4f9 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 25 Feb 2026 15:23:17 +0100 Subject: [PATCH 212/334] Fix random exits due to unset signal flag. --- librz/inquiry/inquiry.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 47c53da66ef..397c0f43095 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -439,6 +439,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent RzBuffer *io_buf = rz_buf_new_with_io(&core->analysis->iob); RzAnalysisILVM *analysis_vm = NULL; RzSetU *branch_targets = rz_set_u_new(); + bool user_sent_signal = false; rz_cons_push(); @@ -533,7 +534,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent } do { - bool user_sent_signal = false; bool bb_decode_failed = false; // Dispatch prototype interpreter into a thread. @@ -557,7 +557,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent while (rz_atomic_bool_get(is_running)) { if (rz_th_terminated(interpr_th) || rz_cons_is_breaked()) { rz_atomic_bool_set(is_running, false); - user_sent_signal = true; + user_sent_signal = rz_cons_is_breaked(); break; } @@ -646,6 +646,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent if ((!interpr_ret && !bb_decode_failed) || user_sent_signal) { if (!user_sent_signal) { RZ_LOG_ERROR("Interpreter failed with an error. Abort.\n"); + } else { + RZ_LOG_ERROR("User sent signal.\n"); } break; } @@ -751,7 +753,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent ht_up_free(il_cache); rz_cons_pop(); - return return_code; + return return_code && !user_sent_signal; } static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry, RzPVector *fcns, From 22e99e7bb29eb86127baeb9b4634e8c9e17d9e97 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 25 Feb 2026 15:54:57 +0100 Subject: [PATCH 213/334] Fix call candidate detection by resetting candidate info each bb --- librz/inquiry/interpreter/p/interpreter_prototype.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 63b477d3b8d..10a2f90f4f8 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -31,6 +31,9 @@ static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, ht_uu_update(pdata->bb_invocation_count, il_bb->bb_addr, ic_pc + 1); RZ_LOG_DEBUG("Eval BB (ic: %" PFMT64d ") = 0x%" PFMT64x "\n", ic_pc, il_bb->bb_addr); + // Reset call candidate tracking for each basic block. + memset(&pdata->call_cand, 0, sizeof(pdata->call_cand)); + void **it; rz_pvector_foreach (il_bb->il_ops, it) { ut64 pc = rz_bv_to_ut64(AD(state->pc->abstr_data)->bv); From 8265cb34a2c22d8a7d69e1e9f8e3ea8a957b65d2 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 25 Feb 2026 18:25:27 +0100 Subject: [PATCH 214/334] Fix wrong logic error --- librz/arch/xrefs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librz/arch/xrefs.c b/librz/arch/xrefs.c index c3c6f1af52f..afdaf027711 100644 --- a/librz/arch/xrefs.c +++ b/librz/arch/xrefs.c @@ -397,7 +397,7 @@ RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, if (include_call_return_pts && rz_analysis_op_is_direct_call(&op)) { // If it is a call, also add the following instruction as reference. // Because it is likely a return point. - rz_set_u_add(branch_targets, op.addr && op.size); + rz_set_u_add(branch_targets, op.addr + op.size); } } addr += op.size; From 572806554c4747a25d96b9d103671323dfb5f131 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 25 Feb 2026 18:25:53 +0100 Subject: [PATCH 215/334] Speed up the whole bb_cfg complementation somewhat. --- librz/inquiry/bb_cfg.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index 2e0e182e485..6aa6b24413b 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -152,13 +152,44 @@ RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_to(con } /** - * \brief Add edges from iq->xrefs and the \p insn_to_insn_edges to the cfg. + * \brief Add edges from \p insn_to_insn_edges to the cfg. + * These are edges statically known by checking RzAnalysisOp->jump and fail. + * * TODO: Crazy inefficient. + * But for now it is left in here. The problem is that the graph has basic blocks as nodes. + * But the xrefs are instruction to instruction. So we have this super expansive |bb| * |E| lookup. + * + * It would be way faster if we have an R-Tree to get bbs by an address it covers. + * Or just do a better design all along. */ RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /**/ *insn_to_insn_edges) { // Add the instruction to instruction edges. RzAnalysisXRef *i2i_edge; rz_vector_foreach(insn_to_insn_edges, i2i_edge) { + + // First check if the edge is already in the CFG. + // If so (not unlikely), skip the step where it iterates over all BBs. + const RzList *incoming = rz_inquiry_bb_cfg_get_neighbours_to(iq->bb_cfg, i2i_edge->to); + if (!incoming || rz_list_length(incoming) == 0) { + // Basic block not present. Ignore. + continue; + } + + RzGraphNode *gn; + RzListIter *lit; + rz_list_foreach(incoming, lit, gn) { + RzInterval *bb = ht_up_find(iq->bb_cfg->basic_blocks, (ut64)gn->data, NULL); + if (!bb) { + continue; + } + if (rz_itv_contain(*bb, i2i_edge->from)) { + // This edge was already covered. + goto next_i2i_edge; + } + } + + // Edge isn't in the CFG yet. + // Now we have to do the crazy expansive |bb| * |E| search. void **it; RzIterator *bb_iter = ht_up_as_iter(iq->bb_cfg->basic_blocks); rz_iterator_foreach(bb_iter, it) { @@ -169,6 +200,8 @@ RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /*bb_cfg, bb->addr, i2i_edge->to); } rz_iterator_free(bb_iter); +next_i2i_edge: + continue; } return true; } From c1100beb3972ace576c1a27c8603e3e510566c81 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 25 Feb 2026 19:02:18 +0100 Subject: [PATCH 216/334] Add now also the addresses after calls to the branch target list. --- librz/arch/xrefs.c | 13 ++++----- librz/include/rz_analysis.h | 1 + librz/inquiry/inquiry.c | 2 +- test/db/inquiry/interpreter/xrefs | 44 ++++++++++++++++++++----------- 4 files changed, 35 insertions(+), 25 deletions(-) diff --git a/librz/arch/xrefs.c b/librz/arch/xrefs.c index afdaf027711..2b842aec6a3 100644 --- a/librz/arch/xrefs.c +++ b/librz/arch/xrefs.c @@ -389,16 +389,13 @@ RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, RzAnalysisXRef edge = { .from = addr, .to = op.fail }; rz_vector_push(insn_to_insn_edges, &edge); - } else if (include_call_return_pts && rz_analysis_op_is_direct_jump(&op)) { - rz_set_u_add(branch_targets, op.fail); - // Don't add to insn_to_ins_edges. Those are only real edges. } } - if (include_call_return_pts && rz_analysis_op_is_direct_call(&op)) { - // If it is a call, also add the following instruction as reference. - // Because it is likely a return point. - rz_set_u_add(branch_targets, op.addr + op.size); - } + } + if (include_call_return_pts && rz_analysis_op_is_call(&op)) { + // If it is a call, also add the following instruction as reference. + // Because it is likely a return point. + rz_set_u_add(branch_targets, op.addr + op.size); } addr += op.size; rz_analysis_op_fini(&op); diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index be8ba6e437b..2960bea02f1 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -1528,6 +1528,7 @@ RZ_API bool rz_analysis_op_is_eob(const RzAnalysisOp *op); RZ_API bool rz_analysis_op_is_call(RZ_NONNULL const RzAnalysisOp *op); RZ_API bool rz_analysis_op_changes_control_flow(RZ_NONNULL const RzAnalysisOp *op); RZ_API bool rz_analysis_op_is_direct_call(RZ_NONNULL const RzAnalysisOp *op); +RZ_API bool rz_analysis_op_is_call(RZ_NONNULL const RzAnalysisOp *op); RZ_API bool rz_analysis_op_is_direct_jump(RZ_NONNULL const RzAnalysisOp *op); RZ_API RzList /**/ *rz_analysis_op_list_new(void); RZ_API int rz_analysis_op(RZ_NONNULL RzAnalysis *analysis, RZ_OUT RzAnalysisOp *op, ut64 addr, const ut8 *data, ut64 len, RzAnalysisOpMask mask); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 397c0f43095..d87d98ad9a9 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -321,7 +321,7 @@ static bool get_branch_targets(RzCore *core, RzSetU *branch_targets, RzVector /* rz_pvector_remove_at(sections, *j); } rz_vector_free(non_x_idx); - if (!rz_analysis_get_all_branch_targets(core->analysis, sections, false, branch_targets, insn_to_insn_edges)) { + if (!rz_analysis_get_all_branch_targets(core->analysis, sections, true, branch_targets, insn_to_insn_edges)) { RZ_LOG_ERROR("Failed to get branch targets.\n"); return false; } diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs index d506b518e6e..0d1a23c77f5 100644 --- a/test/db/inquiry/interpreter/xrefs +++ b/test/db/inquiry/interpreter/xrefs @@ -254,14 +254,6 @@ RUN NAME=Indirect call sparc FILE=bins/inquiry/interpreter/prototype/sparc_icall_malloc CMDS=< CODE -> 0x8000064 sym.function_0+12 reloc..bss.8000048+12 0x8000054 -> CODE -> 0x8000080 sym.function_1+12 + reloc..bss.8000048+12 0x8000054 -> CODE -> 0x800009c sym.function_2+12 sym.function_0+8 0x8000060 -> CODE -> 0x8000040 section..text sym.function_0+24 0x8000070 -> CODE -> 0x80000e0 reloc..data.80000c4+28 sym.function_1+8 0x800007c -> CODE -> 0x8000040 section..text sym.function_1+24 0x800008c -> CODE -> 0x80000e0 reloc..data.80000c4+28 + sym.function_2+8 0x8000098 -> CODE -> 0x8000040 section..text + sym.function_2+24 0x80000a8 -> CODE -> 0x80000e0 reloc..data.80000c4+28 sym.main+16 0x80000bc -> CODE -> 0x8000108 reloc..data.80000c4+68 reloc..data.80000c4+16 0x80000d4 -> DATA -> 0x8000130 section..data reloc..data.80000c4+16 0x80000d4 -> DATA -> 0x8000138 reloc..text.8000138 + reloc..data.80000c4+16 0x80000d4 -> DATA -> 0x8000140 reloc..text.8000140 reloc..data.80000c4+24 0x80000dc -> CODE -> 0x8000058 sym.function_0 reloc..data.80000c4+24 0x80000dc -> CODE -> 0x8000074 sym.function_1 + reloc..data.80000c4+24 0x80000dc -> CODE -> 0x8000090 sym.function_2 reloc..data.80000c4+80 0x8000114 -> CODE -> 0x80000c0 reloc..data addr size traced ninstr jump fail fcns calls xrefs ------------------------------------------------------------------------------------------------------- 0x8000040 24 0 0 0x08000064 0x08000080 0x08000040 0x8000058 12 0 0 0x08000064 0x08000058 0x08000040 0x08000040 +0x8000060 4 0 0 0x8000064 16 0 0 0x080000e0 0x08000058 0x8000074 12 0 0 0x08000080 0x08000074 0x08000040 0x08000040 +0x800007c 4 0 0 0x8000080 16 0 0 0x080000e0 0x08000074 +0x8000090 12 0 0 0x0800009c 0x08000090 0x08000040 0x08000040 +0x8000098 4 0 0 +0x800009c 16 0 0 0x080000e0 0x08000090 0x80000ac 20 0 0 0x08000108 0x080000ac 0x80000c0 32 0 0 0x080000e0 0x08000074 0x080000ac 0xffffffffffffffff 0xffffffffffffffff +0x80000dc 4 0 0 0x80000e0 40 0 0 0x08000108 0x080000ac 0x8000108 16 0 0 0x080000c0 0x08000118 0x080000ac 0x8000118 20 0 0 0x080000ac @@ -522,6 +529,7 @@ EXPECT=< CODE -> 0x8000194 reloc..data.rel.local addr size traced ninstr jump fail fcns calls xrefs --------------------------------------------------------------------------- +0x80000a4 28 0 0 +0x80000f4 28 0 0 +0x8000144 28 0 0 0x8000164 44 0 0 0x080001dc 0x08000164 0x8000194 32 0 0 0x08000164 +0x80001b4 40 0 0 0x80001dc 12 0 0 0x08000194 0x080001e8 0x08000164 0x80001e8 32 0 0 0x08000164 EOF From 2712010baa7ca52b465dd8bfde1ae8b7b438ae89 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 25 Feb 2026 19:49:10 +0100 Subject: [PATCH 217/334] Add non-exectuable maps to ignore list. --- librz/inquiry/inquiry.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index d87d98ad9a9..143060f2b5a 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -14,6 +14,7 @@ #include "rz_il/rz_il_vm.h" #include "rz_inquiry/rz_interpreter.h" #include "rz_inquiry_plugins.h" +#include "rz_io.h" #include "rz_th.h" #include "rz_types.h" #include "rz_util/ht_pp.h" @@ -331,17 +332,27 @@ static bool get_branch_targets(RzCore *core, RzSetU *branch_targets, RzVector /* static RzVector /**/ *get_ignored_code_regions( const RzPVector /**/ *symbols, - RzPVector /**/ *sections) { + RzPVector /**/ *sections, + const RzPVector /**/ *io_maps) { void **it; RzVector *v = rz_vector_new(sizeof(RzInterval), NULL, NULL); rz_pvector_foreach (sections, it) { RzBinSection *sec = *it; - if (sec->layout.role == RZ_BIN_SECTION_ROLE_LINKING) { + if (sec->layout.role == RZ_BIN_SECTION_ROLE_LINKING || !(sec->perm & RZ_PERM_X)) { RzInterval itv = { .addr = sec->vaddr, .size = sec->vsize }; rz_vector_push(v, &itv); } } rz_pvector_free(sections); + + rz_pvector_foreach (io_maps, it) { + RzIOMap *map = *it; + if (!(map->perm & RZ_PERM_X)) { + RzInterval itv = { .addr = map->itv.addr, .size = map->itv.size }; + rz_vector_push(v, &itv); + } + } + rz_pvector_foreach (symbols, it) { RzBinSymbol *sym = *it; if (sym->is_imported) { @@ -526,7 +537,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent rz_vector_clone(entry_points), get_ignored_code_regions( rz_bin_object_get_symbols(core->bin->cur->o), - rz_bin_object_get_sections(core->bin->cur->o))); + rz_bin_object_get_sections(core->bin->cur->o), + rz_io_maps(core->io))); if (!iset) { return_code = false; rz_warn_if_reached(); From b00ebb95392824168ed0cc4a8121bded6aed916b Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 25 Feb 2026 21:17:26 +0100 Subject: [PATCH 218/334] Fix jumps to unmapped symbols --- librz/include/rz_inquiry/rz_interpreter.h | 11 ++++++++++- librz/inquiry/algorithms/revng_fcn_detection.c | 7 +++---- librz/inquiry/inquiry.c | 18 ++++++++++++++++-- librz/inquiry/interpreter/interpreter.c | 12 +++++++----- .../interpreter/p/interpreter_prototype.c | 6 +++++- 5 files changed, 41 insertions(+), 13 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 86726d03b49..73592ddfc74 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -57,6 +57,15 @@ typedef struct { typedef struct { ut64 branching_bb_addr; ///< The address of the bb which branches. ut64 target_addr; ///< The target address it branches to. + /** + * \brief Set after a attempted jump to an ignored code region. + * This is almost always a call to an imported symbol. + * Instead of using the target_addr above, the il_cache should serve the alt_target_addr. + * target_addr should still be marked as potential cfep. + * + * 0 is an invalid address here. + */ + ut64 alt_target; } RzInterpreterBranch; /** @@ -130,7 +139,7 @@ typedef struct { RzInterpreterYieldKind kind; RzInterpreterYieldFilter filter; RzInterpreterYieldFilterData *filter_data; - RzThreadQueue /**/ *yield_queue; + RzThreadQueue *yield_queue; } RzInterpreterYieldQueue; typedef struct { diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index 5244e48db1e..64e89393d1a 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -118,12 +118,11 @@ static void recurse_into_fcn_bbs( // The successor is another function. // If address after the branch is a return point we choose it as // successor. - // If it isn't, then the call at this basic block is likely a tail call. + // If it isn't a return point, then the call at this basic block is likely a tail call. + // But tail calls are ignored. So in either case we just choose the npc as successor. const RzAnalysisCallCandidate *cc = ht_up_find((HtUP *)call_candidates, this_bb_addr, NULL); - if (cc && rz_set_u_contains((RzSetU *)return_addresses, cc->npc)) { + if (cc) { succ_addr = cc->npc; - } else { - continue; } } diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 143060f2b5a..bd2acf2e3f9 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -403,6 +403,7 @@ static const RzInterpreterILBB *get_il_bb(RzCore *core, HtUP *il_cache, ut64 add } #if RZ_BUILD_DEBUG + // Validate IL to catch more errors during testing. RzAnalysisILVM *vm = rz_analysis_il_vm_new(core->analysis, NULL); RzILValidateGlobalContext *ctx = rz_il_validate_global_context_new_from_vm(vm->vm); void **it; @@ -586,8 +587,12 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent rz_warn_if_reached(); break; } - RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x "\n", branch->target_addr); - const RzInterpreterILBB *bb = get_il_bb(core, il_cache, branch->target_addr); + ut64 alt_addr = branch->alt_target; + if (alt_addr) { + branch->alt_target = 0; + } + RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x " (alt: 0x%" PFMT64x ")\n", branch->target_addr, alt_addr); + const RzInterpreterILBB *bb = get_il_bb(core, il_cache, alt_addr ? alt_addr : branch->target_addr); if (!bb) { // Delete the address from the branch targets. // This is currently necessary as a work around, because if the interpreter @@ -595,6 +600,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent // Giving an endless loop. // One of the design thingies to fix in the proper implementation. rz_set_u_delete(branch_targets, branch->target_addr); + if (alt_addr) { + rz_set_u_delete(branch_targets, alt_addr); + } // Signal interpreter the lifting failed. rz_atomic_bool_set(is_running, false); rz_th_queue_close(io_request_q); @@ -605,6 +613,12 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent break; } rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, bb->bb_addr, bb->size); + if (alt_addr) { + // Add a dummy basic block at the address the call originally jumped to. + // This is the basic block for the imported function. + rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, branch->target_addr, 1); + rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); + } rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); rz_th_queue_push(iset->il_queue, (void *)bb, true); } diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 5b45dbc3735..37fbdbfa6ef 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -232,7 +232,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( return set; } -static bool jumps_ignored_code(const RzVector *v, ut64 jump_target) { +static bool jumps_to_ignored_code(const RzVector *v, ut64 jump_target) { void *it; rz_vector_foreach (v, it) { RzInterval *itv = it; @@ -290,15 +290,17 @@ static bool choose_next_pc(RzInterpreterSet *iset, return false; } shared_branch->branching_bb_addr = il_bb->bb_addr; - if (jumps_ignored_code(iset->ignored_code, shared_branch->target_addr)) { + if (jumps_to_ignored_code(iset->ignored_code, shared_branch->target_addr)) { RZ_LOG_DEBUG("interpreter: tried to jump to ignored code region at 0x%" PFMT64x "\n", shared_branch->target_addr); // Ignored code is mostly dynamically linked functions. // Skip to the next following address after the jump. - shared_branch->target_addr = il_bb->bb_addr + il_bb->size; - RZ_LOG_DEBUG("interpreter: Request instead 0x%" PFMT64x "\n", shared_branch->target_addr); + shared_branch->alt_target = il_bb->bb_addr + il_bb->size; } - SuccessorState ss = { .addr = shared_branch->target_addr, .in_state_hash = out_hash }; + SuccessorState ss = { + .addr = shared_branch->alt_target ? shared_branch->alt_target : shared_branch->target_addr, + .in_state_hash = out_hash + }; // The successors are pushed in the same order into the succ_states // vector, as they are requested over the addr_queue. rz_vector_push(succ_states, &ss); diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 10a2f90f4f8..4a5c1a6cc60 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -18,6 +18,9 @@ static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_result, void *plugin_data) { ProtoIntrprPluginData *pdata = plugin_data; + + // Check invocation count of the current address. + // Never execute the same address more than MAX_INVOCATIONS_PER_BB times. bool found = false; ut64 ic_pc = ht_uu_find(pdata->bb_invocation_count, il_bb->bb_addr, &found); if (!found) { @@ -34,6 +37,7 @@ static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, // Reset call candidate tracking for each basic block. memset(&pdata->call_cand, 0, sizeof(pdata->call_cand)); + // Now execute the actual effects of the BB. void **it; rz_pvector_foreach (il_bb->il_ops, it) { ut64 pc = rz_bv_to_ut64(AD(state->pc->abstr_data)->bv); @@ -49,7 +53,7 @@ static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, } // TODO: Clean up local variables. // Or maybe not? Just costs performance. And the uplifted instructions should - // always set it before, otherwise the tests don't pass. + // always set it before reading, otherwise the tests wouldn't pass. return true; } From 1795ff61a4ba5d8239ffa8792984f63592231ae4 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 25 Feb 2026 21:19:11 +0100 Subject: [PATCH 219/334] Add test case to cover latest function detection fixes. --- test/db/inquiry/interpreter/unmapped_fcn_loop | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 test/db/inquiry/interpreter/unmapped_fcn_loop diff --git a/test/db/inquiry/interpreter/unmapped_fcn_loop b/test/db/inquiry/interpreter/unmapped_fcn_loop new file mode 100644 index 00000000000..b16572b1b13 --- /dev/null +++ b/test/db/inquiry/interpreter/unmapped_fcn_loop @@ -0,0 +1,146 @@ +NAME=Indirect call x86 +FILE=bins/inquiry/interpreter/prototype/x86_unmapped_fcn_in_loop +CMDS=< CODE -> 0x8000050 reloc.target.run + reloc.target.run+26 0x800006a -> CODE -> 0x800009f reloc.recurse+27 + reloc.target.run+34 0x8000072 -> CODE -> 0x80000b0 sym.function_0 + reloc.target.run+34 0x8000072 -> CODE -> 0x80000c0 sym.function_1 + reloc.target.run+34 0x8000072 -> DATA -> 0x80000d0 section..data + reloc.target.run+34 0x8000072 -> DATA -> 0x80000d8 reloc..text.80000d8 + reloc.fcn_arr+12 0x8000081 -> CODE -> 0x8000088 reloc.recurse+4 + reloc.fcn_arr+14 0x8000083 -> CODE -> 0x8000040 section..text + reloc.recurse+25 0x800009d -> CODE -> 0x8000066 reloc.target.run+22 + addr size traced ninstr jump fail fcns calls xrefs +--------------------------------------------------------------------------------------- +0x8000040 9 0 0 0x08000049 0x08000040 0x08000050 0x08000050 +0x8000049 2 0 0 0x08000040 +0x8000050 22 0 0 0x08000066 0x08000050 +0x8000066 6 0 0 0x0800006c 0x0800009f 0x08000050 +0x800006c 13 0 0 0x08000079 0x080000c0 0x08000050 +0x8000079 10 0 0 0x08000088 0x08000083 0x08000050 +0x8000083 5 0 0 0x08000088 0x08000050 0x08000040 0x08000040 +0x8000088 23 0 0 0x08000066 0x08000050 +0x800009f 9 0 0 0x08000050 +0x80000b0 14 0 0 0x080000be 0x080000b0 0x08000440 0x08000440 +0x80000be 2 0 0 0x080000b0 +0x80000c0 14 0 0 0x080000ce 0x080000c0 0x08000440 0x08000440 +0x80000ce 2 0 0 0x080000c0 +0x8000440 1 0 0 0x08000440 +EOF +EXPECT_ERR= +RUN From 8e1efd8d3ef246d92611fb7b9bfd3896edf0afc7 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 25 Feb 2026 21:45:02 +0100 Subject: [PATCH 220/334] Fix user send siganls --- librz/inquiry/inquiry.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index bd2acf2e3f9..a3b8c0612d1 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -740,9 +740,12 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent rz_warn_if_reached(); goto error_free; } - if (!rz_inquiry_bb_cfg_complement(core->inquiry, insn_to_insn_edges)) { - rz_warn_if_reached(); - goto error_free; + if (!user_sent_signal) { + printf("Complement BB CFG with statically known xrefs...\n"); + if (!rz_inquiry_bb_cfg_complement(core->inquiry, insn_to_insn_edges)) { + rz_warn_if_reached(); + goto error_free; + } } rz_vector_free(insn_to_insn_edges); From b0ffe42eec433a8dfbd19f5a3af6ac81db665840 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 25 Feb 2026 21:47:55 +0100 Subject: [PATCH 221/334] Better logging. --- librz/core/cmd/cmd_inquiry.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index cec45470abb..af7fc69966a 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -31,13 +31,14 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar } entry_point = *(ut64 *)rz_vector_head(entry_points); bool success = rz_inquiry_interpreter(core, entry_points); - RZ_LOG_INFO("Finished reference recovery: %s\n", success ? "OK" : "FAIL"); + printf("Finished reference recovery: %s\n", success ? "OK" : "FAIL"); if (!success) { return RZ_CMD_STATUS_ERROR; } + printf("Perform function deduction: "); const RzPVector *symbols = rz_bin_object_get_symbols(core->bin->cur->o); success = rz_inquiry_function_deduction(core->analysis, core->inquiry, entry_point, symbols); - RZ_LOG_INFO("Perform function deduction: %s\n", success ? "OK" : "FAIL"); + printf("%s\n", success ? "OK" : "FAIL"); return success ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; } From 834f3618028fffab78e99c4ed8cba7fe4e2f9064 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 7 Mar 2026 18:29:06 +0100 Subject: [PATCH 222/334] Add all function symbol addresses to the branch targets. --- librz/core/cmd/cmd_inquiry.c | 15 +++-- librz/include/rz_inquiry.h | 6 +- .../inquiry/algorithms/revng_fcn_detection.c | 8 ++- librz/inquiry/inquiry.c | 62 ++++++++++++++++--- 4 files changed, 72 insertions(+), 19 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index af7fc69966a..ef9cdb27834 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -29,16 +29,21 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar rz_vector_push(entry_points, &entry_point); } } - entry_point = *(ut64 *)rz_vector_head(entry_points); bool success = rz_inquiry_interpreter(core, entry_points); - printf("Finished reference recovery: %s\n", success ? "OK" : "FAIL"); + eprintf("Finished reference recovery: %s\n", success ? "OK" : "FAIL"); if (!success) { return RZ_CMD_STATUS_ERROR; } - printf("Perform function deduction: "); + eprintf("Perform function deduction: "); + + RzSetU *symbol_addresses = rz_set_u_new(); + if (!rz_inquiry_get_fcn_symbol_addr(core, symbol_addresses)) { + rz_warn_if_reached(); + return RZ_CMD_STATUS_ERROR; + } const RzPVector *symbols = rz_bin_object_get_symbols(core->bin->cur->o); - success = rz_inquiry_function_deduction(core->analysis, core->inquiry, entry_point, symbols); - printf("%s\n", success ? "OK" : "FAIL"); + success = rz_inquiry_function_deduction(core->analysis, core->inquiry, symbol_addresses, symbols); + eprintf("%s\n", success ? "OK" : "FAIL"); return success ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; } diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 54061000880..73fd24b69f0 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -85,7 +85,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry, - ut64 entry_point, + RzSetU *symbol_addresses, const RzPVector /**/ *symbols); //============ @@ -103,10 +103,12 @@ typedef struct { RZ_IPI RZ_OWN RzInquiryFunction *rz_inquiry_function_new(); RZ_API RZ_OWN char *rz_inquiry_function_str(const RzInquiryFunction *fcn); +RZ_API bool rz_inquiry_get_fcn_symbol_addr(RzCore *core, RZ_OUT RzSetU *symbol_targets); + RZ_API void rz_inquiry_function_free(RZ_NULLABLE RZ_OWN RzInquiryFunction *fcn); RZ_API bool rz_inquiry_algo_revng_fcn_detection( - ut64 entry_point, + RzSetU *symbol_addresses, const HtUP /**/ *call_candidates, const RzInquiryBBCFG *bb_cfg, RZ_OUT RzPVector /**/ *fcns); diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index 64e89393d1a..f0a9251e333 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -174,7 +174,7 @@ static void fill_cfep_and_ret_addresses( } RZ_API bool rz_inquiry_algo_revng_fcn_detection( - ut64 entry_point, + RzSetU *symbol_addresses, RZ_NONNULL const HtUP /**/ *call_candidates, RZ_NONNULL const RzInquiryBBCFG *binary_bb_cfg, RZ_NONNULL RZ_OUT RzPVector /**/ *fcns) { @@ -183,7 +183,11 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( // Candidate function entry points RzSetU *return_addresses = rz_set_u_new(); RzVector *cfep_addresses = rz_vector_new(sizeof(ut64), NULL, NULL); - rz_vector_push(cfep_addresses, &entry_point); + RzIterator *iter = rz_set_u_as_iter(symbol_addresses); + ut64 *addr; + rz_iterator_foreach(iter, addr) { + rz_vector_push(cfep_addresses, addr); + } fill_cfep_and_ret_addresses(binary_bb_cfg, call_candidates, return_addresses, cfep_addresses); // Set of handled cfep diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index a3b8c0612d1..910bcd15e41 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -303,9 +303,42 @@ static bool setup_queues(RzCore *core, return false; } +RZ_API bool rz_inquiry_get_fcn_symbol_addr(RzCore *core, RZ_OUT RzSetU *symbol_targets) { + rz_return_val_if_fail(core && symbol_targets, false); + RzPVector /**/ *sections = rz_bin_object_get_sections(core->bin->cur->o); + if (!sections) { + rz_warn_if_reached(); + return false; + } + // Add function addresses of function symbols in the executable region. + void **it; + const RzPVector *symbols = rz_bin_object_get_symbols(core->bin->cur->o); + if (!symbols) { + rz_warn_if_reached(); + return false; + } + rz_pvector_foreach (symbols, it) { + RzBinSymbol *sym = *it; + if (!RZ_STR_EQ(sym->type, RZ_BIN_TYPE_FUNC_STR)) { + continue; + } + void **it2; + rz_pvector_foreach (sections, it2) { + RzBinSection *sec = *it2; + if (sec->perm & RZ_PERM_X && + RZ_BETWEEN_EXCL(sec->vaddr, sym->vaddr, sec->vaddr + sec->vsize)) { + rz_set_u_add(symbol_targets, sym->vaddr); + break; + } + } + } + return true; +} + static bool get_branch_targets(RzCore *core, RzSetU *branch_targets, RzVector /**/ *insn_to_insn_edges) { RzPVector /**/ *sections = rz_bin_object_get_sections(core->bin->cur->o); if (!sections) { + rz_warn_if_reached(); return false; } RzVector *non_x_idx = rz_vector_new(sizeof(size_t), NULL, NULL); @@ -451,6 +484,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent RzBuffer *io_buf = rz_buf_new_with_io(&core->analysis->iob); RzAnalysisILVM *analysis_vm = NULL; RzSetU *branch_targets = rz_set_u_new(); + RzSetU *symbol_targets = rz_set_u_new(); bool user_sent_signal = false; rz_cons_push(); @@ -466,14 +500,22 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent il_cache = ht_up_new(NULL, (RzPVectorFree)rz_interpreter_il_bb_free); RzVector /**/ *insn_to_insn_edges = rz_vector_new(sizeof(RzAnalysisXRef), NULL, NULL); - if (!get_branch_targets(core, branch_targets, insn_to_insn_edges)) { + if (!get_branch_targets(core, branch_targets, insn_to_insn_edges) || + !rz_inquiry_get_fcn_symbol_addr(core, symbol_targets)) { RZ_LOG_ERROR("Failed to get branch targets.\n"); return_code = false; goto error_free; } + RzIterator *iter = rz_set_u_as_iter(symbol_targets); + ut64 *sym_addr; + rz_iterator_foreach(iter, sym_addr) { + rz_set_u_add(branch_targets, *sym_addr); + } + rz_iterator_free(iter); + rz_set_u_free(symbol_targets); if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { - printf("Total branch targets in binary: %" PFMT32d "\n", rz_set_u_size(branch_targets)); + eprintf("Total branch targets in binary: %" PFMT32d "\n", rz_set_u_size(branch_targets)); } // Initialize the abstract state with the architecture's registers. @@ -728,20 +770,20 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent rz_vector_free(covered_jump_targets); } if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { - printf(RZ_CONS_CLEAR_LINE "\rBranch targets left: %" PFMT32d, rz_set_u_size(branch_targets)); + eprintf(RZ_CONS_CLEAR_LINE "\rBranch targets left: %" PFMT32d, rz_set_u_size(branch_targets)); fflush(stdout); } } while (!rz_vector_empty(entry_points)); if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { - printf("\n"); + eprintf("\n"); } if (!rz_inquiry_bb_cfg_reduce(core->inquiry->bb_cfg)) { rz_warn_if_reached(); goto error_free; } if (!user_sent_signal) { - printf("Complement BB CFG with statically known xrefs...\n"); + eprintf("Complement BB CFG with statically known xrefs...\n"); if (!rz_inquiry_bb_cfg_complement(core->inquiry, insn_to_insn_edges)) { rz_warn_if_reached(); goto error_free; @@ -856,14 +898,14 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry return true; } -RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry, ut64 entry_point, +RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry, RzSetU *symbol_addresses, const RzPVector /**/ *symbols) { RzPVector *fcns = rz_pvector_new((RzPVectorFree)rz_inquiry_function_free); if (!rz_inquiry_algo_revng_fcn_detection( - entry_point, - inquiry->call_candidates, - inquiry->bb_cfg, - fcns)) { + symbol_addresses, + inquiry->call_candidates, + inquiry->bb_cfg, + fcns)) { rz_warn_if_reached(); return false; } From 06925d36df004dfe3768bdbc08a881c34dcf68e5 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 11 Mar 2026 17:35:54 +0100 Subject: [PATCH 223/334] Fix bug of too many function detections. --- librz/include/rz_analysis.h | 1 + .../inquiry/algorithms/revng_fcn_detection.c | 15 ++-------- librz/inquiry/bb_cfg.c | 17 +++++++---- librz/inquiry/inquiry.c | 2 +- .../interpreter/prototype/eval_effect.c | 5 ++-- test/db/inquiry/interpreter/xrefs | 28 +++++++++++++------ 6 files changed, 39 insertions(+), 29 deletions(-) diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 2960bea02f1..8e3783896d2 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -989,6 +989,7 @@ typedef struct { ut64 store_addr; ///< The address of the instruction packet storing the NPC. Might be 0, if it was not added by the interpreter. ut64 candidate_addr; ///< Address of the call candidate instruction packet. Might be 0, if it was not added by the interpreter. ut64 npc; ///< The NPC stored. Should point after a basic block. + ut64 target; ///< The address the call candidate jumps to. bool in_mem; ///< True if the NPC is written to memory. False if it is written to a register. } RzAnalysisCallCandidate; diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index f0a9251e333..392d4dc2266 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -154,20 +154,11 @@ static void fill_cfep_and_ret_addresses( RzAnalysisCallCandidate *cc = *it; ut64 ret_addr = cc->npc; const RzList *predecessor = rz_inquiry_bb_cfg_get_neighbours_to(binary_bb_cfg, ret_addr); - if (!predecessor) { - RZ_LOG_WARN("The call candidate (0x%" PFMT64x ") NPC 0x%" PFMT64x " doesn't point to a BB.\n", cc->bb_addr, cc->npc); - continue; - } - if (rz_list_length(predecessor) > 0) { + if (predecessor && rz_list_length(predecessor) > 0) { rz_set_u_add(return_addresses, ret_addr); } - - const RzList *successors = rz_inquiry_bb_cfg_get_neighbours_from(binary_bb_cfg, cc->bb_addr); - RzGraphNode *gnode; - RzListIter *lit; - rz_list_foreach (successors, lit, gnode) { - rz_vector_push(cfep_addresses, &gnode->data); - } + RZ_LOG_DEBUG("Add cfep at 0x%llx based on BB 0x%llx\n", cc->candidate_addr, cc->target); + rz_vector_push(cfep_addresses, &cc->target); } rz_iterator_free(iter); rz_vector_sort(cfep_addresses, cmp, false, NULL); diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index 6aa6b24413b..8b9a15df3e5 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -165,19 +165,24 @@ RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_to(con RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /**/ *insn_to_insn_edges) { // Add the instruction to instruction edges. RzAnalysisXRef *i2i_edge; - rz_vector_foreach(insn_to_insn_edges, i2i_edge) { + rz_vector_foreach (insn_to_insn_edges, i2i_edge) { // First check if the edge is already in the CFG. // If so (not unlikely), skip the step where it iterates over all BBs. const RzList *incoming = rz_inquiry_bb_cfg_get_neighbours_to(iq->bb_cfg, i2i_edge->to); - if (!incoming || rz_list_length(incoming) == 0) { - // Basic block not present. Ignore. + if (!incoming) { + // Basic block not present. continue; } + if (rz_list_length(incoming) == 0) { + // No incoming edges in the CFG yet. + // Find the basic block this edge originates from. + goto find_bb; + } RzGraphNode *gn; RzListIter *lit; - rz_list_foreach(incoming, lit, gn) { + rz_list_foreach (incoming, lit, gn) { RzInterval *bb = ht_up_find(iq->bb_cfg->basic_blocks, (ut64)gn->data, NULL); if (!bb) { continue; @@ -188,6 +193,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /*bb_cfg, bb->addr, i2i_edge->to); } rz_iterator_free(bb_iter); -next_i2i_edge: + } + next_i2i_edge: continue; } return true; diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 910bcd15e41..e8f6fbe7a09 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -416,7 +416,7 @@ static bool handle_yields(RzCore *core, HtUP *yield_queues) { } RzAnalysisCallCandidate *cc_clone = RZ_NEW0(RzAnalysisCallCandidate); memcpy(cc_clone, cc, sizeof(RzAnalysisCallCandidate)); - if (!ht_up_update(core->inquiry->call_candidates, cc_clone->bb_addr, cc_clone)) { + if (ht_up_update(core->inquiry->call_candidates, cc_clone->bb_addr, cc_clone)) { RZ_LOG_DEBUG("Overwrote a call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); } else { RZ_LOG_DEBUG("Added call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 2669d643bcb..cdadcbc7fed 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -95,13 +95,14 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, // Everything is just a jump for it at this point. report_yield_xref(state, insn_pkt_size, yield_queues, rz_bv_to_ut64(pc->bv), &eval_out, RZ_ANALYSIS_XREF_TYPE_CODE); } - if (plugin_data->call_cand.store_addr) { + if (plugin_data->call_cand.store_addr && eval_out.is_concrete) { // An instruction in this basic block stored the next PC. // Report a call candidate. plugin_data->call_cand.candidate_addr = rz_bv_to_ut64(pc->bv); + plugin_data->call_cand.target = rz_bv_to_ut64(eval_out.bv); report_yield_call_candiate(state, yield_queues, plugin_data); - memset(&plugin_data->call_cand, 0, sizeof(plugin_data->call_cand)); } + memset(&plugin_data->call_cand, 0, sizeof(plugin_data->call_cand)); copy_abstr_data(state->pc->abstr_data, &eval_out); break; } diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs index 0d1a23c77f5..d3a0a155ddd 100644 --- a/test/db/inquiry/interpreter/xrefs +++ b/test/db/inquiry/interpreter/xrefs @@ -405,6 +405,7 @@ EOF EXPECT=< DATA -> 0x8000050 reloc..bss section..text+24 0x800004c -> CODE -> 0x8000060 reloc.target.function_0+12 section..text+24 0x800004c -> CODE -> 0x8000080 reloc.target.function_1+12 + section..text+24 0x800004c -> CODE -> 0x80000a0 reloc.target.function_2+12 reloc.target.function_0+8 0x800005c -> CODE -> 0x8000034 section..text reloc.target.function_0+28 0x8000070 -> CODE -> 0x80000e8 sym.main+52 reloc.target.function_1+8 0x800007c -> CODE -> 0x8000034 section..text reloc.target.function_1+28 0x8000090 -> CODE -> 0x80000e8 sym.main+52 + reloc.target.function_2+8 0x800009c -> CODE -> 0x8000034 section..text sym.main+28 0x80000d0 -> CODE -> 0x8000110 sym.main+92 sym.main+32 0x80000d4 -> DATA -> 0x8000130 reloc..data sym.main+40 0x80000dc -> DATA -> 0x8000134 section..data @@ -529,7 +534,8 @@ EXPECT=< CODE -> 0x8000194 reloc..data.rel.local addr size traced ninstr jump fail fcns calls xrefs --------------------------------------------------------------------------- +0x8000040 48 0 0 0x08000040 +0x8000074 48 0 0 0x08000074 0x80000a4 28 0 0 +0x80000c4 48 0 0 0x080000c4 0x80000f4 28 0 0 +0x8000114 48 0 0 0x08000114 0x8000144 28 0 0 0x8000164 44 0 0 0x080001dc 0x08000164 0x8000194 32 0 0 0x08000164 From fa9e7cd06e096755d4567ec0e93b8929e0ee71fc Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 11 Mar 2026 18:36:58 +0100 Subject: [PATCH 224/334] Get rid of wrong assumptions that branch targets must be aligned. --- librz/arch/xrefs.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/librz/arch/xrefs.c b/librz/arch/xrefs.c index 2b842aec6a3..8b1d1699ecd 100644 --- a/librz/arch/xrefs.c +++ b/librz/arch/xrefs.c @@ -314,10 +314,6 @@ RZ_API const char *rz_analysis_ref_type_tostring(RzAnalysisXRefType t) { return "unknown"; } -int addr_at_aligned_x_addr(const ut64 *addr, const RzBinSection *sec, void *user) { - return ((*addr & 3) == 0 && RZ_BETWEEN(sec->vaddr, *addr, sec->vaddr + sec->size)) ? 0 : -1; -} - static bool read_up_to(RzAnalysis *analysis, ut64 addr, ut8 *buf, size_t buf_size) { rz_mem_set_num(buf, buf_size, 0); do { @@ -375,9 +371,8 @@ RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, // Only add jump targets going to executable regions. if ((rz_analysis_op_is_direct_call(&op) || rz_analysis_op_is_direct_jump(&op)) && - op.jump != UT64_MAX && - rz_pvector_find(sections, &op.jump, (RzListComparator)addr_at_aligned_x_addr, NULL)) { - RZ_LOG_DEBUG("Add call target 0x%" PFMT64x " -> 0x%" PFMT64x "\n", op.addr, op.jump); + op.jump != UT64_MAX) { + eprintf("Add branch target 0x%" PFMT64x " -> 0x%" PFMT64x "\n", op.addr, op.jump); rz_set_u_add(branch_targets, op.jump); RzAnalysisXRef edge = { .from = addr, .to = op.jump }; From 4ef02fd95702ab5ac247e02db6d3f13a4d3a2325 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 18 Mar 2026 17:59:53 +0100 Subject: [PATCH 225/334] Remove unnecessary logging --- librz/arch/xrefs.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/librz/arch/xrefs.c b/librz/arch/xrefs.c index 8b1d1699ecd..9fcfc1cbdff 100644 --- a/librz/arch/xrefs.c +++ b/librz/arch/xrefs.c @@ -372,14 +372,12 @@ RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, if ((rz_analysis_op_is_direct_call(&op) || rz_analysis_op_is_direct_jump(&op)) && op.jump != UT64_MAX) { - eprintf("Add branch target 0x%" PFMT64x " -> 0x%" PFMT64x "\n", op.addr, op.jump); rz_set_u_add(branch_targets, op.jump); RzAnalysisXRef edge = { .from = addr, .to = op.jump }; rz_vector_push(insn_to_insn_edges, &edge); if (op.fail != UT64_MAX) { if (rz_analysis_op_is_direct_jump(&op)) { - RZ_LOG_DEBUG("Add branch target 0x%" PFMT64x " -> 0x%" PFMT64x "\n", op.addr, op.fail); rz_set_u_add(branch_targets, op.fail); RzAnalysisXRef edge = { .from = addr, .to = op.fail }; From 158ffca9bdda0d5441e6c5d3b17f524a2027e0b7 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 18 Mar 2026 20:11:17 +0100 Subject: [PATCH 226/334] Move validation of IL ops code into function and deactivate it. --- librz/inquiry/inquiry.c | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index e8f6fbe7a09..4297769e715 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -425,6 +425,27 @@ static bool handle_yields(RzCore *core, HtUP *yield_queues) { return true; } +#if 0 +static void validate_il_bb(RzCore *core, RzInterpreterILBB *bb) { + RzAnalysisILVM *vm = rz_analysis_il_vm_new(core->analysis, NULL); + RzILValidateGlobalContext *ctx = rz_il_validate_global_context_new_from_vm(vm->vm); + void **it; + size_t i = 0; + rz_pvector_enumerate (bb->il_ops, it, i) { + char *report = NULL; + RzInterpreterInsnPkt *pkt = *it; + if (!rz_il_validate_effect(pkt->effect, ctx, NULL, NULL, &report)) { + RZ_LOG_ERROR("Validation failed for IL op %" PFMTSZu " in BB 0x%" PFMT64x " in insn packet:\n" + "\t'%s'\n", + i, bb->bb_addr, report); + } + free(report); + } + rz_analysis_il_vm_free(vm); + rz_il_validate_global_context_free(ctx); +} +#endif + static const RzInterpreterILBB *get_il_bb(RzCore *core, HtUP *il_cache, ut64 addr) { RzInterpreterILBB *bb = ht_up_find(il_cache, addr, NULL); if (!bb) { @@ -435,24 +456,9 @@ static const RzInterpreterILBB *get_il_bb(RzCore *core, HtUP *il_cache, ut64 add return NULL; } -#if RZ_BUILD_DEBUG +#if 0 // Validate IL to catch more errors during testing. - RzAnalysisILVM *vm = rz_analysis_il_vm_new(core->analysis, NULL); - RzILValidateGlobalContext *ctx = rz_il_validate_global_context_new_from_vm(vm->vm); - void **it; - size_t i = 0; - rz_pvector_enumerate (bb->il_ops, it, i) { - char *report = NULL; - RzInterpreterInsnPkt *pkt = *it; - if (!rz_il_validate_effect(pkt->effect, ctx, NULL, NULL, &report)) { - RZ_LOG_ERROR("Validation failed for IL op %" PFMTSZu " in BB 0x%" PFMT64x " in insn packet:\n" - "\t'%s'\n", - i, bb->bb_addr, report); - } - free(report); - } - rz_analysis_il_vm_free(vm); - rz_il_validate_global_context_free(ctx); + validate_il_bb(core, bb); // Otherwise YOLO #endif From 7a64c37702ceb58e19777e7ea8833963c694798b Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 18 Mar 2026 20:18:39 +0100 Subject: [PATCH 227/334] Don't add jumps to invalid addresses to functions. --- librz/core/cmd/cmd_inquiry.c | 41 ++++++++++++- librz/include/rz_inquiry.h | 8 ++- librz/include/rz_inquiry/rz_interpreter.h | 4 +- .../inquiry/algorithms/revng_fcn_detection.c | 27 +++++++-- librz/inquiry/inquiry.c | 48 +++------------- librz/inquiry/interpreter/interpreter.c | 5 +- test/db/inquiry/interpreter/unmapped_fcn_loop | 57 ++++++++----------- 7 files changed, 103 insertions(+), 87 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index ef9cdb27834..883d0442b10 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -10,6 +10,39 @@ #include #include +static RzVector /**/ *get_ignored_code_regions( + const RzPVector /**/ *symbols, + RzPVector /**/ *sections, + const RzPVector /**/ *io_maps) { + void **it; + RzVector *v = rz_vector_new(sizeof(RzInterval), NULL, NULL); + rz_pvector_foreach (sections, it) { + RzBinSection *sec = *it; + if (sec->layout.role == RZ_BIN_SECTION_ROLE_LINKING || !(sec->perm & RZ_PERM_X)) { + RzInterval itv = { .addr = sec->vaddr, .size = sec->vsize }; + rz_vector_push(v, &itv); + } + } + rz_pvector_free(sections); + + rz_pvector_foreach (io_maps, it) { + RzIOMap *map = *it; + if (!(map->perm & RZ_PERM_X)) { + RzInterval itv = { .addr = map->itv.addr, .size = map->itv.size }; + rz_vector_push(v, &itv); + } + } + + rz_pvector_foreach (symbols, it) { + RzBinSymbol *sym = *it; + if (sym->is_imported) { + RzInterval itv = { .addr = sym->vaddr, .size = sym->size }; + rz_vector_push(v, &itv); + } + } + return v; +} + RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int argc, const char **argv) { rz_return_val_if_fail(core->analysis && core->io && core->bin->cur && core->bin->cur->o, RZ_CMD_STATUS_ERROR); RzVector *entry_points = rz_vector_new(sizeof(ut64), NULL, NULL); @@ -29,7 +62,11 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar rz_vector_push(entry_points, &entry_point); } } - bool success = rz_inquiry_interpreter(core, entry_points); + RzVector *ignored_code_regions = get_ignored_code_regions( + rz_bin_object_get_symbols(core->bin->cur->o), + rz_bin_object_get_sections(core->bin->cur->o), + rz_io_maps(core->io)); + bool success = rz_inquiry_interpreter(core, entry_points, ignored_code_regions); eprintf("Finished reference recovery: %s\n", success ? "OK" : "FAIL"); if (!success) { return RZ_CMD_STATUS_ERROR; @@ -42,7 +79,7 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar return RZ_CMD_STATUS_ERROR; } const RzPVector *symbols = rz_bin_object_get_symbols(core->bin->cur->o); - success = rz_inquiry_function_deduction(core->analysis, core->inquiry, symbol_addresses, symbols); + success = rz_inquiry_function_deduction(core->analysis, core->inquiry, symbol_addresses, symbols, ignored_code_regions); eprintf("%s\n", success ? "OK" : "FAIL"); return success ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 73fd24b69f0..df1cf4e2f0c 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -81,12 +81,13 @@ RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzPVector /**/ *allowed_segments); -RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *entry_points); +RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *entry_points, RZ_NONNULL const RzVector /**/ *ignored_code); RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry, RzSetU *symbol_addresses, - const RzPVector /**/ *symbols); + const RzPVector /**/ *symbols, + RZ_NONNULL const RzVector /**/ *ignored_code); //============ // Algorithms @@ -111,7 +112,8 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( RzSetU *symbol_addresses, const HtUP /**/ *call_candidates, const RzInquiryBBCFG *bb_cfg, - RZ_OUT RzPVector /**/ *fcns); + RZ_OUT RzPVector /**/ *fcns, + RZ_NONNULL const RzVector /**/ *ignored_code); #ifdef __cplusplus } diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 73592ddfc74..ef3e4287208 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -253,7 +253,7 @@ typedef struct { RzThreadQueue /**/ *io_request; ///< The queue for read/write requests to the IO layer. RzThreadQueue /**/ *io_result; ///< The queue for the read/write requests' answers. RzAtomicBool *is_running_flag; ///< Flag for the interpreter thread to toggle when done. - RzVector /**/ *ignored_code; + const RzVector /**/ *ignored_code; /** * \brief The entry points for the interpreters. * Each address has its lifted IL op in the il_queue at the same index. @@ -288,7 +288,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag, RZ_NONNULL RZ_OWN RzVector /**/ *entry_points, - RZ_NONNULL RZ_OWN RzVector /**/ *ignored_code); + RZ_NONNULL const RzVector /**/ *ignored_code); RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset); RZ_API void rz_interpreter_set_add_entry_points(RZ_NONNULL RzInterpreterSet *iset, const RzVector /**/ *entry_points); diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index 392d4dc2266..3a4ec68c4a9 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -46,6 +46,7 @@ #include "rz_util/rz_assert.h" #include "rz_util/rz_iterator.h" #include "rz_util/rz_set.h" +#include "rz_vector.h" #include #include #include @@ -59,6 +60,17 @@ static int cmp(const void *a, const void *b, void *user) { return 0; } +static bool jumps_to_ignored_code(const RzVector *v, ut64 jump_target) { + void *it; + rz_vector_foreach (v, it) { + RzInterval *itv = it; + if (rz_itv_contain(*itv, jump_target)) { + return true; + } + } + return false; +} + static void recurse_into_fcn_bbs( RzInquiryFunction *fcn, ut64 predecessor_bb_addr, @@ -67,7 +79,8 @@ static void recurse_into_fcn_bbs( const RzSetU *return_addresses, const RzVector /**/ *cfep_addresses, const HtUP /**/ *call_candidates, - const RzInquiryBBCFG *binary_bb_cfg) { + const RzInquiryBBCFG *binary_bb_cfg, + RZ_NONNULL const RzVector /**/ *ignored_code) { if (rz_set_u_contains(visited_fcn_bbs, this_bb_addr)) { return; } @@ -125,6 +138,9 @@ static void recurse_into_fcn_bbs( succ_addr = cc->npc; } } + if (jumps_to_ignored_code(ignored_code, succ_addr)) { + continue; + } recurse_into_fcn_bbs( fcn, @@ -134,7 +150,8 @@ static void recurse_into_fcn_bbs( return_addresses, cfep_addresses, call_candidates, - binary_bb_cfg); + binary_bb_cfg, + ignored_code); } return; @@ -168,7 +185,8 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( RzSetU *symbol_addresses, RZ_NONNULL const HtUP /**/ *call_candidates, RZ_NONNULL const RzInquiryBBCFG *binary_bb_cfg, - RZ_NONNULL RZ_OUT RzPVector /**/ *fcns) { + RZ_NONNULL RZ_OUT RzPVector /**/ *fcns, + RZ_NONNULL const RzVector /**/ *ignored_code) { rz_return_val_if_fail(call_candidates && binary_bb_cfg && fcns, false); // Candidate function entry points @@ -201,7 +219,8 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( return_addresses, cfep_addresses, call_candidates, - binary_bb_cfg); + binary_bb_cfg, + ignored_code); rz_set_u_free(visited_bbs); rz_set_u_add(cfep_handled, cfep_addr); rz_pvector_push(fcns, fcn); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 4297769e715..a2cbeb742a9 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -363,39 +363,6 @@ static bool get_branch_targets(RzCore *core, RzSetU *branch_targets, RzVector /* return true; } -static RzVector /**/ *get_ignored_code_regions( - const RzPVector /**/ *symbols, - RzPVector /**/ *sections, - const RzPVector /**/ *io_maps) { - void **it; - RzVector *v = rz_vector_new(sizeof(RzInterval), NULL, NULL); - rz_pvector_foreach (sections, it) { - RzBinSection *sec = *it; - if (sec->layout.role == RZ_BIN_SECTION_ROLE_LINKING || !(sec->perm & RZ_PERM_X)) { - RzInterval itv = { .addr = sec->vaddr, .size = sec->vsize }; - rz_vector_push(v, &itv); - } - } - rz_pvector_free(sections); - - rz_pvector_foreach (io_maps, it) { - RzIOMap *map = *it; - if (!(map->perm & RZ_PERM_X)) { - RzInterval itv = { .addr = map->itv.addr, .size = map->itv.size }; - rz_vector_push(v, &itv); - } - } - - rz_pvector_foreach (symbols, it) { - RzBinSymbol *sym = *it; - if (sym->is_imported) { - RzInterval itv = { .addr = sym->vaddr, .size = sym->size }; - rz_vector_push(v, &itv); - } - } - return v; -} - static bool handle_yields(RzCore *core, HtUP *yield_queues) { RzInterpreterYieldQueue *q_xrefs = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); if (!rz_th_queue_is_empty(q_xrefs->yield_queue)) { @@ -474,7 +441,9 @@ static const RzInterpreterILBB *get_il_bb(RzCore *core, HtUP *il_cache, ut64 add * A function to call the prototype interpreter. * Usually these tasks will be split between different caches and yield consumers. */ -RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *entry_points) { +RZ_API bool rz_inquiry_interpreter(RzCore *core, + RZ_OWN RzVector /**/ *entry_points, + RZ_NONNULL const RzVector /**/ *ignored_code) { // All the things we need bool return_code = true; RzThreadQueue *io_request_q = NULL; @@ -584,10 +553,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *ent io_result_q, is_running, rz_vector_clone(entry_points), - get_ignored_code_regions( - rz_bin_object_get_symbols(core->bin->cur->o), - rz_bin_object_get_sections(core->bin->cur->o), - rz_io_maps(core->io))); + ignored_code); if (!iset) { return_code = false; rz_warn_if_reached(); @@ -905,13 +871,15 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry } RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry, RzSetU *symbol_addresses, - const RzPVector /**/ *symbols) { + const RzPVector /**/ *symbols, + RZ_NONNULL const RzVector /**/ *ignored_code) { RzPVector *fcns = rz_pvector_new((RzPVectorFree)rz_inquiry_function_free); if (!rz_inquiry_algo_revng_fcn_detection( symbol_addresses, inquiry->call_candidates, inquiry->bb_cfg, - fcns)) { + fcns, + ignored_code)) { rz_warn_if_reached(); return false; } diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 37fbdbfa6ef..79097a0555b 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -173,9 +173,6 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { if (iset->is_running_flag) { rz_atomic_bool_free(iset->is_running_flag); } - if (iset->ignored_code) { - rz_vector_free(iset->ignored_code); - } if (iset->state) { rz_interpreter_abstr_state_free(iset->state); } @@ -207,7 +204,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag, RZ_NONNULL RZ_OWN RzVector /**/ *entry_points, - RZ_NONNULL RZ_OWN RzVector /**/ *ignored_code) { + RZ_NONNULL const RzVector /**/ *ignored_code) { rz_return_val_if_fail(plugin && state && branch_queue && il_queue && yield_queues && io_request && io_result && is_running_flag && entry_points && ignored_code, NULL); RzInterpreterSet *set = RZ_NEW0(RzInterpreterSet); diff --git a/test/db/inquiry/interpreter/unmapped_fcn_loop b/test/db/inquiry/interpreter/unmapped_fcn_loop index b16572b1b13..3208f0c4383 100644 --- a/test/db/inquiry/interpreter/unmapped_fcn_loop +++ b/test/db/inquiry/interpreter/unmapped_fcn_loop @@ -47,34 +47,29 @@ EXPECT=< CODE -> 0x8000050 reloc.target.run reloc.target.run+26 0x800006a -> CODE -> 0x800009f reloc.recurse+27 reloc.target.run+34 0x8000072 -> CODE -> 0x80000b0 sym.function_0 - reloc.target.run+34 0x8000072 -> CODE -> 0x80000c0 sym.function_1 reloc.target.run+34 0x8000072 -> DATA -> 0x80000d0 section..data - reloc.target.run+34 0x8000072 -> DATA -> 0x80000d8 reloc..text.80000d8 reloc.fcn_arr+12 0x8000081 -> CODE -> 0x8000088 reloc.recurse+4 reloc.fcn_arr+14 0x8000083 -> CODE -> 0x8000040 section..text reloc.recurse+25 0x800009d -> CODE -> 0x8000066 reloc.target.run+22 @@ -130,8 +123,8 @@ EXPECT=< Date: Thu, 19 Mar 2026 17:50:58 +0100 Subject: [PATCH 228/334] Move warnings where they occur. --- .../inquiry/algorithms/revng_fcn_detection.c | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index 3a4ec68c4a9..66ca7f3cc54 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -91,22 +91,27 @@ static void recurse_into_fcn_bbs( // RzInterval this_bb = { 0 }; if (!rz_inquiry_bb_cfg_get_basic_block(binary_bb_cfg, this_bb_addr, &this_bb)) { - goto warn_return; + rz_warn_if_reached(); + goto err_return; } if (!rz_inquiry_bb_cfg_add_basic_block(fcn->bb_cfg, this_bb.addr, this_bb.size)) { - goto warn_return; + rz_warn_if_reached(); + goto err_return; } if (predecessor_bb_addr != UT64_MAX) { RzInterval from_bb = { 0 }; if (!rz_inquiry_bb_cfg_get_basic_block(binary_bb_cfg, predecessor_bb_addr, &from_bb)) { - goto warn_return; + rz_warn_if_reached(); + goto err_return; } if (!rz_inquiry_bb_cfg_add_basic_block(fcn->bb_cfg, from_bb.addr, from_bb.size)) { - goto warn_return; + rz_warn_if_reached(); + goto err_return; } if (!rz_inquiry_bb_cfg_add_edge(fcn->bb_cfg, predecessor_bb_addr, this_bb_addr)) { - goto warn_return; + rz_warn_if_reached(); + goto err_return; } } @@ -115,7 +120,8 @@ static void recurse_into_fcn_bbs( // const RzList *successors = rz_inquiry_bb_cfg_get_neighbours_from(binary_bb_cfg, this_bb_addr); if (!successors) { - goto warn_return; + rz_warn_if_reached(); + goto err_return; } RzListIter *lit; @@ -155,8 +161,7 @@ static void recurse_into_fcn_bbs( } return; -warn_return: - rz_warn_if_reached(); +err_return: return; } From 795573fc06a8e7f9d43c74cd5e78260596272d2c Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 19 Mar 2026 19:09:42 +0100 Subject: [PATCH 229/334] Don't hard fail on errors in the function detection. --- librz/inquiry/inquiry.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index a2cbeb742a9..1c06a8c2798 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -832,7 +832,7 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry RzAnalysisFunction *afcn = rz_analysis_create_function(analysis, new_fcn_name, fcn_addr, RZ_ANALYSIS_FCN_TYPE_FCN); if (!afcn) { rz_warn_if_reached(); - return false; + continue; } void **it2; @@ -842,7 +842,7 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, bb->addr); if (!abb && !(abb = rz_analysis_create_block(analysis, bb->addr, bb->size))) { rz_warn_if_reached(); - return false; + continue; } const RzList *successors = rz_inquiry_bb_cfg_get_neighbours_from(inquiry->bb_cfg, bb->addr); RzGraphNode *n; From b48c2a3903872fe3e819e887d2c46e8701ced729 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 21 Mar 2026 18:13:48 +0100 Subject: [PATCH 230/334] Fix leaks --- librz/core/cmd/cmd_inquiry.c | 2 ++ librz/inquiry/algorithms/revng_fcn_detection.c | 1 + librz/inquiry/inquiry.c | 2 ++ 3 files changed, 5 insertions(+) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 883d0442b10..579727c63d4 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -81,6 +81,8 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar const RzPVector *symbols = rz_bin_object_get_symbols(core->bin->cur->o); success = rz_inquiry_function_deduction(core->analysis, core->inquiry, symbol_addresses, symbols, ignored_code_regions); eprintf("%s\n", success ? "OK" : "FAIL"); + rz_set_u_free(symbol_addresses); + rz_vector_free(ignored_code_regions); return success ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; } diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index 66ca7f3cc54..ed09ca7b322 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -202,6 +202,7 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( rz_iterator_foreach(iter, addr) { rz_vector_push(cfep_addresses, addr); } + rz_iterator_free(iter); fill_cfep_and_ret_addresses(binary_bb_cfg, call_candidates, return_addresses, cfep_addresses); // Set of handled cfep diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 1c06a8c2798..0cf8f5a1134 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -314,6 +314,7 @@ RZ_API bool rz_inquiry_get_fcn_symbol_addr(RzCore *core, RZ_OUT RzSetU *symbol_t void **it; const RzPVector *symbols = rz_bin_object_get_symbols(core->bin->cur->o); if (!symbols) { + rz_pvector_free(sections); rz_warn_if_reached(); return false; } @@ -332,6 +333,7 @@ RZ_API bool rz_inquiry_get_fcn_symbol_addr(RzCore *core, RZ_OUT RzSetU *symbol_t } } } + rz_pvector_free(sections); return true; } From 0f0f023d79956d3eae77523d4fc6e36707a3629e Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 21 Mar 2026 22:10:07 +0100 Subject: [PATCH 231/334] Push the same object over the queue to prepare for multi-threaded interpretation. --- librz/include/rz_inquiry/rz_interpreter.h | 95 +++++++++------- librz/inquiry/inquiry.c | 107 ++++++++++-------- librz/inquiry/interpreter/interpreter.c | 84 ++++++++++---- .../interpreter/p/interpreter_prototype.c | 2 +- librz/inquiry/interpreter/prototype/eval.c | 88 ++++++++------ librz/inquiry/interpreter/prototype/eval.h | 24 ++-- .../interpreter/prototype/eval_effect.c | 6 +- .../inquiry/interpreter/prototype/eval_pure.c | 6 +- 8 files changed, 248 insertions(+), 164 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index ef3e4287208..f19efb2f0e3 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -68,22 +68,43 @@ typedef struct { ut64 alt_target; } RzInterpreterBranch; +typedef enum { + RZ_INTERPRETER_IO_READ, + RZ_INTERPRETER_IO_WRITE, +} RzInterpreterIOReqType; + +typedef struct { + RzInterpreterIOReqType type; + size_t mem_idx; ///< The memory space to read/write. + bool big_endian; ///< Set if the data is big endian ordered. + const RzBitVector *addr; ///< The address to read/write. + const RzBitVector *st_data; ///< The data to store. + RzBitVector *ld_data; ///< The bit vector to load into. It is BORROWED. + size_t n_bits; ///< The number of bits to read/write. +} RzInterpreterIORequest; + +typedef struct { + bool req_ok; ///< Set to true if IO request succeeded. +} RzInterpreterIOResult; + /** - * objects the interpreter should use to send over the queues. - * - * TODO: Race conditions ahead, if one party is faster in overwriting one value - * than the other using it. - * Shouldn't happen though as long as there is only one interpreter - * and one RzInquiry managing everything. + * Objects an interpreter instance sends over queues. */ +RZ_LIFETIME(RzInquiry) typedef struct { - RZ_LIFETIME(RzInquiry) - RzInterpreterBranch branch; ///< The branch object passed to an IL cache for BB requests. + size_t instance_id; ///< The interpreter instance this object belongs to. + /** + * \brief Locked whenever an interpreter sent this object over the queue. + * The consumer releases the lock when it collected the object. + * The producer is not supposed to use this object as long as the lock is closed. + */ + RzThreadLock *received; - RZ_LIFETIME(RzInquiry) + // Not inside the union so they can be read and written at the same time. + RzInterpreterIORequest io_req; ///< An IO request. + RzInterpreterIOResult io_res; ///< The IO result. + RzInterpreterBranch branch; ///< The branch object passed to an IL cache for BB requests. RzAnalysisXRef xref; ///< The xref object passed over the queue. - - RZ_LIFETIME(RzInquiry) RzAnalysisCallCandidate call_cand; ///< The stores next pc info passed over the queue. } RzInterpreterSharedObjects; @@ -97,7 +118,7 @@ typedef struct { RzAnalysisILConfig *il_config; ///< The IL configuration of the RzArch plugin. const char *arch_name; ///< Name of architecture. Used by work-arounds until we have RzArch. /** - * \brief Shared objects. Pointers to the members are passed over the queue. + * \brief Shared objects. This pointer is pushed over the queue. * TODO: This is obviously not the final solution. Just some poor man's shared memory. */ RZ_LIFETIME(RzInquiry) @@ -191,9 +212,9 @@ typedef struct { */ bool (*eval)(RZ_NONNULL RzInterpreterAbstrState *state, RZ_NONNULL const RzInterpreterILBB *il_bb, - RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_request, - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_result, + RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_request, + RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_result, void *plugin_data); /** * \brief Determines the next successor addresses from state. @@ -222,36 +243,18 @@ typedef struct { void *plugin_data); } RzInterpreterPlugin; -typedef enum { - RZ_INTERPRETER_IO_READ, - RZ_INTERPRETER_IO_WRITE, -} RzInterpreterIOReqType; - -typedef struct { - RzInterpreterIOReqType type; - size_t mem_idx; ///< The memory space to read/write. - bool big_endian; ///< Set if the data is big endian ordered. - const RzBitVector *addr; ///< The address to read/write. - const RzBitVector *st_data; ///< The data to store. - RzBitVector *ld_data; ///< The bit vector to load into. It is BORROWED. - size_t n_bits; ///< The number of bits to read/write. -} RzInterpreterIORequest; - -typedef struct { - bool req_ok; ///< Set to true if IO request succeeded. -} RzInterpreterIOResult; - /** * \brief The set of required queues for an interpreter to run. */ +RZ_LIFETIME(RzInquiry) typedef struct { RzInterpreterAbstrState *state; ///< The abstract state of the interpreter. - RzThreadQueue /**/ *branch_queue; ///< The queue to send requests to the cache what address to get the next IL op from. - RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. // TODO: We need to decide how to distribute the yield. - HtUP /**/ *yield_queues; ///< The queues to push the yield of interpretation into. - RzThreadQueue /**/ *io_request; ///< The queue for read/write requests to the IO layer. - RzThreadQueue /**/ *io_result; ///< The queue for the read/write requests' answers. + HtUP /**/ *yield_queues; ///< The queues to push the yield of interpretation into. + RzThreadQueue /**/ *branch_queue; ///< The queue to send requests to the cache what address to get the next IL op from. + RzThreadQueue /**/ *io_request; ///< The queue for read/write requests to the IO layer. + RzThreadQueue /**/ *io_result; ///< The queue for the read/write requests' answers. + RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. RzAtomicBool *is_running_flag; ///< Flag for the interpreter thread to toggle when done. const RzVector /**/ *ignored_code; /** @@ -262,6 +265,10 @@ typedef struct { RzInterpreterPlugin *plugin; } RzInterpreterSet; +RZ_API RZ_OWN RzInterpreterSharedObjects *rz_interpreter_shared_objects_new(size_t instance_id); +RZ_API void rz_interpreter_shared_objects_fini(RZ_NULLABLE RZ_BORROW RzInterpreterSharedObjects *so); +RZ_API void rz_interpreter_shared_objects_free(RZ_NULLABLE RZ_OWN RzInterpreterSharedObjects *so); + RZ_API void rz_interpreter_il_bb_free(RZ_NULLABLE RZ_OWN RzInterpreterILBB *il_bb); RZ_API void rz_interpreter_insn_pkt_free(RZ_NULLABLE RZ_OWN RzInterpreterInsnPkt *pkt); @@ -281,11 +288,11 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, RZ_NONNULL RZ_OWN RzInterpreterAbstrState *state, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *addr_queue, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, - RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_request, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *branch_queue, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, + RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_request, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag, RZ_NONNULL RZ_OWN RzVector /**/ *entry_points, RZ_NONNULL const RzVector /**/ *ignored_code); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 0cf8f5a1134..6a61d035f72 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -31,6 +31,7 @@ #include #include #include +#include RZ_LIB_VERSION(rz_inquiry); @@ -188,7 +189,7 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL co return false; } -static void handle_io_request(RzCore *core, RzPVector /**/ *il_mems, RzInterpreterIORequest *io_req, RZ_OUT RzInterpreterIOResult *io_res) { +static void handle_io_request(RzCore *core, RzPVector /**/ *il_mems, const RzInterpreterIORequest *io_req, RZ_OUT RzInterpreterIOResult *io_res) { RZ_LOG_DEBUG("INQUIRY: Received IO %s request: mem:%" PFMTSZd " 0x%" PFMT64x "\n", io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", io_req->mem_idx, @@ -219,12 +220,12 @@ static bool setup_queues(RzCore *core, RZ_OUT RzThreadQueue **il_queue, RZ_OUT RzThreadQueue **io_request_q, RZ_OUT RzThreadQueue **io_result_q, - RZ_OUT RzThreadQueue **addr_queue, + RZ_OUT RzThreadQueue **branch_queue, RZ_OUT HtUP **yield_queues) { *il_queue = NULL; *io_request_q = NULL; *io_result_q = NULL; - *addr_queue = NULL; + *branch_queue = NULL; *yield_queues = NULL; RzPVector /**/ *boundaries = NULL; @@ -249,8 +250,8 @@ static bool setup_queues(RzCore *core, // The address queue. It is the queue the interpreter can request new Effects. // Of course, currently there is only a single one for the prototype. // In practice there would be one for each interpreter instance. - *addr_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, NULL); - if (!addr_queue) { + *branch_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, NULL); + if (!branch_queue) { goto error_free; } @@ -299,7 +300,7 @@ static bool setup_queues(RzCore *core, rz_th_queue_free(*il_queue); rz_th_queue_free(*io_request_q); rz_th_queue_free(*io_result_q); - rz_th_queue_free(*addr_queue); + rz_th_queue_free(*branch_queue); return false; } @@ -365,26 +366,36 @@ static bool get_branch_targets(RzCore *core, RzSetU *branch_targets, RzVector /* return true; } -static bool handle_yields(RzCore *core, HtUP *yield_queues) { +static bool handle_yields(RzCore *core, RzInterpreterSet *iset, HtUP *yield_queues) { RzInterpreterYieldQueue *q_xrefs = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); if (!rz_th_queue_is_empty(q_xrefs->yield_queue)) { - RzAnalysisXRef *xref = NULL; - if (!rz_th_queue_pop(q_xrefs->yield_queue, false, (void **)&xref) || !xref) { + RzInterpreterSharedObjects *so = NULL; + if (!rz_th_queue_pop(q_xrefs->yield_queue, false, (void **)&so) || !so) { + rz_th_lock_leave(iset->state->shared_obj->received); return false; } - rz_inquiry_add_xref(core->inquiry, xref); - rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, xref->type); - RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)); + rz_inquiry_add_xref(core->inquiry, &so->xref); + rz_analysis_xrefs_set(core->analysis, so->xref.from, so->xref.to, so->xref.type); + RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", so->xref.from, so->xref.to, rz_analysis_ref_type_tostring(so->xref.type)); + rz_th_lock_leave(so->received); } RzInterpreterYieldQueue *q_calls = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, NULL); if (!rz_th_queue_is_empty(q_calls->yield_queue)) { - RzAnalysisCallCandidate *cc = NULL; - if (!rz_th_queue_pop(q_calls->yield_queue, false, (void **)&cc) || !cc) { + RzInterpreterSharedObjects *so = NULL; + if (!rz_th_queue_pop(q_calls->yield_queue, false, (void **)&so) || !so) { + rz_th_lock_leave(iset->state->shared_obj->received); return false; } RzAnalysisCallCandidate *cc_clone = RZ_NEW0(RzAnalysisCallCandidate); - memcpy(cc_clone, cc, sizeof(RzAnalysisCallCandidate)); + if (ht_up_find(core->inquiry->call_candidates, cc_clone->bb_addr, NULL)) { + rz_th_lock_leave(iset->state->shared_obj->received); + return true; + } + + memcpy(cc_clone, &so->call_cand, sizeof(RzAnalysisCallCandidate)); + rz_th_lock_leave(iset->state->shared_obj->received); + if (ht_up_update(core->inquiry->call_candidates, cc_clone->bb_addr, cc_clone)) { RZ_LOG_DEBUG("Overwrote a call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); } else { @@ -450,7 +461,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, bool return_code = true; RzThreadQueue *io_request_q = NULL; RzThreadQueue *io_result_q = NULL; - RzThreadQueue *addr_queue = NULL; + RzThreadQueue *branch_queue = NULL; HtUP *yield_queues = NULL; RzAtomicBool *is_running = rz_atomic_bool_new(true); RzInterpreterAbstrState *abstr_state = NULL; @@ -466,7 +477,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_cons_push(); - if (!setup_queues(core, &il_queue, &io_request_q, &io_result_q, &addr_queue, &yield_queues)) { + if (!setup_queues(core, &il_queue, &io_request_q, &io_result_q, &branch_queue, &yield_queues)) { return_code = false; goto error_free; } @@ -529,10 +540,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, analysis_vm->reg_binding); } - RZ_LOG_DEBUG("INQUIRY: Enforce enabling IO cache.\n"); - const char *io_cache_opt = rz_config_get(core->config, "io.cache"); - rz_config_set(core->config, "io.cache", "true"); - // Bundle all the queues into one object to pass it to the thread. // Later we would pass a unique iset to each interpreter with // the required queues only. @@ -548,7 +555,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // But in general the whole thing should run without RzCore. prototype->p_interpreter, abstr_state, - addr_queue, + branch_queue, il_queue, yield_queues, io_request_q, @@ -569,10 +576,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_LOG_DEBUG("INQUIRY: Start main interpretation thread.\n"); interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); - // Poor man's shared memory. - RzInterpreterIOResult _io_res = { 0 }; - RzInterpreterIOResult *io_res = &_io_res; - // From here on, the code plays the role of the cache, IO handler, // and yield consumer. // - Waiting for new Effects to be requested and sending them. @@ -598,24 +601,25 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // caches them. { if (!rz_th_queue_is_empty(iset->branch_queue)) { - RzInterpreterBranch *branch = NULL; - if (!rz_th_queue_pop(iset->branch_queue, false, (void **)&branch) || !branch) { + RzInterpreterSharedObjects *so = NULL; + if (!rz_th_queue_pop(iset->branch_queue, false, (void **)&so) || !so) { + rz_th_lock_leave(iset->state->shared_obj->received); rz_warn_if_reached(); break; } - ut64 alt_addr = branch->alt_target; + ut64 alt_addr = so->branch.alt_target; if (alt_addr) { - branch->alt_target = 0; + so->branch.alt_target = 0; } - RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x " (alt: 0x%" PFMT64x ")\n", branch->target_addr, alt_addr); - const RzInterpreterILBB *bb = get_il_bb(core, il_cache, alt_addr ? alt_addr : branch->target_addr); + RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x " (alt: 0x%" PFMT64x ")\n", so->branch.target_addr, alt_addr); + const RzInterpreterILBB *bb = get_il_bb(core, il_cache, alt_addr ? alt_addr : so->branch.target_addr); if (!bb) { // Delete the address from the branch targets. // This is currently necessary as a work around, because if the interpreter // fails before interpreting the address, it is added again as next entry point. // Giving an endless loop. // One of the design thingies to fix in the proper implementation. - rz_set_u_delete(branch_targets, branch->target_addr); + rz_set_u_delete(branch_targets, so->branch.target_addr); if (alt_addr) { rz_set_u_delete(branch_targets, alt_addr); } @@ -626,16 +630,19 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_th_queue_close(iset->branch_queue); rz_th_queue_close(iset->il_queue); bb_decode_failed = true; + rz_th_lock_leave(so->received); break; } rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, bb->bb_addr, bb->size); if (alt_addr) { // Add a dummy basic block at the address the call originally jumped to. // This is the basic block for the imported function. - rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, branch->target_addr, 1); - rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); + rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, so->branch.target_addr, 1); + rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, so->branch.branching_bb_addr, so->branch.target_addr); } - rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); + rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, so->branch.branching_bb_addr, so->branch.target_addr); + rz_th_lock_leave(so->received); + rz_th_queue_push(iset->il_queue, (void *)bb, true); } } @@ -651,14 +658,21 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // Because this is not yet implemented, there is only one interpreter thread for now. { if (!rz_th_queue_is_empty(io_request_q)) { - RzInterpreterIORequest *io_req = NULL; - if (!rz_th_queue_pop(io_request_q, false, (void **)&io_req) || !io_req) { + RzInterpreterSharedObjects *so = NULL; + if (!rz_th_queue_pop(io_request_q, false, (void **)&so) || !so) { rz_atomic_bool_set(is_running, false); + rz_th_lock_leave(iset->state->shared_obj->received); rz_warn_if_reached(); break; } - handle_io_request(core, &analysis_vm->vm->vm_memory, io_req, io_res); - rz_th_queue_push(io_result_q, io_res, true); + handle_io_request(core, + &analysis_vm->vm->vm_memory, + &so->io_req, &so->io_res); + rz_th_lock_leave(so->received); + + rz_th_lock_enter(so->received); + rz_th_queue_push(io_result_q, so, true); + // Don't leave collection lock. Consumer will unlock it after it collected. } } @@ -669,7 +683,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // This part plays the role of a yield consumer. // In our prototype it only receives xrefs and call candidates. { - if (!handle_yields(core, yield_queues)) { + if (!handle_yields(core, iset, yield_queues)) { rz_atomic_bool_set(is_running, false); break; } @@ -680,6 +694,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_th_queue_close(io_result_q); rz_th_queue_close(iset->branch_queue); rz_th_queue_close(iset->il_queue); + rz_th_lock_leave(iset->state->shared_obj->received); RZ_LOG_DEBUG("INQUIRY: Wait for join\n"); rz_th_wait(interpr_th); @@ -693,8 +708,10 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, } break; } - // Clear shared objects to not have any left overs in the next run. - memset((ut8 *)iset->state->shared_obj, 0, sizeof(RzInterpreterSharedObjects)); + + rz_th_lock_enter(iset->state->shared_obj->received); + rz_interpreter_shared_objects_fini(iset->state->shared_obj); + // Open queue again, so the interpretation can start at another // jump target again. rz_th_queue_open(io_request_q); @@ -767,8 +784,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_LOG_DEBUG("INQUIRY: Done\n"); - rz_config_set(core->config, "io.cache", io_cache_opt); - // Wait for thread to finish before cleaning. error_free: RZ_LOG_DEBUG("INQUIRY: Close queues\n"); @@ -783,7 +798,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, if (!iset) { // Ownership of all those objects wasn't yet passed to the iset. - rz_th_queue_free(addr_queue); + rz_th_queue_free(branch_queue); rz_th_queue_free(il_queue); rz_th_queue_free(io_request_q); rz_th_queue_free(io_result_q); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 79097a0555b..4ae0bd35744 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -122,7 +122,8 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( state->locals = ht_up_new(NULL, free); state->lets = ht_up_new(NULL, free); state->il_config = il_config; - state->shared_obj = RZ_NEW0(RzInterpreterSharedObjects); + // TODO: Instance Id + state->shared_obj = rz_interpreter_shared_objects_new(0); return state; } @@ -149,7 +150,7 @@ RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbst rz_analysis_il_config_free(state->il_config); } if (state->shared_obj) { - free(state->shared_obj); + rz_interpreter_shared_objects_free(state->shared_obj); } free(state); } @@ -185,6 +186,40 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { free(iset); } + +RZ_API RZ_OWN RzInterpreterSharedObjects *rz_interpreter_shared_objects_new(size_t instance_id) { + RzInterpreterSharedObjects *so = RZ_NEW0(RzInterpreterSharedObjects); + if (!so) { + return NULL; + } + so->instance_id = instance_id; + so->received = rz_th_lock_new(false); + if (!so->received) { + free(so); + return NULL; + } + return so; +} + +RZ_API void rz_interpreter_shared_objects_fini(RZ_NULLABLE RZ_BORROW RzInterpreterSharedObjects *so) { + if (!so) { + return; + } + rz_th_lock_free(so->received); + size_t instance_id = so->instance_id; + memset(so, 0, sizeof(RzInterpreterSharedObjects)); + so->instance_id = instance_id; + so->received = rz_th_lock_new(false); +} + +RZ_API void rz_interpreter_shared_objects_free(RZ_NULLABLE RZ_OWN RzInterpreterSharedObjects *so) { + if (!so) { + return; + } + rz_th_lock_free(so->received); + free(so); +} + /** * \brief Initializes a new RzInterpreterSet and returns it. * If it fails, all arguments are freed. @@ -197,11 +232,11 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, RZ_NONNULL RZ_OWN RzInterpreterAbstrState *state, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *branch_queue, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, - RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_request, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *branch_queue, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, + RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_request, + RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag, RZ_NONNULL RZ_OWN RzVector /**/ *entry_points, RZ_NONNULL const RzVector /**/ *ignored_code) { @@ -265,7 +300,8 @@ static bool choose_next_pc(RzInterpreterSet *iset, // RZ_LOG_DEBUG("%s", s); // free(s); - RzInterpreterBranch *shared_branch = &iset->state->shared_obj->branch; + rz_th_lock_enter(iset->state->shared_obj->received); + RzInterpreterBranch *branch = &iset->state->shared_obj->branch; bool has_succsessor = true; // Determine successors and increase the reference counts for the current out state. @@ -280,30 +316,29 @@ static bool choose_next_pc(RzInterpreterSet *iset, has_succsessor = !rz_vector_empty(tmp_succ_addr); // Request the successor effects over the queue. while (!rz_vector_empty(tmp_succ_addr)) { - rz_vector_pop_front(tmp_succ_addr, &shared_branch->target_addr); - if (shared_branch->target_addr == UT64_MAX || shared_branch->target_addr == 0) { + rz_vector_pop_front(tmp_succ_addr, &branch->target_addr); + if (branch->target_addr == UT64_MAX || branch->target_addr == 0) { RZ_LOG_DEBUG("interpreter: Quit due to invalid PC.\n"); // Obviously wrong address. return false; } - shared_branch->branching_bb_addr = il_bb->bb_addr; - if (jumps_to_ignored_code(iset->ignored_code, shared_branch->target_addr)) { - RZ_LOG_DEBUG("interpreter: tried to jump to ignored code region at 0x%" PFMT64x "\n", shared_branch->target_addr); + branch->branching_bb_addr = il_bb->bb_addr; + if (jumps_to_ignored_code(iset->ignored_code, branch->target_addr)) { + RZ_LOG_DEBUG("interpreter: tried to jump to ignored code region at 0x%" PFMT64x "\n", branch->target_addr); // Ignored code is mostly dynamically linked functions. // Skip to the next following address after the jump. - shared_branch->alt_target = il_bb->bb_addr + il_bb->size; + branch->alt_target = il_bb->bb_addr + il_bb->size; } SuccessorState ss = { - .addr = shared_branch->alt_target ? shared_branch->alt_target : shared_branch->target_addr, + .addr = branch->alt_target ? branch->alt_target : branch->target_addr, .in_state_hash = out_hash }; // The successors are pushed in the same order into the succ_states - // vector, as they are requested over the addr_queue. + // vector, as they are requested over the branch queue. rz_vector_push(succ_states, &ss); - rz_th_queue_push(iset->branch_queue, shared_branch, true); - // TODO: Race condition: - // Multiple jump targets could overwrite the value in shared_addr before it is read. + rz_th_queue_push(iset->branch_queue, iset->state->shared_obj, true); + // Don't leave collection lock. Consumer will unlock it after it collected. } return has_succsessor; } @@ -357,9 +392,10 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { goto pre_loop_error; } - RzInterpreterBranch *shared_branch = &iset->state->shared_obj->branch; - rz_vector_pop_front(iset->entry_points, &shared_branch->target_addr); - if (!plugin->init_state(iset->state, shared_branch->target_addr, plugin_data)) { + rz_th_lock_enter(iset->state->shared_obj->received); + RzInterpreterBranch *branch = &iset->state->shared_obj->branch; + rz_vector_pop_front(iset->entry_points, &branch->target_addr); + if (!plugin->init_state(iset->state, branch->target_addr, plugin_data)) { rz_warn_if_reached(); goto pre_loop_error; } @@ -370,7 +406,9 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { RzInterpreterAbstrState *out_state = NULL; ut64 out_hash = 0; - rz_th_queue_push(iset->branch_queue, shared_branch, true); + rz_th_queue_push(iset->branch_queue, iset->state->shared_obj, true); + // Don't leave collection lock. Consumer will unlock it after it collected. + const RzInterpreterILBB *il_bb = NULL; if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { goto pre_loop_error; diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 4a5c1a6cc60..f4d810aeed9 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -13,7 +13,7 @@ static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, RZ_NONNULL const RzInterpreterILBB *il_bb, - RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, + RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_request, RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_result, void *plugin_data) { diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 87b3942841a..0666f21619c 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -13,7 +13,7 @@ bool report_yield_xref( RzInterpreterAbstrState *state, size_t insn_pkt_size, - HtUP /**/ *yield_queues, + HtUP /**/ *yield_queues, ut64 from, const ProtoIntrprAbstrData *to, RzAnalysisXRefType type) { @@ -41,6 +41,8 @@ bool report_yield_xref( ut64 to_addr = rz_bv_to_ut64(to->bv); if (queue->filter(&to_addr, queue->filter_data->io_boundaries)) { + rz_th_lock_enter(state->shared_obj->received); + RzAnalysisXRef *xref = &state->shared_obj->xref; xref->bb_addr = state->bb_addr; xref->from = from; @@ -50,7 +52,8 @@ bool report_yield_xref( // before the previous one was handled. // But this is fine for the prototype. Real implementation needs some kind // of shared memory anyways. - rz_th_queue_push(queue->yield_queue, xref, true); + rz_th_queue_push(queue->yield_queue, state->shared_obj, true); + // Don't leave collection lock. Consumer will unlock it after it collected. } return true; } @@ -60,7 +63,7 @@ bool report_yield_xref( */ bool report_yield_call_candiate( RzInterpreterAbstrState *state, - HtUP /**/ *yield_queues, + HtUP /**/ *yield_queues, ProtoIntrprPluginData *plugin_data) { RzInterpreterYieldQueue *cc_queue = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, NULL); if (!cc_queue) { @@ -68,9 +71,11 @@ bool report_yield_call_candiate( return false; } + rz_th_lock_enter(state->shared_obj->received); RzAnalysisCallCandidate *cc = &state->shared_obj->call_cand; memcpy(cc, &plugin_data->call_cand, sizeof(plugin_data->call_cand)); - rz_th_queue_push(cc_queue->yield_queue, cc, true); + rz_th_queue_push(cc_queue->yield_queue, state->shared_obj, true); + // Don't leave collection lock. Consumer will unlock it after it collected. return true; } @@ -163,30 +168,39 @@ bool store_abstr_data( RzILMemIndex mem_idx, const ProtoIntrprAbstrData *addr, const ProtoIntrprAbstrData *src, - RzThreadQueue /**/ *io_request, - RzThreadQueue /**/ *io_result) { + RzThreadQueue /**/ *io_request, + RzThreadQueue /**/ *io_result) { if (!src->is_concrete) { // Really don't write? return true; } - RzInterpreterIORequest io_req = { 0 }; - io_req.n_bits = rz_bv_len(src->bv); - io_req.mem_idx = mem_idx; - io_req.big_endian = state->il_config->big_endian; + rz_th_lock_enter(state->shared_obj->received); + RzInterpreterIORequest *io_req = &state->shared_obj->io_req; + io_req->n_bits = rz_bv_len(src->bv); + io_req->mem_idx = mem_idx; + io_req->big_endian = state->il_config->big_endian; - io_req.type = RZ_INTERPRETER_IO_WRITE; - io_req.addr = addr->bv; - io_req.st_data = src->bv; + io_req->type = RZ_INTERPRETER_IO_WRITE; + io_req->addr = addr->bv; + io_req->st_data = src->bv; char *bytes = rz_bv_as_hex_string(src->bv, true); - RZ_LOG_DEBUG("Prototype: STORE @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); + RZ_LOG_DEBUG("Prototype: STORE @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req->addr), bytes); free(bytes); - rz_th_queue_push(io_request, &io_req, true); + rz_th_queue_push(io_request, state->shared_obj, true); + // Don't leave collection lock. Consumer will unlock it after it collected. + // Wait for write being done. - RzInterpreterIOResult *io_res = NULL; - rz_th_queue_pop(io_result, false, (void **)&io_res); - return io_res ? io_res->req_ok : false; + RzInterpreterSharedObjects *so = NULL; + if (!rz_th_queue_pop(io_result, false, (void **)&so) || !so) { + rz_th_lock_leave(state->shared_obj->received); + return false; + }; + bool write_ok = so->io_res.req_ok; + rz_th_lock_leave(so->received); + + return write_ok; } bool load_abstr_data( @@ -195,32 +209,42 @@ bool load_abstr_data( const ProtoIntrprAbstrData *addr, size_t n_bits, RZ_OUT ProtoIntrprAbstrData *out, - RzThreadQueue /**/ *io_request, - RzThreadQueue /**/ *io_result) { - RzInterpreterIORequest io_req = { 0 }; + RzThreadQueue /**/ *io_request, + RzThreadQueue /**/ *io_result) { + + rz_th_lock_enter(state->shared_obj->received); + RzInterpreterIORequest *io_req = &state->shared_obj->io_req; rz_bv_cast_inplace(out->bv, n_bits, 0); - io_req.type = RZ_INTERPRETER_IO_READ; - io_req.addr = addr->bv; - io_req.ld_data = out->bv; - io_req.mem_idx = mem_idx; - io_req.n_bits = n_bits; - io_req.big_endian = state->il_config->big_endian; - rz_th_queue_push(io_request, &io_req, true); + io_req->type = RZ_INTERPRETER_IO_READ; + io_req->addr = addr->bv; + io_req->ld_data = out->bv; + io_req->mem_idx = mem_idx; + io_req->n_bits = n_bits; + io_req->big_endian = state->il_config->big_endian; + ut64 req_addr = rz_bv_to_ut64(io_req->addr); + + rz_th_queue_push(io_request, state->shared_obj, true); + // Don't leave collection lock. Consumer will unlock it after it collected. + // Wait for load being done. - RzInterpreterIOResult *io_res = NULL; - if (!rz_th_queue_pop(io_result, false, (void **)&io_res) || !io_res) { + RzInterpreterSharedObjects *so = NULL; + if (!rz_th_queue_pop(io_result, false, (void **)&so) || !so) { + rz_th_lock_leave(state->shared_obj->received); return false; } - if (!io_res->req_ok) { + if (!so->io_res.req_ok) { RZ_LOG_WARN("Prototype: Failed to read correct number of bytes. Requested: 0x%" PFMTSZx " Received: 0x%" PFMT32x " bits.\n", n_bits, rz_bv_len(out->bv)); + rz_th_lock_leave(so->received); return false; } + rz_th_lock_leave(so->received); + out->is_concrete = true; char *bytes = rz_bv_as_hex_string(out->bv, true); - RZ_LOG_DEBUG("Prototype: READ @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); + RZ_LOG_DEBUG("Prototype: READ @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, req_addr, bytes); free(bytes); return true; } diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 5347090f48f..bda87b8fd5c 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -74,44 +74,44 @@ bool store_abstr_data( RzILMemIndex mem_idx, const ProtoIntrprAbstrData *addr, const ProtoIntrprAbstrData *src, - RzThreadQueue /**/ *io_request, - RzThreadQueue /**/ *io_result); + RzThreadQueue /**/ *io_request, + RzThreadQueue /**/ *io_result); bool load_abstr_data( RzInterpreterAbstrState *state, RzILMemIndex mem_idx, const ProtoIntrprAbstrData *addr, size_t n_bits, RZ_OUT ProtoIntrprAbstrData *out, - RzThreadQueue /**/ *io_request, - RzThreadQueue /**/ *io_result); + RzThreadQueue /**/ *io_request, + RzThreadQueue /**/ *io_result); RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, size_t nop_pc_inc, - HtUP /**/ *yield_queues, - RzThreadQueue /**/ *io_request, - RzThreadQueue /**/ *io_result, + HtUP /**/ *yield_queues, + RzThreadQueue /**/ *io_request, + RzThreadQueue /**/ *io_result, ProtoIntrprPluginData *plugin_data); RZ_IPI bool interpreter_prototype_eval_pure( RzInterpreterAbstrState *state, const RzILOpPure *pure, RZ_OUT ProtoIntrprAbstrData *out, - HtUP /**/ *yield_queues, - RzThreadQueue /**/ *io_request, - RzThreadQueue /**/ *io_result, + HtUP /**/ *yield_queues, + RzThreadQueue /**/ *io_request, + RzThreadQueue /**/ *io_result, ProtoIntrprPluginData *plugin_data); bool report_yield_xref( RzInterpreterAbstrState *state, size_t insn_pkt_size, - HtUP /**/ *yield_queues, + HtUP /**/ *yield_queues, ut64 from, const ProtoIntrprAbstrData *to, RzAnalysisXRefType type); bool report_yield_call_candiate( RzInterpreterAbstrState *state, - HtUP /**/ *yield_queues, + HtUP /**/ *yield_queues, ProtoIntrprPluginData *plugin_data); bool set_pc(RzInterpreterAbstrState *state, ut64 pc, diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index cdadcbc7fed..5f3d5d213bb 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -18,9 +18,9 @@ static bool value_indicates_ret_addr_write(RzInterpreterAbstrState *state, Proto RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, const RzILOpEffect *effect, size_t insn_pkt_size, - HtUP /**/ *yield_queues, - RzThreadQueue /**/ *io_request, - RzThreadQueue /**/ *io_result, + HtUP /**/ *yield_queues, + RzThreadQueue /**/ *io_request, + RzThreadQueue /**/ *io_result, ProtoIntrprPluginData *plugin_data) { STACK_ABSTR_DATA_OUT(eval_out); ProtoIntrprAbstrData *pc = AD(state->pc->abstr_data); diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 272d2262742..28b35fec600 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -12,9 +12,9 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzInterpreterAbstrState *state, const RzILOpPure *pure, RZ_OUT ProtoIntrprAbstrData *out, - HtUP /**/ *yield_queues, - RzThreadQueue /**/ *io_request, - RzThreadQueue /**/ *io_result, + HtUP /**/ *yield_queues, + RzThreadQueue /**/ *io_request, + RzThreadQueue /**/ *io_result, ProtoIntrprPluginData *plugin_data) { switch (pure->code) { default: From 9cfa1f4203767cf002ab609660555883044eb706 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 26 Mar 2026 18:17:10 +0100 Subject: [PATCH 232/334] Fix invalid addition of size --- librz/inquiry/inquiry_helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c index edd5f28e3a0..49f470fb886 100644 --- a/librz/inquiry/inquiry_helpers.c +++ b/librz/inquiry/inquiry_helpers.c @@ -68,8 +68,8 @@ RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana changes_cf = rz_analysis_op_changes_control_flow(&op); } RZ_LOG_DEBUG("\t0x%" PFMT64x "\n", addr); - rz_analysis_op_fini(&op); addr += op.size; + rz_analysis_op_fini(&op); rz_mem_memzero(buf, max_read_size); if (sparc_add_delayed_insn) { // Instruction was added, now the BB is complete. From 60d201c2536cabf47941ab8f46dbe308f46b5a50 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 26 Mar 2026 19:01:23 +0100 Subject: [PATCH 233/334] Move the queue initialization into the rz_interpreter_set_new() --- librz/include/rz_inquiry/rz_interpreter.h | 7 +- librz/inquiry/inquiry.c | 149 ++++------------------ librz/inquiry/interpreter/interpreter.c | 109 ++++++++++++++-- 3 files changed, 125 insertions(+), 140 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index f19efb2f0e3..ee3bfbc3c11 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -288,11 +288,8 @@ RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpre RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, RZ_NONNULL RZ_OWN RzInterpreterAbstrState *state, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *branch_queue, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, - RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_request, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, + RZ_OWN RzPVector /**/ *sections, + RzInterpreterYieldFilter yield_filter, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag, RZ_NONNULL RZ_OWN RzVector /**/ *entry_points, RZ_NONNULL const RzVector /**/ *ignored_code); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 6a61d035f72..732a738b7b6 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -7,14 +7,11 @@ #include "rz_analysis.h" #include "rz_bin.h" -#include "rz_config.h" #include "rz_cons.h" #include "rz_il/definitions/mem.h" -#include "rz_il/rz_il_validate.h" #include "rz_il/rz_il_vm.h" #include "rz_inquiry/rz_interpreter.h" #include "rz_inquiry_plugins.h" -#include "rz_io.h" #include "rz_th.h" #include "rz_types.h" #include "rz_util/ht_pp.h" @@ -216,94 +213,6 @@ static void handle_io_request(RzCore *core, RzPVector /**/ *il_mems, rz_str_bool(io_res->req_ok)); } -static bool setup_queues(RzCore *core, - RZ_OUT RzThreadQueue **il_queue, - RZ_OUT RzThreadQueue **io_request_q, - RZ_OUT RzThreadQueue **io_result_q, - RZ_OUT RzThreadQueue **branch_queue, - RZ_OUT HtUP **yield_queues) { - *il_queue = NULL; - *io_request_q = NULL; - *io_result_q = NULL; - *branch_queue = NULL; - *yield_queues = NULL; - - RzPVector /**/ *boundaries = NULL; - RzInterpreterYieldQueue *yield_queue = NULL; - // The queue to pass the Effects to the interpreter. - // This is only one queue for the prototype. - // In practice it would be one for each interpreter. - *il_queue = rz_th_queue_new(RZ_INTERPRETER_IL_QUEUE_SIZE, NULL); - if (!il_queue) { - goto error_free; - } - - // Setup the IO queues. Each interpreter instance needs it's own queue at - // for writing IO. Because the writing is done on the IO cache, and each - // instance needs its own cache. - *io_request_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); - *io_result_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); - if (!io_request_q || !io_result_q) { - goto error_free; - } - - // The address queue. It is the queue the interpreter can request new Effects. - // Of course, currently there is only a single one for the prototype. - // In practice there would be one for each interpreter instance. - *branch_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, NULL); - if (!branch_queue) { - goto error_free; - } - - // Multiple yield queues can be used by a single interpreter. - // E.g. if the interpreter has a complex abstract memory model - // for stack, heap and constant values. - // Then it can produce three kind of yields. - *yield_queues = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_yield_queue_free); - if (!yield_queues) { - goto error_free; - } - - // Here we build the filter for the yield queue. - // The prototype generates constant xrefs. - // So the filter checks the generated xrefs, if they are within the IO map - // boundaries. - boundaries = rz_bin_object_get_sections(core->bin->cur->o); - if (!boundaries) { - goto error_free; - } - - // These yield queues can be shared between different interpreters. - // So we have one yield queue for each yield type. - - // Xref yield queue. - RzInterpreterYieldKind yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; - yield_queue = rz_interpreter_yield_queue_new( - yield_kind, - (RzInterpreterYieldFilter)rz_inquiry_xref_interpreter_filter, - boundaries); - if (!yield_queue) { - goto error_free; - } - ht_up_insert(*yield_queues, yield_kind, yield_queue); - - yield_kind = RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE; - yield_queue = rz_interpreter_yield_queue_new(yield_kind, NULL, NULL); - if (!yield_queue) { - goto error_free; - } - ht_up_insert(*yield_queues, yield_kind, yield_queue); - return true; - -error_free: - ht_up_free(*yield_queues); - rz_th_queue_free(*il_queue); - rz_th_queue_free(*io_request_q); - rz_th_queue_free(*io_result_q); - rz_th_queue_free(*branch_queue); - return false; -} - RZ_API bool rz_inquiry_get_fcn_symbol_addr(RzCore *core, RZ_OUT RzSetU *symbol_targets) { rz_return_val_if_fail(core && symbol_targets, false); RzPVector /**/ *sections = rz_bin_object_get_sections(core->bin->cur->o); @@ -459,15 +368,10 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_NONNULL const RzVector /**/ *ignored_code) { // All the things we need bool return_code = true; - RzThreadQueue *io_request_q = NULL; - RzThreadQueue *io_result_q = NULL; - RzThreadQueue *branch_queue = NULL; - HtUP *yield_queues = NULL; RzAtomicBool *is_running = rz_atomic_bool_new(true); RzInterpreterAbstrState *abstr_state = NULL; RzInterpreterSet *iset = NULL; HtUP *il_cache = NULL; - RzThreadQueue *il_queue = NULL; RzThread *interpr_th = NULL; RzBuffer *io_buf = rz_buf_new_with_io(&core->analysis->iob); RzAnalysisILVM *analysis_vm = NULL; @@ -477,8 +381,12 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_cons_push(); - if (!setup_queues(core, &il_queue, &io_request_q, &io_result_q, &branch_queue, &yield_queues)) { - return_code = false; + // Here we build the filter for the yield queue. + // The prototype generates constant xrefs. + // So the filter checks the generated xrefs, if they are within the IO map + // boundaries. + RzPVector /**/ *sections = rz_bin_object_get_sections(core->bin->cur->o); + if (!sections) { goto error_free; } @@ -555,11 +463,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // But in general the whole thing should run without RzCore. prototype->p_interpreter, abstr_state, - branch_queue, - il_queue, - yield_queues, - io_request_q, - io_result_q, + sections, + (RzInterpreterYieldFilter)rz_inquiry_xref_interpreter_filter, is_running, rz_vector_clone(entry_points), ignored_code); @@ -625,8 +530,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, } // Signal interpreter the lifting failed. rz_atomic_bool_set(is_running, false); - rz_th_queue_close(io_request_q); - rz_th_queue_close(io_result_q); + rz_th_queue_close(iset->io_request); + rz_th_queue_close(iset->io_result); rz_th_queue_close(iset->branch_queue); rz_th_queue_close(iset->il_queue); bb_decode_failed = true; @@ -657,9 +562,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // (one for each interpreter instance). // Because this is not yet implemented, there is only one interpreter thread for now. { - if (!rz_th_queue_is_empty(io_request_q)) { + if (!rz_th_queue_is_empty(iset->io_request)) { RzInterpreterSharedObjects *so = NULL; - if (!rz_th_queue_pop(io_request_q, false, (void **)&so) || !so) { + if (!rz_th_queue_pop(iset->io_request, false, (void **)&so) || !so) { rz_atomic_bool_set(is_running, false); rz_th_lock_leave(iset->state->shared_obj->received); rz_warn_if_reached(); @@ -671,7 +576,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_th_lock_leave(so->received); rz_th_lock_enter(so->received); - rz_th_queue_push(io_result_q, so, true); + rz_th_queue_push(iset->io_result, so, true); // Don't leave collection lock. Consumer will unlock it after it collected. } } @@ -683,15 +588,15 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // This part plays the role of a yield consumer. // In our prototype it only receives xrefs and call candidates. { - if (!handle_yields(core, iset, yield_queues)) { + if (!handle_yields(core, iset, iset->yield_queues)) { rz_atomic_bool_set(is_running, false); break; } } } - rz_th_queue_close(io_request_q); - rz_th_queue_close(io_result_q); + rz_th_queue_close(iset->io_request); + rz_th_queue_close(iset->io_result); rz_th_queue_close(iset->branch_queue); rz_th_queue_close(iset->il_queue); rz_th_lock_leave(iset->state->shared_obj->received); @@ -714,13 +619,13 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // Open queue again, so the interpretation can start at another // jump target again. - rz_th_queue_open(io_request_q); - rz_th_queue_open(io_result_q); + rz_th_queue_open(iset->io_request); + rz_th_queue_open(iset->io_result); rz_th_queue_open(iset->branch_queue); rz_th_queue_open(iset->il_queue); // Clear queues from any left overs of previous runs. - rz_list_free(rz_th_queue_pop_all(io_result_q)); - rz_list_free(rz_th_queue_pop_all(io_request_q)); + rz_list_free(rz_th_queue_pop_all(iset->io_result)); + rz_list_free(rz_th_queue_pop_all(iset->io_request)); rz_list_free(rz_th_queue_pop_all(iset->il_queue)); rz_list_free(rz_th_queue_pop_all(iset->branch_queue)); @@ -791,19 +696,19 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_set_u_free(branch_targets); rz_buf_free(io_buf); rz_analysis_il_vm_free(analysis_vm); - rz_th_queue_close(io_request_q); - rz_th_queue_close(io_result_q); + rz_th_queue_close(iset->io_request); + rz_th_queue_close(iset->io_result); rz_th_queue_close(iset->branch_queue); rz_th_queue_close(iset->il_queue); if (!iset) { // Ownership of all those objects wasn't yet passed to the iset. - rz_th_queue_free(branch_queue); - rz_th_queue_free(il_queue); - rz_th_queue_free(io_request_q); - rz_th_queue_free(io_result_q); + rz_th_queue_free(iset->branch_queue); + rz_th_queue_free(iset->il_queue); + rz_th_queue_free(iset->io_request); + rz_th_queue_free(iset->io_result); // yield_queues frees each individual queue as well. - ht_up_free(yield_queues); + ht_up_free(iset->yield_queues); rz_atomic_bool_free(is_running); rz_interpreter_abstr_state_free(abstr_state); } else { diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 4ae0bd35744..22647ef8be2 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -220,39 +220,122 @@ RZ_API void rz_interpreter_shared_objects_free(RZ_NULLABLE RZ_OWN RzInterpreterS free(so); } + +static bool setup_queues( + RZ_OWN RzPVector /**/ *sections, + RzInterpreterYieldFilter yield_filter, + RZ_OUT RzThreadQueue **il_queue, + RZ_OUT RzThreadQueue **io_request_q, + RZ_OUT RzThreadQueue **io_result_q, + RZ_OUT RzThreadQueue **branch_queue, + RZ_OUT HtUP **yield_queues) { + *il_queue = NULL; + *io_request_q = NULL; + *io_result_q = NULL; + *branch_queue = NULL; + *yield_queues = NULL; + + RzInterpreterYieldQueue *yield_queue = NULL; + // The queue to pass the Effects to the interpreter. + // This is only one queue for the prototype. + // In practice it would be one for each interpreter. + *il_queue = rz_th_queue_new(RZ_INTERPRETER_IL_QUEUE_SIZE, NULL); + if (!il_queue) { + goto error_free; + } + + // Setup the IO queues. Each interpreter instance needs it's own queue at + // for writing IO. Because the writing is done on the IO cache, and each + // instance needs its own cache. + *io_request_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); + *io_result_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); + if (!io_request_q || !io_result_q) { + goto error_free; + } + + // The address queue. It is the queue the interpreter can request new Effects. + // Of course, currently there is only a single one for the prototype. + // In practice there would be one for each interpreter instance. + *branch_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, NULL); + if (!branch_queue) { + goto error_free; + } + + // Multiple yield queues can be used by a single interpreter. + // E.g. if the interpreter has a complex abstract memory model + // for stack, heap and constant values. + // Then it can produce three kind of yields. + *yield_queues = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_yield_queue_free); + if (!yield_queues) { + goto error_free; + } + + // These yield queues can be shared between different interpreters. + // So we have one yield queue for each yield type. + + // Xref yield queue. + RzInterpreterYieldKind yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; + yield_queue = rz_interpreter_yield_queue_new( + yield_kind, + yield_filter, + sections); + if (!yield_queue) { + goto error_free; + } + ht_up_insert(*yield_queues, yield_kind, yield_queue); + + yield_kind = RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE; + yield_queue = rz_interpreter_yield_queue_new(yield_kind, NULL, NULL); + if (!yield_queue) { + goto error_free; + } + ht_up_insert(*yield_queues, yield_kind, yield_queue); + return true; + +error_free: + ht_up_free(*yield_queues); + rz_th_queue_free(*il_queue); + rz_th_queue_free(*io_request_q); + rz_th_queue_free(*io_result_q); + rz_th_queue_free(*branch_queue); + return false; +} + + /** * \brief Initializes a new RzInterpreterSet and returns it. * If it fails, all arguments are freed. - * - * \param plugin The interpreter plugin. - * \param request_il The request queue. - * \param receive_il The receive queue. - * \param yield_queues The yield queues. */ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, RZ_NONNULL RZ_OWN RzInterpreterAbstrState *state, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *branch_queue, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *il_queue, - RZ_NONNULL RZ_OWN HtUP /**/ *yield_queues, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_request, - RZ_NONNULL RZ_OWN RzThreadQueue /**/ *io_result, + RZ_OWN RzPVector /**/ *sections, + RzInterpreterYieldFilter yield_filter, RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag, RZ_NONNULL RZ_OWN RzVector /**/ *entry_points, RZ_NONNULL const RzVector /**/ *ignored_code) { - rz_return_val_if_fail(plugin && state && branch_queue && il_queue && yield_queues && io_request && io_result && is_running_flag && entry_points && ignored_code, NULL); + rz_return_val_if_fail(plugin && state && is_running_flag && entry_points && ignored_code, NULL); RzInterpreterSet *set = RZ_NEW0(RzInterpreterSet); if (!set) { return false; } + RzThreadQueue *io_request_q = NULL; + RzThreadQueue *io_result_q = NULL; + RzThreadQueue *branch_queue = NULL; + RzThreadQueue *il_queue = NULL; + HtUP *yield_queues = NULL; + if (!setup_queues(sections, yield_filter, &il_queue, &io_request_q, &io_result_q, &branch_queue, &yield_queues)) { + return false; + } + set->plugin = plugin; set->state = state; set->il_queue = il_queue; set->branch_queue = branch_queue; set->yield_queues = yield_queues; - set->io_request = io_request; - set->io_result = io_result; + set->io_request = io_request_q; + set->io_result = io_result_q; set->is_running_flag = is_running_flag; set->entry_points = entry_points; set->ignored_code = ignored_code; From c3cb4671b7d2e7a14c95b92be8f4fa936a9d276e Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 28 Mar 2026 19:50:30 +0100 Subject: [PATCH 234/334] Reduce number of arguments passed. --- librz/include/rz_inquiry/rz_interpreter.h | 11 ++- librz/inquiry/interpreter/interpreter.c | 19 ++-- .../interpreter/p/interpreter_prototype.c | 15 ++-- librz/inquiry/interpreter/prototype/eval.c | 80 ++++++++--------- librz/inquiry/interpreter/prototype/eval.h | 34 +++---- .../interpreter/prototype/eval_effect.c | 57 ++++++------ .../inquiry/interpreter/prototype/eval_pure.c | 89 +++++++++---------- 7 files changed, 136 insertions(+), 169 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index ee3bfbc3c11..60d715bce60 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -174,6 +174,8 @@ typedef struct { size_t insn_pkt_size; ///< The size of the instruction packet. Used to increment the PC if no JMP occurred. } RzInterpreterInsnPkt; +typedef struct rz_interpreter_set RzInterpreterSet; + typedef struct { const char *name; const char *author; @@ -210,11 +212,8 @@ typedef struct { /** * \brief Evaluates an effect with the mutable state. */ - bool (*eval)(RZ_NONNULL RzInterpreterAbstrState *state, + bool (*eval)(RZ_NONNULL RzInterpreterSet *iset, RZ_NONNULL const RzInterpreterILBB *il_bb, - RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_request, - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_result, void *plugin_data); /** * \brief Determines the next successor addresses from state. @@ -247,7 +246,7 @@ typedef struct { * \brief The set of required queues for an interpreter to run. */ RZ_LIFETIME(RzInquiry) -typedef struct { +struct rz_interpreter_set { RzInterpreterAbstrState *state; ///< The abstract state of the interpreter. // TODO: We need to decide how to distribute the yield. HtUP /**/ *yield_queues; ///< The queues to push the yield of interpretation into. @@ -263,7 +262,7 @@ typedef struct { */ RzVector /**/ *entry_points; RzInterpreterPlugin *plugin; -} RzInterpreterSet; +}; RZ_API RZ_OWN RzInterpreterSharedObjects *rz_interpreter_shared_objects_new(size_t instance_id); RZ_API void rz_interpreter_shared_objects_fini(RZ_NULLABLE RZ_BORROW RzInterpreterSharedObjects *so); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 22647ef8be2..dd6154a0880 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -370,7 +370,6 @@ typedef struct { } SuccessorState; static bool choose_next_pc(RzInterpreterSet *iset, - RzInterpreterAbstrState *out_state, ut64 out_hash, RzVector *tmp_succ_addr, RzVector *succ_states, @@ -388,7 +387,7 @@ static bool choose_next_pc(RzInterpreterSet *iset, bool has_succsessor = true; // Determine successors and increase the reference counts for the current out state. - if (!iset->plugin->successors(out_state, tmp_succ_addr, plugin_data)) { + if (!iset->plugin->successors(iset->state, tmp_succ_addr, plugin_data)) { rz_warn_if_reached(); return false; } @@ -482,11 +481,9 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { rz_warn_if_reached(); goto pre_loop_error; } - RzInterpreterAbstrState *in_state = iset->state; #if RZ_BUILD_DEBUG - ut64 in_hash = plugin->hash_state(in_state, plugin_data); + ut64 in_hash = plugin->hash_state(iset->state, plugin_data); #endif - RzInterpreterAbstrState *out_state = NULL; ut64 out_hash = 0; rz_th_queue_push(iset->branch_queue, iset->state->shared_obj, true); @@ -509,18 +506,16 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { iset->state->bb_addr = il_bb->bb_addr; iset->state->bb_size = il_bb->size; // Evaluate the effect on the input state. - if (!plugin->eval(in_state, il_bb, iset->yield_queues, iset->io_request, iset->io_result, plugin_data)) { + if (!plugin->eval(iset, il_bb, plugin_data)) { RZ_LOG_DEBUG("Eval failed\n"); goto in_loop_error; } - // The input state was (almost always) manipulated by eval(). Rename to clarify. - out_state = in_state; - out_hash = plugin->hash_state(out_state, plugin_data); + out_hash = plugin->hash_state(iset->state, plugin_data); #if RZ_BUILD_DEBUG RZ_LOG_DEBUG("in_hash = 0x%llx, out_hash = 0x%llx\n", in_hash, out_hash); #endif - // Add out_state hash to the reachable states and + // Add output state hash to the reachable states and // set a flag if it was a new state. size_t psize = rz_set_u_size(reachable_states); rz_set_u_add(reachable_states, out_hash); @@ -528,7 +523,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // Determine the successor effects to evaluate. // Only newly reached states are allowed to add successors. - if (!(new_state_reached && choose_next_pc(iset, out_state, out_hash, tmp_succ_addr, succ_states, il_bb, plugin_data))) { + if (!(new_state_reached && choose_next_pc(iset, out_hash, tmp_succ_addr, succ_states, il_bb, plugin_data))) { // No new state or address means we can stop interpreting. // Note, that we can't use the queues as cancel condition because they // are asynchronous and checking them would introduces race conditions. @@ -547,7 +542,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // pointed to an unmapped region. goto in_loop_error; } - if (!plugin->set_pc(in_state, next.addr, plugin_data)) { + if (!plugin->set_pc(iset->state, next.addr, plugin_data)) { rz_warn_if_reached(); // Some error occurred lifting this basic block. Or updating the PC. // Abort execution. diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index f4d810aeed9..f924714f388 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -11,11 +11,8 @@ #define MAX_INVOCATIONS_PER_BB 3 -static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, +static bool eval(RZ_NONNULL RzInterpreterSet *iset, RZ_NONNULL const RzInterpreterILBB *il_bb, - RZ_NONNULL RZ_BORROW HtUP /**/ *yield_queues, - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_request, - RZ_NONNULL RZ_BORROW RzThreadQueue /**/ *io_result, void *plugin_data) { ProtoIntrprPluginData *pdata = plugin_data; @@ -28,7 +25,7 @@ static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, } else if (ic_pc > MAX_INVOCATIONS_PER_BB) { // TODO: Make it configurable RZ_LOG_DEBUG("Reached maximum number of invocations of basic block at 0x%" PFMT64x ". Skipping it.\n", il_bb->bb_addr) - set_pc(state, il_bb->bb_addr + il_bb->size, plugin_data); + set_pc(iset->state, il_bb->bb_addr + il_bb->size, plugin_data); return true; } ht_uu_update(pdata->bb_invocation_count, il_bb->bb_addr, ic_pc + 1); @@ -40,15 +37,15 @@ static bool eval(RZ_NONNULL RzInterpreterAbstrState *state, // Now execute the actual effects of the BB. void **it; rz_pvector_foreach (il_bb->il_ops, it) { - ut64 pc = rz_bv_to_ut64(AD(state->pc->abstr_data)->bv); + ut64 pc = rz_bv_to_ut64(AD(iset->state->pc->abstr_data)->bv); RZ_LOG_DEBUG("Eval PC = 0x%" PFMT64x "\n", pc); RzInterpreterInsnPkt *pkt = *it; - if (!interpreter_prototype_eval_effect(state, pkt->effect, pkt->insn_pkt_size, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_effect(iset, pkt->effect, pkt->insn_pkt_size, plugin_data)) { return false; } - if (pc == rz_bv_to_ut64(AD(state->pc->abstr_data)->bv) && AD(state->pc->abstr_data)->is_concrete) { + if (pc == rz_bv_to_ut64(AD(iset->state->pc->abstr_data)->bv) && AD(iset->state->pc->abstr_data)->is_concrete) { // Instruction did not manipulate the PC. Set it to the next instruction (packet). - set_pc(state, pc + pkt->insn_pkt_size, plugin_data); + set_pc(iset->state, pc + pkt->insn_pkt_size, plugin_data); } } // TODO: Clean up local variables. diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 0666f21619c..d1759b2b7c3 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -11,13 +11,12 @@ #include bool report_yield_xref( - RzInterpreterAbstrState *state, + RzInterpreterSet *iset, size_t insn_pkt_size, - HtUP /**/ *yield_queues, ut64 from, const ProtoIntrprAbstrData *to, RzAnalysisXRefType type) { - RzInterpreterYieldQueue *queue = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); + RzInterpreterYieldQueue *queue = ht_up_find(iset->yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); if (!queue) { rz_warn_if_reached(); return false; @@ -27,7 +26,7 @@ bool report_yield_xref( return true; } if (type == RZ_ANALYSIS_XREF_TYPE_CODE && - RZ_STR_EQ(state->arch_name, "hexagon") && + RZ_STR_EQ(iset->state->arch_name, "hexagon") && from + insn_pkt_size == rz_bv_to_ut64(to->bv)) { // Ugly work around. // Because we don't have RzArch yet the Hexagon plugin adds a JUMP at the @@ -41,10 +40,10 @@ bool report_yield_xref( ut64 to_addr = rz_bv_to_ut64(to->bv); if (queue->filter(&to_addr, queue->filter_data->io_boundaries)) { - rz_th_lock_enter(state->shared_obj->received); + rz_th_lock_enter(iset->state->shared_obj->received); - RzAnalysisXRef *xref = &state->shared_obj->xref; - xref->bb_addr = state->bb_addr; + RzAnalysisXRef *xref = &iset->state->shared_obj->xref; + xref->bb_addr = iset->state->bb_addr; xref->from = from; xref->to = to_addr; xref->type = type; @@ -52,7 +51,7 @@ bool report_yield_xref( // before the previous one was handled. // But this is fine for the prototype. Real implementation needs some kind // of shared memory anyways. - rz_th_queue_push(queue->yield_queue, state->shared_obj, true); + rz_th_queue_push(queue->yield_queue, iset->state->shared_obj, true); // Don't leave collection lock. Consumer will unlock it after it collected. } return true; @@ -62,19 +61,18 @@ bool report_yield_xref( * \brief Report the store of the next PC and report it as possible return point. */ bool report_yield_call_candiate( - RzInterpreterAbstrState *state, - HtUP /**/ *yield_queues, + RzInterpreterSet *iset, ProtoIntrprPluginData *plugin_data) { - RzInterpreterYieldQueue *cc_queue = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, NULL); + RzInterpreterYieldQueue *cc_queue = ht_up_find(iset->yield_queues, RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, NULL); if (!cc_queue) { rz_warn_if_reached(); return false; } - rz_th_lock_enter(state->shared_obj->received); - RzAnalysisCallCandidate *cc = &state->shared_obj->call_cand; + rz_th_lock_enter(iset->state->shared_obj->received); + RzAnalysisCallCandidate *cc = &iset->state->shared_obj->call_cand; memcpy(cc, &plugin_data->call_cand, sizeof(plugin_data->call_cand)); - rz_th_queue_push(cc_queue->yield_queue, state->shared_obj, true); + rz_th_queue_push(cc_queue->yield_queue, iset->state->shared_obj, true); // Don't leave collection lock. Consumer will unlock it after it collected. return true; } @@ -86,7 +84,7 @@ void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) dst->is_concrete = src->is_concrete; } -void write_var_to_state(RzInterpreterAbstrState *state, +void write_var_to_state(RzInterpreterSet *iset, RzILVarKind kind, ut64 var_id, const ProtoIntrprAbstrData *data) { @@ -96,13 +94,13 @@ void write_var_to_state(RzInterpreterAbstrState *state, rz_warn_if_reached(); return; case RZ_IL_VAR_KIND_GLOBAL: - ht_vals = state->globals; + ht_vals = iset->state->globals; break; case RZ_IL_VAR_KIND_LOCAL: - ht_vals = state->locals; + ht_vals = iset->state->locals; break; case RZ_IL_VAR_KIND_LOCAL_PURE: - ht_vals = state->lets; + ht_vals = iset->state->lets; break; } RzInterpreterAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); @@ -120,7 +118,7 @@ void write_var_to_state(RzInterpreterAbstrState *state, copy_abstr_data(av->abstr_data, data); } -bool read_var_from_state(RzInterpreterAbstrState *state, +bool read_var_from_state(RzInterpreterSet *iset, RzILVarKind kind, ut64 var_id, RZ_OUT ProtoIntrprAbstrData *data) { @@ -130,13 +128,13 @@ bool read_var_from_state(RzInterpreterAbstrState *state, rz_warn_if_reached(); return false; case RZ_IL_VAR_KIND_GLOBAL: - ht_vals = state->globals; + ht_vals = iset->state->globals; break; case RZ_IL_VAR_KIND_LOCAL: - ht_vals = state->locals; + ht_vals = iset->state->locals; break; case RZ_IL_VAR_KIND_LOCAL_PURE: - ht_vals = state->lets; + ht_vals = iset->state->lets; break; } RzInterpreterAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); @@ -156,7 +154,7 @@ bool read_var_from_state(RzInterpreterAbstrState *state, // TODO: The assumption that true != 0 is invalid. // It depends on the architecture and must be decided by the RzArch plugin. // State is passed due to this here as well. To make later refactoring easier. -bool abstr_is_true(const RzInterpreterAbstrState *state, const ProtoIntrprAbstrData *data) { +bool abstr_is_true(const RzInterpreterSet *iset, const ProtoIntrprAbstrData *data) { if (!data->is_concrete) { return false; } @@ -164,21 +162,19 @@ bool abstr_is_true(const RzInterpreterAbstrState *state, const ProtoIntrprAbstrD } bool store_abstr_data( - RzInterpreterAbstrState *state, + RzInterpreterSet *iset, RzILMemIndex mem_idx, const ProtoIntrprAbstrData *addr, - const ProtoIntrprAbstrData *src, - RzThreadQueue /**/ *io_request, - RzThreadQueue /**/ *io_result) { + const ProtoIntrprAbstrData *src) { if (!src->is_concrete) { // Really don't write? return true; } - rz_th_lock_enter(state->shared_obj->received); - RzInterpreterIORequest *io_req = &state->shared_obj->io_req; + rz_th_lock_enter(iset->state->shared_obj->received); + RzInterpreterIORequest *io_req = &iset->state->shared_obj->io_req; io_req->n_bits = rz_bv_len(src->bv); io_req->mem_idx = mem_idx; - io_req->big_endian = state->il_config->big_endian; + io_req->big_endian = iset->state->il_config->big_endian; io_req->type = RZ_INTERPRETER_IO_WRITE; io_req->addr = addr->bv; @@ -188,13 +184,13 @@ bool store_abstr_data( RZ_LOG_DEBUG("Prototype: STORE @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req->addr), bytes); free(bytes); - rz_th_queue_push(io_request, state->shared_obj, true); + rz_th_queue_push(iset->io_request, iset->state->shared_obj, true); // Don't leave collection lock. Consumer will unlock it after it collected. // Wait for write being done. RzInterpreterSharedObjects *so = NULL; - if (!rz_th_queue_pop(io_result, false, (void **)&so) || !so) { - rz_th_lock_leave(state->shared_obj->received); + if (!rz_th_queue_pop(iset->io_result, false, (void **)&so) || !so) { + rz_th_lock_leave(iset->state->shared_obj->received); return false; }; bool write_ok = so->io_res.req_ok; @@ -204,32 +200,30 @@ bool store_abstr_data( } bool load_abstr_data( - RzInterpreterAbstrState *state, + RzInterpreterSet *iset, RzILMemIndex mem_idx, const ProtoIntrprAbstrData *addr, size_t n_bits, - RZ_OUT ProtoIntrprAbstrData *out, - RzThreadQueue /**/ *io_request, - RzThreadQueue /**/ *io_result) { + RZ_OUT ProtoIntrprAbstrData *out) { - rz_th_lock_enter(state->shared_obj->received); - RzInterpreterIORequest *io_req = &state->shared_obj->io_req; + rz_th_lock_enter(iset->state->shared_obj->received); + RzInterpreterIORequest *io_req = &iset->state->shared_obj->io_req; rz_bv_cast_inplace(out->bv, n_bits, 0); io_req->type = RZ_INTERPRETER_IO_READ; io_req->addr = addr->bv; io_req->ld_data = out->bv; io_req->mem_idx = mem_idx; io_req->n_bits = n_bits; - io_req->big_endian = state->il_config->big_endian; + io_req->big_endian = iset->state->il_config->big_endian; ut64 req_addr = rz_bv_to_ut64(io_req->addr); - rz_th_queue_push(io_request, state->shared_obj, true); + rz_th_queue_push(iset->io_request, iset->state->shared_obj, true); // Don't leave collection lock. Consumer will unlock it after it collected. // Wait for load being done. RzInterpreterSharedObjects *so = NULL; - if (!rz_th_queue_pop(io_result, false, (void **)&so) || !so) { - rz_th_lock_leave(state->shared_obj->received); + if (!rz_th_queue_pop(iset->io_result, false, (void **)&so) || !so) { + rz_th_lock_leave(iset->state->shared_obj->received); return false; } if (!so->io_res.req_ok) { diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index bda87b8fd5c..e6d2162fb74 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -66,52 +66,40 @@ static inline RZ_OWN ProtoIntrprAbstrData *adata_new() { } void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src); -void write_var_to_state(RzInterpreterAbstrState *state, RzILVarKind kind, ut64 var_id, const ProtoIntrprAbstrData *data); -bool read_var_from_state(RzInterpreterAbstrState *state, RzILVarKind kind, ut64 var_id, RZ_OUT ProtoIntrprAbstrData *data); -bool abstr_is_true(const RzInterpreterAbstrState *state, const ProtoIntrprAbstrData *data); +void write_var_to_state(RzInterpreterSet *iset, RzILVarKind kind, ut64 var_id, const ProtoIntrprAbstrData *data); +bool read_var_from_state(RzInterpreterSet *iset, RzILVarKind kind, ut64 var_id, RZ_OUT ProtoIntrprAbstrData *data); +bool abstr_is_true(const RzInterpreterSet *iset, const ProtoIntrprAbstrData *data); bool store_abstr_data( - RzInterpreterAbstrState *state, + RzInterpreterSet *iset, RzILMemIndex mem_idx, const ProtoIntrprAbstrData *addr, - const ProtoIntrprAbstrData *src, - RzThreadQueue /**/ *io_request, - RzThreadQueue /**/ *io_result); + const ProtoIntrprAbstrData *src); bool load_abstr_data( - RzInterpreterAbstrState *state, + RzInterpreterSet *iset, RzILMemIndex mem_idx, const ProtoIntrprAbstrData *addr, size_t n_bits, - RZ_OUT ProtoIntrprAbstrData *out, - RzThreadQueue /**/ *io_request, - RzThreadQueue /**/ *io_result); + RZ_OUT ProtoIntrprAbstrData *out); -RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, +RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterSet *iset, const RzILOpEffect *effect, size_t nop_pc_inc, - HtUP /**/ *yield_queues, - RzThreadQueue /**/ *io_request, - RzThreadQueue /**/ *io_result, ProtoIntrprPluginData *plugin_data); RZ_IPI bool interpreter_prototype_eval_pure( - RzInterpreterAbstrState *state, + RzInterpreterSet *iset, const RzILOpPure *pure, RZ_OUT ProtoIntrprAbstrData *out, - HtUP /**/ *yield_queues, - RzThreadQueue /**/ *io_request, - RzThreadQueue /**/ *io_result, ProtoIntrprPluginData *plugin_data); bool report_yield_xref( - RzInterpreterAbstrState *state, + RzInterpreterSet *iset, size_t insn_pkt_size, - HtUP /**/ *yield_queues, ut64 from, const ProtoIntrprAbstrData *to, RzAnalysisXRefType type); bool report_yield_call_candiate( - RzInterpreterAbstrState *state, - HtUP /**/ *yield_queues, + RzInterpreterSet *iset, ProtoIntrprPluginData *plugin_data); bool set_pc(RzInterpreterAbstrState *state, ut64 pc, diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 5f3d5d213bb..9edc5b883a4 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -7,23 +7,20 @@ #include "rz_util/rz_bitvector.h" #include "rz_util/rz_str.h" -static bool value_indicates_ret_addr_write(RzInterpreterAbstrState *state, ProtoIntrprAbstrData *val) { +static bool value_indicates_ret_addr_write(RzInterpreterSet *iset, ProtoIntrprAbstrData *val) { return val->is_concrete && - (rz_bv_to_ut64(val->bv) == state->bb_addr + state->bb_size || + (rz_bv_to_ut64(val->bv) == iset->state->bb_addr + iset->state->bb_size || // Sparc stores the call instruction PC into o8. // The return instruction jumps then to o7+8. - (rz_str_startswith(state->arch_name, "sparc") && rz_bv_to_ut64(val->bv) == rz_bv_to_ut64(AD(state->pc->abstr_data)->bv))); + (rz_str_startswith(iset->state->arch_name, "sparc") && rz_bv_to_ut64(val->bv) == rz_bv_to_ut64(AD(iset->state->pc->abstr_data)->bv))); } -RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, +RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterSet *iset, const RzILOpEffect *effect, size_t insn_pkt_size, - HtUP /**/ *yield_queues, - RzThreadQueue /**/ *io_request, - RzThreadQueue /**/ *io_result, ProtoIntrprPluginData *plugin_data) { STACK_ABSTR_DATA_OUT(eval_out); - ProtoIntrprAbstrData *pc = AD(state->pc->abstr_data); + ProtoIntrprAbstrData *pc = AD(iset->state->pc->abstr_data); switch (effect->code) { default: @@ -53,32 +50,32 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, break; } case RZ_IL_OP_SEQ: { - if (!interpreter_prototype_eval_effect(state, effect->op.seq.x, insn_pkt_size, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_effect(iset, effect->op.seq.x, insn_pkt_size, plugin_data)) { goto error; } - if (!interpreter_prototype_eval_effect(state, effect->op.seq.y, insn_pkt_size, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_effect(iset, effect->op.seq.y, insn_pkt_size, plugin_data)) { goto error; } break; } case RZ_IL_OP_SET: { ut64 vhash = effect->op.set.hash; - if (!interpreter_prototype_eval_pure(state, effect->op.set.x, &eval_out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, effect->op.set.x, &eval_out, plugin_data)) { goto error; } RzILVarKind kind = effect->op.set.is_local ? RZ_IL_VAR_KIND_LOCAL : RZ_IL_VAR_KIND_GLOBAL; - write_var_to_state(state, kind, vhash, &eval_out); - if (value_indicates_ret_addr_write(state, &eval_out) && + write_var_to_state(iset, kind, vhash, &eval_out); + if (value_indicates_ret_addr_write(iset, &eval_out) && kind == RZ_IL_VAR_KIND_GLOBAL) { plugin_data->call_cand.store_addr = rz_bv_to_ut64(pc->bv); - plugin_data->call_cand.npc = state->bb_addr + state->bb_size; - plugin_data->call_cand.bb_addr = state->bb_addr; + plugin_data->call_cand.npc = iset->state->bb_addr + iset->state->bb_size; + plugin_data->call_cand.bb_addr = iset->state->bb_addr; plugin_data->call_cand.in_mem = false; } break; } case RZ_IL_OP_JMP: { - if (!interpreter_prototype_eval_pure(state, effect->op.jmp.dst, &eval_out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, effect->op.jmp.dst, &eval_out, plugin_data)) { goto error; } if (!eval_out.is_concrete) { @@ -93,21 +90,21 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, if (eval_out.is_concrete) { // NOTE: This prototype can't classify into call or jump. // Everything is just a jump for it at this point. - report_yield_xref(state, insn_pkt_size, yield_queues, rz_bv_to_ut64(pc->bv), &eval_out, RZ_ANALYSIS_XREF_TYPE_CODE); + report_yield_xref(iset, insn_pkt_size, rz_bv_to_ut64(pc->bv), &eval_out, RZ_ANALYSIS_XREF_TYPE_CODE); } if (plugin_data->call_cand.store_addr && eval_out.is_concrete) { // An instruction in this basic block stored the next PC. // Report a call candidate. plugin_data->call_cand.candidate_addr = rz_bv_to_ut64(pc->bv); plugin_data->call_cand.target = rz_bv_to_ut64(eval_out.bv); - report_yield_call_candiate(state, yield_queues, plugin_data); + report_yield_call_candiate(iset, plugin_data); } memset(&plugin_data->call_cand, 0, sizeof(plugin_data->call_cand)); - copy_abstr_data(state->pc->abstr_data, &eval_out); + copy_abstr_data(iset->state->pc->abstr_data, &eval_out); break; } case RZ_IL_OP_BRANCH: { - if (!interpreter_prototype_eval_pure(state, effect->op.branch.condition, &eval_out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, effect->op.branch.condition, &eval_out, plugin_data)) { goto error; } if (!eval_out.is_concrete) { @@ -116,12 +113,12 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, break; } - if (abstr_is_true(state, &eval_out)) { - if (!interpreter_prototype_eval_effect(state, effect->op.branch.true_eff, insn_pkt_size, yield_queues, io_request, io_result, plugin_data)) { + if (abstr_is_true(iset, &eval_out)) { + if (!interpreter_prototype_eval_effect(iset, effect->op.branch.true_eff, insn_pkt_size, plugin_data)) { goto error; } } else { - if (!interpreter_prototype_eval_effect(state, effect->op.branch.false_eff, insn_pkt_size, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_effect(iset, effect->op.branch.false_eff, insn_pkt_size, plugin_data)) { goto error; } } @@ -132,7 +129,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, STACK_ABSTR_DATA_OUT(st_addr); RzILOpPure *key = effect->code == RZ_IL_OP_STORE ? effect->op.store.key : effect->op.storew.key; RzILMemIndex mem_idx = effect->code == RZ_IL_OP_STORE ? 0 : effect->op.storew.mem; - if (!interpreter_prototype_eval_pure(state, key, &st_addr, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, key, &st_addr, plugin_data)) { RZ_LOG_ERROR("prototype: STORE/STOREW key failed to evaluate.\n"); rz_bv_fini(st_addr.bv); goto error; @@ -152,7 +149,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, } RzILOpPure *pval = effect->code == RZ_IL_OP_STORE ? effect->op.store.value : effect->op.storew.value; - if (!interpreter_prototype_eval_pure(state, pval, &eval_out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, pval, &eval_out, plugin_data)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); rz_bv_fini(st_addr.bv); goto error; @@ -161,14 +158,14 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterAbstrState *state, rz_bv_fini(st_addr.bv); break; } - if (value_indicates_ret_addr_write(state, &eval_out)) { + if (value_indicates_ret_addr_write(iset, &eval_out)) { plugin_data->call_cand.store_addr = rz_bv_to_ut64(pc->bv); - plugin_data->call_cand.npc = state->bb_addr + state->bb_size; - plugin_data->call_cand.bb_addr = state->bb_addr; + plugin_data->call_cand.npc = iset->state->bb_addr + iset->state->bb_size; + plugin_data->call_cand.bb_addr = iset->state->bb_addr; plugin_data->call_cand.in_mem = true; } - report_yield_xref(state, insn_pkt_size, yield_queues, rz_bv_to_ut64(pc->bv), &st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); - if (!store_abstr_data(state, mem_idx, &st_addr, &eval_out, io_request, io_result)) { + report_yield_xref(iset, insn_pkt_size, rz_bv_to_ut64(pc->bv), &st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); + if (!store_abstr_data(iset, mem_idx, &st_addr, &eval_out)) { rz_bv_fini(st_addr.bv); goto error; } diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 28b35fec600..8a3a23940e2 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -9,17 +9,14 @@ * \brief Evaluate a pure. */ RZ_IPI bool interpreter_prototype_eval_pure( - RzInterpreterAbstrState *state, + RzInterpreterSet *iset, const RzILOpPure *pure, RZ_OUT ProtoIntrprAbstrData *out, - HtUP /**/ *yield_queues, - RzThreadQueue /**/ *io_request, - RzThreadQueue /**/ *io_result, ProtoIntrprPluginData *plugin_data) { switch (pure->code) { default: case RZ_IL_OP_VAR: { - if (!read_var_from_state(state, pure->op.var.kind, pure->op.var.hash, out)) { + if (!read_var_from_state(iset, pure->op.var.kind, pure->op.var.hash, out)) { RZ_LOG_ERROR("prototype: VAR failed to evaluate. The %s '%s' doesn't exist.\n", rz_il_var_kind_name(pure->op.var.kind), pure->op.var.v); @@ -29,13 +26,13 @@ RZ_IPI bool interpreter_prototype_eval_pure( } case RZ_IL_OP_LET: { ut64 vhash = pure->op.let.hash; - if (!interpreter_prototype_eval_pure(state, pure->op.let.exp, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, pure->op.let.exp, out, plugin_data)) { RZ_LOG_ERROR("prototype: LET expression failed to evaluate.\n"); return false; } - write_var_to_state(state, RZ_IL_VAR_KIND_LOCAL_PURE, vhash, out); + write_var_to_state(iset, RZ_IL_VAR_KIND_LOCAL_PURE, vhash, out); // Evaluate body - if (!interpreter_prototype_eval_pure(state, pure->op.let.body, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, pure->op.let.body, out, plugin_data)) { RZ_LOG_ERROR("prototype: LET body failed to evaluate.\n"); return false; } @@ -44,7 +41,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } case RZ_IL_OP_ITE: { - if (!interpreter_prototype_eval_pure(state, pure->op.ite.condition, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, pure->op.ite.condition, out, plugin_data)) { RZ_LOG_ERROR("prototype: ITE condition failed to evaluate.\n"); return false; } @@ -53,13 +50,13 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } - if (abstr_is_true(state, out)) { - if (!interpreter_prototype_eval_pure(state, pure->op.ite.x, out, yield_queues, io_request, io_result, plugin_data)) { + if (abstr_is_true(iset, out)) { + if (!interpreter_prototype_eval_pure(iset, pure->op.ite.x, out, plugin_data)) { RZ_LOG_ERROR("prototype: ITE x failed to evaluate.\n"); return false; } } else { - if (!interpreter_prototype_eval_pure(state, pure->op.ite.y, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, pure->op.ite.y, out, plugin_data)) { RZ_LOG_ERROR("prototype: ITE y failed to evaluate.\n"); return false; } @@ -81,7 +78,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( out->is_concrete = true; break; case RZ_IL_OP_CAST: { - if (!interpreter_prototype_eval_pure(state, pure->op.cast.val, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, pure->op.cast.val, out, plugin_data)) { RZ_LOG_ERROR("prototype: CAST val failed to evaluate.\n"); return false; } @@ -89,7 +86,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(fill_bit); - if (!interpreter_prototype_eval_pure(state, pure->op.cast.fill, &fill_bit, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, pure->op.cast.fill, &fill_bit, plugin_data)) { RZ_LOG_ERROR("prototype: CAST fill failed to evaluate.\n"); return false; } @@ -97,7 +94,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(fill_bit.bv); goto map_to_bottom; } - rz_bv_cast_inplace(out->bv, pure->op.cast.length, abstr_is_true(state, &fill_bit)); + rz_bv_cast_inplace(out->bv, pure->op.cast.length, abstr_is_true(iset, &fill_bit)); break; } case RZ_IL_OP_BITV: @@ -107,7 +104,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; case RZ_IL_OP_APPEND: { STACK_ABSTR_DATA_OUT(high); - if (!interpreter_prototype_eval_pure(state, pure->op.append.high, &high, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, pure->op.append.high, &high, plugin_data)) { RZ_LOG_ERROR("prototype: APPEND high failed to evaluate.\n"); return false; } @@ -115,7 +112,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(high.bv); goto map_to_bottom; } - if (!interpreter_prototype_eval_pure(state, pure->op.append.low, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, pure->op.append.low, out, plugin_data)) { RZ_LOG_ERROR("prototype: APPEND low failed to evaluate.\n"); rz_bv_fini(high.bv); return false; @@ -133,7 +130,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_LOGNOT: case RZ_IL_OP_INV: { RzILOpPure *x = pure->code == RZ_IL_OP_INV ? pure->op.boolinv.x : pure->op.lognot.bv; - if (!interpreter_prototype_eval_pure(state, x, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, x, out, plugin_data)) { RZ_LOG_ERROR("prototype: INV x failed to evaluate.\n"); return false; } @@ -146,7 +143,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_AND: { RzILOpPure *px = pure->code == RZ_IL_OP_AND ? pure->op.booland.x : pure->op.logand.x; RzILOpPure *py = pure->code == RZ_IL_OP_AND ? pure->op.booland.y : pure->op.logand.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: AND x failed to evaluate.\n"); return false; } @@ -154,7 +151,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: AND y failed to evaluate.\n"); return false; } @@ -173,7 +170,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_OR: { RzILOpPure *px = pure->code == RZ_IL_OP_OR ? pure->op.boolor.x : pure->op.logor.x; RzILOpPure *py = pure->code == RZ_IL_OP_OR ? pure->op.boolor.y : pure->op.logor.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: OR x failed to evaluate.\n"); return false; } @@ -181,7 +178,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: OR y failed to evaluate.\n"); return false; } @@ -200,7 +197,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_XOR: { RzILOpPure *px = pure->code == RZ_IL_OP_XOR ? pure->op.boolxor.x : pure->op.logxor.x; RzILOpPure *py = pure->code == RZ_IL_OP_XOR ? pure->op.boolxor.y : pure->op.logxor.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: XOR x failed to evaluate.\n"); return false; } @@ -208,7 +205,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: XOR y failed to evaluate.\n"); return false; } @@ -245,7 +242,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( truth_test = rz_bv_msb; break; } - if (!interpreter_prototype_eval_pure(state, bv, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, bv, out, plugin_data)) { RZ_LOG_ERROR("prototype: MSB/LSB/IS_ZERO bv failed to evaluate.\n"); return false; } @@ -259,7 +256,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } case RZ_IL_OP_NEG: { - if (!interpreter_prototype_eval_pure(state, pure->op.neg.bv, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, pure->op.neg.bv, out, plugin_data)) { RZ_LOG_ERROR("prototype: NEG bv failed to evaluate.\n"); return false; } @@ -271,7 +268,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_ADD: { RzILOpPure *px = pure->op.add.x; RzILOpPure *py = pure->op.add.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: ADD x failed to evaluate.\n"); return false; } @@ -279,7 +276,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: ADD y failed to evaluate.\n"); return false; } @@ -297,7 +294,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_SUB: { RzILOpPure *px = pure->op.sub.x; RzILOpPure *py = pure->op.sub.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); return false; } @@ -305,7 +302,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: SUB y failed to evaluate.\n"); return false; } @@ -325,7 +322,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzILOpPure *px = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.x : pure->op.shiftl.x; RzILOpPure *py = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.y : pure->op.shiftl.y; RzILOpPure *pfill_bit = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.fill_bit : pure->op.shiftl.fill_bit; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) x failed to evaluate.\n"); return false; } @@ -333,7 +330,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) y failed to evaluate.\n"); return false; } @@ -342,7 +339,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(fill_bit); - if (!interpreter_prototype_eval_pure(state, pfill_bit, &fill_bit, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, pfill_bit, &fill_bit, plugin_data)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) fill_bit failed to evaluate.\n"); return false; } @@ -353,7 +350,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( } bool (*shift)(RzBitVector *bv, ut32 size, bool fill_bit); shift = pure->code == RZ_IL_OP_SHIFTR ? rz_bv_rshift_fill : rz_bv_lshift_fill; - if (!shift(out->bv, rz_bv_to_ut64(y.bv), abstr_is_true(state, &fill_bit))) { + if (!shift(out->bv, rz_bv_to_ut64(y.bv), abstr_is_true(iset, &fill_bit))) { rz_bv_fini(fill_bit.bv); rz_bv_fini(y.bv); goto map_to_bottom; @@ -388,7 +385,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: CMP x failed to evaluate.\n"); return false; } @@ -396,7 +393,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: CMP y failed to evaluate.\n"); return false; } @@ -415,7 +412,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( STACK_ABSTR_DATA_OUT(ld_addr); RzILOpPure *key = pure->code == RZ_IL_OP_LOAD ? pure->op.load.key : pure->op.loadw.key; RzILMemIndex mem_idx = pure->code == RZ_IL_OP_LOAD ? 0 : pure->op.loadw.mem; - if (!interpreter_prototype_eval_pure(state, key, &ld_addr, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, key, &ld_addr, plugin_data)) { RZ_LOG_ERROR("prototype: LOAD/LOADW key failed to evaluate.\n"); rz_bv_fini(ld_addr.bv); return false; @@ -434,9 +431,9 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_and_inplace(ld_addr.bv, &mask); } - report_yield_xref(state, 0, yield_queues, rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), &ld_addr, RZ_ANALYSIS_XREF_TYPE_MEM_READ); - size_t n_bits = pure->code == RZ_IL_OP_LOAD ? state->il_config->mem_key_size : pure->op.loadw.n_bits; - if (!load_abstr_data(state, mem_idx, &ld_addr, n_bits, out, io_request, io_result)) { + report_yield_xref(iset, 0, rz_bv_to_ut64(AD(iset->state->pc->abstr_data)->bv), &ld_addr, RZ_ANALYSIS_XREF_TYPE_MEM_READ); + size_t n_bits = pure->code == RZ_IL_OP_LOAD ? iset->state->il_config->mem_key_size : pure->op.loadw.n_bits; + if (!load_abstr_data(iset, mem_idx, &ld_addr, n_bits, out)) { rz_bv_fini(ld_addr.bv); goto map_to_bottom; } @@ -446,7 +443,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_MUL: { RzILOpPure *px = pure->op.mul.x; RzILOpPure *py = pure->op.mul.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: MUL x failed to evaluate.\n"); return false; } @@ -454,7 +451,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: MUL y failed to evaluate.\n"); return false; } @@ -472,7 +469,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_MOD: { RzILOpPure *px = pure->op.mod.x; RzILOpPure *py = pure->op.mod.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: MOD x failed to evaluate.\n"); return false; } @@ -480,7 +477,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: MOD y failed to evaluate.\n"); return false; } @@ -498,7 +495,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_DIV: { RzILOpPure *px = pure->op.div.x; RzILOpPure *py = pure->op.div.y; - if (!interpreter_prototype_eval_pure(state, px, out, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: DIV x failed to evaluate.\n"); return false; } @@ -506,7 +503,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_bottom; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(state, py, &y, yield_queues, io_request, io_result, plugin_data)) { + if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: DIV y failed to evaluate.\n"); return false; } From 745311e8bf777c05bb19c408964de096ac12991a Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 28 Mar 2026 20:07:22 +0100 Subject: [PATCH 235/334] Revert "Push the same object over the queue to prepare for multi-threaded interpretation." This reverts commit f72dc3cdf2116f506d53422b8b4a923c2e4c9f6c. --- librz/include/rz_inquiry/rz_interpreter.h | 78 +++++++++---------- librz/inquiry/inquiry.c | 89 +++++++++------------- librz/inquiry/interpreter/interpreter.c | 76 +++++------------- librz/inquiry/interpreter/prototype/eval.c | 76 +++++++----------- 4 files changed, 117 insertions(+), 202 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 60d715bce60..47c39399ee3 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -68,43 +68,22 @@ typedef struct { ut64 alt_target; } RzInterpreterBranch; -typedef enum { - RZ_INTERPRETER_IO_READ, - RZ_INTERPRETER_IO_WRITE, -} RzInterpreterIOReqType; - -typedef struct { - RzInterpreterIOReqType type; - size_t mem_idx; ///< The memory space to read/write. - bool big_endian; ///< Set if the data is big endian ordered. - const RzBitVector *addr; ///< The address to read/write. - const RzBitVector *st_data; ///< The data to store. - RzBitVector *ld_data; ///< The bit vector to load into. It is BORROWED. - size_t n_bits; ///< The number of bits to read/write. -} RzInterpreterIORequest; - -typedef struct { - bool req_ok; ///< Set to true if IO request succeeded. -} RzInterpreterIOResult; - /** - * Objects an interpreter instance sends over queues. + * objects the interpreter should use to send over the queues. + * + * TODO: Race conditions ahead, if one party is faster in overwriting one value + * than the other using it. + * Shouldn't happen though as long as there is only one interpreter + * and one RzInquiry managing everything. */ -RZ_LIFETIME(RzInquiry) typedef struct { - size_t instance_id; ///< The interpreter instance this object belongs to. - /** - * \brief Locked whenever an interpreter sent this object over the queue. - * The consumer releases the lock when it collected the object. - * The producer is not supposed to use this object as long as the lock is closed. - */ - RzThreadLock *received; - - // Not inside the union so they can be read and written at the same time. - RzInterpreterIORequest io_req; ///< An IO request. - RzInterpreterIOResult io_res; ///< The IO result. + RZ_LIFETIME(RzInquiry) RzInterpreterBranch branch; ///< The branch object passed to an IL cache for BB requests. + + RZ_LIFETIME(RzInquiry) RzAnalysisXRef xref; ///< The xref object passed over the queue. + + RZ_LIFETIME(RzInquiry) RzAnalysisCallCandidate call_cand; ///< The stores next pc info passed over the queue. } RzInterpreterSharedObjects; @@ -118,7 +97,7 @@ typedef struct { RzAnalysisILConfig *il_config; ///< The IL configuration of the RzArch plugin. const char *arch_name; ///< Name of architecture. Used by work-arounds until we have RzArch. /** - * \brief Shared objects. This pointer is pushed over the queue. + * \brief Shared objects. Pointers to the members are passed over the queue. * TODO: This is obviously not the final solution. Just some poor man's shared memory. */ RZ_LIFETIME(RzInquiry) @@ -242,18 +221,37 @@ typedef struct { void *plugin_data); } RzInterpreterPlugin; +typedef enum { + RZ_INTERPRETER_IO_READ, + RZ_INTERPRETER_IO_WRITE, +} RzInterpreterIOReqType; + +typedef struct { + RzInterpreterIOReqType type; + size_t mem_idx; ///< The memory space to read/write. + bool big_endian; ///< Set if the data is big endian ordered. + const RzBitVector *addr; ///< The address to read/write. + const RzBitVector *st_data; ///< The data to store. + RzBitVector *ld_data; ///< The bit vector to load into. It is BORROWED. + size_t n_bits; ///< The number of bits to read/write. +} RzInterpreterIORequest; + +typedef struct { + bool req_ok; ///< Set to true if IO request succeeded. +} RzInterpreterIOResult; + /** * \brief The set of required queues for an interpreter to run. */ RZ_LIFETIME(RzInquiry) struct rz_interpreter_set { RzInterpreterAbstrState *state; ///< The abstract state of the interpreter. + RzThreadQueue /**/ *branch_queue; ///< The queue to send requests to the cache what address to get the next IL op from. + RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. // TODO: We need to decide how to distribute the yield. - HtUP /**/ *yield_queues; ///< The queues to push the yield of interpretation into. - RzThreadQueue /**/ *branch_queue; ///< The queue to send requests to the cache what address to get the next IL op from. - RzThreadQueue /**/ *io_request; ///< The queue for read/write requests to the IO layer. - RzThreadQueue /**/ *io_result; ///< The queue for the read/write requests' answers. - RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. + HtUP /**/ *yield_queues; ///< The queues to push the yield of interpretation into. + RzThreadQueue /**/ *io_request; ///< The queue for read/write requests to the IO layer. + RzThreadQueue /**/ *io_result; ///< The queue for the read/write requests' answers. RzAtomicBool *is_running_flag; ///< Flag for the interpreter thread to toggle when done. const RzVector /**/ *ignored_code; /** @@ -264,10 +262,6 @@ struct rz_interpreter_set { RzInterpreterPlugin *plugin; }; -RZ_API RZ_OWN RzInterpreterSharedObjects *rz_interpreter_shared_objects_new(size_t instance_id); -RZ_API void rz_interpreter_shared_objects_fini(RZ_NULLABLE RZ_BORROW RzInterpreterSharedObjects *so); -RZ_API void rz_interpreter_shared_objects_free(RZ_NULLABLE RZ_OWN RzInterpreterSharedObjects *so); - RZ_API void rz_interpreter_il_bb_free(RZ_NULLABLE RZ_OWN RzInterpreterILBB *il_bb); RZ_API void rz_interpreter_insn_pkt_free(RZ_NULLABLE RZ_OWN RzInterpreterInsnPkt *pkt); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 732a738b7b6..38a16d9fdcd 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -28,7 +28,6 @@ #include #include #include -#include RZ_LIB_VERSION(rz_inquiry); @@ -186,7 +185,7 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL co return false; } -static void handle_io_request(RzCore *core, RzPVector /**/ *il_mems, const RzInterpreterIORequest *io_req, RZ_OUT RzInterpreterIOResult *io_res) { +static void handle_io_request(RzCore *core, RzPVector /**/ *il_mems, RzInterpreterIORequest *io_req, RZ_OUT RzInterpreterIOResult *io_res) { RZ_LOG_DEBUG("INQUIRY: Received IO %s request: mem:%" PFMTSZd " 0x%" PFMT64x "\n", io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", io_req->mem_idx, @@ -275,36 +274,26 @@ static bool get_branch_targets(RzCore *core, RzSetU *branch_targets, RzVector /* return true; } -static bool handle_yields(RzCore *core, RzInterpreterSet *iset, HtUP *yield_queues) { +static bool handle_yields(RzCore *core, HtUP *yield_queues) { RzInterpreterYieldQueue *q_xrefs = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); if (!rz_th_queue_is_empty(q_xrefs->yield_queue)) { - RzInterpreterSharedObjects *so = NULL; - if (!rz_th_queue_pop(q_xrefs->yield_queue, false, (void **)&so) || !so) { - rz_th_lock_leave(iset->state->shared_obj->received); + RzAnalysisXRef *xref = NULL; + if (!rz_th_queue_pop(q_xrefs->yield_queue, false, (void **)&xref) || !xref) { return false; } - rz_inquiry_add_xref(core->inquiry, &so->xref); - rz_analysis_xrefs_set(core->analysis, so->xref.from, so->xref.to, so->xref.type); - RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", so->xref.from, so->xref.to, rz_analysis_ref_type_tostring(so->xref.type)); - rz_th_lock_leave(so->received); + rz_inquiry_add_xref(core->inquiry, xref); + rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, xref->type); + RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)); } RzInterpreterYieldQueue *q_calls = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, NULL); if (!rz_th_queue_is_empty(q_calls->yield_queue)) { - RzInterpreterSharedObjects *so = NULL; - if (!rz_th_queue_pop(q_calls->yield_queue, false, (void **)&so) || !so) { - rz_th_lock_leave(iset->state->shared_obj->received); + RzAnalysisCallCandidate *cc = NULL; + if (!rz_th_queue_pop(q_calls->yield_queue, false, (void **)&cc) || !cc) { return false; } RzAnalysisCallCandidate *cc_clone = RZ_NEW0(RzAnalysisCallCandidate); - if (ht_up_find(core->inquiry->call_candidates, cc_clone->bb_addr, NULL)) { - rz_th_lock_leave(iset->state->shared_obj->received); - return true; - } - - memcpy(cc_clone, &so->call_cand, sizeof(RzAnalysisCallCandidate)); - rz_th_lock_leave(iset->state->shared_obj->received); - + memcpy(cc_clone, cc, sizeof(RzAnalysisCallCandidate)); if (ht_up_update(core->inquiry->call_candidates, cc_clone->bb_addr, cc_clone)) { RZ_LOG_DEBUG("Overwrote a call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); } else { @@ -448,6 +437,10 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, analysis_vm->reg_binding); } + RZ_LOG_DEBUG("INQUIRY: Enforce enabling IO cache.\n"); + const char *io_cache_opt = rz_config_get(core->config, "io.cache"); + rz_config_set(core->config, "io.cache", "true"); + // Bundle all the queues into one object to pass it to the thread. // Later we would pass a unique iset to each interpreter with // the required queues only. @@ -481,6 +474,10 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_LOG_DEBUG("INQUIRY: Start main interpretation thread.\n"); interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); + // Poor man's shared memory. + RzInterpreterIOResult _io_res = { 0 }; + RzInterpreterIOResult *io_res = &_io_res; + // From here on, the code plays the role of the cache, IO handler, // and yield consumer. // - Waiting for new Effects to be requested and sending them. @@ -506,25 +503,24 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // caches them. { if (!rz_th_queue_is_empty(iset->branch_queue)) { - RzInterpreterSharedObjects *so = NULL; - if (!rz_th_queue_pop(iset->branch_queue, false, (void **)&so) || !so) { - rz_th_lock_leave(iset->state->shared_obj->received); + RzInterpreterBranch *branch = NULL; + if (!rz_th_queue_pop(iset->branch_queue, false, (void **)&branch) || !branch) { rz_warn_if_reached(); break; } - ut64 alt_addr = so->branch.alt_target; + ut64 alt_addr = branch->alt_target; if (alt_addr) { - so->branch.alt_target = 0; + branch->alt_target = 0; } - RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x " (alt: 0x%" PFMT64x ")\n", so->branch.target_addr, alt_addr); - const RzInterpreterILBB *bb = get_il_bb(core, il_cache, alt_addr ? alt_addr : so->branch.target_addr); + RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x " (alt: 0x%" PFMT64x ")\n", branch->target_addr, alt_addr); + const RzInterpreterILBB *bb = get_il_bb(core, il_cache, alt_addr ? alt_addr : branch->target_addr); if (!bb) { // Delete the address from the branch targets. // This is currently necessary as a work around, because if the interpreter // fails before interpreting the address, it is added again as next entry point. // Giving an endless loop. // One of the design thingies to fix in the proper implementation. - rz_set_u_delete(branch_targets, so->branch.target_addr); + rz_set_u_delete(branch_targets, branch->target_addr); if (alt_addr) { rz_set_u_delete(branch_targets, alt_addr); } @@ -535,19 +531,16 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_th_queue_close(iset->branch_queue); rz_th_queue_close(iset->il_queue); bb_decode_failed = true; - rz_th_lock_leave(so->received); break; } rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, bb->bb_addr, bb->size); if (alt_addr) { // Add a dummy basic block at the address the call originally jumped to. // This is the basic block for the imported function. - rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, so->branch.target_addr, 1); - rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, so->branch.branching_bb_addr, so->branch.target_addr); + rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, branch->target_addr, 1); + rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); } - rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, so->branch.branching_bb_addr, so->branch.target_addr); - rz_th_lock_leave(so->received); - + rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); rz_th_queue_push(iset->il_queue, (void *)bb, true); } } @@ -563,21 +556,14 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // Because this is not yet implemented, there is only one interpreter thread for now. { if (!rz_th_queue_is_empty(iset->io_request)) { - RzInterpreterSharedObjects *so = NULL; - if (!rz_th_queue_pop(iset->io_request, false, (void **)&so) || !so) { + RzInterpreterIORequest *io_req = NULL; + if (!rz_th_queue_pop(iset->io_request, false, (void **)&io_req) || !io_req) { rz_atomic_bool_set(is_running, false); - rz_th_lock_leave(iset->state->shared_obj->received); rz_warn_if_reached(); break; } - handle_io_request(core, - &analysis_vm->vm->vm_memory, - &so->io_req, &so->io_res); - rz_th_lock_leave(so->received); - - rz_th_lock_enter(so->received); - rz_th_queue_push(iset->io_result, so, true); - // Don't leave collection lock. Consumer will unlock it after it collected. + handle_io_request(core, &analysis_vm->vm->vm_memory, io_req, io_res); + rz_th_queue_push(iset->io_result, io_res, true); } } @@ -588,7 +574,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // This part plays the role of a yield consumer. // In our prototype it only receives xrefs and call candidates. { - if (!handle_yields(core, iset, iset->yield_queues)) { + if (!handle_yields(core, iset->yield_queues)) { rz_atomic_bool_set(is_running, false); break; } @@ -599,7 +585,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_th_queue_close(iset->io_result); rz_th_queue_close(iset->branch_queue); rz_th_queue_close(iset->il_queue); - rz_th_lock_leave(iset->state->shared_obj->received); RZ_LOG_DEBUG("INQUIRY: Wait for join\n"); rz_th_wait(interpr_th); @@ -613,10 +598,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, } break; } - - rz_th_lock_enter(iset->state->shared_obj->received); - rz_interpreter_shared_objects_fini(iset->state->shared_obj); - + // Clear shared objects to not have any left overs in the next run. + memset((ut8 *)iset->state->shared_obj, 0, sizeof(RzInterpreterSharedObjects)); // Open queue again, so the interpretation can start at another // jump target again. rz_th_queue_open(iset->io_request); @@ -689,6 +672,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_LOG_DEBUG("INQUIRY: Done\n"); + rz_config_set(core->config, "io.cache", io_cache_opt); + // Wait for thread to finish before cleaning. error_free: RZ_LOG_DEBUG("INQUIRY: Close queues\n"); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index dd6154a0880..b15fb8d0580 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -122,8 +122,7 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( state->locals = ht_up_new(NULL, free); state->lets = ht_up_new(NULL, free); state->il_config = il_config; - // TODO: Instance Id - state->shared_obj = rz_interpreter_shared_objects_new(0); + state->shared_obj = RZ_NEW0(RzInterpreterSharedObjects); return state; } @@ -150,7 +149,7 @@ RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbst rz_analysis_il_config_free(state->il_config); } if (state->shared_obj) { - rz_interpreter_shared_objects_free(state->shared_obj); + free(state->shared_obj); } free(state); } @@ -186,41 +185,6 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { free(iset); } - -RZ_API RZ_OWN RzInterpreterSharedObjects *rz_interpreter_shared_objects_new(size_t instance_id) { - RzInterpreterSharedObjects *so = RZ_NEW0(RzInterpreterSharedObjects); - if (!so) { - return NULL; - } - so->instance_id = instance_id; - so->received = rz_th_lock_new(false); - if (!so->received) { - free(so); - return NULL; - } - return so; -} - -RZ_API void rz_interpreter_shared_objects_fini(RZ_NULLABLE RZ_BORROW RzInterpreterSharedObjects *so) { - if (!so) { - return; - } - rz_th_lock_free(so->received); - size_t instance_id = so->instance_id; - memset(so, 0, sizeof(RzInterpreterSharedObjects)); - so->instance_id = instance_id; - so->received = rz_th_lock_new(false); -} - -RZ_API void rz_interpreter_shared_objects_free(RZ_NULLABLE RZ_OWN RzInterpreterSharedObjects *so) { - if (!so) { - return; - } - rz_th_lock_free(so->received); - free(so); -} - - static bool setup_queues( RZ_OWN RzPVector /**/ *sections, RzInterpreterYieldFilter yield_filter, @@ -301,7 +265,6 @@ static bool setup_queues( return false; } - /** * \brief Initializes a new RzInterpreterSet and returns it. * If it fails, all arguments are freed. @@ -382,8 +345,7 @@ static bool choose_next_pc(RzInterpreterSet *iset, // RZ_LOG_DEBUG("%s", s); // free(s); - rz_th_lock_enter(iset->state->shared_obj->received); - RzInterpreterBranch *branch = &iset->state->shared_obj->branch; + RzInterpreterBranch *shared_branch = &iset->state->shared_obj->branch; bool has_succsessor = true; // Determine successors and increase the reference counts for the current out state. @@ -398,29 +360,30 @@ static bool choose_next_pc(RzInterpreterSet *iset, has_succsessor = !rz_vector_empty(tmp_succ_addr); // Request the successor effects over the queue. while (!rz_vector_empty(tmp_succ_addr)) { - rz_vector_pop_front(tmp_succ_addr, &branch->target_addr); - if (branch->target_addr == UT64_MAX || branch->target_addr == 0) { + rz_vector_pop_front(tmp_succ_addr, &shared_branch->target_addr); + if (shared_branch->target_addr == UT64_MAX || shared_branch->target_addr == 0) { RZ_LOG_DEBUG("interpreter: Quit due to invalid PC.\n"); // Obviously wrong address. return false; } - branch->branching_bb_addr = il_bb->bb_addr; - if (jumps_to_ignored_code(iset->ignored_code, branch->target_addr)) { - RZ_LOG_DEBUG("interpreter: tried to jump to ignored code region at 0x%" PFMT64x "\n", branch->target_addr); + shared_branch->branching_bb_addr = il_bb->bb_addr; + if (jumps_to_ignored_code(iset->ignored_code, shared_branch->target_addr)) { + RZ_LOG_DEBUG("interpreter: tried to jump to ignored code region at 0x%" PFMT64x "\n", shared_branch->target_addr); // Ignored code is mostly dynamically linked functions. // Skip to the next following address after the jump. - branch->alt_target = il_bb->bb_addr + il_bb->size; + shared_branch->alt_target = il_bb->bb_addr + il_bb->size; } SuccessorState ss = { - .addr = branch->alt_target ? branch->alt_target : branch->target_addr, + .addr = shared_branch->alt_target ? shared_branch->alt_target : shared_branch->target_addr, .in_state_hash = out_hash }; // The successors are pushed in the same order into the succ_states - // vector, as they are requested over the branch queue. + // vector, as they are requested over the addr_queue. rz_vector_push(succ_states, &ss); - rz_th_queue_push(iset->branch_queue, iset->state->shared_obj, true); - // Don't leave collection lock. Consumer will unlock it after it collected. + rz_th_queue_push(iset->branch_queue, shared_branch, true); + // TODO: Race condition: + // Multiple jump targets could overwrite the value in shared_addr before it is read. } return has_succsessor; } @@ -474,10 +437,9 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { goto pre_loop_error; } - rz_th_lock_enter(iset->state->shared_obj->received); - RzInterpreterBranch *branch = &iset->state->shared_obj->branch; - rz_vector_pop_front(iset->entry_points, &branch->target_addr); - if (!plugin->init_state(iset->state, branch->target_addr, plugin_data)) { + RzInterpreterBranch *shared_branch = &iset->state->shared_obj->branch; + rz_vector_pop_front(iset->entry_points, &shared_branch->target_addr); + if (!plugin->init_state(iset->state, shared_branch->target_addr, plugin_data)) { rz_warn_if_reached(); goto pre_loop_error; } @@ -486,9 +448,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { #endif ut64 out_hash = 0; - rz_th_queue_push(iset->branch_queue, iset->state->shared_obj, true); - // Don't leave collection lock. Consumer will unlock it after it collected. - + rz_th_queue_push(iset->branch_queue, shared_branch, true); const RzInterpreterILBB *il_bb = NULL; if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { goto pre_loop_error; diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index d1759b2b7c3..2850aeadae0 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -40,8 +40,6 @@ bool report_yield_xref( ut64 to_addr = rz_bv_to_ut64(to->bv); if (queue->filter(&to_addr, queue->filter_data->io_boundaries)) { - rz_th_lock_enter(iset->state->shared_obj->received); - RzAnalysisXRef *xref = &iset->state->shared_obj->xref; xref->bb_addr = iset->state->bb_addr; xref->from = from; @@ -51,8 +49,7 @@ bool report_yield_xref( // before the previous one was handled. // But this is fine for the prototype. Real implementation needs some kind // of shared memory anyways. - rz_th_queue_push(queue->yield_queue, iset->state->shared_obj, true); - // Don't leave collection lock. Consumer will unlock it after it collected. + rz_th_queue_push(queue->yield_queue, xref, true); } return true; } @@ -69,11 +66,9 @@ bool report_yield_call_candiate( return false; } - rz_th_lock_enter(iset->state->shared_obj->received); RzAnalysisCallCandidate *cc = &iset->state->shared_obj->call_cand; memcpy(cc, &plugin_data->call_cand, sizeof(plugin_data->call_cand)); - rz_th_queue_push(cc_queue->yield_queue, iset->state->shared_obj, true); - // Don't leave collection lock. Consumer will unlock it after it collected. + rz_th_queue_push(cc_queue->yield_queue, cc, true); return true; } @@ -170,33 +165,24 @@ bool store_abstr_data( // Really don't write? return true; } - rz_th_lock_enter(iset->state->shared_obj->received); - RzInterpreterIORequest *io_req = &iset->state->shared_obj->io_req; - io_req->n_bits = rz_bv_len(src->bv); - io_req->mem_idx = mem_idx; - io_req->big_endian = iset->state->il_config->big_endian; + RzInterpreterIORequest io_req = { 0 }; + io_req.n_bits = rz_bv_len(src->bv); + io_req.mem_idx = mem_idx; + io_req.big_endian = iset->state->il_config->big_endian; - io_req->type = RZ_INTERPRETER_IO_WRITE; - io_req->addr = addr->bv; - io_req->st_data = src->bv; + io_req.type = RZ_INTERPRETER_IO_WRITE; + io_req.addr = addr->bv; + io_req.st_data = src->bv; char *bytes = rz_bv_as_hex_string(src->bv, true); - RZ_LOG_DEBUG("Prototype: STORE @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req->addr), bytes); + RZ_LOG_DEBUG("Prototype: STORE @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); free(bytes); - rz_th_queue_push(iset->io_request, iset->state->shared_obj, true); - // Don't leave collection lock. Consumer will unlock it after it collected. - + rz_th_queue_push(iset->io_request, &io_req, true); // Wait for write being done. - RzInterpreterSharedObjects *so = NULL; - if (!rz_th_queue_pop(iset->io_result, false, (void **)&so) || !so) { - rz_th_lock_leave(iset->state->shared_obj->received); - return false; - }; - bool write_ok = so->io_res.req_ok; - rz_th_lock_leave(so->received); - - return write_ok; + RzInterpreterIOResult *io_res = NULL; + rz_th_queue_pop(iset->io_result, false, (void **)&io_res); + return io_res ? io_res->req_ok : false; } bool load_abstr_data( @@ -205,40 +191,30 @@ bool load_abstr_data( const ProtoIntrprAbstrData *addr, size_t n_bits, RZ_OUT ProtoIntrprAbstrData *out) { - - rz_th_lock_enter(iset->state->shared_obj->received); - RzInterpreterIORequest *io_req = &iset->state->shared_obj->io_req; + RzInterpreterIORequest io_req = { 0 }; rz_bv_cast_inplace(out->bv, n_bits, 0); - io_req->type = RZ_INTERPRETER_IO_READ; - io_req->addr = addr->bv; - io_req->ld_data = out->bv; - io_req->mem_idx = mem_idx; - io_req->n_bits = n_bits; - io_req->big_endian = iset->state->il_config->big_endian; - ut64 req_addr = rz_bv_to_ut64(io_req->addr); - - rz_th_queue_push(iset->io_request, iset->state->shared_obj, true); - // Don't leave collection lock. Consumer will unlock it after it collected. - + io_req.type = RZ_INTERPRETER_IO_READ; + io_req.addr = addr->bv; + io_req.ld_data = out->bv; + io_req.mem_idx = mem_idx; + io_req.n_bits = n_bits; + io_req.big_endian = iset->state->il_config->big_endian; + rz_th_queue_push(iset->io_request, &io_req, true); // Wait for load being done. - RzInterpreterSharedObjects *so = NULL; - if (!rz_th_queue_pop(iset->io_result, false, (void **)&so) || !so) { - rz_th_lock_leave(iset->state->shared_obj->received); + RzInterpreterIOResult *io_res = NULL; + if (!rz_th_queue_pop(iset->io_result, false, (void **)&io_res) || !io_res) { return false; } - if (!so->io_res.req_ok) { + if (!io_res->req_ok) { RZ_LOG_WARN("Prototype: Failed to read correct number of bytes. Requested: 0x%" PFMTSZx " Received: 0x%" PFMT32x " bits.\n", n_bits, rz_bv_len(out->bv)); - rz_th_lock_leave(so->received); return false; } - rz_th_lock_leave(so->received); - out->is_concrete = true; char *bytes = rz_bv_as_hex_string(out->bv, true); - RZ_LOG_DEBUG("Prototype: READ @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, req_addr, bytes); + RZ_LOG_DEBUG("Prototype: READ @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); free(bytes); return true; } From 97224b45b208cee311dd7f4dfd19ac6657e35fb3 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sun, 29 Mar 2026 23:40:44 +0200 Subject: [PATCH 236/334] Add incomplete refactor to ring buffers. --- librz/core/cmd/cmd_inquiry.c | 2 + librz/include/rz_inquiry/rz_interpreter.h | 58 ++---- librz/inquiry/inquiry.c | 187 +++++++++--------- librz/inquiry/interpreter/interpreter.c | 210 +++++++++++---------- librz/inquiry/interpreter/prototype/eval.c | 66 +++---- 5 files changed, 263 insertions(+), 260 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 579727c63d4..40132ec0e84 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -69,12 +69,14 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar bool success = rz_inquiry_interpreter(core, entry_points, ignored_code_regions); eprintf("Finished reference recovery: %s\n", success ? "OK" : "FAIL"); if (!success) { + rz_vector_free(ignored_code_regions); return RZ_CMD_STATUS_ERROR; } eprintf("Perform function deduction: "); RzSetU *symbol_addresses = rz_set_u_new(); if (!rz_inquiry_get_fcn_symbol_addr(core, symbol_addresses)) { + rz_vector_free(ignored_code_regions); rz_warn_if_reached(); return RZ_CMD_STATUS_ERROR; } diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 47c39399ee3..f71840b5f6d 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -19,10 +19,10 @@ /** * \brief Only one IO request at a time is possible (currently). */ -#define RZ_INTERPRETER_IO_QUEUE_SIZE 1 -#define RZ_INTERPRETER_IL_QUEUE_SIZE 128 -#define RZ_INTERPRETER_ADDR_QUEUE_SIZE 1024 -#define RZ_INTERPRETER_YIELD_QUEUE_SIZE 4096 +#define RZ_INTERPRETER_IO_RBUF_SIZE 128 +#define RZ_INTERPRETER_IL_QUEUE_SIZE 128 +#define RZ_INTERPRETER_ADDR_RBUF_SIZE 1024 +#define RZ_INTERPRETER_YIELD_RBUF_SIZE 128 /** * \brief The abstractions this module supports. @@ -68,25 +68,6 @@ typedef struct { ut64 alt_target; } RzInterpreterBranch; -/** - * objects the interpreter should use to send over the queues. - * - * TODO: Race conditions ahead, if one party is faster in overwriting one value - * than the other using it. - * Shouldn't happen though as long as there is only one interpreter - * and one RzInquiry managing everything. - */ -typedef struct { - RZ_LIFETIME(RzInquiry) - RzInterpreterBranch branch; ///< The branch object passed to an IL cache for BB requests. - - RZ_LIFETIME(RzInquiry) - RzAnalysisXRef xref; ///< The xref object passed over the queue. - - RZ_LIFETIME(RzInquiry) - RzAnalysisCallCandidate call_cand; ///< The stores next pc info passed over the queue. -} RzInterpreterSharedObjects; - typedef struct { RzInterpreterAbstraction kinds; ///< The abstractions of the state. HtUP *var_name_hashes; ///< Map of DJB2 hashes to variable names. @@ -96,12 +77,6 @@ typedef struct { RzInterpreterAbstrVal *pc; ///< In our RzIL implementation the PC is not part of the register file. RzAnalysisILConfig *il_config; ///< The IL configuration of the RzArch plugin. const char *arch_name; ///< Name of architecture. Used by work-arounds until we have RzArch. - /** - * \brief Shared objects. Pointers to the members are passed over the queue. - * TODO: This is obviously not the final solution. Just some poor man's shared memory. - */ - RZ_LIFETIME(RzInquiry) - RzInterpreterSharedObjects *shared_obj; ut64 bb_addr; ut64 bb_size; } RzInterpreterAbstrState; @@ -124,7 +99,7 @@ typedef enum { /** * \brief A filter for abstract values to decide if they should be pushed into - * the yield queue or not. + * the yield ring buffer or not. */ typedef bool (*RzInterpreterYieldFilter)(const void *element, const void *filter_data); @@ -133,14 +108,14 @@ typedef struct { } RzInterpreterYieldFilterData; /** - * \brief A queue to push interpretation yields into. + * \brief A ring buffer to push interpretation yields into. */ typedef struct { RzInterpreterYieldKind kind; RzInterpreterYieldFilter filter; RzInterpreterYieldFilterData *filter_data; - RzThreadQueue *yield_queue; -} RzInterpreterYieldQueue; + RzThreadRingBuf *rbuf; +} RzInterpreterYieldRBuf; typedef struct { RzPVector *il_ops; ///< The sequence of IL operations of this basic block. @@ -241,17 +216,16 @@ typedef struct { } RzInterpreterIOResult; /** - * \brief The set of required queues for an interpreter to run. + * \brief The set of required objects for an interpreter to run. */ RZ_LIFETIME(RzInquiry) struct rz_interpreter_set { RzInterpreterAbstrState *state; ///< The abstract state of the interpreter. - RzThreadQueue /**/ *branch_queue; ///< The queue to send requests to the cache what address to get the next IL op from. RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. - // TODO: We need to decide how to distribute the yield. - HtUP /**/ *yield_queues; ///< The queues to push the yield of interpretation into. - RzThreadQueue /**/ *io_request; ///< The queue for read/write requests to the IO layer. - RzThreadQueue /**/ *io_result; ///< The queue for the read/write requests' answers. + RzThreadRingBuf /**/ *branch_rbuf; ///< The ring buffer to send requests to the cache what address to get the next IL op from. + RzThreadRingBuf /**/ *io_request_rbuf; ///< The ring buffer for read/write requests to the IO layer. + RzThreadRingBuf /**/ *io_result_rbuf; ///< The ring buffer for the read/write requests' answers. + HtUP /**/ *yield_rbufs; ///< The ring buffers to push the yield of interpretation into. RzAtomicBool *is_running_flag; ///< Flag for the interpreter thread to toggle when done. const RzVector /**/ *ignored_code; /** @@ -265,7 +239,7 @@ struct rz_interpreter_set { RZ_API void rz_interpreter_il_bb_free(RZ_NULLABLE RZ_OWN RzInterpreterILBB *il_bb); RZ_API void rz_interpreter_insn_pkt_free(RZ_NULLABLE RZ_OWN RzInterpreterInsnPkt *pkt); -RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldQueue *yield_queue); +RZ_API void rz_interpreter_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldRBuf *yield_rbuf); RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( const char *arch_name, @@ -274,7 +248,7 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( RZ_NULLABLE const RzILRegBinding *reg_bindings); RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbstrState *state); -RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpreterYieldKind kind, +RZ_API RZ_OWN RzInterpreterYieldRBuf *rz_interpreter_yield_rbuf_new(RzInterpreterYieldKind kind, RzInterpreterYieldFilter filter, RZ_OWN RZ_NULLABLE void *filter_data); @@ -289,6 +263,6 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset); RZ_API void rz_interpreter_set_add_entry_points(RZ_NONNULL RzInterpreterSet *iset, const RzVector /**/ *entry_points); -RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *queue_set); +RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset); #endif // RZ_INTERPRETER diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 38a16d9fdcd..fd0588d50f2 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -274,30 +274,40 @@ static bool get_branch_targets(RzCore *core, RzSetU *branch_targets, RzVector /* return true; } -static bool handle_yields(RzCore *core, HtUP *yield_queues) { - RzInterpreterYieldQueue *q_xrefs = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); - if (!rz_th_queue_is_empty(q_xrefs->yield_queue)) { - RzAnalysisXRef *xref = NULL; - if (!rz_th_queue_pop(q_xrefs->yield_queue, false, (void **)&xref) || !xref) { +static bool handle_yields(RzCore *core, HtUP *yield_rbufs) { + RzInterpreterYieldRBuf *rbuf_xrefs = ht_up_find(yield_rbufs, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); + rz_return_val_if_fail(rbuf_xrefs, false); + + RzAnalysisXRef xref = { 0 }; + if (!rz_th_ring_buf_is_empty_unsafe(rbuf_xrefs->rbuf)) { + RzThreadRingBufResult r = rz_th_ring_buf_take_blocking(rbuf_xrefs->rbuf, &xref); + if (r == RZ_THREAD_RING_BUF_CLOSED) { + rz_warn_if_reached(); return false; + } else if (r == RZ_THREAD_RING_BUF_OK) { + rz_inquiry_add_xref(core->inquiry, &xref); + rz_analysis_xrefs_set(core->analysis, xref.from, xref.to, xref.type); + RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref.from, xref.to, rz_analysis_ref_type_tostring(xref.type)); } - rz_inquiry_add_xref(core->inquiry, xref); - rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, xref->type); - RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref->from, xref->to, rz_analysis_ref_type_tostring(xref->type)); } - RzInterpreterYieldQueue *q_calls = ht_up_find(yield_queues, RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, NULL); - if (!rz_th_queue_is_empty(q_calls->yield_queue)) { - RzAnalysisCallCandidate *cc = NULL; - if (!rz_th_queue_pop(q_calls->yield_queue, false, (void **)&cc) || !cc) { + RzInterpreterYieldRBuf *rbuf_calls = ht_up_find(yield_rbufs, RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, NULL); + rz_return_val_if_fail(rbuf_calls, false); + + RzAnalysisCallCandidate cc = { 0 }; + if (!rz_th_ring_buf_is_empty_unsafe(rbuf_calls->rbuf)) { + RzThreadRingBufResult r = rz_th_ring_buf_take_blocking(rbuf_calls->rbuf, &cc); + if (r == RZ_THREAD_RING_BUF_CLOSED) { + rz_warn_if_reached(); return false; - } - RzAnalysisCallCandidate *cc_clone = RZ_NEW0(RzAnalysisCallCandidate); - memcpy(cc_clone, cc, sizeof(RzAnalysisCallCandidate)); - if (ht_up_update(core->inquiry->call_candidates, cc_clone->bb_addr, cc_clone)) { - RZ_LOG_DEBUG("Overwrote a call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); - } else { - RZ_LOG_DEBUG("Added call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); + } else if (r == RZ_THREAD_RING_BUF_OK) { + RzAnalysisCallCandidate *cc_clone = RZ_NEW0(RzAnalysisCallCandidate); + memcpy(cc_clone, &cc, sizeof(RzAnalysisCallCandidate)); + if (ht_up_update(core->inquiry->call_candidates, cc_clone->bb_addr, cc_clone)) { + RZ_LOG_DEBUG("Overwrote a call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); + } else { + RZ_LOG_DEBUG("Added call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); + } } } return true; @@ -348,6 +358,41 @@ static const RzInterpreterILBB *get_il_bb(RzCore *core, HtUP *il_cache, ut64 add return bb; } +static bool send_next_il_bb(RzCore *core, + RzInterpreterSet *iset, + HtUP *il_cache, + RzSetU *branch_targets, + RzInterpreterBranch *branch) { + ut64 alt_addr = branch->alt_target; + if (alt_addr) { + branch->alt_target = 0; + } + RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x " (alt: 0x%" PFMT64x ")\n", branch->target_addr, alt_addr); + const RzInterpreterILBB *bb = get_il_bb(core, il_cache, alt_addr ? alt_addr : branch->target_addr); + if (!bb) { + // Delete the address from the branch targets. + // This is currently necessary as a work around, because if the interpreter + // fails before interpreting the address, it is added again as next entry point. + // Giving an endless loop. + // One of the design thingies to fix in the proper implementation. + rz_set_u_delete(branch_targets, branch->target_addr); + if (alt_addr) { + rz_set_u_delete(branch_targets, alt_addr); + } + return false; + } + rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, bb->bb_addr, bb->size); + if (alt_addr) { + // Add a dummy basic block at the address the call originally jumped to. + // This is the basic block for the imported function. + rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, branch->target_addr, 1); + rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); + } + rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); + rz_th_queue_push(iset->il_queue, (void *)bb, true); + return true; +} + /** * A function to call the prototype interpreter. * Usually these tasks will be split between different caches and yield consumers. @@ -367,6 +412,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RzSetU *branch_targets = rz_set_u_new(); RzSetU *symbol_targets = rz_set_u_new(); bool user_sent_signal = false; + RzVector /**/ *insn_to_insn_edges = NULL; rz_cons_push(); @@ -384,7 +430,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // of the pointers. il_cache = ht_up_new(NULL, (RzPVectorFree)rz_interpreter_il_bb_free); - RzVector /**/ *insn_to_insn_edges = rz_vector_new(sizeof(RzAnalysisXRef), NULL, NULL); + insn_to_insn_edges = rz_vector_new(sizeof(RzAnalysisXRef), NULL, NULL); if (!get_branch_targets(core, branch_targets, insn_to_insn_edges) || !rz_inquiry_get_fcn_symbol_addr(core, symbol_targets)) { RZ_LOG_ERROR("Failed to get branch targets.\n"); @@ -475,8 +521,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); // Poor man's shared memory. - RzInterpreterIOResult _io_res = { 0 }; - RzInterpreterIOResult *io_res = &_io_res; + RzInterpreterIOResult io_res = { 0 }; // From here on, the code plays the role of the cache, IO handler, // and yield consumer. @@ -502,46 +547,24 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // This block mimics the IL cache. It uplifts basic blocks and // caches them. { - if (!rz_th_queue_is_empty(iset->branch_queue)) { - RzInterpreterBranch *branch = NULL; - if (!rz_th_queue_pop(iset->branch_queue, false, (void **)&branch) || !branch) { + RzInterpreterBranch branch = { 0 }; + if (!rz_th_ring_buf_is_empty_unsafe(iset->branch_rbuf)) { + RzThreadRingBufResult r = rz_th_ring_buf_take_blocking(iset->branch_rbuf, &branch); + if (r == RZ_THREAD_RING_BUF_CLOSED) { rz_warn_if_reached(); break; - } - ut64 alt_addr = branch->alt_target; - if (alt_addr) { - branch->alt_target = 0; - } - RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x " (alt: 0x%" PFMT64x ")\n", branch->target_addr, alt_addr); - const RzInterpreterILBB *bb = get_il_bb(core, il_cache, alt_addr ? alt_addr : branch->target_addr); - if (!bb) { - // Delete the address from the branch targets. - // This is currently necessary as a work around, because if the interpreter - // fails before interpreting the address, it is added again as next entry point. - // Giving an endless loop. - // One of the design thingies to fix in the proper implementation. - rz_set_u_delete(branch_targets, branch->target_addr); - if (alt_addr) { - rz_set_u_delete(branch_targets, alt_addr); + } else if (r == RZ_THREAD_RING_BUF_OK) { + if (!send_next_il_bb(core, iset, il_cache, branch_targets, &branch)) { + // Signal interpreter the lifting failed. + rz_atomic_bool_set(is_running, false); + rz_th_ring_buf_close(iset->io_request_rbuf); + rz_th_ring_buf_close(iset->io_result_rbuf); + rz_th_ring_buf_close(iset->branch_rbuf); + rz_th_queue_close(iset->il_queue); + bb_decode_failed = true; + break; } - // Signal interpreter the lifting failed. - rz_atomic_bool_set(is_running, false); - rz_th_queue_close(iset->io_request); - rz_th_queue_close(iset->io_result); - rz_th_queue_close(iset->branch_queue); - rz_th_queue_close(iset->il_queue); - bb_decode_failed = true; - break; - } - rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, bb->bb_addr, bb->size); - if (alt_addr) { - // Add a dummy basic block at the address the call originally jumped to. - // This is the basic block for the imported function. - rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, branch->target_addr, 1); - rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); } - rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); - rz_th_queue_push(iset->il_queue, (void *)bb, true); } } @@ -555,15 +578,19 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // (one for each interpreter instance). // Because this is not yet implemented, there is only one interpreter thread for now. { - if (!rz_th_queue_is_empty(iset->io_request)) { - RzInterpreterIORequest *io_req = NULL; - if (!rz_th_queue_pop(iset->io_request, false, (void **)&io_req) || !io_req) { - rz_atomic_bool_set(is_running, false); + RzInterpreterIORequest io_req = { 0 }; + if (!rz_th_ring_buf_is_empty_unsafe(iset->io_request_rbuf)) { + RzThreadRingBufResult r = rz_th_ring_buf_take_blocking(iset->io_request_rbuf, &io_req); + if (r == RZ_THREAD_RING_BUF_CLOSED) { rz_warn_if_reached(); break; + } else if (r == RZ_THREAD_RING_BUF_OK) { + handle_io_request(core, &analysis_vm->vm->vm_memory, &io_req, &io_res); + if (rz_th_ring_buf_put(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { + rz_warn_if_reached(); + break; + } } - handle_io_request(core, &analysis_vm->vm->vm_memory, io_req, io_res); - rz_th_queue_push(iset->io_result, io_res, true); } } @@ -574,16 +601,16 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // This part plays the role of a yield consumer. // In our prototype it only receives xrefs and call candidates. { - if (!handle_yields(core, iset->yield_queues)) { + if (!handle_yields(core, iset->yield_rbufs)) { rz_atomic_bool_set(is_running, false); break; } } } - rz_th_queue_close(iset->io_request); - rz_th_queue_close(iset->io_result); - rz_th_queue_close(iset->branch_queue); + rz_th_ring_buf_close(iset->io_request_rbuf); + rz_th_ring_buf_close(iset->io_result_rbuf); + rz_th_ring_buf_close(iset->branch_rbuf); rz_th_queue_close(iset->il_queue); RZ_LOG_DEBUG("INQUIRY: Wait for join\n"); @@ -598,19 +625,13 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, } break; } - // Clear shared objects to not have any left overs in the next run. - memset((ut8 *)iset->state->shared_obj, 0, sizeof(RzInterpreterSharedObjects)); // Open queue again, so the interpretation can start at another // jump target again. - rz_th_queue_open(iset->io_request); - rz_th_queue_open(iset->io_result); - rz_th_queue_open(iset->branch_queue); + rz_th_ring_buf_open(iset->io_request_rbuf); + rz_th_ring_buf_open(iset->io_result_rbuf); + rz_th_ring_buf_open(iset->branch_rbuf); rz_th_queue_open(iset->il_queue); - // Clear queues from any left overs of previous runs. - rz_list_free(rz_th_queue_pop_all(iset->io_result)); - rz_list_free(rz_th_queue_pop_all(iset->io_request)); rz_list_free(rz_th_queue_pop_all(iset->il_queue)); - rz_list_free(rz_th_queue_pop_all(iset->branch_queue)); // At this point the interpreter is finished and returned. // Now we need to check for executable regions it did not cover. @@ -668,7 +689,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, goto error_free; } } - rz_vector_free(insn_to_insn_edges); RZ_LOG_DEBUG("INQUIRY: Done\n"); @@ -681,19 +701,10 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_set_u_free(branch_targets); rz_buf_free(io_buf); rz_analysis_il_vm_free(analysis_vm); - rz_th_queue_close(iset->io_request); - rz_th_queue_close(iset->io_result); - rz_th_queue_close(iset->branch_queue); - rz_th_queue_close(iset->il_queue); + rz_vector_free(insn_to_insn_edges); if (!iset) { // Ownership of all those objects wasn't yet passed to the iset. - rz_th_queue_free(iset->branch_queue); - rz_th_queue_free(iset->il_queue); - rz_th_queue_free(iset->io_request); - rz_th_queue_free(iset->io_result); - // yield_queues frees each individual queue as well. - ht_up_free(iset->yield_queues); rz_atomic_bool_free(is_running); rz_interpreter_abstr_state_free(abstr_state); } else { diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index b15fb8d0580..4ddc6f3dedb 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -5,6 +5,7 @@ * \file The API implementation for all analysis interpreters. */ +#include "rz_analysis.h" #include "rz_util/rz_assert.h" #include "rz_util/rz_itv.h" #include @@ -32,48 +33,52 @@ RZ_API void rz_interpreter_il_bb_free(RZ_NULLABLE RZ_OWN RzInterpreterILBB *il_b free(il_bb); } -RZ_API void rz_interpreter_yield_queue_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldQueue *yield_queue) { - if (!yield_queue) { +RZ_API void rz_interpreter_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldRBuf *yield_rbufs) { + if (!yield_rbufs) { return; } - if (yield_queue->yield_queue) { - rz_th_queue_free(yield_queue->yield_queue); + if (yield_rbufs->rbuf) { + rz_th_ring_buf_free(yield_rbufs->rbuf); } - if (yield_queue->filter_data && yield_queue->filter_data->io_boundaries) { - rz_pvector_free(yield_queue->filter_data->io_boundaries); + if (yield_rbufs->filter_data && yield_rbufs->filter_data->io_boundaries) { + rz_pvector_free(yield_rbufs->filter_data->io_boundaries); } - free(yield_queue->filter_data); - free(yield_queue); + free(yield_rbufs->filter_data); + free(yield_rbufs); } -RZ_API RZ_OWN RzInterpreterYieldQueue *rz_interpreter_yield_queue_new(RzInterpreterYieldKind kind, +RZ_API RZ_OWN RzInterpreterYieldRBuf *rz_interpreter_yield_rbuf_new(RzInterpreterYieldKind kind, RzInterpreterYieldFilter filter, RZ_OWN RZ_NULLABLE void *filter_data) { - RzInterpreterYieldQueue *yield_queue = RZ_NEW0(RzInterpreterYieldQueue); - if (!yield_queue) { + RzInterpreterYieldRBuf *yield_rbufs = RZ_NEW0(RzInterpreterYieldRBuf); + if (!yield_rbufs) { return NULL; } - RzThreadQueue *queue = NULL; + RzThreadRingBuf *rbuf = NULL; switch (kind) { case RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE: - queue = rz_th_queue_new(RZ_INTERPRETER_YIELD_QUEUE_SIZE, NULL); + rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_YIELD_RBUF_SIZE, sizeof(RzAnalysisCallCandidate)); break; case RZ_INTERPRETER_YIELD_KIND_XREF: if (filter_data) { - yield_queue->filter_data = RZ_NEW0(RzInterpreterYieldFilterData); - yield_queue->filter_data->io_boundaries = filter_data; + yield_rbufs->filter_data = RZ_NEW0(RzInterpreterYieldFilterData); + yield_rbufs->filter_data->io_boundaries = filter_data; + } + rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_YIELD_RBUF_SIZE, sizeof(RzAnalysisXRef)); + if (!rbuf) { + rz_pvector_free(filter_data); + return NULL; } - queue = rz_th_queue_new(RZ_INTERPRETER_YIELD_QUEUE_SIZE, NULL); break; } - if (!queue) { - free(yield_queue); + if (!rbuf) { + free(yield_rbufs); return NULL; } - yield_queue->kind = kind; - yield_queue->yield_queue = queue; - yield_queue->filter = filter; - return yield_queue; + yield_rbufs->kind = kind; + yield_rbufs->rbuf = rbuf; + yield_rbufs->filter = filter; + return yield_rbufs; } /** @@ -122,7 +127,6 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( state->locals = ht_up_new(NULL, free); state->lets = ht_up_new(NULL, free); state->il_config = il_config; - state->shared_obj = RZ_NEW0(RzInterpreterSharedObjects); return state; } @@ -148,9 +152,6 @@ RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbst if (state->il_config) { rz_analysis_il_config_free(state->il_config); } - if (state->shared_obj) { - free(state->shared_obj); - } free(state); } @@ -158,17 +159,17 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { if (!iset) { return; } - if (iset->branch_queue) { - rz_th_queue_free(iset->branch_queue); - } if (iset->il_queue) { rz_th_queue_free(iset->il_queue); } - if (iset->io_request) { - rz_th_queue_free(iset->io_request); + if (iset->branch_rbuf) { + rz_th_ring_buf_free(iset->branch_rbuf); } - if (iset->io_result) { - rz_th_queue_free(iset->io_result); + if (iset->io_request_rbuf) { + rz_th_ring_buf_free(iset->io_request_rbuf); + } + if (iset->io_result_rbuf) { + rz_th_ring_buf_free(iset->io_result_rbuf); } if (iset->is_running_flag) { rz_atomic_bool_free(iset->is_running_flag); @@ -176,8 +177,8 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { if (iset->state) { rz_interpreter_abstr_state_free(iset->state); } - if (iset->yield_queues) { - ht_up_free(iset->yield_queues); + if (iset->yield_rbufs) { + ht_up_free(iset->yield_rbufs); } if (iset->entry_points) { rz_vector_free(iset->entry_points); @@ -189,39 +190,45 @@ static bool setup_queues( RZ_OWN RzPVector /**/ *sections, RzInterpreterYieldFilter yield_filter, RZ_OUT RzThreadQueue **il_queue, - RZ_OUT RzThreadQueue **io_request_q, - RZ_OUT RzThreadQueue **io_result_q, - RZ_OUT RzThreadQueue **branch_queue, - RZ_OUT HtUP **yield_queues) { + RZ_OUT RzThreadRingBuf **io_request_rbuf, + RZ_OUT RzThreadRingBuf **io_result_rbuf, + RZ_OUT RzThreadRingBuf **branch_rbuf, + RZ_OUT HtUP **yield_rbufs) { *il_queue = NULL; - *io_request_q = NULL; - *io_result_q = NULL; - *branch_queue = NULL; - *yield_queues = NULL; + *io_request_rbuf = NULL; + *io_result_rbuf = NULL; + *branch_rbuf = NULL; + *yield_rbufs = NULL; - RzInterpreterYieldQueue *yield_queue = NULL; + RzInterpreterYieldRBuf *rbuf = NULL; // The queue to pass the Effects to the interpreter. // This is only one queue for the prototype. // In practice it would be one for each interpreter. *il_queue = rz_th_queue_new(RZ_INTERPRETER_IL_QUEUE_SIZE, NULL); if (!il_queue) { + rz_warn_if_reached(); + rz_pvector_free(sections); goto error_free; } // Setup the IO queues. Each interpreter instance needs it's own queue at // for writing IO. Because the writing is done on the IO cache, and each // instance needs its own cache. - *io_request_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); - *io_result_q = rz_th_queue_new(RZ_INTERPRETER_IO_QUEUE_SIZE, NULL); - if (!io_request_q || !io_result_q) { + *io_request_rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_IO_RBUF_SIZE, sizeof(RzInterpreterIORequest)); + *io_result_rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_IO_RBUF_SIZE, sizeof(RzInterpreterIOResult)); + if (!*io_request_rbuf || !*io_result_rbuf) { + rz_warn_if_reached(); + rz_pvector_free(sections); goto error_free; } - // The address queue. It is the queue the interpreter can request new Effects. + // The branch ring buffer. It is the ring buffer the interpreter can request new Effects. // Of course, currently there is only a single one for the prototype. // In practice there would be one for each interpreter instance. - *branch_queue = rz_th_queue_new(RZ_INTERPRETER_ADDR_QUEUE_SIZE, NULL); - if (!branch_queue) { + *branch_rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_ADDR_RBUF_SIZE, sizeof(RzInterpreterBranch)); + if (!*branch_rbuf) { + rz_warn_if_reached(); + rz_pvector_free(sections); goto error_free; } @@ -229,39 +236,43 @@ static bool setup_queues( // E.g. if the interpreter has a complex abstract memory model // for stack, heap and constant values. // Then it can produce three kind of yields. - *yield_queues = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_yield_queue_free); - if (!yield_queues) { + *yield_rbufs = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_yield_rbuf_free); + if (!*yield_rbufs) { + rz_warn_if_reached(); + rz_pvector_free(sections); goto error_free; } // These yield queues can be shared between different interpreters. // So we have one yield queue for each yield type. - // Xref yield queue. - RzInterpreterYieldKind yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; - yield_queue = rz_interpreter_yield_queue_new( - yield_kind, - yield_filter, - sections); - if (!yield_queue) { + RzInterpreterYieldKind yield_kind = RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE; + rbuf = rz_interpreter_yield_rbuf_new(yield_kind, NULL, NULL); + if (!rbuf) { + rz_warn_if_reached(); + rz_pvector_free(sections); goto error_free; } - ht_up_insert(*yield_queues, yield_kind, yield_queue); + ht_up_insert(*yield_rbufs, yield_kind, rbuf); - yield_kind = RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE; - yield_queue = rz_interpreter_yield_queue_new(yield_kind, NULL, NULL); - if (!yield_queue) { + yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; + rbuf = rz_interpreter_yield_rbuf_new( + yield_kind, + yield_filter, + sections); + if (!rbuf) { + rz_warn_if_reached(); goto error_free; } - ht_up_insert(*yield_queues, yield_kind, yield_queue); + ht_up_insert(*yield_rbufs, yield_kind, rbuf); return true; error_free: - ht_up_free(*yield_queues); + ht_up_free(*yield_rbufs); rz_th_queue_free(*il_queue); - rz_th_queue_free(*io_request_q); - rz_th_queue_free(*io_result_q); - rz_th_queue_free(*branch_queue); + rz_th_ring_buf_free(*io_request_rbuf); + rz_th_ring_buf_free(*io_result_rbuf); + rz_th_ring_buf_free(*branch_rbuf); return false; } @@ -281,24 +292,28 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RzInterpreterSet *set = RZ_NEW0(RzInterpreterSet); if (!set) { - return false; + rz_vector_free(entry_points); + rz_pvector_free(sections); + return NULL; } - RzThreadQueue *io_request_q = NULL; - RzThreadQueue *io_result_q = NULL; - RzThreadQueue *branch_queue = NULL; + RzThreadRingBuf *io_request_rbuf = NULL; + RzThreadRingBuf *io_result_rbuf = NULL; + RzThreadRingBuf *branch_rbuf = NULL; RzThreadQueue *il_queue = NULL; - HtUP *yield_queues = NULL; - if (!setup_queues(sections, yield_filter, &il_queue, &io_request_q, &io_result_q, &branch_queue, &yield_queues)) { - return false; + HtUP *yield_rbufs = NULL; + if (!setup_queues(sections, yield_filter, &il_queue, &io_request_rbuf, &io_result_rbuf, &branch_rbuf, &yield_rbufs)) { + rz_vector_free(entry_points); + free(set); + return NULL; } set->plugin = plugin; set->state = state; set->il_queue = il_queue; - set->branch_queue = branch_queue; - set->yield_queues = yield_queues; - set->io_request = io_request_q; - set->io_result = io_result_q; + set->branch_rbuf = branch_rbuf; + set->yield_rbufs = yield_rbufs; + set->io_request_rbuf = io_request_rbuf; + set->io_result_rbuf = io_result_rbuf; set->is_running_flag = is_running_flag; set->entry_points = entry_points; set->ignored_code = ignored_code; @@ -344,8 +359,6 @@ static bool choose_next_pc(RzInterpreterSet *iset, // char *s = rz_strbuf_drain_nofree(state_str); // RZ_LOG_DEBUG("%s", s); // free(s); - - RzInterpreterBranch *shared_branch = &iset->state->shared_obj->branch; bool has_succsessor = true; // Determine successors and increase the reference counts for the current out state. @@ -360,30 +373,31 @@ static bool choose_next_pc(RzInterpreterSet *iset, has_succsessor = !rz_vector_empty(tmp_succ_addr); // Request the successor effects over the queue. while (!rz_vector_empty(tmp_succ_addr)) { - rz_vector_pop_front(tmp_succ_addr, &shared_branch->target_addr); - if (shared_branch->target_addr == UT64_MAX || shared_branch->target_addr == 0) { + RzInterpreterBranch branch = { 0 }; + rz_vector_pop_front(tmp_succ_addr, &branch.target_addr); + if (branch.target_addr == UT64_MAX || branch.target_addr == 0) { RZ_LOG_DEBUG("interpreter: Quit due to invalid PC.\n"); // Obviously wrong address. return false; } - shared_branch->branching_bb_addr = il_bb->bb_addr; - if (jumps_to_ignored_code(iset->ignored_code, shared_branch->target_addr)) { - RZ_LOG_DEBUG("interpreter: tried to jump to ignored code region at 0x%" PFMT64x "\n", shared_branch->target_addr); + branch.branching_bb_addr = il_bb->bb_addr; + if (jumps_to_ignored_code(iset->ignored_code, branch.target_addr)) { + RZ_LOG_DEBUG("interpreter: tried to jump to ignored code region at 0x%" PFMT64x "\n", branch.target_addr); // Ignored code is mostly dynamically linked functions. // Skip to the next following address after the jump. - shared_branch->alt_target = il_bb->bb_addr + il_bb->size; + branch.alt_target = il_bb->bb_addr + il_bb->size; } SuccessorState ss = { - .addr = shared_branch->alt_target ? shared_branch->alt_target : shared_branch->target_addr, + .addr = branch.alt_target ? branch.alt_target : branch.target_addr, .in_state_hash = out_hash }; // The successors are pushed in the same order into the succ_states // vector, as they are requested over the addr_queue. rz_vector_push(succ_states, &ss); - rz_th_queue_push(iset->branch_queue, shared_branch, true); - // TODO: Race condition: - // Multiple jump targets could overwrite the value in shared_addr before it is read. + if (rz_th_ring_buf_put(iset->branch_rbuf, &branch) != RZ_THREAD_RING_BUF_OK) { + return false; + } } return has_succsessor; } @@ -394,9 +408,9 @@ static bool choose_next_pc(RzInterpreterSet *iset, RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { rz_goto_if_fail(iset && iset->state && - iset->branch_queue && + iset->branch_rbuf && iset->il_queue && - iset->yield_queues && + iset->yield_rbufs && iset->is_running_flag && iset->plugin && iset->plugin->eval && @@ -437,9 +451,9 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { goto pre_loop_error; } - RzInterpreterBranch *shared_branch = &iset->state->shared_obj->branch; - rz_vector_pop_front(iset->entry_points, &shared_branch->target_addr); - if (!plugin->init_state(iset->state, shared_branch->target_addr, plugin_data)) { + RzInterpreterBranch branch = { 0 }; + rz_vector_pop_front(iset->entry_points, &branch.target_addr); + if (!plugin->init_state(iset->state, branch.target_addr, plugin_data)) { rz_warn_if_reached(); goto pre_loop_error; } @@ -448,7 +462,9 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { #endif ut64 out_hash = 0; - rz_th_queue_push(iset->branch_queue, shared_branch, true); + if (rz_th_ring_buf_put(iset->branch_rbuf, &branch) != RZ_THREAD_RING_BUF_OK) { + goto pre_loop_error; + } const RzInterpreterILBB *il_bb = NULL; if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { goto pre_loop_error; diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 2850aeadae0..ab38fb56657 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -16,11 +16,6 @@ bool report_yield_xref( ut64 from, const ProtoIntrprAbstrData *to, RzAnalysisXRefType type) { - RzInterpreterYieldQueue *queue = ht_up_find(iset->yield_queues, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); - if (!queue) { - rz_warn_if_reached(); - return false; - } if (!to->is_concrete || rz_bv_len(to->bv) > 64) { // Isn't reported return true; @@ -38,18 +33,19 @@ bool report_yield_xref( return true; } + RzInterpreterYieldRBuf *yrbuf = ht_up_find(iset->yield_rbufs, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); + rz_return_val_if_fail(yrbuf, false); + ut64 to_addr = rz_bv_to_ut64(to->bv); - if (queue->filter(&to_addr, queue->filter_data->io_boundaries)) { - RzAnalysisXRef *xref = &iset->state->shared_obj->xref; - xref->bb_addr = iset->state->bb_addr; - xref->from = from; - xref->to = to_addr; - xref->type = type; - // TODO: Possible race condition here, if the interpreter pushes a new xref - // before the previous one was handled. - // But this is fine for the prototype. Real implementation needs some kind - // of shared memory anyways. - rz_th_queue_push(queue->yield_queue, xref, true); + if (yrbuf->filter(&to_addr, yrbuf->filter_data->io_boundaries)) { + RzAnalysisXRef xref = { 0 }; + xref.bb_addr = iset->state->bb_addr; + xref.from = from; + xref.to = to_addr; + xref.type = type; + if (rz_th_ring_buf_put(yrbuf->rbuf, &xref) != RZ_THREAD_RING_BUF_OK) { + return false; + } } return true; } @@ -60,15 +56,14 @@ bool report_yield_xref( bool report_yield_call_candiate( RzInterpreterSet *iset, ProtoIntrprPluginData *plugin_data) { - RzInterpreterYieldQueue *cc_queue = ht_up_find(iset->yield_queues, RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, NULL); - if (!cc_queue) { - rz_warn_if_reached(); + RzInterpreterYieldRBuf *cc_rbuf = ht_up_find(iset->yield_rbufs, RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, NULL); + rz_return_val_if_fail(cc_rbuf, false); + + RzAnalysisCallCandidate cc = { 0 }; + memcpy(&cc, &plugin_data->call_cand, sizeof(plugin_data->call_cand)); + if (rz_th_ring_buf_put(cc_rbuf->rbuf, &cc) != RZ_THREAD_RING_BUF_OK) { return false; } - - RzAnalysisCallCandidate *cc = &iset->state->shared_obj->call_cand; - memcpy(cc, &plugin_data->call_cand, sizeof(plugin_data->call_cand)); - rz_th_queue_push(cc_queue->yield_queue, cc, true); return true; } @@ -178,11 +173,15 @@ bool store_abstr_data( RZ_LOG_DEBUG("Prototype: STORE @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); free(bytes); - rz_th_queue_push(iset->io_request, &io_req, true); - // Wait for write being done. - RzInterpreterIOResult *io_res = NULL; - rz_th_queue_pop(iset->io_result, false, (void **)&io_res); - return io_res ? io_res->req_ok : false; + if (rz_th_ring_buf_put(iset->io_request_rbuf, &io_req) != RZ_THREAD_RING_BUF_OK) { + return false; + } + + RzInterpreterIOResult io_res = { 0 }; + if (rz_th_ring_buf_take_blocking(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { + return false; + } + return io_res.req_ok; } bool load_abstr_data( @@ -199,13 +198,14 @@ bool load_abstr_data( io_req.mem_idx = mem_idx; io_req.n_bits = n_bits; io_req.big_endian = iset->state->il_config->big_endian; - rz_th_queue_push(iset->io_request, &io_req, true); - // Wait for load being done. - RzInterpreterIOResult *io_res = NULL; - if (!rz_th_queue_pop(iset->io_result, false, (void **)&io_res) || !io_res) { + if (rz_th_ring_buf_put(iset->io_request_rbuf, &io_req) != RZ_THREAD_RING_BUF_OK) { + return false; + } + RzInterpreterIOResult io_res = { 0 }; + if (rz_th_ring_buf_take_blocking(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { return false; } - if (!io_res->req_ok) { + if (!io_res.req_ok) { RZ_LOG_WARN("Prototype: Failed to read correct number of bytes. Requested: 0x%" PFMTSZx " Received: 0x%" PFMT32x " bits.\n", n_bits, rz_bv_len(out->bv)); From 3577b10821eaac89a790aa8525f09fab697e129d Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 30 Mar 2026 17:34:38 +0200 Subject: [PATCH 237/334] Move initialization code into own function. --- librz/inquiry/inquiry.c | 4 +- librz/inquiry/interpreter/interpreter.c | 80 +++++++++++++------------ 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index fd0588d50f2..6251cd48172 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -520,9 +520,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_LOG_DEBUG("INQUIRY: Start main interpretation thread.\n"); interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); - // Poor man's shared memory. - RzInterpreterIOResult io_res = { 0 }; - // From here on, the code plays the role of the cache, IO handler, // and yield consumer. // - Waiting for new Effects to be requested and sending them. @@ -585,6 +582,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_warn_if_reached(); break; } else if (r == RZ_THREAD_RING_BUF_OK) { + RzInterpreterIOResult io_res = { 0 }; handle_io_request(core, &analysis_vm->vm->vm_memory, &io_req, &io_res); if (rz_th_ring_buf_put(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { rz_warn_if_reached(); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 4ddc6f3dedb..4a14f20c6f0 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -402,6 +402,46 @@ static bool choose_next_pc(RzInterpreterSet *iset, return has_succsessor; } +static bool pre_loop_setup( + RzInterpreterSet *iset, + const RzInterpreterILBB **il_bb, + RzVector **tmp_succ_addr, + RzSetU **reachable_states, + RzVector **succ_states, + void *plugin_data) { + // TODO: Add support for multiple entry points by spawning an interpreter for each of them. + // For now let's just ignore them. + if (rz_vector_len(iset->entry_points) > 1) { + RZ_LOG_ERROR("More than one entry point is not yet supported by the prototype.\n"); + return false; + } + + RzInterpreterBranch branch = { 0 }; + rz_vector_pop_front(iset->entry_points, &branch.target_addr); + if (!iset->plugin->init_state(iset->state, branch.target_addr, plugin_data)) { + rz_warn_if_reached(); + return false; + } + + if (rz_th_ring_buf_put(iset->branch_rbuf, &branch) != RZ_THREAD_RING_BUF_OK) { + rz_warn_if_reached(); + return false; + } + if (!rz_th_queue_pop(iset->il_queue, false, (void **)il_bb) || !*il_bb) { + rz_warn_if_reached(); + return false; + } + + *tmp_succ_addr = rz_vector_new(sizeof(ut64), NULL, NULL); + *succ_states = rz_vector_new(sizeof(SuccessorState), NULL, NULL); + *reachable_states = rz_set_u_new(); + if (!tmp_succ_addr || !succ_states || !*il_bb || !reachable_states) { + rz_warn_if_reached(); + return false; + } + return true; +} + /** * Main interpretation. */ @@ -434,8 +474,6 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // Start interpretation // - RzStrBuf *state_str = rz_strbuf_new(""); - // A vector for the plugin to push the determined successors into. RzVector *tmp_succ_addr = NULL; // The set of reachable states. @@ -443,41 +481,14 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // The successor states to evaluate. // This vector must have the same order as the elements pushed into branch_queue. RzVector *succ_states = NULL; - - // TODO: Add support for multiple entry points by spawning an interpreter for each of them. - // For now let's just ignore them. - if (rz_vector_len(iset->entry_points) > 1) { - RZ_LOG_ERROR("More than one entry point is not yet supported by the prototype.\n"); - goto pre_loop_error; - } - - RzInterpreterBranch branch = { 0 }; - rz_vector_pop_front(iset->entry_points, &branch.target_addr); - if (!plugin->init_state(iset->state, branch.target_addr, plugin_data)) { - rz_warn_if_reached(); - goto pre_loop_error; - } -#if RZ_BUILD_DEBUG - ut64 in_hash = plugin->hash_state(iset->state, plugin_data); -#endif - ut64 out_hash = 0; - - if (rz_th_ring_buf_put(iset->branch_rbuf, &branch) != RZ_THREAD_RING_BUF_OK) { - goto pre_loop_error; - } const RzInterpreterILBB *il_bb = NULL; - if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { - goto pre_loop_error; - } - tmp_succ_addr = rz_vector_new(sizeof(ut64), NULL, NULL); - succ_states = rz_vector_new(sizeof(SuccessorState), NULL, NULL); - reachable_states = rz_set_u_new(); - if (!tmp_succ_addr || !succ_states || !il_bb || !reachable_states) { + if (!pre_loop_setup(iset, &il_bb, &tmp_succ_addr, &reachable_states, &succ_states, plugin_data)) { rz_warn_if_reached(); goto pre_loop_error; } + ut64 out_hash = 0; while (rz_atomic_bool_get(iset->is_running_flag)) { iset->state->bb_addr = il_bb->bb_addr; iset->state->bb_size = il_bb->size; @@ -487,9 +498,6 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { goto in_loop_error; } out_hash = plugin->hash_state(iset->state, plugin_data); -#if RZ_BUILD_DEBUG - RZ_LOG_DEBUG("in_hash = 0x%llx, out_hash = 0x%llx\n", in_hash, out_hash); -#endif // Add output state hash to the reachable states and // set a flag if it was a new state. @@ -510,9 +518,6 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // Set effect and state for next evaluation. SuccessorState next = { 0 }; rz_vector_pop_front(succ_states, &next); -#if RZ_BUILD_DEBUG - in_hash = next.in_state_hash; -#endif if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { // The il op lifting failed. Likely because the PC // pointed to an unmapped region. @@ -527,7 +532,6 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { } loop_cleanup: - rz_strbuf_free(state_str); rz_vector_free(tmp_succ_addr); rz_vector_free(succ_states); rz_set_u_free(reachable_states); From 5b9092e1698e01de2ef77176a55e6eb3ecfa16af Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 30 Mar 2026 19:54:52 +0200 Subject: [PATCH 238/334] Refactor to single running interpreter, instead of spawning mutliple threads. --- librz/include/rz_inquiry/rz_interpreter.h | 45 +++- librz/inquiry/inquiry.c | 277 ++++++++++----------- librz/inquiry/interpreter/interpreter.c | 287 +++++++++++++--------- 3 files changed, 341 insertions(+), 268 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index f71840b5f6d..8c9b157ac13 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -221,19 +221,50 @@ typedef struct { RZ_LIFETIME(RzInquiry) struct rz_interpreter_set { RzInterpreterAbstrState *state; ///< The abstract state of the interpreter. + RzAnalysisILVM *il_vm; ///< The RzAnalysisILVM for memory IO. + RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. RzThreadRingBuf /**/ *branch_rbuf; ///< The ring buffer to send requests to the cache what address to get the next IL op from. RzThreadRingBuf /**/ *io_request_rbuf; ///< The ring buffer for read/write requests to the IO layer. RzThreadRingBuf /**/ *io_result_rbuf; ///< The ring buffer for the read/write requests' answers. - HtUP /**/ *yield_rbufs; ///< The ring buffers to push the yield of interpretation into. - RzAtomicBool *is_running_flag; ///< Flag for the interpreter thread to toggle when done. + + /** + * \brief The ring buffers to push the yield of interpretation into. + * These ring buffers are shared with other interpreter sets. + */ + HtUP /**/ *yield_rbufs; + /** + * \brief This flag signals if an interpreter is executing IL ops for. + * With the exception of an error state, this flag should + * only be toggled by the interpreter itself. + */ + RzAtomicBool *emulating; + /** + * \brief This flag signals if an should stand by for entry points to start interpeting from. + * With the exception of an error state, this flag should + * only be toggled by the inquiry module. + */ + RzAtomicBool *on_duty; + /** + * \brief Ignored address ranges. + */ const RzVector /**/ *ignored_code; /** * \brief The entry points for the interpreters. - * Each address has its lifted IL op in the il_queue at the same index. */ - RzVector /**/ *entry_points; + RzThreadRingBuf /**/ *entry_points; + + RzThreadCond *inq_intrpr_sync; ///< Condition to let interpreter wait until inquiry set up everything. + RzThreadLock *inq_intrpr_lock; ///< Lock for task_sync condition. + bool intrp_waits_to_run; + /** + * \brief The interpreter plugin. + */ RzInterpreterPlugin *plugin; + /** + * \brief The private data of a single interpreter thread. + */ + RZ_BORROW void *intrpr_priv; }; RZ_API void rz_interpreter_il_bb_free(RZ_NULLABLE RZ_OWN RzInterpreterILBB *il_bb); @@ -253,15 +284,13 @@ RZ_API RZ_OWN RzInterpreterYieldRBuf *rz_interpreter_yield_rbuf_new(RzInterprete RZ_OWN RZ_NULLABLE void *filter_data); RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( + RzAnalysis *analysis, RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, - RZ_NONNULL RZ_OWN RzInterpreterAbstrState *state, + RzInterpreterAbstraction abstraction, RZ_OWN RzPVector /**/ *sections, RzInterpreterYieldFilter yield_filter, - RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag, - RZ_NONNULL RZ_OWN RzVector /**/ *entry_points, RZ_NONNULL const RzVector /**/ *ignored_code); RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset); -RZ_API void rz_interpreter_set_add_entry_points(RZ_NONNULL RzInterpreterSet *iset, const RzVector /**/ *entry_points); RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 6251cd48172..8a5129949d9 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -23,6 +23,7 @@ #include "rz_util/rz_log.h" #include "rz_util/rz_set.h" #include "rz_util/rz_str.h" +#include "rz_util/rz_sys.h" #include "rz_vector.h" #include #include @@ -61,9 +62,6 @@ RZ_API bool rz_inquiry_plugin_add(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OW rz_warn_if_reached(); return false; } - if (plugin->p_interpreter->init) { - plugin->p_interpreter->init(ht_sp_find(inquiry->plugins_data, plugin->p_interpreter->name, NULL)); - } return true; } @@ -359,7 +357,7 @@ static const RzInterpreterILBB *get_il_bb(RzCore *core, HtUP *il_cache, ut64 add } static bool send_next_il_bb(RzCore *core, - RzInterpreterSet *iset, + RzThreadQueue *il_queue, HtUP *il_cache, RzSetU *branch_targets, RzInterpreterBranch *branch) { @@ -389,10 +387,78 @@ static bool send_next_il_bb(RzCore *core, rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); } rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); - rz_th_queue_push(iset->il_queue, (void *)bb, true); + rz_th_queue_push(il_queue, (void *)bb, true); return true; } +// TODO: il_cache should be passed here. +static bool add_entry_point( + RzInterpreterSet *iset, + HtUP *il_cache, + RZ_BORROW RzVector /**/ *entry_points, + RzSetU *branch_targets) { + // Add the next entry point we need to check for executable regions the interpreters did not cover. + // For this we simply delete all jump targets from our set, which point + // into the already handled basic blocks. + // Then add a few addresses as new entry point. + // The addresses we add are jump targets from jump/call instructions in the binary. + + rz_vector_clear(entry_points); + RzVector *covered_jump_targets = rz_vector_new(sizeof(ut64), NULL, NULL); + RzIterator *ct_iter = rz_set_u_as_iter(branch_targets); + ut64 *ct; + rz_iterator_foreach(ct_iter, ct) { + if (ht_up_find(il_cache, *ct, NULL)) { + // This call target was interpreted before (hence is in the IL cache). + rz_vector_push(covered_jump_targets, ct); + continue; + } + rz_vector_push(entry_points, ct); + } + rz_iterator_free(ct_iter); + if (rz_vector_empty(entry_points)) { + rz_vector_free(covered_jump_targets); + return false; + } + + // TODO: Push entrypoints evenly to other interpreters + ut64 entry_point = 0; + rz_vector_pop(entry_points, &entry_point); + rz_th_ring_buf_put(iset->entry_points, &entry_point); + + ut64 *ep; + rz_vector_foreach (covered_jump_targets, ep) { + // Delete the selected ones from the jump target set. + // So they are not requested again. + rz_set_u_delete(branch_targets, *ep); + } + rz_vector_free(covered_jump_targets); + if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { + eprintf(RZ_CONS_CLEAR_LINE "\rBranch targets left: %" PFMT32d, rz_set_u_size(branch_targets)); + fflush(stdout); + } + return true; +} + +static void close_reset_ipc_obj(RzInterpreterSet *iset) { + // Close and clear all the IPC objects of this interpreter. + // This also clears the buffer and queues + rz_th_ring_buf_close(iset->io_request_rbuf); + rz_th_ring_buf_close(iset->io_result_rbuf); + rz_th_ring_buf_close(iset->branch_rbuf); + rz_th_queue_close(iset->il_queue); + rz_list_free(rz_th_queue_pop_all(iset->il_queue)); +} + +static void open_ipc_obj(RzInterpreterSet *iset) { + // Open queue again, so the interpretation can start at another + // jump target again. + rz_th_ring_buf_open(iset->io_request_rbuf); + rz_th_ring_buf_open(iset->io_result_rbuf); + rz_th_ring_buf_open(iset->branch_rbuf); + rz_th_queue_open(iset->il_queue); +} + /** * A function to call the prototype interpreter. * Usually these tasks will be split between different caches and yield consumers. @@ -402,13 +468,10 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_NONNULL const RzVector /**/ *ignored_code) { // All the things we need bool return_code = true; - RzAtomicBool *is_running = rz_atomic_bool_new(true); - RzInterpreterAbstrState *abstr_state = NULL; RzInterpreterSet *iset = NULL; HtUP *il_cache = NULL; RzThread *interpr_th = NULL; RzBuffer *io_buf = rz_buf_new_with_io(&core->analysis->iob); - RzAnalysisILVM *analysis_vm = NULL; RzSetU *branch_targets = rz_set_u_new(); RzSetU *symbol_targets = rz_set_u_new(); bool user_sent_signal = false; @@ -416,15 +479,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_cons_push(); - // Here we build the filter for the yield queue. - // The prototype generates constant xrefs. - // So the filter checks the generated xrefs, if they are within the IO map - // boundaries. - RzPVector /**/ *sections = rz_bin_object_get_sections(core->bin->cur->o); - if (!sections) { - goto error_free; - } - // The pseudo cache of IL effects. // This is only a vector so we can simulate the ownership separation // of the pointers. @@ -456,33 +510,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, goto error_free; } - // Perform the RzAnalysisILVM and abstract state setup procedure. - // This prototype won't use the RzAnalysisILVM directly but its components. - // That is because the prototypes doesn't handle the VM tasks (track PC, handle IO) - // in one VM object, but in separated modules. - // So analysis_vm->vm->vm_memorys is used for handling IO requests and - // analysis_vm->reg_binding is used for the abstract state setup. - // - // TODO: Is it a good idea to separate these tasks into different modules? - // It allows the IO handler to buffer reads in r-- sections for multiple interpreters. - // Possibly allows to optimize the IO access, because there is only module accessing it (not every interpreter). - // But is there any other advantage? - { - analysis_vm = rz_analysis_il_vm_new(core->analysis, core->analysis->reg); - if (!analysis_vm) { - RZ_LOG_ERROR("Failed during RzAnalysisILVM setup.\n"); - return_code = false; - goto error_free; - } - - RzAnalysisILConfig *config = core->analysis->cur->il_config(core->analysis); - abstr_state = rz_interpreter_abstr_state_new( - core->analysis->cur->arch, - RZ_INTERPRETER_ABSTRACTION_CONST, - config, - analysis_vm->reg_binding); - } - RZ_LOG_DEBUG("INQUIRY: Enforce enabling IO cache.\n"); const char *io_cache_opt = rz_config_get(core->config, "io.cache"); rz_config_set(core->config, "io.cache", "true"); @@ -498,27 +525,27 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, goto error_free; } iset = rz_interpreter_set_new( - // TODO: Maybe use the pointer from RzCore. - // But in general the whole thing should run without RzCore. + core->analysis, prototype->p_interpreter, - abstr_state, - sections, + RZ_INTERPRETER_ABSTRACTION_CONST, + rz_bin_object_get_sections(core->bin->cur->o), (RzInterpreterYieldFilter)rz_inquiry_xref_interpreter_filter, - is_running, - rz_vector_clone(entry_points), ignored_code); + ut64 entry_point = 0; + rz_vector_pop(entry_points, &entry_point); + rz_th_ring_buf_put(iset->entry_points, &entry_point); + if (!iset) { return_code = false; rz_warn_if_reached(); goto error_free; } - do { - bool bb_decode_failed = false; + // Dispatch prototype interpreter into a thread. + RZ_LOG_DEBUG("INQUIRY: Start main interpretation thread.\n"); + interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); - // Dispatch prototype interpreter into a thread. - RZ_LOG_DEBUG("INQUIRY: Start main interpretation thread.\n"); - interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); + while (rz_atomic_bool_get(iset->on_duty)) { // From here on, the code plays the role of the cache, IO handler, // and yield consumer. @@ -528,13 +555,12 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // In the final implementation each of those roles would be split into // two or more separated modules running in parallel. RZ_LOG_DEBUG("INQUIRY: Start IL providing loop.\n"); - rz_atomic_bool_set(is_running, true); - - while (rz_atomic_bool_get(is_running)) { - if (rz_th_terminated(interpr_th) || rz_cons_is_breaked()) { - rz_atomic_bool_set(is_running, false); - user_sent_signal = rz_cons_is_breaked(); - break; + while (rz_atomic_bool_get(iset->emulating)) { + if (rz_cons_is_breaked()) { + rz_atomic_bool_set(iset->emulating, false); + rz_atomic_bool_set(iset->on_duty, false); + user_sent_signal = true; + goto end_interpretation; } // ========= @@ -551,14 +577,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_warn_if_reached(); break; } else if (r == RZ_THREAD_RING_BUF_OK) { - if (!send_next_il_bb(core, iset, il_cache, branch_targets, &branch)) { + if (!send_next_il_bb(core, iset->il_queue, il_cache, branch_targets, &branch)) { // Signal interpreter the lifting failed. - rz_atomic_bool_set(is_running, false); - rz_th_ring_buf_close(iset->io_request_rbuf); - rz_th_ring_buf_close(iset->io_result_rbuf); - rz_th_ring_buf_close(iset->branch_rbuf); - rz_th_queue_close(iset->il_queue); - bb_decode_failed = true; + rz_atomic_bool_set(iset->emulating, false); break; } } @@ -583,7 +604,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, break; } else if (r == RZ_THREAD_RING_BUF_OK) { RzInterpreterIOResult io_res = { 0 }; - handle_io_request(core, &analysis_vm->vm->vm_memory, &io_req, &io_res); + handle_io_request(core, &iset->il_vm->vm->vm_memory, &io_req, &io_res); if (rz_th_ring_buf_put(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { rz_warn_if_reached(); break; @@ -600,78 +621,57 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // In our prototype it only receives xrefs and call candidates. { if (!handle_yields(core, iset->yield_rbufs)) { - rz_atomic_bool_set(is_running, false); + rz_warn_if_reached(); + rz_atomic_bool_set(iset->emulating, false); + rz_atomic_bool_set(iset->on_duty, false); break; } } } - rz_th_ring_buf_close(iset->io_request_rbuf); - rz_th_ring_buf_close(iset->io_result_rbuf); - rz_th_ring_buf_close(iset->branch_rbuf); - rz_th_queue_close(iset->il_queue); - - RZ_LOG_DEBUG("INQUIRY: Wait for join\n"); - rz_th_wait(interpr_th); - bool interpr_ret = rz_th_get_retv(interpr_th); - rz_th_free(interpr_th); - if ((!interpr_ret && !bb_decode_failed) || user_sent_signal) { - if (!user_sent_signal) { - RZ_LOG_ERROR("Interpreter failed with an error. Abort.\n"); - } else { - RZ_LOG_ERROR("User sent signal.\n"); - } - break; - } - // Open queue again, so the interpretation can start at another - // jump target again. - rz_th_ring_buf_open(iset->io_request_rbuf); - rz_th_ring_buf_open(iset->io_result_rbuf); - rz_th_ring_buf_open(iset->branch_rbuf); - rz_th_queue_open(iset->il_queue); - rz_list_free(rz_th_queue_pop_all(iset->il_queue)); - - // At this point the interpreter is finished and returned. - // Now we need to check for executable regions it did not cover. - // For this we simply delete all jump targets from our set, which point - // into the already handled basic blocks. - // Then add a few addresses as new entry point. - // The addresses we add are jump targets from jump/call instructions in the binary. - { - rz_vector_clear(entry_points); - RzVector *covered_jump_targets = rz_vector_new(sizeof(ut64), NULL, NULL); - RzIterator *ct_iter = rz_set_u_as_iter(branch_targets); - size_t x = 0; - ut64 *ct; - rz_iterator_foreach(ct_iter, ct) { - if (ht_up_find(il_cache, *ct, NULL)) { - // This call target was interpreted before (hence is in the IL cache). - rz_vector_push(covered_jump_targets, ct); - continue; - } - x++; - rz_vector_push(entry_points, ct); - // Experiment how many new entry points we add. - if (x >= 1) { - break; - } - } - rz_iterator_free(ct_iter); - rz_interpreter_set_add_entry_points(iset, entry_points); - - ut64 *ep; - rz_vector_foreach (covered_jump_targets, ep) { - // Delete the selected ones from the jump target set. - // So they are not requested again. - rz_set_u_delete(branch_targets, *ep); - } - rz_vector_free(covered_jump_targets); + // ================= + // CLEAR IPC OBJECTS + // ================= + close_reset_ipc_obj(iset); + + // busy wait until the interpreter waits to take the next emulation task. + RZ_LOG_DEBUG("INQUIRY: Wait until interpreter thread is ready.\n"); + bool intrp_waits_to_run = false; + do { + rz_th_lock_enter(iset->inq_intrpr_lock); + intrp_waits_to_run = iset->intrp_waits_to_run; + rz_th_lock_leave(iset->inq_intrpr_lock); + } while (!intrp_waits_to_run); + RZ_LOG_DEBUG("INQUIRY: Interpreter thread is ready.\n"); + + // TODO: This syncro thingy doesn't work as nicely as I wish. + open_ipc_obj(iset); + + // At this point the interpreter is finished and is waiting for the next emulation task. + if (!add_entry_point(iset, il_cache, entry_points, branch_targets)) { + // Non left. + rz_atomic_bool_set(iset->on_duty, false); } - if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { - eprintf(RZ_CONS_CLEAR_LINE "\rBranch targets left: %" PFMT32d, rz_set_u_size(branch_targets)); - fflush(stdout); + rz_atomic_bool_set(iset->emulating, true); + rz_th_cond_signal_all(iset->inq_intrpr_sync); + } + +end_interpretation: + + RZ_LOG_DEBUG("INQUIRY: Wait for join\n"); + rz_atomic_bool_set(iset->on_duty, false); + close_reset_ipc_obj(iset); + rz_th_wait(interpr_th); + bool interpr_ret = rz_th_get_retv(interpr_th); + rz_th_free(interpr_th); + if (!interpr_ret || user_sent_signal) { + return_code = false; + if (!user_sent_signal) { + RZ_LOG_ERROR("Interpreter failed with an error. Abort.\n"); + } else { + RZ_LOG_ERROR("User sent signal.\n"); } - } while (!rz_vector_empty(entry_points)); + } if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { eprintf("\n"); @@ -698,14 +698,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_vector_free(entry_points); rz_set_u_free(branch_targets); rz_buf_free(io_buf); - rz_analysis_il_vm_free(analysis_vm); rz_vector_free(insn_to_insn_edges); - if (!iset) { - // Ownership of all those objects wasn't yet passed to the iset. - rz_atomic_bool_free(is_running); - rz_interpreter_abstr_state_free(abstr_state); - } else { + if (iset) { // Ownership was passed to iset rz_interpreter_set_free(iset); } diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 4a14f20c6f0..9f2f8bd629b 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -171,34 +171,48 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { if (iset->io_result_rbuf) { rz_th_ring_buf_free(iset->io_result_rbuf); } - if (iset->is_running_flag) { - rz_atomic_bool_free(iset->is_running_flag); + if (iset->emulating) { + rz_atomic_bool_free(iset->emulating); + } + if (iset->on_duty) { + rz_atomic_bool_free(iset->on_duty); } if (iset->state) { rz_interpreter_abstr_state_free(iset->state); } + if (iset->il_vm) { + rz_analysis_il_vm_free(iset->il_vm); + } if (iset->yield_rbufs) { ht_up_free(iset->yield_rbufs); } if (iset->entry_points) { - rz_vector_free(iset->entry_points); + rz_th_ring_buf_free(iset->entry_points); + } + if (iset->inq_intrpr_lock) { + rz_th_lock_free(iset->inq_intrpr_lock); + } + if (iset->inq_intrpr_sync) { + rz_th_cond_free(iset->inq_intrpr_sync); } free(iset); } -static bool setup_queues( +static bool setup_ipc_objects( RZ_OWN RzPVector /**/ *sections, RzInterpreterYieldFilter yield_filter, RZ_OUT RzThreadQueue **il_queue, RZ_OUT RzThreadRingBuf **io_request_rbuf, RZ_OUT RzThreadRingBuf **io_result_rbuf, RZ_OUT RzThreadRingBuf **branch_rbuf, + RZ_OUT RzThreadRingBuf **entry_points_rbuf, RZ_OUT HtUP **yield_rbufs) { *il_queue = NULL; *io_request_rbuf = NULL; *io_result_rbuf = NULL; *branch_rbuf = NULL; *yield_rbufs = NULL; + *entry_points_rbuf = NULL; RzInterpreterYieldRBuf *rbuf = NULL; // The queue to pass the Effects to the interpreter. @@ -222,9 +236,7 @@ static bool setup_queues( goto error_free; } - // The branch ring buffer. It is the ring buffer the interpreter can request new Effects. - // Of course, currently there is only a single one for the prototype. - // In practice there would be one for each interpreter instance. + // The branch ring buffer. It is the ring buffer the interpreter can request new Effects over. *branch_rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_ADDR_RBUF_SIZE, sizeof(RzInterpreterBranch)); if (!*branch_rbuf) { rz_warn_if_reached(); @@ -232,6 +244,15 @@ static bool setup_queues( goto error_free; } + // The entry_points ring buffer. It is the ring buffer the interpreter gets + // new entry points passed over. + *entry_points_rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_IL_QUEUE_SIZE, sizeof(ut64)); + if (!*entry_points_rbuf) { + rz_warn_if_reached(); + rz_pvector_free(sections); + goto error_free; + } + // Multiple yield queues can be used by a single interpreter. // E.g. if the interpreter has a complex abstract memory model // for stack, heap and constant values. @@ -281,48 +302,78 @@ static bool setup_queues( * If it fails, all arguments are freed. */ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( + RzAnalysis *analysis, RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, - RZ_NONNULL RZ_OWN RzInterpreterAbstrState *state, + RzInterpreterAbstraction abstraction, RZ_OWN RzPVector /**/ *sections, RzInterpreterYieldFilter yield_filter, - RZ_NONNULL RZ_OWN RzAtomicBool *is_running_flag, - RZ_NONNULL RZ_OWN RzVector /**/ *entry_points, RZ_NONNULL const RzVector /**/ *ignored_code) { - rz_return_val_if_fail(plugin && state && is_running_flag && entry_points && ignored_code, NULL); + rz_return_val_if_fail(plugin && ignored_code && analysis, NULL); + + if (abstraction != (plugin->supported_abstractions & abstraction)) { + RZ_LOG_ERROR("Plugin does not support all required abstractions.\n"); + return NULL; + } - RzInterpreterSet *set = RZ_NEW0(RzInterpreterSet); - if (!set) { - rz_vector_free(entry_points); + RzInterpreterSet *iset = RZ_NEW0(RzInterpreterSet); + if (!iset) { rz_pvector_free(sections); return NULL; } + + // Perform the RzAnalysisILVM and abstract state setup procedure. + // This prototype won't use the RzAnalysisILVM directly but its components. + // That is because the prototypes doesn't handle the VM tasks (track PC, handle IO) + // in one VM object, but in separated modules. + // So analysis_vm->vm->vm_memorys is used for handling IO requests and + // analysis_vm->reg_binding is used for the abstract state setup. + // + // TODO: Is it a good idea to separate these tasks into different modules? + // It allows the IO handler to buffer reads in r-- sections for multiple interpreters. + // Possibly allows to optimize the IO access, because there is only module accessing it (not every interpreter). + // But is there any other advantage? + RzAnalysisILVM *il_vm = rz_analysis_il_vm_new(analysis, analysis->reg); + if (!il_vm) { + free(iset); + RZ_LOG_ERROR("Failed during RzAnalysisILVM setup.\n"); + return NULL; + } + + RzAnalysisILConfig *config = analysis->cur->il_config(analysis); + RzInterpreterAbstrState *state = rz_interpreter_abstr_state_new( + analysis->cur->arch, + abstraction, + config, + il_vm->reg_binding); + RzThreadRingBuf *io_request_rbuf = NULL; RzThreadRingBuf *io_result_rbuf = NULL; RzThreadRingBuf *branch_rbuf = NULL; + RzThreadRingBuf *entry_points = NULL; RzThreadQueue *il_queue = NULL; HtUP *yield_rbufs = NULL; - if (!setup_queues(sections, yield_filter, &il_queue, &io_request_rbuf, &io_result_rbuf, &branch_rbuf, &yield_rbufs)) { - rz_vector_free(entry_points); - free(set); + if (!setup_ipc_objects(sections, yield_filter, &il_queue, &io_request_rbuf, &io_result_rbuf, &branch_rbuf, &entry_points, &yield_rbufs)) { + free(iset); + rz_analysis_il_vm_free(il_vm); return NULL; } - set->plugin = plugin; - set->state = state; - set->il_queue = il_queue; - set->branch_rbuf = branch_rbuf; - set->yield_rbufs = yield_rbufs; - set->io_request_rbuf = io_request_rbuf; - set->io_result_rbuf = io_result_rbuf; - set->is_running_flag = is_running_flag; - set->entry_points = entry_points; - set->ignored_code = ignored_code; - if (state->kinds != (plugin->supported_abstractions & state->kinds)) { - RZ_LOG_ERROR("Abstract state doesn't fit to interpreter.\n"); - rz_interpreter_set_free(set); - return NULL; - } - return set; + iset->plugin = plugin; + iset->state = state; + iset->il_vm = il_vm; + iset->il_queue = il_queue; + iset->branch_rbuf = branch_rbuf; + iset->yield_rbufs = yield_rbufs; + iset->io_request_rbuf = io_request_rbuf; + iset->io_result_rbuf = io_result_rbuf; + iset->emulating = rz_atomic_bool_new(true); + iset->on_duty = rz_atomic_bool_new(true); + iset->inq_intrpr_lock = rz_th_lock_new(false); + iset->inq_intrpr_sync = rz_th_cond_new(); + iset->intrp_waits_to_run = false; + iset->entry_points = entry_points; + iset->ignored_code = ignored_code; + return iset; } static bool jumps_to_ignored_code(const RzVector *v, ut64 jump_target) { @@ -336,12 +387,6 @@ static bool jumps_to_ignored_code(const RzVector *v, ut64 jump_target) { return false; } -RZ_API void rz_interpreter_set_add_entry_points(RZ_NONNULL RzInterpreterSet *iset, const RzVector /**/ *entry_points) { - rz_return_if_fail(iset && entry_points); - rz_vector_clear(iset->entry_points); - rz_vector_clone_intof(iset->entry_points, entry_points, NULL); -} - typedef struct { ut64 addr; ut64 in_state_hash; @@ -351,18 +396,17 @@ static bool choose_next_pc(RzInterpreterSet *iset, ut64 out_hash, RzVector *tmp_succ_addr, RzVector *succ_states, - const RzInterpreterILBB *il_bb, - void *plugin_data) { + const RzInterpreterILBB *il_bb) { // Debug printing whole state of VM. // - // plugin->state_as_str(out_state, state_str, plugin_data); + // plugin->state_as_str(out_state, state_str, iset->intrpr_priv); // char *s = rz_strbuf_drain_nofree(state_str); // RZ_LOG_DEBUG("%s", s); // free(s); bool has_succsessor = true; // Determine successors and increase the reference counts for the current out state. - if (!iset->plugin->successors(iset->state, tmp_succ_addr, plugin_data)) { + if (!iset->plugin->successors(iset->state, tmp_succ_addr, iset->intrpr_priv)) { rz_warn_if_reached(); return false; } @@ -402,27 +446,25 @@ static bool choose_next_pc(RzInterpreterSet *iset, return has_succsessor; } -static bool pre_loop_setup( +static bool setup_intrpr_state( RzInterpreterSet *iset, + ut64 entry_point, const RzInterpreterILBB **il_bb, RzVector **tmp_succ_addr, RzSetU **reachable_states, - RzVector **succ_states, - void *plugin_data) { - // TODO: Add support for multiple entry points by spawning an interpreter for each of them. - // For now let's just ignore them. - if (rz_vector_len(iset->entry_points) > 1) { - RZ_LOG_ERROR("More than one entry point is not yet supported by the prototype.\n"); - return false; + RzVector **succ_states) { + + if (iset->plugin->init) { + iset->plugin->init(&iset->intrpr_priv); } - RzInterpreterBranch branch = { 0 }; - rz_vector_pop_front(iset->entry_points, &branch.target_addr); - if (!iset->plugin->init_state(iset->state, branch.target_addr, plugin_data)) { + if (!iset->plugin->init_state(iset->state, entry_point, iset->intrpr_priv)) { rz_warn_if_reached(); return false; } + RzInterpreterBranch branch = { 0 }; + branch.target_addr = entry_point; if (rz_th_ring_buf_put(iset->branch_rbuf, &branch) != RZ_THREAD_RING_BUF_OK) { rz_warn_if_reached(); return false; @@ -451,7 +493,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { iset->branch_rbuf && iset->il_queue && iset->yield_rbufs && - iset->is_running_flag && + iset->emulating && iset->plugin && iset->plugin->eval && iset->plugin->successors && @@ -464,12 +506,6 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { RZ_LOG_DEBUG("INTERPRETER Main: Hello.\n"); RzInterpreterPlugin *plugin = iset->plugin; - void *priv_ptr = NULL; - if (iset->plugin->init) { - iset->plugin->init(&priv_ptr); - } - void *plugin_data = priv_ptr ? priv_ptr : NULL; - // // Start interpretation // @@ -483,76 +519,89 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { RzVector *succ_states = NULL; const RzInterpreterILBB *il_bb = NULL; - if (!pre_loop_setup(iset, &il_bb, &tmp_succ_addr, &reachable_states, &succ_states, plugin_data)) { - rz_warn_if_reached(); - goto pre_loop_error; - } - - ut64 out_hash = 0; - while (rz_atomic_bool_get(iset->is_running_flag)) { - iset->state->bb_addr = il_bb->bb_addr; - iset->state->bb_size = il_bb->size; - // Evaluate the effect on the input state. - if (!plugin->eval(iset, il_bb, plugin_data)) { - RZ_LOG_DEBUG("Eval failed\n"); - goto in_loop_error; + while (rz_atomic_bool_get(iset->on_duty)) { + ut64 entry_point; + if (rz_th_ring_buf_take_blocking(iset->entry_points, &entry_point) != RZ_THREAD_RING_BUF_OK) { + rz_atomic_bool_set(iset->on_duty, false); + rz_atomic_bool_set(iset->on_duty, false); + return true; } - out_hash = plugin->hash_state(iset->state, plugin_data); - - // Add output state hash to the reachable states and - // set a flag if it was a new state. - size_t psize = rz_set_u_size(reachable_states); - rz_set_u_add(reachable_states, out_hash); - bool new_state_reached = psize < rz_set_u_size(reachable_states); - - // Determine the successor effects to evaluate. - // Only newly reached states are allowed to add successors. - if (!(new_state_reached && choose_next_pc(iset, out_hash, tmp_succ_addr, succ_states, il_bb, plugin_data))) { - // No new state or address means we can stop interpreting. - // Note, that we can't use the queues as cancel condition because they - // are asynchronous and checking them would introduces race conditions. - // TODO: This doesn't work if the interpreter can produce multiple out states. - break; + + // Initializes the current interpreter's private data and its state. + if (!setup_intrpr_state(iset, entry_point, &il_bb, &tmp_succ_addr, &reachable_states, &succ_states)) { + goto pre_emu_loop_error; } - // Set effect and state for next evaluation. - SuccessorState next = { 0 }; - rz_vector_pop_front(succ_states, &next); - if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { - // The il op lifting failed. Likely because the PC - // pointed to an unmapped region. - goto in_loop_error; + rz_atomic_bool_set(iset->emulating, true); + ut64 out_hash = 0; + while (rz_atomic_bool_get(iset->emulating)) { + iset->state->bb_addr = il_bb->bb_addr; + iset->state->bb_size = il_bb->size; + // Evaluate the effect on the input state. + if (!plugin->eval(iset, il_bb, iset->intrpr_priv)) { + RZ_LOG_DEBUG("Eval failed\n"); + goto emu_done; + } + out_hash = plugin->hash_state(iset->state, iset->intrpr_priv); + + // Add output state hash to the reachable states and + // set a flag if it was a new state. + size_t psize = rz_set_u_size(reachable_states); + rz_set_u_add(reachable_states, out_hash); + bool new_state_reached = psize < rz_set_u_size(reachable_states); + + // Determine the successor effects to evaluate. + // Only newly reached states are allowed to add successors. + if (!(new_state_reached && choose_next_pc(iset, out_hash, tmp_succ_addr, succ_states, il_bb))) { + // No new state or address means we can stop interpreting. + // Note, that we can't use the queues as cancel condition because they + // are asynchronous and checking them would introduces race conditions. + // TODO: This doesn't work if the interpreter can produce multiple out states. + goto emu_done; + } + + // Set effect and state for next evaluation. + SuccessorState next = { 0 }; + rz_vector_pop_front(succ_states, &next); + if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { + RZ_LOG_DEBUG("Getting il bb failed\n"); + // The il op lifting failed. Likely because the PC + // pointed to an unmapped region. + goto emu_done; + } + if (!plugin->set_pc(iset->state, next.addr, iset->intrpr_priv)) { + rz_warn_if_reached(); + goto emu_done; + } } - if (!plugin->set_pc(iset->state, next.addr, plugin_data)) { - rz_warn_if_reached(); - // Some error occurred lifting this basic block. Or updating the PC. - // Abort execution. - goto in_loop_error; + emu_done: + rz_atomic_bool_set(iset->emulating, false); + RZ_FREE_CUSTOM(tmp_succ_addr, rz_vector_free); + RZ_FREE_CUSTOM(succ_states, rz_vector_free); + RZ_FREE_CUSTOM(reachable_states, rz_set_u_free); + if (iset->plugin->fini) { + iset->plugin->fini(iset->intrpr_priv); } - } + iset->plugin->fini_state(iset->state, iset->intrpr_priv); -loop_cleanup: - rz_vector_free(tmp_succ_addr); - rz_vector_free(succ_states); - rz_set_u_free(reachable_states); - iset->plugin->fini_state(iset->state, plugin_data); - if (iset->plugin->fini) { - iset->plugin->fini(plugin_data); + // Wait until RzInquiry asks to start again. + rz_th_lock_enter(iset->inq_intrpr_lock); + iset->intrp_waits_to_run = true; + rz_th_cond_wait(iset->inq_intrpr_sync, iset->inq_intrpr_lock); + iset->intrp_waits_to_run = false; + rz_th_lock_leave(iset->inq_intrpr_lock); } - rz_atomic_bool_set(iset->is_running_flag, false); + rz_atomic_bool_set(iset->on_duty, false); return success; -in_loop_error: - RZ_LOG_DEBUG("in_loop_error\n"); - success = false; - goto loop_cleanup; - -pre_loop_error: +pre_emu_loop_error: RZ_LOG_DEBUG("pre_loop_error\n"); + rz_warn_if_reached(); + rz_atomic_bool_set(iset->on_duty, false); success = false; - goto loop_cleanup; + goto emu_done; entry_assert_error: - rz_atomic_bool_set(iset->is_running_flag, false); + rz_atomic_bool_set(iset->on_duty, false); return false; } From 04595ccf1c98883ddf128806fcc9da6e6b4bf3e8 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 1 Apr 2026 17:52:48 +0200 Subject: [PATCH 239/334] Rename state to astate (for abstract). --- librz/include/rz_inquiry/rz_interpreter.h | 2 +- librz/inquiry/interpreter/interpreter.c | 22 +++++++++---------- .../interpreter/p/interpreter_prototype.c | 8 +++---- librz/inquiry/interpreter/prototype/eval.c | 20 ++++++++--------- .../interpreter/prototype/eval_effect.c | 16 +++++++------- .../inquiry/interpreter/prototype/eval_pure.c | 4 ++-- 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 8c9b157ac13..ef4fd21fad4 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -220,7 +220,7 @@ typedef struct { */ RZ_LIFETIME(RzInquiry) struct rz_interpreter_set { - RzInterpreterAbstrState *state; ///< The abstract state of the interpreter. + RzInterpreterAbstrState *astate; ///< The abstract state of the interpreter. RzAnalysisILVM *il_vm; ///< The RzAnalysisILVM for memory IO. RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 9f2f8bd629b..3eda1d2fa19 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -177,8 +177,8 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { if (iset->on_duty) { rz_atomic_bool_free(iset->on_duty); } - if (iset->state) { - rz_interpreter_abstr_state_free(iset->state); + if (iset->astate) { + rz_interpreter_abstr_state_free(iset->astate); } if (iset->il_vm) { rz_analysis_il_vm_free(iset->il_vm); @@ -359,7 +359,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( } iset->plugin = plugin; - iset->state = state; + iset->astate = state; iset->il_vm = il_vm; iset->il_queue = il_queue; iset->branch_rbuf = branch_rbuf; @@ -406,7 +406,7 @@ static bool choose_next_pc(RzInterpreterSet *iset, bool has_succsessor = true; // Determine successors and increase the reference counts for the current out state. - if (!iset->plugin->successors(iset->state, tmp_succ_addr, iset->intrpr_priv)) { + if (!iset->plugin->successors(iset->astate, tmp_succ_addr, iset->intrpr_priv)) { rz_warn_if_reached(); return false; } @@ -458,7 +458,7 @@ static bool setup_intrpr_state( iset->plugin->init(&iset->intrpr_priv); } - if (!iset->plugin->init_state(iset->state, entry_point, iset->intrpr_priv)) { + if (!iset->plugin->init_state(iset->astate, entry_point, iset->intrpr_priv)) { rz_warn_if_reached(); return false; } @@ -489,7 +489,7 @@ static bool setup_intrpr_state( */ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { rz_goto_if_fail(iset && - iset->state && + iset->astate && iset->branch_rbuf && iset->il_queue && iset->yield_rbufs && @@ -535,14 +535,14 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { rz_atomic_bool_set(iset->emulating, true); ut64 out_hash = 0; while (rz_atomic_bool_get(iset->emulating)) { - iset->state->bb_addr = il_bb->bb_addr; - iset->state->bb_size = il_bb->size; + iset->astate->bb_addr = il_bb->bb_addr; + iset->astate->bb_size = il_bb->size; // Evaluate the effect on the input state. if (!plugin->eval(iset, il_bb, iset->intrpr_priv)) { RZ_LOG_DEBUG("Eval failed\n"); goto emu_done; } - out_hash = plugin->hash_state(iset->state, iset->intrpr_priv); + out_hash = plugin->hash_state(iset->astate, iset->intrpr_priv); // Add output state hash to the reachable states and // set a flag if it was a new state. @@ -569,7 +569,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // pointed to an unmapped region. goto emu_done; } - if (!plugin->set_pc(iset->state, next.addr, iset->intrpr_priv)) { + if (!plugin->set_pc(iset->astate, next.addr, iset->intrpr_priv)) { rz_warn_if_reached(); goto emu_done; } @@ -582,7 +582,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { if (iset->plugin->fini) { iset->plugin->fini(iset->intrpr_priv); } - iset->plugin->fini_state(iset->state, iset->intrpr_priv); + iset->plugin->fini_state(iset->astate, iset->intrpr_priv); // Wait until RzInquiry asks to start again. rz_th_lock_enter(iset->inq_intrpr_lock); diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index f924714f388..b436a7acf75 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -25,7 +25,7 @@ static bool eval(RZ_NONNULL RzInterpreterSet *iset, } else if (ic_pc > MAX_INVOCATIONS_PER_BB) { // TODO: Make it configurable RZ_LOG_DEBUG("Reached maximum number of invocations of basic block at 0x%" PFMT64x ". Skipping it.\n", il_bb->bb_addr) - set_pc(iset->state, il_bb->bb_addr + il_bb->size, plugin_data); + set_pc(iset->astate, il_bb->bb_addr + il_bb->size, plugin_data); return true; } ht_uu_update(pdata->bb_invocation_count, il_bb->bb_addr, ic_pc + 1); @@ -37,15 +37,15 @@ static bool eval(RZ_NONNULL RzInterpreterSet *iset, // Now execute the actual effects of the BB. void **it; rz_pvector_foreach (il_bb->il_ops, it) { - ut64 pc = rz_bv_to_ut64(AD(iset->state->pc->abstr_data)->bv); + ut64 pc = rz_bv_to_ut64(AD(iset->astate->pc->abstr_data)->bv); RZ_LOG_DEBUG("Eval PC = 0x%" PFMT64x "\n", pc); RzInterpreterInsnPkt *pkt = *it; if (!interpreter_prototype_eval_effect(iset, pkt->effect, pkt->insn_pkt_size, plugin_data)) { return false; } - if (pc == rz_bv_to_ut64(AD(iset->state->pc->abstr_data)->bv) && AD(iset->state->pc->abstr_data)->is_concrete) { + if (pc == rz_bv_to_ut64(AD(iset->astate->pc->abstr_data)->bv) && AD(iset->astate->pc->abstr_data)->is_concrete) { // Instruction did not manipulate the PC. Set it to the next instruction (packet). - set_pc(iset->state, pc + pkt->insn_pkt_size, plugin_data); + set_pc(iset->astate, pc + pkt->insn_pkt_size, plugin_data); } } // TODO: Clean up local variables. diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index ab38fb56657..7318ea8e556 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -21,7 +21,7 @@ bool report_yield_xref( return true; } if (type == RZ_ANALYSIS_XREF_TYPE_CODE && - RZ_STR_EQ(iset->state->arch_name, "hexagon") && + RZ_STR_EQ(iset->astate->arch_name, "hexagon") && from + insn_pkt_size == rz_bv_to_ut64(to->bv)) { // Ugly work around. // Because we don't have RzArch yet the Hexagon plugin adds a JUMP at the @@ -39,7 +39,7 @@ bool report_yield_xref( ut64 to_addr = rz_bv_to_ut64(to->bv); if (yrbuf->filter(&to_addr, yrbuf->filter_data->io_boundaries)) { RzAnalysisXRef xref = { 0 }; - xref.bb_addr = iset->state->bb_addr; + xref.bb_addr = iset->astate->bb_addr; xref.from = from; xref.to = to_addr; xref.type = type; @@ -84,13 +84,13 @@ void write_var_to_state(RzInterpreterSet *iset, rz_warn_if_reached(); return; case RZ_IL_VAR_KIND_GLOBAL: - ht_vals = iset->state->globals; + ht_vals = iset->astate->globals; break; case RZ_IL_VAR_KIND_LOCAL: - ht_vals = iset->state->locals; + ht_vals = iset->astate->locals; break; case RZ_IL_VAR_KIND_LOCAL_PURE: - ht_vals = iset->state->lets; + ht_vals = iset->astate->lets; break; } RzInterpreterAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); @@ -118,13 +118,13 @@ bool read_var_from_state(RzInterpreterSet *iset, rz_warn_if_reached(); return false; case RZ_IL_VAR_KIND_GLOBAL: - ht_vals = iset->state->globals; + ht_vals = iset->astate->globals; break; case RZ_IL_VAR_KIND_LOCAL: - ht_vals = iset->state->locals; + ht_vals = iset->astate->locals; break; case RZ_IL_VAR_KIND_LOCAL_PURE: - ht_vals = iset->state->lets; + ht_vals = iset->astate->lets; break; } RzInterpreterAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); @@ -163,7 +163,7 @@ bool store_abstr_data( RzInterpreterIORequest io_req = { 0 }; io_req.n_bits = rz_bv_len(src->bv); io_req.mem_idx = mem_idx; - io_req.big_endian = iset->state->il_config->big_endian; + io_req.big_endian = iset->astate->il_config->big_endian; io_req.type = RZ_INTERPRETER_IO_WRITE; io_req.addr = addr->bv; @@ -197,7 +197,7 @@ bool load_abstr_data( io_req.ld_data = out->bv; io_req.mem_idx = mem_idx; io_req.n_bits = n_bits; - io_req.big_endian = iset->state->il_config->big_endian; + io_req.big_endian = iset->astate->il_config->big_endian; if (rz_th_ring_buf_put(iset->io_request_rbuf, &io_req) != RZ_THREAD_RING_BUF_OK) { return false; } diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 9edc5b883a4..003be75b8c9 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -9,10 +9,10 @@ static bool value_indicates_ret_addr_write(RzInterpreterSet *iset, ProtoIntrprAbstrData *val) { return val->is_concrete && - (rz_bv_to_ut64(val->bv) == iset->state->bb_addr + iset->state->bb_size || + (rz_bv_to_ut64(val->bv) == iset->astate->bb_addr + iset->astate->bb_size || // Sparc stores the call instruction PC into o8. // The return instruction jumps then to o7+8. - (rz_str_startswith(iset->state->arch_name, "sparc") && rz_bv_to_ut64(val->bv) == rz_bv_to_ut64(AD(iset->state->pc->abstr_data)->bv))); + (rz_str_startswith(iset->astate->arch_name, "sparc") && rz_bv_to_ut64(val->bv) == rz_bv_to_ut64(AD(iset->astate->pc->abstr_data)->bv))); } RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterSet *iset, @@ -20,7 +20,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterSet *iset, size_t insn_pkt_size, ProtoIntrprPluginData *plugin_data) { STACK_ABSTR_DATA_OUT(eval_out); - ProtoIntrprAbstrData *pc = AD(iset->state->pc->abstr_data); + ProtoIntrprAbstrData *pc = AD(iset->astate->pc->abstr_data); switch (effect->code) { default: @@ -68,8 +68,8 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterSet *iset, if (value_indicates_ret_addr_write(iset, &eval_out) && kind == RZ_IL_VAR_KIND_GLOBAL) { plugin_data->call_cand.store_addr = rz_bv_to_ut64(pc->bv); - plugin_data->call_cand.npc = iset->state->bb_addr + iset->state->bb_size; - plugin_data->call_cand.bb_addr = iset->state->bb_addr; + plugin_data->call_cand.npc = iset->astate->bb_addr + iset->astate->bb_size; + plugin_data->call_cand.bb_addr = iset->astate->bb_addr; plugin_data->call_cand.in_mem = false; } break; @@ -100,7 +100,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterSet *iset, report_yield_call_candiate(iset, plugin_data); } memset(&plugin_data->call_cand, 0, sizeof(plugin_data->call_cand)); - copy_abstr_data(iset->state->pc->abstr_data, &eval_out); + copy_abstr_data(iset->astate->pc->abstr_data, &eval_out); break; } case RZ_IL_OP_BRANCH: { @@ -160,8 +160,8 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterSet *iset, } if (value_indicates_ret_addr_write(iset, &eval_out)) { plugin_data->call_cand.store_addr = rz_bv_to_ut64(pc->bv); - plugin_data->call_cand.npc = iset->state->bb_addr + iset->state->bb_size; - plugin_data->call_cand.bb_addr = iset->state->bb_addr; + plugin_data->call_cand.npc = iset->astate->bb_addr + iset->astate->bb_size; + plugin_data->call_cand.bb_addr = iset->astate->bb_addr; plugin_data->call_cand.in_mem = true; } report_yield_xref(iset, insn_pkt_size, rz_bv_to_ut64(pc->bv), &st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 8a3a23940e2..be6ac5bd104 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -431,8 +431,8 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_and_inplace(ld_addr.bv, &mask); } - report_yield_xref(iset, 0, rz_bv_to_ut64(AD(iset->state->pc->abstr_data)->bv), &ld_addr, RZ_ANALYSIS_XREF_TYPE_MEM_READ); - size_t n_bits = pure->code == RZ_IL_OP_LOAD ? iset->state->il_config->mem_key_size : pure->op.loadw.n_bits; + report_yield_xref(iset, 0, rz_bv_to_ut64(AD(iset->astate->pc->abstr_data)->bv), &ld_addr, RZ_ANALYSIS_XREF_TYPE_MEM_READ); + size_t n_bits = pure->code == RZ_IL_OP_LOAD ? iset->astate->il_config->mem_key_size : pure->op.loadw.n_bits; if (!load_abstr_data(iset, mem_idx, &ld_addr, n_bits, out)) { rz_bv_fini(ld_addr.bv); goto map_to_bottom; From 7746574b16ad46502a7c50fc9962d0454c3492c0 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 1 Apr 2026 18:00:00 +0200 Subject: [PATCH 240/334] Add state flag for the interpreter skeleton. --- librz/include/rz_inquiry/rz_interpreter.h | 8 +++ librz/include/rz_inquiry/rz_state.h | 10 ++++ librz/inquiry/interpreter/state.c | 67 +++++++++++++++++++++++ librz/inquiry/state.c | 1 + 4 files changed, 86 insertions(+) create mode 100644 librz/include/rz_inquiry/rz_state.h create mode 100644 librz/inquiry/interpreter/state.c create mode 100644 librz/inquiry/state.c diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index ef4fd21fad4..f21e3058ce1 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -24,6 +24,9 @@ #define RZ_INTERPRETER_ADDR_RBUF_SIZE 1024 #define RZ_INTERPRETER_YIELD_RBUF_SIZE 128 +typedef struct rz_intp_state RzIntpState; +typedef enum rz_intp_state_flag RzIntpStateFlag; + /** * \brief The abstractions this module supports. */ @@ -267,6 +270,11 @@ struct rz_interpreter_set { RZ_BORROW void *intrpr_priv; }; +RZ_API RZ_OWN RzIntpState *rz_intp_state_new(); +RZ_API void rz_intp_state_free(RZ_OWN RZ_NULLABLE RzIntpState *state); +RZ_API RzIntpStateFlag rz_intp_state_get(RZ_BORROW RZ_NONNULL RzIntpState *state); +RZ_API void rz_intp_state_set(RZ_BORROW RZ_NONNULL RzIntpState *state, RzIntpStateFlag flag); + RZ_API void rz_interpreter_il_bb_free(RZ_NULLABLE RZ_OWN RzInterpreterILBB *il_bb); RZ_API void rz_interpreter_insn_pkt_free(RZ_NULLABLE RZ_OWN RzInterpreterInsnPkt *pkt); diff --git a/librz/include/rz_inquiry/rz_state.h b/librz/include/rz_inquiry/rz_state.h new file mode 100644 index 00000000000..6f0599a1d3d --- /dev/null +++ b/librz/include/rz_inquiry/rz_state.h @@ -0,0 +1,10 @@ + + +/** + * \file The header file for the RzInterpreter contains declarations for + * all RzIL based interpreters. + */ + +#ifndef RZ_INTERPRETER_STATE +#define RZ_INTERPRETER_STATE +#endif // RZ_STATE diff --git a/librz/inquiry/interpreter/state.c b/librz/inquiry/interpreter/state.c new file mode 100644 index 00000000000..89bfe836a4b --- /dev/null +++ b/librz/inquiry/interpreter/state.c @@ -0,0 +1,67 @@ +#include "rz_th.h" +#include "rz_types.h" +#include "rz_util/rz_assert.h" +#include + +enum rz_intp_state_flag { + RZ_INTP_STATE_INIT, ///< Initialization state. + RZ_INTP_STATE_EMU, ///< Emulation state. + RZ_INTP_STATE_CLEAN, ///< Cleaning state. + RZ_INTP_STATE_TERM, ///< Termination state. +}; + +struct rz_intp_state { + RzThreadLock *lock; ///< The mutex around the state flag. + RzIntpStateFlag flag; ///< The current set state. +}; + +RZ_API const char *rz_intp_state_flag_str(RzIntpStateFlag flag) { + switch (flag) { + case RZ_INTP_STATE_INIT: + return "I"; + case RZ_INTP_STATE_EMU: + return "O"; + case RZ_INTP_STATE_CLEAN: + return "C"; + case RZ_INTP_STATE_TERM: + return "T"; + } + rz_warn_if_reached(); + return "-"; +} + +RZ_API RZ_OWN RzIntpState *rz_intp_state_new() { + RzIntpState *state = RZ_NEW0(RzIntpState); + if (!state) { + return NULL; + } + state->flag = RZ_INTP_STATE_INIT; + state->lock = rz_th_lock_new(false); + if (!state->lock) { + free(state); + return NULL; + } + return state; +} + +RZ_API void rz_intp_state_free(RZ_OWN RZ_NULLABLE RzIntpState *state) { + if (!state) { + return; + } + rz_th_lock_free(state->lock); + free(state); +} + +RZ_API RzIntpStateFlag rz_intp_state_get(RZ_BORROW RZ_NONNULL RzIntpState *state) { + rz_return_val_if_fail(state, RZ_INTP_STATE_TERM); + rz_th_lock_enter(state->lock); + RzIntpStateFlag flag = state->flag; + rz_th_lock_leave(state->lock); + return flag; +} + +RZ_API void rz_intp_state_set(RZ_BORROW RZ_NONNULL RzIntpState *state, RzIntpStateFlag flag) { + rz_th_lock_enter(state->lock); + state->flag = flag; + rz_th_lock_leave(state->lock); +} diff --git a/librz/inquiry/state.c b/librz/inquiry/state.c new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/librz/inquiry/state.c @@ -0,0 +1 @@ + From 29bda640a5376a5986b0e98f5d70bfa13f50e69d Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 1 Apr 2026 20:21:39 +0200 Subject: [PATCH 241/334] Unfinished refactor to properly defined states of the interpreter. --- doc/abstract_interpreter/state_diagrams.svg | 262 ++++++++++++++++++++ librz/include/rz_inquiry/rz_interpreter.h | 47 ++-- librz/inquiry/inquiry.c | 97 ++++---- librz/inquiry/interpreter/interpreter.c | 205 +++++++-------- librz/inquiry/interpreter/meson.build | 1 + librz/inquiry/interpreter/state.c | 37 ++- 6 files changed, 454 insertions(+), 195 deletions(-) create mode 100644 doc/abstract_interpreter/state_diagrams.svg diff --git a/doc/abstract_interpreter/state_diagrams.svg b/doc/abstract_interpreter/state_diagrams.svg new file mode 100644 index 00000000000..9aaca00990e --- /dev/null +++ b/doc/abstract_interpreter/state_diagrams.svg @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ls + exp + Error + + + + + + + + + + + + + + B I + + + + + + + + T + + + + + + + + + + + wait + - + + + + + + + + + T + + wait + - + + + + + + + + + B II + B III + + + + + + + + + + + + + + + + + wait + - + + + + + + + + + clear + open + B IV + + + + T + + + + + + + + + + + wait + - + + + + + + + + + clear + B IV + + + + T + + + + + + emulation + error + + + + + + + + + + + + + + init + B I + basic block + + + + + + + emulation + B II + memory + B III + basic block + + + + + + + + + clean + B IV + sync + + + + + + + T + + + term + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + T + + + + + + + + + diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index f21e3058ce1..1e1699ceac6 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -24,8 +24,14 @@ #define RZ_INTERPRETER_ADDR_RBUF_SIZE 1024 #define RZ_INTERPRETER_YIELD_RBUF_SIZE 128 -typedef struct rz_intp_state RzIntpState; -typedef enum rz_intp_state_flag RzIntpStateFlag; +typedef struct rz_intp_run_state RzIntpRunState; + +typedef enum rz_intp_state_flag { + RZ_INTP_RUN_STATE_INIT, ///< Initialization state. + RZ_INTP_RUN_STATE_EMU, ///< Emulation state. + RZ_INTP_RUN_STATE_CLEAN, ///< Cleaning state. + RZ_INTP_RUN_STATE_TERM, ///< Termination state. +} RzIntpRunStateFlag; /** * \brief The abstractions this module supports. @@ -223,7 +229,15 @@ typedef struct { */ RZ_LIFETIME(RzInquiry) struct rz_interpreter_set { + // TODO: Move this one into each plugin? RzInterpreterAbstrState *astate; ///< The abstract state of the interpreter. + + RzIntpRunState *run_state; ///< The state the interpreter is currently in. + /** + * \brief The semaphore to sync RzInquiry and the interpreter between the Clean and Init run state. + */ + RzThreadSemaphore *run_state_sync; + RzAnalysisILVM *il_vm; ///< The RzAnalysisILVM for memory IO. RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. @@ -236,30 +250,10 @@ struct rz_interpreter_set { * These ring buffers are shared with other interpreter sets. */ HtUP /**/ *yield_rbufs; - /** - * \brief This flag signals if an interpreter is executing IL ops for. - * With the exception of an error state, this flag should - * only be toggled by the interpreter itself. - */ - RzAtomicBool *emulating; - /** - * \brief This flag signals if an should stand by for entry points to start interpeting from. - * With the exception of an error state, this flag should - * only be toggled by the inquiry module. - */ - RzAtomicBool *on_duty; /** * \brief Ignored address ranges. */ const RzVector /**/ *ignored_code; - /** - * \brief The entry points for the interpreters. - */ - RzThreadRingBuf /**/ *entry_points; - - RzThreadCond *inq_intrpr_sync; ///< Condition to let interpreter wait until inquiry set up everything. - RzThreadLock *inq_intrpr_lock; ///< Lock for task_sync condition. - bool intrp_waits_to_run; /** * \brief The interpreter plugin. */ @@ -270,10 +264,11 @@ struct rz_interpreter_set { RZ_BORROW void *intrpr_priv; }; -RZ_API RZ_OWN RzIntpState *rz_intp_state_new(); -RZ_API void rz_intp_state_free(RZ_OWN RZ_NULLABLE RzIntpState *state); -RZ_API RzIntpStateFlag rz_intp_state_get(RZ_BORROW RZ_NONNULL RzIntpState *state); -RZ_API void rz_intp_state_set(RZ_BORROW RZ_NONNULL RzIntpState *state, RzIntpStateFlag flag); +RZ_API RZ_OWN RzIntpRunState *rz_intp_run_state_new(); +RZ_API void rz_intp_run_state_free(RZ_OWN RZ_NULLABLE RzIntpRunState *state); +RZ_API RzIntpRunStateFlag rz_intp_run_state_get(RZ_BORROW RZ_NONNULL RzIntpRunState *state); +RZ_API void rz_intp_run_state_set(RZ_BORROW RZ_NONNULL RzIntpRunState *state, RzIntpRunStateFlag flag); +RZ_API const char *rz_intp_run_state_flag_str(RzIntpRunStateFlag flag); RZ_API void rz_interpreter_il_bb_free(RZ_NULLABLE RZ_OWN RzInterpreterILBB *il_bb); RZ_API void rz_interpreter_insn_pkt_free(RZ_NULLABLE RZ_OWN RzInterpreterInsnPkt *pkt); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 8a5129949d9..6b88dcb309f 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -459,6 +459,12 @@ static void open_ipc_obj(RzInterpreterSet *iset) { rz_th_queue_open(iset->il_queue); } +struct ituple { + RzThread *ithread; + RzInterpreterSet *iset; + RzIntpRunStateFlag expected_run_state; +} + /** * A function to call the prototype interpreter. * Usually these tasks will be split between different caches and yield consumers. @@ -468,7 +474,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_NONNULL const RzVector /**/ *ignored_code) { // All the things we need bool return_code = true; - RzInterpreterSet *iset = NULL; + RzInterpreterSet *intp_iset = NULL; HtUP *il_cache = NULL; RzThread *interpr_th = NULL; RzBuffer *io_buf = rz_buf_new_with_io(&core->analysis->iob); @@ -524,7 +530,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_warn_if_reached(); goto error_free; } - iset = rz_interpreter_set_new( + intp_iset = rz_interpreter_set_new( core->analysis, prototype->p_interpreter, RZ_INTERPRETER_ABSTRACTION_CONST, @@ -533,9 +539,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, ignored_code); ut64 entry_point = 0; rz_vector_pop(entry_points, &entry_point); - rz_th_ring_buf_put(iset->entry_points, &entry_point); + rz_th_ring_buf_put(intp_iset->entry_points, &entry_point); - if (!iset) { + if (!intp_iset) { return_code = false; rz_warn_if_reached(); goto error_free; @@ -543,25 +549,31 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // Dispatch prototype interpreter into a thread. RZ_LOG_DEBUG("INQUIRY: Start main interpretation thread.\n"); - interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, iset); - - while (rz_atomic_bool_get(iset->on_duty)) { - - // From here on, the code plays the role of the cache, IO handler, - // and yield consumer. - // - Waiting for new Effects to be requested and sending them. - // - Handling IO requests. - // - Receiving and adding the found xrefs to RzAnalysis. - // In the final implementation each of those roles would be split into - // two or more separated modules running in parallel. - RZ_LOG_DEBUG("INQUIRY: Start IL providing loop.\n"); - while (rz_atomic_bool_get(iset->emulating)) { - if (rz_cons_is_breaked()) { - rz_atomic_bool_set(iset->emulating, false); - rz_atomic_bool_set(iset->on_duty, false); - user_sent_signal = true; - goto end_interpretation; - } + interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, intp_iset); + struct ituple iset_map[] = { + { .ithread = interpr_th, + .iset = intp_iset, + .expected_run_state = RZ_INTP_RUN_STATE_INIT } }; + + // TODO: Add the other threads. + for (ut64 i = 0; ; ) { + if (rz_cons_is_breaked()) { + user_sent_signal = true; + goto fatal_error; + } + RzInterpreterSet *iset = iset_map[i].iset; + RzIntpRunStateFlag exp_rs = iset_map[i].expected_run_state; + + if (rz_intp_run_state_get(iset->run_state) == RZ_INTP_RUN_STATE_INIT && exp_rs == RZ_INTP_RUN_STATE_INIT) { + + } else if (rz_intp_run_state_get(iset->run_state) == RZ_INTP_RUN_STATE_EMU && exp_rs == RZ_INTP_RUN_STATE_EMU) { + // From here on, the code plays the role of the cache, IO handler, + // and yield consumer. + // - Waiting for new Effects to be requested and sending them. + // - Handling IO requests. + // - Receiving and adding the found xrefs to RzAnalysis. + // In the final implementation each of those roles would be split into + // two or more separated modules running in parallel. // ========= // IL CACHE @@ -579,8 +591,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, } else if (r == RZ_THREAD_RING_BUF_OK) { if (!send_next_il_bb(core, iset->il_queue, il_cache, branch_targets, &branch)) { // Signal interpreter the lifting failed. - rz_atomic_bool_set(iset->emulating, false); - break; + rz_th_queue_close(iset->il_queue); + iset_map[i].expected_run_state = RZ_INTP_RUN_STATE_CLEAN; } } } @@ -601,13 +613,13 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RzThreadRingBufResult r = rz_th_ring_buf_take_blocking(iset->io_request_rbuf, &io_req); if (r == RZ_THREAD_RING_BUF_CLOSED) { rz_warn_if_reached(); - break; + goto fatal_error; } else if (r == RZ_THREAD_RING_BUF_OK) { RzInterpreterIOResult io_res = { 0 }; handle_io_request(core, &iset->il_vm->vm->vm_memory, &io_req, &io_res); if (rz_th_ring_buf_put(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { rz_warn_if_reached(); - break; + goto fatal_error; } } } @@ -622,45 +634,32 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, { if (!handle_yields(core, iset->yield_rbufs)) { rz_warn_if_reached(); - rz_atomic_bool_set(iset->emulating, false); - rz_atomic_bool_set(iset->on_duty, false); - break; + goto fatal_error; } } } - // ================= - // CLEAR IPC OBJECTS - // ================= close_reset_ipc_obj(iset); + in_clean: // busy wait until the interpreter waits to take the next emulation task. - RZ_LOG_DEBUG("INQUIRY: Wait until interpreter thread is ready.\n"); - bool intrp_waits_to_run = false; - do { - rz_th_lock_enter(iset->inq_intrpr_lock); - intrp_waits_to_run = iset->intrp_waits_to_run; - rz_th_lock_leave(iset->inq_intrpr_lock); - } while (!intrp_waits_to_run); - RZ_LOG_DEBUG("INQUIRY: Interpreter thread is ready.\n"); - - // TODO: This syncro thingy doesn't work as nicely as I wish. + rz_th_sem_post(iset->run_state_sync); open_ipc_obj(iset); + in_init: // At this point the interpreter is finished and is waiting for the next emulation task. if (!add_entry_point(iset, il_cache, entry_points, branch_targets)) { // Non left. - rz_atomic_bool_set(iset->on_duty, false); + goto fatal_error; } - rz_atomic_bool_set(iset->emulating, true); - rz_th_cond_signal_all(iset->inq_intrpr_sync); } -end_interpretation: +fatal_error: RZ_LOG_DEBUG("INQUIRY: Wait for join\n"); - rz_atomic_bool_set(iset->on_duty, false); - close_reset_ipc_obj(iset); + for (size_t i = 0; i < RZ_ARRAY_SIZE(iset_map); i++) { + close_reset_ipc_obj(iset_map[i].iset); + } rz_th_wait(interpr_th); bool interpr_ret = rz_th_get_retv(interpr_th); rz_th_free(interpr_th); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 3eda1d2fa19..b982914cc11 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -171,15 +171,15 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { if (iset->io_result_rbuf) { rz_th_ring_buf_free(iset->io_result_rbuf); } - if (iset->emulating) { - rz_atomic_bool_free(iset->emulating); - } - if (iset->on_duty) { - rz_atomic_bool_free(iset->on_duty); + if (iset->run_state_sync) { + rz_th_sem_free(iset->run_state_sync); } if (iset->astate) { rz_interpreter_abstr_state_free(iset->astate); } + if (iset->run_state) { + rz_intp_run_state_free(iset->run_state); + } if (iset->il_vm) { rz_analysis_il_vm_free(iset->il_vm); } @@ -189,12 +189,6 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { if (iset->entry_points) { rz_th_ring_buf_free(iset->entry_points); } - if (iset->inq_intrpr_lock) { - rz_th_lock_free(iset->inq_intrpr_lock); - } - if (iset->inq_intrpr_sync) { - rz_th_cond_free(iset->inq_intrpr_sync); - } free(iset); } @@ -360,17 +354,14 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( iset->plugin = plugin; iset->astate = state; + iset->run_state = rz_intp_run_state_new(); iset->il_vm = il_vm; iset->il_queue = il_queue; iset->branch_rbuf = branch_rbuf; iset->yield_rbufs = yield_rbufs; iset->io_request_rbuf = io_request_rbuf; iset->io_result_rbuf = io_result_rbuf; - iset->emulating = rz_atomic_bool_new(true); - iset->on_duty = rz_atomic_bool_new(true); - iset->inq_intrpr_lock = rz_th_lock_new(false); - iset->inq_intrpr_sync = rz_th_cond_new(); - iset->intrp_waits_to_run = false; + iset->run_state_sync = rz_th_sem_new(0); iset->entry_points = entry_points; iset->ignored_code = ignored_code; return iset; @@ -488,19 +479,20 @@ static bool setup_intrpr_state( * Main interpretation. */ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { - rz_goto_if_fail(iset && + rz_return_val_if_fail(iset && iset->astate && iset->branch_rbuf && iset->il_queue && iset->yield_rbufs && - iset->emulating && + iset->run_state_sync && iset->plugin && iset->plugin->eval && iset->plugin->successors && iset->plugin->init_state && iset->plugin->fini_state && iset->plugin->hash_state, - entry_assert_error); + false); + bool success = true; RZ_LOG_DEBUG("INTERPRETER Main: Hello.\n"); @@ -518,90 +510,107 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // This vector must have the same order as the elements pushed into branch_queue. RzVector *succ_states = NULL; const RzInterpreterILBB *il_bb = NULL; + ut64 astate_hash = 0; - while (rz_atomic_bool_get(iset->on_duty)) { - ut64 entry_point; - if (rz_th_ring_buf_take_blocking(iset->entry_points, &entry_point) != RZ_THREAD_RING_BUF_OK) { - rz_atomic_bool_set(iset->on_duty, false); - rz_atomic_bool_set(iset->on_duty, false); - return true; - } +// TODO: It is probably better to make the following stuff while-loops. +// Because otherwise it doesn't make sense without the docs. +// But while debugging and developing, I keep it this way to separate clearly +// what the interpreter does in each state. - // Initializes the current interpreter's private data and its state. - if (!setup_intrpr_state(iset, entry_point, &il_bb, &tmp_succ_addr, &reachable_states, &succ_states)) { - goto pre_emu_loop_error; - } +INIT: { + rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_INIT); - rz_atomic_bool_set(iset->emulating, true); - ut64 out_hash = 0; - while (rz_atomic_bool_get(iset->emulating)) { - iset->astate->bb_addr = il_bb->bb_addr; - iset->astate->bb_size = il_bb->size; - // Evaluate the effect on the input state. - if (!plugin->eval(iset, il_bb, iset->intrpr_priv)) { - RZ_LOG_DEBUG("Eval failed\n"); - goto emu_done; - } - out_hash = plugin->hash_state(iset->astate, iset->intrpr_priv); - - // Add output state hash to the reachable states and - // set a flag if it was a new state. - size_t psize = rz_set_u_size(reachable_states); - rz_set_u_add(reachable_states, out_hash); - bool new_state_reached = psize < rz_set_u_size(reachable_states); - - // Determine the successor effects to evaluate. - // Only newly reached states are allowed to add successors. - if (!(new_state_reached && choose_next_pc(iset, out_hash, tmp_succ_addr, succ_states, il_bb))) { - // No new state or address means we can stop interpreting. - // Note, that we can't use the queues as cancel condition because they - // are asynchronous and checking them would introduces race conditions. - // TODO: This doesn't work if the interpreter can produce multiple out states. - goto emu_done; - } - - // Set effect and state for next evaluation. - SuccessorState next = { 0 }; - rz_vector_pop_front(succ_states, &next); - if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { - RZ_LOG_DEBUG("Getting il bb failed\n"); - // The il op lifting failed. Likely because the PC - // pointed to an unmapped region. - goto emu_done; - } - if (!plugin->set_pc(iset->astate, next.addr, iset->intrpr_priv)) { - rz_warn_if_reached(); - goto emu_done; - } - } - emu_done: - rz_atomic_bool_set(iset->emulating, false); - RZ_FREE_CUSTOM(tmp_succ_addr, rz_vector_free); - RZ_FREE_CUSTOM(succ_states, rz_vector_free); - RZ_FREE_CUSTOM(reachable_states, rz_set_u_free); - if (iset->plugin->fini) { - iset->plugin->fini(iset->intrpr_priv); - } - iset->plugin->fini_state(iset->astate, iset->intrpr_priv); + // Get the next entry point. + ut64 entry_point; + if (rz_th_ring_buf_take_blocking(iset->entry_points, &entry_point) != RZ_THREAD_RING_BUF_OK) { + // No entry point to emulate. Interpreter is done. + success = true; + goto TERM; + } - // Wait until RzInquiry asks to start again. - rz_th_lock_enter(iset->inq_intrpr_lock); - iset->intrp_waits_to_run = true; - rz_th_cond_wait(iset->inq_intrpr_sync, iset->inq_intrpr_lock); - iset->intrp_waits_to_run = false; - rz_th_lock_leave(iset->inq_intrpr_lock); + // Initializes the current interpreter's private data and its state. + if (!setup_intrpr_state(iset, entry_point, &il_bb, &tmp_succ_addr, &reachable_states, &succ_states)) { + success = false; + goto TERM; } - rz_atomic_bool_set(iset->on_duty, false); - return success; -pre_emu_loop_error: - RZ_LOG_DEBUG("pre_loop_error\n"); - rz_warn_if_reached(); - rz_atomic_bool_set(iset->on_duty, false); - success = false; - goto emu_done; + goto EMU; +} -entry_assert_error: - rz_atomic_bool_set(iset->on_duty, false); - return false; +EMU: { + rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_EMU); + + iset->astate->bb_addr = il_bb->bb_addr; + iset->astate->bb_size = il_bb->size; + // Evaluate the effect on the abstract state. + if (!plugin->eval(iset, il_bb, iset->intrpr_priv)) { + RZ_LOG_DEBUG("Eval failed\n"); + goto CLEAN; + } + astate_hash = plugin->hash_state(iset->astate, iset->intrpr_priv); + + // Add output state hash to the reachable states and + // set a flag if it was a new state. + size_t psize = rz_set_u_size(reachable_states); + rz_set_u_add(reachable_states, astate_hash); + bool new_state_reached = psize < rz_set_u_size(reachable_states); + + // Determine the successor effects to evaluate. + // Only newly reached states are allowed to add successors. + if (!(new_state_reached && choose_next_pc(iset, astate_hash, tmp_succ_addr, succ_states, il_bb))) { + // No new state or address means we can stop interpreting. + // Note, that we can't use the queues as cancel condition because they + // are asynchronous and checking them would introduces race conditions. + // TODO: This doesn't work if the interpreter can produce multiple out states. + goto CLEAN; + } + + // Set effect and state for next evaluation. + SuccessorState next = { 0 }; + rz_vector_pop_front(succ_states, &next); + if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { + RZ_LOG_DEBUG("Getting il bb failed\n"); + // The il op lifting failed. Likely because the PC + // pointed to an unmapped region. + goto CLEAN; + } + if (!plugin->set_pc(iset->astate, next.addr, iset->intrpr_priv)) { + rz_warn_if_reached(); + goto CLEAN; + } + + // Loop back. Interpret next basic block. + goto EMU; +} + +CLEAN: { + rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_CLEAN); + + RZ_FREE_CUSTOM(tmp_succ_addr, rz_vector_free); + RZ_FREE_CUSTOM(succ_states, rz_vector_free); + RZ_FREE_CUSTOM(reachable_states, rz_set_u_free); + iset->plugin->fini_state(iset->astate, iset->intrpr_priv); + if (iset->plugin->fini) { + iset->plugin->fini(iset->intrpr_priv); + } + + // Wait until RzInquiry asks to start again. + rz_th_sem_wait(iset->run_state_sync); + + // Clean can only transition to Init. + goto INIT; +} + +TERM: { + rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_TERM); + + RZ_FREE_CUSTOM(tmp_succ_addr, rz_vector_free); + RZ_FREE_CUSTOM(succ_states, rz_vector_free); + RZ_FREE_CUSTOM(reachable_states, rz_set_u_free); + iset->plugin->fini_state(iset->astate, iset->intrpr_priv); + if (iset->plugin->fini) { + iset->plugin->fini(iset->intrpr_priv); + } + return success; +} } diff --git a/librz/inquiry/interpreter/meson.build b/librz/inquiry/interpreter/meson.build index 2001f212842..3dda29212c8 100644 --- a/librz/inquiry/interpreter/meson.build +++ b/librz/inquiry/interpreter/meson.build @@ -13,6 +13,7 @@ interpreter_plugins = { rz_interpreter_sources = [ 'interpreter.c', + 'state.c', 'p/interpreter_prototype.c', 'prototype/eval_effect.c', 'prototype/eval_pure.c', diff --git a/librz/inquiry/interpreter/state.c b/librz/inquiry/interpreter/state.c index 89bfe836a4b..679b18264f2 100644 --- a/librz/inquiry/interpreter/state.c +++ b/librz/inquiry/interpreter/state.c @@ -3,39 +3,32 @@ #include "rz_util/rz_assert.h" #include -enum rz_intp_state_flag { - RZ_INTP_STATE_INIT, ///< Initialization state. - RZ_INTP_STATE_EMU, ///< Emulation state. - RZ_INTP_STATE_CLEAN, ///< Cleaning state. - RZ_INTP_STATE_TERM, ///< Termination state. -}; - -struct rz_intp_state { +struct rz_intp_run_state { RzThreadLock *lock; ///< The mutex around the state flag. - RzIntpStateFlag flag; ///< The current set state. + RzIntpRunStateFlag flag; ///< The current set state. }; -RZ_API const char *rz_intp_state_flag_str(RzIntpStateFlag flag) { +RZ_API const char *rz_intp_run_state_flag_str(RzIntpRunStateFlag flag) { switch (flag) { - case RZ_INTP_STATE_INIT: + case RZ_INTP_RUN_STATE_INIT: return "I"; - case RZ_INTP_STATE_EMU: + case RZ_INTP_RUN_STATE_EMU: return "O"; - case RZ_INTP_STATE_CLEAN: + case RZ_INTP_RUN_STATE_CLEAN: return "C"; - case RZ_INTP_STATE_TERM: + case RZ_INTP_RUN_STATE_TERM: return "T"; } rz_warn_if_reached(); return "-"; } -RZ_API RZ_OWN RzIntpState *rz_intp_state_new() { - RzIntpState *state = RZ_NEW0(RzIntpState); +RZ_API RZ_OWN RzIntpRunState *rz_intp_run_state_new() { + RzIntpRunState *state = RZ_NEW0(RzIntpRunState); if (!state) { return NULL; } - state->flag = RZ_INTP_STATE_INIT; + state->flag = RZ_INTP_RUN_STATE_INIT; state->lock = rz_th_lock_new(false); if (!state->lock) { free(state); @@ -44,7 +37,7 @@ RZ_API RZ_OWN RzIntpState *rz_intp_state_new() { return state; } -RZ_API void rz_intp_state_free(RZ_OWN RZ_NULLABLE RzIntpState *state) { +RZ_API void rz_intp_run_state_free(RZ_OWN RZ_NULLABLE RzIntpRunState *state) { if (!state) { return; } @@ -52,15 +45,15 @@ RZ_API void rz_intp_state_free(RZ_OWN RZ_NULLABLE RzIntpState *state) { free(state); } -RZ_API RzIntpStateFlag rz_intp_state_get(RZ_BORROW RZ_NONNULL RzIntpState *state) { - rz_return_val_if_fail(state, RZ_INTP_STATE_TERM); +RZ_API RzIntpRunStateFlag rz_intp_run_state_get(RZ_BORROW RZ_NONNULL RzIntpRunState *state) { + rz_return_val_if_fail(state, RZ_INTP_RUN_STATE_TERM); rz_th_lock_enter(state->lock); - RzIntpStateFlag flag = state->flag; + RzIntpRunStateFlag flag = state->flag; rz_th_lock_leave(state->lock); return flag; } -RZ_API void rz_intp_state_set(RZ_BORROW RZ_NONNULL RzIntpState *state, RzIntpStateFlag flag) { +RZ_API void rz_intp_run_state_set(RZ_BORROW RZ_NONNULL RzIntpRunState *state, RzIntpRunStateFlag flag) { rz_th_lock_enter(state->lock); state->flag = flag; rz_th_lock_leave(state->lock); From a32441080e481897acb1881f814c72fec784b378 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 2 Apr 2026 19:06:42 +0200 Subject: [PATCH 242/334] Finish refactor to run-state based interpreters. --- librz/core/cmd/cmd_inquiry.c | 7 +- librz/include/rz_inquiry.h | 2 +- librz/include/rz_inquiry/rz_interpreter.h | 8 +- librz/inquiry/inquiry.c | 297 +++++++++++----------- librz/inquiry/interpreter/interpreter.c | 55 ++-- librz/inquiry/interpreter/state.c | 10 +- 6 files changed, 191 insertions(+), 188 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 40132ec0e84..957f5f69359 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2025 RizinOrg // SPDX-License-Identifier: LGPL-3.0-only +#include "rz_util/rz_set.h" #include #include #include @@ -45,7 +46,7 @@ static RzVector /**/ *get_ignored_code_regions( RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int argc, const char **argv) { rz_return_val_if_fail(core->analysis && core->io && core->bin->cur && core->bin->cur->o, RZ_CMD_STATUS_ERROR); - RzVector *entry_points = rz_vector_new(sizeof(ut64), NULL, NULL); + RzSetU *entry_points = rz_set_u_new(); if (!entry_points) { return RZ_CMD_STATUS_ERROR; } @@ -54,12 +55,12 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar if (argc == 1) { // No specific entry point given. Pick the one provided by the bin plugin. entry_point = rz_bin_get_first_entrypoint(core->bin->cur->o); - rz_vector_push(entry_points, &entry_point); + rz_set_u_add(entry_points, entry_point); } else { // Add all entry points given as arguments. for (size_t i = 1; i < argc; i++) { ut64 entry_point = rz_num_get(core->num, argv[i]); - rz_vector_push(entry_points, &entry_point); + rz_set_u_add(entry_points, entry_point); } } RzVector *ignored_code_regions = get_ignored_code_regions( diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index df1cf4e2f0c..d3a8379e7bc 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -81,7 +81,7 @@ RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzPVector /**/ *allowed_segments); -RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzVector /**/ *entry_points, RZ_NONNULL const RzVector /**/ *ignored_code); +RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzSetU /**/ *entry_points, RZ_NONNULL const RzVector /**/ *ignored_code); RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry, diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 1e1699ceac6..f01ee348a43 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -27,6 +27,11 @@ typedef struct rz_intp_run_state RzIntpRunState; typedef enum rz_intp_state_flag { + /** + * \brief Interpreter is still outside of its defined loop. + * E.g. shortly after its thread was spawned. + */ + RZ_INTP_RUN_STATE_OUT_OF_LOOP, RZ_INTP_RUN_STATE_INIT, ///< Initialization state. RZ_INTP_RUN_STATE_EMU, ///< Emulation state. RZ_INTP_RUN_STATE_CLEAN, ///< Cleaning state. @@ -267,9 +272,10 @@ struct rz_interpreter_set { RZ_API RZ_OWN RzIntpRunState *rz_intp_run_state_new(); RZ_API void rz_intp_run_state_free(RZ_OWN RZ_NULLABLE RzIntpRunState *state); RZ_API RzIntpRunStateFlag rz_intp_run_state_get(RZ_BORROW RZ_NONNULL RzIntpRunState *state); -RZ_API void rz_intp_run_state_set(RZ_BORROW RZ_NONNULL RzIntpRunState *state, RzIntpRunStateFlag flag); RZ_API const char *rz_intp_run_state_flag_str(RzIntpRunStateFlag flag); +RZ_IPI void rz_intp_run_state_set(RZ_BORROW RZ_NONNULL RzIntpRunState *state, RzIntpRunStateFlag flag); + RZ_API void rz_interpreter_il_bb_free(RZ_NULLABLE RZ_OWN RzInterpreterILBB *il_bb); RZ_API void rz_interpreter_insn_pkt_free(RZ_NULLABLE RZ_OWN RzInterpreterInsnPkt *pkt); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 6b88dcb309f..bef3d986123 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -23,7 +23,6 @@ #include "rz_util/rz_log.h" #include "rz_util/rz_set.h" #include "rz_util/rz_str.h" -#include "rz_util/rz_sys.h" #include "rz_vector.h" #include #include @@ -359,84 +358,56 @@ static const RzInterpreterILBB *get_il_bb(RzCore *core, HtUP *il_cache, ut64 add static bool send_next_il_bb(RzCore *core, RzThreadQueue *il_queue, HtUP *il_cache, - RzSetU *branch_targets, + RzSetU *entry_points, RzInterpreterBranch *branch) { - ut64 alt_addr = branch->alt_target; - if (alt_addr) { - branch->alt_target = 0; - } - RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x " (alt: 0x%" PFMT64x ")\n", branch->target_addr, alt_addr); - const RzInterpreterILBB *bb = get_il_bb(core, il_cache, alt_addr ? alt_addr : branch->target_addr); + RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x " (alt: 0x%" PFMT64x ")\n", branch->target_addr, branch->alt_target); + const RzInterpreterILBB *bb = get_il_bb(core, il_cache, branch->alt_target ? branch->alt_target : branch->target_addr); if (!bb) { // Delete the address from the branch targets. // This is currently necessary as a work around, because if the interpreter // fails before interpreting the address, it is added again as next entry point. // Giving an endless loop. // One of the design thingies to fix in the proper implementation. - rz_set_u_delete(branch_targets, branch->target_addr); - if (alt_addr) { - rz_set_u_delete(branch_targets, alt_addr); + rz_set_u_delete(entry_points, branch->target_addr); + if (branch->alt_target) { + rz_set_u_delete(entry_points, branch->alt_target); } return false; } rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, bb->bb_addr, bb->size); - if (alt_addr) { + if (branch->alt_target) { // Add a dummy basic block at the address the call originally jumped to. // This is the basic block for the imported function. rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, branch->target_addr, 1); - rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); } rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); rz_th_queue_push(il_queue, (void *)bb, true); return true; } -// TODO: il_cache should be passed here. -static bool add_entry_point( +static bool reduce_get_entry_points( RzInterpreterSet *iset, HtUP *il_cache, - RZ_BORROW RzVector /**/ *entry_points, - RzSetU *branch_targets) { + RZ_BORROW RzSetU /**/ *entry_points, + RZ_OUT ut64 *next_entry_point) { // Add the next entry point we need to check for executable regions the interpreters did not cover. - // For this we simply delete all jump targets from our set, which point + // For this we simply delete all entry points which point // into the already handled basic blocks. // Then add a few addresses as new entry point. // The addresses we add are jump targets from jump/call instructions in the binary. - rz_vector_clear(entry_points); - RzVector *covered_jump_targets = rz_vector_new(sizeof(ut64), NULL, NULL); - RzIterator *ct_iter = rz_set_u_as_iter(branch_targets); + RzIterator *il_bb_iter = ht_up_as_iter_keys(il_cache); ut64 *ct; - rz_iterator_foreach(ct_iter, ct) { - if (ht_up_find(il_cache, *ct, NULL)) { - // This call target was interpreted before (hence is in the IL cache). - rz_vector_push(covered_jump_targets, ct); - continue; - } - rz_vector_push(entry_points, ct); + rz_iterator_foreach(il_bb_iter, ct) { + // This call target was interpreted before (hence is in the IL cache). + rz_set_u_delete(entry_points, *ct); } - rz_iterator_free(ct_iter); - if (rz_vector_empty(entry_points)) { - rz_vector_free(covered_jump_targets); + rz_iterator_free(il_bb_iter); + if (rz_set_u_size(entry_points) == 0) { return false; } - // TODO: Push entrypoints evenly to other interpreters - ut64 entry_point = 0; - rz_vector_pop(entry_points, &entry_point); - rz_th_ring_buf_put(iset->entry_points, &entry_point); - - ut64 *ep; - rz_vector_foreach (covered_jump_targets, ep) { - // Delete the selected ones from the jump target set. - // So they are not requested again. - rz_set_u_delete(branch_targets, *ep); - } - rz_vector_free(covered_jump_targets); - if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { - eprintf(RZ_CONS_CLEAR_LINE "\rBranch targets left: %" PFMT32d, rz_set_u_size(branch_targets)); - fflush(stdout); - } + *next_entry_point = rz_set_u_take(entry_points); return true; } @@ -459,29 +430,47 @@ static void open_ipc_obj(RzInterpreterSet *iset) { rz_th_queue_open(iset->il_queue); } +static bool collect_entry_points(RzCore *core, + RzVector /**/ *insn_to_insn_edges, + RzSetU *entry_points, + RzSetU *symbol_targets) { + + if (!get_branch_targets(core, entry_points, insn_to_insn_edges) || + !rz_inquiry_get_fcn_symbol_addr(core, symbol_targets)) { + rz_warn_if_reached(); + return false; + } + RzIterator *iter = rz_set_u_as_iter(symbol_targets); + ut64 *sym_addr; + rz_iterator_foreach(iter, sym_addr) { + rz_set_u_add(entry_points, *sym_addr); + } + rz_iterator_free(iter); + rz_set_u_free(symbol_targets); + return true; +} + struct ituple { RzThread *ithread; RzInterpreterSet *iset; - RzIntpRunStateFlag expected_run_state; -} + RzIntpRunStateFlag next_run_state; +}; /** * A function to call the prototype interpreter. * Usually these tasks will be split between different caches and yield consumers. */ RZ_API bool rz_inquiry_interpreter(RzCore *core, - RZ_OWN RzVector /**/ *entry_points, + RZ_OWN RzSetU *entry_points, RZ_NONNULL const RzVector /**/ *ignored_code) { // All the things we need bool return_code = true; RzInterpreterSet *intp_iset = NULL; HtUP *il_cache = NULL; - RzThread *interpr_th = NULL; RzBuffer *io_buf = rz_buf_new_with_io(&core->analysis->iob); - RzSetU *branch_targets = rz_set_u_new(); RzSetU *symbol_targets = rz_set_u_new(); bool user_sent_signal = false; - RzVector /**/ *insn_to_insn_edges = NULL; + RzVector /**/ *insn_to_insn_edges = rz_vector_new(sizeof(RzAnalysisXRef), NULL, NULL); rz_cons_push(); @@ -490,23 +479,10 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // of the pointers. il_cache = ht_up_new(NULL, (RzPVectorFree)rz_interpreter_il_bb_free); - insn_to_insn_edges = rz_vector_new(sizeof(RzAnalysisXRef), NULL, NULL); - if (!get_branch_targets(core, branch_targets, insn_to_insn_edges) || - !rz_inquiry_get_fcn_symbol_addr(core, symbol_targets)) { - RZ_LOG_ERROR("Failed to get branch targets.\n"); - return_code = false; - goto error_free; - } - RzIterator *iter = rz_set_u_as_iter(symbol_targets); - ut64 *sym_addr; - rz_iterator_foreach(iter, sym_addr) { - rz_set_u_add(branch_targets, *sym_addr); - } - rz_iterator_free(iter); - rz_set_u_free(symbol_targets); + collect_entry_points(core, insn_to_insn_edges, entry_points, symbol_targets); if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { - eprintf("Total branch targets in binary: %" PFMT32d "\n", rz_set_u_size(branch_targets)); + eprintf("Total branch targets in binary: %" PFMT32d "\n", rz_set_u_size(entry_points)); } // Initialize the abstract state with the architecture's registers. @@ -537,10 +513,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_bin_object_get_sections(core->bin->cur->o), (RzInterpreterYieldFilter)rz_inquiry_xref_interpreter_filter, ignored_code); - ut64 entry_point = 0; - rz_vector_pop(entry_points, &entry_point); - rz_th_ring_buf_put(intp_iset->entry_points, &entry_point); - if (!intp_iset) { return_code = false; rz_warn_if_reached(); @@ -549,24 +521,56 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // Dispatch prototype interpreter into a thread. RZ_LOG_DEBUG("INQUIRY: Start main interpretation thread.\n"); - interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, intp_iset); + RzThread *interpr_th = interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, intp_iset); struct ituple iset_map[] = { { .ithread = interpr_th, .iset = intp_iset, - .expected_run_state = RZ_INTP_RUN_STATE_INIT } }; + .next_run_state = RZ_INTP_RUN_STATE_INIT } + }; + ut64 intpr_terminated = 0; // TODO: Add the other threads. - for (ut64 i = 0; ; ) { + for (ut64 i = 0;;) { if (rz_cons_is_breaked()) { user_sent_signal = true; - goto fatal_error; + break; } RzInterpreterSet *iset = iset_map[i].iset; - RzIntpRunStateFlag exp_rs = iset_map[i].expected_run_state; + RzIntpRunStateFlag expected_rs = iset_map[i].next_run_state; - if (rz_intp_run_state_get(iset->run_state) == RZ_INTP_RUN_STATE_INIT && exp_rs == RZ_INTP_RUN_STATE_INIT) { - - } else if (rz_intp_run_state_get(iset->run_state) == RZ_INTP_RUN_STATE_EMU && exp_rs == RZ_INTP_RUN_STATE_EMU) { + switch (rz_intp_run_state_get(iset->run_state)) { + case RZ_INTP_RUN_STATE_OUT_OF_LOOP: + break; + case RZ_INTP_RUN_STATE_INIT: { + if (expected_rs != RZ_INTP_RUN_STATE_INIT) { + break; + } + // This interpreter is waiting for the next emulation task. + RzInterpreterBranch branch = { 0 }; + if (!reduce_get_entry_points(iset, il_cache, entry_points, &branch.target_addr)) { + // None left. + rz_th_queue_close(iset->il_queue); + iset_map[i].next_run_state = RZ_INTP_RUN_STATE_TERM; + intpr_terminated++; + RZ_LOG_DEBUG("Next: TERM\n"); + continue; + } + if (send_next_il_bb(core, iset->il_queue, il_cache, entry_points, &branch)) { + // Successfully lifted and pushed the entry point's basic block into the queue. + // Expect the interpreter to emulate now. + iset_map[i].next_run_state = RZ_INTP_RUN_STATE_EMU; + RZ_LOG_DEBUG("Next: EMU\n"); + } else { + iset_map[i].next_run_state = RZ_INTP_RUN_STATE_CLEAN; + rz_th_queue_close(iset->il_queue); + RZ_LOG_DEBUG("Next: CLEAN\n"); + } + break; + } + case RZ_INTP_RUN_STATE_EMU: { + if (expected_rs != RZ_INTP_RUN_STATE_EMU) { + break; + } // From here on, the code plays the role of the cache, IO handler, // and yield consumer. // - Waiting for new Effects to be requested and sending them. @@ -581,21 +585,24 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // // This block mimics the IL cache. It uplifts basic blocks and // caches them. - { + if (!rz_th_ring_buf_is_empty_unsafe(iset->branch_rbuf)) { RzInterpreterBranch branch = { 0 }; - if (!rz_th_ring_buf_is_empty_unsafe(iset->branch_rbuf)) { - RzThreadRingBufResult r = rz_th_ring_buf_take_blocking(iset->branch_rbuf, &branch); - if (r == RZ_THREAD_RING_BUF_CLOSED) { - rz_warn_if_reached(); - break; - } else if (r == RZ_THREAD_RING_BUF_OK) { - if (!send_next_il_bb(core, iset->il_queue, il_cache, branch_targets, &branch)) { - // Signal interpreter the lifting failed. - rz_th_queue_close(iset->il_queue); - iset_map[i].expected_run_state = RZ_INTP_RUN_STATE_CLEAN; - } + RzThreadRingBufResult r = rz_th_ring_buf_take(iset->branch_rbuf, &branch); + if (r == RZ_THREAD_RING_BUF_CLOSED) { + rz_warn_if_reached(); + goto fatal_error; + } else if (r == RZ_THREAD_RING_BUF_OK) { + if (!send_next_il_bb(core, iset->il_queue, il_cache, entry_points, &branch)) { + // Signal interpreter the lifting failed. + rz_th_queue_close(iset->il_queue); + iset_map[i].next_run_state = RZ_INTP_RUN_STATE_CLEAN; + RZ_LOG_DEBUG("Next: CLEAN\n"); + } else { + RZ_LOG_DEBUG("Pushed: il_bb: 0x%llx\n", branch.target_addr); } } + // Else r == RZ_THREAD_RING_BUF_FAIL + // Due to a race condition the ring buffer was actually empty. } // ========== @@ -607,22 +614,22 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // But this requires multiple IO write caches // (one for each interpreter instance). // Because this is not yet implemented, there is only one interpreter thread for now. - { - RzInterpreterIORequest io_req = { 0 }; - if (!rz_th_ring_buf_is_empty_unsafe(iset->io_request_rbuf)) { - RzThreadRingBufResult r = rz_th_ring_buf_take_blocking(iset->io_request_rbuf, &io_req); - if (r == RZ_THREAD_RING_BUF_CLOSED) { + RzInterpreterIORequest io_req = { 0 }; + if (!rz_th_ring_buf_is_empty_unsafe(iset->io_request_rbuf)) { + RzThreadRingBufResult r = rz_th_ring_buf_take(iset->io_request_rbuf, &io_req); + if (r == RZ_THREAD_RING_BUF_CLOSED) { + rz_warn_if_reached(); + goto fatal_error; + } else if (r == RZ_THREAD_RING_BUF_OK) { + RzInterpreterIOResult io_res = { 0 }; + handle_io_request(core, &iset->il_vm->vm->vm_memory, &io_req, &io_res); + if (rz_th_ring_buf_put(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { rz_warn_if_reached(); goto fatal_error; - } else if (r == RZ_THREAD_RING_BUF_OK) { - RzInterpreterIOResult io_res = { 0 }; - handle_io_request(core, &iset->il_vm->vm->vm_memory, &io_req, &io_res); - if (rz_th_ring_buf_put(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { - rz_warn_if_reached(); - goto fatal_error; - } } } + // Else r == RZ_THREAD_RING_BUF_FAIL + // Due to a race condition the ring buffer was actually empty. } // ============== @@ -631,26 +638,35 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // // This part plays the role of a yield consumer. // In our prototype it only receives xrefs and call candidates. - { - if (!handle_yields(core, iset->yield_rbufs)) { - rz_warn_if_reached(); - goto fatal_error; - } + if (!handle_yields(core, iset->yield_rbufs)) { + iset_map[i].next_run_state = RZ_INTP_RUN_STATE_TERM; + intpr_terminated++; + RZ_LOG_DEBUG("Next: TERM\n"); } + break; } - - close_reset_ipc_obj(iset); - - in_clean: - // busy wait until the interpreter waits to take the next emulation task. - rz_th_sem_post(iset->run_state_sync); - open_ipc_obj(iset); - - in_init: - // At this point the interpreter is finished and is waiting for the next emulation task. - if (!add_entry_point(iset, il_cache, entry_points, branch_targets)) { - // Non left. - goto fatal_error; + case RZ_INTP_RUN_STATE_CLEAN: { + if (!((expected_rs == RZ_INTP_RUN_STATE_CLEAN || expected_rs == RZ_INTP_RUN_STATE_EMU))) { + break; + } + close_reset_ipc_obj(iset); + open_ipc_obj(iset); + rz_th_sem_post(iset->run_state_sync); + iset_map[i].next_run_state = RZ_INTP_RUN_STATE_INIT; + RZ_LOG_DEBUG("Next: INIT\n"); + break; + } + case RZ_INTP_RUN_STATE_TERM: { + if (expected_rs != RZ_INTP_RUN_STATE_TERM) { + iset_map[i].next_run_state = RZ_INTP_RUN_STATE_TERM; + intpr_terminated++; + } + RZ_LOG_DEBUG("Next: TERM\n"); + break; + } + } + if (intpr_terminated == RZ_ARRAY_SIZE(iset_map)) { + break; } } @@ -659,17 +675,24 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_LOG_DEBUG("INQUIRY: Wait for join\n"); for (size_t i = 0; i < RZ_ARRAY_SIZE(iset_map); i++) { close_reset_ipc_obj(iset_map[i].iset); + // Open semaphore so the interpreter can transition + // EMU -> CLEAN -> INIT -> TERM + rz_th_sem_post(iset_map[i].iset->run_state_sync); } - rz_th_wait(interpr_th); - bool interpr_ret = rz_th_get_retv(interpr_th); - rz_th_free(interpr_th); - if (!interpr_ret || user_sent_signal) { - return_code = false; - if (!user_sent_signal) { - RZ_LOG_ERROR("Interpreter failed with an error. Abort.\n"); - } else { - RZ_LOG_ERROR("User sent signal.\n"); + + for (size_t i = 0; i < RZ_ARRAY_SIZE(iset_map); i++) { + rz_th_wait(iset_map[i].ithread); + bool interpr_ret = rz_th_get_retv(iset_map[i].ithread); + rz_th_free(iset_map[i].ithread); + if (!interpr_ret || user_sent_signal) { + return_code = false; + if (!user_sent_signal) { + RZ_LOG_ERROR("Interpreter failed with an error. Abort.\n"); + } else { + RZ_LOG_ERROR("User sent signal.\n"); + } } + rz_interpreter_set_free(iset_map[i].iset); } if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { @@ -677,13 +700,11 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, } if (!rz_inquiry_bb_cfg_reduce(core->inquiry->bb_cfg)) { rz_warn_if_reached(); - goto error_free; } if (!user_sent_signal) { eprintf("Complement BB CFG with statically known xrefs...\n"); if (!rz_inquiry_bb_cfg_complement(core->inquiry, insn_to_insn_edges)) { rz_warn_if_reached(); - goto error_free; } } @@ -693,18 +714,10 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // Wait for thread to finish before cleaning. error_free: - RZ_LOG_DEBUG("INQUIRY: Close queues\n"); - rz_vector_free(entry_points); - rz_set_u_free(branch_targets); + rz_set_u_free(entry_points); rz_buf_free(io_buf); rz_vector_free(insn_to_insn_edges); - - if (iset) { - // Ownership was passed to iset - rz_interpreter_set_free(iset); - } ht_up_free(il_cache); - rz_cons_pop(); return return_code && !user_sent_signal; } diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index b982914cc11..a70d5b79a7c 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -8,6 +8,7 @@ #include "rz_analysis.h" #include "rz_util/rz_assert.h" #include "rz_util/rz_itv.h" +#include "rz_util/rz_log.h" #include #include #include @@ -186,9 +187,6 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { if (iset->yield_rbufs) { ht_up_free(iset->yield_rbufs); } - if (iset->entry_points) { - rz_th_ring_buf_free(iset->entry_points); - } free(iset); } @@ -199,14 +197,12 @@ static bool setup_ipc_objects( RZ_OUT RzThreadRingBuf **io_request_rbuf, RZ_OUT RzThreadRingBuf **io_result_rbuf, RZ_OUT RzThreadRingBuf **branch_rbuf, - RZ_OUT RzThreadRingBuf **entry_points_rbuf, RZ_OUT HtUP **yield_rbufs) { *il_queue = NULL; *io_request_rbuf = NULL; *io_result_rbuf = NULL; *branch_rbuf = NULL; *yield_rbufs = NULL; - *entry_points_rbuf = NULL; RzInterpreterYieldRBuf *rbuf = NULL; // The queue to pass the Effects to the interpreter. @@ -238,15 +234,6 @@ static bool setup_ipc_objects( goto error_free; } - // The entry_points ring buffer. It is the ring buffer the interpreter gets - // new entry points passed over. - *entry_points_rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_IL_QUEUE_SIZE, sizeof(ut64)); - if (!*entry_points_rbuf) { - rz_warn_if_reached(); - rz_pvector_free(sections); - goto error_free; - } - // Multiple yield queues can be used by a single interpreter. // E.g. if the interpreter has a complex abstract memory model // for stack, heap and constant values. @@ -343,10 +330,9 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RzThreadRingBuf *io_request_rbuf = NULL; RzThreadRingBuf *io_result_rbuf = NULL; RzThreadRingBuf *branch_rbuf = NULL; - RzThreadRingBuf *entry_points = NULL; RzThreadQueue *il_queue = NULL; HtUP *yield_rbufs = NULL; - if (!setup_ipc_objects(sections, yield_filter, &il_queue, &io_request_rbuf, &io_result_rbuf, &branch_rbuf, &entry_points, &yield_rbufs)) { + if (!setup_ipc_objects(sections, yield_filter, &il_queue, &io_request_rbuf, &io_result_rbuf, &branch_rbuf, &yield_rbufs)) { free(iset); rz_analysis_il_vm_free(il_vm); return NULL; @@ -362,7 +348,6 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( iset->io_request_rbuf = io_request_rbuf; iset->io_result_rbuf = io_result_rbuf; iset->run_state_sync = rz_th_sem_new(0); - iset->entry_points = entry_points; iset->ignored_code = ignored_code; return iset; } @@ -440,7 +425,6 @@ static bool choose_next_pc(RzInterpreterSet *iset, static bool setup_intrpr_state( RzInterpreterSet *iset, ut64 entry_point, - const RzInterpreterILBB **il_bb, RzVector **tmp_succ_addr, RzSetU **reachable_states, RzVector **succ_states) { @@ -454,21 +438,10 @@ static bool setup_intrpr_state( return false; } - RzInterpreterBranch branch = { 0 }; - branch.target_addr = entry_point; - if (rz_th_ring_buf_put(iset->branch_rbuf, &branch) != RZ_THREAD_RING_BUF_OK) { - rz_warn_if_reached(); - return false; - } - if (!rz_th_queue_pop(iset->il_queue, false, (void **)il_bb) || !*il_bb) { - rz_warn_if_reached(); - return false; - } - *tmp_succ_addr = rz_vector_new(sizeof(ut64), NULL, NULL); *succ_states = rz_vector_new(sizeof(SuccessorState), NULL, NULL); *reachable_states = rz_set_u_new(); - if (!tmp_succ_addr || !succ_states || !*il_bb || !reachable_states) { + if (!tmp_succ_addr || !succ_states || !reachable_states) { rz_warn_if_reached(); return false; } @@ -518,18 +491,17 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // what the interpreter does in each state. INIT: { + RZ_LOG_DEBUG("Enter INIT\n"); rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_INIT); - // Get the next entry point. - ut64 entry_point; - if (rz_th_ring_buf_take_blocking(iset->entry_points, &entry_point) != RZ_THREAD_RING_BUF_OK) { - // No entry point to emulate. Interpreter is done. + if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { + // No more BBs to interpret. Terminate. success = true; goto TERM; } // Initializes the current interpreter's private data and its state. - if (!setup_intrpr_state(iset, entry_point, &il_bb, &tmp_succ_addr, &reachable_states, &succ_states)) { + if (!setup_intrpr_state(iset, il_bb->bb_addr, &tmp_succ_addr, &reachable_states, &succ_states)) { success = false; goto TERM; } @@ -538,6 +510,7 @@ INIT: { } EMU: { + RZ_LOG_DEBUG("Enter EMU\n"); rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_EMU); iset->astate->bb_addr = il_bb->bb_addr; @@ -566,6 +539,7 @@ EMU: { } // Set effect and state for next evaluation. + il_bb = NULL; SuccessorState next = { 0 }; rz_vector_pop_front(succ_states, &next); if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { @@ -574,6 +548,7 @@ EMU: { // pointed to an unmapped region. goto CLEAN; } + RZ_LOG_DEBUG("Received il_bb: 0x%llx\n", il_bb->bb_addr); if (!plugin->set_pc(iset->astate, next.addr, iset->intrpr_priv)) { rz_warn_if_reached(); goto CLEAN; @@ -584,14 +559,15 @@ EMU: { } CLEAN: { + RZ_LOG_DEBUG("Enter CLEAN\n"); rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_CLEAN); RZ_FREE_CUSTOM(tmp_succ_addr, rz_vector_free); RZ_FREE_CUSTOM(succ_states, rz_vector_free); RZ_FREE_CUSTOM(reachable_states, rz_set_u_free); iset->plugin->fini_state(iset->astate, iset->intrpr_priv); - if (iset->plugin->fini) { - iset->plugin->fini(iset->intrpr_priv); + if (iset->plugin->fini && iset->intrpr_priv) { + RZ_FREE_CUSTOM(iset->intrpr_priv, iset->plugin->fini); } // Wait until RzInquiry asks to start again. @@ -602,14 +578,15 @@ CLEAN: { } TERM: { + RZ_LOG_DEBUG("Enter TERM\n"); rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_TERM); RZ_FREE_CUSTOM(tmp_succ_addr, rz_vector_free); RZ_FREE_CUSTOM(succ_states, rz_vector_free); RZ_FREE_CUSTOM(reachable_states, rz_set_u_free); iset->plugin->fini_state(iset->astate, iset->intrpr_priv); - if (iset->plugin->fini) { - iset->plugin->fini(iset->intrpr_priv); + if (iset->plugin->fini && iset->intrpr_priv) { + RZ_FREE_CUSTOM(iset->intrpr_priv, iset->plugin->fini); } return success; } diff --git a/librz/inquiry/interpreter/state.c b/librz/inquiry/interpreter/state.c index 679b18264f2..80e341a709e 100644 --- a/librz/inquiry/interpreter/state.c +++ b/librz/inquiry/interpreter/state.c @@ -10,6 +10,8 @@ struct rz_intp_run_state { RZ_API const char *rz_intp_run_state_flag_str(RzIntpRunStateFlag flag) { switch (flag) { + case RZ_INTP_RUN_STATE_OUT_OF_LOOP: + return "-"; case RZ_INTP_RUN_STATE_INIT: return "I"; case RZ_INTP_RUN_STATE_EMU: @@ -28,7 +30,7 @@ RZ_API RZ_OWN RzIntpRunState *rz_intp_run_state_new() { if (!state) { return NULL; } - state->flag = RZ_INTP_RUN_STATE_INIT; + state->flag = RZ_INTP_RUN_STATE_OUT_OF_LOOP; state->lock = rz_th_lock_new(false); if (!state->lock) { free(state); @@ -53,7 +55,11 @@ RZ_API RzIntpRunStateFlag rz_intp_run_state_get(RZ_BORROW RZ_NONNULL RzIntpRunSt return flag; } -RZ_API void rz_intp_run_state_set(RZ_BORROW RZ_NONNULL RzIntpRunState *state, RzIntpRunStateFlag flag) { +/** + * \brief Sets the run state. + * This function is declared IPI, so it is not used outside of the interpreter module! + */ +RZ_IPI void rz_intp_run_state_set(RZ_BORROW RZ_NONNULL RzIntpRunState *state, RzIntpRunStateFlag flag) { rz_th_lock_enter(state->lock); state->flag = flag; rz_th_lock_leave(state->lock); From d825e2e3f44fe41cdc7cf1e41e9ccdbf491da115 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 2 Apr 2026 21:40:00 +0200 Subject: [PATCH 243/334] Reset abstract state instead of freeing and allocating it. --- librz/include/rz_inquiry/rz_interpreter.h | 7 ++- librz/inquiry/interpreter/interpreter.c | 26 ++++++---- .../interpreter/p/interpreter_prototype.c | 48 ++++++++++++++++++- 3 files changed, 69 insertions(+), 12 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index f01ee348a43..cf9077ebac6 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -159,11 +159,16 @@ typedef struct { */ RzInterpreterYieldKind supported_yields; bool (*init)(void **plugin_data); + bool (*reset)(void *plugin_data); bool (*fini)(void *plugin_data); /** * \brief Initializes the abstract state. */ - bool (*init_state)(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_point, void *plugin_data); + bool (*init_state)(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data); + /** + * \brief Reset the abstract state. + */ + bool (*reset_state)(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_point, void *plugin_data); /** * \brief Closes the abstract state and frees all its abstract data and sets the pointers to NULL. */ diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index a70d5b79a7c..324f7772db4 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -422,18 +422,18 @@ static bool choose_next_pc(RzInterpreterSet *iset, return has_succsessor; } -static bool setup_intrpr_state( +static bool reset_intrpr_state( RzInterpreterSet *iset, ut64 entry_point, RzVector **tmp_succ_addr, RzSetU **reachable_states, RzVector **succ_states) { - if (iset->plugin->init) { - iset->plugin->init(&iset->intrpr_priv); + if (iset->plugin->reset) { + iset->plugin->reset(iset->intrpr_priv); } - if (!iset->plugin->init_state(iset->astate, entry_point, iset->intrpr_priv)) { + if (!iset->plugin->reset_state(iset->astate, entry_point, iset->intrpr_priv)) { rz_warn_if_reached(); return false; } @@ -485,6 +485,14 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { const RzInterpreterILBB *il_bb = NULL; ut64 astate_hash = 0; + if (iset->plugin->init) { + iset->plugin->init(&iset->intrpr_priv); + } + if (!iset->plugin->init_state(iset->astate, iset->intrpr_priv)) { + rz_warn_if_reached(); + return false; + } + // TODO: It is probably better to make the following stuff while-loops. // Because otherwise it doesn't make sense without the docs. // But while debugging and developing, I keep it this way to separate clearly @@ -501,7 +509,7 @@ INIT: { } // Initializes the current interpreter's private data and its state. - if (!setup_intrpr_state(iset, il_bb->bb_addr, &tmp_succ_addr, &reachable_states, &succ_states)) { + if (!reset_intrpr_state(iset, il_bb->bb_addr, &tmp_succ_addr, &reachable_states, &succ_states)) { success = false; goto TERM; } @@ -565,10 +573,6 @@ CLEAN: { RZ_FREE_CUSTOM(tmp_succ_addr, rz_vector_free); RZ_FREE_CUSTOM(succ_states, rz_vector_free); RZ_FREE_CUSTOM(reachable_states, rz_set_u_free); - iset->plugin->fini_state(iset->astate, iset->intrpr_priv); - if (iset->plugin->fini && iset->intrpr_priv) { - RZ_FREE_CUSTOM(iset->intrpr_priv, iset->plugin->fini); - } // Wait until RzInquiry asks to start again. rz_th_sem_wait(iset->run_state_sync); @@ -580,6 +584,10 @@ CLEAN: { TERM: { RZ_LOG_DEBUG("Enter TERM\n"); rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_TERM); + iset->plugin->fini_state(iset->astate, iset->intrpr_priv); + if (iset->plugin->fini && iset->intrpr_priv) { + RZ_FREE_CUSTOM(iset->intrpr_priv, iset->plugin->fini); + } RZ_FREE_CUSTOM(tmp_succ_addr, rz_vector_free); RZ_FREE_CUSTOM(succ_states, rz_vector_free); diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index b436a7acf75..8c1d461dd7c 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -8,6 +8,7 @@ #include "../prototype/eval.h" #include "rz_util/ht_uu.h" +#include "rz_util/rz_bitvector.h" #define MAX_INVOCATIONS_PER_BB 3 @@ -80,9 +81,9 @@ bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, return true; } -static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_point, void *plugin_data) { +static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data) { state->pc->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); - AD(state->pc->abstr_data)->bv = rz_bv_new_from_ut64(state->il_config->mem_key_size, entry_point); + AD(state->pc->abstr_data)->bv = rz_bv_new_from_ut64(state->il_config->mem_key_size, 0); AD(state->pc->abstr_data)->is_concrete = true; RzIterator *it = ht_up_as_iter_keys(state->globals); ut64 *k; @@ -117,6 +118,36 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_poin return true; } +static bool reset_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_point, void *plugin_data) { + rz_bv_set_from_ut64(AD(state->pc->abstr_data)->bv, entry_point); + AD(state->pc->abstr_data)->is_concrete = true; + + RzIterator *it = ht_up_as_iter_keys(state->globals); + ut64 *k; + rz_iterator_foreach(it, k) { + ut64 djb2_reg_name = *k; + RzInterpreterAbstrVal *av = ht_up_find(state->globals, djb2_reg_name, NULL); + rz_bv_set_from_ut64(AD(av->abstr_data)->bv, 0); + AD(av->abstr_data)->is_concrete = true; + if (state->il_config->init_state) { + RzAnalysisILInitStateVar *il_var; + rz_vector_foreach (&state->il_config->init_state->vars, il_var) { + if (rz_str_djb2_hash(il_var->name) != djb2_reg_name) { + continue; + } + // The RzArch plugin defined a default value for this global. + RzBitVector *default_val = rz_il_value_to_bv(il_var->val); + rz_bv_copy(AD(av->abstr_data)->bv, default_val); + rz_bv_free(default_val); + } + } + } + rz_iterator_free(it); + state->bb_addr = 0; + state->bb_size = 0; + return true; +} + static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data) { ProtoIntrprAbstrData *ad = state->pc->abstr_data; if (ad && ad->bv) { @@ -240,6 +271,17 @@ bool fini(void *plugin_data) { return true; } +bool reset(void *plugin_data) { + if (!plugin_data) { + return true; + } + RZ_LOG_DEBUG("prototype: reset()\n"); + ProtoIntrprPluginData *pdata = plugin_data; + ht_uu_clear(pdata->bb_invocation_count); + memset(&pdata->call_cand, 0, sizeof(RzAnalysisCallCandidate)); + return true; +} + static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .name = "abstr_int_prototype", .author = "Rot127", @@ -249,10 +291,12 @@ static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .supported_abstractions = RZ_INTERPRETER_ABSTRACTION_CONST, .supported_yields = RZ_INTERPRETER_YIELD_KIND_XREF | RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, .init = init, + .reset = reset, .fini = fini, .eval = eval, .successors = successors, .init_state = init_state, + .reset_state = reset_state, .fini_state = fini_state, .hash_state = hash_state, .set_pc = set_pc, From 09a4cff40921462e3eb4555d9dc1833c1435a16e Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 3 Apr 2026 20:36:25 +0200 Subject: [PATCH 244/334] Added wrong file. --- librz/inquiry/interpreter/state.c | 4 ++-- librz/inquiry/state.c | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 librz/inquiry/state.c diff --git a/librz/inquiry/interpreter/state.c b/librz/inquiry/interpreter/state.c index 80e341a709e..7fc2b4e4182 100644 --- a/librz/inquiry/interpreter/state.c +++ b/librz/inquiry/interpreter/state.c @@ -49,9 +49,9 @@ RZ_API void rz_intp_run_state_free(RZ_OWN RZ_NULLABLE RzIntpRunState *state) { RZ_API RzIntpRunStateFlag rz_intp_run_state_get(RZ_BORROW RZ_NONNULL RzIntpRunState *state) { rz_return_val_if_fail(state, RZ_INTP_RUN_STATE_TERM); - rz_th_lock_enter(state->lock); + // rz_th_lock_enter(state->lock); RzIntpRunStateFlag flag = state->flag; - rz_th_lock_leave(state->lock); + // rz_th_lock_leave(state->lock); return flag; } diff --git a/librz/inquiry/state.c b/librz/inquiry/state.c deleted file mode 100644 index 8b137891791..00000000000 --- a/librz/inquiry/state.c +++ /dev/null @@ -1 +0,0 @@ - From efc8986c275720f530c6f2578aa2b9b73bdbe95a Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 3 Apr 2026 21:58:17 +0200 Subject: [PATCH 245/334] Add unsafe function to check for state. --- librz/include/rz_inquiry/rz_interpreter.h | 1 + librz/inquiry/interpreter/state.c | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index cf9077ebac6..3439947b888 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -277,6 +277,7 @@ struct rz_interpreter_set { RZ_API RZ_OWN RzIntpRunState *rz_intp_run_state_new(); RZ_API void rz_intp_run_state_free(RZ_OWN RZ_NULLABLE RzIntpRunState *state); RZ_API RzIntpRunStateFlag rz_intp_run_state_get(RZ_BORROW RZ_NONNULL RzIntpRunState *state); +RZ_API RzIntpRunStateFlag rz_intp_run_state_get_unsafe(const RZ_NONNULL RzIntpRunState *state); RZ_API const char *rz_intp_run_state_flag_str(RzIntpRunStateFlag flag); RZ_IPI void rz_intp_run_state_set(RZ_BORROW RZ_NONNULL RzIntpRunState *state, RzIntpRunStateFlag flag); diff --git a/librz/inquiry/interpreter/state.c b/librz/inquiry/interpreter/state.c index 7fc2b4e4182..c5beeaa020d 100644 --- a/librz/inquiry/interpreter/state.c +++ b/librz/inquiry/interpreter/state.c @@ -49,12 +49,17 @@ RZ_API void rz_intp_run_state_free(RZ_OWN RZ_NULLABLE RzIntpRunState *state) { RZ_API RzIntpRunStateFlag rz_intp_run_state_get(RZ_BORROW RZ_NONNULL RzIntpRunState *state) { rz_return_val_if_fail(state, RZ_INTP_RUN_STATE_TERM); - // rz_th_lock_enter(state->lock); + rz_th_lock_enter(state->lock); RzIntpRunStateFlag flag = state->flag; - // rz_th_lock_leave(state->lock); + rz_th_lock_leave(state->lock); return flag; } +RZ_API RzIntpRunStateFlag rz_intp_run_state_get_unsafe(const RZ_NONNULL RzIntpRunState *state) { + rz_return_val_if_fail(state, RZ_INTP_RUN_STATE_TERM); + return state->flag; +} + /** * \brief Sets the run state. * This function is declared IPI, so it is not used outside of the interpreter module! From a4c8b821096ce2575c2c0916c1f79400c5cebcf6 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 3 Apr 2026 21:59:16 +0200 Subject: [PATCH 246/334] Convert yield_rbufs to an array to safe HT lookup time. --- librz/include/rz_inquiry/rz_interpreter.h | 9 +++-- librz/inquiry/inquiry.c | 8 ++-- librz/inquiry/interpreter/interpreter.c | 40 +++++++++---------- .../interpreter/p/interpreter_prototype.c | 2 +- librz/inquiry/interpreter/prototype/eval.c | 4 +- 5 files changed, 31 insertions(+), 32 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 3439947b888..440bc3f6c05 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -99,7 +99,7 @@ typedef enum { /** * \brief The yield is an cross reference. */ - RZ_INTERPRETER_YIELD_KIND_XREF = 1 << 0, + RZ_INTERPRETER_YIELD_KIND_XREF = 0, /** * \brief This yield is a simple flag, signaling if the current basic block @@ -108,7 +108,8 @@ typedef enum { * If the last branch instruction does not jump to the neighboring basic block * it is a strong indicator that the jump is a call and the next address a return point. */ - RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE = 1 << 1, + RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, + RZ_INTERPRETER_YIELD_KIND_NUM, } RzInterpreterYieldKind; /** @@ -157,7 +158,7 @@ typedef struct { /** * \brief The yield type this interpreter generates. */ - RzInterpreterYieldKind supported_yields; + RzInterpreterYieldKind supported_yields[RZ_INTERPRETER_YIELD_KIND_NUM]; bool (*init)(void **plugin_data); bool (*reset)(void *plugin_data); bool (*fini)(void *plugin_data); @@ -259,7 +260,7 @@ struct rz_interpreter_set { * \brief The ring buffers to push the yield of interpretation into. * These ring buffers are shared with other interpreter sets. */ - HtUP /**/ *yield_rbufs; + RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM]; /** * \brief Ignored address ranges. */ diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index bef3d986123..d66f0c1ff33 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -271,8 +271,8 @@ static bool get_branch_targets(RzCore *core, RzSetU *branch_targets, RzVector /* return true; } -static bool handle_yields(RzCore *core, HtUP *yield_rbufs) { - RzInterpreterYieldRBuf *rbuf_xrefs = ht_up_find(yield_rbufs, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); +static bool handle_yields(RzCore *core, RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM]) { + RzInterpreterYieldRBuf *rbuf_xrefs = yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF]; rz_return_val_if_fail(rbuf_xrefs, false); RzAnalysisXRef xref = { 0 }; @@ -288,7 +288,7 @@ static bool handle_yields(RzCore *core, HtUP *yield_rbufs) { } } - RzInterpreterYieldRBuf *rbuf_calls = ht_up_find(yield_rbufs, RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, NULL); + RzInterpreterYieldRBuf *rbuf_calls = yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE]; rz_return_val_if_fail(rbuf_calls, false); RzAnalysisCallCandidate cc = { 0 }; @@ -538,7 +538,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RzInterpreterSet *iset = iset_map[i].iset; RzIntpRunStateFlag expected_rs = iset_map[i].next_run_state; - switch (rz_intp_run_state_get(iset->run_state)) { + switch (rz_intp_run_state_get_unsafe(iset->run_state)) { case RZ_INTP_RUN_STATE_OUT_OF_LOOP: break; case RZ_INTP_RUN_STATE_INIT: { diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 324f7772db4..1e2d6382c0e 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -57,6 +57,9 @@ RZ_API RZ_OWN RzInterpreterYieldRBuf *rz_interpreter_yield_rbuf_new(RzInterprete } RzThreadRingBuf *rbuf = NULL; switch (kind) { + default: + rz_warn_if_reached(); + return NULL; case RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE: rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_YIELD_RBUF_SIZE, sizeof(RzAnalysisCallCandidate)); break; @@ -184,12 +187,10 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { if (iset->il_vm) { rz_analysis_il_vm_free(iset->il_vm); } - if (iset->yield_rbufs) { - ht_up_free(iset->yield_rbufs); - } + rz_interpreter_yield_rbuf_free(iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF]); + rz_interpreter_yield_rbuf_free(iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE]); free(iset); } - static bool setup_ipc_objects( RZ_OWN RzPVector /**/ *sections, RzInterpreterYieldFilter yield_filter, @@ -197,12 +198,13 @@ static bool setup_ipc_objects( RZ_OUT RzThreadRingBuf **io_request_rbuf, RZ_OUT RzThreadRingBuf **io_result_rbuf, RZ_OUT RzThreadRingBuf **branch_rbuf, - RZ_OUT HtUP **yield_rbufs) { + RZ_OUT RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM]) { *il_queue = NULL; *io_request_rbuf = NULL; *io_result_rbuf = NULL; *branch_rbuf = NULL; - *yield_rbufs = NULL; + yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE] = NULL; + yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF] = NULL; RzInterpreterYieldRBuf *rbuf = NULL; // The queue to pass the Effects to the interpreter. @@ -234,17 +236,10 @@ static bool setup_ipc_objects( goto error_free; } - // Multiple yield queues can be used by a single interpreter. + // A single interpreter can produce different yields. // E.g. if the interpreter has a complex abstract memory model // for stack, heap and constant values. // Then it can produce three kind of yields. - *yield_rbufs = ht_up_new(NULL, (HtUPFreeValue)rz_interpreter_yield_rbuf_free); - if (!*yield_rbufs) { - rz_warn_if_reached(); - rz_pvector_free(sections); - goto error_free; - } - // These yield queues can be shared between different interpreters. // So we have one yield queue for each yield type. @@ -255,7 +250,7 @@ static bool setup_ipc_objects( rz_pvector_free(sections); goto error_free; } - ht_up_insert(*yield_rbufs, yield_kind, rbuf); + yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE] = rbuf; yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; rbuf = rz_interpreter_yield_rbuf_new( @@ -266,11 +261,12 @@ static bool setup_ipc_objects( rz_warn_if_reached(); goto error_free; } - ht_up_insert(*yield_rbufs, yield_kind, rbuf); + yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF] = rbuf; return true; error_free: - ht_up_free(*yield_rbufs); + rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF]); + rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE]); rz_th_queue_free(*il_queue); rz_th_ring_buf_free(*io_request_rbuf); rz_th_ring_buf_free(*io_result_rbuf); @@ -331,8 +327,8 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RzThreadRingBuf *io_result_rbuf = NULL; RzThreadRingBuf *branch_rbuf = NULL; RzThreadQueue *il_queue = NULL; - HtUP *yield_rbufs = NULL; - if (!setup_ipc_objects(sections, yield_filter, &il_queue, &io_request_rbuf, &io_result_rbuf, &branch_rbuf, &yield_rbufs)) { + RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM]; + if (!setup_ipc_objects(sections, yield_filter, &il_queue, &io_request_rbuf, &io_result_rbuf, &branch_rbuf, yield_rbufs)) { free(iset); rz_analysis_il_vm_free(il_vm); return NULL; @@ -344,7 +340,8 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( iset->il_vm = il_vm; iset->il_queue = il_queue; iset->branch_rbuf = branch_rbuf; - iset->yield_rbufs = yield_rbufs; + iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF] = yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF]; + iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE] = yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE]; iset->io_request_rbuf = io_request_rbuf; iset->io_result_rbuf = io_result_rbuf; iset->run_state_sync = rz_th_sem_new(0); @@ -456,7 +453,8 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { iset->astate && iset->branch_rbuf && iset->il_queue && - iset->yield_rbufs && + iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF] && + iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE] && iset->run_state_sync && iset->plugin && iset->plugin->eval && diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 8c1d461dd7c..db35ba4e4c3 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -289,7 +289,7 @@ static RzInterpreterPlugin rz_interpreter_plugin_prototype = { .desc = "A prototype interpreter for constant/bottom abstractions.", .license = "LGPL-3.0-only", .supported_abstractions = RZ_INTERPRETER_ABSTRACTION_CONST, - .supported_yields = RZ_INTERPRETER_YIELD_KIND_XREF | RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, + .supported_yields = { RZ_INTERPRETER_YIELD_KIND_XREF, RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE }, .init = init, .reset = reset, .fini = fini, diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 7318ea8e556..5479357a568 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -33,7 +33,7 @@ bool report_yield_xref( return true; } - RzInterpreterYieldRBuf *yrbuf = ht_up_find(iset->yield_rbufs, RZ_INTERPRETER_YIELD_KIND_XREF, NULL); + RzInterpreterYieldRBuf *yrbuf = iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF]; rz_return_val_if_fail(yrbuf, false); ut64 to_addr = rz_bv_to_ut64(to->bv); @@ -56,7 +56,7 @@ bool report_yield_xref( bool report_yield_call_candiate( RzInterpreterSet *iset, ProtoIntrprPluginData *plugin_data) { - RzInterpreterYieldRBuf *cc_rbuf = ht_up_find(iset->yield_rbufs, RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, NULL); + RzInterpreterYieldRBuf *cc_rbuf = iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE]; rz_return_val_if_fail(cc_rbuf, false); RzAnalysisCallCandidate cc = { 0 }; From 2559d02622fb1e6f07bf14e1c097d669cb235849 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 3 Apr 2026 23:04:08 +0200 Subject: [PATCH 247/334] Limit number of checks on rz_cons_breaked. --- librz/include/rz_inquiry.h | 6 ++++++ librz/inquiry/inquiry.c | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index d3a8379e7bc..7210e3114eb 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -15,6 +15,12 @@ extern "C" { #include +/** + * \brief The number of iterations inquiry checks for a user given signal. + * Checking it costs performance, so it is just checked the X iterations. + */ +#define RZ_INQUIRY_CHECK_USER_SIGNAL_ITC 1000 + typedef struct rz_inquiry_plugin_t { RzInterpreterPlugin *p_interpreter; } RzInquiryPlugin; diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index d66f0c1ff33..ce6055f6491 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -528,10 +528,11 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, .next_run_state = RZ_INTP_RUN_STATE_INIT } }; ut64 intpr_terminated = 0; + ut64 check_signal = 0; // TODO: Add the other threads. - for (ut64 i = 0;;) { - if (rz_cons_is_breaked()) { + for (ut64 i = 0;; check_signal++) { + if (check_signal % RZ_INQUIRY_CHECK_USER_SIGNAL_ITC == 0 && rz_cons_is_breaked()) { user_sent_signal = true; break; } From 94a744769a69173c2a521cc2737edd72134123f6 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 7 Apr 2026 15:51:11 +0200 Subject: [PATCH 248/334] Fix rebase issues --- librz/inquiry/inquiry.c | 5 +++-- librz/inquiry/inquiry_helpers.c | 6 +++--- librz/inquiry/interpreter/interpreter.c | 7 ++++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index ce6055f6491..9d768921a30 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -467,7 +467,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, bool return_code = true; RzInterpreterSet *intp_iset = NULL; HtUP *il_cache = NULL; - RzBuffer *io_buf = rz_buf_new_with_io(&core->analysis->iob); + + RzBuffer *io_buf = rz_buf_new_with_io(rz_analysis_get_io_bind(core->analysis)); RzSetU *symbol_targets = rz_set_u_new(); bool user_sent_signal = false; RzVector /**/ *insn_to_insn_edges = rz_vector_new(sizeof(RzAnalysisXRef), NULL, NULL); @@ -486,7 +487,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, } // Initialize the abstract state with the architecture's registers. - if (!core->analysis->cur->il_config) { + if (!rz_analysis_plugin_current(core->analysis)->il_config) { RZ_LOG_ERROR("The RzArch plugin doesn't have il_config() implemented.\n"); return_code = false; goto error_free; diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c index 49f470fb886..b350b035788 100644 --- a/librz/inquiry/inquiry_helpers.c +++ b/librz/inquiry/inquiry_helpers.c @@ -13,12 +13,12 @@ #include RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr) { - rz_return_val_if_fail(analysis && analysis->cur && io, NULL); + rz_return_val_if_fail(analysis && rz_analysis_plugin_current(analysis) && io, NULL); RzInterpreterILBB *il_bb = NULL; RzAnalysisOp op = { 0 }; rz_analysis_op_init(&op); // Estimate a reasonable number of bytes to read. - int max_read_size = (analysis->cur->bits / 8) * 16; + int max_read_size = (rz_analysis_plugin_current(analysis)->bits / 8) * 16; ut8 *buf = RZ_NEWS0(ut8, max_read_size); if (!max_read_size || !buf) { rz_warn_if_reached(); @@ -75,7 +75,7 @@ RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *ana // Instruction was added, now the BB is complete. break; } - if (changes_cf && RZ_STR_EQ(analysis->cur->arch, "sparc")) { + if (changes_cf && RZ_STR_EQ(rz_analysis_plugin_current(analysis)->arch, "sparc")) { // We need to add the instruction after the branch. // So one more iteration is needed. sparc_add_delayed_insn = true; diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 1e2d6382c0e..e52d4bce5e9 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -309,16 +309,17 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( // It allows the IO handler to buffer reads in r-- sections for multiple interpreters. // Possibly allows to optimize the IO access, because there is only module accessing it (not every interpreter). // But is there any other advantage? - RzAnalysisILVM *il_vm = rz_analysis_il_vm_new(analysis, analysis->reg); + RzAnalysisILVM *il_vm = rz_analysis_il_vm_new(analysis, rz_analysis_get_reg(analysis)); if (!il_vm) { free(iset); RZ_LOG_ERROR("Failed during RzAnalysisILVM setup.\n"); return NULL; } - RzAnalysisILConfig *config = analysis->cur->il_config(analysis); + const RzAnalysisPlugin *cur = rz_analysis_plugin_current(analysis); + RzAnalysisILConfig *config = cur->il_config(analysis); RzInterpreterAbstrState *state = rz_interpreter_abstr_state_new( - analysis->cur->arch, + cur->arch, abstraction, config, il_vm->reg_binding); From 96a4ca297c5261ed04c5db4754050bfc163c795c Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 7 Apr 2026 18:28:47 +0200 Subject: [PATCH 249/334] Improve performance by casting before setting. --- librz/inquiry/interpreter/prototype/eval_effect.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 003be75b8c9..4357eaef667 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -33,8 +33,12 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterSet *iset, break; } STACK_ABSTR_DATA_OUT(inc); - rz_bv_set_from_ut64(inc.bv, insn_pkt_size); + // First cast the bitvector, then set it. + // This is performance critical. Since the stack allocated bv is >64 bit + // the rz_bv_set_from_ut64() will set its whole memory, eating a lot of runtime. + // If we cast before, it is simply an assignment to bv->small_bits. rz_bv_cast_inplace(inc.bv, rz_bv_len(pc->bv), false); + rz_bv_set_from_ut64(inc.bv, insn_pkt_size); #if RZ_BUILD_DEBUG ut64 old_pc = rz_bv_to_ut64(pc->bv); #endif From a661a8307796a787b49c7555ee5d3331db0eea85 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 7 Apr 2026 18:29:51 +0200 Subject: [PATCH 250/334] Speed up rz_bv_set_from functions. --- test/unit/test_bitvector.c | 1 - 1 file changed, 1 deletion(-) diff --git a/test/unit/test_bitvector.c b/test/unit/test_bitvector.c index 2d1b53edfdd..daac8ca85f1 100644 --- a/test/unit/test_bitvector.c +++ b/test/unit/test_bitvector.c @@ -92,7 +92,6 @@ bool test_rz_bv_init128(void) { rz_bv_free(bits_dup); mu_end; } - bool test_rz_bv_init70(void) { char *s = NULL; From 6952825ced3f1ffe555d3c797217000865b17bb3 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 7 Apr 2026 19:50:43 +0200 Subject: [PATCH 251/334] Add multiple threads --- librz/include/rz_inquiry/rz_interpreter.h | 5 +- librz/inquiry/inquiry.c | 93 +++++++++++++++++------ librz/inquiry/interpreter/interpreter.c | 49 +----------- 3 files changed, 76 insertions(+), 71 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 440bc3f6c05..49c8d892ebd 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -260,7 +260,7 @@ struct rz_interpreter_set { * \brief The ring buffers to push the yield of interpretation into. * These ring buffers are shared with other interpreter sets. */ - RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM]; + RZ_BORROW RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM]; /** * \brief Ignored address ranges. */ @@ -303,8 +303,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RzAnalysis *analysis, RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, RzInterpreterAbstraction abstraction, - RZ_OWN RzPVector /**/ *sections, - RzInterpreterYieldFilter yield_filter, + RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM], RZ_NONNULL const RzVector /**/ *ignored_code); RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 9d768921a30..dff3ab27578 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -450,6 +450,39 @@ static bool collect_entry_points(RzCore *core, return true; } +static bool setup_yield_rbufs( + RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM], + RZ_OWN RzPVector /**/ *sections, + RzInterpreterYieldFilter yield_filter) { + // A single interpreter can produce different yields. + // E.g. if the interpreter has a complex abstract memory model + // for stack, heap and constant values. + // Then it can produce three kind of yields. + // These yield queues can be shared between different interpreters. + // So we have one yield queue for each yield type. + + RzInterpreterYieldKind yield_kind = RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE; + RzInterpreterYieldRBuf *rbuf = NULL; + rbuf = rz_interpreter_yield_rbuf_new(yield_kind, NULL, NULL); + if (!rbuf) { + rz_warn_if_reached(); + return false; + } + yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE] = rbuf; + + yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; + rbuf = rz_interpreter_yield_rbuf_new( + yield_kind, + yield_filter, + sections); + if (!rbuf) { + rz_warn_if_reached(); + return false; + } + yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF] = rbuf; + return true; +} + struct ituple { RzThread *ithread; RzInterpreterSet *iset; @@ -467,10 +500,11 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, bool return_code = true; RzInterpreterSet *intp_iset = NULL; HtUP *il_cache = NULL; - + RzBuffer *io_buf = rz_buf_new_with_io(rz_analysis_get_io_bind(core->analysis)); RzSetU *symbol_targets = rz_set_u_new(); bool user_sent_signal = false; + struct ituple *iset_map = NULL; RzVector /**/ *insn_to_insn_edges = rz_vector_new(sizeof(RzAnalysisXRef), NULL, NULL); rz_cons_push(); @@ -507,32 +541,42 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_warn_if_reached(); goto error_free; } - intp_iset = rz_interpreter_set_new( - core->analysis, - prototype->p_interpreter, - RZ_INTERPRETER_ABSTRACTION_CONST, - rz_bin_object_get_sections(core->bin->cur->o), - (RzInterpreterYieldFilter)rz_inquiry_xref_interpreter_filter, - ignored_code); - if (!intp_iset) { + size_t n_threads = 8; + iset_map = RZ_NEWS0(struct ituple, n_threads); + + RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM] = { 0 }; + if (!setup_yield_rbufs(yield_rbufs, rz_bin_object_get_sections(core->bin->cur->o), + (RzInterpreterYieldFilter)rz_inquiry_xref_interpreter_filter)) { return_code = false; rz_warn_if_reached(); goto error_free; } - // Dispatch prototype interpreter into a thread. - RZ_LOG_DEBUG("INQUIRY: Start main interpretation thread.\n"); - RzThread *interpr_th = interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, intp_iset); - struct ituple iset_map[] = { - { .ithread = interpr_th, - .iset = intp_iset, - .next_run_state = RZ_INTP_RUN_STATE_INIT } - }; + for (size_t i = 0; i < n_threads; ++i) { + intp_iset = rz_interpreter_set_new( + core->analysis, + prototype->p_interpreter, + RZ_INTERPRETER_ABSTRACTION_CONST, + yield_rbufs, + ignored_code); + if (!intp_iset) { + return_code = false; + rz_warn_if_reached(); + goto error_free; + } + + // Dispatch prototype interpreter into a thread. + RZ_LOG_DEBUG("INQUIRY: Start main interpretation thread.\n"); + RzThread *interpr_th = interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, intp_iset); + iset_map[i].ithread = interpr_th; + iset_map[i].iset = intp_iset; + iset_map[i].next_run_state = RZ_INTP_RUN_STATE_INIT; + } + ut64 intpr_terminated = 0; ut64 check_signal = 0; - // TODO: Add the other threads. - for (ut64 i = 0;; check_signal++) { + for (ut64 i = 0;; check_signal++, i = (i + 1) % n_threads) { if (check_signal % RZ_INQUIRY_CHECK_USER_SIGNAL_ITC == 0 && rz_cons_is_breaked()) { user_sent_signal = true; break; @@ -667,7 +711,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, break; } } - if (intpr_terminated == RZ_ARRAY_SIZE(iset_map)) { + if (intpr_terminated == n_threads) { break; } } @@ -675,14 +719,15 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, fatal_error: RZ_LOG_DEBUG("INQUIRY: Wait for join\n"); - for (size_t i = 0; i < RZ_ARRAY_SIZE(iset_map); i++) { + for (size_t i = 0; i < n_threads; i++) { close_reset_ipc_obj(iset_map[i].iset); // Open semaphore so the interpreter can transition // EMU -> CLEAN -> INIT -> TERM rz_th_sem_post(iset_map[i].iset->run_state_sync); } - for (size_t i = 0; i < RZ_ARRAY_SIZE(iset_map); i++) { + // Wait for thread to finish before cleaning. + for (size_t i = 0; i < n_threads; i++) { rz_th_wait(iset_map[i].ithread); bool interpr_ret = rz_th_get_retv(iset_map[i].ithread); rz_th_free(iset_map[i].ithread); @@ -714,8 +759,10 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_config_set(core->config, "io.cache", io_cache_opt); - // Wait for thread to finish before cleaning. error_free: + rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF]); + rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE]); + free(iset_map); rz_set_u_free(entry_points); rz_buf_free(io_buf); rz_vector_free(insn_to_insn_edges); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index e52d4bce5e9..2b200048351 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -187,33 +187,25 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { if (iset->il_vm) { rz_analysis_il_vm_free(iset->il_vm); } - rz_interpreter_yield_rbuf_free(iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF]); - rz_interpreter_yield_rbuf_free(iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE]); free(iset); } + static bool setup_ipc_objects( - RZ_OWN RzPVector /**/ *sections, - RzInterpreterYieldFilter yield_filter, RZ_OUT RzThreadQueue **il_queue, RZ_OUT RzThreadRingBuf **io_request_rbuf, RZ_OUT RzThreadRingBuf **io_result_rbuf, - RZ_OUT RzThreadRingBuf **branch_rbuf, - RZ_OUT RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM]) { + RZ_OUT RzThreadRingBuf **branch_rbuf) { *il_queue = NULL; *io_request_rbuf = NULL; *io_result_rbuf = NULL; *branch_rbuf = NULL; - yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE] = NULL; - yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF] = NULL; - RzInterpreterYieldRBuf *rbuf = NULL; // The queue to pass the Effects to the interpreter. // This is only one queue for the prototype. // In practice it would be one for each interpreter. *il_queue = rz_th_queue_new(RZ_INTERPRETER_IL_QUEUE_SIZE, NULL); if (!il_queue) { rz_warn_if_reached(); - rz_pvector_free(sections); goto error_free; } @@ -224,7 +216,6 @@ static bool setup_ipc_objects( *io_result_rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_IO_RBUF_SIZE, sizeof(RzInterpreterIOResult)); if (!*io_request_rbuf || !*io_result_rbuf) { rz_warn_if_reached(); - rz_pvector_free(sections); goto error_free; } @@ -232,41 +223,12 @@ static bool setup_ipc_objects( *branch_rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_ADDR_RBUF_SIZE, sizeof(RzInterpreterBranch)); if (!*branch_rbuf) { rz_warn_if_reached(); - rz_pvector_free(sections); goto error_free; } - // A single interpreter can produce different yields. - // E.g. if the interpreter has a complex abstract memory model - // for stack, heap and constant values. - // Then it can produce three kind of yields. - // These yield queues can be shared between different interpreters. - // So we have one yield queue for each yield type. - - RzInterpreterYieldKind yield_kind = RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE; - rbuf = rz_interpreter_yield_rbuf_new(yield_kind, NULL, NULL); - if (!rbuf) { - rz_warn_if_reached(); - rz_pvector_free(sections); - goto error_free; - } - yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE] = rbuf; - - yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; - rbuf = rz_interpreter_yield_rbuf_new( - yield_kind, - yield_filter, - sections); - if (!rbuf) { - rz_warn_if_reached(); - goto error_free; - } - yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF] = rbuf; return true; error_free: - rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF]); - rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE]); rz_th_queue_free(*il_queue); rz_th_ring_buf_free(*io_request_rbuf); rz_th_ring_buf_free(*io_result_rbuf); @@ -282,8 +244,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RzAnalysis *analysis, RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, RzInterpreterAbstraction abstraction, - RZ_OWN RzPVector /**/ *sections, - RzInterpreterYieldFilter yield_filter, + RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM], RZ_NONNULL const RzVector /**/ *ignored_code) { rz_return_val_if_fail(plugin && ignored_code && analysis, NULL); @@ -294,7 +255,6 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RzInterpreterSet *iset = RZ_NEW0(RzInterpreterSet); if (!iset) { - rz_pvector_free(sections); return NULL; } @@ -328,8 +288,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RzThreadRingBuf *io_result_rbuf = NULL; RzThreadRingBuf *branch_rbuf = NULL; RzThreadQueue *il_queue = NULL; - RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM]; - if (!setup_ipc_objects(sections, yield_filter, &il_queue, &io_request_rbuf, &io_result_rbuf, &branch_rbuf, yield_rbufs)) { + if (!setup_ipc_objects(&il_queue, &io_request_rbuf, &io_result_rbuf, &branch_rbuf)) { free(iset); rz_analysis_il_vm_free(il_vm); return NULL; From 6c5c02607679f0c21d983ed70b066ac370ed5e4c Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 7 Apr 2026 23:43:26 +0200 Subject: [PATCH 252/334] Remove one hash table lookup. --- .../interpreter/p/interpreter_prototype.c | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index db35ba4e4c3..bf51bb52cc2 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -20,17 +20,19 @@ static bool eval(RZ_NONNULL RzInterpreterSet *iset, // Check invocation count of the current address. // Never execute the same address more than MAX_INVOCATIONS_PER_BB times. bool found = false; - ut64 ic_pc = ht_uu_find(pdata->bb_invocation_count, il_bb->bb_addr, &found); - if (!found) { - ic_pc = 0; - } else if (ic_pc > MAX_INVOCATIONS_PER_BB) { - // TODO: Make it configurable - RZ_LOG_DEBUG("Reached maximum number of invocations of basic block at 0x%" PFMT64x ". Skipping it.\n", il_bb->bb_addr) - set_pc(iset->astate, il_bb->bb_addr + il_bb->size, plugin_data); - return true; + HtUUKv *ic_pc = ht_uu_find_kv(pdata->bb_invocation_count, il_bb->bb_addr, &found); + if (found) { + ic_pc->value++; + RZ_LOG_DEBUG("Eval BB (ic: %" PFMT64d ") = 0x%" PFMT64x "\n", ic_pc->value, il_bb->bb_addr); + if (ic_pc->value > MAX_INVOCATIONS_PER_BB) { + // TODO: Make it configurable + RZ_LOG_DEBUG("Reached maximum number of invocations of basic block at 0x%" PFMT64x ". Skipping it.\n", il_bb->bb_addr) + set_pc(iset->astate, il_bb->bb_addr + il_bb->size, plugin_data); + return true; + } + } else { + ht_uu_update(pdata->bb_invocation_count, il_bb->bb_addr, 1); } - ht_uu_update(pdata->bb_invocation_count, il_bb->bb_addr, ic_pc + 1); - RZ_LOG_DEBUG("Eval BB (ic: %" PFMT64d ") = 0x%" PFMT64x "\n", ic_pc, il_bb->bb_addr); // Reset call candidate tracking for each basic block. memset(&pdata->call_cand, 0, sizeof(pdata->call_cand)); From 0b0b43da83a3c46c6c193e940ed07cf10901dd9a Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 13 Apr 2026 11:35:53 +0200 Subject: [PATCH 253/334] Refactor to new graph. --- librz/include/rz_inquiry.h | 37 +-- librz/include/rz_inquiry/rz_bb_graph.h | 64 +++++ .../inquiry/algorithms/revng_fcn_detection.c | 22 +- librz/inquiry/bb_cfg.c | 245 ++++++------------ librz/inquiry/inquiry.c | 102 ++++++-- 5 files changed, 234 insertions(+), 236 deletions(-) create mode 100644 librz/include/rz_inquiry/rz_bb_graph.h diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 7210e3114eb..432847e9b8d 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -14,6 +14,7 @@ extern "C" { #endif #include +#include /** * \brief The number of iterations inquiry checks for a user given signal. @@ -25,32 +26,6 @@ typedef struct rz_inquiry_plugin_t { RzInterpreterPlugin *p_interpreter; } RzInquiryPlugin; -/** - * \brief A control flow graph with basic blocks as nodes. - */ -typedef struct { - /** - * \brief Indexed by start address of basic block. - */ - HtUP /**/ *basic_blocks; - - /** - * \brief Maps a basic block address to its node index in the RzGraph instance. - */ - HtUU *bb_gidx_map; - - /** - * \brief Maps a basic block address to the RzgraphNode pointer of the RzGraph instance. - */ - HtUP /**/ *bb_gnode_map; - - /** - * \brief The CFG discovered during interpretation. - * The node data are the addresses of basic blocks, cast to (void *). - */ - RzGraph *graph; -} RzInquiryBBCFG; - typedef struct { /** * \brief RzInquiry interpreter plugins. Indexed by name. @@ -63,17 +38,7 @@ typedef struct { RzInquiryBBCFG *bb_cfg; ///< The control flow graph all the basic blocks build. } RzInquiry; -RZ_IPI RZ_OWN RzInquiryBBCFG *rz_inquiry_bb_cfg_new(); -RZ_IPI void rz_inquiry_bb_cfg_free(RZ_NULLABLE RZ_OWN RzInquiryBBCFG *bb_cfg); -RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size); -RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RzInterval *bb); -RZ_IPI bool rz_inquiry_bb_cfg_del_out_edges(RzInquiryBBCFG *cfg, ut64 bb_addr); -RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb); -RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_from(const RzInquiryBBCFG *cfg, ut64 bb_addr); -RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_to(const RzInquiryBBCFG *cfg, ut64 bb_addr); - RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /**/ *insn_to_insn_edges); -RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg); RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref); diff --git a/librz/include/rz_inquiry/rz_bb_graph.h b/librz/include/rz_inquiry/rz_bb_graph.h new file mode 100644 index 00000000000..8174d2b18a8 --- /dev/null +++ b/librz/include/rz_inquiry/rz_bb_graph.h @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: 2026 Rot127 +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef RZ_INQUIRY_BB_GRAPH_H +#define RZ_INQUIRY_BB_GRAPH_H + +#include +#include + +typedef struct { + ut64 addr; + ut64 size; +} RzInquiryBB; + +/** + * \brief The different kind of BB CFG edges. + */ +typedef enum { + RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE = 0, + /** + * \brief An control flow change via a RzIL JMP. The "from" BB ends with a jump. + */ + RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP, + /** + * \brief A control flow edge between two blocks where the "from" block does not end with a JUMP. + * It is between two blocks, if they were split from a single one. + */ + RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF, + /** + * \brief This edge does not depict a control flow edge. + * It is an opaque edge between a call instruction and the (possible) return point of the procedure called. + * Note that the return point is often, but not always, the instruction at the following memory address after the call. + * There are multiple exceptions to it though, like tail calls, non-returning procedures, or delayed calls. + */ + RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET, +} RzInquiryBBCFGEdgeType; + +/** + * \brief A control flow graph with basic blocks as nodes. + * Edges can be of different jump, and call/return type. + */ +typedef struct { + /** + * \brief The CFG discovered during interpretation. + * The node data are the addresses of basic blocks, cast to (void *). + */ + RzGraph /**/ *graph; +} RzInquiryBBCFG; + +RZ_IPI RZ_OWN RzInquiryBB *rz_inquiry_bb_new(ut64 addr, ut64 size); +RZ_IPI void rz_inquiry_bb_free(RZ_NULLABLE RZ_OWN RzInquiryBB *bb); + +RZ_IPI RZ_OWN RzInquiryBBCFG *rz_inquiry_bb_cfg_new(); +RZ_IPI void rz_inquiry_bb_cfg_free(RZ_NULLABLE RZ_OWN RzInquiryBBCFG *bb_cfg); +RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size); +RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RzInquiryBB *bb); +RZ_IPI bool rz_inquiry_bb_cfg_del_out_edges(RzInquiryBBCFG *cfg, ut64 bb_addr); +RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBBCFGEdgeType type); +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_neighbours_from(const RzInquiryBBCFG *cfg, ut64 bb_addr); +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_neighbours_to(const RzInquiryBBCFG *cfg, ut64 bb_addr); + +RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg); + +#endif // RZ_INQUIRY_BB_GRAPH_H diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index ed09ca7b322..3650fabc41b 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -40,10 +40,11 @@ */ #include "rz_analysis.h" -#include "rz_list.h" +#include "rz_inquiry/rz_bb_graph.h" #include "rz_types_base.h" #include "rz_util/ht_up.h" #include "rz_util/rz_assert.h" +#include "rz_util/rz_graph.h" #include "rz_util/rz_iterator.h" #include "rz_util/rz_set.h" #include "rz_vector.h" @@ -89,7 +90,7 @@ static void recurse_into_fcn_bbs( // // Add edge // - RzInterval this_bb = { 0 }; + RzInquiryBB this_bb = { 0 }; if (!rz_inquiry_bb_cfg_get_basic_block(binary_bb_cfg, this_bb_addr, &this_bb)) { rz_warn_if_reached(); goto err_return; @@ -100,7 +101,7 @@ static void recurse_into_fcn_bbs( } if (predecessor_bb_addr != UT64_MAX) { - RzInterval from_bb = { 0 }; + RzInquiryBB from_bb = { 0 }; if (!rz_inquiry_bb_cfg_get_basic_block(binary_bb_cfg, predecessor_bb_addr, &from_bb)) { rz_warn_if_reached(); goto err_return; @@ -109,7 +110,7 @@ static void recurse_into_fcn_bbs( rz_warn_if_reached(); goto err_return; } - if (!rz_inquiry_bb_cfg_add_edge(fcn->bb_cfg, predecessor_bb_addr, this_bb_addr)) { + if (!rz_inquiry_bb_cfg_add_edge(fcn->bb_cfg, predecessor_bb_addr, this_bb_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP)) { rz_warn_if_reached(); goto err_return; } @@ -118,16 +119,15 @@ static void recurse_into_fcn_bbs( // // Visit neighbors // - const RzList *successors = rz_inquiry_bb_cfg_get_neighbours_from(binary_bb_cfg, this_bb_addr); + RzIterator *successors = rz_inquiry_bb_cfg_get_neighbours_from(binary_bb_cfg, this_bb_addr); if (!successors) { rz_warn_if_reached(); goto err_return; } - RzListIter *lit; const RzGraphNode *s; - rz_list_foreach (successors, lit, s) { - ut64 succ_addr = (ut64)s->data; + rz_iterator_foreach (successors, s) { + ut64 succ_addr = rz_graph_node_get_id(s); if (rz_set_u_contains((RzSetU *)return_addresses, succ_addr)) { // Ignore tail called functions and return points // because they belong to a different function. @@ -159,6 +159,7 @@ static void recurse_into_fcn_bbs( binary_bb_cfg, ignored_code); } + rz_iterator_free(successors); return; err_return: @@ -175,12 +176,13 @@ static void fill_cfep_and_ret_addresses( rz_iterator_foreach(iter, it) { RzAnalysisCallCandidate *cc = *it; ut64 ret_addr = cc->npc; - const RzList *predecessor = rz_inquiry_bb_cfg_get_neighbours_to(binary_bb_cfg, ret_addr); - if (predecessor && rz_list_length(predecessor) > 0) { + RzIterator *predecessor = rz_inquiry_bb_cfg_get_neighbours_to(binary_bb_cfg, ret_addr); + if (rz_iterator_next(predecessor)) { rz_set_u_add(return_addresses, ret_addr); } RZ_LOG_DEBUG("Add cfep at 0x%llx based on BB 0x%llx\n", cc->candidate_addr, cc->target); rz_vector_push(cfep_addresses, &cc->target); + rz_iterator_free(predecessor); } rz_iterator_free(iter); rz_vector_sort(cfep_addresses, cmp, false, NULL); diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index 8b9a15df3e5..26341deea7d 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -1,28 +1,24 @@ // SPDX-FileCopyrightText: 2026 RizinOrg // SPDX-License-Identifier: LGPL-3.0-only -#include "rz_list.h" -#include "rz_util/ht_up.h" -#include "rz_util/ht_uu.h" +#include "rz_types_base.h" #include "rz_util/rz_assert.h" #include "rz_util/rz_graph.h" -#include "rz_util/rz_itv.h" -#include "rz_util/rz_log.h" -#include +#include "rz_util/rz_iterator.h" +#include + +static ut64 hash_node(const void *data) { + const RzInquiryBB *bb = data; + return bb->addr; +} RZ_IPI RZ_OWN RzInquiryBBCFG *rz_inquiry_bb_cfg_new() { RzInquiryBBCFG *bb_cfg = RZ_NEW0(RzInquiryBBCFG); if (!bb_cfg) { return NULL; } - bb_cfg->basic_blocks = ht_up_new(NULL, free); - bb_cfg->bb_gnode_map = ht_up_new(NULL, NULL); - bb_cfg->bb_gidx_map = ht_uu_new(); - bb_cfg->graph = rz_graph_new(); - if (!bb_cfg->basic_blocks || - !bb_cfg->bb_gnode_map || - !bb_cfg->bb_gidx_map || - !bb_cfg->graph) { + bb_cfg->graph = rz_graph_new(RZ_GRAPH_IMPL_MATRIX, hash_node, free, NULL); + if (!bb_cfg->graph) { rz_inquiry_bb_cfg_free(bb_cfg); return NULL; } @@ -33,66 +29,22 @@ RZ_IPI void rz_inquiry_bb_cfg_free(RZ_NULLABLE RZ_OWN RzInquiryBBCFG *bb_cfg) { if (!bb_cfg) { return; } - ht_uu_free(bb_cfg->bb_gidx_map); - ht_up_free(bb_cfg->basic_blocks); - ht_up_free(bb_cfg->bb_gnode_map); rz_graph_free(bb_cfg->graph); free(bb_cfg); } -RZ_IPI bool rz_inquiry_bb_cfg_add_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size) { - RzInterval *bb = ht_up_find(cfg->basic_blocks, addr, NULL); - if (bb && bb->size == size) { - return true; - } else if (bb && bb->size != size) { - RZ_LOG_ERROR("inquiry: Attempt to overwrite basic block size. Ignoring new version: " - "previous: (0x%" PFMT64x ", %" PFMT64d ") - new: (0x%" PFMT64x ", %" PFMT64d ")\n", - bb->addr, bb->size, addr, size); - return false; - } - bb = rz_itv_new(addr, size); - ht_up_insert(cfg->basic_blocks, addr, bb); - - return true; -} - -static /*const*/ RZ_BORROW RzGraphNode *get_node(RzInquiryBBCFG *cfg, ut64 bb_addr) { - return ht_up_find(cfg->bb_gnode_map, bb_addr, NULL); -} - -/** - * \brief Adds new node or returns existing one. - */ -static /*const*/ RZ_BORROW RzGraphNode *get_add_node_to_cfg(RzInquiryBBCFG *cfg, ut64 bb_addr) { - RzGraphNode *n = get_node(cfg, bb_addr); - if (n) { - return n; - } - n = rz_graph_add_node(cfg->graph, (void *)bb_addr); - if (!n) { - return NULL; - } - ht_uu_insert(cfg->bb_gidx_map, bb_addr, n->idx); - ht_up_insert(cfg->bb_gnode_map, bb_addr, n); - return n; +static bool edge_from(const RzGraphEdge *e, void *addr) { + ut64 from_addr = (utptr) addr; + return rz_graph_node_get_id(rz_graph_edge_get_from(e)) == from_addr; } RZ_IPI bool rz_inquiry_bb_cfg_del_out_edges(RzInquiryBBCFG *cfg, ut64 bb_addr) { - RzGraphNode *f = get_node(cfg, bb_addr); - rz_return_val_if_fail(f, false); - const RzList /**/ *neighs = rz_inquiry_bb_cfg_get_neighbours_from(cfg, bb_addr); - RzGraphNode *t; - RzListIter *it; - RzList *ptr_clones = rz_list_clone(neighs); - rz_list_foreach (ptr_clones, it, t) { - rz_graph_del_edge(cfg->graph, f, t); - } - rz_list_free(ptr_clones); - return true; + return rz_graph_del_edges(cfg->graph, edge_from, RZ_GRAPH_INT_AS_DATA(bb_addr)); } /** * \brief Adds an edge to the basic block CFG. + * If one of the blocks doesn't exist, it creates one with size 1. * * \param cfg The basic block CFG to edit. * \param from_bb The address of the basic block with the branch. @@ -101,129 +53,84 @@ RZ_IPI bool rz_inquiry_bb_cfg_del_out_edges(RzInquiryBBCFG *cfg, ut64 bb_addr) { * * \return False if an error occurred. True otherwise. */ -RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb) { - RzGraphNode *f = get_add_node_to_cfg(cfg, from_bb); - RzGraphNode *t = get_add_node_to_cfg(cfg, to_bb); - if (!f || !t) { +RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBBCFGEdgeType type) { + bool existed; + RzInquiryBB *f_bb = RZ_NEW(RzInquiryBB); + RzInquiryBB *t_bb = RZ_NEW(RzInquiryBB); + if (!f_bb || !t_bb) { + free(f_bb); + free(t_bb); + return false; + } + f_bb->addr = from_bb; + f_bb->size = 1; + t_bb->addr = to_bb; + t_bb->size = 1; + + if (!rz_graph_add_get_node(cfg->graph, f_bb, &existed)) { + free(f_bb); + free(t_bb); rz_warn_if_reached(); return false; } - const RzList /**/ *neighs = rz_inquiry_bb_cfg_get_neighbours_from(cfg, from_bb); - if (rz_list_contains(neighs, t)) { - // Edge already added. - return true; + if (existed) { + free(f_bb); } - rz_graph_add_edge(cfg->graph, f, t); - return true; + if (!rz_graph_add_get_node(cfg->graph, t_bb, &existed)) { + free(t_bb); + rz_warn_if_reached(); + return false; + } + if (existed) { + free(t_bb); + } + return rz_graph_add_edge_by_id(cfg->graph, from_bb, to_bb, RZ_GRAPH_INT_AS_DATA(type)); } -RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RzInterval *bb) { +RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RzInquiryBB *bb) { rz_return_val_if_fail(cfg && bb, false); - const RzInterval *itv = ht_up_find(cfg->basic_blocks, bb_addr, NULL); - if (!itv) { + const RzGraphNode *n = rz_graph_find_node(cfg->graph, bb_addr); + if (!n) { RZ_LOG_WARN("Could not find BB at 0x%" PFMT64x "\n", bb_addr); return false; } - bb->addr = itv->addr; - bb->size = itv->size; + const RzInquiryBB *n_data = rz_graph_node_get_data(n); + bb->addr = n_data->addr; + bb->size = n_data->size; return true; } -RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_from(const RzInquiryBBCFG *cfg, ut64 bb_addr) { +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_neighbours_from(const RzInquiryBBCFG *cfg, ut64 bb_addr) { rz_return_val_if_fail(cfg, NULL); - - const RzGraphNode *n = ht_up_find(cfg->bb_gnode_map, bb_addr, NULL); - if (!n) { - rz_warn_if_reached(); - return NULL; - } - return rz_graph_get_neighbours(cfg->graph, n); + return rz_graph_out_neighbors_by_id(cfg->graph, bb_addr); } -RZ_API const RzList /**/ *rz_inquiry_bb_cfg_get_neighbours_to(const RzInquiryBBCFG *cfg, ut64 bb_addr) { +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_neighbours_to(const RzInquiryBBCFG *cfg, ut64 bb_addr) { rz_return_val_if_fail(cfg, NULL); - - const RzGraphNode *n = ht_up_find(cfg->bb_gnode_map, bb_addr, NULL); - if (!n) { - rz_warn_if_reached(); - return NULL; - } - return rz_graph_innodes(cfg->graph, n); + return rz_graph_in_neighbors_by_id(cfg->graph, bb_addr); } /** - * \brief Add edges from \p insn_to_insn_edges to the cfg. - * These are edges statically known by checking RzAnalysisOp->jump and fail. - * - * TODO: Crazy inefficient. - * But for now it is left in here. The problem is that the graph has basic blocks as nodes. - * But the xrefs are instruction to instruction. So we have this super expansive |bb| * |E| lookup. - * - * It would be way faster if we have an R-Tree to get bbs by an address it covers. - * Or just do a better design all along. + * \brief Does not update the BB if it is already present. + * Returns false if it already exists. */ -RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /**/ *insn_to_insn_edges) { - // Add the instruction to instruction edges. - RzAnalysisXRef *i2i_edge; - rz_vector_foreach (insn_to_insn_edges, i2i_edge) { - - // First check if the edge is already in the CFG. - // If so (not unlikely), skip the step where it iterates over all BBs. - const RzList *incoming = rz_inquiry_bb_cfg_get_neighbours_to(iq->bb_cfg, i2i_edge->to); - if (!incoming) { - // Basic block not present. - continue; - } - if (rz_list_length(incoming) == 0) { - // No incoming edges in the CFG yet. - // Find the basic block this edge originates from. - goto find_bb; - } - - RzGraphNode *gn; - RzListIter *lit; - rz_list_foreach (incoming, lit, gn) { - RzInterval *bb = ht_up_find(iq->bb_cfg->basic_blocks, (ut64)gn->data, NULL); - if (!bb) { - continue; - } - if (rz_itv_contain(*bb, i2i_edge->from)) { - // This edge was already covered. - goto next_i2i_edge; - } - } - - find_bb: { - // Edge isn't in the CFG yet. - // Now we have to do the crazy expansive |bb| * |E| search. - void **it; - RzIterator *bb_iter = ht_up_as_iter(iq->bb_cfg->basic_blocks); - rz_iterator_foreach(bb_iter, it) { - RzInterval *bb = *it; - if (!rz_itv_contain(*bb, i2i_edge->from)) { - continue; - } - rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, i2i_edge->to); - } - rz_iterator_free(bb_iter); - } - next_i2i_edge: - continue; - } - return true; -} - RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size) { - if (ht_up_find(cfg->basic_blocks, addr, NULL)) { - return true; + RzInquiryBB *bb = RZ_NEW(RzInquiryBB); + if (!bb) { + return false; } - - if (!get_add_node_to_cfg(cfg, addr)) { - rz_warn_if_reached(); + bb->addr = addr; + bb->size = size; + bool existed; + RzGraphNode *n = rz_graph_add_get_node(cfg->graph, bb, &existed); + if (!n) { return false; } - RzInterval *bb = rz_itv_new(addr, size); - if (!bb || !ht_up_insert(cfg->basic_blocks, addr, bb)) { + if (existed) { + free(bb); + } + const RzInquiryBB *nbb = rz_graph_node_get_data(n); + if (!nbb || nbb->addr != addr) { rz_warn_if_reached(); return false; } @@ -248,20 +155,22 @@ RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg) { // Index is end address of bb, values are starting address of bbs with that end address. HtUP *overlapping_bbs = ht_up_new(NULL, (HtUPFreeValue)rz_vector_free); - RzIterator *iter = ht_up_as_iter(cfg->basic_blocks); - void **it; - rz_iterator_foreach(iter, it) { - RzInterval *bb = *it; + RzIterator *iter = rz_graph_get_nodes(cfg->graph); + RzGraphNode *n; + rz_iterator_foreach(iter, n) { + const RzInquiryBB *bb = rz_graph_node_get_data(n); ut64 end = bb->addr + bb->size; RzVector *start_addresses = ht_up_find(overlapping_bbs, end, NULL); if (!start_addresses) { start_addresses = rz_vector_new(sizeof(ut64), NULL, NULL); ht_up_insert(overlapping_bbs, end, start_addresses); } - rz_vector_push(start_addresses, &bb->addr); + // Cast due to not constified vector API. + rz_vector_push(start_addresses, (void *)&bb->addr); } rz_iterator_free(iter); + void **it; iter = ht_up_as_iter(overlapping_bbs); rz_iterator_foreach(iter, it) { RzVector *addrs = *it; @@ -276,13 +185,13 @@ RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg) { rz_goto_if_fail(small_bb_addr > big_bb_addr, fail); // Change size of big bb - RzInterval *big_bb = ht_up_find(cfg->basic_blocks, big_bb_addr, NULL); + RzInquiryBB *big_bb = rz_graph_node_get_data_mut(rz_graph_find_node(cfg->graph, big_bb_addr)); rz_goto_if_fail(big_bb, fail); big_bb->size = small_bb_addr - big_bb_addr; // add edge between big to small bb, remove old edges. rz_inquiry_bb_cfg_del_out_edges(cfg, big_bb_addr); - if (!rz_inquiry_bb_cfg_add_edge(cfg, big_bb_addr, small_bb_addr)) { + if (!rz_inquiry_bb_cfg_add_edge(cfg, big_bb_addr, small_bb_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF)) { goto fail; } } diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index dff3ab27578..8d501ff6059 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -10,6 +10,7 @@ #include "rz_cons.h" #include "rz_il/definitions/mem.h" #include "rz_il/rz_il_vm.h" +#include "rz_inquiry/rz_bb_graph.h" #include "rz_inquiry/rz_interpreter.h" #include "rz_inquiry_plugins.h" #include "rz_th.h" @@ -19,6 +20,7 @@ #include "rz_util/ht_up.h" #include "rz_util/rz_bitvector.h" #include "rz_util/rz_buf.h" +#include "rz_util/rz_graph.h" #include "rz_util/rz_iterator.h" #include "rz_util/rz_log.h" #include "rz_util/rz_set.h" @@ -111,16 +113,67 @@ RZ_API RZ_OWN char *rz_inquiry_function_str(const RzInquiryFunction *fcn) { rz_strbuf_appendf(buf, "%s0x%" PFMT64x, (i++ > 0 ? ", " : " "), *it); } rz_strbuf_append(buf, " ]\n"); - RzIterator *iter = ht_up_as_iter(fcn->bb_cfg->basic_blocks); - RzInterval **itv; - rz_iterator_foreach(iter, itv) { - RzInterval *bb = *itv; + RzIterator *iter = rz_graph_get_nodes(fcn->bb_cfg->graph); + RzInquiryBB *bb; + rz_iterator_foreach(iter, bb) { rz_strbuf_appendf(buf, "\t0x%" PFMT64x ":0x%" PFMT64x "\n", bb->addr, bb->size); } rz_iterator_free(iter); return rz_strbuf_drain(buf); } +/** + * \brief Add edges from \p insn_to_insn_edges to the cfg. + * These are edges statically known by checking RzAnalysisOp->jump and fail. + * + * TODO: Crazy inefficient. + * But for now it is left in here. The problem is that the graph has basic blocks as nodes. + * But the xrefs are instruction to instruction. So we have this super expansive |bb| * |E| lookup. + * + * It would be way faster if we have an R-Tree to get bbs by an address it covers. + * Or just do a better design all along. + */ +RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /**/ *insn_to_insn_edges) { + // Add the instruction to instruction edges. + RzAnalysisXRef *i2i_edge; + rz_vector_foreach (insn_to_insn_edges, i2i_edge) { + + // First check if the edge is already in the CFG. + // If so (not unlikely), skip the step where it iterates over all BBs. + RzIterator *incoming = rz_inquiry_bb_cfg_get_neighbours_to(iq->bb_cfg, i2i_edge->to); + if (!incoming) { + // Basic block not present. + continue; + } + RzGraphNode *gn; + rz_iterator_foreach(incoming, gn) { + const RzInquiryBB *bb = rz_graph_node_get_data(gn); + if (RZ_BETWEEN_EXCL(bb->addr, i2i_edge->from, bb->addr + bb->size)) { + rz_iterator_free(incoming); + // This edge was already covered. + goto next_i2i_edge; + } + } + rz_iterator_free(incoming); + + // Edge isn't in the CFG yet. + // Now we have to do the crazy expansive |bb| * |E| search. + RzGraphNode *n; + RzIterator *bb_iter = rz_graph_get_nodes(iq->bb_cfg->graph); + rz_iterator_foreach(bb_iter, n) { + const RzInquiryBB *bb = rz_graph_node_get_data(n); + if (RZ_BETWEEN_EXCL(bb->addr, i2i_edge->from, bb->addr + bb->size)) { + continue; + } + rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, i2i_edge->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP); + } + rz_iterator_free(bb_iter); + next_i2i_edge: + continue; + } + return true; +} + RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void) { RzInquiry *iq = RZ_NEW0(RzInquiry); if (!iq) { @@ -163,7 +216,7 @@ RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *iq) { RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref) { rz_vector_push(iq->xrefs, (void *)xref); if (xref->type == RZ_ANALYSIS_XREF_TYPE_CODE) { - rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, xref->bb_addr, xref->to); + rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP); } } @@ -380,7 +433,7 @@ static bool send_next_il_bb(RzCore *core, // This is the basic block for the imported function. rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, branch->target_addr, 1); } - rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr); + rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP); rz_th_queue_push(il_queue, (void *)bb, true); return true; } @@ -775,10 +828,9 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry const RzPVector /**/ *symbols) { // Add all discovered binary blocks to analysis - RzIterator *iter = ht_up_as_iter(inquiry->bb_cfg->basic_blocks); - RzInterval **it_bb; - rz_iterator_foreach(iter, it_bb) { - RzInterval *bb = *it_bb; + RzIterator *iter = rz_graph_get_nodes(inquiry->bb_cfg->graph); + RzInquiryBB *bb; + rz_iterator_foreach(iter, bb) { rz_analysis_add_bb(analysis, bb->addr, bb->size); } rz_iterator_free(iter); @@ -807,25 +859,31 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry continue; } - void **it2; - RzIterator *iter = ht_up_as_iter(fcn->bb_cfg->basic_blocks); - rz_iterator_foreach(iter, it2) { - RzInterval *bb = *it2; + RzIterator *iter = rz_graph_get_nodes(fcn->bb_cfg->graph); + RzInquiryBB *bb; + rz_iterator_foreach(iter, bb) { RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, bb->addr); if (!abb && !(abb = rz_analysis_create_block(analysis, bb->addr, bb->size))) { rz_warn_if_reached(); continue; } - const RzList *successors = rz_inquiry_bb_cfg_get_neighbours_from(inquiry->bb_cfg, bb->addr); + RzIterator *successors = rz_inquiry_bb_cfg_get_neighbours_from(inquiry->bb_cfg, bb->addr); RzGraphNode *n; - if (rz_list_length(successors) > 0) { - n = rz_list_get_n(successors, 0); - abb->jump = (ut64)n->data; - } - if (rz_list_length(successors) > 1) { - n = rz_list_get_n(successors, 1); - abb->fail = (ut64)n->data; + size_t i = 0; + rz_iterator_foreach(successors, n) { + if (i == 0) { + abb->jump = rz_graph_node_get_id(n); + } else if (i == 1) { + abb->fail = rz_graph_node_get_id(n); + } else { + RZ_LOG_WARN("The basic block at 0x%" PFMT64x " has more than two outgoing edges. " + "Rizin can't model this currently :(\n", rz_graph_node_get_id(n)); + break; + } + i++; } + rz_iterator_free(successors); + RzAnalysisCallCandidate *cc; if ((cc = ht_up_find(inquiry->call_candidates, bb->addr, NULL))) { // Calls need an edge between the call instruction and its return address. From 52c65866da4e91bff6850dd9841cc16e9c6171da Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 13 Apr 2026 14:36:34 +0200 Subject: [PATCH 254/334] Remove outdated comments --- test/db/inquiry/interpreter/xrefs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/db/inquiry/interpreter/xrefs b/test/db/inquiry/interpreter/xrefs index d3a0a155ddd..bae0f75e684 100644 --- a/test/db/inquiry/interpreter/xrefs +++ b/test/db/inquiry/interpreter/xrefs @@ -1,11 +1,6 @@ NAME=Indirect call x86 FILE=bins/inquiry/interpreter/prototype/x86_icall_malloc CMDS=< Date: Mon, 13 Apr 2026 20:04:28 +0200 Subject: [PATCH 255/334] Don't add nodes of length 1 if an edge points to it. --- librz/include/rz_inquiry/rz_bb_graph.h | 1 + librz/inquiry/bb_cfg.c | 58 ++++++++++---------------- librz/inquiry/inquiry.c | 14 ++++--- 3 files changed, 31 insertions(+), 42 deletions(-) diff --git a/librz/include/rz_inquiry/rz_bb_graph.h b/librz/include/rz_inquiry/rz_bb_graph.h index 8174d2b18a8..91e3db14981 100644 --- a/librz/include/rz_inquiry/rz_bb_graph.h +++ b/librz/include/rz_inquiry/rz_bb_graph.h @@ -53,6 +53,7 @@ RZ_IPI void rz_inquiry_bb_free(RZ_NULLABLE RZ_OWN RzInquiryBB *bb); RZ_IPI RZ_OWN RzInquiryBBCFG *rz_inquiry_bb_cfg_new(); RZ_IPI void rz_inquiry_bb_cfg_free(RZ_NULLABLE RZ_OWN RzInquiryBBCFG *bb_cfg); RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size); +RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, RzVector /**/ *xrefs); RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RzInquiryBB *bb); RZ_IPI bool rz_inquiry_bb_cfg_del_out_edges(RzInquiryBBCFG *cfg, ut64 bb_addr); RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBBCFGEdgeType type); diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index 26341deea7d..d6d761a7170 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2026 RizinOrg // SPDX-License-Identifier: LGPL-3.0-only +#include "rz_analysis.h" #include "rz_types_base.h" #include "rz_util/rz_assert.h" #include "rz_util/rz_graph.h" @@ -44,46 +45,15 @@ RZ_IPI bool rz_inquiry_bb_cfg_del_out_edges(RzInquiryBBCFG *cfg, ut64 bb_addr) { /** * \brief Adds an edge to the basic block CFG. - * If one of the blocks doesn't exist, it creates one with size 1. * * \param cfg The basic block CFG to edit. * \param from_bb The address of the basic block with the branch. * Not the address of the branch instruction! * \param to_bb The address of the basic block the branch leads to. * - * \return False if an error occurred. True otherwise. + * \return True if edge was added. False for error or if a node doesn't exist. */ RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBBCFGEdgeType type) { - bool existed; - RzInquiryBB *f_bb = RZ_NEW(RzInquiryBB); - RzInquiryBB *t_bb = RZ_NEW(RzInquiryBB); - if (!f_bb || !t_bb) { - free(f_bb); - free(t_bb); - return false; - } - f_bb->addr = from_bb; - f_bb->size = 1; - t_bb->addr = to_bb; - t_bb->size = 1; - - if (!rz_graph_add_get_node(cfg->graph, f_bb, &existed)) { - free(f_bb); - free(t_bb); - rz_warn_if_reached(); - return false; - } - if (existed) { - free(f_bb); - } - if (!rz_graph_add_get_node(cfg->graph, t_bb, &existed)) { - free(t_bb); - rz_warn_if_reached(); - return false; - } - if (existed) { - free(t_bb); - } return rz_graph_add_edge_by_id(cfg->graph, from_bb, to_bb, RZ_GRAPH_INT_AS_DATA(type)); } @@ -126,13 +96,25 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut if (!n) { return false; } + const RzInquiryBB *nbb = rz_graph_node_get_data(n); + if (nbb->size != size) { + rz_warn_if_reached(); + } if (existed) { free(bb); } - const RzInquiryBB *nbb = rz_graph_node_get_data(n); - if (!nbb || nbb->addr != addr) { - rz_warn_if_reached(); - return false; + return true; +} + +RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, RzVector /**/ *xrefs) { + RzAnalysisXRef *xref; + rz_vector_foreach (xrefs, xref) { + if (xref->type != RZ_ANALYSIS_XREF_TYPE_CODE) { + continue; + } + if (!rz_inquiry_bb_cfg_add_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP)) { + RZ_LOG_DEBUG("Did not add edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, xref->to); + } } return true; } @@ -155,8 +137,8 @@ RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg) { // Index is end address of bb, values are starting address of bbs with that end address. HtUP *overlapping_bbs = ht_up_new(NULL, (HtUPFreeValue)rz_vector_free); - RzIterator *iter = rz_graph_get_nodes(cfg->graph); RzGraphNode *n; + RzIterator *iter = rz_graph_get_nodes(cfg->graph); rz_iterator_foreach(iter, n) { const RzInquiryBB *bb = rz_graph_node_get_data(n); ut64 end = bb->addr + bb->size; @@ -178,6 +160,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg) { if (i == 1) { continue; } + // Sort start addresses. rz_vector_sort(addrs, (RzVectorComparator)cmp, false, NULL); for (i = i - 1; i > 0; i--) { ut64 small_bb_addr = *((ut64 *)rz_vector_index_ptr(addrs, i)); @@ -192,6 +175,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg) { // add edge between big to small bb, remove old edges. rz_inquiry_bb_cfg_del_out_edges(cfg, big_bb_addr); if (!rz_inquiry_bb_cfg_add_edge(cfg, big_bb_addr, small_bb_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF)) { + RZ_LOG_DEBUG("Did not add edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", big_bb_addr, small_bb_addr); goto fail; } } diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 8d501ff6059..642c4d67ab9 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -165,7 +165,9 @@ RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /*addr, i2i_edge->from, bb->addr + bb->size)) { continue; } - rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, i2i_edge->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP); + if (!rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, i2i_edge->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP)) { + rz_warn_if_reached(); + } } rz_iterator_free(bb_iter); next_i2i_edge: @@ -215,9 +217,6 @@ RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *iq) { RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref) { rz_vector_push(iq->xrefs, (void *)xref); - if (xref->type == RZ_ANALYSIS_XREF_TYPE_CODE) { - rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP); - } } RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzPVector /**/ *allowed_segments) { @@ -433,7 +432,9 @@ static bool send_next_il_bb(RzCore *core, // This is the basic block for the imported function. rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, branch->target_addr, 1); } - rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP); + if (RZ_LIKELY(branch->branching_bb_addr)) { + rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP); + } rz_th_queue_push(il_queue, (void *)bb, true); return true; } @@ -801,6 +802,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, if (!rz_inquiry_bb_cfg_reduce(core->inquiry->bb_cfg)) { rz_warn_if_reached(); } + if (!rz_inquiry_bb_cfg_add_xrefs(core->inquiry->bb_cfg, core->inquiry->xrefs)) { + rz_warn_if_reached(); + } if (!user_sent_signal) { eprintf("Complement BB CFG with statically known xrefs...\n"); if (!rz_inquiry_bb_cfg_complement(core->inquiry, insn_to_insn_edges)) { From 7b4cb857839bd0350fdeffffbf9466afcc171d11 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 14 Apr 2026 18:01:55 +0200 Subject: [PATCH 256/334] Fix incorrect types --- librz/inquiry/inquiry.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 642c4d67ab9..8e33d314a22 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -114,8 +114,9 @@ RZ_API RZ_OWN char *rz_inquiry_function_str(const RzInquiryFunction *fcn) { } rz_strbuf_append(buf, " ]\n"); RzIterator *iter = rz_graph_get_nodes(fcn->bb_cfg->graph); - RzInquiryBB *bb; - rz_iterator_foreach(iter, bb) { + RzGraphNode *n; + rz_iterator_foreach(iter, n) { + const RzInquiryBB *bb = rz_graph_node_get_data(n); rz_strbuf_appendf(buf, "\t0x%" PFMT64x ":0x%" PFMT64x "\n", bb->addr, bb->size); } rz_iterator_free(iter); @@ -833,8 +834,9 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry // Add all discovered binary blocks to analysis RzIterator *iter = rz_graph_get_nodes(inquiry->bb_cfg->graph); - RzInquiryBB *bb; - rz_iterator_foreach(iter, bb) { + RzGraphNode *n; + rz_iterator_foreach(iter, n) { + const RzInquiryBB *bb = rz_graph_node_get_data(n); rz_analysis_add_bb(analysis, bb->addr, bb->size); } rz_iterator_free(iter); @@ -864,8 +866,9 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry } RzIterator *iter = rz_graph_get_nodes(fcn->bb_cfg->graph); - RzInquiryBB *bb; - rz_iterator_foreach(iter, bb) { + RzGraphNode *n; + rz_iterator_foreach(iter, n) { + const RzInquiryBB *bb = rz_graph_node_get_data(n); RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, bb->addr); if (!abb && !(abb = rz_analysis_create_block(analysis, bb->addr, bb->size))) { rz_warn_if_reached(); From 34303ce91acda56879a4bf6439e1cd6b6cdecf59 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 15 Apr 2026 19:33:39 +0200 Subject: [PATCH 257/334] Rename getter for neighboring nodes for clarity --- librz/include/rz_inquiry/rz_bb_graph.h | 4 ++-- .../inquiry/algorithms/revng_fcn_detection.c | 4 ++-- librz/inquiry/bb_cfg.c | 10 ++++++++-- librz/inquiry/inquiry.c | 19 +++++++++---------- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/librz/include/rz_inquiry/rz_bb_graph.h b/librz/include/rz_inquiry/rz_bb_graph.h index 91e3db14981..9419c9a5899 100644 --- a/librz/include/rz_inquiry/rz_bb_graph.h +++ b/librz/include/rz_inquiry/rz_bb_graph.h @@ -57,8 +57,8 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, RzVector /**/ *rz_inquiry_bb_cfg_get_neighbours_from(const RzInquiryBBCFG *cfg, ut64 bb_addr); -RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_neighbours_to(const RzInquiryBBCFG *cfg, ut64 bb_addr); +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_outgoing_nodes(const RzInquiryBBCFG *cfg, ut64 bb_addr); +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_incoming_nodes(const RzInquiryBBCFG *cfg, ut64 bb_addr); RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg); diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index 3650fabc41b..b15f3b8c515 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -119,7 +119,7 @@ static void recurse_into_fcn_bbs( // // Visit neighbors // - RzIterator *successors = rz_inquiry_bb_cfg_get_neighbours_from(binary_bb_cfg, this_bb_addr); + RzIterator *successors = rz_inquiry_bb_cfg_get_outgoing_nodes(binary_bb_cfg, this_bb_addr); if (!successors) { rz_warn_if_reached(); goto err_return; @@ -176,7 +176,7 @@ static void fill_cfep_and_ret_addresses( rz_iterator_foreach(iter, it) { RzAnalysisCallCandidate *cc = *it; ut64 ret_addr = cc->npc; - RzIterator *predecessor = rz_inquiry_bb_cfg_get_neighbours_to(binary_bb_cfg, ret_addr); + RzIterator *predecessor = rz_inquiry_bb_cfg_get_incoming_nodes(binary_bb_cfg, ret_addr); if (rz_iterator_next(predecessor)) { rz_set_u_add(return_addresses, ret_addr); } diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index d6d761a7170..54ea106adc7 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -70,12 +70,18 @@ RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb return true; } -RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_neighbours_from(const RzInquiryBBCFG *cfg, ut64 bb_addr) { +/** + * \brief Neighbors of outgoing edges. + */ +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_outgoing_nodes(const RzInquiryBBCFG *cfg, ut64 bb_addr) { rz_return_val_if_fail(cfg, NULL); return rz_graph_out_neighbors_by_id(cfg->graph, bb_addr); } -RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_neighbours_to(const RzInquiryBBCFG *cfg, ut64 bb_addr) { +/** + * \brief Neighbors of incoming edges. + */ +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_incoming_nodes(const RzInquiryBBCFG *cfg, ut64 bb_addr) { rz_return_val_if_fail(cfg, NULL); return rz_graph_in_neighbors_by_id(cfg->graph, bb_addr); } diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 8e33d314a22..4ebd3005b61 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -138,10 +138,9 @@ RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /*bb_cfg, i2i_edge->to); + RzIterator *incoming = rz_inquiry_bb_cfg_get_incoming_nodes(iq->bb_cfg, i2i_edge->to); if (!incoming) { // Basic block not present. continue; @@ -653,18 +652,18 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_th_queue_close(iset->il_queue); iset_map[i].next_run_state = RZ_INTP_RUN_STATE_TERM; intpr_terminated++; - RZ_LOG_DEBUG("Next: TERM\n"); + // RZ_LOG_DEBUG("Next: TERM\n"); continue; } if (send_next_il_bb(core, iset->il_queue, il_cache, entry_points, &branch)) { // Successfully lifted and pushed the entry point's basic block into the queue. // Expect the interpreter to emulate now. iset_map[i].next_run_state = RZ_INTP_RUN_STATE_EMU; - RZ_LOG_DEBUG("Next: EMU\n"); + // RZ_LOG_DEBUG("Next: EMU\n"); } else { iset_map[i].next_run_state = RZ_INTP_RUN_STATE_CLEAN; rz_th_queue_close(iset->il_queue); - RZ_LOG_DEBUG("Next: CLEAN\n"); + // RZ_LOG_DEBUG("Next: CLEAN\n"); } break; } @@ -697,7 +696,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // Signal interpreter the lifting failed. rz_th_queue_close(iset->il_queue); iset_map[i].next_run_state = RZ_INTP_RUN_STATE_CLEAN; - RZ_LOG_DEBUG("Next: CLEAN\n"); + // RZ_LOG_DEBUG("Next: CLEAN\n"); } else { RZ_LOG_DEBUG("Pushed: il_bb: 0x%llx\n", branch.target_addr); } @@ -742,7 +741,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, if (!handle_yields(core, iset->yield_rbufs)) { iset_map[i].next_run_state = RZ_INTP_RUN_STATE_TERM; intpr_terminated++; - RZ_LOG_DEBUG("Next: TERM\n"); + // RZ_LOG_DEBUG("Next: TERM\n"); } break; } @@ -754,7 +753,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, open_ipc_obj(iset); rz_th_sem_post(iset->run_state_sync); iset_map[i].next_run_state = RZ_INTP_RUN_STATE_INIT; - RZ_LOG_DEBUG("Next: INIT\n"); + // RZ_LOG_DEBUG("Next: INIT\n"); break; } case RZ_INTP_RUN_STATE_TERM: { @@ -762,7 +761,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, iset_map[i].next_run_state = RZ_INTP_RUN_STATE_TERM; intpr_terminated++; } - RZ_LOG_DEBUG("Next: TERM\n"); + // RZ_LOG_DEBUG("Next: TERM\n"); break; } } @@ -874,7 +873,7 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry rz_warn_if_reached(); continue; } - RzIterator *successors = rz_inquiry_bb_cfg_get_neighbours_from(inquiry->bb_cfg, bb->addr); + RzIterator *successors = rz_inquiry_bb_cfg_get_outgoing_nodes(inquiry->bb_cfg, bb->addr); RzGraphNode *n; size_t i = 0; rz_iterator_foreach(successors, n) { From 0810120963b4e4eccbda63470a3b6a9532a0c5d0 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 15 Apr 2026 19:34:23 +0200 Subject: [PATCH 258/334] Add missing negation from refactor --- librz/inquiry/inquiry.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 4ebd3005b61..b8a023a6be2 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -162,7 +162,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /*bb_cfg->graph); rz_iterator_foreach(bb_iter, n) { const RzInquiryBB *bb = rz_graph_node_get_data(n); - if (RZ_BETWEEN_EXCL(bb->addr, i2i_edge->from, bb->addr + bb->size)) { + if (!RZ_BETWEEN_EXCL(bb->addr, i2i_edge->from, bb->addr + bb->size)) { continue; } if (!rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, i2i_edge->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP)) { From 89521ddf79cc35ce390141bfc1bd49301f298042 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 17 Apr 2026 20:53:50 +0200 Subject: [PATCH 259/334] Use list based graph. --- librz/include/rz_inquiry/rz_bb_graph.h | 2 +- librz/inquiry/bb_cfg.c | 4 ++-- librz/inquiry/inquiry.c | 7 ++++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/librz/include/rz_inquiry/rz_bb_graph.h b/librz/include/rz_inquiry/rz_bb_graph.h index 9419c9a5899..1951c2d6488 100644 --- a/librz/include/rz_inquiry/rz_bb_graph.h +++ b/librz/include/rz_inquiry/rz_bb_graph.h @@ -50,7 +50,7 @@ typedef struct { RZ_IPI RZ_OWN RzInquiryBB *rz_inquiry_bb_new(ut64 addr, ut64 size); RZ_IPI void rz_inquiry_bb_free(RZ_NULLABLE RZ_OWN RzInquiryBB *bb); -RZ_IPI RZ_OWN RzInquiryBBCFG *rz_inquiry_bb_cfg_new(); +RZ_IPI RZ_OWN RzInquiryBBCFG *rz_inquiry_bb_cfg_new(RzGraphImplType impl_type); RZ_IPI void rz_inquiry_bb_cfg_free(RZ_NULLABLE RZ_OWN RzInquiryBBCFG *bb_cfg); RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size); RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, RzVector /**/ *xrefs); diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index 54ea106adc7..cd29f5c3d3a 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -13,12 +13,12 @@ static ut64 hash_node(const void *data) { return bb->addr; } -RZ_IPI RZ_OWN RzInquiryBBCFG *rz_inquiry_bb_cfg_new() { +RZ_IPI RZ_OWN RzInquiryBBCFG *rz_inquiry_bb_cfg_new(RzGraphImplType impl_type) { RzInquiryBBCFG *bb_cfg = RZ_NEW0(RzInquiryBBCFG); if (!bb_cfg) { return NULL; } - bb_cfg->graph = rz_graph_new(RZ_GRAPH_IMPL_MATRIX, hash_node, free, NULL); + bb_cfg->graph = rz_graph_new(impl_type, hash_node, free, NULL); if (!bb_cfg->graph) { rz_inquiry_bb_cfg_free(bb_cfg); return NULL; diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index b8a023a6be2..6e8c23da1c1 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -95,7 +95,7 @@ RZ_IPI RZ_OWN RzInquiryFunction *rz_inquiry_function_new() { if (!fcn) { return NULL; } - fcn->bb_cfg = rz_inquiry_bb_cfg_new(); + fcn->bb_cfg = rz_inquiry_bb_cfg_new(RZ_GRAPH_IMPL_LIST); fcn->entry_points = rz_vector_new(sizeof(ut64), NULL, NULL); if (!fcn->bb_cfg || !fcn->entry_points) { rz_inquiry_function_free(fcn); @@ -185,7 +185,7 @@ RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void) { iq->plugins_data = ht_sp_new(HT_STR_CONST, NULL, NULL); iq->call_candidates = ht_up_new(NULL, free); iq->xrefs = rz_vector_new(sizeof(RzAnalysisXRef), NULL, NULL); - iq->bb_cfg = rz_inquiry_bb_cfg_new(); + iq->bb_cfg = rz_inquiry_bb_cfg_new(RZ_GRAPH_IMPL_LIST); if (!iq->plugins || !iq->plugins_data || !iq->bb_cfg) { ht_sp_free(iq->plugins); ht_sp_free(iq->plugins_data); @@ -883,7 +883,8 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry abb->fail = rz_graph_node_get_id(n); } else { RZ_LOG_WARN("The basic block at 0x%" PFMT64x " has more than two outgoing edges. " - "Rizin can't model this currently :(\n", rz_graph_node_get_id(n)); + "Rizin can't model this currently :(\n", + rz_graph_node_get_id(n)); break; } i++; From 18724f45bff85d999110822248cc9bd3fe252aa9 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 20 Apr 2026 20:44:01 +0200 Subject: [PATCH 260/334] Start refactor to CFGs with edge types. --- librz/arch/xrefs.c | 22 ++- librz/core/cmd/cmd_analysis.c | 3 + librz/include/rz_analysis.h | 3 + librz/include/rz_inquiry.h | 3 +- librz/include/rz_inquiry/rz_bb_graph.h | 9 +- .../inquiry/algorithms/revng_fcn_detection.c | 95 ++++++---- librz/inquiry/bb_cfg.c | 43 +++-- librz/inquiry/inquiry.c | 171 +++++++++++------- .../interpreter/prototype/eval_effect.c | 17 +- 9 files changed, 237 insertions(+), 129 deletions(-) diff --git a/librz/arch/xrefs.c b/librz/arch/xrefs.c index 9fcfc1cbdff..5565e759032 100644 --- a/librz/arch/xrefs.c +++ b/librz/arch/xrefs.c @@ -5,6 +5,8 @@ // SPDX-License-Identifier: LGPL-3.0-only #include "analysis_private.h" +#include "rz_analysis.h" +#include "rz_util/rz_assert.h" #include /* @@ -310,6 +312,10 @@ RZ_API const char *rz_analysis_ref_type_tostring(RzAnalysisXRefType t) { return "string"; case RZ_ANALYSIS_XREF_TYPE_MEM_WRITE: return "write"; + case RZ_ANALYSIS_XREF_TYPE_CALL_RET: + return "call_ret_pt"; + case RZ_ANALYSIS_XREF_TYPE_RETURN: + return "return"; } return "unknown"; } @@ -369,15 +375,18 @@ RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, continue; } // Only add jump targets going to executable regions. - if ((rz_analysis_op_is_direct_call(&op) || - rz_analysis_op_is_direct_jump(&op)) && - op.jump != UT64_MAX) { + bool op_is_call = rz_analysis_op_is_direct_call(&op); + bool op_is_jump = rz_analysis_op_is_direct_jump(&op); + if ((op_is_call || op_is_jump) && op.jump != UT64_MAX) { rz_set_u_add(branch_targets, op.jump); RzAnalysisXRef edge = { .from = addr, .to = op.jump }; + op.type = op_is_call ? RZ_ANALYSIS_XREF_TYPE_CALL : RZ_ANALYSIS_XREF_TYPE_CODE; rz_vector_push(insn_to_insn_edges, &edge); + if (op.fail != UT64_MAX) { - if (rz_analysis_op_is_direct_jump(&op)) { + if (op_is_jump) { + op.type = RZ_ANALYSIS_XREF_TYPE_CODE; rz_set_u_add(branch_targets, op.fail); RzAnalysisXRef edge = { .from = addr, .to = op.fail }; @@ -385,10 +394,13 @@ RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, } } } - if (include_call_return_pts && rz_analysis_op_is_call(&op)) { + if (include_call_return_pts && op_is_call) { // If it is a call, also add the following instruction as reference. // Because it is likely a return point. rz_set_u_add(branch_targets, op.addr + op.size); + op.type = RZ_ANALYSIS_XREF_TYPE_CALL_RET; + RzAnalysisXRef edge = { .from = addr, .to = op.fail }; + rz_vector_push(insn_to_insn_edges, &edge); } addr += op.size; rz_analysis_op_fini(&op); diff --git a/librz/core/cmd/cmd_analysis.c b/librz/core/cmd/cmd_analysis.c index c903ecc7b84..f2cfc548f14 100644 --- a/librz/core/cmd/cmd_analysis.c +++ b/librz/core/cmd/cmd_analysis.c @@ -10,6 +10,7 @@ #include #include "../core_private.h" +#include "rz_analysis.h" #define MAX_SCAN_SIZE 0x7ffffff @@ -2126,9 +2127,11 @@ RZ_IPI RzCmdStatus rz_analysis_function_xrefs_handler(RzCore *core, int argc, co rz_cons_printf("0x%08" PFMT64x " ", xref->to); break; case RZ_ANALYSIS_XREF_TYPE_MEM_WRITE: + case RZ_ANALYSIS_XREF_TYPE_CALL_RET: case RZ_ANALYSIS_XREF_TYPE_CODE: case RZ_ANALYSIS_XREF_TYPE_CALL: case RZ_ANALYSIS_XREF_TYPE_DATA: + case RZ_ANALYSIS_XREF_TYPE_RETURN: rz_cons_printf("0x%08" PFMT64x " ", xref->to); rz_core_seek(core, xref->from, 1); rz_core_print_disasm_instructions(core, 0, 1); diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 8e3783896d2..c871f68f290 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -969,6 +969,9 @@ typedef enum { */ RZ_ANALYSIS_XREF_TYPE_MEM_READ = RZ_ANALYSIS_XREF_TYPE_DATA, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE = 'w', ///< Memory write reference + + RZ_ANALYSIS_XREF_TYPE_CALL_RET = 'r', // Call return point (next pc after a call). + RZ_ANALYSIS_XREF_TYPE_RETURN = 'R', // A returning jump } RzAnalysisXRefType; typedef struct rz_analysis_ref_t { diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 432847e9b8d..7d97a965d57 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -34,7 +34,7 @@ typedef struct { HtSP /**/ *plugins_data; HtUP /**/ *call_candidates; ///< Indexed by address of basic block with the call candidate. - RzVector /**/ *xrefs; ///< All xrefs it detected. + RzVector /**/ *xrefs; ///< All xrefs the interpreter detected. RzInquiryBBCFG *bb_cfg; ///< The control flow graph all the basic blocks build. } RzInquiry; @@ -69,6 +69,7 @@ RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, */ typedef struct { RzVector /**/ *entry_points; + RzVector /**/ *call_candidates; RzInquiryBBCFG *bb_cfg; } RzInquiryFunction; diff --git a/librz/include/rz_inquiry/rz_bb_graph.h b/librz/include/rz_inquiry/rz_bb_graph.h index 1951c2d6488..adfb4110997 100644 --- a/librz/include/rz_inquiry/rz_bb_graph.h +++ b/librz/include/rz_inquiry/rz_bb_graph.h @@ -21,6 +21,11 @@ typedef enum { * \brief An control flow change via a RzIL JMP. The "from" BB ends with a jump. */ RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP, + /** + * \brief An control flow change via a RzIL JMP but by a call candidate. + * The "from" BB ends with a jump, very likely to a procedure. + */ + RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL, /** * \brief A control flow edge between two blocks where the "from" block does not end with a JUMP. * It is between two blocks, if they were split from a single one. @@ -57,8 +62,8 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, RzVector /**/ *rz_inquiry_bb_cfg_get_outgoing_nodes(const RzInquiryBBCFG *cfg, ut64 bb_addr); -RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_incoming_nodes(const RzInquiryBBCFG *cfg, ut64 bb_addr); +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_outgoing_edges(const RzInquiryBBCFG *cfg, ut64 bb_addr); +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_incoming_edges(const RzInquiryBBCFG *cfg, ut64 bb_addr); RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg); diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index b15f3b8c515..db24453d72a 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -76,8 +76,8 @@ static void recurse_into_fcn_bbs( RzInquiryFunction *fcn, ut64 predecessor_bb_addr, ut64 this_bb_addr, + RzInquiryBBCFGEdgeType edge_type, RzSetU *visited_fcn_bbs, - const RzSetU *return_addresses, const RzVector /**/ *cfep_addresses, const HtUP /**/ *call_candidates, const RzInquiryBBCFG *binary_bb_cfg, @@ -100,7 +100,7 @@ static void recurse_into_fcn_bbs( goto err_return; } - if (predecessor_bb_addr != UT64_MAX) { + if (edge_type != RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE) { RzInquiryBB from_bb = { 0 }; if (!rz_inquiry_bb_cfg_get_basic_block(binary_bb_cfg, predecessor_bb_addr, &from_bb)) { rz_warn_if_reached(); @@ -110,7 +110,7 @@ static void recurse_into_fcn_bbs( rz_warn_if_reached(); goto err_return; } - if (!rz_inquiry_bb_cfg_add_edge(fcn->bb_cfg, predecessor_bb_addr, this_bb_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP)) { + if (!rz_inquiry_bb_cfg_add_edge(fcn->bb_cfg, predecessor_bb_addr, this_bb_addr, edge_type)) { rz_warn_if_reached(); goto err_return; } @@ -119,30 +119,49 @@ static void recurse_into_fcn_bbs( // // Visit neighbors // - RzIterator *successors = rz_inquiry_bb_cfg_get_outgoing_nodes(binary_bb_cfg, this_bb_addr); + RzIterator *successors = rz_inquiry_bb_cfg_get_outgoing_edges(binary_bb_cfg, this_bb_addr); if (!successors) { rz_warn_if_reached(); goto err_return; } - const RzGraphNode *s; - rz_iterator_foreach (successors, s) { - ut64 succ_addr = rz_graph_node_get_id(s); - if (rz_set_u_contains((RzSetU *)return_addresses, succ_addr)) { - // Ignore tail called functions and return points - // because they belong to a different function. - continue; - } - if (rz_vector_contains(cfep_addresses, &succ_addr)) { - // The successor is another function. - // If address after the branch is a return point we choose it as - // successor. - // If it isn't a return point, then the call at this basic block is likely a tail call. - // But tail calls are ignored. So in either case we just choose the npc as successor. - const RzAnalysisCallCandidate *cc = ht_up_find((HtUP *)call_candidates, this_bb_addr, NULL); + const RzGraphEdge *e; + rz_iterator_foreach(successors, e) { + RzInquiryBBCFGEdgeType etype = (RzInquiryBBCFGEdgeType)(utptr)rz_graph_edge_get_data(e); + ut64 succ_addr = rz_graph_node_get_id(rz_graph_edge_get_to(e)); + switch (etype) { + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE: + rz_warn_if_reached(); + goto err_return; + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF: + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP: + // Just an edge between two basic blocks. + break; + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET: + // This is the next instruction packet after a call candidate. + // It might be an non-existing edge which was only added as a guess. + // In general all instructions after a call are added to the with + // an edge, to increase coverage. + // Even for call which never return (being a tail call or exit). + // + // The prototype doesn't handle tail calls. But to not loose too much + // precission we can check here if the NPC after the call (this_bb address), + // is a candidate function entry point. + // If so, we can assume that the NPC is in fact not a return point of a procedure. + // Hence, it shouldn't be added to the function CFG. + if (rz_vector_contains(cfep_addresses, &succ_addr)) { + continue; + } + break; + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL: { + RzAnalysisCallCandidate *cc = ht_up_find((HtUP *)call_candidates, this_bb_addr, NULL); + // Don't recurse edges to other procedures. + // Instead we add this address to the functions call targets. if (cc) { - succ_addr = cc->npc; + rz_vector_push(fcn->call_candidates, cc); } + continue; + } } if (jumps_to_ignored_code(ignored_code, succ_addr)) { continue; @@ -152,36 +171,46 @@ static void recurse_into_fcn_bbs( fcn, this_bb_addr, succ_addr, + etype, visited_fcn_bbs, - return_addresses, cfep_addresses, call_candidates, binary_bb_cfg, ignored_code); } rz_iterator_free(successors); - return; err_return: return; } -static void fill_cfep_and_ret_addresses( +static void fill_candidate_fcn_entry_points( const RzInquiryBBCFG *binary_bb_cfg, const HtUP /**/ *call_candidates, - RzSetU *return_addresses, RzVector *cfep_addresses) { void **it; RzIterator *iter = ht_up_as_iter(call_candidates); rz_iterator_foreach(iter, it) { RzAnalysisCallCandidate *cc = *it; - ut64 ret_addr = cc->npc; - RzIterator *predecessor = rz_inquiry_bb_cfg_get_incoming_nodes(binary_bb_cfg, ret_addr); - if (rz_iterator_next(predecessor)) { - rz_set_u_add(return_addresses, ret_addr); + RzIterator *predecessor = rz_inquiry_bb_cfg_get_outgoing_edges(binary_bb_cfg, cc->bb_addr); + const RzGraphEdge *e; + rz_iterator_foreach(predecessor, e) { + RzInquiryBBCFGEdgeType type = (RzInquiryBBCFGEdgeType)(utptr)rz_graph_edge_get_data(e); + if (type == RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET) { + // TODO: Remove this check + const RzGraphNode *nr = rz_graph_edge_get_to(e); + ut64 target = rz_graph_node_get_id(nr); + rz_warn_if_fail(target == cc->npc); + } else if (type == RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL) { + const RzGraphNode *nc = rz_graph_edge_get_to(e); + ut64 target = rz_graph_node_get_id(nc); + rz_warn_if_fail(target == cc->target); + rz_vector_push(cfep_addresses, &target); + RZ_LOG_DEBUG("Add cfep at 0x%" PFMT64x " (call: 0x%" PFMT64x ") " + "based on BB 0x%" PFMT64x "\n", + cc->bb_addr, cc->candidate_addr, target); + } } - RZ_LOG_DEBUG("Add cfep at 0x%llx based on BB 0x%llx\n", cc->candidate_addr, cc->target); - rz_vector_push(cfep_addresses, &cc->target); rz_iterator_free(predecessor); } rz_iterator_free(iter); @@ -197,7 +226,6 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( rz_return_val_if_fail(call_candidates && binary_bb_cfg && fcns, false); // Candidate function entry points - RzSetU *return_addresses = rz_set_u_new(); RzVector *cfep_addresses = rz_vector_new(sizeof(ut64), NULL, NULL); RzIterator *iter = rz_set_u_as_iter(symbol_addresses); ut64 *addr; @@ -205,7 +233,7 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( rz_vector_push(cfep_addresses, addr); } rz_iterator_free(iter); - fill_cfep_and_ret_addresses(binary_bb_cfg, call_candidates, return_addresses, cfep_addresses); + fill_candidate_fcn_entry_points(binary_bb_cfg, call_candidates, cfep_addresses); // Set of handled cfep RzSetU *cfep_handled = rz_set_u_new(); @@ -223,8 +251,8 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( recurse_into_fcn_bbs(fcn, UT64_MAX, cfep_addr, + RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE, visited_bbs, - return_addresses, cfep_addresses, call_candidates, binary_bb_cfg, @@ -235,7 +263,6 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( } rz_set_u_free(cfep_handled); - rz_set_u_free(return_addresses); rz_vector_free(cfep_addresses); return true; } diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index cd29f5c3d3a..cfe7bab68ab 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -35,7 +35,7 @@ RZ_IPI void rz_inquiry_bb_cfg_free(RZ_NULLABLE RZ_OWN RzInquiryBBCFG *bb_cfg) { } static bool edge_from(const RzGraphEdge *e, void *addr) { - ut64 from_addr = (utptr) addr; + ut64 from_addr = (utptr)addr; return rz_graph_node_get_id(rz_graph_edge_get_from(e)) == from_addr; } @@ -73,17 +73,17 @@ RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb /** * \brief Neighbors of outgoing edges. */ -RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_outgoing_nodes(const RzInquiryBBCFG *cfg, ut64 bb_addr) { +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_outgoing_edges(const RzInquiryBBCFG *cfg, ut64 bb_addr) { rz_return_val_if_fail(cfg, NULL); - return rz_graph_out_neighbors_by_id(cfg->graph, bb_addr); + return rz_graph_out_edges_by_id(cfg->graph, bb_addr); } /** * \brief Neighbors of incoming edges. */ -RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_incoming_nodes(const RzInquiryBBCFG *cfg, ut64 bb_addr) { +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_incoming_edges(const RzInquiryBBCFG *cfg, ut64 bb_addr) { rz_return_val_if_fail(cfg, NULL); - return rz_graph_in_neighbors_by_id(cfg->graph, bb_addr); + return rz_graph_in_edges_by_id(cfg->graph, bb_addr); } /** @@ -102,10 +102,10 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut if (!n) { return false; } - const RzInquiryBB *nbb = rz_graph_node_get_data(n); - if (nbb->size != size) { - rz_warn_if_reached(); - } + // const RzInquiryBB *nbb = rz_graph_node_get_data(n); + // if (nbb->size != size) { + // rz_warn_if_reached(); + // } if (existed) { free(bb); } @@ -115,11 +115,28 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, RzVector /**/ *xrefs) { RzAnalysisXRef *xref; rz_vector_foreach (xrefs, xref) { - if (xref->type != RZ_ANALYSIS_XREF_TYPE_CODE) { + switch (xref->type) { + default: continue; - } - if (!rz_inquiry_bb_cfg_add_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP)) { - RZ_LOG_DEBUG("Did not add edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, xref->to); + case RZ_ANALYSIS_XREF_TYPE_CODE: + if (!rz_inquiry_bb_cfg_add_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP)) { + RZ_LOG_DEBUG("Did not add JMP edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, xref->to); + } + break; + case RZ_ANALYSIS_XREF_TYPE_CALL: + if (!rz_inquiry_bb_cfg_add_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL)) { + RZ_LOG_DEBUG("Did not add CALL edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, xref->to); + } + RzInquiryBB bb = { 0 }; + if (!rz_inquiry_bb_cfg_get_basic_block(cfg, xref->bb_addr, &bb)) { + rz_warn_if_reached(); + break; + } + ut64 ret_addr = bb.addr + bb.size; + if (!rz_inquiry_bb_cfg_add_edge(cfg, xref->bb_addr, ret_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET)) { + RZ_LOG_DEBUG("Did not add CALL_RET edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, ret_addr); + } + break; } } return true; diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 6e8c23da1c1..6db7f5b26c3 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -87,6 +87,7 @@ RZ_API void rz_inquiry_function_free(RZ_NULLABLE RZ_OWN RzInquiryFunction *fcn) } rz_inquiry_bb_cfg_free(fcn->bb_cfg); rz_vector_free(fcn->entry_points); + rz_vector_free(fcn->call_candidates); free(fcn); } @@ -97,6 +98,7 @@ RZ_IPI RZ_OWN RzInquiryFunction *rz_inquiry_function_new() { } fcn->bb_cfg = rz_inquiry_bb_cfg_new(RZ_GRAPH_IMPL_LIST); fcn->entry_points = rz_vector_new(sizeof(ut64), NULL, NULL); + fcn->call_candidates = rz_vector_new(sizeof(RzAnalysisCallCandidate), NULL, NULL); if (!fcn->bb_cfg || !fcn->entry_points) { rz_inquiry_function_free(fcn); return NULL; @@ -140,14 +142,14 @@ RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /*bb_cfg, i2i_edge->to); + RzIterator *incoming = rz_inquiry_bb_cfg_get_incoming_edges(iq->bb_cfg, i2i_edge->to); if (!incoming) { // Basic block not present. continue; } - RzGraphNode *gn; - rz_iterator_foreach(incoming, gn) { - const RzInquiryBB *bb = rz_graph_node_get_data(gn); + RzGraphEdge *in_e; + rz_iterator_foreach(incoming, in_e) { + const RzInquiryBB *bb = rz_graph_node_get_data(rz_graph_edge_get_from(in_e)); if (RZ_BETWEEN_EXCL(bb->addr, i2i_edge->from, bb->addr + bb->size)) { rz_iterator_free(incoming); // This edge was already covered. @@ -165,8 +167,26 @@ RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /*addr, i2i_edge->from, bb->addr + bb->size)) { continue; } - if (!rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, i2i_edge->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP)) { + switch (i2i_edge->type) { + default: rz_warn_if_reached(); + break; + case RZ_ANALYSIS_XREF_TYPE_CALL: + if (!rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, i2i_edge->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL)) { + rz_warn_if_reached(); + } + break; + case RZ_ANALYSIS_XREF_TYPE_CODE: + eprintf("Add i2i jump: 0x%llx -> 0x%llx\n", bb->addr, i2i_edge->to); + if (!rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, i2i_edge->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP)) { + rz_warn_if_reached(); + } + break; + case RZ_ANALYSIS_XREF_TYPE_CALL_RET: + if (!rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, i2i_edge->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET)) { + rz_warn_if_reached(); + } + break; } } rz_iterator_free(bb_iter); @@ -335,7 +355,6 @@ static bool handle_yields(RzCore *core, RzInterpreterYieldRBuf *yield_rbufs[RZ_I return false; } else if (r == RZ_THREAD_RING_BUF_OK) { rz_inquiry_add_xref(core->inquiry, &xref); - rz_analysis_xrefs_set(core->analysis, xref.from, xref.to, xref.type); RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref.from, xref.to, rz_analysis_ref_type_tostring(xref.type)); } } @@ -432,9 +451,6 @@ static bool send_next_il_bb(RzCore *core, // This is the basic block for the imported function. rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, branch->target_addr, 1); } - if (RZ_LIKELY(branch->branching_bb_addr)) { - rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, branch->target_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP); - } rz_th_queue_push(il_queue, (void *)bb, true); return true; } @@ -799,10 +815,10 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { eprintf("\n"); } - if (!rz_inquiry_bb_cfg_reduce(core->inquiry->bb_cfg)) { + if (!rz_inquiry_bb_cfg_add_xrefs(core->inquiry->bb_cfg, core->inquiry->xrefs)) { rz_warn_if_reached(); } - if (!rz_inquiry_bb_cfg_add_xrefs(core->inquiry->bb_cfg, core->inquiry->xrefs)) { + if (!rz_inquiry_bb_cfg_reduce(core->inquiry->bb_cfg)) { rz_warn_if_reached(); } if (!user_sent_signal) { @@ -840,69 +856,90 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry } rz_iterator_free(iter); - // Convert the Inquiry functions to analysis function. + + iter = ht_up_as_iter(inquiry->call_candidates); void **it; - rz_pvector_foreach (fcns, it) { - RzInquiryFunction *fcn = *it; - - ut64 fcn_addr = *(ut64 *)rz_vector_head(fcn->entry_points); - char new_fcn_name[64] = { 0 }; - void **it; - rz_pvector_foreach (symbols, it) { - RzBinSymbol *s = *it; - if (s->vaddr == fcn_addr && RZ_STR_EQ(s->type, RZ_BIN_TYPE_FUNC_STR)) { - rz_strf(new_fcn_name, "sym.%s", s->name); - break; - } - } - if (new_fcn_name[0] == '\0') { - rz_strf(new_fcn_name, "fcn_0x%" PFMT64x, fcn_addr); - } - RzAnalysisFunction *afcn = rz_analysis_create_function(analysis, new_fcn_name, fcn_addr, RZ_ANALYSIS_FCN_TYPE_FCN); - if (!afcn) { - rz_warn_if_reached(); - continue; - } + rz_iterator_foreach (iter, it) { + RzAnalysisCallCandidate *cc = *it; + rz_analysis_xrefs_set(analysis, cc->candidate_addr, cc->target, RZ_ANALYSIS_XREF_TYPE_CALL); + } + rz_iterator_free(iter); - RzIterator *iter = rz_graph_get_nodes(fcn->bb_cfg->graph); - RzGraphNode *n; - rz_iterator_foreach(iter, n) { - const RzInquiryBB *bb = rz_graph_node_get_data(n); - RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, bb->addr); - if (!abb && !(abb = rz_analysis_create_block(analysis, bb->addr, bb->size))) { - rz_warn_if_reached(); + RzAnalysisXRef *xref; + rz_vector_foreach(inquiry->xrefs, xref) { + rz_analysis_xrefs_set(analysis, xref->from, xref->to, xref->type != RZ_ANALYSIS_XREF_TYPE_CALL_RET ? xref->type : RZ_ANALYSIS_XREF_TYPE_CODE); + RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, xref->bb_addr); + RzIterator *out_edges = rz_inquiry_bb_cfg_get_outgoing_edges(inquiry->bb_cfg, xref->bb_addr); + if (!out_edges) { continue; } - RzIterator *successors = rz_inquiry_bb_cfg_get_outgoing_nodes(inquiry->bb_cfg, bb->addr); - RzGraphNode *n; - size_t i = 0; - rz_iterator_foreach(successors, n) { - if (i == 0) { - abb->jump = rz_graph_node_get_id(n); - } else if (i == 1) { - abb->fail = rz_graph_node_get_id(n); - } else { - RZ_LOG_WARN("The basic block at 0x%" PFMT64x " has more than two outgoing edges. " - "Rizin can't model this currently :(\n", - rz_graph_node_get_id(n)); + RzGraphEdge *e; + rz_iterator_foreach(out_edges, e) { + RzInquiryBBCFGEdgeType type = (RzInquiryBBCFGEdgeType)(utptr)rz_graph_edge_get_data(e); + switch (type) { + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE: + rz_warn_if_reached(); + // fall through + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF: + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP: { + ut64 target = rz_graph_node_get_id(rz_graph_edge_get_to(e)); + if (abb->jump == UT64_MAX || abb->jump == target) { + abb->jump = target; + eprintf("Add jump = 0x%llx -> 0x%llx (%d)\n", xref->bb_addr, target, type); + } else if (abb->fail == UT64_MAX || abb->fail == target) { + abb->fail = target; + eprintf("Add fail = 0x%llx -> 0x%llx (%d)\n", xref->bb_addr, target, type); + } else if (abb->fail != target && abb->jump != target) { + RZ_LOG_WARN("The basic block at 0x%" PFMT64x " has more than two outgoing edges.\n" + "\t\tHas jump = 0x%llx fail = 0x%llx. Will miss = 0x%llx (%d)\n", xref->bb_addr, abb->jump, abb->fail, + target, type); + } + break; + } + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET: + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL: break; } - i++; - } - rz_iterator_free(successors); - - RzAnalysisCallCandidate *cc; - if ((cc = ht_up_find(inquiry->call_candidates, bb->addr, NULL))) { - // Calls need an edge between the call instruction and its return address. - // That is technically wrong, because the call could be a tail call. - // But the prototype doesn't model this. - // So just add an edge. - abb->jump = cc->npc; } - rz_analysis_function_add_block(afcn, abb); - } - rz_iterator_free(iter); - } + rz_iterator_free(out_edges); + } + + // // Convert the Inquiry functions to analysis function. + // rz_pvector_foreach (fcns, it) { + // RzInquiryFunction *fcn = *it; + + // ut64 fcn_addr = *(ut64 *)rz_vector_head(fcn->entry_points); + // char new_fcn_name[64] = { 0 }; + // void **it; + // rz_pvector_foreach (symbols, it) { + // RzBinSymbol *s = *it; + // if (s->vaddr == fcn_addr && RZ_STR_EQ(s->type, RZ_BIN_TYPE_FUNC_STR)) { + // rz_strf(new_fcn_name, "sym.%s", s->name); + // break; + // } + // } + // if (new_fcn_name[0] == '\0') { + // rz_strf(new_fcn_name, "fcn_0x%" PFMT64x, fcn_addr); + // } + // RzAnalysisFunction *afcn = rz_analysis_create_function(analysis, new_fcn_name, fcn_addr, RZ_ANALYSIS_FCN_TYPE_FCN); + // if (!afcn) { + // rz_warn_if_reached(); + // continue; + // } + + // RzIterator *iter = rz_graph_get_nodes(fcn->bb_cfg->graph); + // RzGraphNode *n; + // rz_iterator_foreach(iter, n) { + // const RzInquiryBB *bb = rz_graph_node_get_data(n); + // RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, bb->addr); + // if (!abb && !(abb = rz_analysis_create_block(analysis, bb->addr, bb->size))) { + // rz_warn_if_reached(); + // continue; + // } + // rz_analysis_function_add_block(afcn, abb); + // } + // rz_iterator_free(iter); + // } rz_pvector_free(fcns); return true; } diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 4357eaef667..3e287cc829a 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -89,21 +89,24 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterSet *iset, rz_bv_to_ut64(pc->bv), rz_bv_to_ut64(eval_out.bv), eval_out.is_concrete ? "Concrete" : "Abstract"); - // Setting the PC to a bottom value is allowed here! - // The successor function will handle this case. - if (eval_out.is_concrete) { - // NOTE: This prototype can't classify into call or jump. - // Everything is just a jump for it at this point. - report_yield_xref(iset, insn_pkt_size, rz_bv_to_ut64(pc->bv), &eval_out, RZ_ANALYSIS_XREF_TYPE_CODE); - } + bool reported_cc = false; if (plugin_data->call_cand.store_addr && eval_out.is_concrete) { // An instruction in this basic block stored the next PC. // Report a call candidate. plugin_data->call_cand.candidate_addr = rz_bv_to_ut64(pc->bv); plugin_data->call_cand.target = rz_bv_to_ut64(eval_out.bv); report_yield_call_candiate(iset, plugin_data); + reported_cc = true; } memset(&plugin_data->call_cand, 0, sizeof(plugin_data->call_cand)); + + if (eval_out.is_concrete) { + report_yield_xref(iset, insn_pkt_size, rz_bv_to_ut64(pc->bv), &eval_out, + reported_cc ? RZ_ANALYSIS_XREF_TYPE_CALL : RZ_ANALYSIS_XREF_TYPE_CODE); + } + + // Setting the PC to a bottom value is allowed here! + // The successor function will handle this case. copy_abstr_data(iset->astate->pc->abstr_data, &eval_out); break; } From 80c3eb9b70a5c687e35156956221ad8fb689212c Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 30 May 2026 17:55:51 +0200 Subject: [PATCH 261/334] Refactor Inquiry graph stuff (sorry forgot what exactly, I only rebase this) --- librz/arch/xrefs.c | 11 +- librz/include/rz_inquiry.h | 1 - librz/include/rz_inquiry/rz_bb_graph.h | 14 +- .../inquiry/algorithms/revng_fcn_detection.c | 14 +- librz/inquiry/bb_cfg.c | 30 +++- librz/inquiry/inquiry.c | 170 +++++++++--------- .../interpreter/p/interpreter_prototype.c | 10 +- librz/inquiry/interpreter/prototype/eval.c | 30 ++++ librz/inquiry/interpreter/prototype/eval.h | 30 ++++ .../interpreter/prototype/eval_effect.c | 45 +++-- 10 files changed, 232 insertions(+), 123 deletions(-) diff --git a/librz/arch/xrefs.c b/librz/arch/xrefs.c index 5565e759032..15aebf42cac 100644 --- a/librz/arch/xrefs.c +++ b/librz/arch/xrefs.c @@ -218,6 +218,8 @@ RZ_API const char *rz_analysis_xrefs_type_tostring(RzAnalysisXRefType type) { return "STRING"; case RZ_ANALYSIS_XREF_TYPE_MEM_WRITE: return "MEM_WRITE"; + case RZ_ANALYSIS_XREF_TYPE_RETURN: + return "RETURN"; case RZ_ANALYSIS_XREF_TYPE_NULL: default: return "UNKNOWN"; @@ -381,15 +383,13 @@ RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, rz_set_u_add(branch_targets, op.jump); RzAnalysisXRef edge = { .from = addr, .to = op.jump }; - op.type = op_is_call ? RZ_ANALYSIS_XREF_TYPE_CALL : RZ_ANALYSIS_XREF_TYPE_CODE; + edge.type = op_is_call ? RZ_ANALYSIS_XREF_TYPE_CALL : RZ_ANALYSIS_XREF_TYPE_CODE; rz_vector_push(insn_to_insn_edges, &edge); if (op.fail != UT64_MAX) { if (op_is_jump) { - op.type = RZ_ANALYSIS_XREF_TYPE_CODE; + RzAnalysisXRef edge = { .from = addr, .to = op.fail, .type = RZ_ANALYSIS_XREF_TYPE_CODE }; rz_set_u_add(branch_targets, op.fail); - - RzAnalysisXRef edge = { .from = addr, .to = op.fail }; rz_vector_push(insn_to_insn_edges, &edge); } } @@ -398,8 +398,7 @@ RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, // If it is a call, also add the following instruction as reference. // Because it is likely a return point. rz_set_u_add(branch_targets, op.addr + op.size); - op.type = RZ_ANALYSIS_XREF_TYPE_CALL_RET; - RzAnalysisXRef edge = { .from = addr, .to = op.fail }; + RzAnalysisXRef edge = { .from = addr, .to = op.fail, .type = RZ_ANALYSIS_XREF_TYPE_CALL_RET }; rz_vector_push(insn_to_insn_edges, &edge); } addr += op.size; diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 7d97a965d57..c27b7dfda7b 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -69,7 +69,6 @@ RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, */ typedef struct { RzVector /**/ *entry_points; - RzVector /**/ *call_candidates; RzInquiryBBCFG *bb_cfg; } RzInquiryFunction; diff --git a/librz/include/rz_inquiry/rz_bb_graph.h b/librz/include/rz_inquiry/rz_bb_graph.h index adfb4110997..9af689b1ed7 100644 --- a/librz/include/rz_inquiry/rz_bb_graph.h +++ b/librz/include/rz_inquiry/rz_bb_graph.h @@ -21,16 +21,16 @@ typedef enum { * \brief An control flow change via a RzIL JMP. The "from" BB ends with a jump. */ RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP, + /** + * \brief A control flow edge between two blocks where the "from" block does not end with a JUMP. + * It is between two blocks which were split from a single one. + */ + RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF, /** * \brief An control flow change via a RzIL JMP but by a call candidate. * The "from" BB ends with a jump, very likely to a procedure. */ RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL, - /** - * \brief A control flow edge between two blocks where the "from" block does not end with a JUMP. - * It is between two blocks, if they were split from a single one. - */ - RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF, /** * \brief This edge does not depict a control flow edge. * It is an opaque edge between a call instruction and the (possible) return point of the procedure called. @@ -38,6 +38,10 @@ typedef enum { * There are multiple exceptions to it though, like tail calls, non-returning procedures, or delayed calls. */ RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET, + /** + * \brief An edge from a return instruction to its target. + */ + RZ_INQUIRY_BB_CFG_EDGE_TYPE_RETURN, } RzInquiryBBCFGEdgeType; /** diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index db24453d72a..0583ad08032 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -46,6 +46,7 @@ #include "rz_util/rz_assert.h" #include "rz_util/rz_graph.h" #include "rz_util/rz_iterator.h" +#include "rz_util/rz_log.h" #include "rz_util/rz_set.h" #include "rz_vector.h" #include @@ -121,7 +122,7 @@ static void recurse_into_fcn_bbs( // RzIterator *successors = rz_inquiry_bb_cfg_get_outgoing_edges(binary_bb_cfg, this_bb_addr); if (!successors) { - rz_warn_if_reached(); + // Node has no successors. goto err_return; } @@ -153,16 +154,10 @@ static void recurse_into_fcn_bbs( continue; } break; - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL: { - RzAnalysisCallCandidate *cc = ht_up_find((HtUP *)call_candidates, this_bb_addr, NULL); - // Don't recurse edges to other procedures. - // Instead we add this address to the functions call targets. - if (cc) { - rz_vector_push(fcn->call_candidates, cc); - } + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL: + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_RETURN: continue; } - } if (jumps_to_ignored_code(ignored_code, succ_addr)) { continue; } @@ -204,7 +199,6 @@ static void fill_candidate_fcn_entry_points( } else if (type == RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL) { const RzGraphNode *nc = rz_graph_edge_get_to(e); ut64 target = rz_graph_node_get_id(nc); - rz_warn_if_fail(target == cc->target); rz_vector_push(cfep_addresses, &target); RZ_LOG_DEBUG("Add cfep at 0x%" PFMT64x " (call: 0x%" PFMT64x ") " "based on BB 0x%" PFMT64x "\n", diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index cfe7bab68ab..82e3dfb6bfd 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -116,8 +116,6 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, RzVector /*type) { - default: - continue; case RZ_ANALYSIS_XREF_TYPE_CODE: if (!rz_inquiry_bb_cfg_add_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP)) { RZ_LOG_DEBUG("Did not add JMP edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, xref->to); @@ -137,6 +135,17 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, RzVector /* 0x%" PFMT64x "\n", xref->bb_addr, ret_addr); } break; + case RZ_ANALYSIS_XREF_TYPE_RETURN: + if (!rz_inquiry_bb_cfg_add_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_RETURN)) { + RZ_LOG_DEBUG("Did not add RETURN edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, xref->to); + } + break; + case RZ_ANALYSIS_XREF_TYPE_NULL: + case RZ_ANALYSIS_XREF_TYPE_DATA: + case RZ_ANALYSIS_XREF_TYPE_STRING: + case RZ_ANALYSIS_XREF_TYPE_MEM_WRITE: + case RZ_ANALYSIS_XREF_TYPE_CALL_RET: + continue; } } return true; @@ -195,11 +204,23 @@ RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg) { rz_goto_if_fail(big_bb, fail); big_bb->size = small_bb_addr - big_bb_addr; - // add edge between big to small bb, remove old edges. + // add edge between big to small bb, move old edges to small_bb. + RzGraphEdge *e; + RzIterator *out_edges = rz_inquiry_bb_cfg_get_outgoing_edges(cfg, big_bb_addr); + rz_iterator_foreach(out_edges, e) { + ut64 to = rz_graph_node_get_id(rz_graph_edge_get_to(e)); + RzInquiryBBCFGEdgeType type = (utptr)rz_graph_edge_get_data(e); + if (!rz_inquiry_bb_cfg_add_edge(cfg, small_bb_addr, to, type)) { + RZ_LOG_DEBUG("Did not add edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", big_bb_addr, small_bb_addr); + continue; + } + } + rz_iterator_free(out_edges); + rz_inquiry_bb_cfg_del_out_edges(cfg, big_bb_addr); if (!rz_inquiry_bb_cfg_add_edge(cfg, big_bb_addr, small_bb_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF)) { RZ_LOG_DEBUG("Did not add edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", big_bb_addr, small_bb_addr); - goto fail; + continue; } } } @@ -208,6 +229,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg) { return true; fail: + rz_iterator_free(iter); ht_up_free(overlapping_bbs); rz_warn_if_reached(); return false; diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 6db7f5b26c3..c33b22fbef0 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -87,7 +87,6 @@ RZ_API void rz_inquiry_function_free(RZ_NULLABLE RZ_OWN RzInquiryFunction *fcn) } rz_inquiry_bb_cfg_free(fcn->bb_cfg); rz_vector_free(fcn->entry_points); - rz_vector_free(fcn->call_candidates); free(fcn); } @@ -98,7 +97,6 @@ RZ_IPI RZ_OWN RzInquiryFunction *rz_inquiry_function_new() { } fcn->bb_cfg = rz_inquiry_bb_cfg_new(RZ_GRAPH_IMPL_LIST); fcn->entry_points = rz_vector_new(sizeof(ut64), NULL, NULL); - fcn->call_candidates = rz_vector_new(sizeof(RzAnalysisCallCandidate), NULL, NULL); if (!fcn->bb_cfg || !fcn->entry_points) { rz_inquiry_function_free(fcn); return NULL; @@ -168,16 +166,12 @@ RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /*type) { - default: - rz_warn_if_reached(); - break; case RZ_ANALYSIS_XREF_TYPE_CALL: if (!rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, i2i_edge->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL)) { rz_warn_if_reached(); } break; case RZ_ANALYSIS_XREF_TYPE_CODE: - eprintf("Add i2i jump: 0x%llx -> 0x%llx\n", bb->addr, i2i_edge->to); if (!rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, i2i_edge->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP)) { rz_warn_if_reached(); } @@ -187,6 +181,19 @@ RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /*bb_cfg, bb->addr, i2i_edge->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_RETURN)) { + rz_warn_if_reached(); + } + break; + case RZ_ANALYSIS_XREF_TYPE_NULL: + case RZ_ANALYSIS_XREF_TYPE_DATA: + case RZ_ANALYSIS_XREF_TYPE_STRING: + case RZ_ANALYSIS_XREF_TYPE_MEM_WRITE: + RZ_LOG_WARN("Xref of type %s for code edge.\n", + rz_analysis_xrefs_type_tostring(i2i_edge->type)); + rz_warn_if_reached(); + break; } } rz_iterator_free(bb_iter); @@ -856,90 +863,87 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry } rz_iterator_free(iter); - - iter = ht_up_as_iter(inquiry->call_candidates); - void **it; - rz_iterator_foreach (iter, it) { - RzAnalysisCallCandidate *cc = *it; - rz_analysis_xrefs_set(analysis, cc->candidate_addr, cc->target, RZ_ANALYSIS_XREF_TYPE_CALL); - } - rz_iterator_free(iter); - RzAnalysisXRef *xref; - rz_vector_foreach(inquiry->xrefs, xref) { - rz_analysis_xrefs_set(analysis, xref->from, xref->to, xref->type != RZ_ANALYSIS_XREF_TYPE_CALL_RET ? xref->type : RZ_ANALYSIS_XREF_TYPE_CODE); + rz_vector_foreach (inquiry->xrefs, xref) { RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, xref->bb_addr); - RzIterator *out_edges = rz_inquiry_bb_cfg_get_outgoing_edges(inquiry->bb_cfg, xref->bb_addr); - if (!out_edges) { - continue; - } - RzGraphEdge *e; - rz_iterator_foreach(out_edges, e) { - RzInquiryBBCFGEdgeType type = (RzInquiryBBCFGEdgeType)(utptr)rz_graph_edge_get_data(e); - switch (type) { - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE: - rz_warn_if_reached(); - // fall through - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF: - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP: { - ut64 target = rz_graph_node_get_id(rz_graph_edge_get_to(e)); - if (abb->jump == UT64_MAX || abb->jump == target) { - abb->jump = target; - eprintf("Add jump = 0x%llx -> 0x%llx (%d)\n", xref->bb_addr, target, type); - } else if (abb->fail == UT64_MAX || abb->fail == target) { - abb->fail = target; - eprintf("Add fail = 0x%llx -> 0x%llx (%d)\n", xref->bb_addr, target, type); - } else if (abb->fail != target && abb->jump != target) { - RZ_LOG_WARN("The basic block at 0x%" PFMT64x " has more than two outgoing edges.\n" - "\t\tHas jump = 0x%llx fail = 0x%llx. Will miss = 0x%llx (%d)\n", xref->bb_addr, abb->jump, abb->fail, - target, type); - } - break; - } - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET: - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL: - break; + RzIterator *out_edges = rz_inquiry_bb_cfg_get_outgoing_edges(inquiry->bb_cfg, xref->bb_addr); + if (!out_edges) { + continue; + } + RzGraphEdge *e; + rz_iterator_foreach(out_edges, e) { + RzInquiryBBCFGEdgeType type = (RzInquiryBBCFGEdgeType)(utptr)rz_graph_edge_get_data(e); + switch (type) { + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE: + rz_warn_if_reached(); + break; + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF: + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP: { + ut64 target = rz_graph_node_get_id(rz_graph_edge_get_to(e)); + if (abb->jump == UT64_MAX && abb->fail != target) { + abb->jump = target; + } else if (abb->fail == UT64_MAX && abb->jump != target) { + abb->fail = target; + } else if (abb->fail != target && abb->jump != target) { + RZ_LOG_WARN("The basic block at 0x%" PFMT64x " has more than two outgoing edges.\n" + "\t\tHas jump = 0x%llx fail = 0x%llx. Will miss = 0x%llx (%d)\n", + xref->bb_addr, abb->jump, abb->fail, + target, type); } + rz_analysis_xrefs_set(analysis, xref->from, xref->to, RZ_ANALYSIS_XREF_TYPE_CODE); + break; } - rz_iterator_free(out_edges); + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET: + rz_analysis_xrefs_set(analysis, xref->from, xref->to, RZ_ANALYSIS_XREF_TYPE_CODE); + break; + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL: + rz_analysis_xrefs_set(analysis, xref->from, xref->to, RZ_ANALYSIS_XREF_TYPE_CALL); + break; + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_RETURN: + rz_analysis_xrefs_set(analysis, xref->from, xref->to, RZ_ANALYSIS_XREF_TYPE_RETURN); + break; + } + } + rz_iterator_free(out_edges); } // // Convert the Inquiry functions to analysis function. - // rz_pvector_foreach (fcns, it) { - // RzInquiryFunction *fcn = *it; - - // ut64 fcn_addr = *(ut64 *)rz_vector_head(fcn->entry_points); - // char new_fcn_name[64] = { 0 }; - // void **it; - // rz_pvector_foreach (symbols, it) { - // RzBinSymbol *s = *it; - // if (s->vaddr == fcn_addr && RZ_STR_EQ(s->type, RZ_BIN_TYPE_FUNC_STR)) { - // rz_strf(new_fcn_name, "sym.%s", s->name); - // break; - // } - // } - // if (new_fcn_name[0] == '\0') { - // rz_strf(new_fcn_name, "fcn_0x%" PFMT64x, fcn_addr); - // } - // RzAnalysisFunction *afcn = rz_analysis_create_function(analysis, new_fcn_name, fcn_addr, RZ_ANALYSIS_FCN_TYPE_FCN); - // if (!afcn) { - // rz_warn_if_reached(); - // continue; - // } - - // RzIterator *iter = rz_graph_get_nodes(fcn->bb_cfg->graph); - // RzGraphNode *n; - // rz_iterator_foreach(iter, n) { - // const RzInquiryBB *bb = rz_graph_node_get_data(n); - // RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, bb->addr); - // if (!abb && !(abb = rz_analysis_create_block(analysis, bb->addr, bb->size))) { - // rz_warn_if_reached(); - // continue; - // } - // rz_analysis_function_add_block(afcn, abb); - // } - // rz_iterator_free(iter); - // } + void **it; + rz_pvector_foreach (fcns, it) { + RzInquiryFunction *fcn = *it; + + ut64 fcn_addr = *(ut64 *)rz_vector_head(fcn->entry_points); + char new_fcn_name[64] = { 0 }; + void **it; + rz_pvector_foreach (symbols, it) { + RzBinSymbol *s = *it; + if (s->vaddr == fcn_addr && RZ_STR_EQ(s->type, RZ_BIN_TYPE_FUNC_STR)) { + rz_strf(new_fcn_name, "sym.%s", s->name); + break; + } + } + if (new_fcn_name[0] == '\0') { + rz_strf(new_fcn_name, "fcn_0x%" PFMT64x, fcn_addr); + } + RzAnalysisFunction *afcn = rz_analysis_create_function(analysis, new_fcn_name, fcn_addr, RZ_ANALYSIS_FCN_TYPE_FCN); + if (!afcn) { + rz_warn_if_reached(); + continue; + } + + RzIterator *iter = rz_graph_get_nodes(fcn->bb_cfg->graph); + RzGraphNode *n; + rz_iterator_foreach(iter, n) { + const RzInquiryBB *bb = rz_graph_node_get_data(n); + RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, bb->addr); + if (!abb && !(abb = rz_analysis_create_block(analysis, bb->addr, bb->size))) { + rz_warn_if_reached(); + continue; + } + rz_analysis_function_add_block(afcn, abb); + } + rz_iterator_free(iter); + } rz_pvector_free(fcns); return true; } diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index bf51bb52cc2..f5ff55d3c15 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -10,6 +10,8 @@ #include "rz_util/ht_uu.h" #include "rz_util/rz_bitvector.h" +#define INITIAL_STACK_CAPACITY 8 + #define MAX_INVOCATIONS_PER_BB 3 static bool eval(RZ_NONNULL RzInterpreterSet *iset, @@ -105,7 +107,7 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da AD(av->abstr_data)->is_concrete = true; if (state->il_config->init_state) { RzAnalysisILInitStateVar *il_var; - rz_vector_foreach(&state->il_config->init_state->vars, il_var) { + rz_vector_foreach (&state->il_config->init_state->vars, il_var) { if (rz_str_djb2_hash(il_var->name) != djb2_reg_name) { continue; } @@ -258,6 +260,10 @@ bool init(void **plugin_data) { free(pdata); return false; } + rz_vector_init(&pdata->stack, + sizeof(ProtoInterprAbstrStackFrame), + (RzVectorFree)stack_frame_fini, NULL); + rz_vector_reserve(&pdata->stack, INITIAL_STACK_CAPACITY); *plugin_data = pdata; return true; } @@ -269,6 +275,7 @@ bool fini(void *plugin_data) { RZ_LOG_DEBUG("prototype: fini()\n"); ProtoIntrprPluginData *pdata = plugin_data; ht_uu_free(pdata->bb_invocation_count); + rz_vector_fini(&pdata->stack); free(pdata); return true; } @@ -281,6 +288,7 @@ bool reset(void *plugin_data) { ProtoIntrprPluginData *pdata = plugin_data; ht_uu_clear(pdata->bb_invocation_count); memset(&pdata->call_cand, 0, sizeof(RzAnalysisCallCandidate)); + rz_vector_purge(&pdata->stack); return true; } diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 5479357a568..edeb526bac5 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -228,3 +228,33 @@ bool set_pc(RzInterpreterAbstrState *state, ut64 pc, pc); return rz_bv_set_from_ut64(AD(state->pc->abstr_data)->bv, pc); } + +void stack_frame_fini(ProtoInterprAbstrStackFrame *frame, void *unused) { + if (!frame) { + return; + } + rz_bv_fini(&frame->return_addr); + rz_bv_fini(&frame->entry_point); +} + +void stack_frame_push(ProtoIntrprPluginData *pdata, RzBitVector *entry_point, RzBitVector *return_addr, ut64 instance) { + ProtoInterprAbstrStackFrame frame = { 0 }; + rz_bv_init(&frame.return_addr, rz_bv_len(return_addr)); + rz_bv_copy(&frame.return_addr, return_addr); + rz_bv_init(&frame.entry_point, rz_bv_len(entry_point)); + rz_bv_copy(&frame.entry_point, entry_point); + frame.instance = instance; + rz_vector_push(&pdata->stack, &frame); +} + +void stack_frame_pop(ProtoIntrprPluginData *pdata, RZ_NULLABLE ProtoInterprAbstrStackFrame *frame) { + rz_vector_pop(&pdata->stack, frame); +} + +bool stack_frame_top_ret_addr_cmp(ProtoIntrprPluginData *pdata, RzBitVector *addr) { + ProtoInterprAbstrStackFrame *frame = rz_vector_tail(&pdata->stack); + if (!frame) { + return false; + } + return rz_bv_eq(&frame->return_addr, addr); +} diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index e6d2162fb74..6208b47b674 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -27,9 +27,34 @@ typedef struct { RzBitVector *bv; } ProtoIntrprAbstrData; +typedef struct { + /** + * \brief The procedure's entry point this frame was initialized at. + */ + RzBitVector entry_point; + /** + * \brief The number of times that frame was initialized at the entry point. + * This is equivalent to the number of times the function was called at this entry point. + */ + ut64 instance; + /** + * \brief The return address of the procedure. + * TODO: The return address might be wrong in case of tail calls. + * The prototype doesn't really check what address was stored in the link register + * or on the stack (due to missing abstraction of archs calling convention). + * Instead, it simply stores the instruction address which comes after the call + * in memory. + */ + RzBitVector return_addr; + + // TODO: The abstract stack pointer at the point of procedure entry should be tracked here. + // But this needs to wait until we have a proper memory model implemented. +} ProtoInterprAbstrStackFrame; + typedef struct { HtUU *bb_invocation_count; RzAnalysisCallCandidate call_cand; ///< Data of a call candidate. + RzVector /**/ stack; ///< The call frame stack. } ProtoIntrprPluginData; /** @@ -105,4 +130,9 @@ bool report_yield_call_candiate( bool set_pc(RzInterpreterAbstrState *state, ut64 pc, void *plugin_data); +void stack_frame_fini(ProtoInterprAbstrStackFrame *frame, void *unused); +void stack_frame_push(ProtoIntrprPluginData *pdata, RzBitVector *entry_point, RzBitVector *return_addr, ut64 instance); +void stack_frame_pop(ProtoIntrprPluginData *pdata, RZ_NULLABLE ProtoInterprAbstrStackFrame *frame); +bool stack_frame_top_ret_addr_cmp(ProtoIntrprPluginData *pdata, RzBitVector *addr); + #endif // PROTOYPE_EVAL_H diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 3e287cc829a..4471190e0bd 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -85,24 +85,43 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterSet *iset, if (!eval_out.is_concrete) { RZ_LOG_DEBUG("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", rz_bv_to_ut64(pc->bv)); } + ut64 target = rz_bv_to_ut64(eval_out.bv); RZ_LOG_DEBUG("Prototype: JMP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", - rz_bv_to_ut64(pc->bv), - rz_bv_to_ut64(eval_out.bv), + rz_bv_to_ut64(pc->bv), target, eval_out.is_concrete ? "Concrete" : "Abstract"); - bool reported_cc = false; - if (plugin_data->call_cand.store_addr && eval_out.is_concrete) { - // An instruction in this basic block stored the next PC. - // Report a call candidate. - plugin_data->call_cand.candidate_addr = rz_bv_to_ut64(pc->bv); - plugin_data->call_cand.target = rz_bv_to_ut64(eval_out.bv); - report_yield_call_candiate(iset, plugin_data); - reported_cc = true; - } - memset(&plugin_data->call_cand, 0, sizeof(plugin_data->call_cand)); if (eval_out.is_concrete) { + RzAnalysisXRefType xref_type = RZ_ANALYSIS_XREF_TYPE_CODE; + + if (plugin_data->call_cand.store_addr) { + // An instruction in this basic block stored the next PC. + // Report a call candidate and assume this jump is a call. + plugin_data->call_cand.candidate_addr = rz_bv_to_ut64(pc->bv); + plugin_data->call_cand.target = target; + report_yield_call_candiate(iset, plugin_data); + + // For a call, we need to push a new frame. + RzBitVector ret_addr = { 0 }; + rz_bv_init(&ret_addr, rz_bv_len(eval_out.bv)); + rz_bv_set_from_ut64(&ret_addr, plugin_data->call_cand.npc); + + bool found = false; + ut64 ic = ht_uu_find(plugin_data->bb_invocation_count, plugin_data->call_cand.target, &found); + stack_frame_push(plugin_data, eval_out.bv, &ret_addr, !found ? 0 : ic); + rz_bv_fini(&ret_addr); + + xref_type = RZ_ANALYSIS_XREF_TYPE_CALL; + } + if (xref_type == RZ_ANALYSIS_XREF_TYPE_CODE && stack_frame_top_ret_addr_cmp(plugin_data, eval_out.bv)) { + stack_frame_pop(plugin_data, NULL); + xref_type = RZ_ANALYSIS_XREF_TYPE_RETURN; + } + report_yield_xref(iset, insn_pkt_size, rz_bv_to_ut64(pc->bv), &eval_out, - reported_cc ? RZ_ANALYSIS_XREF_TYPE_CALL : RZ_ANALYSIS_XREF_TYPE_CODE); + xref_type); + + // Clear the call candidate tracking variable. + memset(&plugin_data->call_cand, 0, sizeof(plugin_data->call_cand)); } // Setting the PC to a bottom value is allowed here! From c714e533335ffe2b012c52754d6aefbcabbb63d8 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 22 Apr 2026 14:52:24 +0200 Subject: [PATCH 262/334] For each requested branch, add control flow edges. --- librz/inquiry/bb_cfg.c | 31 +++++++++++++++++++++++++++---- librz/inquiry/inquiry.c | 3 +++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index 82e3dfb6bfd..b325858f0ec 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -50,6 +50,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_del_out_edges(RzInquiryBBCFG *cfg, ut64 bb_addr) { * \param from_bb The address of the basic block with the branch. * Not the address of the branch instruction! * \param to_bb The address of the basic block the branch leads to. + * \param type The type of the edge. * * \return True if edge was added. False for error or if a node doesn't exist. */ @@ -57,6 +58,28 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 t return rz_graph_add_edge_by_id(cfg->graph, from_bb, to_bb, RZ_GRAPH_INT_AS_DATA(type)); } +static bool is_cf_edge(const RzGraphEdge *e, void *unused) { + RzInquiryBBCFGEdgeType type = (utptr)rz_graph_edge_get_data(e); + return type == RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF; +} + +/** + * \brief Updates or adds an edge to the basic block CFG. + * Only edges of type RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF are updated to \p type. + * Otherwise the edge is not updated. + * + * \param cfg The basic block CFG to edit. + * \param from_bb The address of the basic block with the branch. + * Not the address of the branch instruction! + * \param to_bb The address of the basic block the branch leads to. + * \param type The type of the edge. + * + * \return True if edge was added. False in case of error. + */ +RZ_IPI bool rz_inquiry_bb_cfg_update_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBBCFGEdgeType type) { + return rz_graph_update_edge_by_id(cfg->graph, from_bb, to_bb, RZ_GRAPH_INT_AS_DATA(type), is_cf_edge, NULL); +} + RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RzInquiryBB *bb) { rz_return_val_if_fail(cfg && bb, false); const RzGraphNode *n = rz_graph_find_node(cfg->graph, bb_addr); @@ -117,12 +140,12 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, RzVector /*type) { case RZ_ANALYSIS_XREF_TYPE_CODE: - if (!rz_inquiry_bb_cfg_add_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP)) { + if (!rz_inquiry_bb_cfg_update_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP)) { RZ_LOG_DEBUG("Did not add JMP edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, xref->to); } break; case RZ_ANALYSIS_XREF_TYPE_CALL: - if (!rz_inquiry_bb_cfg_add_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL)) { + if (!rz_inquiry_bb_cfg_update_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL)) { RZ_LOG_DEBUG("Did not add CALL edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, xref->to); } RzInquiryBB bb = { 0 }; @@ -131,12 +154,12 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, RzVector /*bb_addr, ret_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET)) { + if (!rz_inquiry_bb_cfg_update_edge(cfg, xref->bb_addr, ret_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET)) { RZ_LOG_DEBUG("Did not add CALL_RET edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, ret_addr); } break; case RZ_ANALYSIS_XREF_TYPE_RETURN: - if (!rz_inquiry_bb_cfg_add_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_RETURN)) { + if (!rz_inquiry_bb_cfg_update_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_RETURN)) { RZ_LOG_DEBUG("Did not add RETURN edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, xref->to); } break; diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index c33b22fbef0..0e803cb700f 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -458,6 +458,9 @@ static bool send_next_il_bb(RzCore *core, // This is the basic block for the imported function. rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, branch->target_addr, 1); } + // Add a simple control flow edge here. + // It gets later updated to another type if a reported xref has it. + rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, bb->bb_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF); rz_th_queue_push(il_queue, (void *)bb, true); return true; } From 1b7138e314f614fccb6a27c0e40810ee703c7847 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 22 Apr 2026 21:07:27 +0200 Subject: [PATCH 263/334] Fix conversion of RzInquiry to RzAnalysis xrefs --- librz/inquiry/inquiry.c | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 0e803cb700f..1aa70ccfb43 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -863,26 +863,21 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry rz_iterator_foreach(iter, n) { const RzInquiryBB *bb = rz_graph_node_get_data(n); rz_analysis_add_bb(analysis, bb->addr, bb->size); - } - rz_iterator_free(iter); - - RzAnalysisXRef *xref; - rz_vector_foreach (inquiry->xrefs, xref) { - RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, xref->bb_addr); - RzIterator *out_edges = rz_inquiry_bb_cfg_get_outgoing_edges(inquiry->bb_cfg, xref->bb_addr); + RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, bb->addr); + RzIterator *out_edges = rz_inquiry_bb_cfg_get_outgoing_edges(inquiry->bb_cfg, bb->addr); if (!out_edges) { continue; } RzGraphEdge *e; rz_iterator_foreach(out_edges, e) { + ut64 target = rz_graph_node_get_id(rz_graph_edge_get_to(e)); RzInquiryBBCFGEdgeType type = (RzInquiryBBCFGEdgeType)(utptr)rz_graph_edge_get_data(e); switch (type) { - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE: - rz_warn_if_reached(); - break; + default: + continue; + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET: case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF: case RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP: { - ut64 target = rz_graph_node_get_id(rz_graph_edge_get_to(e)); if (abb->jump == UT64_MAX && abb->fail != target) { abb->jump = target; } else if (abb->fail == UT64_MAX && abb->jump != target) { @@ -890,27 +885,23 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry } else if (abb->fail != target && abb->jump != target) { RZ_LOG_WARN("The basic block at 0x%" PFMT64x " has more than two outgoing edges.\n" "\t\tHas jump = 0x%llx fail = 0x%llx. Will miss = 0x%llx (%d)\n", - xref->bb_addr, abb->jump, abb->fail, + bb->addr, abb->jump, abb->fail, target, type); } - rz_analysis_xrefs_set(analysis, xref->from, xref->to, RZ_ANALYSIS_XREF_TYPE_CODE); break; } - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET: - rz_analysis_xrefs_set(analysis, xref->from, xref->to, RZ_ANALYSIS_XREF_TYPE_CODE); - break; - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL: - rz_analysis_xrefs_set(analysis, xref->from, xref->to, RZ_ANALYSIS_XREF_TYPE_CALL); - break; - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_RETURN: - rz_analysis_xrefs_set(analysis, xref->from, xref->to, RZ_ANALYSIS_XREF_TYPE_RETURN); - break; } } rz_iterator_free(out_edges); } + rz_iterator_free(iter); + + RzAnalysisXRef *xref; + rz_vector_foreach (inquiry->xrefs, xref) { + rz_analysis_xrefs_set(analysis, xref->from, xref->to, xref->type); + } - // // Convert the Inquiry functions to analysis function. + // Convert the Inquiry functions to analysis function. void **it; rz_pvector_foreach (fcns, it) { RzInquiryFunction *fcn = *it; From 07fe2416c70d0cea8dd3b8d3604039f8a057f240 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 22 Apr 2026 21:07:41 +0200 Subject: [PATCH 264/334] Add API to get dotgraph of cfg. --- librz/include/rz_inquiry/rz_bb_graph.h | 1 + librz/inquiry/bb_cfg.c | 28 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/librz/include/rz_inquiry/rz_bb_graph.h b/librz/include/rz_inquiry/rz_bb_graph.h index 9af689b1ed7..9e9acb2e6e6 100644 --- a/librz/include/rz_inquiry/rz_bb_graph.h +++ b/librz/include/rz_inquiry/rz_bb_graph.h @@ -70,5 +70,6 @@ RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_outgoing_edg RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_incoming_edges(const RzInquiryBBCFG *cfg, ut64 bb_addr); RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg); +RZ_IPI RZ_OWN char *rz_inquiry_bb_cfg_as_dot(const RzInquiryBBCFG *bb_cfg, RZ_NULLABLE const char *name); #endif // RZ_INQUIRY_BB_GRAPH_H diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index b325858f0ec..958c7e509d2 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -13,6 +13,34 @@ static ut64 hash_node(const void *data) { return bb->addr; } +static RZ_OWN char *node_formatter(const RzGraphNode *n) { + return rz_str_newf("[label=\"0x%" PFMT64x "\"]", rz_graph_node_get_id(n)); +} + +static RZ_OWN char *edge_formatter(const RzGraphEdge *e) { + RzInquiryBBCFGEdgeType type = (utptr)rz_graph_edge_get_data(e); + switch (type) { + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE: + return rz_str_dup("[label=Unknown]"); + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP: + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF: + return NULL; + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET: + return rz_str_dup("[style=dotted label=npc]"); + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL: + return rz_str_dup("[style=dotted label=call]"); + case RZ_INQUIRY_BB_CFG_EDGE_TYPE_RETURN: + return rz_str_dup("[style=dotted label=ret]"); + } + rz_warn_if_reached(); + return NULL; +} + +RZ_IPI RZ_OWN char *rz_inquiry_bb_cfg_as_dot(const RzInquiryBBCFG *bb_cfg, RZ_NULLABLE const char *name) { + rz_return_val_if_fail(bb_cfg, NULL); + return rz_graph_as_dot_str(bb_cfg->graph, name, node_formatter, edge_formatter); +} + RZ_IPI RZ_OWN RzInquiryBBCFG *rz_inquiry_bb_cfg_new(RzGraphImplType impl_type) { RzInquiryBBCFG *bb_cfg = RZ_NEW0(RzInquiryBBCFG); if (!bb_cfg) { From 66b684e7aee90e618ce985a520b8b8d840ca2a77 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 27 Apr 2026 14:12:50 +0200 Subject: [PATCH 265/334] Let the Interpreter fill out the whole branch object for request. Not just the address. --- librz/include/rz_inquiry/rz_interpreter.h | 9 +++- librz/inquiry/interpreter/interpreter.c | 6 +-- .../interpreter/p/interpreter_prototype.c | 42 +++++++++---------- librz/inquiry/interpreter/prototype/eval.c | 31 ++++++++++++-- librz/inquiry/interpreter/prototype/eval.h | 4 ++ .../interpreter/prototype/eval_effect.c | 20 +++------ 6 files changed, 68 insertions(+), 44 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 49c8d892ebd..5b7f574e6be 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -69,7 +69,12 @@ typedef struct { } RzInterpreterAbstrVal; typedef struct { - ut64 branching_bb_addr; ///< The address of the bb which branches. + /** + * \brief The address of the bb which branches. + * This might be UT64_MAX, if there was no jump that branch occurred on. + * For example, if the interpreter was just initialized. + */ + ut64 branching_bb_addr; ut64 target_addr; ///< The target address it branches to. /** * \brief Set after a attempted jump to an ignored code region. @@ -196,7 +201,7 @@ typedef struct { * True otherwise. */ bool (*successors)(RZ_NONNULL const RzInterpreterAbstrState *state, - RZ_NONNULL RZ_OUT RzVector /**/ *successors, + RZ_NONNULL RZ_OUT RzVector /**/ *successors, void *plugin_data); /** diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 2b200048351..34765437673 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -351,14 +351,14 @@ static bool choose_next_pc(RzInterpreterSet *iset, // Request the successor effects over the queue. while (!rz_vector_empty(tmp_succ_addr)) { RzInterpreterBranch branch = { 0 }; - rz_vector_pop_front(tmp_succ_addr, &branch.target_addr); + rz_vector_pop_front(tmp_succ_addr, &branch); if (branch.target_addr == UT64_MAX || branch.target_addr == 0) { RZ_LOG_DEBUG("interpreter: Quit due to invalid PC.\n"); // Obviously wrong address. return false; } branch.branching_bb_addr = il_bb->bb_addr; - if (jumps_to_ignored_code(iset->ignored_code, branch.target_addr)) { + if (jumps_to_ignored_code(iset->ignored_code, branch.target_addr) && !branch.alt_target) { RZ_LOG_DEBUG("interpreter: tried to jump to ignored code region at 0x%" PFMT64x "\n", branch.target_addr); // Ignored code is mostly dynamically linked functions. // Skip to the next following address after the jump. @@ -395,7 +395,7 @@ static bool reset_intrpr_state( return false; } - *tmp_succ_addr = rz_vector_new(sizeof(ut64), NULL, NULL); + *tmp_succ_addr = rz_vector_new(sizeof(RzInterpreterBranch), NULL, NULL); *succ_states = rz_vector_new(sizeof(SuccessorState), NULL, NULL); *reachable_states = rz_set_u_new(); if (!tmp_succ_addr || !succ_states || !reachable_states) { diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index f5ff55d3c15..7fa697f9443 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -42,53 +42,51 @@ static bool eval(RZ_NONNULL RzInterpreterSet *iset, // Now execute the actual effects of the BB. void **it; rz_pvector_foreach (il_bb->il_ops, it) { - ut64 pc = rz_bv_to_ut64(AD(iset->astate->pc->abstr_data)->bv); + ProtoIntrprAbstrData *apc = AD(iset->astate->pc->abstr_data); + ut64 pc = rz_bv_to_ut64(apc->bv); RZ_LOG_DEBUG("Eval PC = 0x%" PFMT64x "\n", pc); RzInterpreterInsnPkt *pkt = *it; if (!interpreter_prototype_eval_effect(iset, pkt->effect, pkt->insn_pkt_size, plugin_data)) { return false; } - if (pc == rz_bv_to_ut64(AD(iset->astate->pc->abstr_data)->bv) && AD(iset->astate->pc->abstr_data)->is_concrete) { + if (pc == rz_bv_to_ut64(apc->bv) && apc->is_concrete) { // Instruction did not manipulate the PC. Set it to the next instruction (packet). set_pc(iset->astate, pc + pkt->insn_pkt_size, plugin_data); } } - // TODO: Clean up local variables. - // Or maybe not? Just costs performance. And the uplifted instructions should - // always set it before reading, otherwise the tests wouldn't pass. return true; } bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, - RZ_NONNULL RZ_OUT RzVector /**/ *successors, + RZ_NONNULL RZ_OUT RzVector /**/ *successors, void *plugin_data) { rz_return_val_if_fail(state && successors, false); - - RzInterpreterAbstrVal *pc = state->pc; - if (!pc || !pc->abstr_data) { - RZ_LOG_ERROR("No PC found.\n"); - return false; - } - ProtoIntrprAbstrData *adata = pc->abstr_data; - if (!adata->is_concrete) { + ProtoIntrprPluginData *pdata = plugin_data; + ProtoIntrprAbstrData *apc = state->pc->abstr_data; + if (!apc->is_concrete) { // The PC is not a concrete value. // This prototype can't estimate a reasonable concretization for it. return true; } - if (rz_bv_len(adata->bv) > 64) { + if (rz_bv_len(apc->bv) > 64) { RZ_LOG_WARN("PC has a length of more than 64 bits!\n"); return true; } - ut64 next_pc = rz_bv_to_ut64(adata->bv); - rz_vector_push(successors, &next_pc); + ut64 next_pc = rz_bv_to_ut64(apc->bv); + RzInterpreterBranch branch = { 0 }; + branch.target_addr = next_pc; + branch.branching_bb_addr = pdata->prev_pc; + rz_vector_push(successors, &branch); return true; } static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data) { state->pc->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); - AD(state->pc->abstr_data)->bv = rz_bv_new_from_ut64(state->il_config->mem_key_size, 0); - AD(state->pc->abstr_data)->is_concrete = true; + ProtoIntrprAbstrData *apc = AD(state->pc->abstr_data); + apc->bv = rz_bv_new_from_ut64(state->il_config->mem_key_size, 0); + apc->is_concrete = true; + RzIterator *it = ht_up_as_iter_keys(state->globals); ut64 *k; rz_iterator_foreach(it, k) { @@ -123,8 +121,9 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da } static bool reset_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_point, void *plugin_data) { - rz_bv_set_from_ut64(AD(state->pc->abstr_data)->bv, entry_point); - AD(state->pc->abstr_data)->is_concrete = true; + ProtoIntrprAbstrData *apc = AD(state->pc->abstr_data); + rz_bv_set_from_ut64(apc->bv, entry_point); + apc->is_concrete = true; RzIterator *it = ht_up_as_iter_keys(state->globals); ut64 *k; @@ -286,6 +285,7 @@ bool reset(void *plugin_data) { } RZ_LOG_DEBUG("prototype: reset()\n"); ProtoIntrprPluginData *pdata = plugin_data; + pdata->prev_pc = UT64_MAX; ht_uu_clear(pdata->bb_invocation_count); memset(&pdata->call_cand, 0, sizeof(RzAnalysisCallCandidate)); rz_vector_purge(&pdata->stack); diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index edeb526bac5..d2f9916a51a 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -219,14 +219,37 @@ bool load_abstr_data( return true; } +bool set_abstr_pc(RzInterpreterAbstrState *state, ProtoIntrprAbstrData *pc, + void *plugin_data) { + rz_return_val_if_fail(state && pc, false); + ProtoIntrprPluginData *pdata = plugin_data; + ProtoIntrprAbstrData *apc = AD(state->pc->abstr_data); + if (!apc->is_concrete || rz_bv_len(apc->bv) > 64) { + pdata->prev_pc = UT64_MAX; + } else { + pdata->prev_pc = rz_bv_to_ut64(apc->bv); + } + copy_abstr_data(state->pc->abstr_data, pc); + RZ_LOG_DEBUG("Prototype: set_abstr_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", + pdata->prev_pc, rz_bv_to_ut64(apc->bv), apc->is_concrete ? "Concrete" : "Abstract"); + return true; +} + bool set_pc(RzInterpreterAbstrState *state, ut64 pc, void *plugin_data) { rz_return_val_if_fail(state, false); - AD(state->pc->abstr_data)->is_concrete = true; + ProtoIntrprPluginData *pdata = plugin_data; + ProtoIntrprAbstrData *apc = AD(state->pc->abstr_data); + if (!apc->is_concrete || rz_bv_len(apc->bv) > 64) { + pdata->prev_pc = UT64_MAX; + } else { + pdata->prev_pc = rz_bv_to_ut64(apc->bv); + } + + apc->is_concrete = true; RZ_LOG_DEBUG("Prototype: set_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (Concrete)\n", - rz_bv_to_ut64(AD(state->pc->abstr_data)->bv), - pc); - return rz_bv_set_from_ut64(AD(state->pc->abstr_data)->bv, pc); + rz_bv_to_ut64(apc->bv), pc); + return rz_bv_set_from_ut64(apc->bv, pc); } void stack_frame_fini(ProtoInterprAbstrStackFrame *frame, void *unused) { diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 6208b47b674..472696de522 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -55,6 +55,7 @@ typedef struct { HtUU *bb_invocation_count; RzAnalysisCallCandidate call_cand; ///< Data of a call candidate. RzVector /**/ stack; ///< The call frame stack. + ut64 prev_pc; ///< Previous PC. Set to UT64_MAX if it was invalid. } ProtoIntrprPluginData; /** @@ -130,6 +131,9 @@ bool report_yield_call_candiate( bool set_pc(RzInterpreterAbstrState *state, ut64 pc, void *plugin_data); +bool set_abstr_pc(RzInterpreterAbstrState *state, ProtoIntrprAbstrData *pc, + void *plugin_data); + void stack_frame_fini(ProtoInterprAbstrStackFrame *frame, void *unused); void stack_frame_push(ProtoIntrprPluginData *pdata, RzBitVector *entry_point, RzBitVector *return_addr, ut64 instance); void stack_frame_pop(ProtoIntrprPluginData *pdata, RZ_NULLABLE ProtoInterprAbstrStackFrame *frame); diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 4471190e0bd..cc315940e5f 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -32,25 +32,17 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterSet *iset, // This plugin has no addition for it defined. break; } - STACK_ABSTR_DATA_OUT(inc); + STACK_ABSTR_DATA_OUT(npc); // First cast the bitvector, then set it. // This is performance critical. Since the stack allocated bv is >64 bit // the rz_bv_set_from_ut64() will set its whole memory, eating a lot of runtime. // If we cast before, it is simply an assignment to bv->small_bits. - rz_bv_cast_inplace(inc.bv, rz_bv_len(pc->bv), false); - rz_bv_set_from_ut64(inc.bv, insn_pkt_size); -#if RZ_BUILD_DEBUG - ut64 old_pc = rz_bv_to_ut64(pc->bv); -#endif - if (!rz_bv_add_inplace(pc->bv, inc.bv, NULL)) { + rz_bv_cast_inplace(npc.bv, rz_bv_len(pc->bv), false); + rz_bv_set_from_ut64(npc.bv, insn_pkt_size); + if (!rz_bv_add_inplace(npc.bv, pc->bv, NULL)) { goto error; } -#if RZ_BUILD_DEBUG - RZ_LOG_DEBUG("Prototype: NOP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", - old_pc, - rz_bv_to_ut64(pc->bv), - pc->is_concrete ? "Concrete" : "Abstract"); -#endif + set_abstr_pc(iset->astate, &npc, plugin_data); break; } case RZ_IL_OP_SEQ: { @@ -126,7 +118,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterSet *iset, // Setting the PC to a bottom value is allowed here! // The successor function will handle this case. - copy_abstr_data(iset->astate->pc->abstr_data, &eval_out); + set_abstr_pc(iset->astate, &eval_out, plugin_data); break; } case RZ_IL_OP_BRANCH: { From 9cde4a19cfb6c4c1d9fd73b560f7d3fa7dfd5ab4 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 29 Apr 2026 12:03:11 +0200 Subject: [PATCH 266/334] IL Cache implementation. --- doc/abstract_interpreter/state_diagrams.svg | 5 +- librz/arch/xrefs.c | 12 +- librz/include/rz_analysis.h | 3 +- librz/include/rz_inquiry.h | 5 +- librz/include/rz_inquiry/rz_bb_graph.h | 3 +- librz/include/rz_inquiry/rz_il_cache.h | 90 ++++ librz/include/rz_inquiry/rz_interpreter.h | 81 +-- .../inquiry/algorithms/revng_fcn_detection.c | 2 - librz/inquiry/bb_cfg.c | 16 +- librz/inquiry/il_cache.c | 473 ++++++++++++++++++ librz/inquiry/inquiry.c | 393 ++++++--------- librz/inquiry/inquiry_helpers.c | 94 ---- librz/inquiry/interpreter/interpreter.c | 149 +++--- .../interpreter/p/interpreter_prototype.c | 20 +- librz/inquiry/meson.build | 2 +- 15 files changed, 871 insertions(+), 477 deletions(-) create mode 100644 librz/include/rz_inquiry/rz_il_cache.h create mode 100644 librz/inquiry/il_cache.c delete mode 100644 librz/inquiry/inquiry_helpers.c diff --git a/doc/abstract_interpreter/state_diagrams.svg b/doc/abstract_interpreter/state_diagrams.svg index 9aaca00990e..0c00768e4b5 100644 --- a/doc/abstract_interpreter/state_diagrams.svg +++ b/doc/abstract_interpreter/state_diagrams.svg @@ -186,8 +186,7 @@ init - B I - basic block + B I - Get entry point @@ -198,7 +197,7 @@ B II memory B III - basic block + basic block diff --git a/librz/arch/xrefs.c b/librz/arch/xrefs.c index 15aebf42cac..38550a56702 100644 --- a/librz/arch/xrefs.c +++ b/librz/arch/xrefs.c @@ -348,8 +348,7 @@ static bool read_up_to(RzAnalysis *analysis, ut64 addr, ut8 *buf, size_t buf_siz RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, const RzPVector /**/ *sections, bool include_call_return_pts, - RZ_NONNULL RZ_OUT RzSetU *branch_targets, - RZ_NONNULL RZ_OUT RzVector /**/ *insn_to_insn_edges) { + RZ_NONNULL RZ_OUT RzSetU *branch_targets) { rz_return_val_if_fail(analysis && analysis->cur && sections && branch_targets, false); size_t buf_size = (analysis->cur->bits / 8) * 16; ut8 *buf = RZ_NEWS0(ut8, buf_size); @@ -381,16 +380,9 @@ RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, bool op_is_jump = rz_analysis_op_is_direct_jump(&op); if ((op_is_call || op_is_jump) && op.jump != UT64_MAX) { rz_set_u_add(branch_targets, op.jump); - - RzAnalysisXRef edge = { .from = addr, .to = op.jump }; - edge.type = op_is_call ? RZ_ANALYSIS_XREF_TYPE_CALL : RZ_ANALYSIS_XREF_TYPE_CODE; - rz_vector_push(insn_to_insn_edges, &edge); - if (op.fail != UT64_MAX) { if (op_is_jump) { - RzAnalysisXRef edge = { .from = addr, .to = op.fail, .type = RZ_ANALYSIS_XREF_TYPE_CODE }; rz_set_u_add(branch_targets, op.fail); - rz_vector_push(insn_to_insn_edges, &edge); } } } @@ -398,8 +390,6 @@ RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, // If it is a call, also add the following instruction as reference. // Because it is likely a return point. rz_set_u_add(branch_targets, op.addr + op.size); - RzAnalysisXRef edge = { .from = addr, .to = op.fail, .type = RZ_ANALYSIS_XREF_TYPE_CALL_RET }; - rz_vector_push(insn_to_insn_edges, &edge); } addr += op.size; rz_analysis_op_fini(&op); diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index c871f68f290..8233aef8190 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -1644,8 +1644,7 @@ RZ_API bool rz_analysis_xref_del(RzAnalysis *analysis, ut64 from, ut64 to); RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, const RzPVector /**/ *sections, bool include_call_return_pts, - RZ_NONNULL RZ_OUT RzSetU *branch_targets, - RZ_NONNULL RZ_OUT RzVector /**/ *edges); + RZ_NONNULL RZ_OUT RzSetU *branch_targets); /* var.c */ RZ_API RZ_BORROW RzAnalysisVar *rz_analysis_function_set_var( diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index c27b7dfda7b..671738b6257 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -13,6 +13,7 @@ extern "C" { #endif +#include #include #include @@ -34,7 +35,7 @@ typedef struct { HtSP /**/ *plugins_data; HtUP /**/ *call_candidates; ///< Indexed by address of basic block with the call candidate. - RzVector /**/ *xrefs; ///< All xrefs the interpreter detected. + RzVector /**/ *dynamic_xrefs; ///< All xrefs the interpreter detected. RzInquiryBBCFG *bb_cfg; ///< The control flow graph all the basic blocks build. } RzInquiry; @@ -48,8 +49,6 @@ RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NO RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void); RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *q); -RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr); - RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzPVector /**/ *allowed_segments); RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzSetU /**/ *entry_points, RZ_NONNULL const RzVector /**/ *ignored_code); diff --git a/librz/include/rz_inquiry/rz_bb_graph.h b/librz/include/rz_inquiry/rz_bb_graph.h index 9e9acb2e6e6..ad53f32f6c4 100644 --- a/librz/include/rz_inquiry/rz_bb_graph.h +++ b/librz/include/rz_inquiry/rz_bb_graph.h @@ -62,7 +62,8 @@ RZ_IPI void rz_inquiry_bb_free(RZ_NULLABLE RZ_OWN RzInquiryBB *bb); RZ_IPI RZ_OWN RzInquiryBBCFG *rz_inquiry_bb_cfg_new(RzGraphImplType impl_type); RZ_IPI void rz_inquiry_bb_cfg_free(RZ_NULLABLE RZ_OWN RzInquiryBBCFG *bb_cfg); RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size); -RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, RzVector /**/ *xrefs); +RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, const RzVector /**/ *xrefs); + RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RzInquiryBB *bb); RZ_IPI bool rz_inquiry_bb_cfg_del_out_edges(RzInquiryBBCFG *cfg, ut64 bb_addr); RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBBCFGEdgeType type); diff --git a/librz/include/rz_inquiry/rz_il_cache.h b/librz/include/rz_inquiry/rz_il_cache.h new file mode 100644 index 00000000000..b971f17b040 --- /dev/null +++ b/librz/include/rz_inquiry/rz_il_cache.h @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2026 Rot127 +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef RZ_INQUIRY_IL_CACHE_H +#define RZ_INQUIRY_IL_CACHE_H + +#include "rz_th.h" +#include "rz_types.h" +#include +#include + +/** + * \brief This value is pushed over the IL ops queue if the requested + * IL op failed to be lifted. + */ +#define RZ_IL_CACHE_FAILED_LIFTING_PTR ((void *)(utptr)UT64_MAX) + +typedef struct rz_il_cache_t RzILCache; + +typedef struct { + RzILOpEffect *effect; ///< Vector with the IL ops of an instruction packets. + size_t insn_pkt_size; ///< The size of the instruction packet. Used to increment the PC if no JMP occurred. +} RzILCacheInsnPkt; + +/** + * \brief A sequence of instructions of which the last instruction is always + * a branch or a terminating instruction. + */ +typedef struct { + RzPVector /**/ *il_ops; ///< The sequence of IL operations of this block. + size_t size; ///< The number of bytes the instructions of the block cover in memory. + ut64 addr; ///< The address where the block starts. +} RzILCacheBlock; + +typedef enum { + RZ_IL_CACHE_CONFIG_DEFAULT = 0, + /** + * \brief The IL cache will replace un-lifted instructions + * with a NOP. + */ + RZ_IL_CACHE_CONFIG_NOP_UNLIFTED = 1 << 0, + /** + * \brief The IL cache will lift instructions only on request. + * If unset, it will lift all instructions in all executable maps + * on initialization. + */ + RZ_IL_CACHE_CONFIG_LIFT_ON_REQUEST = 1 << 1, + + // TODO: Sleep times are not really a useful setting. + // They are just here for experiments. + + /** + * \brief If set, the cache doesn't sleep in its serve loop. + * It continuously checks for requests. + */ + RZ_IL_CACHE_CONFIG_NO_SLEEP = 1 << 2, + /** + * \brief If set, the cache sleeps a short time between checks for requests. + */ + RZ_IL_CACHE_CONFIG_SLEEP_SHORT = 1 << 3, + /** + * \brief If set, the cache sleeps a relatively long time between checks for requests. + */ + RZ_IL_CACHE_CONFIG_SLEEP_LONG = RZ_IL_CACHE_CONFIG_NO_SLEEP | RZ_IL_CACHE_CONFIG_SLEEP_SHORT, +} RzILCacheConfig; + +RZ_API RZ_OWN char *rz_il_cache_block_str(RZ_NONNULL const RzILCacheBlock *block); + +RZ_API RZ_OWN RzILCache *rz_il_cache_new( + RZ_BORROW RZ_NONNULL RzAnalysis *analysis, + RZ_BORROW RZ_NONNULL RzIO *io, + RZ_OWN RzPVector /**/ *bin_sections, + RzILCacheConfig config); +RZ_API void rz_il_cache_free(RZ_OWN RZ_NULLABLE RzILCache *cache); +RZ_API void rz_il_cache_close(RZ_BORROW RZ_NONNULL RzILCache *cache); +RZ_API void rz_il_cache_stop_serving(RZ_BORROW RZ_NONNULL RzILCache *cache); +RZ_API const RzVector /**/ *rz_il_cache_get_static_xrefs(const RzILCache *cache); +RZ_API RzIterator /**/ *rz_il_cache_get_blocks(const RzILCache *cache); + +RZ_API bool rz_il_cache_get_new_ring_buf( + RZ_BORROW RzILCache *cache, + RZ_NONNULL RZ_BORROW RZ_OUT RzThreadRingBuf **req_rbuf, + RZ_NONNULL RZ_BORROW RZ_OUT RzThreadQueue **il_queue); +RZ_API bool rz_il_cache_was_requested( + RZ_BORROW RzILCache *cache, + ut64 addr); +RZ_API bool rz_il_cache_serve(RZ_NONNULL RzILCache *cache); +RZ_API RZ_OWN RzILCacheBlock *rz_il_cache_lift_il_block(const RzILCache *cache, ut64 addr); + +#endif // RZ_INQUIRY_IL_CACHE_H diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 5b7f574e6be..8530ff83058 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -9,7 +9,8 @@ #ifndef RZ_INTERPRETER #define RZ_INTERPRETER -#include "rz_util/rz_bitvector.h" +#include "rz_inquiry/rz_bb_graph.h" +#include #include #include #include @@ -19,10 +20,9 @@ /** * \brief Only one IO request at a time is possible (currently). */ -#define RZ_INTERPRETER_IO_RBUF_SIZE 128 -#define RZ_INTERPRETER_IL_QUEUE_SIZE 128 -#define RZ_INTERPRETER_ADDR_RBUF_SIZE 1024 -#define RZ_INTERPRETER_YIELD_RBUF_SIZE 128 +#define RZ_INTERPRETER_IO_RBUF_SIZE 128 +#define RZ_INTERPRETER_YIELD_RBUF_SIZE 128 +#define RZ_INTERPRETER_ENTRY_POINTS_RBUF_SIZE 4 typedef struct rz_intp_run_state RzIntpRunState; @@ -69,23 +69,38 @@ typedef struct { } RzInterpreterAbstrVal; typedef struct { + RzInquiryBBCFGEdgeType cf_type; ///< Control flow type. /** - * \brief The address of the bb which branches. - * This might be UT64_MAX, if there was no jump that branch occurred on. - * For example, if the interpreter was just initialized. + * \brief The address of the block which changes the control flow. + * This might be UT64_MAX, if there was no jump that flow originated from. + * If the interpreter was just initialized, for example. */ - ut64 branching_bb_addr; - ut64 target_addr; ///< The target address it branches to. + ut64 src_block_addr; + /** + * \brief The original target address a block branches to. + */ + ut64 target_addr; /** * \brief Set after a attempted jump to an ignored code region. - * This is almost always a call to an imported symbol. - * Instead of using the target_addr above, the il_cache should serve the alt_target_addr. - * target_addr should still be marked as potential cfep. - * + * This is almost always a call to an unloaded imported symbol. * 0 is an invalid address here. */ ut64 alt_target; -} RzInterpreterBranch; + /** + * \brief Equal to alt_target if alt_target != 0. + * Otherwise equal to target_addr + */ + ut64 actual_target; + /** + * \brief Number of bytes of the destination code block. + * Only set after the IL block was provided by the cache. + */ + size_t target_block_size; + /** + * \brief Control flow type. + */ + RzInquiryBBCFGEdgeType type; +} RzInterpreterCtrlFlow; typedef struct { RzInterpreterAbstraction kinds; ///< The abstractions of the state. @@ -114,6 +129,12 @@ typedef enum { * it is a strong indicator that the jump is a call and the next address a return point. */ RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, + + /** + * \brief Yield is a RzInterpreterCtrlFlow. + * Reported by every interpreter. + */ + RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW, RZ_INTERPRETER_YIELD_KIND_NUM, } RzInterpreterYieldKind; @@ -137,17 +158,6 @@ typedef struct { RzThreadRingBuf *rbuf; } RzInterpreterYieldRBuf; -typedef struct { - RzPVector *il_ops; ///< The sequence of IL operations of this basic block. - size_t size; ///< The number of bytes the basic block has. - ut64 bb_addr; ///< The address where the basic block starts. -} RzInterpreterILBB; - -typedef struct { - RzILOpEffect *effect; ///< Vector with all instruction packets of a basic block. - size_t insn_pkt_size; ///< The size of the instruction packet. Used to increment the PC if no JMP occurred. -} RzInterpreterInsnPkt; - typedef struct rz_interpreter_set RzInterpreterSet; typedef struct { @@ -192,7 +202,7 @@ typedef struct { * \brief Evaluates an effect with the mutable state. */ bool (*eval)(RZ_NONNULL RzInterpreterSet *iset, - RZ_NONNULL const RzInterpreterILBB *il_bb, + RZ_NONNULL const RzILCacheBlock *il_bb, void *plugin_data); /** * \brief Determines the next successor addresses from state. @@ -254,10 +264,18 @@ struct rz_interpreter_set { */ RzThreadSemaphore *run_state_sync; + /** + * \brief Entry points the interpreter starts interpreting from. + */ + RzThreadRingBuf *entry_points; + RzAnalysisILVM *il_vm; ///< The RzAnalysisILVM for memory IO. - RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. - RzThreadRingBuf /**/ *branch_rbuf; ///< The ring buffer to send requests to the cache what address to get the next IL op from. + RZ_LIFETIME(RzILCache) + RZ_BORROW RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. + RZ_LIFETIME(RzILCache) + RZ_BORROW RzThreadRingBuf /**/ *il_request_rbuf; ///< The ring buffer to send requests to the cache what address to get the next IL op from. + RzThreadRingBuf /**/ *io_request_rbuf; ///< The ring buffer for read/write requests to the IO layer. RzThreadRingBuf /**/ *io_result_rbuf; ///< The ring buffer for the read/write requests' answers. @@ -288,9 +306,6 @@ RZ_API const char *rz_intp_run_state_flag_str(RzIntpRunStateFlag flag); RZ_IPI void rz_intp_run_state_set(RZ_BORROW RZ_NONNULL RzIntpRunState *state, RzIntpRunStateFlag flag); -RZ_API void rz_interpreter_il_bb_free(RZ_NULLABLE RZ_OWN RzInterpreterILBB *il_bb); -RZ_API void rz_interpreter_insn_pkt_free(RZ_NULLABLE RZ_OWN RzInterpreterInsnPkt *pkt); - RZ_API void rz_interpreter_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldRBuf *yield_rbuf); RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( @@ -308,6 +323,8 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RzAnalysis *analysis, RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, RzInterpreterAbstraction abstraction, + RZ_NONNULL RZ_BORROW RzThreadRingBuf *il_request_rbuf, + RZ_NONNULL RZ_BORROW RzThreadQueue *il_queue, RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM], RZ_NONNULL const RzVector /**/ *ignored_code); RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset); diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index 0583ad08032..bdd933af220 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -132,8 +132,6 @@ static void recurse_into_fcn_bbs( ut64 succ_addr = rz_graph_node_get_id(rz_graph_edge_get_to(e)); switch (etype) { case RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE: - rz_warn_if_reached(); - goto err_return; case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF: case RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP: // Just an edge between two basic blocks. diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index 958c7e509d2..8baca2ef27c 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -88,7 +88,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 t static bool is_cf_edge(const RzGraphEdge *e, void *unused) { RzInquiryBBCFGEdgeType type = (utptr)rz_graph_edge_get_data(e); - return type == RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF; + return type == RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF || type == RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE; } /** @@ -108,16 +108,18 @@ RZ_IPI bool rz_inquiry_bb_cfg_update_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut6 return rz_graph_update_edge_by_id(cfg->graph, from_bb, to_bb, RZ_GRAPH_INT_AS_DATA(type), is_cf_edge, NULL); } -RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RzInquiryBB *bb) { - rz_return_val_if_fail(cfg && bb, false); +RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RZ_NULLABLE RzInquiryBB *bb) { + rz_return_val_if_fail(cfg, false); const RzGraphNode *n = rz_graph_find_node(cfg->graph, bb_addr); if (!n) { RZ_LOG_WARN("Could not find BB at 0x%" PFMT64x "\n", bb_addr); return false; } - const RzInquiryBB *n_data = rz_graph_node_get_data(n); - bb->addr = n_data->addr; - bb->size = n_data->size; + if (bb) { + const RzInquiryBB *n_data = rz_graph_node_get_data(n); + bb->addr = n_data->addr; + bb->size = n_data->size; + } return true; } @@ -163,7 +165,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut return true; } -RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, RzVector /**/ *xrefs) { +RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, const RzVector /**/ *xrefs) { RzAnalysisXRef *xref; rz_vector_foreach (xrefs, xref) { switch (xref->type) { diff --git a/librz/inquiry/il_cache.c b/librz/inquiry/il_cache.c new file mode 100644 index 00000000000..64978868def --- /dev/null +++ b/librz/inquiry/il_cache.c @@ -0,0 +1,473 @@ +// SPDX-FileCopyrightText: 2026 Rot127 +// SPDX-License-Identifier: LGPL-3.0-only + +#include "rz_analysis.h" +#include "rz_il/rz_il_opcodes.h" +#include "rz_skyline.h" +#include "rz_th.h" +#include "rz_types.h" +#include "rz_util/ht_up.h" +#include "rz_util/rz_assert.h" +#include "rz_util/rz_log.h" +#include "rz_util/rz_str.h" +#include "rz_util/rz_sys.h" +#include "rz_vector.h" +#include + +#define RZ_IL_OPS_CACHE_IL_QUEUE_SIZE 128 +#define RZ_IL_OPS_CACHE_ADDR_RBUF_SIZE 1024 + +#define SLEEP_NONE_US 0 +#define SLEEP_SHORT_US (5 * 1000) +#define SLEEP_LONG_US (100 * 1000) + +RZ_API RZ_OWN char *rz_il_cache_block_str(RZ_NONNULL const RzILCacheBlock *block) { + rz_return_val_if_fail(block, NULL); + return rz_str_newf("[0x%" PFMT64x ", 0x%" PFMT64x ")", block->addr, block->addr + block->size); +} + +struct rz_il_cache_t { + RZ_BORROW RzAnalysis *analysis; + RZ_BORROW RzIO *io; + + RzPVector /**/ *bin_sections; + + size_t n_serving; ///< Number of caches serving currently. + RzThreadLock *n_serving_lock; + + RzAtomicBool *is_serving; ///< Flag to signal cache to serve or not to serve IL ops. + + /** + * \brief Statically known control flow edges. + * Collected for each lifted IL block with a static control flow target. + */ + RzVector /**/ *static_xrefs; + + HtUP /**/ *cache; + + // The pair of ring_buffer + queue are always at the same offset. + RzPVector /**/ *req_rbufs; ///< The ring buffers the cache receives IL block requests. + RzPVector /**/ *il_queues; ///< The queues the cache serves IL blocks over. + + RzThreadLock *skyline_lock; + /** + * \brief A skyline (functionally the same as an r-tree) to log the code regions + * the cache served as IL blocks. + */ + RzSkyline *served_regions; + + RzILCacheConfig config; ///< The cache configuration. + + size_t us_sleep; +}; + +static void rz_il_cache_insn_pkt_free(RZ_NULLABLE RZ_OWN RzILCacheInsnPkt *pkt) { + if (!pkt) { + return; + } + if (pkt->effect) { + rz_il_op_effect_free(pkt->effect); + } + free(pkt); +} + +static void rz_il_cache_block_free(RZ_NULLABLE RZ_OWN RzILCacheBlock *il_bb) { + if (!il_bb) { + return; + } + rz_pvector_free(il_bb->il_ops); + free(il_bb); +} + +static RzAnalysisXRefType op_type_to_xref_type(const RzAnalysisOp *op) { + switch (op->type & RZ_ANALYSIS_OP_TYPE_MASK) { + case RZ_ANALYSIS_OP_TYPE_JMP: + case RZ_ANALYSIS_OP_TYPE_UJMP: + case RZ_ANALYSIS_OP_TYPE_RJMP: + case RZ_ANALYSIS_OP_TYPE_IJMP: + case RZ_ANALYSIS_OP_TYPE_IRJMP: + case RZ_ANALYSIS_OP_TYPE_CJMP: + case RZ_ANALYSIS_OP_TYPE_RCJMP: + case RZ_ANALYSIS_OP_TYPE_MJMP: + case RZ_ANALYSIS_OP_TYPE_MCJMP: + case RZ_ANALYSIS_OP_TYPE_UCJMP: + return RZ_ANALYSIS_XREF_TYPE_CODE; + case RZ_ANALYSIS_OP_TYPE_CALL: + case RZ_ANALYSIS_OP_TYPE_UCALL: + case RZ_ANALYSIS_OP_TYPE_RCALL: + case RZ_ANALYSIS_OP_TYPE_ICALL: + case RZ_ANALYSIS_OP_TYPE_IRCALL: + case RZ_ANALYSIS_OP_TYPE_CCALL: + case RZ_ANALYSIS_OP_TYPE_UCCALL: + return RZ_ANALYSIS_XREF_TYPE_CALL; + case RZ_ANALYSIS_OP_TYPE_RET: + case RZ_ANALYSIS_OP_TYPE_CRET: + return RZ_ANALYSIS_XREF_TYPE_RETURN; + case RZ_ANALYSIS_OP_TYPE_ILL: + case RZ_ANALYSIS_OP_TYPE_UNK: + case RZ_ANALYSIS_OP_TYPE_TRAP: + case RZ_ANALYSIS_OP_TYPE_SWI: + case RZ_ANALYSIS_OP_TYPE_CSWI: + case RZ_ANALYSIS_OP_TYPE_LEAVE: + case RZ_ANALYSIS_OP_TYPE_SWITCH: + default: + break; + } + rz_warn_if_reached(); + return RZ_ANALYSIS_XREF_TYPE_NULL; +} + +RZ_API RZ_OWN RzILCacheBlock *rz_il_cache_lift_il_block(const RzILCache *cache, ut64 addr) { + rz_return_val_if_fail(cache && rz_analysis_plugin_current(cache->analysis) && cache->io, NULL); + RzILCacheBlock *il_block = NULL; + RzAnalysisOp op = { 0 }; + rz_analysis_op_init(&op); + // Estimate a reasonable number of bytes to read. + int max_read_size = (rz_analysis_plugin_current(cache->analysis)->bits / 8) * 16; + ut8 *buf = RZ_NEWS0(ut8, max_read_size); + if (!max_read_size || !buf) { + rz_warn_if_reached(); + goto fail; + } + + il_block = RZ_NEW0(RzILCacheBlock); + if (!il_block) { + rz_warn_if_reached(); + goto fail; + } + il_block->il_ops = rz_pvector_new((RzPVectorFree)rz_il_cache_insn_pkt_free); + if (!il_block->il_ops) { + rz_warn_if_reached(); + goto fail; + } + il_block->addr = addr; + RZ_LOG_DEBUG("ILCache: Gen BB:\n"); + bool sparc_add_delayed_insn = false; + bool changes_cf = true; + do { + // Don't use rz_io_read_at_mapped() here. + // It fails if it reads beyond a mapped memory region. + // Although this is expected here. rz_io_nread_at() on the other hand just + // reads less bytes. + if (rz_io_nread_at(cache->io, addr, buf, max_read_size) == 0) { + goto fail; + } + if (rz_analysis_op(cache->analysis, &op, addr, buf, max_read_size, + RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_INSN_PKT) <= 0) { + RZ_LOG_ERROR("Failed to decode IL op\n"); + goto fail; + } + bool lifted = true; + if (!op.il_op && cache->config & RZ_IL_CACHE_CONFIG_NOP_UNLIFTED) { + // Not lifted. Map to NOP + lifted = false; + op.il_op = rz_il_op_new_nop(); + } + RzILCacheInsnPkt *pkt = RZ_NEW0(RzILCacheInsnPkt); + pkt->effect = op.il_op; + // Take ownership of IL op pointer. + op.il_op = NULL; + pkt->insn_pkt_size = op.size; + il_block->size += op.size; + rz_pvector_push(il_block->il_ops, pkt); + + if (lifted) { + changes_cf = rz_analysis_op_changes_control_flow(&op); + } + + if (changes_cf && op.jump != UT64_MAX && cache->static_xrefs) { + // The op.jump/op.fail says nothing about the + // type of the control flow. + // This one has to be deduced by the op type. + // Really annoying and imprecise, but so be it. + // The op.fail is to my knowledge always a "normal" control flow + // as in "go to next instruction if branch condition fails". + RzAnalysisXRef xref = { + .bb_addr = il_block->addr, + .from = op.addr, + .to = op.jump, + .type = op_type_to_xref_type(&op) + }; + rz_vector_push(cache->static_xrefs, &xref); + if (op.fail != UT64_MAX) { + xref.to = op.fail; + xref.type = RZ_ANALYSIS_XREF_TYPE_CODE; + rz_vector_push(cache->static_xrefs, &xref); + } + } + + RZ_LOG_DEBUG("ILCache: \t0x%" PFMT64x "\n", addr); + addr += op.size; + rz_analysis_op_fini(&op); + rz_mem_memzero(buf, max_read_size); + if (sparc_add_delayed_insn) { + // Instruction was added, now the BB is complete. + break; + } + if (changes_cf && RZ_STR_EQ(rz_analysis_plugin_current(cache->analysis)->arch, "sparc")) { + // We need to add the instruction after the branch. + // So one more iteration is needed. + sparc_add_delayed_insn = true; + changes_cf = false; + } + } while (!changes_cf); + + free(buf); + return il_block; + +fail: + free(buf); + rz_analysis_op_fini(&op); + rz_il_cache_block_free(il_block); + return NULL; +} + +static const RzILCacheBlock *lift_il_block(RzILCache *cache, ut64 addr) { + RzILCacheBlock *block = ht_up_find(cache->cache, addr, NULL); + if (block) { + char *bstr = rz_il_cache_block_str(block); + RZ_LOG_DEBUG("ILCache: Serve block %s from cache\n", bstr); + free(bstr); + return block; + } + RZ_LOG_DEBUG("ILCache: Lift new BB\n"); + block = rz_il_cache_lift_il_block(cache, addr); + if (!block) { + RZ_LOG_DEBUG("ILCache: Failed to lift block at 0x%" PFMT64x "\n", addr); + return NULL; + } + ht_up_insert(cache->cache, block->addr, block); + return block; +} + +static bool lift_executable_maps(RzILCache *cache) { + void **it; + rz_pvector_foreach (cache->bin_sections, it) { + RzBinSection *sec = *it; + if (!(sec->perm & RZ_PERM_X)) { + continue; + } + ut64 addr = sec->vaddr; + while (addr < sec->vaddr + sec->vsize) { + const RzILCacheBlock *block = lift_il_block(cache, addr); + if (!block) { + RZ_LOG_WARN("Failed to lift IL block at 0x%" PFMT64x + " - Stop lifting section '%s'\n", + addr, sec->name); + break; + } + addr += block->size; + } + } + return true; +} +RZ_API bool rz_il_cache_serve(RZ_NONNULL RzILCache *cache) { + rz_return_val_if_fail(cache, false); + rz_th_lock_enter(cache->n_serving_lock); + cache->n_serving++; + rz_th_lock_leave(cache->n_serving_lock); + + bool success = true; + + rz_atomic_bool_set(cache->is_serving, true); + size_t clients = rz_pvector_len(cache->req_rbufs); + while (rz_atomic_bool_get(cache->is_serving)) { + for (size_t i = 0; i < clients; ++i) { + RzThreadRingBuf *req_rbuf = rz_pvector_at(cache->req_rbufs, i); + RzThreadQueue *serve_queue = rz_pvector_at(cache->il_queues, i); + + // TODO: This unsafe check permits race conditions. + // Not sure if it improve performance here. + // Needs more benchmarks. + if (rz_th_ring_buf_is_empty_unsafe(req_rbuf)) { + continue; + } + + ut64 req_addr = 0; + RzThreadRingBufResult r = rz_th_ring_buf_take(req_rbuf, &req_addr); + if (r == RZ_THREAD_RING_BUF_CLOSED) { + goto stop_serving; + } else if (r == RZ_THREAD_RING_BUF_OK) { + const RzILCacheBlock *block = lift_il_block(cache, req_addr); + if (block) { + rz_th_queue_push(serve_queue, (void *)block, true); + } else { + rz_th_queue_push(serve_queue, RZ_IL_CACHE_FAILED_LIFTING_PTR, true); + RZ_LOG_WARN("Failed to lift IL block at 0x%" PFMT64x "\n", req_addr); + } + } + } + rz_sys_usleep(cache->us_sleep); + } + +stop_serving: + rz_th_lock_enter(cache->n_serving_lock); + cache->n_serving--; + rz_th_lock_leave(cache->n_serving_lock); + return success; +} + +/** + * \brief Close all IL Cache owned ring buffers. + */ +RZ_API void rz_il_cache_close(RZ_BORROW RZ_NONNULL RzILCache *cache) { + rz_return_if_fail(cache); + + rz_atomic_bool_set(cache->is_serving, false); + + for (size_t i = 0; i < rz_pvector_len(cache->req_rbufs); ++i) { + RzThreadRingBuf *rb = rz_pvector_at(cache->req_rbufs, i); + rz_th_ring_buf_close(rb); + RzThreadQueue *il_queue = rz_pvector_at(cache->il_queues, i); + rz_th_queue_close(il_queue); + } +} + +RZ_API void rz_il_cache_stop_serving(RZ_BORROW RZ_NONNULL RzILCache *cache) { + rz_return_if_fail(cache); + rz_atomic_bool_set(cache->is_serving, false); +} + +/** + * \brief Returns the static xrefs pointing from IL block to IL block. + */ +RZ_API const RzVector /**/ *rz_il_cache_get_static_xrefs(const RzILCache *cache) { + rz_return_val_if_fail(cache, NULL); + return cache->static_xrefs; +} + +RZ_API RZ_OWN RzIterator /**/ *rz_il_cache_get_blocks(const RzILCache *cache) { + rz_return_val_if_fail(cache, NULL); + return ht_up_as_iter(cache->cache); +} + +RZ_API void rz_il_cache_free(RZ_OWN RZ_NULLABLE RzILCache *cache) { + if (!cache) { + return; + } + + rz_il_cache_close(cache); + // Wait until all are left. + bool all_stopped = false; + do { + rz_th_lock_enter(cache->n_serving_lock); + all_stopped = cache->n_serving == 0; + rz_th_lock_leave(cache->n_serving_lock); + } while (!all_stopped); + + rz_pvector_free(cache->req_rbufs); + rz_pvector_free(cache->il_queues); + + ht_up_free(cache->cache); + rz_vector_free(cache->static_xrefs); + rz_th_lock_free(cache->n_serving_lock); + rz_atomic_bool_free(cache->is_serving); + rz_pvector_free(cache->bin_sections); + rz_th_lock_free(cache->skyline_lock); + if (cache->served_regions) { + rz_skyline_fini(cache->served_regions); + free(cache->served_regions); + } + free(cache); +} + +RZ_API RZ_OWN RzILCache *rz_il_cache_new( + RZ_BORROW RZ_NONNULL RzAnalysis *analysis, + RZ_BORROW RZ_NONNULL RzIO *io, + RZ_OWN RZ_NONNULL RzPVector /**/ *bin_sections, + RzILCacheConfig config) { + rz_return_val_if_fail(analysis && io && bin_sections, NULL); + RzILCache *cache = RZ_NEW0(RzILCache); + if (!cache) { + rz_warn_if_reached(); + return NULL; + } + cache->analysis = analysis; + cache->io = io; + cache->bin_sections = bin_sections; + + cache->cache = ht_up_new(NULL, (HtUPFreeValue)rz_il_cache_block_free); + cache->is_serving = rz_atomic_bool_new(false); + cache->n_serving_lock = rz_th_lock_new(false); + cache->static_xrefs = rz_vector_new(sizeof(RzAnalysisXRef), NULL, NULL); + if (!cache->cache || !cache->is_serving || !cache->static_xrefs) { + goto err; + } + + cache->skyline_lock = rz_th_lock_new(false); + cache->served_regions = RZ_NEW0(RzSkyline); + if (!cache->skyline_lock || !cache->served_regions) { + goto err; + } + rz_skyline_init(cache->served_regions); + + cache->il_queues = rz_pvector_new((RzPVectorFree)rz_th_queue_free); + cache->req_rbufs = rz_pvector_new((RzPVectorFree)rz_th_ring_buf_free); + if (!cache->il_queues || !cache->req_rbufs) { + goto err; + } + + cache->config = config; + cache->us_sleep = SLEEP_NONE_US; + + if ((cache->config & RZ_IL_CACHE_CONFIG_SLEEP_LONG) == RZ_IL_CACHE_CONFIG_SLEEP_LONG) { + cache->us_sleep = SLEEP_LONG_US; + } else if ((cache->config & RZ_IL_CACHE_CONFIG_SLEEP_LONG) == RZ_IL_CACHE_CONFIG_SLEEP_SHORT) { + cache->us_sleep = SLEEP_SHORT_US; + } + + if (!(cache->config & RZ_IL_CACHE_CONFIG_LIFT_ON_REQUEST) && + !lift_executable_maps(cache)) { + RZ_LOG_WARN("An error occurred when lifting the executable sections.\n"); + } + + return cache; + +err: + rz_warn_if_reached(); + rz_il_cache_free(cache); + return NULL; +} + +/** + * \brief Check if the given address has been requested from the cache. + */ +RZ_API bool rz_il_cache_was_requested( + RZ_BORROW RzILCache *cache, + ut64 addr) { + rz_th_lock_enter(cache->skyline_lock); + bool r = rz_skyline_contains(cache->served_regions, addr); + rz_th_lock_leave(cache->skyline_lock); + return r; +} + +RZ_API bool rz_il_cache_get_new_ring_buf( + RZ_BORROW RzILCache *cache, + RZ_NONNULL RZ_BORROW RZ_OUT RzThreadRingBuf **request_rbuf, + RZ_NONNULL RZ_BORROW RZ_OUT RzThreadQueue **il_queue) { + rz_return_val_if_fail(cache && request_rbuf && il_queue, false); + + *request_rbuf = NULL; + *il_queue = NULL; + // The queue to pass the Effects to the interpreter. + *il_queue = rz_th_queue_new(RZ_IL_OPS_CACHE_IL_QUEUE_SIZE, NULL); + if (!il_queue) { + rz_warn_if_reached(); + goto error_free; + } + rz_pvector_push(cache->il_queues, *il_queue); + + // The ring buffer the interpreter can request new Effects over. + *request_rbuf = rz_th_ring_buf_new(RZ_IL_OPS_CACHE_ADDR_RBUF_SIZE, sizeof(ut64)); + if (!*request_rbuf) { + rz_warn_if_reached(); + goto error_free; + } + rz_pvector_push(cache->req_rbufs, *request_rbuf); + + return true; +error_free: + rz_th_queue_free(*il_queue); + rz_th_ring_buf_free(*request_rbuf); + return false; +} diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 1aa70ccfb43..81bb067386b 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2025 RizinOrg // SPDX-License-Identifier: LGPL-3.0-only +#include #include #include #include @@ -11,6 +12,7 @@ #include "rz_il/definitions/mem.h" #include "rz_il/rz_il_vm.h" #include "rz_inquiry/rz_bb_graph.h" +#include "rz_inquiry/rz_il_cache.h" #include "rz_inquiry/rz_interpreter.h" #include "rz_inquiry_plugins.h" #include "rz_th.h" @@ -123,86 +125,6 @@ RZ_API RZ_OWN char *rz_inquiry_function_str(const RzInquiryFunction *fcn) { return rz_strbuf_drain(buf); } -/** - * \brief Add edges from \p insn_to_insn_edges to the cfg. - * These are edges statically known by checking RzAnalysisOp->jump and fail. - * - * TODO: Crazy inefficient. - * But for now it is left in here. The problem is that the graph has basic blocks as nodes. - * But the xrefs are instruction to instruction. So we have this super expansive |bb| * |E| lookup. - * - * It would be way faster if we have an R-Tree to get bbs by an address it covers. - * Or just do a better design all along. - */ -RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /**/ *insn_to_insn_edges) { - // Add the instruction to instruction edges. - RzAnalysisXRef *i2i_edge; - rz_vector_foreach (insn_to_insn_edges, i2i_edge) { - // First check if the edge is already in the CFG. - // If so (not unlikely), skip the step where it iterates over all BBs. - RzIterator *incoming = rz_inquiry_bb_cfg_get_incoming_edges(iq->bb_cfg, i2i_edge->to); - if (!incoming) { - // Basic block not present. - continue; - } - RzGraphEdge *in_e; - rz_iterator_foreach(incoming, in_e) { - const RzInquiryBB *bb = rz_graph_node_get_data(rz_graph_edge_get_from(in_e)); - if (RZ_BETWEEN_EXCL(bb->addr, i2i_edge->from, bb->addr + bb->size)) { - rz_iterator_free(incoming); - // This edge was already covered. - goto next_i2i_edge; - } - } - rz_iterator_free(incoming); - - // Edge isn't in the CFG yet. - // Now we have to do the crazy expansive |bb| * |E| search. - RzGraphNode *n; - RzIterator *bb_iter = rz_graph_get_nodes(iq->bb_cfg->graph); - rz_iterator_foreach(bb_iter, n) { - const RzInquiryBB *bb = rz_graph_node_get_data(n); - if (!RZ_BETWEEN_EXCL(bb->addr, i2i_edge->from, bb->addr + bb->size)) { - continue; - } - switch (i2i_edge->type) { - case RZ_ANALYSIS_XREF_TYPE_CALL: - if (!rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, i2i_edge->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL)) { - rz_warn_if_reached(); - } - break; - case RZ_ANALYSIS_XREF_TYPE_CODE: - if (!rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, i2i_edge->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP)) { - rz_warn_if_reached(); - } - break; - case RZ_ANALYSIS_XREF_TYPE_CALL_RET: - if (!rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, i2i_edge->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET)) { - rz_warn_if_reached(); - } - break; - case RZ_ANALYSIS_XREF_TYPE_RETURN: - if (!rz_inquiry_bb_cfg_add_edge(iq->bb_cfg, bb->addr, i2i_edge->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_RETURN)) { - rz_warn_if_reached(); - } - break; - case RZ_ANALYSIS_XREF_TYPE_NULL: - case RZ_ANALYSIS_XREF_TYPE_DATA: - case RZ_ANALYSIS_XREF_TYPE_STRING: - case RZ_ANALYSIS_XREF_TYPE_MEM_WRITE: - RZ_LOG_WARN("Xref of type %s for code edge.\n", - rz_analysis_xrefs_type_tostring(i2i_edge->type)); - rz_warn_if_reached(); - break; - } - } - rz_iterator_free(bb_iter); - next_i2i_edge: - continue; - } - return true; -} - RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void) { RzInquiry *iq = RZ_NEW0(RzInquiry); if (!iq) { @@ -211,7 +133,7 @@ RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void) { iq->plugins = ht_sp_new(HT_STR_CONST, NULL, NULL); iq->plugins_data = ht_sp_new(HT_STR_CONST, NULL, NULL); iq->call_candidates = ht_up_new(NULL, free); - iq->xrefs = rz_vector_new(sizeof(RzAnalysisXRef), NULL, NULL); + iq->dynamic_xrefs = rz_vector_new(sizeof(RzAnalysisXRef), NULL, NULL); iq->bb_cfg = rz_inquiry_bb_cfg_new(RZ_GRAPH_IMPL_LIST); if (!iq->plugins || !iq->plugins_data || !iq->bb_cfg) { ht_sp_free(iq->plugins); @@ -238,12 +160,12 @@ RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *iq) { ht_sp_free(iq->plugins_data); ht_up_free(iq->call_candidates); rz_inquiry_bb_cfg_free(iq->bb_cfg); - rz_vector_free(iq->xrefs); + rz_vector_free(iq->dynamic_xrefs); free(iq); } RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref) { - rz_vector_push(iq->xrefs, (void *)xref); + rz_vector_push(iq->dynamic_xrefs, (void *)xref); } RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzPVector /**/ *allowed_segments) { @@ -261,7 +183,7 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL co return false; } -static void handle_io_request(RzCore *core, RzPVector /**/ *il_mems, RzInterpreterIORequest *io_req, RZ_OUT RzInterpreterIOResult *io_res) { +static void handle_io_request(RzPVector /**/ *il_mems, RzInterpreterIORequest *io_req, RZ_OUT RzInterpreterIOResult *io_res) { RZ_LOG_DEBUG("INQUIRY: Received IO %s request: mem:%" PFMTSZd " 0x%" PFMT64x "\n", io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", io_req->mem_idx, @@ -322,7 +244,7 @@ RZ_API bool rz_inquiry_get_fcn_symbol_addr(RzCore *core, RZ_OUT RzSetU *symbol_t return true; } -static bool get_branch_targets(RzCore *core, RzSetU *branch_targets, RzVector /**/ *insn_to_insn_edges) { +static bool get_branch_targets(RzCore *core, RzSetU *branch_targets) { RzPVector /**/ *sections = rz_bin_object_get_sections(core->bin->cur->o); if (!sections) { rz_warn_if_reached(); @@ -342,7 +264,7 @@ static bool get_branch_targets(RzCore *core, RzSetU *branch_targets, RzVector /* rz_pvector_remove_at(sections, *j); } rz_vector_free(non_x_idx); - if (!rz_analysis_get_all_branch_targets(core->analysis, sections, true, branch_targets, insn_to_insn_edges)) { + if (!rz_analysis_get_all_branch_targets(core->analysis, sections, true, branch_targets)) { RZ_LOG_ERROR("Failed to get branch targets.\n"); return false; } @@ -350,35 +272,62 @@ static bool get_branch_targets(RzCore *core, RzSetU *branch_targets, RzVector /* return true; } -static bool handle_yields(RzCore *core, RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM]) { +static bool log_control_flow(RzInquiry *inquiry, RzInterpreterCtrlFlow *cf) { + RZ_LOG_DEBUG("INQUIRY: Received control flow: 0x%" PFMT64x " size: %" PFMTSZu " (alt: 0x%" PFMT64x ")\n", + cf->target_addr, cf->target_block_size, cf->alt_target); + rz_inquiry_bb_cfg_add_basic_block(inquiry->bb_cfg, cf->actual_target, cf->target_block_size); + if (cf->alt_target) { + // Add a dummy basic block at the address the call originally jumped to. + // This is the basic block for the imported function. + rz_inquiry_bb_cfg_add_basic_block(inquiry->bb_cfg, cf->target_addr, 1); + } + // Add a simple control flow edge here. + // It gets later updated to another type if a reported xref has it. + rz_inquiry_bb_cfg_add_edge(inquiry->bb_cfg, cf->src_block_addr, cf->actual_target, cf->type); + return true; +} + +static bool handle_yields(RzInquiry *inquiry, RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM]) { RzInterpreterYieldRBuf *rbuf_xrefs = yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF]; rz_return_val_if_fail(rbuf_xrefs, false); RzAnalysisXRef xref = { 0 }; if (!rz_th_ring_buf_is_empty_unsafe(rbuf_xrefs->rbuf)) { - RzThreadRingBufResult r = rz_th_ring_buf_take_blocking(rbuf_xrefs->rbuf, &xref); + RzThreadRingBufResult r = rz_th_ring_buf_take(rbuf_xrefs->rbuf, &xref); if (r == RZ_THREAD_RING_BUF_CLOSED) { rz_warn_if_reached(); return false; } else if (r == RZ_THREAD_RING_BUF_OK) { - rz_inquiry_add_xref(core->inquiry, &xref); + rz_inquiry_add_xref(inquiry, &xref); RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref.from, xref.to, rz_analysis_ref_type_tostring(xref.type)); } } + RzInterpreterYieldRBuf *rbuf_cf = yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW]; + if (!rz_th_ring_buf_is_empty_unsafe(rbuf_cf->rbuf)) { + RzInterpreterCtrlFlow cf = { 0 }; + RzThreadRingBufResult r = rz_th_ring_buf_take(rbuf_cf->rbuf, &cf); + if (r == RZ_THREAD_RING_BUF_CLOSED) { + rz_warn_if_reached(); + return false; + } else if (r == RZ_THREAD_RING_BUF_OK) { + log_control_flow(inquiry, &cf); + } + } + RzInterpreterYieldRBuf *rbuf_calls = yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE]; rz_return_val_if_fail(rbuf_calls, false); RzAnalysisCallCandidate cc = { 0 }; if (!rz_th_ring_buf_is_empty_unsafe(rbuf_calls->rbuf)) { - RzThreadRingBufResult r = rz_th_ring_buf_take_blocking(rbuf_calls->rbuf, &cc); + RzThreadRingBufResult r = rz_th_ring_buf_take(rbuf_calls->rbuf, &cc); if (r == RZ_THREAD_RING_BUF_CLOSED) { rz_warn_if_reached(); return false; } else if (r == RZ_THREAD_RING_BUF_OK) { RzAnalysisCallCandidate *cc_clone = RZ_NEW0(RzAnalysisCallCandidate); memcpy(cc_clone, &cc, sizeof(RzAnalysisCallCandidate)); - if (ht_up_update(core->inquiry->call_candidates, cc_clone->bb_addr, cc_clone)) { + if (ht_up_update(inquiry->call_candidates, cc_clone->bb_addr, cc_clone)) { RZ_LOG_DEBUG("Overwrote a call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); } else { RZ_LOG_DEBUG("Added call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); @@ -388,106 +337,44 @@ static bool handle_yields(RzCore *core, RzInterpreterYieldRBuf *yield_rbufs[RZ_I return true; } -#if 0 -static void validate_il_bb(RzCore *core, RzInterpreterILBB *bb) { - RzAnalysisILVM *vm = rz_analysis_il_vm_new(core->analysis, NULL); - RzILValidateGlobalContext *ctx = rz_il_validate_global_context_new_from_vm(vm->vm); - void **it; - size_t i = 0; - rz_pvector_enumerate (bb->il_ops, it, i) { - char *report = NULL; - RzInterpreterInsnPkt *pkt = *it; - if (!rz_il_validate_effect(pkt->effect, ctx, NULL, NULL, &report)) { - RZ_LOG_ERROR("Validation failed for IL op %" PFMTSZu " in BB 0x%" PFMT64x " in insn packet:\n" - "\t'%s'\n", - i, bb->bb_addr, report); - } - free(report); - } - rz_analysis_il_vm_free(vm); - rz_il_validate_global_context_free(ctx); -} -#endif - -static const RzInterpreterILBB *get_il_bb(RzCore *core, HtUP *il_cache, ut64 addr) { - RzInterpreterILBB *bb = ht_up_find(il_cache, addr, NULL); - if (!bb) { - RZ_LOG_DEBUG("INQUIRY: Lift new BB\n"); - bb = rz_inquiry_gen_il_bb(core->analysis, core->io, addr); - if (!bb) { - RZ_LOG_DEBUG("Failed to lift basic block at 0x%" PFMT64x "\n", addr); - return NULL; - } - -#if 0 - // Validate IL to catch more errors during testing. - validate_il_bb(core, bb); - // Otherwise YOLO -#endif - - RZ_LOG_DEBUG("INQUIRY: Send IL result: %p.\n", bb); - ht_up_insert(il_cache, bb->bb_addr, bb); - } else { - RZ_LOG_DEBUG("INQUIRY: Serve BB from cache\n"); - } - return bb; -} - -static bool send_next_il_bb(RzCore *core, - RzThreadQueue *il_queue, - HtUP *il_cache, - RzSetU *entry_points, - RzInterpreterBranch *branch) { - RZ_LOG_DEBUG("INQUIRY: Received IL request: 0x%" PFMT64x " (alt: 0x%" PFMT64x ")\n", branch->target_addr, branch->alt_target); - const RzInterpreterILBB *bb = get_il_bb(core, il_cache, branch->alt_target ? branch->alt_target : branch->target_addr); - if (!bb) { - // Delete the address from the branch targets. - // This is currently necessary as a work around, because if the interpreter - // fails before interpreting the address, it is added again as next entry point. - // Giving an endless loop. - // One of the design thingies to fix in the proper implementation. - rz_set_u_delete(entry_points, branch->target_addr); - if (branch->alt_target) { - rz_set_u_delete(entry_points, branch->alt_target); - } - return false; - } - rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, bb->bb_addr, bb->size); - if (branch->alt_target) { - // Add a dummy basic block at the address the call originally jumped to. - // This is the basic block for the imported function. - rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, branch->target_addr, 1); - } - // Add a simple control flow edge here. - // It gets later updated to another type if a reported xref has it. - rz_inquiry_bb_cfg_add_edge(core->inquiry->bb_cfg, branch->branching_bb_addr, bb->bb_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF); - rz_th_queue_push(il_queue, (void *)bb, true); - return true; -} - +/** + * \brief Removes entry points which were requested from the IL cache + * (and hence have been interpreted). + * Returns false if no entry points are left to interpret. + * True otherwise. + */ +// TODO: Optimize static bool reduce_get_entry_points( RzInterpreterSet *iset, - HtUP *il_cache, - RZ_BORROW RzSetU /**/ *entry_points, - RZ_OUT ut64 *next_entry_point) { + RzILCache *il_cache, + RZ_BORROW RzSetU /**/ *entry_points) { // Add the next entry point we need to check for executable regions the interpreters did not cover. // For this we simply delete all entry points which point // into the already handled basic blocks. // Then add a few addresses as new entry point. // The addresses we add are jump targets from jump/call instructions in the binary. - RzIterator *il_bb_iter = ht_up_as_iter_keys(il_cache); + RzVector to_del = { 0 }; + rz_vector_init(&to_del, sizeof(ut64), NULL, NULL); + + RzIterator *entries = rz_set_u_as_iter(entry_points); ut64 *ct; - rz_iterator_foreach(il_bb_iter, ct) { + rz_iterator_foreach(entries, ct) { // This call target was interpreted before (hence is in the IL cache). + if (rz_il_cache_was_requested(il_cache, *ct)) { + rz_vector_push(&to_del, ct); + } + } + rz_iterator_free(entries); + + rz_vector_foreach (&to_del, ct) { rz_set_u_delete(entry_points, *ct); } - rz_iterator_free(il_bb_iter); + rz_vector_fini(&to_del); + if (rz_set_u_size(entry_points) == 0) { return false; } - - *next_entry_point = rz_set_u_take(entry_points); return true; } @@ -496,7 +383,8 @@ static void close_reset_ipc_obj(RzInterpreterSet *iset) { // This also clears the buffer and queues rz_th_ring_buf_close(iset->io_request_rbuf); rz_th_ring_buf_close(iset->io_result_rbuf); - rz_th_ring_buf_close(iset->branch_rbuf); + rz_th_ring_buf_close(iset->il_request_rbuf); + rz_th_ring_buf_close(iset->entry_points); rz_th_queue_close(iset->il_queue); rz_list_free(rz_th_queue_pop_all(iset->il_queue)); } @@ -506,16 +394,16 @@ static void open_ipc_obj(RzInterpreterSet *iset) { // jump target again. rz_th_ring_buf_open(iset->io_request_rbuf); rz_th_ring_buf_open(iset->io_result_rbuf); - rz_th_ring_buf_open(iset->branch_rbuf); + rz_th_ring_buf_open(iset->il_request_rbuf); + rz_th_ring_buf_open(iset->entry_points); rz_th_queue_open(iset->il_queue); } static bool collect_entry_points(RzCore *core, - RzVector /**/ *insn_to_insn_edges, RzSetU *entry_points, RzSetU *symbol_targets) { - if (!get_branch_targets(core, entry_points, insn_to_insn_edges) || + if (!get_branch_targets(core, entry_points) || !rz_inquiry_get_fcn_symbol_addr(core, symbol_targets)) { rz_warn_if_reached(); return false; @@ -550,6 +438,15 @@ static bool setup_yield_rbufs( } yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE] = rbuf; + yield_kind = RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW; + rbuf = NULL; + rbuf = rz_interpreter_yield_rbuf_new(yield_kind, NULL, NULL); + if (!rbuf) { + rz_warn_if_reached(); + return false; + } + yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW] = rbuf; + yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; rbuf = rz_interpreter_yield_rbuf_new( yield_kind, @@ -579,22 +476,24 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // All the things we need bool return_code = true; RzInterpreterSet *intp_iset = NULL; - HtUP *il_cache = NULL; RzBuffer *io_buf = rz_buf_new_with_io(rz_analysis_get_io_bind(core->analysis)); RzSetU *symbol_targets = rz_set_u_new(); bool user_sent_signal = false; struct ituple *iset_map = NULL; - RzVector /**/ *insn_to_insn_edges = rz_vector_new(sizeof(RzAnalysisXRef), NULL, NULL); + RzThread *il_cache_th = NULL; rz_cons_push(); - // The pseudo cache of IL effects. - // This is only a vector so we can simulate the ownership separation - // of the pointers. - il_cache = ht_up_new(NULL, (RzPVectorFree)rz_interpreter_il_bb_free); + RzILCache *il_cache = rz_il_cache_new(core->analysis, core->io, + rz_bin_object_get_sections(core->bin->cur->o), + RZ_IL_CACHE_CONFIG_NOP_UNLIFTED | RZ_IL_CACHE_CONFIG_SLEEP_SHORT); + if (!il_cache) { + return_code = false; + goto error_free; + } - collect_entry_points(core, insn_to_insn_edges, entry_points, symbol_targets); + collect_entry_points(core, entry_points, symbol_targets); if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { eprintf("Total branch targets in binary: %" PFMT32d "\n", rz_set_u_size(entry_points)); @@ -621,7 +520,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_warn_if_reached(); goto error_free; } - size_t n_threads = 8; + size_t n_threads = 1; iset_map = RZ_NEWS0(struct ituple, n_threads); RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM] = { 0 }; @@ -632,11 +531,22 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, goto error_free; } + // + // Initialize and spawn the interpreters. + // for (size_t i = 0; i < n_threads; ++i) { + RzThreadRingBuf *il_req = NULL; + RzThreadQueue *il_queue = NULL; + if (!rz_il_cache_get_new_ring_buf(il_cache, &il_req, &il_queue)) { + return_code = false; + rz_warn_if_reached(); + goto error_free; + } intp_iset = rz_interpreter_set_new( core->analysis, prototype->p_interpreter, RZ_INTERPRETER_ABSTRACTION_CONST, + il_req, il_queue, yield_rbufs, ignored_code); if (!intp_iset) { @@ -647,15 +557,23 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // Dispatch prototype interpreter into a thread. RZ_LOG_DEBUG("INQUIRY: Start main interpretation thread.\n"); - RzThread *interpr_th = interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, intp_iset); + RzThread *interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, intp_iset); iset_map[i].ithread = interpr_th; iset_map[i].iset = intp_iset; iset_map[i].next_run_state = RZ_INTP_RUN_STATE_INIT; } + // + // Spawn the IL cache. + // + il_cache_th = rz_th_new((RzThreadFunction)rz_il_cache_serve, il_cache); + ut64 intpr_terminated = 0; ut64 check_signal = 0; + // + // Enter the loop to serve the interpreters. + // for (ut64 i = 0;; check_signal++, i = (i + 1) % n_threads) { if (check_signal % RZ_INQUIRY_CHECK_USER_SIGNAL_ITC == 0 && rz_cons_is_breaked()) { user_sent_signal = true; @@ -672,24 +590,38 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, break; } // This interpreter is waiting for the next emulation task. - RzInterpreterBranch branch = { 0 }; - if (!reduce_get_entry_points(iset, il_cache, entry_points, &branch.target_addr)) { + // TODO: Really reduce the entry points each and every time? + // This eats too much runtime I think. + // Better live with some duplicate emulation and reduce less often? + if (!reduce_get_entry_points(iset, il_cache, entry_points)) { // None left. + // TODO Remove? rz_th_queue_close(iset->il_queue); iset_map[i].next_run_state = RZ_INTP_RUN_STATE_TERM; intpr_terminated++; // RZ_LOG_DEBUG("Next: TERM\n"); continue; } - if (send_next_il_bb(core, iset->il_queue, il_cache, entry_points, &branch)) { + ut64 next_entry_point = rz_set_u_take(entry_points); + switch (rz_th_ring_buf_put(iset->entry_points, &next_entry_point)) { + case RZ_THREAD_RING_BUF_OK: // Successfully lifted and pushed the entry point's basic block into the queue. // Expect the interpreter to emulate now. iset_map[i].next_run_state = RZ_INTP_RUN_STATE_EMU; // RZ_LOG_DEBUG("Next: EMU\n"); - } else { - iset_map[i].next_run_state = RZ_INTP_RUN_STATE_CLEAN; - rz_th_queue_close(iset->il_queue); - // RZ_LOG_DEBUG("Next: CLEAN\n"); + break; + case RZ_THREAD_RING_BUF_FAIL: + // The entry point buffer is full. + // Insert the entry point to the todo set again. + rz_set_u_add(entry_points, next_entry_point); + break; + case RZ_THREAD_RING_BUF_CLOSED: + rz_warn_if_reached(); + // Something went pretty wrong. + iset_map[i].next_run_state = RZ_INTP_RUN_STATE_TERM; + intpr_terminated++; + // RZ_LOG_DEBUG("Next: TERM\n"); + continue; } break; } @@ -697,40 +629,13 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, if (expected_rs != RZ_INTP_RUN_STATE_EMU) { break; } - // From here on, the code plays the role of the cache, IO handler, + // From here on, the code plays the role of the IO handler, // and yield consumer. - // - Waiting for new Effects to be requested and sending them. // - Handling IO requests. // - Receiving and adding the found xrefs to RzAnalysis. // In the final implementation each of those roles would be split into // two or more separated modules running in parallel. - // ========= - // IL CACHE - // ========= - // - // This block mimics the IL cache. It uplifts basic blocks and - // caches them. - if (!rz_th_ring_buf_is_empty_unsafe(iset->branch_rbuf)) { - RzInterpreterBranch branch = { 0 }; - RzThreadRingBufResult r = rz_th_ring_buf_take(iset->branch_rbuf, &branch); - if (r == RZ_THREAD_RING_BUF_CLOSED) { - rz_warn_if_reached(); - goto fatal_error; - } else if (r == RZ_THREAD_RING_BUF_OK) { - if (!send_next_il_bb(core, iset->il_queue, il_cache, entry_points, &branch)) { - // Signal interpreter the lifting failed. - rz_th_queue_close(iset->il_queue); - iset_map[i].next_run_state = RZ_INTP_RUN_STATE_CLEAN; - // RZ_LOG_DEBUG("Next: CLEAN\n"); - } else { - RZ_LOG_DEBUG("Pushed: il_bb: 0x%llx\n", branch.target_addr); - } - } - // Else r == RZ_THREAD_RING_BUF_FAIL - // Due to a race condition the ring buffer was actually empty. - } - // ========== // IO HANDLER // ========== @@ -748,7 +653,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, goto fatal_error; } else if (r == RZ_THREAD_RING_BUF_OK) { RzInterpreterIOResult io_res = { 0 }; - handle_io_request(core, &iset->il_vm->vm->vm_memory, &io_req, &io_res); + handle_io_request(&iset->il_vm->vm->vm_memory, &io_req, &io_res); if (rz_th_ring_buf_put(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { rz_warn_if_reached(); goto fatal_error; @@ -764,7 +669,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // // This part plays the role of a yield consumer. // In our prototype it only receives xrefs and call candidates. - if (!handle_yields(core, iset->yield_rbufs)) { + if (!handle_yields(core->inquiry, iset->yield_rbufs)) { iset_map[i].next_run_state = RZ_INTP_RUN_STATE_TERM; intpr_terminated++; // RZ_LOG_DEBUG("Next: TERM\n"); @@ -825,17 +730,28 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { eprintf("\n"); } - if (!rz_inquiry_bb_cfg_add_xrefs(core->inquiry->bb_cfg, core->inquiry->xrefs)) { + RzIterator *iter = rz_il_cache_get_blocks(il_cache); + if (!iter) { rz_warn_if_reached(); + goto error_free; } - if (!rz_inquiry_bb_cfg_reduce(core->inquiry->bb_cfg)) { + void **it; + rz_iterator_foreach(iter, it) { + RzILCacheBlock *block = *it; + char *bstr = rz_il_cache_block_str(block); + RZ_LOG_DEBUG("Inquiry: Add ILCache block: %s\n", bstr); + free(bstr); + rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, block->addr, block->size); + } + rz_iterator_free(iter); + if (!rz_inquiry_bb_cfg_add_xrefs(core->inquiry->bb_cfg, rz_il_cache_get_static_xrefs(il_cache))) { rz_warn_if_reached(); } - if (!user_sent_signal) { - eprintf("Complement BB CFG with statically known xrefs...\n"); - if (!rz_inquiry_bb_cfg_complement(core->inquiry, insn_to_insn_edges)) { - rz_warn_if_reached(); - } + if (!rz_inquiry_bb_cfg_add_xrefs(core->inquiry->bb_cfg, core->inquiry->dynamic_xrefs)) { + rz_warn_if_reached(); + } + if (!rz_inquiry_bb_cfg_reduce(core->inquiry->bb_cfg)) { + rz_warn_if_reached(); } RZ_LOG_DEBUG("INQUIRY: Done\n"); @@ -845,11 +761,16 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, error_free: rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF]); rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE]); + rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW]); free(iset_map); rz_set_u_free(entry_points); rz_buf_free(io_buf); - rz_vector_free(insn_to_insn_edges); - ht_up_free(il_cache); + rz_il_cache_stop_serving(il_cache); + if (il_cache_th) { + rz_th_wait(il_cache_th); + rz_th_free(il_cache_th); + } + rz_il_cache_free(il_cache); rz_cons_pop(); return return_code && !user_sent_signal; } @@ -897,7 +818,7 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry rz_iterator_free(iter); RzAnalysisXRef *xref; - rz_vector_foreach (inquiry->xrefs, xref) { + rz_vector_foreach (inquiry->dynamic_xrefs, xref) { rz_analysis_xrefs_set(analysis, xref->from, xref->to, xref->type); } diff --git a/librz/inquiry/inquiry_helpers.c b/librz/inquiry/inquiry_helpers.c deleted file mode 100644 index b350b035788..00000000000 --- a/librz/inquiry/inquiry_helpers.c +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-FileCopyrightText: 2025 RizinOrg -// SPDX-License-Identifier: LGPL-3.0-only - -/** - * \file Helper functions for the new analysis. Some of them not yet temporarily. - */ - -#include -#include -#include -#include -#include -#include - -RZ_API RZ_OWN RzInterpreterILBB *rz_inquiry_gen_il_bb(RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, ut64 addr) { - rz_return_val_if_fail(analysis && rz_analysis_plugin_current(analysis) && io, NULL); - RzInterpreterILBB *il_bb = NULL; - RzAnalysisOp op = { 0 }; - rz_analysis_op_init(&op); - // Estimate a reasonable number of bytes to read. - int max_read_size = (rz_analysis_plugin_current(analysis)->bits / 8) * 16; - ut8 *buf = RZ_NEWS0(ut8, max_read_size); - if (!max_read_size || !buf) { - rz_warn_if_reached(); - goto fail; - } - - il_bb = RZ_NEW0(RzInterpreterILBB); - if (!il_bb) { - rz_warn_if_reached(); - goto fail; - } - il_bb->il_ops = rz_pvector_new((RzPVectorFree)rz_interpreter_insn_pkt_free); - if (!il_bb->il_ops) { - rz_warn_if_reached(); - goto fail; - } - il_bb->bb_addr = addr; - RZ_LOG_DEBUG("Gen BB:\n"); - bool sparc_add_delayed_insn = false; - bool changes_cf = true; - do { - // Don't use rz_io_read_at_mapped() here. - // It fails if it reads beyond a mapped memory region. - // Although this is expected here. rz_io_nread_at() on the other hand just - // reads less bytes. - if (rz_io_nread_at(io, addr, buf, max_read_size) == 0) { - goto fail; - } - if (rz_analysis_op(analysis, &op, addr, buf, max_read_size, RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_INSN_PKT) <= 0) { - RZ_LOG_ERROR("Failed to decode IL op\n"); - goto fail; - } - bool lifted = true; - if (!op.il_op) { - // Not lifted. Map to NOP - lifted = false; - op.il_op = rz_il_op_new_nop(); - } - RzInterpreterInsnPkt *pkt = RZ_NEW0(RzInterpreterInsnPkt); - pkt->effect = op.il_op; - pkt->insn_pkt_size = op.size; - il_bb->size += op.size; - rz_pvector_push(il_bb->il_ops, pkt); - // Take ownership of IL op pointer. - op.il_op = NULL; - if (lifted) { - changes_cf = rz_analysis_op_changes_control_flow(&op); - } - RZ_LOG_DEBUG("\t0x%" PFMT64x "\n", addr); - addr += op.size; - rz_analysis_op_fini(&op); - rz_mem_memzero(buf, max_read_size); - if (sparc_add_delayed_insn) { - // Instruction was added, now the BB is complete. - break; - } - if (changes_cf && RZ_STR_EQ(rz_analysis_plugin_current(analysis)->arch, "sparc")) { - // We need to add the instruction after the branch. - // So one more iteration is needed. - sparc_add_delayed_insn = true; - changes_cf = false; - } - } while (!changes_cf); - - free(buf); - return il_bb; - -fail: - free(buf); - rz_analysis_op_fini(&op); - rz_interpreter_il_bb_free(il_bb); - return NULL; -} diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 34765437673..bde4113139b 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -16,24 +16,6 @@ #include #include -RZ_API void rz_interpreter_insn_pkt_free(RZ_NULLABLE RZ_OWN RzInterpreterInsnPkt *pkt) { - if (!pkt) { - return; - } - if (pkt->effect) { - rz_il_op_effect_free(pkt->effect); - } - free(pkt); -} - -RZ_API void rz_interpreter_il_bb_free(RZ_NULLABLE RZ_OWN RzInterpreterILBB *il_bb) { - if (!il_bb) { - return; - } - rz_pvector_free(il_bb->il_ops); - free(il_bb); -} - RZ_API void rz_interpreter_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldRBuf *yield_rbufs) { if (!yield_rbufs) { return; @@ -63,6 +45,9 @@ RZ_API RZ_OWN RzInterpreterYieldRBuf *rz_interpreter_yield_rbuf_new(RzInterprete case RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE: rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_YIELD_RBUF_SIZE, sizeof(RzAnalysisCallCandidate)); break; + case RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW: + rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_YIELD_RBUF_SIZE, sizeof(RzInterpreterCtrlFlow)); + break; case RZ_INTERPRETER_YIELD_KIND_XREF: if (filter_data) { yield_rbufs->filter_data = RZ_NEW0(RzInterpreterYieldFilterData); @@ -163,18 +148,15 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { if (!iset) { return; } - if (iset->il_queue) { - rz_th_queue_free(iset->il_queue); - } - if (iset->branch_rbuf) { - rz_th_ring_buf_free(iset->branch_rbuf); - } if (iset->io_request_rbuf) { rz_th_ring_buf_free(iset->io_request_rbuf); } if (iset->io_result_rbuf) { rz_th_ring_buf_free(iset->io_result_rbuf); } + if (iset->entry_points) { + rz_th_ring_buf_free(iset->entry_points); + } if (iset->run_state_sync) { rz_th_sem_free(iset->run_state_sync); } @@ -191,37 +173,20 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { } static bool setup_ipc_objects( - RZ_OUT RzThreadQueue **il_queue, RZ_OUT RzThreadRingBuf **io_request_rbuf, RZ_OUT RzThreadRingBuf **io_result_rbuf, - RZ_OUT RzThreadRingBuf **branch_rbuf) { - *il_queue = NULL; + RZ_OUT RzThreadRingBuf **entry_points) { *io_request_rbuf = NULL; *io_result_rbuf = NULL; - *branch_rbuf = NULL; - - // The queue to pass the Effects to the interpreter. - // This is only one queue for the prototype. - // In practice it would be one for each interpreter. - *il_queue = rz_th_queue_new(RZ_INTERPRETER_IL_QUEUE_SIZE, NULL); - if (!il_queue) { - rz_warn_if_reached(); - goto error_free; - } + *entry_points = NULL; // Setup the IO queues. Each interpreter instance needs it's own queue at // for writing IO. Because the writing is done on the IO cache, and each // instance needs its own cache. *io_request_rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_IO_RBUF_SIZE, sizeof(RzInterpreterIORequest)); *io_result_rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_IO_RBUF_SIZE, sizeof(RzInterpreterIOResult)); - if (!*io_request_rbuf || !*io_result_rbuf) { - rz_warn_if_reached(); - goto error_free; - } - - // The branch ring buffer. It is the ring buffer the interpreter can request new Effects over. - *branch_rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_ADDR_RBUF_SIZE, sizeof(RzInterpreterBranch)); - if (!*branch_rbuf) { + *entry_points = rz_th_ring_buf_new(RZ_INTERPRETER_ENTRY_POINTS_RBUF_SIZE, sizeof(ut64)); + if (!*io_request_rbuf || !*io_result_rbuf || !*entry_points) { rz_warn_if_reached(); goto error_free; } @@ -229,10 +194,9 @@ static bool setup_ipc_objects( return true; error_free: - rz_th_queue_free(*il_queue); rz_th_ring_buf_free(*io_request_rbuf); rz_th_ring_buf_free(*io_result_rbuf); - rz_th_ring_buf_free(*branch_rbuf); + rz_th_ring_buf_free(*entry_points); return false; } @@ -244,9 +208,11 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( RzAnalysis *analysis, RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, RzInterpreterAbstraction abstraction, + RZ_NONNULL RZ_BORROW RzThreadRingBuf *il_request_rbuf, + RZ_NONNULL RZ_BORROW RzThreadQueue *il_queue, RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM], RZ_NONNULL const RzVector /**/ *ignored_code) { - rz_return_val_if_fail(plugin && ignored_code && analysis, NULL); + rz_return_val_if_fail(plugin && ignored_code && analysis && il_request_rbuf && il_queue, NULL); if (abstraction != (plugin->supported_abstractions & abstraction)) { RZ_LOG_ERROR("Plugin does not support all required abstractions.\n"); @@ -283,14 +249,20 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( abstraction, config, il_vm->reg_binding); + if (!state) { + free(iset); + rz_analysis_il_vm_free(il_vm); + rz_analysis_il_config_free(config); + return NULL; + } RzThreadRingBuf *io_request_rbuf = NULL; RzThreadRingBuf *io_result_rbuf = NULL; - RzThreadRingBuf *branch_rbuf = NULL; - RzThreadQueue *il_queue = NULL; - if (!setup_ipc_objects(&il_queue, &io_request_rbuf, &io_result_rbuf, &branch_rbuf)) { + RzThreadRingBuf *entry_points = NULL; + if (!setup_ipc_objects(&io_request_rbuf, &io_result_rbuf, &entry_points)) { free(iset); rz_analysis_il_vm_free(il_vm); + rz_interpreter_abstr_state_free(state); return NULL; } @@ -299,9 +271,11 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( iset->run_state = rz_intp_run_state_new(); iset->il_vm = il_vm; iset->il_queue = il_queue; - iset->branch_rbuf = branch_rbuf; + iset->il_request_rbuf = il_request_rbuf; + iset->entry_points = entry_points; iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF] = yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF]; iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE] = yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE]; + iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW] = yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW]; iset->io_request_rbuf = io_request_rbuf; iset->io_result_rbuf = io_result_rbuf; iset->run_state_sync = rz_th_sem_new(0); @@ -321,7 +295,7 @@ static bool jumps_to_ignored_code(const RzVector *v, ut64 jump_target) { } typedef struct { - ut64 addr; + RzInterpreterCtrlFlow ctrl_flow; ut64 in_state_hash; } SuccessorState; @@ -329,7 +303,7 @@ static bool choose_next_pc(RzInterpreterSet *iset, ut64 out_hash, RzVector *tmp_succ_addr, RzVector *succ_states, - const RzInterpreterILBB *il_bb) { + const RzILCacheBlock *il_bb) { // Debug printing whole state of VM. // // plugin->state_as_str(out_state, state_str, iset->intrpr_priv); @@ -350,35 +324,39 @@ static bool choose_next_pc(RzInterpreterSet *iset, has_succsessor = !rz_vector_empty(tmp_succ_addr); // Request the successor effects over the queue. while (!rz_vector_empty(tmp_succ_addr)) { - RzInterpreterBranch branch = { 0 }; - rz_vector_pop_front(tmp_succ_addr, &branch); - if (branch.target_addr == UT64_MAX || branch.target_addr == 0) { + RzInterpreterCtrlFlow cf = { 0 }; + rz_vector_pop_front(tmp_succ_addr, &cf); + if (cf.target_addr == UT64_MAX || cf.target_addr == 0) { RZ_LOG_DEBUG("interpreter: Quit due to invalid PC.\n"); // Obviously wrong address. return false; } - branch.branching_bb_addr = il_bb->bb_addr; - if (jumps_to_ignored_code(iset->ignored_code, branch.target_addr) && !branch.alt_target) { - RZ_LOG_DEBUG("interpreter: tried to jump to ignored code region at 0x%" PFMT64x "\n", branch.target_addr); + cf.src_block_addr = il_bb->addr; + if (jumps_to_ignored_code(iset->ignored_code, cf.target_addr) && !cf.alt_target) { + RZ_LOG_DEBUG("interpreter: tried to jump to ignored code region at 0x%" PFMT64x "\n", cf.target_addr); // Ignored code is mostly dynamically linked functions. // Skip to the next following address after the jump. - branch.alt_target = il_bb->bb_addr + il_bb->size; + cf.alt_target = il_bb->addr + il_bb->size; + cf.actual_target = cf.alt_target; } SuccessorState ss = { - .addr = branch.alt_target ? branch.alt_target : branch.target_addr, + .ctrl_flow = cf, .in_state_hash = out_hash }; // The successors are pushed in the same order into the succ_states - // vector, as they are requested over the addr_queue. + // vector, as they are requested over the queue. rz_vector_push(succ_states, &ss); - if (rz_th_ring_buf_put(iset->branch_rbuf, &branch) != RZ_THREAD_RING_BUF_OK) { + if (rz_th_ring_buf_put(iset->il_request_rbuf, &cf.actual_target) != RZ_THREAD_RING_BUF_OK) { return false; } } return has_succsessor; } +/** + * \brief Set entry point and reset (clean) the vectors and sets. + */ static bool reset_intrpr_state( RzInterpreterSet *iset, ut64 entry_point, @@ -395,7 +373,7 @@ static bool reset_intrpr_state( return false; } - *tmp_succ_addr = rz_vector_new(sizeof(RzInterpreterBranch), NULL, NULL); + *tmp_succ_addr = rz_vector_new(sizeof(RzInterpreterCtrlFlow), NULL, NULL); *succ_states = rz_vector_new(sizeof(SuccessorState), NULL, NULL); *reachable_states = rz_set_u_new(); if (!tmp_succ_addr || !succ_states || !reachable_states) { @@ -411,10 +389,11 @@ static bool reset_intrpr_state( RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { rz_return_val_if_fail(iset && iset->astate && - iset->branch_rbuf && + iset->il_request_rbuf && iset->il_queue && iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF] && iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE] && + iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW] && iset->run_state_sync && iset->plugin && iset->plugin->eval && @@ -440,7 +419,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // The successor states to evaluate. // This vector must have the same order as the elements pushed into branch_queue. RzVector *succ_states = NULL; - const RzInterpreterILBB *il_bb = NULL; + const RzILCacheBlock *il_bb = NULL; ut64 astate_hash = 0; if (iset->plugin->init) { @@ -460,18 +439,29 @@ INIT: { RZ_LOG_DEBUG("Enter INIT\n"); rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_INIT); - if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { - // No more BBs to interpret. Terminate. + ut64 entry_point; + if (rz_th_ring_buf_take_blocking(iset->entry_points, &entry_point) != RZ_THREAD_RING_BUF_OK || + rz_th_ring_buf_put(iset->il_request_rbuf, &entry_point) != RZ_THREAD_RING_BUF_OK) { + // No more entry points to interpret => Terminate. + // OR + // Can't request IL block => Cache closed => Terminate. success = true; goto TERM; } // Initializes the current interpreter's private data and its state. - if (!reset_intrpr_state(iset, il_bb->bb_addr, &tmp_succ_addr, &reachable_states, &succ_states)) { + if (!reset_intrpr_state(iset, entry_point, &tmp_succ_addr, &reachable_states, &succ_states)) { success = false; goto TERM; } + if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || + il_bb == RZ_IL_CACHE_FAILED_LIFTING_PTR || !il_bb) { + // No more BBs to interpret. Terminate. + success = true; + goto TERM; + } + goto EMU; } @@ -479,7 +469,7 @@ EMU: { RZ_LOG_DEBUG("Enter EMU\n"); rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_EMU); - iset->astate->bb_addr = il_bb->bb_addr; + iset->astate->bb_addr = il_bb->addr; iset->astate->bb_size = il_bb->size; // Evaluate the effect on the abstract state. if (!plugin->eval(iset, il_bb, iset->intrpr_priv)) { @@ -508,14 +498,23 @@ EMU: { il_bb = NULL; SuccessorState next = { 0 }; rz_vector_pop_front(succ_states, &next); - if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || !il_bb) { - RZ_LOG_DEBUG("Getting il bb failed\n"); + if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || + il_bb == RZ_IL_CACHE_FAILED_LIFTING_PTR || !il_bb) { + RZ_LOG_DEBUG("INTPR: Getting il bb failed\n"); // The il op lifting failed. Likely because the PC // pointed to an unmapped region. goto CLEAN; } - RZ_LOG_DEBUG("Received il_bb: 0x%llx\n", il_bb->bb_addr); - if (!plugin->set_pc(iset->astate, next.addr, iset->intrpr_priv)) { + // Now we know the size of the destination block. + // Set it and report the control flow change. + next.ctrl_flow.target_block_size = il_bb->size; + RzThreadRingBuf *cf_rbuf = iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW]->rbuf; + if (rz_th_ring_buf_put(cf_rbuf, &next.ctrl_flow) != RZ_THREAD_RING_BUF_OK) { + return false; + } + + RZ_LOG_DEBUG("INTPR: Received il_bb: 0x%" PFMT64x "\n", il_bb->addr); + if (!plugin->set_pc(iset->astate, next.ctrl_flow.actual_target, iset->intrpr_priv)) { rz_warn_if_reached(); goto CLEAN; } diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 7fa697f9443..751a05d7ef9 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -15,25 +15,25 @@ #define MAX_INVOCATIONS_PER_BB 3 static bool eval(RZ_NONNULL RzInterpreterSet *iset, - RZ_NONNULL const RzInterpreterILBB *il_bb, + RZ_NONNULL const RzILCacheBlock *il_bb, void *plugin_data) { ProtoIntrprPluginData *pdata = plugin_data; // Check invocation count of the current address. // Never execute the same address more than MAX_INVOCATIONS_PER_BB times. bool found = false; - HtUUKv *ic_pc = ht_uu_find_kv(pdata->bb_invocation_count, il_bb->bb_addr, &found); + HtUUKv *ic_pc = ht_uu_find_kv(pdata->bb_invocation_count, il_bb->addr, &found); if (found) { ic_pc->value++; - RZ_LOG_DEBUG("Eval BB (ic: %" PFMT64d ") = 0x%" PFMT64x "\n", ic_pc->value, il_bb->bb_addr); + RZ_LOG_DEBUG("Eval BB (ic: %" PFMT64d ") = 0x%" PFMT64x "\n", ic_pc->value, il_bb->addr); if (ic_pc->value > MAX_INVOCATIONS_PER_BB) { // TODO: Make it configurable - RZ_LOG_DEBUG("Reached maximum number of invocations of basic block at 0x%" PFMT64x ". Skipping it.\n", il_bb->bb_addr) - set_pc(iset->astate, il_bb->bb_addr + il_bb->size, plugin_data); + RZ_LOG_DEBUG("Reached maximum number of invocations of basic block at 0x%" PFMT64x ". Skipping it.\n", il_bb->addr) + set_pc(iset->astate, il_bb->addr + il_bb->size, plugin_data); return true; } } else { - ht_uu_update(pdata->bb_invocation_count, il_bb->bb_addr, 1); + ht_uu_update(pdata->bb_invocation_count, il_bb->addr, 1); } // Reset call candidate tracking for each basic block. @@ -45,7 +45,7 @@ static bool eval(RZ_NONNULL RzInterpreterSet *iset, ProtoIntrprAbstrData *apc = AD(iset->astate->pc->abstr_data); ut64 pc = rz_bv_to_ut64(apc->bv); RZ_LOG_DEBUG("Eval PC = 0x%" PFMT64x "\n", pc); - RzInterpreterInsnPkt *pkt = *it; + RzILCacheInsnPkt *pkt = *it; if (!interpreter_prototype_eval_effect(iset, pkt->effect, pkt->insn_pkt_size, plugin_data)) { return false; } @@ -74,9 +74,9 @@ bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, } ut64 next_pc = rz_bv_to_ut64(apc->bv); - RzInterpreterBranch branch = { 0 }; - branch.target_addr = next_pc; - branch.branching_bb_addr = pdata->prev_pc; + RzInterpreterCtrlFlow branch = { 0 }; + branch.target_addr = branch.actual_target = next_pc; + branch.src_block_addr = pdata->prev_pc; rz_vector_push(successors, &branch); return true; } diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index 9ef58b10e90..b6fc48786be 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -15,7 +15,7 @@ rz_inquiry_sources = [ 'algorithms/revng_fcn_detection.c', 'bb_cfg.c', 'inquiry.c', - 'inquiry_helpers.c', + 'il_cache.c', ] subdir('interpreter') From d5a0f27492c49b3d777c6e5fb071d9a1a3e588db Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 6 May 2026 19:33:52 +0200 Subject: [PATCH 267/334] Fix NULL derefence --- librz/inquiry/bb_cfg.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index 8baca2ef27c..da90d52f707 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -260,15 +260,17 @@ RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg) { // add edge between big to small bb, move old edges to small_bb. RzGraphEdge *e; RzIterator *out_edges = rz_inquiry_bb_cfg_get_outgoing_edges(cfg, big_bb_addr); - rz_iterator_foreach(out_edges, e) { - ut64 to = rz_graph_node_get_id(rz_graph_edge_get_to(e)); - RzInquiryBBCFGEdgeType type = (utptr)rz_graph_edge_get_data(e); - if (!rz_inquiry_bb_cfg_add_edge(cfg, small_bb_addr, to, type)) { - RZ_LOG_DEBUG("Did not add edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", big_bb_addr, small_bb_addr); - continue; + if (out_edges) { + rz_iterator_foreach(out_edges, e) { + ut64 to = rz_graph_node_get_id(rz_graph_edge_get_to(e)); + RzInquiryBBCFGEdgeType type = (utptr)rz_graph_edge_get_data(e); + if (!rz_inquiry_bb_cfg_add_edge(cfg, small_bb_addr, to, type)) { + RZ_LOG_DEBUG("Did not add edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", big_bb_addr, small_bb_addr); + continue; + } } + rz_iterator_free(out_edges); } - rz_iterator_free(out_edges); rz_inquiry_bb_cfg_del_out_edges(cfg, big_bb_addr); if (!rz_inquiry_bb_cfg_add_edge(cfg, big_bb_addr, small_bb_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF)) { From c86521e75544e2384d95ea130e707084910e64c7 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 7 May 2026 16:21:47 +0200 Subject: [PATCH 268/334] Fix filter of xrefs to check for permissions. --- librz/include/rz_inquiry.h | 2 +- librz/inquiry/il_cache.c | 1 + librz/inquiry/inquiry.c | 22 ++++++++++++++++++---- librz/inquiry/interpreter/prototype/eval.c | 13 +++++++------ 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 671738b6257..6b1fda23a1d 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -49,7 +49,7 @@ RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_NO RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void); RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *q); -RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzPVector /**/ *allowed_segments); +RZ_API bool rz_inquiry_xref_interpreter_filter(RZ_NONNULL const RzAnalysisXRef *xref, RZ_NONNULL const RzPVector /**/ *allowed_segments); RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzSetU /**/ *entry_points, RZ_NONNULL const RzVector /**/ *ignored_code); diff --git a/librz/inquiry/il_cache.c b/librz/inquiry/il_cache.c index 64978868def..62c58f51276 100644 --- a/librz/inquiry/il_cache.c +++ b/librz/inquiry/il_cache.c @@ -261,6 +261,7 @@ static bool lift_executable_maps(RzILCache *cache) { } return true; } + RZ_API bool rz_il_cache_serve(RZ_NONNULL RzILCache *cache) { rz_return_val_if_fail(cache, false); rz_th_lock_enter(cache->n_serving_lock); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 81bb067386b..1efcbb3ead8 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -168,16 +168,30 @@ RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref) { rz_vector_push(iq->dynamic_xrefs, (void *)xref); } -RZ_API bool rz_inquiry_xref_interpreter_filter(ut64 *xref_to_addr, RZ_NONNULL const RzPVector /**/ *allowed_segments) { - rz_return_val_if_fail(xref_to_addr && allowed_segments, false); +RZ_API bool rz_inquiry_xref_interpreter_filter(RZ_NONNULL const RzAnalysisXRef *xref, RZ_NONNULL const RzPVector /**/ *allowed_segments) { + rz_return_val_if_fail(xref && allowed_segments, false); void **it; rz_pvector_foreach (allowed_segments, it) { const RzBinSection *sec = *it; ut64 start = sec->vaddr; ut64 end = start + sec->vsize; - if (RZ_BETWEEN(start, *xref_to_addr, end)) { - return true; + if (RZ_BETWEEN_EXCL(start, xref->to, end)) { + switch (xref->type) { + case RZ_ANALYSIS_XREF_TYPE_CALL_RET: + case RZ_ANALYSIS_XREF_TYPE_CALL: + case RZ_ANALYSIS_XREF_TYPE_CODE: + case RZ_ANALYSIS_XREF_TYPE_RETURN: + return sec->perm & RZ_PERM_X; + case RZ_ANALYSIS_XREF_TYPE_MEM_WRITE: + return sec->perm & RZ_PERM_W; + case RZ_ANALYSIS_XREF_TYPE_MEM_READ: + case RZ_ANALYSIS_XREF_TYPE_STRING: + return sec->perm & RZ_PERM_R; + case RZ_ANALYSIS_XREF_TYPE_NULL: + rz_warn_if_reached(); + return false; + } } } return false; diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index d2f9916a51a..4e7c33d0a66 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -37,12 +37,13 @@ bool report_yield_xref( rz_return_val_if_fail(yrbuf, false); ut64 to_addr = rz_bv_to_ut64(to->bv); - if (yrbuf->filter(&to_addr, yrbuf->filter_data->io_boundaries)) { - RzAnalysisXRef xref = { 0 }; - xref.bb_addr = iset->astate->bb_addr; - xref.from = from; - xref.to = to_addr; - xref.type = type; + RzAnalysisXRef xref = { 0 }; + xref.bb_addr = iset->astate->bb_addr; + xref.from = from; + xref.to = to_addr; + xref.type = type; + if (yrbuf->filter(&xref, yrbuf->filter_data->io_boundaries)) { + RZ_LOG_DEBUG("REPORT xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref.from, xref.to, rz_analysis_ref_type_tostring(xref.type)); if (rz_th_ring_buf_put(yrbuf->rbuf, &xref) != RZ_THREAD_RING_BUF_OK) { return false; } From a7f1218b6dcbc280153b92783ef2e1ee1fec69e2 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 7 May 2026 18:10:16 +0200 Subject: [PATCH 269/334] Rename --- librz/include/rz_inquiry/rz_bb_graph.h | 5 +---- librz/inquiry/algorithms/revng_fcn_detection.c | 4 ++-- librz/inquiry/bb_cfg.c | 7 ++++--- librz/inquiry/inquiry.c | 8 ++++---- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/librz/include/rz_inquiry/rz_bb_graph.h b/librz/include/rz_inquiry/rz_bb_graph.h index ad53f32f6c4..180afaff43f 100644 --- a/librz/include/rz_inquiry/rz_bb_graph.h +++ b/librz/include/rz_inquiry/rz_bb_graph.h @@ -56,12 +56,9 @@ typedef struct { RzGraph /**/ *graph; } RzInquiryBBCFG; -RZ_IPI RZ_OWN RzInquiryBB *rz_inquiry_bb_new(ut64 addr, ut64 size); -RZ_IPI void rz_inquiry_bb_free(RZ_NULLABLE RZ_OWN RzInquiryBB *bb); - RZ_IPI RZ_OWN RzInquiryBBCFG *rz_inquiry_bb_cfg_new(RzGraphImplType impl_type); RZ_IPI void rz_inquiry_bb_cfg_free(RZ_NULLABLE RZ_OWN RzInquiryBBCFG *bb_cfg); -RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size); +RZ_IPI bool rz_inquiry_bb_cfg_add_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size); RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, const RzVector /**/ *xrefs); RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RzInquiryBB *bb); diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index bdd933af220..019cfe25b5f 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -96,7 +96,7 @@ static void recurse_into_fcn_bbs( rz_warn_if_reached(); goto err_return; } - if (!rz_inquiry_bb_cfg_add_basic_block(fcn->bb_cfg, this_bb.addr, this_bb.size)) { + if (!rz_inquiry_bb_cfg_add_block(fcn->bb_cfg, this_bb.addr, this_bb.size)) { rz_warn_if_reached(); goto err_return; } @@ -107,7 +107,7 @@ static void recurse_into_fcn_bbs( rz_warn_if_reached(); goto err_return; } - if (!rz_inquiry_bb_cfg_add_basic_block(fcn->bb_cfg, from_bb.addr, from_bb.size)) { + if (!rz_inquiry_bb_cfg_add_block(fcn->bb_cfg, from_bb.addr, from_bb.size)) { rz_warn_if_reached(); goto err_return; } diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index da90d52f707..76cbacb782d 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -143,7 +143,7 @@ RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_incoming_edg * \brief Does not update the BB if it is already present. * Returns false if it already exists. */ -RZ_IPI bool rz_inquiry_bb_cfg_add_basic_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size) { +RZ_IPI bool rz_inquiry_bb_cfg_add_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size) { RzInquiryBB *bb = RZ_NEW(RzInquiryBB); if (!bb) { return false; @@ -214,9 +214,10 @@ static int cmp(const ut64 *a, const ut64 *b, void *user) { } /** - * \brief Reduces all basic blocks in the cfg to their minimum size. + * \brief Reduces all blocks in the cfg to their minimum size. * Removing duplicates and overlapping basic blocks. - * This function makes each basic block just have a single entry point. + * This function makes each block just have a single entry point. + * This makes each block as "basic block" in the sense of One Entry/One Exit. */ RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg) { // Index is end address of bb, values are starting address of bbs with that end address. diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 1efcbb3ead8..fc3236cdeb8 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -289,11 +289,11 @@ static bool get_branch_targets(RzCore *core, RzSetU *branch_targets) { static bool log_control_flow(RzInquiry *inquiry, RzInterpreterCtrlFlow *cf) { RZ_LOG_DEBUG("INQUIRY: Received control flow: 0x%" PFMT64x " size: %" PFMTSZu " (alt: 0x%" PFMT64x ")\n", cf->target_addr, cf->target_block_size, cf->alt_target); - rz_inquiry_bb_cfg_add_basic_block(inquiry->bb_cfg, cf->actual_target, cf->target_block_size); + rz_inquiry_bb_cfg_add_block(inquiry->bb_cfg, cf->actual_target, cf->target_block_size); if (cf->alt_target) { // Add a dummy basic block at the address the call originally jumped to. // This is the basic block for the imported function. - rz_inquiry_bb_cfg_add_basic_block(inquiry->bb_cfg, cf->target_addr, 1); + rz_inquiry_bb_cfg_add_block(inquiry->bb_cfg, cf->target_addr, 1); } // Add a simple control flow edge here. // It gets later updated to another type if a reported xref has it. @@ -534,7 +534,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_warn_if_reached(); goto error_free; } - size_t n_threads = 1; + size_t n_threads = 8; iset_map = RZ_NEWS0(struct ituple, n_threads); RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM] = { 0 }; @@ -755,7 +755,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, char *bstr = rz_il_cache_block_str(block); RZ_LOG_DEBUG("Inquiry: Add ILCache block: %s\n", bstr); free(bstr); - rz_inquiry_bb_cfg_add_basic_block(core->inquiry->bb_cfg, block->addr, block->size); + rz_inquiry_bb_cfg_add_block(core->inquiry->bb_cfg, block->addr, block->size); } rz_iterator_free(iter); if (!rz_inquiry_bb_cfg_add_xrefs(core->inquiry->bb_cfg, rz_il_cache_get_static_xrefs(il_cache))) { From 5d8ad31a353439e17fc20a9dba7f4b451188200b Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 7 May 2026 18:40:47 +0200 Subject: [PATCH 270/334] Stop cache early --- librz/inquiry/inquiry.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index fc3236cdeb8..95a9de78f69 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -499,6 +499,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_cons_push(); + RZ_LOG_DEBUG("Create IL Cache"); RzILCache *il_cache = rz_il_cache_new(core->analysis, core->io, rz_bin_object_get_sections(core->bin->cur->o), RZ_IL_CACHE_CONFIG_NOP_UNLIFTED | RZ_IL_CACHE_CONFIG_SLEEP_SHORT); @@ -580,6 +581,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // // Spawn the IL cache. // + RZ_LOG_DEBUG("Spawn IL Cache"); il_cache_th = rz_th_new((RzThreadFunction)rz_il_cache_serve, il_cache); ut64 intpr_terminated = 0; @@ -740,6 +742,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, } rz_interpreter_set_free(iset_map[i].iset); } + rz_il_cache_stop_serving(il_cache); if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { eprintf("\n"); From cb2f8e527b23f76e05192d50f817a6534b26f299 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 7 May 2026 19:05:01 +0200 Subject: [PATCH 271/334] Speedup reduce function --- librz/include/rz_inquiry/rz_bb_graph.h | 1 + librz/inquiry/bb_cfg.c | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/librz/include/rz_inquiry/rz_bb_graph.h b/librz/include/rz_inquiry/rz_bb_graph.h index 180afaff43f..240050c5a3b 100644 --- a/librz/include/rz_inquiry/rz_bb_graph.h +++ b/librz/include/rz_inquiry/rz_bb_graph.h @@ -64,6 +64,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, const RzVector /**/ *rz_inquiry_bb_cfg_get_outgoing_edges(const RzInquiryBBCFG *cfg, ut64 bb_addr); RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_incoming_edges(const RzInquiryBBCFG *cfg, ut64 bb_addr); diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index 76cbacb782d..b77ab07ba5f 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -6,6 +6,7 @@ #include "rz_util/rz_assert.h" #include "rz_util/rz_graph.h" #include "rz_util/rz_iterator.h" +#include "rz_vector.h" #include static ut64 hash_node(const void *data) { @@ -68,6 +69,7 @@ static bool edge_from(const RzGraphEdge *e, void *addr) { } RZ_IPI bool rz_inquiry_bb_cfg_del_out_edges(RzInquiryBBCFG *cfg, ut64 bb_addr) { + return rz_graph_del_edges(cfg->graph, edge_from, RZ_GRAPH_INT_AS_DATA(bb_addr)); } @@ -86,6 +88,10 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 t return rz_graph_add_edge_by_id(cfg->graph, from_bb, to_bb, RZ_GRAPH_INT_AS_DATA(type)); } +RZ_IPI bool rz_inquiry_bb_cfg_del_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb) { + return rz_graph_del_edge_by_id(cfg->graph, from_bb, to_bb); +} + static bool is_cf_edge(const RzGraphEdge *e, void *unused) { RzInquiryBBCFGEdgeType type = (utptr)rz_graph_edge_get_data(e); return type == RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF || type == RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE; @@ -262,18 +268,30 @@ RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg) { RzGraphEdge *e; RzIterator *out_edges = rz_inquiry_bb_cfg_get_outgoing_edges(cfg, big_bb_addr); if (out_edges) { + RzVector big_out_addr = { 0 }; + rz_vector_init(&big_out_addr, sizeof(ut64), NULL, NULL); + rz_iterator_foreach(out_edges, e) { ut64 to = rz_graph_node_get_id(rz_graph_edge_get_to(e)); RzInquiryBBCFGEdgeType type = (utptr)rz_graph_edge_get_data(e); + if (type == RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL) { + // Keep call edges. + continue; + } if (!rz_inquiry_bb_cfg_add_edge(cfg, small_bb_addr, to, type)) { RZ_LOG_DEBUG("Did not add edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", big_bb_addr, small_bb_addr); continue; } + rz_vector_push(&big_out_addr, &to); } rz_iterator_free(out_edges); + ut64 *to; + rz_vector_foreach_prev (&big_out_addr, to) { + rz_inquiry_bb_cfg_del_edge(cfg, big_bb_addr, *to); + } + rz_vector_fini(&big_out_addr); } - rz_inquiry_bb_cfg_del_out_edges(cfg, big_bb_addr); if (!rz_inquiry_bb_cfg_add_edge(cfg, big_bb_addr, small_bb_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF)) { RZ_LOG_DEBUG("Did not add edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", big_bb_addr, small_bb_addr); continue; From 293845f90cad5fc51b767763bf4e1eeb121290c9 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 11 May 2026 20:16:25 +0200 Subject: [PATCH 272/334] Clean up reduce function for bb cfgs. --- librz/inquiry/bb_cfg.c | 184 ++++++++++++++++++++++++---------------- librz/inquiry/inquiry.c | 5 +- 2 files changed, 115 insertions(+), 74 deletions(-) diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index b77ab07ba5f..755fa1e7244 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -15,7 +15,9 @@ static ut64 hash_node(const void *data) { } static RZ_OWN char *node_formatter(const RzGraphNode *n) { - return rz_str_newf("[label=\"0x%" PFMT64x "\"]", rz_graph_node_get_id(n)); + const RzInquiryBB *bb = rz_graph_node_get_data(n); + return rz_str_newf("[label=\"0x%" PFMT64x ":%" PFMT64u "\"]", + bb->addr, bb->size); } static RZ_OWN char *edge_formatter(const RzGraphEdge *e) { @@ -210,10 +212,10 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, const RzVector /*addr < b->addr) { return -1; - } else if (*a > *b) { + } else if (a->addr > b->addr) { return 1; } return 0; @@ -221,90 +223,130 @@ static int cmp(const ut64 *a, const ut64 *b, void *user) { /** * \brief Reduces all blocks in the cfg to their minimum size. - * Removing duplicates and overlapping basic blocks. + * Removing duplicates and split overlapping blocks to basic blocks. + * * This function makes each block just have a single entry point. - * This makes each block as "basic block" in the sense of One Entry/One Exit. + * So each becomes a basic block (one Entry/one Exit). + * + * Example: + * + * +- 0x1000 + * | + * | + * | +- 0x1058 + * | | + * | | +- 0x105c + * | | | + * | | | + * | | | + * +-+-+- 0x1080 + * | + * | JUMP + * v + * +-- 0x1084 + * | + * | + * | +- 0x108c + * | | + * | | + * +-+- 0x10a0 + * + * Gets converted to: + * + * +-- 0x1000 + * | + * +-- + * | CTRL FLOW + * v + * +-- 0x1058 + * | + * +-- + * | CTRL FLOW + * v + * +-- 0x105c + * | + * | + * | + * +-- 0x1080 + * | + * | JUMP + * v + * +-- 0x1084 + * | + * +-- + * | CTRL FLOW + * v + * +-- 0x108c + * | + * | + * +-- 0x10a0 */ RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg) { - // Index is end address of bb, values are starting address of bbs with that end address. - HtUP *overlapping_bbs = ht_up_new(NULL, (HtUPFreeValue)rz_vector_free); + RzPVector blocks = { 0 }; + rz_pvector_init(&blocks, NULL); + rz_pvector_reserve(&blocks, rz_graph_get_n_nodes(cfg->graph)); RzGraphNode *n; RzIterator *iter = rz_graph_get_nodes(cfg->graph); rz_iterator_foreach(iter, n) { - const RzInquiryBB *bb = rz_graph_node_get_data(n); - ut64 end = bb->addr + bb->size; - RzVector *start_addresses = ht_up_find(overlapping_bbs, end, NULL); - if (!start_addresses) { - start_addresses = rz_vector_new(sizeof(ut64), NULL, NULL); - ht_up_insert(overlapping_bbs, end, start_addresses); - } - // Cast due to not constified vector API. - rz_vector_push(start_addresses, (void *)&bb->addr); + RzInquiryBB *b = rz_graph_node_get_data_mut(n); + rz_pvector_push(&blocks, b); // Cast because API is not constified. } rz_iterator_free(iter); - void **it; - iter = ht_up_as_iter(overlapping_bbs); - rz_iterator_foreach(iter, it) { - RzVector *addrs = *it; - size_t i = rz_vector_len(addrs); - if (i == 1) { - continue; - } - // Sort start addresses. - rz_vector_sort(addrs, (RzVectorComparator)cmp, false, NULL); - for (i = i - 1; i > 0; i--) { - ut64 small_bb_addr = *((ut64 *)rz_vector_index_ptr(addrs, i)); - ut64 big_bb_addr = *((ut64 *)rz_vector_index_ptr(addrs, i - 1)); - rz_goto_if_fail(small_bb_addr > big_bb_addr, fail); + // Sorting by starting address. + // Note that this means overlapping blocks (with the same end address) + // will lie sequentially in this vector. + rz_pvector_sort(&blocks, (RzPVectorComparator)cmp, NULL); - // Change size of big bb - RzInquiryBB *big_bb = rz_graph_node_get_data_mut(rz_graph_find_node(cfg->graph, big_bb_addr)); - rz_goto_if_fail(big_bb, fail); - big_bb->size = small_bb_addr - big_bb_addr; + RzVector outedges = { 0 }; + rz_vector_init(&outedges, sizeof(ut64), NULL, NULL); + // It is unlikely any node will have more than 8 outgoing edges. + rz_vector_reserve(&outedges, 8); - // add edge between big to small bb, move old edges to small_bb. - RzGraphEdge *e; - RzIterator *out_edges = rz_inquiry_bb_cfg_get_outgoing_edges(cfg, big_bb_addr); - if (out_edges) { - RzVector big_out_addr = { 0 }; - rz_vector_init(&big_out_addr, sizeof(ut64), NULL, NULL); + size_t n_blocks = rz_pvector_len(&blocks); + for (size_t i = 0; i < n_blocks - 1; ++i) { + RzInquiryBB *a = rz_pvector_at(&blocks, i); - rz_iterator_foreach(out_edges, e) { - ut64 to = rz_graph_node_get_id(rz_graph_edge_get_to(e)); - RzInquiryBBCFGEdgeType type = (utptr)rz_graph_edge_get_data(e); - if (type == RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL) { - // Keep call edges. - continue; - } - if (!rz_inquiry_bb_cfg_add_edge(cfg, small_bb_addr, to, type)) { - RZ_LOG_DEBUG("Did not add edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", big_bb_addr, small_bb_addr); - continue; - } - rz_vector_push(&big_out_addr, &to); - } - rz_iterator_free(out_edges); - ut64 *to; - rz_vector_foreach_prev (&big_out_addr, to) { - rz_inquiry_bb_cfg_del_edge(cfg, big_bb_addr, *to); - } - rz_vector_fini(&big_out_addr); + // Split of all blocks a overlaps with. + for (; i < n_blocks - 1; ++i) { + RzInquiryBB *b = rz_pvector_at(&blocks, i + 1); + if ((a->addr + a->size) != (b->addr + b->size)) { + // End addresses don't match => b lies not within a. + break; } - - if (!rz_inquiry_bb_cfg_add_edge(cfg, big_bb_addr, small_bb_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF)) { - RZ_LOG_DEBUG("Did not add edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", big_bb_addr, small_bb_addr); + // b and a have the same end address => b lies within a. + // Shrink a. + a->size = b->addr - a->addr; + RzIterator *out_iter = rz_graph_out_edges_by_id(cfg->graph, a->addr); + if (!out_iter) { + // No edges to update. + // Check for blocks b overlaps with. + a = b; continue; } + + // Replace a's outgoing edges with a single edge to b, + RzGraphEdge *e; + rz_iterator_foreach(out_iter, e) { + const RzGraphNode *to_node = rz_graph_edge_get_to(e); + ut64 to = rz_graph_node_get_id(to_node); + rz_vector_push(&outedges, &to); + } + rz_iterator_free(out_iter); + + ut64 *to; + rz_vector_foreach (&outedges, to) { + rz_graph_del_edge_by_id(cfg->graph, a->addr, *to); + } + rz_vector_purge(&outedges); + rz_graph_add_edge_by_id(cfg->graph, a->addr, b->addr, RZ_GRAPH_INT_AS_DATA(RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF)); + // Check for blocks b overlaps with. + a = b; } } - rz_iterator_free(iter); - ht_up_free(overlapping_bbs); - return true; + rz_vector_fini(&outedges); + rz_pvector_fini(&blocks); -fail: - rz_iterator_free(iter); - ht_up_free(overlapping_bbs); - rz_warn_if_reached(); - return false; + return true; } diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 95a9de78f69..df0bdf3a923 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: 2025 RizinOrg // SPDX-License-Identifier: LGPL-3.0-only -#include #include #include #include @@ -502,7 +501,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_LOG_DEBUG("Create IL Cache"); RzILCache *il_cache = rz_il_cache_new(core->analysis, core->io, rz_bin_object_get_sections(core->bin->cur->o), - RZ_IL_CACHE_CONFIG_NOP_UNLIFTED | RZ_IL_CACHE_CONFIG_SLEEP_SHORT); + RZ_IL_CACHE_CONFIG_NOP_UNLIFTED | RZ_IL_CACHE_CONFIG_NO_SLEEP); if (!il_cache) { return_code = false; goto error_free; @@ -535,7 +534,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_warn_if_reached(); goto error_free; } - size_t n_threads = 8; + size_t n_threads = 32; iset_map = RZ_NEWS0(struct ituple, n_threads); RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM] = { 0 }; From fc29b67c8de465008ab489f48952c1dd1ffdcd88 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 11 May 2026 20:54:03 +0200 Subject: [PATCH 273/334] Rename BB to block, since they are no BBs. --- librz/include/rz_inquiry/rz_bb_graph.h | 7 +++++-- .../inquiry/algorithms/revng_fcn_detection.c | 16 +++++++-------- librz/inquiry/bb_cfg.c | 20 +++++++++---------- librz/inquiry/il_cache.c | 6 +++--- librz/inquiry/inquiry.c | 14 +++++++++---- .../interpreter/p/interpreter_prototype.c | 10 +++++----- 6 files changed, 41 insertions(+), 32 deletions(-) diff --git a/librz/include/rz_inquiry/rz_bb_graph.h b/librz/include/rz_inquiry/rz_bb_graph.h index 240050c5a3b..2624e545dfe 100644 --- a/librz/include/rz_inquiry/rz_bb_graph.h +++ b/librz/include/rz_inquiry/rz_bb_graph.h @@ -7,10 +7,13 @@ #include #include +/** + * \brief A block of instructions which end in a jump/call/exit. + */ typedef struct { ut64 addr; ut64 size; -} RzInquiryBB; +} RzInquiryBlock; /** * \brief The different kind of BB CFG edges. @@ -61,7 +64,7 @@ RZ_IPI void rz_inquiry_bb_cfg_free(RZ_NULLABLE RZ_OWN RzInquiryBBCFG *bb_cfg); RZ_IPI bool rz_inquiry_bb_cfg_add_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size); RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, const RzVector /**/ *xrefs); -RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RzInquiryBB *bb); +RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RzInquiryBlock *bb); RZ_IPI bool rz_inquiry_bb_cfg_del_out_edges(RzInquiryBBCFG *cfg, ut64 bb_addr); RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBBCFGEdgeType type); RZ_IPI bool rz_inquiry_bb_cfg_del_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb); diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index 019cfe25b5f..90c63370f59 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -7,14 +7,15 @@ * chapter: "4.2 Function boundaries recovery" * doi: 10.1145/3033019.3033028 * - * The actualy algorithm is really simple. + * The actually algorithm is really simple. * * TERMS: * - * Basic Block: A sequence of instructions ending with exactly one known entry point and one branch at the end. + * Basic Block (BB): A sequence of instructions having exactly one entry point and + * one jump/call/exit at the end. * Call candidate: RzAnalysisCallCandidate * Candidate function entry points (CFEP): Addresses of possible function entry point. - * Return Addresses: address after a call candidate BB, with an xref to it. + * Return Addresses: Address after a call candidate BB, with an xref to it. * Basic Block CFG: Control Flow Graph with basic blocks as nodes. * * IN: @@ -28,15 +29,14 @@ * It simply iterates over all CFEPs. * For each one it follows its edges in the bb_cfg. * If an edge belongs to a call, it is NOT taken. - * Instead it continues with the basic block after the call, if it is a return address. * * Every walked edge and the basic blocks are added to the function. * - * Tail calls are ignored. + * Tail calls are not modelled. * * OUT: * - List of functions - * - Each functions starts with one CFEP, and has a sub-graph in the bb_cfg. + * - Each functions starts with one CFEP and is a sub-graph in the bb_cfg. */ #include "rz_analysis.h" @@ -91,7 +91,7 @@ static void recurse_into_fcn_bbs( // // Add edge // - RzInquiryBB this_bb = { 0 }; + RzInquiryBlock this_bb = { 0 }; if (!rz_inquiry_bb_cfg_get_basic_block(binary_bb_cfg, this_bb_addr, &this_bb)) { rz_warn_if_reached(); goto err_return; @@ -102,7 +102,7 @@ static void recurse_into_fcn_bbs( } if (edge_type != RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE) { - RzInquiryBB from_bb = { 0 }; + RzInquiryBlock from_bb = { 0 }; if (!rz_inquiry_bb_cfg_get_basic_block(binary_bb_cfg, predecessor_bb_addr, &from_bb)) { rz_warn_if_reached(); goto err_return; diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bb_cfg.c index 755fa1e7244..a4ed4b5bd5b 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bb_cfg.c @@ -10,12 +10,12 @@ #include static ut64 hash_node(const void *data) { - const RzInquiryBB *bb = data; + const RzInquiryBlock *bb = data; return bb->addr; } static RZ_OWN char *node_formatter(const RzGraphNode *n) { - const RzInquiryBB *bb = rz_graph_node_get_data(n); + const RzInquiryBlock *bb = rz_graph_node_get_data(n); return rz_str_newf("[label=\"0x%" PFMT64x ":%" PFMT64u "\"]", bb->addr, bb->size); } @@ -116,7 +116,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_update_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut6 return rz_graph_update_edge_by_id(cfg->graph, from_bb, to_bb, RZ_GRAPH_INT_AS_DATA(type), is_cf_edge, NULL); } -RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RZ_NULLABLE RzInquiryBB *bb) { +RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RZ_NULLABLE RzInquiryBlock *bb) { rz_return_val_if_fail(cfg, false); const RzGraphNode *n = rz_graph_find_node(cfg->graph, bb_addr); if (!n) { @@ -124,7 +124,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb return false; } if (bb) { - const RzInquiryBB *n_data = rz_graph_node_get_data(n); + const RzInquiryBlock *n_data = rz_graph_node_get_data(n); bb->addr = n_data->addr; bb->size = n_data->size; } @@ -152,7 +152,7 @@ RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_incoming_edg * Returns false if it already exists. */ RZ_IPI bool rz_inquiry_bb_cfg_add_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size) { - RzInquiryBB *bb = RZ_NEW(RzInquiryBB); + RzInquiryBlock *bb = RZ_NEW(RzInquiryBlock); if (!bb) { return false; } @@ -186,7 +186,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, const RzVector /*bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL)) { RZ_LOG_DEBUG("Did not add CALL edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, xref->to); } - RzInquiryBB bb = { 0 }; + RzInquiryBlock bb = { 0 }; if (!rz_inquiry_bb_cfg_get_basic_block(cfg, xref->bb_addr, &bb)) { rz_warn_if_reached(); break; @@ -212,7 +212,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, const RzVector /*addr < b->addr) { return -1; } else if (a->addr > b->addr) { @@ -289,7 +289,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg) { RzGraphNode *n; RzIterator *iter = rz_graph_get_nodes(cfg->graph); rz_iterator_foreach(iter, n) { - RzInquiryBB *b = rz_graph_node_get_data_mut(n); + RzInquiryBlock *b = rz_graph_node_get_data_mut(n); rz_pvector_push(&blocks, b); // Cast because API is not constified. } rz_iterator_free(iter); @@ -306,11 +306,11 @@ RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg) { size_t n_blocks = rz_pvector_len(&blocks); for (size_t i = 0; i < n_blocks - 1; ++i) { - RzInquiryBB *a = rz_pvector_at(&blocks, i); + RzInquiryBlock *a = rz_pvector_at(&blocks, i); // Split of all blocks a overlaps with. for (; i < n_blocks - 1; ++i) { - RzInquiryBB *b = rz_pvector_at(&blocks, i + 1); + RzInquiryBlock *b = rz_pvector_at(&blocks, i + 1); if ((a->addr + a->size) != (b->addr + b->size)) { // End addresses don't match => b lies not within a. break; diff --git a/librz/inquiry/il_cache.c b/librz/inquiry/il_cache.c index 62c58f51276..579e263f461 100644 --- a/librz/inquiry/il_cache.c +++ b/librz/inquiry/il_cache.c @@ -141,7 +141,7 @@ RZ_API RZ_OWN RzILCacheBlock *rz_il_cache_lift_il_block(const RzILCache *cache, goto fail; } il_block->addr = addr; - RZ_LOG_DEBUG("ILCache: Gen BB:\n"); + RZ_LOG_DEBUG("ILCache: Gen block:\n"); bool sparc_add_delayed_insn = false; bool changes_cf = true; do { @@ -201,7 +201,7 @@ RZ_API RZ_OWN RzILCacheBlock *rz_il_cache_lift_il_block(const RzILCache *cache, rz_analysis_op_fini(&op); rz_mem_memzero(buf, max_read_size); if (sparc_add_delayed_insn) { - // Instruction was added, now the BB is complete. + // Instruction was added, now the block is complete. break; } if (changes_cf && RZ_STR_EQ(rz_analysis_plugin_current(cache->analysis)->arch, "sparc")) { @@ -230,7 +230,7 @@ static const RzILCacheBlock *lift_il_block(RzILCache *cache, ut64 addr) { free(bstr); return block; } - RZ_LOG_DEBUG("ILCache: Lift new BB\n"); + RZ_LOG_DEBUG("ILCache: Lift new block\n"); block = rz_il_cache_lift_il_block(cache, addr); if (!block) { RZ_LOG_DEBUG("ILCache: Failed to lift block at 0x%" PFMT64x "\n", addr); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index df0bdf3a923..e6fd969146d 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -117,7 +117,7 @@ RZ_API RZ_OWN char *rz_inquiry_function_str(const RzInquiryFunction *fcn) { RzIterator *iter = rz_graph_get_nodes(fcn->bb_cfg->graph); RzGraphNode *n; rz_iterator_foreach(iter, n) { - const RzInquiryBB *bb = rz_graph_node_get_data(n); + const RzInquiryBlock *bb = rz_graph_node_get_data(n); rz_strbuf_appendf(buf, "\t0x%" PFMT64x ":0x%" PFMT64x "\n", bb->addr, bb->size); } rz_iterator_free(iter); @@ -534,7 +534,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_warn_if_reached(); goto error_free; } - size_t n_threads = 32; + size_t n_threads = 1; iset_map = RZ_NEWS0(struct ituple, n_threads); RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM] = { 0 }; @@ -766,9 +766,15 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, if (!rz_inquiry_bb_cfg_add_xrefs(core->inquiry->bb_cfg, core->inquiry->dynamic_xrefs)) { rz_warn_if_reached(); } + char *g = rz_inquiry_bb_cfg_as_dot(core->inquiry->bb_cfg, "not_reduced"); + printf("%s\n", g); + free(g); if (!rz_inquiry_bb_cfg_reduce(core->inquiry->bb_cfg)) { rz_warn_if_reached(); } + g = rz_inquiry_bb_cfg_as_dot(core->inquiry->bb_cfg, "reduced"); + printf("%s\n", g); + free(g); RZ_LOG_DEBUG("INQUIRY: Done\n"); @@ -798,7 +804,7 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry RzIterator *iter = rz_graph_get_nodes(inquiry->bb_cfg->graph); RzGraphNode *n; rz_iterator_foreach(iter, n) { - const RzInquiryBB *bb = rz_graph_node_get_data(n); + const RzInquiryBlock *bb = rz_graph_node_get_data(n); rz_analysis_add_bb(analysis, bb->addr, bb->size); RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, bb->addr); RzIterator *out_edges = rz_inquiry_bb_cfg_get_outgoing_edges(inquiry->bb_cfg, bb->addr); @@ -865,7 +871,7 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry RzIterator *iter = rz_graph_get_nodes(fcn->bb_cfg->graph); RzGraphNode *n; rz_iterator_foreach(iter, n) { - const RzInquiryBB *bb = rz_graph_node_get_data(n); + const RzInquiryBlock *bb = rz_graph_node_get_data(n); RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, bb->addr); if (!abb && !(abb = rz_analysis_create_block(analysis, bb->addr, bb->size))) { rz_warn_if_reached(); diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 751a05d7ef9..9aca2d2ba13 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -12,7 +12,7 @@ #define INITIAL_STACK_CAPACITY 8 -#define MAX_INVOCATIONS_PER_BB 3 +#define MAX_INVOCATIONS_PER_BLOCK 3 static bool eval(RZ_NONNULL RzInterpreterSet *iset, RZ_NONNULL const RzILCacheBlock *il_bb, @@ -20,13 +20,13 @@ static bool eval(RZ_NONNULL RzInterpreterSet *iset, ProtoIntrprPluginData *pdata = plugin_data; // Check invocation count of the current address. - // Never execute the same address more than MAX_INVOCATIONS_PER_BB times. + // Never execute the same address more than MAX_INVOCATIONS_PER_BLOCK times. bool found = false; HtUUKv *ic_pc = ht_uu_find_kv(pdata->bb_invocation_count, il_bb->addr, &found); if (found) { ic_pc->value++; - RZ_LOG_DEBUG("Eval BB (ic: %" PFMT64d ") = 0x%" PFMT64x "\n", ic_pc->value, il_bb->addr); - if (ic_pc->value > MAX_INVOCATIONS_PER_BB) { + RZ_LOG_DEBUG("Eval BLOCK (ic: %" PFMT64d ") = 0x%" PFMT64x "\n", ic_pc->value, il_bb->addr); + if (ic_pc->value > MAX_INVOCATIONS_PER_BLOCK) { // TODO: Make it configurable RZ_LOG_DEBUG("Reached maximum number of invocations of basic block at 0x%" PFMT64x ". Skipping it.\n", il_bb->addr) set_pc(iset->astate, il_bb->addr + il_bb->size, plugin_data); @@ -39,7 +39,7 @@ static bool eval(RZ_NONNULL RzInterpreterSet *iset, // Reset call candidate tracking for each basic block. memset(&pdata->call_cand, 0, sizeof(pdata->call_cand)); - // Now execute the actual effects of the BB. + // Now execute the actual effects of the BLOCK. void **it; rz_pvector_foreach (il_bb->il_ops, it) { ProtoIntrprAbstrData *apc = AD(iset->astate->pc->abstr_data); From 853233aad35660019b63391d9a348de97e6fa6dd Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 11 May 2026 21:10:46 +0200 Subject: [PATCH 274/334] Add prefixes to log messages. --- librz/inquiry/inquiry.c | 26 +++++++++---------- librz/inquiry/interpreter/interpreter.c | 16 ++++++------ .../interpreter/p/interpreter_prototype.c | 6 ++--- librz/inquiry/interpreter/prototype/eval.c | 12 ++++----- .../interpreter/prototype/eval_effect.c | 2 +- 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index e6fd969146d..bac4ed68cd1 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -197,7 +197,7 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(RZ_NONNULL const RzAnalysisXRef * } static void handle_io_request(RzPVector /**/ *il_mems, RzInterpreterIORequest *io_req, RZ_OUT RzInterpreterIOResult *io_res) { - RZ_LOG_DEBUG("INQUIRY: Received IO %s request: mem:%" PFMTSZd " 0x%" PFMT64x "\n", + RZ_LOG_DEBUG("inquiry: Received IO %s request: mem:%" PFMTSZd " 0x%" PFMT64x "\n", io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", io_req->mem_idx, rz_bv_to_ut64(io_req->addr)); @@ -218,7 +218,7 @@ static void handle_io_request(RzPVector /**/ *il_mems, RzInterpreterI } else { io_res->req_ok = rz_il_mem_storew(mem, io_req->addr, io_req->st_data, io_req->big_endian); } - RZ_LOG_DEBUG("INQUIRY: Sent IO %s result. Success = %s.\n", + RZ_LOG_DEBUG("inquiry: Sent IO %s result. Success = %s.\n", io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", rz_str_bool(io_res->req_ok)); } @@ -286,7 +286,7 @@ static bool get_branch_targets(RzCore *core, RzSetU *branch_targets) { } static bool log_control_flow(RzInquiry *inquiry, RzInterpreterCtrlFlow *cf) { - RZ_LOG_DEBUG("INQUIRY: Received control flow: 0x%" PFMT64x " size: %" PFMTSZu " (alt: 0x%" PFMT64x ")\n", + RZ_LOG_DEBUG("inquiry: Received control flow: 0x%" PFMT64x " size: %" PFMTSZu " (alt: 0x%" PFMT64x ")\n", cf->target_addr, cf->target_block_size, cf->alt_target); rz_inquiry_bb_cfg_add_block(inquiry->bb_cfg, cf->actual_target, cf->target_block_size); if (cf->alt_target) { @@ -312,7 +312,7 @@ static bool handle_yields(RzInquiry *inquiry, RzInterpreterYieldRBuf *yield_rbuf return false; } else if (r == RZ_THREAD_RING_BUF_OK) { rz_inquiry_add_xref(inquiry, &xref); - RZ_LOG_DEBUG("Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref.from, xref.to, rz_analysis_ref_type_tostring(xref.type)); + RZ_LOG_DEBUG("inquiry: Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref.from, xref.to, rz_analysis_ref_type_tostring(xref.type)); } } @@ -341,9 +341,9 @@ static bool handle_yields(RzInquiry *inquiry, RzInterpreterYieldRBuf *yield_rbuf RzAnalysisCallCandidate *cc_clone = RZ_NEW0(RzAnalysisCallCandidate); memcpy(cc_clone, &cc, sizeof(RzAnalysisCallCandidate)); if (ht_up_update(inquiry->call_candidates, cc_clone->bb_addr, cc_clone)) { - RZ_LOG_DEBUG("Overwrote a call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); + RZ_LOG_DEBUG("inquiry: Overwrote a call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); } else { - RZ_LOG_DEBUG("Added call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); + RZ_LOG_DEBUG("inquiry: Added call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); } } } @@ -498,7 +498,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_cons_push(); - RZ_LOG_DEBUG("Create IL Cache"); + RZ_LOG_DEBUG("inquiry: Create IL Cache"); RzILCache *il_cache = rz_il_cache_new(core->analysis, core->io, rz_bin_object_get_sections(core->bin->cur->o), RZ_IL_CACHE_CONFIG_NOP_UNLIFTED | RZ_IL_CACHE_CONFIG_NO_SLEEP); @@ -520,7 +520,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, goto error_free; } - RZ_LOG_DEBUG("INQUIRY: Enforce enabling IO cache.\n"); + RZ_LOG_DEBUG("inquiry: Enforce enabling IO cache.\n"); const char *io_cache_opt = rz_config_get(core->config, "io.cache"); rz_config_set(core->config, "io.cache", "true"); @@ -570,7 +570,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, } // Dispatch prototype interpreter into a thread. - RZ_LOG_DEBUG("INQUIRY: Start main interpretation thread.\n"); + RZ_LOG_DEBUG("inquiry: Start main interpretation thread.\n"); RzThread *interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, intp_iset); iset_map[i].ithread = interpr_th; iset_map[i].iset = intp_iset; @@ -580,7 +580,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // // Spawn the IL cache. // - RZ_LOG_DEBUG("Spawn IL Cache"); + RZ_LOG_DEBUG("inquiry: Spawn IL Cache"); il_cache_th = rz_th_new((RzThreadFunction)rz_il_cache_serve, il_cache); ut64 intpr_terminated = 0; @@ -718,7 +718,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, fatal_error: - RZ_LOG_DEBUG("INQUIRY: Wait for join\n"); + RZ_LOG_DEBUG("inquiry: Wait for join\n"); for (size_t i = 0; i < n_threads; i++) { close_reset_ipc_obj(iset_map[i].iset); // Open semaphore so the interpreter can transition @@ -755,7 +755,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_iterator_foreach(iter, it) { RzILCacheBlock *block = *it; char *bstr = rz_il_cache_block_str(block); - RZ_LOG_DEBUG("Inquiry: Add ILCache block: %s\n", bstr); + RZ_LOG_DEBUG("inquiry: Add ILCache block: %s\n", bstr); free(bstr); rz_inquiry_bb_cfg_add_block(core->inquiry->bb_cfg, block->addr, block->size); } @@ -776,7 +776,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, printf("%s\n", g); free(g); - RZ_LOG_DEBUG("INQUIRY: Done\n"); + RZ_LOG_DEBUG("inquiry: inquiry: inquiry: Done\n"); rz_config_set(core->config, "io.cache", io_cache_opt); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index bde4113139b..8f5d62b0607 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -405,7 +405,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { bool success = true; - RZ_LOG_DEBUG("INTERPRETER Main: Hello.\n"); + RZ_LOG_DEBUG("interpreter: Main: Hello.\n"); RzInterpreterPlugin *plugin = iset->plugin; // @@ -436,7 +436,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { // what the interpreter does in each state. INIT: { - RZ_LOG_DEBUG("Enter INIT\n"); + RZ_LOG_DEBUG("interpreter: Enter INIT\n"); rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_INIT); ut64 entry_point; @@ -466,14 +466,14 @@ INIT: { } EMU: { - RZ_LOG_DEBUG("Enter EMU\n"); + RZ_LOG_DEBUG("interpreter: Enter EMU\n"); rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_EMU); iset->astate->bb_addr = il_bb->addr; iset->astate->bb_size = il_bb->size; // Evaluate the effect on the abstract state. if (!plugin->eval(iset, il_bb, iset->intrpr_priv)) { - RZ_LOG_DEBUG("Eval failed\n"); + RZ_LOG_DEBUG("interpreter: Eval failed\n"); goto CLEAN; } astate_hash = plugin->hash_state(iset->astate, iset->intrpr_priv); @@ -500,7 +500,7 @@ EMU: { rz_vector_pop_front(succ_states, &next); if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || il_bb == RZ_IL_CACHE_FAILED_LIFTING_PTR || !il_bb) { - RZ_LOG_DEBUG("INTPR: Getting il bb failed\n"); + RZ_LOG_DEBUG("interpreter: Getting il bb failed\n"); // The il op lifting failed. Likely because the PC // pointed to an unmapped region. goto CLEAN; @@ -513,7 +513,7 @@ EMU: { return false; } - RZ_LOG_DEBUG("INTPR: Received il_bb: 0x%" PFMT64x "\n", il_bb->addr); + RZ_LOG_DEBUG("interpreter: Received il_bb: 0x%" PFMT64x "\n", il_bb->addr); if (!plugin->set_pc(iset->astate, next.ctrl_flow.actual_target, iset->intrpr_priv)) { rz_warn_if_reached(); goto CLEAN; @@ -524,7 +524,7 @@ EMU: { } CLEAN: { - RZ_LOG_DEBUG("Enter CLEAN\n"); + RZ_LOG_DEBUG("interpreter: Enter CLEAN\n"); rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_CLEAN); RZ_FREE_CUSTOM(tmp_succ_addr, rz_vector_free); @@ -539,7 +539,7 @@ CLEAN: { } TERM: { - RZ_LOG_DEBUG("Enter TERM\n"); + RZ_LOG_DEBUG("interpreter: Enter TERM\n"); rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_TERM); iset->plugin->fini_state(iset->astate, iset->intrpr_priv); if (iset->plugin->fini && iset->intrpr_priv) { diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 9aca2d2ba13..a5e63515c43 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -25,10 +25,10 @@ static bool eval(RZ_NONNULL RzInterpreterSet *iset, HtUUKv *ic_pc = ht_uu_find_kv(pdata->bb_invocation_count, il_bb->addr, &found); if (found) { ic_pc->value++; - RZ_LOG_DEBUG("Eval BLOCK (ic: %" PFMT64d ") = 0x%" PFMT64x "\n", ic_pc->value, il_bb->addr); + RZ_LOG_DEBUG("prototype: Eval BLOCK (ic: %" PFMT64d ") = 0x%" PFMT64x "\n", ic_pc->value, il_bb->addr); if (ic_pc->value > MAX_INVOCATIONS_PER_BLOCK) { // TODO: Make it configurable - RZ_LOG_DEBUG("Reached maximum number of invocations of basic block at 0x%" PFMT64x ". Skipping it.\n", il_bb->addr) + RZ_LOG_DEBUG("prototype: Reached maximum number of invocations of basic block at 0x%" PFMT64x ". Skipping it.\n", il_bb->addr) set_pc(iset->astate, il_bb->addr + il_bb->size, plugin_data); return true; } @@ -44,7 +44,7 @@ static bool eval(RZ_NONNULL RzInterpreterSet *iset, rz_pvector_foreach (il_bb->il_ops, it) { ProtoIntrprAbstrData *apc = AD(iset->astate->pc->abstr_data); ut64 pc = rz_bv_to_ut64(apc->bv); - RZ_LOG_DEBUG("Eval PC = 0x%" PFMT64x "\n", pc); + RZ_LOG_DEBUG("prototype: Eval PC = 0x%" PFMT64x "\n", pc); RzILCacheInsnPkt *pkt = *it; if (!interpreter_prototype_eval_effect(iset, pkt->effect, pkt->insn_pkt_size, plugin_data)) { return false; diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 4e7c33d0a66..38dd45f215a 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -43,7 +43,7 @@ bool report_yield_xref( xref.to = to_addr; xref.type = type; if (yrbuf->filter(&xref, yrbuf->filter_data->io_boundaries)) { - RZ_LOG_DEBUG("REPORT xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref.from, xref.to, rz_analysis_ref_type_tostring(xref.type)); + RZ_LOG_DEBUG("prototype: REPORT xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref.from, xref.to, rz_analysis_ref_type_tostring(xref.type)); if (rz_th_ring_buf_put(yrbuf->rbuf, &xref) != RZ_THREAD_RING_BUF_OK) { return false; } @@ -171,7 +171,7 @@ bool store_abstr_data( io_req.st_data = src->bv; char *bytes = rz_bv_as_hex_string(src->bv, true); - RZ_LOG_DEBUG("Prototype: STORE @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); + RZ_LOG_DEBUG("pprototype: ototype: STORE @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); free(bytes); if (rz_th_ring_buf_put(iset->io_request_rbuf, &io_req) != RZ_THREAD_RING_BUF_OK) { @@ -207,7 +207,7 @@ bool load_abstr_data( return false; } if (!io_res.req_ok) { - RZ_LOG_WARN("Prototype: Failed to read correct number of bytes. Requested: 0x%" PFMTSZx + RZ_LOG_WARN("prototype: Failed to read correct number of bytes. Requested: 0x%" PFMTSZx " Received: 0x%" PFMT32x " bits.\n", n_bits, rz_bv_len(out->bv)); return false; @@ -215,7 +215,7 @@ bool load_abstr_data( out->is_concrete = true; char *bytes = rz_bv_as_hex_string(out->bv, true); - RZ_LOG_DEBUG("Prototype: READ @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); + RZ_LOG_DEBUG("prototype: READ @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); free(bytes); return true; } @@ -231,7 +231,7 @@ bool set_abstr_pc(RzInterpreterAbstrState *state, ProtoIntrprAbstrData *pc, pdata->prev_pc = rz_bv_to_ut64(apc->bv); } copy_abstr_data(state->pc->abstr_data, pc); - RZ_LOG_DEBUG("Prototype: set_abstr_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", + RZ_LOG_DEBUG("prototype: set_abstr_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", pdata->prev_pc, rz_bv_to_ut64(apc->bv), apc->is_concrete ? "Concrete" : "Abstract"); return true; } @@ -248,7 +248,7 @@ bool set_pc(RzInterpreterAbstrState *state, ut64 pc, } apc->is_concrete = true; - RZ_LOG_DEBUG("Prototype: set_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (Concrete)\n", + RZ_LOG_DEBUG("prototype: set_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (Concrete)\n", rz_bv_to_ut64(apc->bv), pc); return rz_bv_set_from_ut64(apc->bv, pc); } diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index cc315940e5f..46091adebac 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -78,7 +78,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterSet *iset, RZ_LOG_DEBUG("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", rz_bv_to_ut64(pc->bv)); } ut64 target = rz_bv_to_ut64(eval_out.bv); - RZ_LOG_DEBUG("Prototype: JMP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", + RZ_LOG_DEBUG("prototype: JMP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", rz_bv_to_ut64(pc->bv), target, eval_out.is_concrete ? "Concrete" : "Abstract"); From 6c5060374e0fdc50bb4d4203c1138771c1b0a251 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 29 May 2026 19:41:54 +0200 Subject: [PATCH 275/334] Rename Basic Block CFG -> Block CFG, Interpreter -> Interp, add -f command option. --- librz/core/cmd/cmd_inquiry.c | 23 ++- librz/core/cmd_descs/cmd_analysis.yaml | 16 ++ librz/core/cmd_descs/cmd_descs.c | 21 ++- librz/include/rz_inquiry.h | 30 ++-- librz/include/rz_inquiry/rz_bb_graph.h | 77 --------- librz/include/rz_inquiry/rz_bcfg.h | 77 +++++++++ librz/include/rz_inquiry/rz_interpreter.h | 144 ++++++++--------- librz/include/rz_inquiry/rz_state.h | 6 +- .../inquiry/algorithms/revng_fcn_detection.c | 62 ++++---- librz/inquiry/{bb_cfg.c => bcfg.c} | 98 ++++++------ librz/inquiry/inquiry.c | 150 +++++++++--------- librz/inquiry/interpreter/interpreter.c | 88 +++++----- .../interpreter/p/interpreter_prototype.c | 42 ++--- librz/inquiry/interpreter/prototype/eval.c | 42 ++--- librz/inquiry/interpreter/prototype/eval.h | 24 +-- .../interpreter/prototype/eval_effect.c | 4 +- .../inquiry/interpreter/prototype/eval_pure.c | 2 +- librz/inquiry/meson.build | 2 +- test/db/inquiry/interpreter/unmapped_fcn_loop | 2 +- test/db/inquiry/interpreter/xrefs | 10 +- 20 files changed, 490 insertions(+), 430 deletions(-) delete mode 100644 librz/include/rz_inquiry/rz_bb_graph.h create mode 100644 librz/include/rz_inquiry/rz_bcfg.h rename librz/inquiry/{bb_cfg.c => bcfg.c} (68%) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 957f5f69359..a1505f05f4f 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -50,15 +50,16 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar if (!entry_points) { return RZ_CMD_STATUS_ERROR; } + bool run_fcn_detection = RZ_STR_EQ(argv[1], "-f"); ut64 entry_point; - if (argc == 1) { + if (argc == (run_fcn_detection ? 2 : 1)) { // No specific entry point given. Pick the one provided by the bin plugin. entry_point = rz_bin_get_first_entrypoint(core->bin->cur->o); rz_set_u_add(entry_points, entry_point); } else { // Add all entry points given as arguments. - for (size_t i = 1; i < argc; i++) { + for (size_t i = (run_fcn_detection ? 2 : 1); i < argc; i++) { ut64 entry_point = rz_num_get(core->num, argv[i]); rz_set_u_add(entry_points, entry_point); } @@ -73,19 +74,29 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar rz_vector_free(ignored_code_regions); return RZ_CMD_STATUS_ERROR; } - eprintf("Perform function deduction: "); RzSetU *symbol_addresses = rz_set_u_new(); - if (!rz_inquiry_get_fcn_symbol_addr(core, symbol_addresses)) { + if (!symbol_addresses || !rz_inquiry_get_fcn_symbol_addr(core, symbol_addresses)) { rz_vector_free(ignored_code_regions); rz_warn_if_reached(); return RZ_CMD_STATUS_ERROR; } const RzPVector *symbols = rz_bin_object_get_symbols(core->bin->cur->o); - success = rz_inquiry_function_deduction(core->analysis, core->inquiry, symbol_addresses, symbols, ignored_code_regions); - eprintf("%s\n", success ? "OK" : "FAIL"); + RzPVector *fcns = rz_pvector_new((RzPVectorFree)rz_inquiry_function_free); + + if (!fcns || !symbols) { + return RZ_CMD_STATUS_ERROR; + } + if (run_fcn_detection) { + eprintf("Perform function deduction: "); + success &= rz_inquiry_function_deduction(core->analysis, core->inquiry, symbol_addresses, symbols, ignored_code_regions, fcns); + eprintf("%s\n", success ? "OK" : "FAIL"); + } rz_set_u_free(symbol_addresses); rz_vector_free(ignored_code_regions); + success &= rz_inquiry_convert_and_add_to_analysis(core->analysis, core->inquiry, fcns, symbols); + rz_pvector_free(fcns); + return success ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; } diff --git a/librz/core/cmd_descs/cmd_analysis.yaml b/librz/core/cmd_descs/cmd_analysis.yaml index 935be819f26..7ab3203c31b 100644 --- a/librz/core/cmd_descs/cmd_analysis.yaml +++ b/librz/core/cmd_descs/cmd_analysis.yaml @@ -28,10 +28,26 @@ commands: summary: Abstract Interpreter Prototype cname: inquiry_interpreter_prototype args: + - name: f + type: RZ_CMD_ARG_TYPE_OPTION + flags: RZ_CMD_ARG_FLAG_OPTION + default_value: 0 - name: entry_points type: RZ_CMD_ARG_TYPE_RZNUM flags: RZ_CMD_ARG_FLAG_ARRAY optional: true + details: + - name: Examples + entries: + - text: "aaaaIp" + arg_str: "" + comment: "Run prototype RzIL analysis detecting cross references." + - text: "aaaaIp" + arg_str: " 0x1000" + comment: "Run prototype RzIL analysis starting at address 0x1000." + - text: "aaaaIp" + arg_str: " -f" + comment: "Run prototype RzIL analysis with function detection." - name: aac summary: Analysis function calls commands subcommands: diff --git a/librz/core/cmd_descs/cmd_descs.c b/librz/core/cmd_descs/cmd_descs.c index d4f9e9ac6dc..b7a20549476 100644 --- a/librz/core/cmd_descs/cmd_descs.c +++ b/librz/core/cmd_descs/cmd_descs.c @@ -43,6 +43,7 @@ static const RzCmdDescDetail compare_and_set_core_num_value_details[2]; static const RzCmdDescDetail exec_cmd_if_core_num_value_positive_details[2]; static const RzCmdDescDetail exec_cmd_if_core_num_value_negative_details[2]; static const RzCmdDescDetail push_escaped_details[3]; +static const RzCmdDescDetail inquiry_interpreter_prototype_details[2]; static const RzCmdDescDetail analysis_all_esil_details[2]; static const RzCmdDescDetail analyze_all_preludes_details[2]; static const RzCmdDescDetail analysis_functions_merge_details[2]; @@ -251,7 +252,7 @@ static const RzCmdDescArg input_msg_args[2]; static const RzCmdDescArg input_conditional_args[2]; static const RzCmdDescArg get_addr_references_args[2]; static const RzCmdDescArg push_escaped_args[2]; -static const RzCmdDescArg inquiry_interpreter_prototype_args[2]; +static const RzCmdDescArg inquiry_interpreter_prototype_args[3]; static const RzCmdDescArg analysis_all_esil_args[2]; static const RzCmdDescArg analyze_all_consecutive_functions_in_section_args[2]; static const RzCmdDescArg analyze_xrefs_section_bytes_args[2]; @@ -4053,7 +4054,24 @@ static const RzCmdDescHelp analyze_everything_experimental_help = { static const RzCmdDescHelp aaaaI_help = { .summary = "New RzInquiry analysis.", }; +static const RzCmdDescDetailEntry inquiry_interpreter_prototype_Examples_detail_entries[] = { + { .text = "aaaaIp", .arg_str = "", .comment = "Run prototype RzIL analysis detecting cross references." }, + { .text = "aaaaIp", .arg_str = " 0x1000", .comment = "Run prototype RzIL analysis starting at address 0x1000." }, + { .text = "aaaaIp", .arg_str = " -f", .comment = "Run prototype RzIL analysis with function detection." }, + { 0 }, +}; +static const RzCmdDescDetail inquiry_interpreter_prototype_details[] = { + { .name = "Examples", .entries = inquiry_interpreter_prototype_Examples_detail_entries }, + { 0 }, +}; static const RzCmdDescArg inquiry_interpreter_prototype_args[] = { + { + .name = "f", + .type = RZ_CMD_ARG_TYPE_OPTION, + .flags = RZ_CMD_ARG_FLAG_OPTION, + .default_value = "0", + + }, { .name = "entry_points", .type = RZ_CMD_ARG_TYPE_RZNUM, @@ -4065,6 +4083,7 @@ static const RzCmdDescArg inquiry_interpreter_prototype_args[] = { }; static const RzCmdDescHelp inquiry_interpreter_prototype_help = { .summary = "Abstract Interpreter Prototype", + .details = inquiry_interpreter_prototype_details, .args = inquiry_interpreter_prototype_args, }; diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 6b1fda23a1d..c6ffdd209a6 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -15,7 +15,7 @@ extern "C" { #include #include -#include +#include /** * \brief The number of iterations inquiry checks for a user given signal. @@ -24,7 +24,7 @@ extern "C" { #define RZ_INQUIRY_CHECK_USER_SIGNAL_ITC 1000 typedef struct rz_inquiry_plugin_t { - RzInterpreterPlugin *p_interpreter; + RzInterpPlugin *p_interpreter; } RzInquiryPlugin; typedef struct { @@ -36,10 +36,10 @@ typedef struct { HtUP /**/ *call_candidates; ///< Indexed by address of basic block with the call candidate. RzVector /**/ *dynamic_xrefs; ///< All xrefs the interpreter detected. - RzInquiryBBCFG *bb_cfg; ///< The control flow graph all the basic blocks build. + RzInquiryBCFG *bcfg; ///< The control flow graph all the basic blocks build. } RzInquiry; -RZ_IPI bool rz_inquiry_bb_cfg_complement(RzInquiry *iq, RzVector /**/ *insn_to_insn_edges); +RZ_IPI bool rz_inquiry_bcfg_complement(RzInquiry *iq, RzVector /**/ *insn_to_insn_edges); RZ_IPI void rz_inquiry_add_xref(RzInquiry *iq, const RzAnalysisXRef *xref); @@ -53,11 +53,13 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(RZ_NONNULL const RzAnalysisXRef * RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzSetU /**/ *entry_points, RZ_NONNULL const RzVector /**/ *ignored_code); -RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, - RzInquiry *inquiry, - RzSetU *symbol_addresses, - const RzPVector /**/ *symbols, - RZ_NONNULL const RzVector /**/ *ignored_code); +RZ_API bool rz_inquiry_function_deduction( + RZ_NONNULL RZ_BORROW RzAnalysis *analysis, + RZ_NONNULL RZ_BORROW RzInquiry *inquiry, + RZ_NONNULL RZ_BORROW RzSetU *symbol_addresses, + RZ_NONNULL const RzPVector /**/ *symbols, + RZ_NONNULL const RzVector /**/ *ignored_code, + RZ_NONNULL RZ_OUT RzPVector /**/ *inquiry_fcns); //============ // Algorithms @@ -68,7 +70,7 @@ RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, */ typedef struct { RzVector /**/ *entry_points; - RzInquiryBBCFG *bb_cfg; + RzInquiryBCFG *bcfg; } RzInquiryFunction; RZ_IPI RZ_OWN RzInquiryFunction *rz_inquiry_function_new(); @@ -81,10 +83,16 @@ RZ_API void rz_inquiry_function_free(RZ_NULLABLE RZ_OWN RzInquiryFunction *fcn); RZ_API bool rz_inquiry_algo_revng_fcn_detection( RzSetU *symbol_addresses, const HtUP /**/ *call_candidates, - const RzInquiryBBCFG *bb_cfg, + const RzInquiryBCFG *bcfg, RZ_OUT RzPVector /**/ *fcns, RZ_NONNULL const RzVector /**/ *ignored_code); +RZ_IPI bool rz_inquiry_convert_and_add_to_analysis( + RzAnalysis *analysis, + RzInquiry *inquiry, + const RzPVector /**/ *fcns, + const RzPVector /**/ *symbols); + #ifdef __cplusplus } #endif diff --git a/librz/include/rz_inquiry/rz_bb_graph.h b/librz/include/rz_inquiry/rz_bb_graph.h deleted file mode 100644 index 2624e545dfe..00000000000 --- a/librz/include/rz_inquiry/rz_bb_graph.h +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-FileCopyrightText: 2026 Rot127 -// SPDX-License-Identifier: LGPL-3.0-only - -#ifndef RZ_INQUIRY_BB_GRAPH_H -#define RZ_INQUIRY_BB_GRAPH_H - -#include -#include - -/** - * \brief A block of instructions which end in a jump/call/exit. - */ -typedef struct { - ut64 addr; - ut64 size; -} RzInquiryBlock; - -/** - * \brief The different kind of BB CFG edges. - */ -typedef enum { - RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE = 0, - /** - * \brief An control flow change via a RzIL JMP. The "from" BB ends with a jump. - */ - RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP, - /** - * \brief A control flow edge between two blocks where the "from" block does not end with a JUMP. - * It is between two blocks which were split from a single one. - */ - RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF, - /** - * \brief An control flow change via a RzIL JMP but by a call candidate. - * The "from" BB ends with a jump, very likely to a procedure. - */ - RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL, - /** - * \brief This edge does not depict a control flow edge. - * It is an opaque edge between a call instruction and the (possible) return point of the procedure called. - * Note that the return point is often, but not always, the instruction at the following memory address after the call. - * There are multiple exceptions to it though, like tail calls, non-returning procedures, or delayed calls. - */ - RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET, - /** - * \brief An edge from a return instruction to its target. - */ - RZ_INQUIRY_BB_CFG_EDGE_TYPE_RETURN, -} RzInquiryBBCFGEdgeType; - -/** - * \brief A control flow graph with basic blocks as nodes. - * Edges can be of different jump, and call/return type. - */ -typedef struct { - /** - * \brief The CFG discovered during interpretation. - * The node data are the addresses of basic blocks, cast to (void *). - */ - RzGraph /**/ *graph; -} RzInquiryBBCFG; - -RZ_IPI RZ_OWN RzInquiryBBCFG *rz_inquiry_bb_cfg_new(RzGraphImplType impl_type); -RZ_IPI void rz_inquiry_bb_cfg_free(RZ_NULLABLE RZ_OWN RzInquiryBBCFG *bb_cfg); -RZ_IPI bool rz_inquiry_bb_cfg_add_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size); -RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, const RzVector /**/ *xrefs); - -RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RzInquiryBlock *bb); -RZ_IPI bool rz_inquiry_bb_cfg_del_out_edges(RzInquiryBBCFG *cfg, ut64 bb_addr); -RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBBCFGEdgeType type); -RZ_IPI bool rz_inquiry_bb_cfg_del_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb); -RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_outgoing_edges(const RzInquiryBBCFG *cfg, ut64 bb_addr); -RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_incoming_edges(const RzInquiryBBCFG *cfg, ut64 bb_addr); - -RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg); -RZ_IPI RZ_OWN char *rz_inquiry_bb_cfg_as_dot(const RzInquiryBBCFG *bb_cfg, RZ_NULLABLE const char *name); - -#endif // RZ_INQUIRY_BB_GRAPH_H diff --git a/librz/include/rz_inquiry/rz_bcfg.h b/librz/include/rz_inquiry/rz_bcfg.h new file mode 100644 index 00000000000..32924a84d39 --- /dev/null +++ b/librz/include/rz_inquiry/rz_bcfg.h @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2026 Rot127 +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef RZ_INQUIRY_BCFG_H +#define RZ_INQUIRY_BCFG_H + +#include +#include + +/** + * \brief A block of instructions which end in a jump/call/exit. + */ +typedef struct { + ut64 addr; + ut64 size; +} RzInquiryBlock; + +/** + * \brief The different kind of BB CFG edges. + */ +typedef enum { + RZ_INQUIRY_BCFG_EDGE_TYPE_NONE = 0, + /** + * \brief An control flow change via a RzIL JMP. The "from" BB ends with a jump. + */ + RZ_INQUIRY_BCFG_EDGE_TYPE_JMP, + /** + * \brief A control flow edge between two blocks where the "from" block does not end with a JUMP. + * It is between two blocks which were split from a single one. + */ + RZ_INQUIRY_BCFG_EDGE_TYPE_CF, + /** + * \brief An control flow change via a RzIL JMP but by a call candidate. + * The "from" BB ends with a jump, very likely to a procedure. + */ + RZ_INQUIRY_BCFG_EDGE_TYPE_CALL, + /** + * \brief This edge does not depict a control flow edge. + * It is an opaque edge between a call instruction and the (possible) return point of the procedure called. + * Note that the return point is often, but not always, the instruction at the following memory address after the call. + * There are multiple exceptions to it though, like tail calls, non-returning procedures, or delayed calls. + */ + RZ_INQUIRY_BCFG_EDGE_TYPE_CALL_RET, + /** + * \brief An edge from a return instruction to its target. + */ + RZ_INQUIRY_BCFG_EDGE_TYPE_RETURN, +} RzInquiryBCFGEdgeType; + +/** + * \brief A control flow graph with blocks as nodes. + * Edges can be of different jump, and call/return type. + */ +typedef struct { + /** + * \brief The CFG discovered during interpretation. + * The node data are the addresses of basic blocks, cast to (void *). + */ + RzGraph /**/ *graph; +} RzInquiryBCFG; + +RZ_IPI RZ_OWN RzInquiryBCFG *rz_inquiry_bcfg_new(RzGraphImplType impl_type); +RZ_IPI void rz_inquiry_bcfg_free(RZ_NULLABLE RZ_OWN RzInquiryBCFG *bcfg); +RZ_IPI bool rz_inquiry_bcfg_add_block(RzInquiryBCFG *cfg, ut64 addr, ut64 size); + +RZ_IPI bool rz_inquiry_bcfg_get_block(const RzInquiryBCFG *cfg, ut64 bb_addr, RZ_OUT RzInquiryBlock *bb); +RZ_IPI bool rz_inquiry_bcfg_del_out_edges(RzInquiryBCFG *cfg, ut64 bb_addr); +RZ_IPI bool rz_inquiry_bcfg_add_edge(RzInquiryBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBCFGEdgeType type); +RZ_IPI bool rz_inquiry_bcfg_add_edge_xref(RzInquiryBCFG *cfg, const RzVector /**/ *xrefs); +RZ_IPI bool rz_inquiry_bcfg_del_edge(RzInquiryBCFG *cfg, ut64 from_bb, ut64 to_bb); +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bcfg_get_outgoing_edges(const RzInquiryBCFG *cfg, ut64 bb_addr); +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bcfg_get_incoming_edges(const RzInquiryBCFG *cfg, ut64 bb_addr); + +RZ_IPI bool rz_inquiry_bcfg_reduce(RzInquiryBCFG *cfg); +RZ_IPI RZ_OWN char *rz_inquiry_bcfg_as_dot(const RzInquiryBCFG *bcfg, RZ_NULLABLE const char *name); + +#endif // RZ_INQUIRY_BCFG_H diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 8530ff83058..23768133ca9 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -2,14 +2,14 @@ // SPDX-License-Identifier: LGPL-3.0-only /** - * \file The header file for the RzInterpreter contains declarations for + * \file The header file for the RzInterp contains declarations for * all RzIL based interpreters. */ #ifndef RZ_INTERPRETER #define RZ_INTERPRETER -#include "rz_inquiry/rz_bb_graph.h" +#include #include #include #include @@ -20,9 +20,9 @@ /** * \brief Only one IO request at a time is possible (currently). */ -#define RZ_INTERPRETER_IO_RBUF_SIZE 128 -#define RZ_INTERPRETER_YIELD_RBUF_SIZE 128 -#define RZ_INTERPRETER_ENTRY_POINTS_RBUF_SIZE 4 +#define RZ_INTERP_IO_RBUF_SIZE 128 +#define RZ_INTERP_YIELD_RBUF_SIZE 128 +#define RZ_INTERP_ENTRY_POINTS_RBUF_SIZE 4 typedef struct rz_intp_run_state RzIntpRunState; @@ -45,31 +45,31 @@ typedef enum { /** * \brief An undefined abstracted value. */ - RZ_INTERPRETER_ABSTRACTION_UNDEF = 0, + RZ_INTERP_ABSTRACTION_UNDEF = 0, /** * \brief Value abstraction into constant and bottom values. */ - RZ_INTERPRETER_ABSTRACTION_CONST = 1 << 0, + RZ_INTERP_ABSTRACTION_CONST = 1 << 0, /** * \brief Value abstraction into Heap[base, offset] and bottom values. */ - RZ_INTERPRETER_ABSTRACTION_HEAP = 1 << 1, + RZ_INTERP_ABSTRACTION_HEAP = 1 << 1, /** * \brief Value abstraction into Stack[base, offset] and bottom values. */ - RZ_INTERPRETER_ABSTRACTION_STACK = 1 << 2, -} RzInterpreterAbstraction; + RZ_INTERP_ABSTRACTION_STACK = 1 << 2, +} RzInterpAbstraction; /** * \brief An arbitrary abstract value. */ typedef struct { - RzInterpreterAbstraction kind; ///< The abstraction of the value. + RzInterpAbstraction kind; ///< The abstraction of the value. void *abstr_data; ///< The abstract data. It is managed by individual interpreter. -} RzInterpreterAbstrVal; +} RzInterpAbstrVal; typedef struct { - RzInquiryBBCFGEdgeType cf_type; ///< Control flow type. + RzInquiryBCFGEdgeType cf_type; ///< Control flow type. /** * \brief The address of the block which changes the control flow. * This might be UT64_MAX, if there was no jump that flow originated from. @@ -99,27 +99,27 @@ typedef struct { /** * \brief Control flow type. */ - RzInquiryBBCFGEdgeType type; -} RzInterpreterCtrlFlow; + RzInquiryBCFGEdgeType type; +} RzInterpCtrlFlow; typedef struct { - RzInterpreterAbstraction kinds; ///< The abstractions of the state. + RzInterpAbstraction kinds; ///< The abstractions of the state. HtUP *var_name_hashes; ///< Map of DJB2 hashes to variable names. - HtUP /**/ *globals; ///< Global variables (mostly registers). Indexed by DJB2 hash of global name. - HtUP /**/ *locals; ///< Local variables. Indexed by DJB2 hash of the local name. - HtUP /**/ *lets; ///< Let variables. Indexed by DJB2 hash of the let name. - RzInterpreterAbstrVal *pc; ///< In our RzIL implementation the PC is not part of the register file. + HtUP /**/ *globals; ///< Global variables (mostly registers). Indexed by DJB2 hash of global name. + HtUP /**/ *locals; ///< Local variables. Indexed by DJB2 hash of the local name. + HtUP /**/ *lets; ///< Let variables. Indexed by DJB2 hash of the let name. + RzInterpAbstrVal *pc; ///< In our RzIL implementation the PC is not part of the register file. RzAnalysisILConfig *il_config; ///< The IL configuration of the RzArch plugin. const char *arch_name; ///< Name of architecture. Used by work-arounds until we have RzArch. ut64 bb_addr; ut64 bb_size; -} RzInterpreterAbstrState; +} RzInterpAbstrState; typedef enum { /** * \brief The yield is an cross reference. */ - RZ_INTERPRETER_YIELD_KIND_XREF = 0, + RZ_INTERP_YIELD_KIND_XREF = 0, /** * \brief This yield is a simple flag, signaling if the current basic block @@ -128,37 +128,37 @@ typedef enum { * If the last branch instruction does not jump to the neighboring basic block * it is a strong indicator that the jump is a call and the next address a return point. */ - RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE, + RZ_INTERP_YIELD_KIND_CALL_CANDIDATE, /** - * \brief Yield is a RzInterpreterCtrlFlow. + * \brief Yield is a RzInterpCtrlFlow. * Reported by every interpreter. */ - RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW, - RZ_INTERPRETER_YIELD_KIND_NUM, -} RzInterpreterYieldKind; + RZ_INTERP_YIELD_KIND_CONTROL_FLOW, + RZ_INTERP_YIELD_KIND_NUM, +} RzInterpYieldKind; /** * \brief A filter for abstract values to decide if they should be pushed into * the yield ring buffer or not. */ -typedef bool (*RzInterpreterYieldFilter)(const void *element, const void *filter_data); +typedef bool (*RzInterpYieldFilter)(const void *element, const void *filter_data); typedef struct { RzPVector /**/ *io_boundaries; -} RzInterpreterYieldFilterData; +} RzInterpYieldFilterData; /** * \brief A ring buffer to push interpretation yields into. */ typedef struct { - RzInterpreterYieldKind kind; - RzInterpreterYieldFilter filter; - RzInterpreterYieldFilterData *filter_data; + RzInterpYieldKind kind; + RzInterpYieldFilter filter; + RzInterpYieldFilterData *filter_data; RzThreadRingBuf *rbuf; -} RzInterpreterYieldRBuf; +} RzInterpYieldRBuf; -typedef struct rz_interpreter_set RzInterpreterSet; +typedef struct rz_interpreter_set RzInterpSet; typedef struct { const char *name; @@ -169,39 +169,39 @@ typedef struct { /** * \brief Supported abstractions. Multiple flags can be set. */ - RzInterpreterAbstraction supported_abstractions; + RzInterpAbstraction supported_abstractions; /** * \brief The yield type this interpreter generates. */ - RzInterpreterYieldKind supported_yields[RZ_INTERPRETER_YIELD_KIND_NUM]; + RzInterpYieldKind supported_yields[RZ_INTERP_YIELD_KIND_NUM]; bool (*init)(void **plugin_data); bool (*reset)(void *plugin_data); bool (*fini)(void *plugin_data); /** * \brief Initializes the abstract state. */ - bool (*init_state)(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data); + bool (*init_state)(RZ_BORROW RzInterpAbstrState *state, void *plugin_data); /** * \brief Reset the abstract state. */ - bool (*reset_state)(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_point, void *plugin_data); + bool (*reset_state)(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point, void *plugin_data); /** * \brief Closes the abstract state and frees all its abstract data and sets the pointers to NULL. */ - bool (*fini_state)(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data); + bool (*fini_state)(RZ_BORROW RzInterpAbstrState *state, void *plugin_data); /** * \brief Clones the abstract state. */ - RZ_OWN RzInterpreterAbstrState *(*clone_state)(const RzInterpreterAbstrState *state, void *plugin_data); + RZ_OWN RzInterpAbstrState *(*clone_state)(const RzInterpAbstrState *state, void *plugin_data); /** * \brief Hashes the state. */ - ut64 (*hash_state)(RZ_NONNULL const RzInterpreterAbstrState *state, + ut64 (*hash_state)(RZ_NONNULL const RzInterpAbstrState *state, void *plugin_data); /** * \brief Evaluates an effect with the mutable state. */ - bool (*eval)(RZ_NONNULL RzInterpreterSet *iset, + bool (*eval)(RZ_NONNULL RzInterpSet *iset, RZ_NONNULL const RzILCacheBlock *il_bb, void *plugin_data); /** @@ -210,8 +210,8 @@ typedef struct { * \return Returns false in case of error. The interpretation must abort. * True otherwise. */ - bool (*successors)(RZ_NONNULL const RzInterpreterAbstrState *state, - RZ_NONNULL RZ_OUT RzVector /**/ *successors, + bool (*successors)(RZ_NONNULL const RzInterpAbstrState *state, + RZ_NONNULL RZ_OUT RzVector /**/ *successors, void *plugin_data); /** @@ -220,35 +220,35 @@ typedef struct { * \return Returns false in case of error. The interpretation must abort. * True otherwise. */ - bool (*state_as_str)(RZ_NONNULL const RzInterpreterAbstrState *state, + bool (*state_as_str)(RZ_NONNULL const RzInterpAbstrState *state, RZ_NONNULL RZ_OUT RzStrBuf *str_buf, void *plugin_data); /** * \brief Set the abstract PC to the given address. */ - bool (*set_pc)(RZ_NONNULL RzInterpreterAbstrState *state, + bool (*set_pc)(RZ_NONNULL RzInterpAbstrState *state, ut64 pc, void *plugin_data); -} RzInterpreterPlugin; +} RzInterpPlugin; typedef enum { - RZ_INTERPRETER_IO_READ, - RZ_INTERPRETER_IO_WRITE, -} RzInterpreterIOReqType; + RZ_INTERP_IO_READ, + RZ_INTERP_IO_WRITE, +} RzInterpIOReqType; typedef struct { - RzInterpreterIOReqType type; + RzInterpIOReqType type; size_t mem_idx; ///< The memory space to read/write. bool big_endian; ///< Set if the data is big endian ordered. const RzBitVector *addr; ///< The address to read/write. const RzBitVector *st_data; ///< The data to store. RzBitVector *ld_data; ///< The bit vector to load into. It is BORROWED. size_t n_bits; ///< The number of bits to read/write. -} RzInterpreterIORequest; +} RzInterpIORequest; typedef struct { bool req_ok; ///< Set to true if IO request succeeded. -} RzInterpreterIOResult; +} RzInterpIOResult; /** * \brief The set of required objects for an interpreter to run. @@ -256,7 +256,7 @@ typedef struct { RZ_LIFETIME(RzInquiry) struct rz_interpreter_set { // TODO: Move this one into each plugin? - RzInterpreterAbstrState *astate; ///< The abstract state of the interpreter. + RzInterpAbstrState *astate; ///< The abstract state of the interpreter. RzIntpRunState *run_state; ///< The state the interpreter is currently in. /** @@ -272,18 +272,18 @@ struct rz_interpreter_set { RzAnalysisILVM *il_vm; ///< The RzAnalysisILVM for memory IO. RZ_LIFETIME(RzILCache) - RZ_BORROW RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. + RZ_BORROW RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. RZ_LIFETIME(RzILCache) - RZ_BORROW RzThreadRingBuf /**/ *il_request_rbuf; ///< The ring buffer to send requests to the cache what address to get the next IL op from. + RZ_BORROW RzThreadRingBuf /**/ *il_request_rbuf; ///< The ring buffer to send requests to the cache what address to get the next IL op from. - RzThreadRingBuf /**/ *io_request_rbuf; ///< The ring buffer for read/write requests to the IO layer. - RzThreadRingBuf /**/ *io_result_rbuf; ///< The ring buffer for the read/write requests' answers. + RzThreadRingBuf /**/ *io_request_rbuf; ///< The ring buffer for read/write requests to the IO layer. + RzThreadRingBuf /**/ *io_result_rbuf; ///< The ring buffer for the read/write requests' answers. /** * \brief The ring buffers to push the yield of interpretation into. * These ring buffers are shared with other interpreter sets. */ - RZ_BORROW RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM]; + RZ_BORROW RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM]; /** * \brief Ignored address ranges. */ @@ -291,7 +291,7 @@ struct rz_interpreter_set { /** * \brief The interpreter plugin. */ - RzInterpreterPlugin *plugin; + RzInterpPlugin *plugin; /** * \brief The private data of a single interpreter thread. */ @@ -306,29 +306,29 @@ RZ_API const char *rz_intp_run_state_flag_str(RzIntpRunStateFlag flag); RZ_IPI void rz_intp_run_state_set(RZ_BORROW RZ_NONNULL RzIntpRunState *state, RzIntpRunStateFlag flag); -RZ_API void rz_interpreter_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldRBuf *yield_rbuf); +RZ_API void rz_interpreter_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yield_rbuf); -RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( +RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_new( const char *arch_name, - RzInterpreterAbstraction kinds, + RzInterpAbstraction kinds, RZ_OWN RZ_NONNULL RzAnalysisILConfig *il_config, RZ_NULLABLE const RzILRegBinding *reg_bindings); -RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbstrState *state); +RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpAbstrState *state); -RZ_API RZ_OWN RzInterpreterYieldRBuf *rz_interpreter_yield_rbuf_new(RzInterpreterYieldKind kind, - RzInterpreterYieldFilter filter, +RZ_API RZ_OWN RzInterpYieldRBuf *rz_interpreter_yield_rbuf_new(RzInterpYieldKind kind, + RzInterpYieldFilter filter, RZ_OWN RZ_NULLABLE void *filter_data); -RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( +RZ_API RZ_OWN RzInterpSet *rz_interpreter_set_new( RzAnalysis *analysis, - RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, - RzInterpreterAbstraction abstraction, + RZ_NONNULL RZ_OWN RzInterpPlugin *plugin, + RzInterpAbstraction abstraction, RZ_NONNULL RZ_BORROW RzThreadRingBuf *il_request_rbuf, RZ_NONNULL RZ_BORROW RzThreadQueue *il_queue, - RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM], + RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM], RZ_NONNULL const RzVector /**/ *ignored_code); -RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset); +RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpSet *iset); -RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset); +RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpSet *iset); #endif // RZ_INTERPRETER diff --git a/librz/include/rz_inquiry/rz_state.h b/librz/include/rz_inquiry/rz_state.h index 6f0599a1d3d..4f54f986502 100644 --- a/librz/include/rz_inquiry/rz_state.h +++ b/librz/include/rz_inquiry/rz_state.h @@ -1,10 +1,10 @@ /** - * \file The header file for the RzInterpreter contains declarations for + * \file The header file for the RzInterp contains declarations for * all RzIL based interpreters. */ -#ifndef RZ_INTERPRETER_STATE -#define RZ_INTERPRETER_STATE +#ifndef RZ_INTERP_STATE +#define RZ_INTERP_STATE #endif // RZ_STATE diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index 90c63370f59..774a24c173a 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -22,12 +22,12 @@ * - Call candidates. * - CFEPs * - Return Addresses - * - Basic Block CFG (bb_cfg) + * - Basic Block CFG (bcfg) * * ALGO: * * It simply iterates over all CFEPs. - * For each one it follows its edges in the bb_cfg. + * For each one it follows its edges in the bcfg. * If an edge belongs to a call, it is NOT taken. * * Every walked edge and the basic blocks are added to the function. @@ -36,11 +36,11 @@ * * OUT: * - List of functions - * - Each functions starts with one CFEP and is a sub-graph in the bb_cfg. + * - Each functions starts with one CFEP and is a sub-graph in the bcfg. */ #include "rz_analysis.h" -#include "rz_inquiry/rz_bb_graph.h" +#include "rz_inquiry/rz_bcfg.h" #include "rz_types_base.h" #include "rz_util/ht_up.h" #include "rz_util/rz_assert.h" @@ -77,11 +77,11 @@ static void recurse_into_fcn_bbs( RzInquiryFunction *fcn, ut64 predecessor_bb_addr, ut64 this_bb_addr, - RzInquiryBBCFGEdgeType edge_type, + RzInquiryBCFGEdgeType edge_type, RzSetU *visited_fcn_bbs, const RzVector /**/ *cfep_addresses, const HtUP /**/ *call_candidates, - const RzInquiryBBCFG *binary_bb_cfg, + const RzInquiryBCFG *binary_bcfg, RZ_NONNULL const RzVector /**/ *ignored_code) { if (rz_set_u_contains(visited_fcn_bbs, this_bb_addr)) { return; @@ -92,26 +92,26 @@ static void recurse_into_fcn_bbs( // Add edge // RzInquiryBlock this_bb = { 0 }; - if (!rz_inquiry_bb_cfg_get_basic_block(binary_bb_cfg, this_bb_addr, &this_bb)) { + if (!rz_inquiry_bcfg_get_block(binary_bcfg, this_bb_addr, &this_bb)) { rz_warn_if_reached(); goto err_return; } - if (!rz_inquiry_bb_cfg_add_block(fcn->bb_cfg, this_bb.addr, this_bb.size)) { + if (!rz_inquiry_bcfg_add_block(fcn->bcfg, this_bb.addr, this_bb.size)) { rz_warn_if_reached(); goto err_return; } - if (edge_type != RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE) { + if (edge_type != RZ_INQUIRY_BCFG_EDGE_TYPE_NONE) { RzInquiryBlock from_bb = { 0 }; - if (!rz_inquiry_bb_cfg_get_basic_block(binary_bb_cfg, predecessor_bb_addr, &from_bb)) { + if (!rz_inquiry_bcfg_get_block(binary_bcfg, predecessor_bb_addr, &from_bb)) { rz_warn_if_reached(); goto err_return; } - if (!rz_inquiry_bb_cfg_add_block(fcn->bb_cfg, from_bb.addr, from_bb.size)) { + if (!rz_inquiry_bcfg_add_block(fcn->bcfg, from_bb.addr, from_bb.size)) { rz_warn_if_reached(); goto err_return; } - if (!rz_inquiry_bb_cfg_add_edge(fcn->bb_cfg, predecessor_bb_addr, this_bb_addr, edge_type)) { + if (!rz_inquiry_bcfg_add_edge(fcn->bcfg, predecessor_bb_addr, this_bb_addr, edge_type)) { rz_warn_if_reached(); goto err_return; } @@ -120,7 +120,7 @@ static void recurse_into_fcn_bbs( // // Visit neighbors // - RzIterator *successors = rz_inquiry_bb_cfg_get_outgoing_edges(binary_bb_cfg, this_bb_addr); + RzIterator *successors = rz_inquiry_bcfg_get_outgoing_edges(binary_bcfg, this_bb_addr); if (!successors) { // Node has no successors. goto err_return; @@ -128,15 +128,15 @@ static void recurse_into_fcn_bbs( const RzGraphEdge *e; rz_iterator_foreach(successors, e) { - RzInquiryBBCFGEdgeType etype = (RzInquiryBBCFGEdgeType)(utptr)rz_graph_edge_get_data(e); + RzInquiryBCFGEdgeType etype = (RzInquiryBCFGEdgeType)(utptr)rz_graph_edge_get_data(e); ut64 succ_addr = rz_graph_node_get_id(rz_graph_edge_get_to(e)); switch (etype) { - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE: - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF: - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP: + case RZ_INQUIRY_BCFG_EDGE_TYPE_NONE: + case RZ_INQUIRY_BCFG_EDGE_TYPE_CF: + case RZ_INQUIRY_BCFG_EDGE_TYPE_JMP: // Just an edge between two basic blocks. break; - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET: + case RZ_INQUIRY_BCFG_EDGE_TYPE_CALL_RET: // This is the next instruction packet after a call candidate. // It might be an non-existing edge which was only added as a guess. // In general all instructions after a call are added to the with @@ -152,8 +152,8 @@ static void recurse_into_fcn_bbs( continue; } break; - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL: - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_RETURN: + case RZ_INQUIRY_BCFG_EDGE_TYPE_CALL: + case RZ_INQUIRY_BCFG_EDGE_TYPE_RETURN: continue; } if (jumps_to_ignored_code(ignored_code, succ_addr)) { @@ -168,7 +168,7 @@ static void recurse_into_fcn_bbs( visited_fcn_bbs, cfep_addresses, call_candidates, - binary_bb_cfg, + binary_bcfg, ignored_code); } rz_iterator_free(successors); @@ -178,23 +178,23 @@ static void recurse_into_fcn_bbs( } static void fill_candidate_fcn_entry_points( - const RzInquiryBBCFG *binary_bb_cfg, + const RzInquiryBCFG *binary_bcfg, const HtUP /**/ *call_candidates, RzVector *cfep_addresses) { void **it; RzIterator *iter = ht_up_as_iter(call_candidates); rz_iterator_foreach(iter, it) { RzAnalysisCallCandidate *cc = *it; - RzIterator *predecessor = rz_inquiry_bb_cfg_get_outgoing_edges(binary_bb_cfg, cc->bb_addr); + RzIterator *predecessor = rz_inquiry_bcfg_get_outgoing_edges(binary_bcfg, cc->bb_addr); const RzGraphEdge *e; rz_iterator_foreach(predecessor, e) { - RzInquiryBBCFGEdgeType type = (RzInquiryBBCFGEdgeType)(utptr)rz_graph_edge_get_data(e); - if (type == RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET) { + RzInquiryBCFGEdgeType type = (RzInquiryBCFGEdgeType)(utptr)rz_graph_edge_get_data(e); + if (type == RZ_INQUIRY_BCFG_EDGE_TYPE_CALL_RET) { // TODO: Remove this check const RzGraphNode *nr = rz_graph_edge_get_to(e); ut64 target = rz_graph_node_get_id(nr); rz_warn_if_fail(target == cc->npc); - } else if (type == RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL) { + } else if (type == RZ_INQUIRY_BCFG_EDGE_TYPE_CALL) { const RzGraphNode *nc = rz_graph_edge_get_to(e); ut64 target = rz_graph_node_get_id(nc); rz_vector_push(cfep_addresses, &target); @@ -212,10 +212,10 @@ static void fill_candidate_fcn_entry_points( RZ_API bool rz_inquiry_algo_revng_fcn_detection( RzSetU *symbol_addresses, RZ_NONNULL const HtUP /**/ *call_candidates, - RZ_NONNULL const RzInquiryBBCFG *binary_bb_cfg, + RZ_NONNULL const RzInquiryBCFG *binary_bcfg, RZ_NONNULL RZ_OUT RzPVector /**/ *fcns, RZ_NONNULL const RzVector /**/ *ignored_code) { - rz_return_val_if_fail(call_candidates && binary_bb_cfg && fcns, false); + rz_return_val_if_fail(call_candidates && binary_bcfg && fcns, false); // Candidate function entry points RzVector *cfep_addresses = rz_vector_new(sizeof(ut64), NULL, NULL); @@ -225,7 +225,7 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( rz_vector_push(cfep_addresses, addr); } rz_iterator_free(iter); - fill_candidate_fcn_entry_points(binary_bb_cfg, call_candidates, cfep_addresses); + fill_candidate_fcn_entry_points(binary_bcfg, call_candidates, cfep_addresses); // Set of handled cfep RzSetU *cfep_handled = rz_set_u_new(); @@ -243,11 +243,11 @@ RZ_API bool rz_inquiry_algo_revng_fcn_detection( recurse_into_fcn_bbs(fcn, UT64_MAX, cfep_addr, - RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE, + RZ_INQUIRY_BCFG_EDGE_TYPE_NONE, visited_bbs, cfep_addresses, call_candidates, - binary_bb_cfg, + binary_bcfg, ignored_code); rz_set_u_free(visited_bbs); rz_set_u_add(cfep_handled, cfep_addr); diff --git a/librz/inquiry/bb_cfg.c b/librz/inquiry/bcfg.c similarity index 68% rename from librz/inquiry/bb_cfg.c rename to librz/inquiry/bcfg.c index a4ed4b5bd5b..7a1c688e7db 100644 --- a/librz/inquiry/bb_cfg.c +++ b/librz/inquiry/bcfg.c @@ -7,7 +7,7 @@ #include "rz_util/rz_graph.h" #include "rz_util/rz_iterator.h" #include "rz_vector.h" -#include +#include static ut64 hash_node(const void *data) { const RzInquiryBlock *bb = data; @@ -21,48 +21,48 @@ static RZ_OWN char *node_formatter(const RzGraphNode *n) { } static RZ_OWN char *edge_formatter(const RzGraphEdge *e) { - RzInquiryBBCFGEdgeType type = (utptr)rz_graph_edge_get_data(e); + RzInquiryBCFGEdgeType type = (utptr)rz_graph_edge_get_data(e); switch (type) { - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE: + case RZ_INQUIRY_BCFG_EDGE_TYPE_NONE: return rz_str_dup("[label=Unknown]"); - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP: - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF: + case RZ_INQUIRY_BCFG_EDGE_TYPE_JMP: + case RZ_INQUIRY_BCFG_EDGE_TYPE_CF: return NULL; - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET: + case RZ_INQUIRY_BCFG_EDGE_TYPE_CALL_RET: return rz_str_dup("[style=dotted label=npc]"); - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL: + case RZ_INQUIRY_BCFG_EDGE_TYPE_CALL: return rz_str_dup("[style=dotted label=call]"); - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_RETURN: + case RZ_INQUIRY_BCFG_EDGE_TYPE_RETURN: return rz_str_dup("[style=dotted label=ret]"); } rz_warn_if_reached(); return NULL; } -RZ_IPI RZ_OWN char *rz_inquiry_bb_cfg_as_dot(const RzInquiryBBCFG *bb_cfg, RZ_NULLABLE const char *name) { - rz_return_val_if_fail(bb_cfg, NULL); - return rz_graph_as_dot_str(bb_cfg->graph, name, node_formatter, edge_formatter); +RZ_IPI RZ_OWN char *rz_inquiry_bcfg_as_dot(const RzInquiryBCFG *bcfg, RZ_NULLABLE const char *name) { + rz_return_val_if_fail(bcfg, NULL); + return rz_graph_as_dot_str(bcfg->graph, name, node_formatter, edge_formatter); } -RZ_IPI RZ_OWN RzInquiryBBCFG *rz_inquiry_bb_cfg_new(RzGraphImplType impl_type) { - RzInquiryBBCFG *bb_cfg = RZ_NEW0(RzInquiryBBCFG); - if (!bb_cfg) { +RZ_IPI RZ_OWN RzInquiryBCFG *rz_inquiry_bcfg_new(RzGraphImplType impl_type) { + RzInquiryBCFG *bcfg = RZ_NEW0(RzInquiryBCFG); + if (!bcfg) { return NULL; } - bb_cfg->graph = rz_graph_new(impl_type, hash_node, free, NULL); - if (!bb_cfg->graph) { - rz_inquiry_bb_cfg_free(bb_cfg); + bcfg->graph = rz_graph_new(impl_type, hash_node, free, NULL); + if (!bcfg->graph) { + rz_inquiry_bcfg_free(bcfg); return NULL; } - return bb_cfg; + return bcfg; } -RZ_IPI void rz_inquiry_bb_cfg_free(RZ_NULLABLE RZ_OWN RzInquiryBBCFG *bb_cfg) { - if (!bb_cfg) { +RZ_IPI void rz_inquiry_bcfg_free(RZ_NULLABLE RZ_OWN RzInquiryBCFG *bcfg) { + if (!bcfg) { return; } - rz_graph_free(bb_cfg->graph); - free(bb_cfg); + rz_graph_free(bcfg->graph); + free(bcfg); } static bool edge_from(const RzGraphEdge *e, void *addr) { @@ -70,53 +70,53 @@ static bool edge_from(const RzGraphEdge *e, void *addr) { return rz_graph_node_get_id(rz_graph_edge_get_from(e)) == from_addr; } -RZ_IPI bool rz_inquiry_bb_cfg_del_out_edges(RzInquiryBBCFG *cfg, ut64 bb_addr) { +RZ_IPI bool rz_inquiry_bcfg_del_out_edges(RzInquiryBCFG *cfg, ut64 bb_addr) { return rz_graph_del_edges(cfg->graph, edge_from, RZ_GRAPH_INT_AS_DATA(bb_addr)); } /** - * \brief Adds an edge to the basic block CFG. + * \brief Adds an edge to the block CFG. * - * \param cfg The basic block CFG to edit. - * \param from_bb The address of the basic block with the branch. + * \param cfg The block CFG to edit. + * \param from_bb The address of the block with the branch. * Not the address of the branch instruction! - * \param to_bb The address of the basic block the branch leads to. + * \param to_bb The address of the block the branch leads to. * \param type The type of the edge. * * \return True if edge was added. False for error or if a node doesn't exist. */ -RZ_IPI bool rz_inquiry_bb_cfg_add_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBBCFGEdgeType type) { +RZ_IPI bool rz_inquiry_bcfg_add_edge(RzInquiryBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBCFGEdgeType type) { return rz_graph_add_edge_by_id(cfg->graph, from_bb, to_bb, RZ_GRAPH_INT_AS_DATA(type)); } -RZ_IPI bool rz_inquiry_bb_cfg_del_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb) { +RZ_IPI bool rz_inquiry_bcfg_del_edge(RzInquiryBCFG *cfg, ut64 from_bb, ut64 to_bb) { return rz_graph_del_edge_by_id(cfg->graph, from_bb, to_bb); } static bool is_cf_edge(const RzGraphEdge *e, void *unused) { - RzInquiryBBCFGEdgeType type = (utptr)rz_graph_edge_get_data(e); - return type == RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF || type == RZ_INQUIRY_BB_CFG_EDGE_TYPE_NONE; + RzInquiryBCFGEdgeType type = (utptr)rz_graph_edge_get_data(e); + return type == RZ_INQUIRY_BCFG_EDGE_TYPE_CF || type == RZ_INQUIRY_BCFG_EDGE_TYPE_NONE; } /** - * \brief Updates or adds an edge to the basic block CFG. - * Only edges of type RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF are updated to \p type. + * \brief Updates or adds an edge to the block CFG. + * Only edges of type RZ_INQUIRY_BCFG_EDGE_TYPE_CF are updated to \p type. * Otherwise the edge is not updated. * - * \param cfg The basic block CFG to edit. - * \param from_bb The address of the basic block with the branch. + * \param cfg The block CFG to edit. + * \param from_bb The address of the block with the branch. * Not the address of the branch instruction! - * \param to_bb The address of the basic block the branch leads to. + * \param to_bb The address of the block the branch leads to. * \param type The type of the edge. * * \return True if edge was added. False in case of error. */ -RZ_IPI bool rz_inquiry_bb_cfg_update_edge(RzInquiryBBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBBCFGEdgeType type) { +RZ_IPI bool rz_inquiry_bcfg_update_edge(RzInquiryBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBCFGEdgeType type) { return rz_graph_update_edge_by_id(cfg->graph, from_bb, to_bb, RZ_GRAPH_INT_AS_DATA(type), is_cf_edge, NULL); } -RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb_addr, RZ_OUT RZ_NULLABLE RzInquiryBlock *bb) { +RZ_IPI bool rz_inquiry_bcfg_get_block(const RzInquiryBCFG *cfg, ut64 bb_addr, RZ_OUT RZ_NULLABLE RzInquiryBlock *bb) { rz_return_val_if_fail(cfg, false); const RzGraphNode *n = rz_graph_find_node(cfg->graph, bb_addr); if (!n) { @@ -134,7 +134,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_get_basic_block(const RzInquiryBBCFG *cfg, ut64 bb /** * \brief Neighbors of outgoing edges. */ -RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_outgoing_edges(const RzInquiryBBCFG *cfg, ut64 bb_addr) { +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bcfg_get_outgoing_edges(const RzInquiryBCFG *cfg, ut64 bb_addr) { rz_return_val_if_fail(cfg, NULL); return rz_graph_out_edges_by_id(cfg->graph, bb_addr); } @@ -142,7 +142,7 @@ RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_outgoing_edg /** * \brief Neighbors of incoming edges. */ -RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_incoming_edges(const RzInquiryBBCFG *cfg, ut64 bb_addr) { +RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bcfg_get_incoming_edges(const RzInquiryBCFG *cfg, ut64 bb_addr) { rz_return_val_if_fail(cfg, NULL); return rz_graph_in_edges_by_id(cfg->graph, bb_addr); } @@ -151,7 +151,7 @@ RZ_API RZ_OWN RzIterator /**/ *rz_inquiry_bb_cfg_get_incoming_edg * \brief Does not update the BB if it is already present. * Returns false if it already exists. */ -RZ_IPI bool rz_inquiry_bb_cfg_add_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 size) { +RZ_IPI bool rz_inquiry_bcfg_add_block(RzInquiryBCFG *cfg, ut64 addr, ut64 size) { RzInquiryBlock *bb = RZ_NEW(RzInquiryBlock); if (!bb) { return false; @@ -173,31 +173,31 @@ RZ_IPI bool rz_inquiry_bb_cfg_add_block(RzInquiryBBCFG *cfg, ut64 addr, ut64 siz return true; } -RZ_IPI bool rz_inquiry_bb_cfg_add_xrefs(RzInquiryBBCFG *cfg, const RzVector /**/ *xrefs) { +RZ_IPI bool rz_inquiry_bcfg_add_edge_xref(RzInquiryBCFG *cfg, const RzVector /**/ *xrefs) { RzAnalysisXRef *xref; rz_vector_foreach (xrefs, xref) { switch (xref->type) { case RZ_ANALYSIS_XREF_TYPE_CODE: - if (!rz_inquiry_bb_cfg_update_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP)) { + if (!rz_inquiry_bcfg_update_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BCFG_EDGE_TYPE_JMP)) { RZ_LOG_DEBUG("Did not add JMP edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, xref->to); } break; case RZ_ANALYSIS_XREF_TYPE_CALL: - if (!rz_inquiry_bb_cfg_update_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL)) { + if (!rz_inquiry_bcfg_update_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BCFG_EDGE_TYPE_CALL)) { RZ_LOG_DEBUG("Did not add CALL edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, xref->to); } RzInquiryBlock bb = { 0 }; - if (!rz_inquiry_bb_cfg_get_basic_block(cfg, xref->bb_addr, &bb)) { + if (!rz_inquiry_bcfg_get_block(cfg, xref->bb_addr, &bb)) { rz_warn_if_reached(); break; } ut64 ret_addr = bb.addr + bb.size; - if (!rz_inquiry_bb_cfg_update_edge(cfg, xref->bb_addr, ret_addr, RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET)) { + if (!rz_inquiry_bcfg_update_edge(cfg, xref->bb_addr, ret_addr, RZ_INQUIRY_BCFG_EDGE_TYPE_CALL_RET)) { RZ_LOG_DEBUG("Did not add CALL_RET edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, ret_addr); } break; case RZ_ANALYSIS_XREF_TYPE_RETURN: - if (!rz_inquiry_bb_cfg_update_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BB_CFG_EDGE_TYPE_RETURN)) { + if (!rz_inquiry_bcfg_update_edge(cfg, xref->bb_addr, xref->to, RZ_INQUIRY_BCFG_EDGE_TYPE_RETURN)) { RZ_LOG_DEBUG("Did not add RETURN edge: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", xref->bb_addr, xref->to); } break; @@ -281,7 +281,7 @@ static int cmp(const RzInquiryBlock *a, const RzInquiryBlock *b, void *user) { * | * +-- 0x10a0 */ -RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg) { +RZ_IPI bool rz_inquiry_bcfg_reduce(RzInquiryBCFG *cfg) { RzPVector blocks = { 0 }; rz_pvector_init(&blocks, NULL); rz_pvector_reserve(&blocks, rz_graph_get_n_nodes(cfg->graph)); @@ -340,7 +340,7 @@ RZ_IPI bool rz_inquiry_bb_cfg_reduce(RzInquiryBBCFG *cfg) { rz_graph_del_edge_by_id(cfg->graph, a->addr, *to); } rz_vector_purge(&outedges); - rz_graph_add_edge_by_id(cfg->graph, a->addr, b->addr, RZ_GRAPH_INT_AS_DATA(RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF)); + rz_graph_add_edge_by_id(cfg->graph, a->addr, b->addr, RZ_GRAPH_INT_AS_DATA(RZ_INQUIRY_BCFG_EDGE_TYPE_CF)); // Check for blocks b overlaps with. a = b; } diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index bac4ed68cd1..109a35b392d 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -10,7 +10,7 @@ #include "rz_cons.h" #include "rz_il/definitions/mem.h" #include "rz_il/rz_il_vm.h" -#include "rz_inquiry/rz_bb_graph.h" +#include "rz_inquiry/rz_bcfg.h" #include "rz_inquiry/rz_il_cache.h" #include "rz_inquiry/rz_interpreter.h" #include "rz_inquiry_plugins.h" @@ -86,7 +86,7 @@ RZ_API void rz_inquiry_function_free(RZ_NULLABLE RZ_OWN RzInquiryFunction *fcn) if (!fcn) { return; } - rz_inquiry_bb_cfg_free(fcn->bb_cfg); + rz_inquiry_bcfg_free(fcn->bcfg); rz_vector_free(fcn->entry_points); free(fcn); } @@ -96,9 +96,9 @@ RZ_IPI RZ_OWN RzInquiryFunction *rz_inquiry_function_new() { if (!fcn) { return NULL; } - fcn->bb_cfg = rz_inquiry_bb_cfg_new(RZ_GRAPH_IMPL_LIST); + fcn->bcfg = rz_inquiry_bcfg_new(RZ_GRAPH_IMPL_LIST); fcn->entry_points = rz_vector_new(sizeof(ut64), NULL, NULL); - if (!fcn->bb_cfg || !fcn->entry_points) { + if (!fcn->bcfg || !fcn->entry_points) { rz_inquiry_function_free(fcn); return NULL; } @@ -114,7 +114,7 @@ RZ_API RZ_OWN char *rz_inquiry_function_str(const RzInquiryFunction *fcn) { rz_strbuf_appendf(buf, "%s0x%" PFMT64x, (i++ > 0 ? ", " : " "), *it); } rz_strbuf_append(buf, " ]\n"); - RzIterator *iter = rz_graph_get_nodes(fcn->bb_cfg->graph); + RzIterator *iter = rz_graph_get_nodes(fcn->bcfg->graph); RzGraphNode *n; rz_iterator_foreach(iter, n) { const RzInquiryBlock *bb = rz_graph_node_get_data(n); @@ -133,11 +133,11 @@ RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void) { iq->plugins_data = ht_sp_new(HT_STR_CONST, NULL, NULL); iq->call_candidates = ht_up_new(NULL, free); iq->dynamic_xrefs = rz_vector_new(sizeof(RzAnalysisXRef), NULL, NULL); - iq->bb_cfg = rz_inquiry_bb_cfg_new(RZ_GRAPH_IMPL_LIST); - if (!iq->plugins || !iq->plugins_data || !iq->bb_cfg) { + iq->bcfg = rz_inquiry_bcfg_new(RZ_GRAPH_IMPL_LIST); + if (!iq->plugins || !iq->plugins_data || !iq->bcfg) { ht_sp_free(iq->plugins); ht_sp_free(iq->plugins_data); - rz_inquiry_bb_cfg_free(iq->bb_cfg); + rz_inquiry_bcfg_free(iq->bcfg); free(iq); return NULL; } @@ -158,7 +158,7 @@ RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *iq) { ht_sp_free(iq->plugins); ht_sp_free(iq->plugins_data); ht_up_free(iq->call_candidates); - rz_inquiry_bb_cfg_free(iq->bb_cfg); + rz_inquiry_bcfg_free(iq->bcfg); rz_vector_free(iq->dynamic_xrefs); free(iq); } @@ -196,9 +196,9 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(RZ_NONNULL const RzAnalysisXRef * return false; } -static void handle_io_request(RzPVector /**/ *il_mems, RzInterpreterIORequest *io_req, RZ_OUT RzInterpreterIOResult *io_res) { +static void handle_io_request(RzPVector /**/ *il_mems, RzInterpIORequest *io_req, RZ_OUT RzInterpIOResult *io_res) { RZ_LOG_DEBUG("inquiry: Received IO %s request: mem:%" PFMTSZd " 0x%" PFMT64x "\n", - io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", + io_req->type == RZ_INTERP_IO_WRITE ? "write" : "read", io_req->mem_idx, rz_bv_to_ut64(io_req->addr)); io_res->req_ok = false; @@ -213,13 +213,13 @@ static void handle_io_request(RzPVector /**/ *il_mems, RzInterpreterI return; } RzILMem *mem = rz_pvector_at(il_mems, mem_idx); - if (io_req->type == RZ_INTERPRETER_IO_READ) { + if (io_req->type == RZ_INTERP_IO_READ) { io_res->req_ok = rz_il_mem_loadw_into(mem, io_req->ld_data, io_req->addr, io_req->n_bits, io_req->big_endian); } else { io_res->req_ok = rz_il_mem_storew(mem, io_req->addr, io_req->st_data, io_req->big_endian); } RZ_LOG_DEBUG("inquiry: Sent IO %s result. Success = %s.\n", - io_req->type == RZ_INTERPRETER_IO_WRITE ? "write" : "read", + io_req->type == RZ_INTERP_IO_WRITE ? "write" : "read", rz_str_bool(io_res->req_ok)); } @@ -285,23 +285,23 @@ static bool get_branch_targets(RzCore *core, RzSetU *branch_targets) { return true; } -static bool log_control_flow(RzInquiry *inquiry, RzInterpreterCtrlFlow *cf) { +static bool log_control_flow(RzInquiry *inquiry, RzInterpCtrlFlow *cf) { RZ_LOG_DEBUG("inquiry: Received control flow: 0x%" PFMT64x " size: %" PFMTSZu " (alt: 0x%" PFMT64x ")\n", cf->target_addr, cf->target_block_size, cf->alt_target); - rz_inquiry_bb_cfg_add_block(inquiry->bb_cfg, cf->actual_target, cf->target_block_size); + rz_inquiry_bcfg_add_block(inquiry->bcfg, cf->actual_target, cf->target_block_size); if (cf->alt_target) { // Add a dummy basic block at the address the call originally jumped to. // This is the basic block for the imported function. - rz_inquiry_bb_cfg_add_block(inquiry->bb_cfg, cf->target_addr, 1); + rz_inquiry_bcfg_add_block(inquiry->bcfg, cf->target_addr, 1); } // Add a simple control flow edge here. // It gets later updated to another type if a reported xref has it. - rz_inquiry_bb_cfg_add_edge(inquiry->bb_cfg, cf->src_block_addr, cf->actual_target, cf->type); + rz_inquiry_bcfg_add_edge(inquiry->bcfg, cf->src_block_addr, cf->actual_target, cf->type); return true; } -static bool handle_yields(RzInquiry *inquiry, RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM]) { - RzInterpreterYieldRBuf *rbuf_xrefs = yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF]; +static bool handle_yields(RzInquiry *inquiry, RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM]) { + RzInterpYieldRBuf *rbuf_xrefs = yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; rz_return_val_if_fail(rbuf_xrefs, false); RzAnalysisXRef xref = { 0 }; @@ -316,9 +316,9 @@ static bool handle_yields(RzInquiry *inquiry, RzInterpreterYieldRBuf *yield_rbuf } } - RzInterpreterYieldRBuf *rbuf_cf = yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW]; + RzInterpYieldRBuf *rbuf_cf = yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW]; if (!rz_th_ring_buf_is_empty_unsafe(rbuf_cf->rbuf)) { - RzInterpreterCtrlFlow cf = { 0 }; + RzInterpCtrlFlow cf = { 0 }; RzThreadRingBufResult r = rz_th_ring_buf_take(rbuf_cf->rbuf, &cf); if (r == RZ_THREAD_RING_BUF_CLOSED) { rz_warn_if_reached(); @@ -328,7 +328,7 @@ static bool handle_yields(RzInquiry *inquiry, RzInterpreterYieldRBuf *yield_rbuf } } - RzInterpreterYieldRBuf *rbuf_calls = yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE]; + RzInterpYieldRBuf *rbuf_calls = yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; rz_return_val_if_fail(rbuf_calls, false); RzAnalysisCallCandidate cc = { 0 }; @@ -358,7 +358,7 @@ static bool handle_yields(RzInquiry *inquiry, RzInterpreterYieldRBuf *yield_rbuf */ // TODO: Optimize static bool reduce_get_entry_points( - RzInterpreterSet *iset, + RzInterpSet *iset, RzILCache *il_cache, RZ_BORROW RzSetU /**/ *entry_points) { // Add the next entry point we need to check for executable regions the interpreters did not cover. @@ -391,7 +391,7 @@ static bool reduce_get_entry_points( return true; } -static void close_reset_ipc_obj(RzInterpreterSet *iset) { +static void close_reset_ipc_obj(RzInterpSet *iset) { // Close and clear all the IPC objects of this interpreter. // This also clears the buffer and queues rz_th_ring_buf_close(iset->io_request_rbuf); @@ -402,7 +402,7 @@ static void close_reset_ipc_obj(RzInterpreterSet *iset) { rz_list_free(rz_th_queue_pop_all(iset->il_queue)); } -static void open_ipc_obj(RzInterpreterSet *iset) { +static void open_ipc_obj(RzInterpSet *iset) { // Open queue again, so the interpretation can start at another // jump target again. rz_th_ring_buf_open(iset->io_request_rbuf); @@ -432,9 +432,9 @@ static bool collect_entry_points(RzCore *core, } static bool setup_yield_rbufs( - RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM], + RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM], RZ_OWN RzPVector /**/ *sections, - RzInterpreterYieldFilter yield_filter) { + RzInterpYieldFilter yield_filter) { // A single interpreter can produce different yields. // E.g. if the interpreter has a complex abstract memory model // for stack, heap and constant values. @@ -442,25 +442,25 @@ static bool setup_yield_rbufs( // These yield queues can be shared between different interpreters. // So we have one yield queue for each yield type. - RzInterpreterYieldKind yield_kind = RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE; - RzInterpreterYieldRBuf *rbuf = NULL; + RzInterpYieldKind yield_kind = RZ_INTERP_YIELD_KIND_CALL_CANDIDATE; + RzInterpYieldRBuf *rbuf = NULL; rbuf = rz_interpreter_yield_rbuf_new(yield_kind, NULL, NULL); if (!rbuf) { rz_warn_if_reached(); return false; } - yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE] = rbuf; + yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] = rbuf; - yield_kind = RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW; + yield_kind = RZ_INTERP_YIELD_KIND_CONTROL_FLOW; rbuf = NULL; rbuf = rz_interpreter_yield_rbuf_new(yield_kind, NULL, NULL); if (!rbuf) { rz_warn_if_reached(); return false; } - yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW] = rbuf; + yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] = rbuf; - yield_kind = RZ_INTERPRETER_YIELD_KIND_XREF; + yield_kind = RZ_INTERP_YIELD_KIND_XREF; rbuf = rz_interpreter_yield_rbuf_new( yield_kind, yield_filter, @@ -469,13 +469,13 @@ static bool setup_yield_rbufs( rz_warn_if_reached(); return false; } - yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF] = rbuf; + yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] = rbuf; return true; } struct ituple { RzThread *ithread; - RzInterpreterSet *iset; + RzInterpSet *iset; RzIntpRunStateFlag next_run_state; }; @@ -488,7 +488,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_NONNULL const RzVector /**/ *ignored_code) { // All the things we need bool return_code = true; - RzInterpreterSet *intp_iset = NULL; + RzInterpSet *intp_iset = NULL; RzBuffer *io_buf = rz_buf_new_with_io(rz_analysis_get_io_bind(core->analysis)); RzSetU *symbol_targets = rz_set_u_new(); @@ -537,9 +537,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, size_t n_threads = 1; iset_map = RZ_NEWS0(struct ituple, n_threads); - RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM] = { 0 }; + RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM] = { 0 }; if (!setup_yield_rbufs(yield_rbufs, rz_bin_object_get_sections(core->bin->cur->o), - (RzInterpreterYieldFilter)rz_inquiry_xref_interpreter_filter)) { + (RzInterpYieldFilter)rz_inquiry_xref_interpreter_filter)) { return_code = false; rz_warn_if_reached(); goto error_free; @@ -559,7 +559,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, intp_iset = rz_interpreter_set_new( core->analysis, prototype->p_interpreter, - RZ_INTERPRETER_ABSTRACTION_CONST, + RZ_INTERP_ABSTRACTION_CONST, il_req, il_queue, yield_rbufs, ignored_code); @@ -594,7 +594,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, user_sent_signal = true; break; } - RzInterpreterSet *iset = iset_map[i].iset; + RzInterpSet *iset = iset_map[i].iset; RzIntpRunStateFlag expected_rs = iset_map[i].next_run_state; switch (rz_intp_run_state_get_unsafe(iset->run_state)) { @@ -660,14 +660,14 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // But this requires multiple IO write caches // (one for each interpreter instance). // Because this is not yet implemented, there is only one interpreter thread for now. - RzInterpreterIORequest io_req = { 0 }; + RzInterpIORequest io_req = { 0 }; if (!rz_th_ring_buf_is_empty_unsafe(iset->io_request_rbuf)) { RzThreadRingBufResult r = rz_th_ring_buf_take(iset->io_request_rbuf, &io_req); if (r == RZ_THREAD_RING_BUF_CLOSED) { rz_warn_if_reached(); goto fatal_error; } else if (r == RZ_THREAD_RING_BUF_OK) { - RzInterpreterIOResult io_res = { 0 }; + RzInterpIOResult io_res = { 0 }; handle_io_request(&iset->il_vm->vm->vm_memory, &io_req, &io_res); if (rz_th_ring_buf_put(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { rz_warn_if_reached(); @@ -757,33 +757,33 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, char *bstr = rz_il_cache_block_str(block); RZ_LOG_DEBUG("inquiry: Add ILCache block: %s\n", bstr); free(bstr); - rz_inquiry_bb_cfg_add_block(core->inquiry->bb_cfg, block->addr, block->size); + rz_inquiry_bcfg_add_block(core->inquiry->bcfg, block->addr, block->size); } rz_iterator_free(iter); - if (!rz_inquiry_bb_cfg_add_xrefs(core->inquiry->bb_cfg, rz_il_cache_get_static_xrefs(il_cache))) { + if (!rz_inquiry_bcfg_add_edge_xref(core->inquiry->bcfg, rz_il_cache_get_static_xrefs(il_cache))) { rz_warn_if_reached(); } - if (!rz_inquiry_bb_cfg_add_xrefs(core->inquiry->bb_cfg, core->inquiry->dynamic_xrefs)) { + if (!rz_inquiry_bcfg_add_edge_xref(core->inquiry->bcfg, core->inquiry->dynamic_xrefs)) { rz_warn_if_reached(); } - char *g = rz_inquiry_bb_cfg_as_dot(core->inquiry->bb_cfg, "not_reduced"); - printf("%s\n", g); - free(g); - if (!rz_inquiry_bb_cfg_reduce(core->inquiry->bb_cfg)) { + // char *g = rz_inquiry_bcfg_as_dot(core->inquiry->bcfg, "not_reduced"); + // printf("%s\n", g); + // free(g); + if (!rz_inquiry_bcfg_reduce(core->inquiry->bcfg)) { rz_warn_if_reached(); } - g = rz_inquiry_bb_cfg_as_dot(core->inquiry->bb_cfg, "reduced"); - printf("%s\n", g); - free(g); + // g = rz_inquiry_bcfg_as_dot(core->inquiry->bcfg, "reduced"); + // printf("%s\n", g); + // free(g); RZ_LOG_DEBUG("inquiry: inquiry: inquiry: Done\n"); rz_config_set(core->config, "io.cache", io_cache_opt); error_free: - rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF]); - rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE]); - rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW]); + rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]); + rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]); + rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW]); free(iset_map); rz_set_u_free(entry_points); rz_buf_free(io_buf); @@ -797,30 +797,34 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, return return_code && !user_sent_signal; } -static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry, RzPVector *fcns, +RZ_API bool rz_inquiry_convert_and_add_to_analysis( + RzAnalysis *analysis, + RzInquiry *inquiry, + const RzPVector /**/ *fcns, const RzPVector /**/ *symbols) { + rz_return_val_if_fail(analysis && inquiry && fcns && symbols, false); // Add all discovered binary blocks to analysis - RzIterator *iter = rz_graph_get_nodes(inquiry->bb_cfg->graph); + RzIterator *iter = rz_graph_get_nodes(inquiry->bcfg->graph); RzGraphNode *n; rz_iterator_foreach(iter, n) { const RzInquiryBlock *bb = rz_graph_node_get_data(n); rz_analysis_add_bb(analysis, bb->addr, bb->size); RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, bb->addr); - RzIterator *out_edges = rz_inquiry_bb_cfg_get_outgoing_edges(inquiry->bb_cfg, bb->addr); + RzIterator *out_edges = rz_inquiry_bcfg_get_outgoing_edges(inquiry->bcfg, bb->addr); if (!out_edges) { continue; } RzGraphEdge *e; rz_iterator_foreach(out_edges, e) { ut64 target = rz_graph_node_get_id(rz_graph_edge_get_to(e)); - RzInquiryBBCFGEdgeType type = (RzInquiryBBCFGEdgeType)(utptr)rz_graph_edge_get_data(e); + RzInquiryBCFGEdgeType type = (RzInquiryBCFGEdgeType)(utptr)rz_graph_edge_get_data(e); switch (type) { default: continue; - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CALL_RET: - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_CF: - case RZ_INQUIRY_BB_CFG_EDGE_TYPE_JMP: { + case RZ_INQUIRY_BCFG_EDGE_TYPE_CALL_RET: + case RZ_INQUIRY_BCFG_EDGE_TYPE_CF: + case RZ_INQUIRY_BCFG_EDGE_TYPE_JMP: { if (abb->jump == UT64_MAX && abb->fail != target) { abb->jump = target; } else if (abb->fail == UT64_MAX && abb->jump != target) { @@ -868,7 +872,7 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry continue; } - RzIterator *iter = rz_graph_get_nodes(fcn->bb_cfg->graph); + RzIterator *iter = rz_graph_get_nodes(fcn->bcfg->graph); RzGraphNode *n; rz_iterator_foreach(iter, n) { const RzInquiryBlock *bb = rz_graph_node_get_data(n); @@ -881,23 +885,25 @@ static bool convert_and_add_to_analysis(RzAnalysis *analysis, RzInquiry *inquiry } rz_iterator_free(iter); } - rz_pvector_free(fcns); return true; } -RZ_API bool rz_inquiry_function_deduction(RzAnalysis *analysis, RzInquiry *inquiry, RzSetU *symbol_addresses, - const RzPVector /**/ *symbols, - RZ_NONNULL const RzVector /**/ *ignored_code) { - RzPVector *fcns = rz_pvector_new((RzPVectorFree)rz_inquiry_function_free); +RZ_API bool rz_inquiry_function_deduction( + RZ_NONNULL RZ_BORROW RzAnalysis *analysis, + RZ_NONNULL RZ_BORROW RzInquiry *inquiry, + RZ_NONNULL RZ_BORROW RzSetU *symbol_addresses, + RZ_NONNULL const RzPVector /**/ *symbols, + RZ_NONNULL const RzVector /**/ *ignored_code, + RZ_NONNULL RZ_OUT RzPVector /**/ *inquiry_fcns) { + rz_return_val_if_fail(analysis && inquiry && symbol_addresses && symbols && ignored_code && inquiry_fcns, false); if (!rz_inquiry_algo_revng_fcn_detection( symbol_addresses, inquiry->call_candidates, - inquiry->bb_cfg, - fcns, + inquiry->bcfg, + inquiry_fcns, ignored_code)) { rz_warn_if_reached(); return false; } - - return convert_and_add_to_analysis(analysis, inquiry, fcns, symbols); + return true; } diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 8f5d62b0607..2fcbd30a09e 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -16,7 +16,7 @@ #include #include -RZ_API void rz_interpreter_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpreterYieldRBuf *yield_rbufs) { +RZ_API void rz_interpreter_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yield_rbufs) { if (!yield_rbufs) { return; } @@ -30,10 +30,10 @@ RZ_API void rz_interpreter_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpreterYield free(yield_rbufs); } -RZ_API RZ_OWN RzInterpreterYieldRBuf *rz_interpreter_yield_rbuf_new(RzInterpreterYieldKind kind, - RzInterpreterYieldFilter filter, +RZ_API RZ_OWN RzInterpYieldRBuf *rz_interpreter_yield_rbuf_new(RzInterpYieldKind kind, + RzInterpYieldFilter filter, RZ_OWN RZ_NULLABLE void *filter_data) { - RzInterpreterYieldRBuf *yield_rbufs = RZ_NEW0(RzInterpreterYieldRBuf); + RzInterpYieldRBuf *yield_rbufs = RZ_NEW0(RzInterpYieldRBuf); if (!yield_rbufs) { return NULL; } @@ -42,18 +42,18 @@ RZ_API RZ_OWN RzInterpreterYieldRBuf *rz_interpreter_yield_rbuf_new(RzInterprete default: rz_warn_if_reached(); return NULL; - case RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE: - rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_YIELD_RBUF_SIZE, sizeof(RzAnalysisCallCandidate)); + case RZ_INTERP_YIELD_KIND_CALL_CANDIDATE: + rbuf = rz_th_ring_buf_new(RZ_INTERP_YIELD_RBUF_SIZE, sizeof(RzAnalysisCallCandidate)); break; - case RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW: - rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_YIELD_RBUF_SIZE, sizeof(RzInterpreterCtrlFlow)); + case RZ_INTERP_YIELD_KIND_CONTROL_FLOW: + rbuf = rz_th_ring_buf_new(RZ_INTERP_YIELD_RBUF_SIZE, sizeof(RzInterpCtrlFlow)); break; - case RZ_INTERPRETER_YIELD_KIND_XREF: + case RZ_INTERP_YIELD_KIND_XREF: if (filter_data) { - yield_rbufs->filter_data = RZ_NEW0(RzInterpreterYieldFilterData); + yield_rbufs->filter_data = RZ_NEW0(RzInterpYieldFilterData); yield_rbufs->filter_data->io_boundaries = filter_data; } - rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_YIELD_RBUF_SIZE, sizeof(RzAnalysisXRef)); + rbuf = rz_th_ring_buf_new(RZ_INTERP_YIELD_RBUF_SIZE, sizeof(RzAnalysisXRef)); if (!rbuf) { rz_pvector_free(filter_data); return NULL; @@ -74,19 +74,19 @@ RZ_API RZ_OWN RzInterpreterYieldRBuf *rz_interpreter_yield_rbuf_new(RzInterprete * \brief Initializes an abstract state for specified abstract kinds. Optionally with a list of registers. * The register name list should always be given if the architecture has some. */ -RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( +RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_new( const char *arch_name, - RzInterpreterAbstraction kinds, + RzInterpAbstraction kinds, RZ_OWN RZ_NONNULL RzAnalysisILConfig *il_config, RZ_NULLABLE const RzILRegBinding *reg_bindings) { rz_return_val_if_fail(il_config && reg_bindings, NULL); - RzInterpreterAbstrState *state = RZ_NEW0(RzInterpreterAbstrState); + RzInterpAbstrState *state = RZ_NEW0(RzInterpAbstrState); if (!state) { return NULL; } state->arch_name = arch_name; state->kinds = kinds; - state->pc = RZ_NEW0(RzInterpreterAbstrVal); + state->pc = RZ_NEW0(RzInterpAbstrVal); if (!state->pc) { return NULL; } @@ -95,7 +95,7 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( state->globals = ht_up_new(NULL, free); for (size_t i = 0; i < reg_bindings->regs_count; i++) { const char *rname = reg_bindings->regs[i].name; - RzInterpreterAbstrVal *aval = RZ_NEW0(RzInterpreterAbstrVal); + RzInterpAbstrVal *aval = RZ_NEW0(RzInterpAbstrVal); if (!aval) { ht_up_free(state->globals); ht_up_free(state->var_name_hashes); @@ -103,7 +103,7 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( return NULL; } - aval->kind = RZ_INTERPRETER_ABSTRACTION_UNDEF; + aval->kind = RZ_INTERP_ABSTRACTION_UNDEF; ut64 djb2_reg_hash = rz_str_djb2_hash(rname); if (!ht_up_insert(state->globals, djb2_reg_hash, aval) || !ht_up_insert(state->var_name_hashes, djb2_reg_hash, rz_str_dup(rname))) { @@ -119,7 +119,7 @@ RZ_API RZ_OWN RzInterpreterAbstrState *rz_interpreter_abstr_state_new( return state; } -RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbstrState *state) { +RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpAbstrState *state) { if (!state) { return; } @@ -144,7 +144,7 @@ RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpreterAbst free(state); } -RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpreterSet *iset) { +RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpSet *iset) { if (!iset) { return; } @@ -183,9 +183,9 @@ static bool setup_ipc_objects( // Setup the IO queues. Each interpreter instance needs it's own queue at // for writing IO. Because the writing is done on the IO cache, and each // instance needs its own cache. - *io_request_rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_IO_RBUF_SIZE, sizeof(RzInterpreterIORequest)); - *io_result_rbuf = rz_th_ring_buf_new(RZ_INTERPRETER_IO_RBUF_SIZE, sizeof(RzInterpreterIOResult)); - *entry_points = rz_th_ring_buf_new(RZ_INTERPRETER_ENTRY_POINTS_RBUF_SIZE, sizeof(ut64)); + *io_request_rbuf = rz_th_ring_buf_new(RZ_INTERP_IO_RBUF_SIZE, sizeof(RzInterpIORequest)); + *io_result_rbuf = rz_th_ring_buf_new(RZ_INTERP_IO_RBUF_SIZE, sizeof(RzInterpIOResult)); + *entry_points = rz_th_ring_buf_new(RZ_INTERP_ENTRY_POINTS_RBUF_SIZE, sizeof(ut64)); if (!*io_request_rbuf || !*io_result_rbuf || !*entry_points) { rz_warn_if_reached(); goto error_free; @@ -201,16 +201,16 @@ static bool setup_ipc_objects( } /** - * \brief Initializes a new RzInterpreterSet and returns it. + * \brief Initializes a new RzInterpSet and returns it. * If it fails, all arguments are freed. */ -RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( +RZ_API RZ_OWN RzInterpSet *rz_interpreter_set_new( RzAnalysis *analysis, - RZ_NONNULL RZ_OWN RzInterpreterPlugin *plugin, - RzInterpreterAbstraction abstraction, + RZ_NONNULL RZ_OWN RzInterpPlugin *plugin, + RzInterpAbstraction abstraction, RZ_NONNULL RZ_BORROW RzThreadRingBuf *il_request_rbuf, RZ_NONNULL RZ_BORROW RzThreadQueue *il_queue, - RzInterpreterYieldRBuf *yield_rbufs[RZ_INTERPRETER_YIELD_KIND_NUM], + RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM], RZ_NONNULL const RzVector /**/ *ignored_code) { rz_return_val_if_fail(plugin && ignored_code && analysis && il_request_rbuf && il_queue, NULL); @@ -219,7 +219,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( return NULL; } - RzInterpreterSet *iset = RZ_NEW0(RzInterpreterSet); + RzInterpSet *iset = RZ_NEW0(RzInterpSet); if (!iset) { return NULL; } @@ -244,7 +244,7 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( const RzAnalysisPlugin *cur = rz_analysis_plugin_current(analysis); RzAnalysisILConfig *config = cur->il_config(analysis); - RzInterpreterAbstrState *state = rz_interpreter_abstr_state_new( + RzInterpAbstrState *state = rz_interpreter_abstr_state_new( cur->arch, abstraction, config, @@ -273,9 +273,9 @@ RZ_API RZ_OWN RzInterpreterSet *rz_interpreter_set_new( iset->il_queue = il_queue; iset->il_request_rbuf = il_request_rbuf; iset->entry_points = entry_points; - iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF] = yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF]; - iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE] = yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE]; - iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW] = yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW]; + iset->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] = yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; + iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] = yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; + iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] = yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW]; iset->io_request_rbuf = io_request_rbuf; iset->io_result_rbuf = io_result_rbuf; iset->run_state_sync = rz_th_sem_new(0); @@ -295,11 +295,11 @@ static bool jumps_to_ignored_code(const RzVector *v, ut64 jump_target) { } typedef struct { - RzInterpreterCtrlFlow ctrl_flow; + RzInterpCtrlFlow ctrl_flow; ut64 in_state_hash; } SuccessorState; -static bool choose_next_pc(RzInterpreterSet *iset, +static bool choose_next_pc(RzInterpSet *iset, ut64 out_hash, RzVector *tmp_succ_addr, RzVector *succ_states, @@ -324,7 +324,7 @@ static bool choose_next_pc(RzInterpreterSet *iset, has_succsessor = !rz_vector_empty(tmp_succ_addr); // Request the successor effects over the queue. while (!rz_vector_empty(tmp_succ_addr)) { - RzInterpreterCtrlFlow cf = { 0 }; + RzInterpCtrlFlow cf = { 0 }; rz_vector_pop_front(tmp_succ_addr, &cf); if (cf.target_addr == UT64_MAX || cf.target_addr == 0) { RZ_LOG_DEBUG("interpreter: Quit due to invalid PC.\n"); @@ -358,7 +358,7 @@ static bool choose_next_pc(RzInterpreterSet *iset, * \brief Set entry point and reset (clean) the vectors and sets. */ static bool reset_intrpr_state( - RzInterpreterSet *iset, + RzInterpSet *iset, ut64 entry_point, RzVector **tmp_succ_addr, RzSetU **reachable_states, @@ -373,7 +373,7 @@ static bool reset_intrpr_state( return false; } - *tmp_succ_addr = rz_vector_new(sizeof(RzInterpreterCtrlFlow), NULL, NULL); + *tmp_succ_addr = rz_vector_new(sizeof(RzInterpCtrlFlow), NULL, NULL); *succ_states = rz_vector_new(sizeof(SuccessorState), NULL, NULL); *reachable_states = rz_set_u_new(); if (!tmp_succ_addr || !succ_states || !reachable_states) { @@ -386,14 +386,14 @@ static bool reset_intrpr_state( /** * Main interpretation. */ -RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { +RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpSet *iset) { rz_return_val_if_fail(iset && iset->astate && iset->il_request_rbuf && iset->il_queue && - iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF] && - iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE] && - iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW] && + iset->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] && + iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] && + iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] && iset->run_state_sync && iset->plugin && iset->plugin->eval && @@ -406,7 +406,7 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpreterSet *iset) { bool success = true; RZ_LOG_DEBUG("interpreter: Main: Hello.\n"); - RzInterpreterPlugin *plugin = iset->plugin; + RzInterpPlugin *plugin = iset->plugin; // // Start interpretation @@ -508,7 +508,7 @@ EMU: { // Now we know the size of the destination block. // Set it and report the control flow change. next.ctrl_flow.target_block_size = il_bb->size; - RzThreadRingBuf *cf_rbuf = iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CONTROL_FLOW]->rbuf; + RzThreadRingBuf *cf_rbuf = iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW]->rbuf; if (rz_th_ring_buf_put(cf_rbuf, &next.ctrl_flow) != RZ_THREAD_RING_BUF_OK) { return false; } @@ -519,7 +519,7 @@ EMU: { goto CLEAN; } - // Loop back. Interpret next basic block. + // Loop back. Interpret next block. goto EMU; } diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index a5e63515c43..abfb3eebb3f 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -14,7 +14,7 @@ #define MAX_INVOCATIONS_PER_BLOCK 3 -static bool eval(RZ_NONNULL RzInterpreterSet *iset, +static bool eval(RZ_NONNULL RzInterpSet *iset, RZ_NONNULL const RzILCacheBlock *il_bb, void *plugin_data) { ProtoIntrprPluginData *pdata = plugin_data; @@ -57,8 +57,8 @@ static bool eval(RZ_NONNULL RzInterpreterSet *iset, return true; } -bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, - RZ_NONNULL RZ_OUT RzVector /**/ *successors, +bool successors(RZ_NONNULL const RzInterpAbstrState *state, + RZ_NONNULL RZ_OUT RzVector /**/ *successors, void *plugin_data) { rz_return_val_if_fail(state && successors, false); ProtoIntrprPluginData *pdata = plugin_data; @@ -74,14 +74,14 @@ bool successors(RZ_NONNULL const RzInterpreterAbstrState *state, } ut64 next_pc = rz_bv_to_ut64(apc->bv); - RzInterpreterCtrlFlow branch = { 0 }; + RzInterpCtrlFlow branch = { 0 }; branch.target_addr = branch.actual_target = next_pc; branch.src_block_addr = pdata->prev_pc; rz_vector_push(successors, &branch); return true; } -static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data) { +static bool init_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { state->pc->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); ProtoIntrprAbstrData *apc = AD(state->pc->abstr_data); apc->bv = rz_bv_new_from_ut64(state->il_config->mem_key_size, 0); @@ -91,7 +91,7 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da ut64 *k; rz_iterator_foreach(it, k) { ut64 djb2_reg_name = *k; - RzInterpreterAbstrVal *av = ht_up_find(state->globals, djb2_reg_name, NULL); + RzInterpAbstrVal *av = ht_up_find(state->globals, djb2_reg_name, NULL); rz_return_val_if_fail(av, false); av->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); // Length doesn't matter here. Because the destination is always @@ -120,7 +120,7 @@ static bool init_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da return true; } -static bool reset_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_point, void *plugin_data) { +static bool reset_state(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point, void *plugin_data) { ProtoIntrprAbstrData *apc = AD(state->pc->abstr_data); rz_bv_set_from_ut64(apc->bv, entry_point); apc->is_concrete = true; @@ -129,7 +129,7 @@ static bool reset_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_poi ut64 *k; rz_iterator_foreach(it, k) { ut64 djb2_reg_name = *k; - RzInterpreterAbstrVal *av = ht_up_find(state->globals, djb2_reg_name, NULL); + RzInterpAbstrVal *av = ht_up_find(state->globals, djb2_reg_name, NULL); rz_bv_set_from_ut64(AD(av->abstr_data)->bv, 0); AD(av->abstr_data)->is_concrete = true; if (state->il_config->init_state) { @@ -151,7 +151,7 @@ static bool reset_state(RZ_BORROW RzInterpreterAbstrState *state, ut64 entry_poi return true; } -static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_data) { +static bool fini_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { ProtoIntrprAbstrData *ad = state->pc->abstr_data; if (ad && ad->bv) { rz_bv_free(ad->bv); @@ -160,9 +160,9 @@ static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da state->pc->abstr_data = NULL; RzIterator *it = ht_up_as_iter(state->globals); - RzInterpreterAbstrVal **v; + RzInterpAbstrVal **v; rz_iterator_foreach(it, v) { - RzInterpreterAbstrVal *av = *v; + RzInterpAbstrVal *av = *v; ProtoIntrprAbstrData *ad = av->abstr_data; if (ad && ad->bv) { rz_bv_free(ad->bv); @@ -174,7 +174,7 @@ static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da it = ht_up_as_iter(state->locals); rz_iterator_foreach(it, v) { - RzInterpreterAbstrVal *av = *v; + RzInterpAbstrVal *av = *v; ProtoIntrprAbstrData *ad = av->abstr_data; if (ad && ad->bv) { rz_bv_free(ad->bv); @@ -186,7 +186,7 @@ static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da it = ht_up_as_iter(state->lets); rz_iterator_foreach(it, v) { - RzInterpreterAbstrVal *av = *v; + RzInterpAbstrVal *av = *v; ProtoIntrprAbstrData *ad = av->abstr_data; if (ad && ad->bv) { rz_bv_free(ad->bv); @@ -203,16 +203,16 @@ static bool fini_state(RZ_BORROW RzInterpreterAbstrState *state, void *plugin_da * It is likely not sufficient to prevent collisions. * It is also slow. */ -static ut64 hash_state(RZ_NONNULL const RzInterpreterAbstrState *state, void *plugin_data) { +static ut64 hash_state(RZ_NONNULL const RzInterpAbstrState *state, void *plugin_data) { ut64 h = 5381; ProtoIntrprAbstrData *ad = state->pc->abstr_data; if (ad->bv) { h = (h ^ (h << 5)) ^ rz_bv_to_ut64(ad->bv); } RzIterator *it = ht_up_as_iter(state->globals); - RzInterpreterAbstrVal **v; + RzInterpAbstrVal **v; rz_iterator_foreach(it, v) { - RzInterpreterAbstrVal *av = *v; + RzInterpAbstrVal *av = *v; ProtoIntrprAbstrData *ad = av->abstr_data; if (ad->bv) { h = (h ^ (h << 5)) ^ rz_bv_to_ut64(ad->bv); @@ -222,7 +222,7 @@ static ut64 hash_state(RZ_NONNULL const RzInterpreterAbstrState *state, void *pl return h; } -bool state_as_str(RZ_NONNULL const RzInterpreterAbstrState *state, +bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, RZ_NONNULL RZ_OUT RzStrBuf *sb, void *plugin_data) { rz_return_val_if_fail(state && sb, false); @@ -238,7 +238,7 @@ bool state_as_str(RZ_NONNULL const RzInterpreterAbstrState *state, ut64 *k; rz_iterator_foreach(it, k) { const char *gname = ht_up_find(state->var_name_hashes, *k, NULL); - RzInterpreterAbstrVal *av = ht_up_find(state->globals, *k, NULL); + RzInterpAbstrVal *av = ht_up_find(state->globals, *k, NULL); ProtoIntrprAbstrData *ad = av->abstr_data; value = ad->is_concrete ? rz_bv_as_hex_string(ad->bv, true) : rz_str_dup("⊥"); rz_strbuf_appendf(sb, "\t%s = %s\n", gname, value); @@ -292,14 +292,14 @@ bool reset(void *plugin_data) { return true; } -static RzInterpreterPlugin rz_interpreter_plugin_prototype = { +static RzInterpPlugin rz_interpreter_plugin_prototype = { .name = "abstr_int_prototype", .author = "Rot127", .version = "0.1p", .desc = "A prototype interpreter for constant/bottom abstractions.", .license = "LGPL-3.0-only", - .supported_abstractions = RZ_INTERPRETER_ABSTRACTION_CONST, - .supported_yields = { RZ_INTERPRETER_YIELD_KIND_XREF, RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE }, + .supported_abstractions = RZ_INTERP_ABSTRACTION_CONST, + .supported_yields = { RZ_INTERP_YIELD_KIND_XREF, RZ_INTERP_YIELD_KIND_CALL_CANDIDATE }, .init = init, .reset = reset, .fini = fini, diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 38dd45f215a..474f4d1659d 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -11,7 +11,7 @@ #include bool report_yield_xref( - RzInterpreterSet *iset, + RzInterpSet *iset, size_t insn_pkt_size, ut64 from, const ProtoIntrprAbstrData *to, @@ -33,7 +33,7 @@ bool report_yield_xref( return true; } - RzInterpreterYieldRBuf *yrbuf = iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_XREF]; + RzInterpYieldRBuf *yrbuf = iset->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; rz_return_val_if_fail(yrbuf, false); ut64 to_addr = rz_bv_to_ut64(to->bv); @@ -55,9 +55,9 @@ bool report_yield_xref( * \brief Report the store of the next PC and report it as possible return point. */ bool report_yield_call_candiate( - RzInterpreterSet *iset, + RzInterpSet *iset, ProtoIntrprPluginData *plugin_data) { - RzInterpreterYieldRBuf *cc_rbuf = iset->yield_rbufs[RZ_INTERPRETER_YIELD_KIND_CALL_CANDIDATE]; + RzInterpYieldRBuf *cc_rbuf = iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; rz_return_val_if_fail(cc_rbuf, false); RzAnalysisCallCandidate cc = { 0 }; @@ -75,7 +75,7 @@ void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) dst->is_concrete = src->is_concrete; } -void write_var_to_state(RzInterpreterSet *iset, +void write_var_to_state(RzInterpSet *iset, RzILVarKind kind, ut64 var_id, const ProtoIntrprAbstrData *data) { @@ -94,22 +94,22 @@ void write_var_to_state(RzInterpreterSet *iset, ht_vals = iset->astate->lets; break; } - RzInterpreterAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); + RzInterpAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); if (!av) { if (kind == RZ_IL_VAR_KIND_GLOBAL) { RZ_LOG_WARN("New global variable created: 0x%" PFMT64x "\n", var_id) } - av = RZ_NEW0(RzInterpreterAbstrVal); + av = RZ_NEW0(RzInterpAbstrVal); ht_up_insert(ht_vals, var_id, av); } if (!av->abstr_data) { - av->kind = RZ_INTERPRETER_ABSTRACTION_CONST; + av->kind = RZ_INTERP_ABSTRACTION_CONST; av->abstr_data = adata_new(); } copy_abstr_data(av->abstr_data, data); } -bool read_var_from_state(RzInterpreterSet *iset, +bool read_var_from_state(RzInterpSet *iset, RzILVarKind kind, ut64 var_id, RZ_OUT ProtoIntrprAbstrData *data) { @@ -128,7 +128,7 @@ bool read_var_from_state(RzInterpreterSet *iset, ht_vals = iset->astate->lets; break; } - RzInterpreterAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); + RzInterpAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); if (!av || !av->abstr_data) { // Variable doesn't exist. // This should never happen and is a bug. @@ -145,7 +145,7 @@ bool read_var_from_state(RzInterpreterSet *iset, // TODO: The assumption that true != 0 is invalid. // It depends on the architecture and must be decided by the RzArch plugin. // State is passed due to this here as well. To make later refactoring easier. -bool abstr_is_true(const RzInterpreterSet *iset, const ProtoIntrprAbstrData *data) { +bool abstr_is_true(const RzInterpSet *iset, const ProtoIntrprAbstrData *data) { if (!data->is_concrete) { return false; } @@ -153,7 +153,7 @@ bool abstr_is_true(const RzInterpreterSet *iset, const ProtoIntrprAbstrData *dat } bool store_abstr_data( - RzInterpreterSet *iset, + RzInterpSet *iset, RzILMemIndex mem_idx, const ProtoIntrprAbstrData *addr, const ProtoIntrprAbstrData *src) { @@ -161,12 +161,12 @@ bool store_abstr_data( // Really don't write? return true; } - RzInterpreterIORequest io_req = { 0 }; + RzInterpIORequest io_req = { 0 }; io_req.n_bits = rz_bv_len(src->bv); io_req.mem_idx = mem_idx; io_req.big_endian = iset->astate->il_config->big_endian; - io_req.type = RZ_INTERPRETER_IO_WRITE; + io_req.type = RZ_INTERP_IO_WRITE; io_req.addr = addr->bv; io_req.st_data = src->bv; @@ -178,7 +178,7 @@ bool store_abstr_data( return false; } - RzInterpreterIOResult io_res = { 0 }; + RzInterpIOResult io_res = { 0 }; if (rz_th_ring_buf_take_blocking(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { return false; } @@ -186,14 +186,14 @@ bool store_abstr_data( } bool load_abstr_data( - RzInterpreterSet *iset, + RzInterpSet *iset, RzILMemIndex mem_idx, const ProtoIntrprAbstrData *addr, size_t n_bits, RZ_OUT ProtoIntrprAbstrData *out) { - RzInterpreterIORequest io_req = { 0 }; + RzInterpIORequest io_req = { 0 }; rz_bv_cast_inplace(out->bv, n_bits, 0); - io_req.type = RZ_INTERPRETER_IO_READ; + io_req.type = RZ_INTERP_IO_READ; io_req.addr = addr->bv; io_req.ld_data = out->bv; io_req.mem_idx = mem_idx; @@ -202,7 +202,7 @@ bool load_abstr_data( if (rz_th_ring_buf_put(iset->io_request_rbuf, &io_req) != RZ_THREAD_RING_BUF_OK) { return false; } - RzInterpreterIOResult io_res = { 0 }; + RzInterpIOResult io_res = { 0 }; if (rz_th_ring_buf_take_blocking(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { return false; } @@ -220,7 +220,7 @@ bool load_abstr_data( return true; } -bool set_abstr_pc(RzInterpreterAbstrState *state, ProtoIntrprAbstrData *pc, +bool set_abstr_pc(RzInterpAbstrState *state, ProtoIntrprAbstrData *pc, void *plugin_data) { rz_return_val_if_fail(state && pc, false); ProtoIntrprPluginData *pdata = plugin_data; @@ -236,7 +236,7 @@ bool set_abstr_pc(RzInterpreterAbstrState *state, ProtoIntrprAbstrData *pc, return true; } -bool set_pc(RzInterpreterAbstrState *state, ut64 pc, +bool set_pc(RzInterpAbstrState *state, ut64 pc, void *plugin_data) { rz_return_val_if_fail(state, false); ProtoIntrprPluginData *pdata = plugin_data; diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 472696de522..211957e66b1 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -11,7 +11,7 @@ #include /** - * \brief Abstract data getter from the RzInterpreterAbstrVal + * \brief Abstract data getter from the RzInterpAbstrVal */ #define AD(av) (((ProtoIntrprAbstrData *)av)) @@ -92,46 +92,46 @@ static inline RZ_OWN ProtoIntrprAbstrData *adata_new() { } void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src); -void write_var_to_state(RzInterpreterSet *iset, RzILVarKind kind, ut64 var_id, const ProtoIntrprAbstrData *data); -bool read_var_from_state(RzInterpreterSet *iset, RzILVarKind kind, ut64 var_id, RZ_OUT ProtoIntrprAbstrData *data); -bool abstr_is_true(const RzInterpreterSet *iset, const ProtoIntrprAbstrData *data); +void write_var_to_state(RzInterpSet *iset, RzILVarKind kind, ut64 var_id, const ProtoIntrprAbstrData *data); +bool read_var_from_state(RzInterpSet *iset, RzILVarKind kind, ut64 var_id, RZ_OUT ProtoIntrprAbstrData *data); +bool abstr_is_true(const RzInterpSet *iset, const ProtoIntrprAbstrData *data); bool store_abstr_data( - RzInterpreterSet *iset, + RzInterpSet *iset, RzILMemIndex mem_idx, const ProtoIntrprAbstrData *addr, const ProtoIntrprAbstrData *src); bool load_abstr_data( - RzInterpreterSet *iset, + RzInterpSet *iset, RzILMemIndex mem_idx, const ProtoIntrprAbstrData *addr, size_t n_bits, RZ_OUT ProtoIntrprAbstrData *out); -RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterSet *iset, +RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, const RzILOpEffect *effect, size_t nop_pc_inc, ProtoIntrprPluginData *plugin_data); RZ_IPI bool interpreter_prototype_eval_pure( - RzInterpreterSet *iset, + RzInterpSet *iset, const RzILOpPure *pure, RZ_OUT ProtoIntrprAbstrData *out, ProtoIntrprPluginData *plugin_data); bool report_yield_xref( - RzInterpreterSet *iset, + RzInterpSet *iset, size_t insn_pkt_size, ut64 from, const ProtoIntrprAbstrData *to, RzAnalysisXRefType type); bool report_yield_call_candiate( - RzInterpreterSet *iset, + RzInterpSet *iset, ProtoIntrprPluginData *plugin_data); -bool set_pc(RzInterpreterAbstrState *state, ut64 pc, +bool set_pc(RzInterpAbstrState *state, ut64 pc, void *plugin_data); -bool set_abstr_pc(RzInterpreterAbstrState *state, ProtoIntrprAbstrData *pc, +bool set_abstr_pc(RzInterpAbstrState *state, ProtoIntrprAbstrData *pc, void *plugin_data); void stack_frame_fini(ProtoInterprAbstrStackFrame *frame, void *unused); diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 46091adebac..6bc68467898 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -7,7 +7,7 @@ #include "rz_util/rz_bitvector.h" #include "rz_util/rz_str.h" -static bool value_indicates_ret_addr_write(RzInterpreterSet *iset, ProtoIntrprAbstrData *val) { +static bool value_indicates_ret_addr_write(RzInterpSet *iset, ProtoIntrprAbstrData *val) { return val->is_concrete && (rz_bv_to_ut64(val->bv) == iset->astate->bb_addr + iset->astate->bb_size || // Sparc stores the call instruction PC into o8. @@ -15,7 +15,7 @@ static bool value_indicates_ret_addr_write(RzInterpreterSet *iset, ProtoIntrprAb (rz_str_startswith(iset->astate->arch_name, "sparc") && rz_bv_to_ut64(val->bv) == rz_bv_to_ut64(AD(iset->astate->pc->abstr_data)->bv))); } -RZ_IPI bool interpreter_prototype_eval_effect(RzInterpreterSet *iset, +RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, const RzILOpEffect *effect, size_t insn_pkt_size, ProtoIntrprPluginData *plugin_data) { diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index be6ac5bd104..c6868f7e57f 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -9,7 +9,7 @@ * \brief Evaluate a pure. */ RZ_IPI bool interpreter_prototype_eval_pure( - RzInterpreterSet *iset, + RzInterpSet *iset, const RzILOpPure *pure, RZ_OUT ProtoIntrprAbstrData *out, ProtoIntrprPluginData *plugin_data) { diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index b6fc48786be..2519484e928 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -13,7 +13,7 @@ inquiry_plugins = { rz_inquiry_sources = [ 'algorithms/revng_fcn_detection.c', - 'bb_cfg.c', + 'bcfg.c', 'inquiry.c', 'il_cache.c', ] diff --git a/test/db/inquiry/interpreter/unmapped_fcn_loop b/test/db/inquiry/interpreter/unmapped_fcn_loop index 3208f0c4383..245b287a088 100644 --- a/test/db/inquiry/interpreter/unmapped_fcn_loop +++ b/test/db/inquiry/interpreter/unmapped_fcn_loop @@ -1,7 +1,7 @@ NAME=Indirect call x86 FILE=bins/inquiry/interpreter/prototype/x86_unmapped_fcn_in_loop CMDS=< Date: Fri, 29 May 2026 20:46:19 +0200 Subject: [PATCH 276/334] Add missing NULL check --- librz/inquiry/bcfg.c | 23 ++++++++++------------- librz/inquiry/inquiry.c | 2 +- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/librz/inquiry/bcfg.c b/librz/inquiry/bcfg.c index 7a1c688e7db..45f513fcd3d 100644 --- a/librz/inquiry/bcfg.c +++ b/librz/inquiry/bcfg.c @@ -71,8 +71,8 @@ static bool edge_from(const RzGraphEdge *e, void *addr) { } RZ_IPI bool rz_inquiry_bcfg_del_out_edges(RzInquiryBCFG *cfg, ut64 bb_addr) { - - return rz_graph_del_edges(cfg->graph, edge_from, RZ_GRAPH_INT_AS_DATA(bb_addr)); + rz_return_val_if_fail(cfg, false); + return rz_graph_del_edges(cfg->graph, edge_from, RZ_GRAPH_INT_AS_DATA(bb_addr)) != RZ_GRAPH_STATUS_ERR; } /** @@ -87,11 +87,15 @@ RZ_IPI bool rz_inquiry_bcfg_del_out_edges(RzInquiryBCFG *cfg, ut64 bb_addr) { * \return True if edge was added. False for error or if a node doesn't exist. */ RZ_IPI bool rz_inquiry_bcfg_add_edge(RzInquiryBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBCFGEdgeType type) { - return rz_graph_add_edge_by_id(cfg->graph, from_bb, to_bb, RZ_GRAPH_INT_AS_DATA(type)); + RzGraphStatus s = rz_graph_add_edge_by_id(cfg->graph, from_bb, to_bb, RZ_GRAPH_INT_AS_DATA(type)); + if (s == RZ_GRAPH_STATUS_OK || s == RZ_GRAPH_STATUS_EXISTED) { + return true; + } + return false; } RZ_IPI bool rz_inquiry_bcfg_del_edge(RzInquiryBCFG *cfg, ut64 from_bb, ut64 to_bb) { - return rz_graph_del_edge_by_id(cfg->graph, from_bb, to_bb); + return rz_graph_del_edge_by_id(cfg->graph, from_bb, to_bb) != RZ_GRAPH_STATUS_ERR; } static bool is_cf_edge(const RzGraphEdge *e, void *unused) { @@ -113,7 +117,7 @@ static bool is_cf_edge(const RzGraphEdge *e, void *unused) { * \return True if edge was added. False in case of error. */ RZ_IPI bool rz_inquiry_bcfg_update_edge(RzInquiryBCFG *cfg, ut64 from_bb, ut64 to_bb, RzInquiryBCFGEdgeType type) { - return rz_graph_update_edge_by_id(cfg->graph, from_bb, to_bb, RZ_GRAPH_INT_AS_DATA(type), is_cf_edge, NULL); + return rz_graph_update_edge_by_id(cfg->graph, from_bb, to_bb, RZ_GRAPH_INT_AS_DATA(type), is_cf_edge, NULL) != RZ_GRAPH_STATUS_ERR; } RZ_IPI bool rz_inquiry_bcfg_get_block(const RzInquiryBCFG *cfg, ut64 bb_addr, RZ_OUT RZ_NULLABLE RzInquiryBlock *bb) { @@ -158,18 +162,11 @@ RZ_IPI bool rz_inquiry_bcfg_add_block(RzInquiryBCFG *cfg, ut64 addr, ut64 size) } bb->addr = addr; bb->size = size; - bool existed; - RzGraphNode *n = rz_graph_add_get_node(cfg->graph, bb, &existed); - if (!n) { - return false; - } + rz_graph_add_node(cfg->graph, bb, NULL); // const RzInquiryBB *nbb = rz_graph_node_get_data(n); // if (nbb->size != size) { // rz_warn_if_reached(); // } - if (existed) { - free(bb); - } return true; } diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 109a35b392d..19a541eb0e7 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -831,7 +831,7 @@ RZ_API bool rz_inquiry_convert_and_add_to_analysis( abb->fail = target; } else if (abb->fail != target && abb->jump != target) { RZ_LOG_WARN("The basic block at 0x%" PFMT64x " has more than two outgoing edges.\n" - "\t\tHas jump = 0x%llx fail = 0x%llx. Will miss = 0x%llx (%d)\n", + "\t\tHas jump = 0x%" PFMT64x " fail = 0x%" PFMT64x ". Will miss = 0x%" PFMT64x " (%d)\n", bb->addr, abb->jump, abb->fail, target, type); } From 6d04a5f4191a5261fd6951a1aeab68f03fd8596e Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 29 May 2026 21:18:24 +0200 Subject: [PATCH 277/334] Reduce log level for common error cases. --- librz/inquiry/il_cache.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/librz/inquiry/il_cache.c b/librz/inquiry/il_cache.c index 579e263f461..d1b791ffc97 100644 --- a/librz/inquiry/il_cache.c +++ b/librz/inquiry/il_cache.c @@ -154,7 +154,7 @@ RZ_API RZ_OWN RzILCacheBlock *rz_il_cache_lift_il_block(const RzILCache *cache, } if (rz_analysis_op(cache->analysis, &op, addr, buf, max_read_size, RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_INSN_PKT) <= 0) { - RZ_LOG_ERROR("Failed to decode IL op\n"); + RZ_LOG_DEBUG("Failed to decode IL op\n"); goto fail; } bool lifted = true; @@ -251,7 +251,7 @@ static bool lift_executable_maps(RzILCache *cache) { while (addr < sec->vaddr + sec->vsize) { const RzILCacheBlock *block = lift_il_block(cache, addr); if (!block) { - RZ_LOG_WARN("Failed to lift IL block at 0x%" PFMT64x + RZ_LOG_INFO("Failed to lift IL block at 0x%" PFMT64x " - Stop lifting section '%s'\n", addr, sec->name); break; @@ -294,7 +294,7 @@ RZ_API bool rz_il_cache_serve(RZ_NONNULL RzILCache *cache) { rz_th_queue_push(serve_queue, (void *)block, true); } else { rz_th_queue_push(serve_queue, RZ_IL_CACHE_FAILED_LIFTING_PTR, true); - RZ_LOG_WARN("Failed to lift IL block at 0x%" PFMT64x "\n", req_addr); + RZ_LOG_DEBUG("Failed to lift IL block at 0x%" PFMT64x "\n", req_addr); } } } From 754ec57f7e568559a6fd66b9b5b5bfbfa5c74357 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 29 May 2026 21:42:01 +0200 Subject: [PATCH 278/334] Fix tests --- test/db/cmd/cmd_help | 8 +- test/db/inquiry/interpreter/unmapped_fcn_loop | 152 ++++++----- test/db/inquiry/interpreter/xrefs | 257 +++++++++--------- 3 files changed, 220 insertions(+), 197 deletions(-) diff --git a/test/db/cmd/cmd_help b/test/db/cmd/cmd_help index 183f4578c37..e0a76033375 100644 --- a/test/db/cmd/cmd_help +++ b/test/db/cmd/cmd_help @@ -19,7 +19,7 @@ EXPECT=< ...] # Abstract Interpreter Prototype +| aaaaIp -f [ ...] # Abstract Interpreter Prototype | aac # Analyze function calls | aaci # Analyze all function calls to imports | aaC # Analysis classes from RzBin @@ -65,7 +65,7 @@ CMDS=< ...]","args":[{"type":"expression","name":"entry_points","is_array":true}],"description":"","summary":"Abstract Interpreter Prototype","details":[]},"aac":{"cmd":"aac","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze function calls","details":[]},"aaci":{"cmd":"aaci","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all function calls to imports","details":[]},"aaC":{"cmd":"aaC","type":"argv","args_str":"","args":[],"description":"","summary":"Analysis classes from RzBin","details":[]},"aad":{"cmd":"aad","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze data references to code","details":[]},"aae":{"cmd":"aae","type":"argv","args_str":" []","args":[{"type":"expression","name":"len","is_last":true}],"description":"","summary":"Analyze references with ESIL","details":[{"name":"Examples","entries":[{"text":"aae","comment":"analyze ranges given by analysis.in","arg_str":""},{"text":"aae","comment":"analyze the whole section","arg_str":" $SS @ $S"}]}]},"aaef":{"cmd":"aaef","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze references with ESIL in all functions","details":[]},"aaf":{"cmd":"aaf","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions","details":[]},"aafe":{"cmd":"aafe","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions using ESIL","details":[]},"aafr":{"cmd":"aafr","type":"argv","args_str":" ","args":[{"type":"number","name":"length","required":true}],"description":"","summary":"Analyze all consecutive functions in section","details":[]},"aaft":{"cmd":"aaft","type":"argv","args_str":"","args":[],"description":"","summary":"Performs recursive type matching in all functions","details":[]},"aai":{"cmd":"aai","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details","details":[]},"aaij":{"cmd":"aaij","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details (JSON mode)","details":[]},"aaj":{"cmd":"aaj","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all unresolved jumps","details":[]},"aalg":{"cmd":"aalg","type":"argv","args_str":"","args":[],"description":"","summary":"Recover and analyze all Golang functions and strings","details":[]},"aalor":{"cmd":"aalor","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all Objective-C references from selector usages to their implementations","details":[]},"aalos":{"cmd":"aalos","type":"argv","args_str":"","args":[],"description":"","summary":"Recover all Objective-C selector stub names (__objc_stubs section contents)","details":[]},"aan":{"cmd":"aan","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions based on their strings or calls","details":[]},"aanr":{"cmd":"aanr","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions which does not return","details":[]},"aap":{"cmd":"aap","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all preludes","details":[{"name":"Search a custom prelude","entries":[{"text":"e analysis.prelude='90AEF630'","comment":"Set new prelude","arg_str":""},{"text":"aap","comment":"Search for 90AEF630 and create a new function","arg_str":""}]}]},"aar":{"cmd":"aar","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Analyze xrefs in current section or by n_bytes","details":[]},"aas":{"cmd":"aas","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the symbols","details":[]},"aaS":{"cmd":"aaS","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the flags starting as sym.* and entry*","details":[]},"aat":{"cmd":"aat","type":"argv","args_str":" []","args":[{"type":"function","name":"func_name"}],"description":"","summary":"Analyze all/given function to convert immediate to linked structure offsets","details":[]},"aaT":{"cmd":"aaT","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Prints commands to create functions after a trap call","details":[]},"aau":{"cmd":"aau","type":"argv","args_str":" []","args":[{"type":"number","name":"min_len"}],"description":"","summary":"Print memory areas not covered by functions","details":[]},"aav":{"cmd":"aav","type":"argv_state","args_str":"","args":[],"description":"","summary":"Analyze values referencing a specific section or map","details":[]},"aav*":{"cmd":"aav*","type":"argv_state","args_str":"","args":[],"description":"","summary":"Analyze values referencing a specific section or map (rizin mode)","details":[]}} +{"aa":{"cmd":"aa","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all flags starting with sym. and entry","details":[]},"aaa":{"cmd":"aaa","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all calls, references, emulation and applies signatures","details":[]},"aaaa":{"cmd":"aaaa","type":"argv","args_str":"","args":[],"description":"","summary":"Legacy experimental analysis","details":[]},"aaaaIp":{"cmd":"aaaaIp","type":"argv","args_str":" -f [ ...]","args":[{"type":"option","name":"f","required":true,"is_option":true,"default":"0"},{"type":"expression","name":"entry_points","is_array":true}],"description":"","summary":"Abstract Interpreter Prototype","details":[{"name":"Examples","entries":[{"text":"aaaaIp","comment":"Run prototype RzIL analysis detecting cross references.","arg_str":""},{"text":"aaaaIp","comment":"Run prototype RzIL analysis starting at address 0x1000.","arg_str":" 0x1000"},{"text":"aaaaIp","comment":"Run prototype RzIL analysis with function detection.","arg_str":" -f"}]}]},"aac":{"cmd":"aac","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze function calls","details":[]},"aaci":{"cmd":"aaci","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all function calls to imports","details":[]},"aaC":{"cmd":"aaC","type":"argv","args_str":"","args":[],"description":"","summary":"Analysis classes from RzBin","details":[]},"aad":{"cmd":"aad","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze data references to code","details":[]},"aae":{"cmd":"aae","type":"argv","args_str":" []","args":[{"type":"expression","name":"len","is_last":true}],"description":"","summary":"Analyze references with ESIL","details":[{"name":"Examples","entries":[{"text":"aae","comment":"analyze ranges given by analysis.in","arg_str":""},{"text":"aae","comment":"analyze the whole section","arg_str":" $SS @ $S"}]}]},"aaef":{"cmd":"aaef","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze references with ESIL in all functions","details":[]},"aaf":{"cmd":"aaf","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions","details":[]},"aafe":{"cmd":"aafe","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions using ESIL","details":[]},"aafr":{"cmd":"aafr","type":"argv","args_str":" ","args":[{"type":"number","name":"length","required":true}],"description":"","summary":"Analyze all consecutive functions in section","details":[]},"aaft":{"cmd":"aaft","type":"argv","args_str":"","args":[],"description":"","summary":"Performs recursive type matching in all functions","details":[]},"aai":{"cmd":"aai","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details","details":[]},"aaij":{"cmd":"aaij","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details (JSON mode)","details":[]},"aaj":{"cmd":"aaj","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all unresolved jumps","details":[]},"aalg":{"cmd":"aalg","type":"argv","args_str":"","args":[],"description":"","summary":"Recover and analyze all Golang functions and strings","details":[]},"aalor":{"cmd":"aalor","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all Objective-C references from selector usages to their implementations","details":[]},"aalos":{"cmd":"aalos","type":"argv","args_str":"","args":[],"description":"","summary":"Recover all Objective-C selector stub names (__objc_stubs section contents)","details":[]},"aan":{"cmd":"aan","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions based on their strings or calls","details":[]},"aanr":{"cmd":"aanr","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions which does not return","details":[]},"aap":{"cmd":"aap","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all preludes","details":[{"name":"Search a custom prelude","entries":[{"text":"e analysis.prelude='90AEF630'","comment":"Set new prelude","arg_str":""},{"text":"aap","comment":"Search for 90AEF630 and create a new function","arg_str":""}]}]},"aar":{"cmd":"aar","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Analyze xrefs in current section or by n_bytes","details":[]},"aas":{"cmd":"aas","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the symbols","details":[]},"aaS":{"cmd":"aaS","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the flags starting as sym.* and entry*","details":[]},"aat":{"cmd":"aat","type":"argv","args_str":" []","args":[{"type":"function","name":"func_name"}],"description":"","summary":"Analyze all/given function to convert immediate to linked structure offsets","details":[]},"aaT":{"cmd":"aaT","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Prints commands to create functions after a trap call","details":[]},"aau":{"cmd":"aau","type":"argv","args_str":" []","args":[{"type":"number","name":"min_len"}],"description":"","summary":"Print memory areas not covered by functions","details":[]},"aav":{"cmd":"aav","type":"argv_state","args_str":"","args":[],"description":"","summary":"Analyze values referencing a specific section or map","details":[]}} EOF RUN @@ -78,7 +78,7 @@ EXPECT=< ...] # Abstract Interpreter Prototype +| aaaaIp -f [ ...] # Abstract Interpreter Prototype | aac # Analyze function calls | aaci # Analyze all function calls to imports | aaC # Analysis classes from RzBin @@ -173,7 +173,7 @@ CMDS=< ...]","args":[{"type":"expression","name":"entry_points","is_array":true}],"description":"","summary":"Abstract Interpreter Prototype","details":[]},"aac":{"cmd":"aac","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze function calls","details":[]},"aaci":{"cmd":"aaci","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all function calls to imports","details":[]},"aaC":{"cmd":"aaC","type":"argv","args_str":"","args":[],"description":"","summary":"Analysis classes from RzBin","details":[]},"aad":{"cmd":"aad","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze data references to code","details":[]},"aae":{"cmd":"aae","type":"argv","args_str":" []","args":[{"type":"expression","name":"len","is_last":true}],"description":"","summary":"Analyze references with ESIL","details":[{"name":"Examples","entries":[{"text":"aae","comment":"analyze ranges given by analysis.in","arg_str":""},{"text":"aae","comment":"analyze the whole section","arg_str":" $SS @ $S"}]}]},"aaef":{"cmd":"aaef","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze references with ESIL in all functions","details":[]},"aaf":{"cmd":"aaf","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions","details":[]},"aafe":{"cmd":"aafe","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions using ESIL","details":[]},"aafr":{"cmd":"aafr","type":"argv","args_str":" ","args":[{"type":"number","name":"length","required":true}],"description":"","summary":"Analyze all consecutive functions in section","details":[]},"aaft":{"cmd":"aaft","type":"argv","args_str":"","args":[],"description":"","summary":"Performs recursive type matching in all functions","details":[]},"aai":{"cmd":"aai","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details","details":[]},"aaij":{"cmd":"aaij","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details (JSON mode)","details":[]},"aaj":{"cmd":"aaj","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all unresolved jumps","details":[]},"aalg":{"cmd":"aalg","type":"argv","args_str":"","args":[],"description":"","summary":"Recover and analyze all Golang functions and strings","details":[]},"aalor":{"cmd":"aalor","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all Objective-C references from selector usages to their implementations","details":[]},"aalos":{"cmd":"aalos","type":"argv","args_str":"","args":[],"description":"","summary":"Recover all Objective-C selector stub names (__objc_stubs section contents)","details":[]},"aan":{"cmd":"aan","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions based on their strings or calls","details":[]},"aanr":{"cmd":"aanr","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions which does not return","details":[]},"aap":{"cmd":"aap","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all preludes","details":[{"name":"Search a custom prelude","entries":[{"text":"e analysis.prelude='90AEF630'","comment":"Set new prelude","arg_str":""},{"text":"aap","comment":"Search for 90AEF630 and create a new function","arg_str":""}]}]},"aar":{"cmd":"aar","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Analyze xrefs in current section or by n_bytes","details":[]},"aas":{"cmd":"aas","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the symbols","details":[]},"aaS":{"cmd":"aaS","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the flags starting as sym.* and entry*","details":[]},"aat":{"cmd":"aat","type":"argv","args_str":" []","args":[{"type":"function","name":"func_name"}],"description":"","summary":"Analyze all/given function to convert immediate to linked structure offsets","details":[]},"aaT":{"cmd":"aaT","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Prints commands to create functions after a trap call","details":[]},"aau":{"cmd":"aau","type":"argv","args_str":" []","args":[{"type":"number","name":"min_len"}],"description":"","summary":"Print memory areas not covered by functions","details":[]},"aav":{"cmd":"aav","type":"argv_state","args_str":"","args":[],"description":"","summary":"Analyze values referencing a specific section or map","details":[]},"aav*":{"cmd":"aav*","type":"argv_state","args_str":"","args":[],"description":"","summary":"Analyze values referencing a specific section or map (rizin mode)","details":[]}} +{"aa":{"cmd":"aa","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all flags starting with sym. and entry","details":[]},"aaa":{"cmd":"aaa","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all calls, references, emulation and applies signatures","details":[]},"aaaa":{"cmd":"aaaa","type":"argv","args_str":"","args":[],"description":"","summary":"Legacy experimental analysis","details":[]},"aaaaIp":{"cmd":"aaaaIp","type":"argv","args_str":" -f [ ...]","args":[{"type":"option","name":"f","required":true,"is_option":true,"default":"0"},{"type":"expression","name":"entry_points","is_array":true}],"description":"","summary":"Abstract Interpreter Prototype","details":[{"name":"Examples","entries":[{"text":"aaaaIp","comment":"Run prototype RzIL analysis detecting cross references.","arg_str":""},{"text":"aaaaIp","comment":"Run prototype RzIL analysis starting at address 0x1000.","arg_str":" 0x1000"},{"text":"aaaaIp","comment":"Run prototype RzIL analysis with function detection.","arg_str":" -f"}]}]},"aac":{"cmd":"aac","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze function calls","details":[]},"aaci":{"cmd":"aaci","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all function calls to imports","details":[]},"aaC":{"cmd":"aaC","type":"argv","args_str":"","args":[],"description":"","summary":"Analysis classes from RzBin","details":[]},"aad":{"cmd":"aad","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze data references to code","details":[]},"aae":{"cmd":"aae","type":"argv","args_str":" []","args":[{"type":"expression","name":"len","is_last":true}],"description":"","summary":"Analyze references with ESIL","details":[{"name":"Examples","entries":[{"text":"aae","comment":"analyze ranges given by analysis.in","arg_str":""},{"text":"aae","comment":"analyze the whole section","arg_str":" $SS @ $S"}]}]},"aaef":{"cmd":"aaef","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze references with ESIL in all functions","details":[]},"aaf":{"cmd":"aaf","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions","details":[]},"aafe":{"cmd":"aafe","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions using ESIL","details":[]},"aafr":{"cmd":"aafr","type":"argv","args_str":" ","args":[{"type":"number","name":"length","required":true}],"description":"","summary":"Analyze all consecutive functions in section","details":[]},"aaft":{"cmd":"aaft","type":"argv","args_str":"","args":[],"description":"","summary":"Performs recursive type matching in all functions","details":[]},"aai":{"cmd":"aai","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details","details":[]},"aaij":{"cmd":"aaij","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details (JSON mode)","details":[]},"aaj":{"cmd":"aaj","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all unresolved jumps","details":[]},"aalg":{"cmd":"aalg","type":"argv","args_str":"","args":[],"description":"","summary":"Recover and analyze all Golang functions and strings","details":[]},"aalor":{"cmd":"aalor","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all Objective-C references from selector usages to their implementations","details":[]},"aalos":{"cmd":"aalos","type":"argv","args_str":"","args":[],"description":"","summary":"Recover all Objective-C selector stub names (__objc_stubs section contents)","details":[]},"aan":{"cmd":"aan","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions based on their strings or calls","details":[]},"aanr":{"cmd":"aanr","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions which does not return","details":[]},"aap":{"cmd":"aap","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all preludes","details":[{"name":"Search a custom prelude","entries":[{"text":"e analysis.prelude='90AEF630'","comment":"Set new prelude","arg_str":""},{"text":"aap","comment":"Search for 90AEF630 and create a new function","arg_str":""}]}]},"aar":{"cmd":"aar","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Analyze xrefs in current section or by n_bytes","details":[]},"aas":{"cmd":"aas","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the symbols","details":[]},"aaS":{"cmd":"aaS","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the flags starting as sym.* and entry*","details":[]},"aat":{"cmd":"aat","type":"argv","args_str":" []","args":[{"type":"function","name":"func_name"}],"description":"","summary":"Analyze all/given function to convert immediate to linked structure offsets","details":[]},"aaT":{"cmd":"aaT","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Prints commands to create functions after a trap call","details":[]},"aau":{"cmd":"aau","type":"argv","args_str":" []","args":[{"type":"number","name":"min_len"}],"description":"","summary":"Print memory areas not covered by functions","details":[]},"aav":{"cmd":"aav","type":"argv_state","args_str":"","args":[],"description":"","summary":"Analyze values referencing a specific section or map","details":[]}} EOF RUN diff --git a/test/db/inquiry/interpreter/unmapped_fcn_loop b/test/db/inquiry/interpreter/unmapped_fcn_loop index 245b287a088..bfc679979a6 100644 --- a/test/db/inquiry/interpreter/unmapped_fcn_loop +++ b/test/db/inquiry/interpreter/unmapped_fcn_loop @@ -9,71 +9,83 @@ axl ablt EOF EXPECT=< CODE -> 0x8000050 reloc.target.run + section..text+4 0x8000044 -> CALL -> 0x8000050 reloc.target.run reloc.target.run+26 0x800006a -> CODE -> 0x800009f reloc.recurse+27 - reloc.target.run+34 0x8000072 -> CODE -> 0x80000b0 sym.function_0 + reloc.target.run+34 0x8000072 -> CALL -> 0x80000b0 sym.function_0 reloc.target.run+34 0x8000072 -> DATA -> 0x80000d0 section..data - reloc.fcn_arr+12 0x8000081 -> CODE -> 0x8000088 reloc.recurse+4 - reloc.fcn_arr+14 0x8000083 -> CODE -> 0x8000040 section..text + reloc.fcn_arr+14 0x8000083 -> CALL -> 0x8000040 section..text reloc.recurse+25 0x800009d -> CODE -> 0x8000066 reloc.target.run+22 addr size traced ninstr jump fail fcns calls xrefs --------------------------------------------------------------------------------------- 0x8000040 9 0 0 0x08000049 0x08000040 0x08000050 0x08000050 0x8000049 2 0 0 0x08000040 +0x800004b 5 0 0 0x08000050 0x8000050 22 0 0 0x08000066 0x08000050 0x8000066 6 0 0 0x0800009f 0x0800006c 0x08000050 0x800006c 13 0 0 0x08000079 0x08000050 @@ -129,11 +141,15 @@ EXPECT=< 0x0800008d mov eax, dword [rbp-0x08] | 0x08000090 add rsp, 0x20 | 0x08000094 pop rbp \ 0x08000095 ret 0x08000096 nop word [rax+rax*1], ax - ; CODE XREF from sym.main @ 0x800006a + ; CALL XREF from sym.main @ 0x800006a / sym.function_0(); | 0x080000a0 push rbp | 0x080000a1 mov rbp, rsp | 0x080000a4 call sym.some_ptr -| ; CODE XREF from sym.some_ptr @ 0x80000bf +| ; RETURN XREF from sym.some_ptr @ 0x80000bf | 0x080000a9 pop rbp \ 0x080000aa ret 0x080000ab nop dword [rax+rax*1], eax - ; CODE XREF from sym.function_0 @ 0x80000a4 - ; CODE XREF from sym.function_1 @ 0x80000c4 - ; CODE XREF from sym.function_2 @ 0x80000d4 + ; CALL XREF from sym.function_0 @ 0x80000a4 + ; CALL XREF from sym.function_1 @ 0x80000c4 + ; CALL XREF from sym.function_2 @ 0x80000d4 / sym.some_ptr(); | 0x080000b0 push rbp | 0x080000b1 mov rbp, rsp @@ -61,54 +58,48 @@ EXPECT=< CODE -> 0x800008d reloc..data+32 - section..text+42 0x800006a -> CODE -> 0x80000a0 sym.function_0 - section..text+42 0x800006a -> CODE -> 0x80000c0 sym.function_1 - section..text+42 0x800006a -> CODE -> 0x80000d0 sym.function_2 + section..text+42 0x800006a -> CALL -> 0x80000a0 sym.function_0 section..text+42 0x800006a -> DATA -> 0x80000e0 section..data - section..text+42 0x800006a -> DATA -> 0x80000e8 reloc..text.80000e8 - section..text+42 0x800006a -> DATA -> 0x80000f0 reloc..text.80000f0 reloc..data+30 0x800008b -> CODE -> 0x800005d section..text+29 - sym.function_0+4 0x80000a4 -> CODE -> 0x80000b0 sym.some_ptr - sym.function_0+10 0x80000aa -> CODE -> 0x8000071 reloc..data+4 - reloc..bss+9 0x80000bf -> CODE -> 0x80000a9 sym.function_0+9 - reloc..bss+9 0x80000bf -> CODE -> 0x80000c9 sym.function_1+9 - reloc..bss+9 0x80000bf -> CODE -> 0x80000d9 sym.function_2+9 - sym.function_1+4 0x80000c4 -> CODE -> 0x80000b0 sym.some_ptr - sym.function_1+10 0x80000ca -> CODE -> 0x8000071 reloc..data+4 - sym.function_2+4 0x80000d4 -> CODE -> 0x80000b0 sym.some_ptr - sym.function_2+10 0x80000da -> CODE -> 0x8000071 reloc..data+4 + sym.function_0+4 0x80000a4 -> CALL -> 0x80000b0 sym.some_ptr + sym.function_0+10 0x80000aa -> RETURN -> 0x8000071 reloc..data+4 + reloc..bss+9 0x80000bf -> RETURN -> 0x80000a9 sym.function_0+9 + reloc..bss+9 0x80000bf -> RETURN -> 0x80000c9 sym.function_1+9 + reloc..bss+9 0x80000bf -> RETURN -> 0x80000d9 sym.function_2+9 + sym.function_1+4 0x80000c4 -> CALL -> 0x80000b0 sym.some_ptr + sym.function_2+4 0x80000d4 -> CALL -> 0x80000b0 sym.some_ptr addr size traced ninstr jump fail fcns calls xrefs --------------------------------------------------------------------------------------- 0x8000040 29 0 0 0x0800005d 0x08000040 -0x800005d 6 0 0 0x08000063 0x0800008d 0x08000040 -0x8000063 14 0 0 0x08000071 0x080000c0 0x08000040 +0x800005d 6 0 0 0x0800008d 0x08000063 0x08000040 +0x8000063 14 0 0 0x08000071 0x08000040 0x8000071 28 0 0 0x0800005d 0x08000040 0x800008d 9 0 0 0x08000040 +0x8000096 10 0 0 0x080000a0 0x80000a0 9 0 0 0x080000a9 0x080000a0 0x080000b0 0x080000b0 -0x80000a9 2 0 0 0x08000071 0x080000a0 -0x80000b0 16 0 0 0x080000a9 0x080000c9 0x080000b0 +0x80000a9 2 0 0 0x080000a0 +0x80000ab 5 0 0 +0x80000b0 16 0 0 0x080000b0 0x80000c0 9 0 0 0x080000c9 0x080000c0 0x080000b0 0x080000b0 -0x80000c9 2 0 0 0x08000071 0x080000c0 +0x80000c9 2 0 0 0x080000c0 +0x80000cb 5 0 0 0x080000d0 0x80000d0 9 0 0 0x080000d9 0x080000d0 0x080000b0 0x080000b0 -0x80000d9 2 0 0 0x08000071 0x080000d0 +0x80000d9 2 0 0 0x080000d0 EOF RUN @@ -144,9 +135,9 @@ EXPECT=< 0x080000ac [ R0 = memw(FP+##-0x8) \ 0x080000b0 [ LR:FP = dealloc_return(FP):raw - ; CODE XREF from sym.main @ 0x800007c + ; CALL XREF from sym.main @ 0x800007c / sym.function_0(); | 0x080000b4 [ allocframe(SP,#0x0):raw | 0x080000b8 [ call sym.some_ptr -| ; CODE XREF from sym.some_ptr @ 0x80000cc +| ; RETURN XREF from sym.some_ptr @ 0x80000cc \ 0x080000bc [ LR:FP = dealloc_return(FP):raw - ; CODE XREF from sym.function_0 @ 0x80000b8 - ; CODE XREF from sym.function_1 @ 0x80000d4 - ; CODE XREF from sym.function_2 @ 0x80000e0 + ; CALL XREF from sym.function_0 @ 0x80000b8 + ; CALL XREF from sym.function_1 @ 0x80000d4 + ; CALL XREF from sym.function_2 @ 0x80000e0 / sym.some_ptr(); | 0x080000c0 [ allocframe(SP,#0x0):raw | ;-- .bss: @@ -177,17 +168,17 @@ EXPECT=< DATA -> 0x80000e8 section..data reloc..data 0x8000074 -> DATA -> 0x80000ec reloc..text.80000ec reloc..data 0x8000074 -> DATA -> 0x80000f0 reloc..text.80000f0 - reloc..data.8000078+4 0x800007c -> CODE -> 0x80000b4 sym.function_0 - reloc..data.8000078+4 0x800007c -> CODE -> 0x80000d0 sym.function_1 - reloc..data.8000078+4 0x800007c -> CODE -> 0x80000dc sym.function_2 + reloc..data.8000078+4 0x800007c -> CALL -> 0x80000b4 sym.function_0 + reloc..data.8000078+4 0x800007c -> CALL -> 0x80000d0 sym.function_1 + reloc..data.8000078+4 0x800007c -> CALL -> 0x80000dc sym.function_2 reloc..data.8000078+48 0x80000a8 -> CODE -> 0x8000060 section..text+32 - sym.function_0+4 0x80000b8 -> CODE -> 0x80000c0 sym.some_ptr - sym.function_0+8 0x80000bc -> CODE -> 0x8000080 reloc..data.8000078+8 - reloc..bss.80000c8+4 0x80000cc -> CODE -> 0x80000bc sym.function_0+8 - reloc..bss.80000c8+4 0x80000cc -> CODE -> 0x80000d8 sym.function_1+8 - reloc..bss.80000c8+4 0x80000cc -> CODE -> 0x80000e4 sym.function_2+8 - sym.function_1+4 0x80000d4 -> CODE -> 0x80000c0 sym.some_ptr - sym.function_1+8 0x80000d8 -> CODE -> 0x8000080 reloc..data.8000078+8 - sym.function_2+4 0x80000e0 -> CODE -> 0x80000c0 sym.some_ptr - sym.function_2+8 0x80000e4 -> CODE -> 0x8000080 reloc..data.8000078+8 + sym.function_0+4 0x80000b8 -> CALL -> 0x80000c0 sym.some_ptr + sym.function_0+8 0x80000bc -> RETURN -> 0x8000080 reloc..data.8000078+8 + reloc..bss.80000c8+4 0x80000cc -> RETURN -> 0x80000bc sym.function_0+8 + reloc..bss.80000c8+4 0x80000cc -> RETURN -> 0x80000d8 sym.function_1+8 + reloc..bss.80000c8+4 0x80000cc -> RETURN -> 0x80000e4 sym.function_2+8 + sym.function_1+4 0x80000d4 -> CALL -> 0x80000c0 sym.some_ptr + sym.function_1+8 0x80000d8 -> RETURN -> 0x8000080 reloc..data.8000078+8 + sym.function_2+4 0x80000e0 -> CALL -> 0x80000c0 sym.some_ptr + sym.function_2+8 0x80000e4 -> RETURN -> 0x8000080 reloc..data.8000078+8 addr size traced ninstr jump fail fcns calls xrefs --------------------------------------------------------------------------------------- 0x8000040 32 0 0 0x08000060 0x08000040 0x8000060 12 0 0 0x0800006c 0x080000ac 0x08000040 0x800006c 4 0 0 0x08000070 0x08000040 -0x8000070 16 0 0 0x08000080 0x080000d0 0x08000040 +0x8000070 16 0 0 0x08000080 0x08000040 0x8000080 28 0 0 0x0800009c 0x08000040 0x800009c 16 0 0 0x08000060 0x08000040 0x80000ac 8 0 0 0x08000040 0x80000b4 8 0 0 0x080000bc 0x080000b4 0x080000c0 0x080000c0 -0x80000bc 4 0 0 0x08000080 0x080000b4 -0x80000c0 16 0 0 0x080000bc 0x080000d8 0x080000c0 +0x80000bc 4 0 0 0x080000b4 +0x80000c0 16 0 0 0x080000c0 0x80000d0 8 0 0 0x080000d8 0x080000d0 0x080000c0 0x080000c0 -0x80000d8 4 0 0 0x08000080 0x080000d0 +0x80000d8 4 0 0 0x080000d0 0x80000dc 8 0 0 0x080000e4 0x080000dc 0x080000c0 0x080000c0 -0x80000e4 4 0 0 0x08000080 0x080000dc +0x80000e4 4 0 0 0x080000dc EOF EXPECT_ERR=< 0x08000108 ld [fp+0x7f7], g1 | : 0x0800010c cmp g1, 2 | `==< 0x08000110 bleu icc, reloc..data +| ; CODE XREF from sym.main @ 0x80000e0 | 0x08000114 nop | 0x08000118 ld [fp+0x7fb], g1 | 0x0800011c sra g1, 0, g1 @@ -341,40 +335,42 @@ EXPECT=< CODE -> 0x8000064 sym.function_0+12 - reloc..bss.8000048+12 0x8000054 -> CODE -> 0x8000080 sym.function_1+12 - reloc..bss.8000048+12 0x8000054 -> CODE -> 0x800009c sym.function_2+12 - sym.function_0+8 0x8000060 -> CODE -> 0x8000040 section..text - sym.function_0+24 0x8000070 -> CODE -> 0x80000e0 reloc..data.80000c4+28 - sym.function_1+8 0x800007c -> CODE -> 0x8000040 section..text - sym.function_1+24 0x800008c -> CODE -> 0x80000e0 reloc..data.80000c4+28 - sym.function_2+8 0x8000098 -> CODE -> 0x8000040 section..text - sym.function_2+24 0x80000a8 -> CODE -> 0x80000e0 reloc..data.80000c4+28 + reloc..bss.8000048+12 0x8000054 -> RETURN -> 0x8000064 sym.function_0+12 + reloc..bss.8000048+12 0x8000054 -> RETURN -> 0x8000080 sym.function_1+12 + reloc..bss.8000048+12 0x8000054 -> RETURN -> 0x800009c sym.function_2+12 + sym.function_0+8 0x8000060 -> CALL -> 0x8000040 section..text + sym.function_0+24 0x8000070 -> RETURN -> 0x80000e0 reloc..data.80000c4+28 + sym.function_1+8 0x800007c -> CALL -> 0x8000040 section..text + sym.function_1+24 0x800008c -> RETURN -> 0x80000e0 reloc..data.80000c4+28 + sym.function_2+8 0x8000098 -> CALL -> 0x8000040 section..text + sym.function_2+24 0x80000a8 -> RETURN -> 0x80000e0 reloc..data.80000c4+28 sym.main+16 0x80000bc -> CODE -> 0x8000108 reloc..data.80000c4+68 reloc..data.80000c4+16 0x80000d4 -> DATA -> 0x8000130 section..data reloc..data.80000c4+16 0x80000d4 -> DATA -> 0x8000138 reloc..text.8000138 reloc..data.80000c4+16 0x80000d4 -> DATA -> 0x8000140 reloc..text.8000140 - reloc..data.80000c4+24 0x80000dc -> CODE -> 0x8000058 sym.function_0 - reloc..data.80000c4+24 0x80000dc -> CODE -> 0x8000074 sym.function_1 - reloc..data.80000c4+24 0x80000dc -> CODE -> 0x8000090 sym.function_2 + reloc..data.80000c4+24 0x80000dc -> CALL -> 0x8000058 sym.function_0 + reloc..data.80000c4+24 0x80000dc -> CALL -> 0x8000074 sym.function_1 + reloc..data.80000c4+24 0x80000dc -> CALL -> 0x8000090 sym.function_2 + reloc..data.80000c4+28 0x80000e0 -> CODE -> 0x8000114 reloc..data.80000c4+80 reloc..data.80000c4+80 0x8000114 -> CODE -> 0x80000c0 reloc..data addr size traced ninstr jump fail fcns calls xrefs ------------------------------------------------------------------------------------------------------- -0x8000040 24 0 0 0x08000064 0x08000080 0x08000040 +0x8000040 24 0 0 0x08000040 0x8000058 12 0 0 0x08000064 0x08000058 0x08000040 0x08000040 0x8000060 4 0 0 -0x8000064 16 0 0 0x080000e0 0x08000058 +0x8000064 16 0 0 0x08000058 0x8000074 12 0 0 0x08000080 0x08000074 0x08000040 0x08000040 0x800007c 4 0 0 -0x8000080 16 0 0 0x080000e0 0x08000074 +0x8000080 16 0 0 0x08000074 0x8000090 12 0 0 0x0800009c 0x08000090 0x08000040 0x08000040 0x8000098 4 0 0 -0x800009c 16 0 0 0x080000e0 0x08000090 +0x800009c 16 0 0 0x08000090 0x80000ac 20 0 0 0x08000108 0x080000ac -0x80000c0 32 0 0 0x080000e0 0x08000074 0x080000ac 0xffffffffffffffff 0xffffffffffffffff -0x80000dc 4 0 0 +0x80000c0 32 0 0 0x080000e0 0x080000ac 0xffffffffffffffff 0xffffffffffffffff +0x80000dc 4 0 0 0x080000e0 0x80000e0 40 0 0 0x08000108 0x080000ac 0x8000108 16 0 0 0x080000c0 0x08000118 0x080000ac +0x8000114 4 0 0 0x8000118 20 0 0 0x080000ac EOF EXPECT_ERR=< 0x080000d4 ldr r2, obj.fcn_arr ; [reloc..data:4]=0x8000134 obj.fcn_arr ; "4\U00000001" | :| 0x080000d8 ldr r3, [fp, -0xc] ; 12 -| :| 0x080000dc ldr r3, [r2, r3, lsl 2] ; 0x8000134 ; "T" +| :| 0x080000dc ldr r3, [r2, r3, lsl 2] ; 0x8000130 ; "4\U00000001" | :| 0x080000e0 mov lr, pc | :| 0x080000e4 bx r3 ; RELOC 0 -| :| ; CODE XREF from sym.function_0 @ 0x8000070 -| :| ; CODE XREF from sym.function_1 @ 0x8000090 +| :| ; RETURN XREF from sym.function_0 @ 0x8000070 +| :| ; RETURN XREF from sym.function_1 @ 0x8000090 | :| 0x080000e8 str r0, [fp, -0x10] ; 16 | :| 0x080000ec ldr r3, [fp, -0x10] ; 16 | :| 0x080000f0 ldrb r3, [r3] @@ -481,7 +479,7 @@ EXPECT=< DATA -> 0x8000050 reloc..bss - section..text+24 0x800004c -> CODE -> 0x8000060 reloc.target.function_0+12 - section..text+24 0x800004c -> CODE -> 0x8000080 reloc.target.function_1+12 - section..text+24 0x800004c -> CODE -> 0x80000a0 reloc.target.function_2+12 - reloc.target.function_0+8 0x800005c -> CODE -> 0x8000034 section..text - reloc.target.function_0+28 0x8000070 -> CODE -> 0x80000e8 sym.main+52 - reloc.target.function_1+8 0x800007c -> CODE -> 0x8000034 section..text - reloc.target.function_1+28 0x8000090 -> CODE -> 0x80000e8 sym.main+52 - reloc.target.function_2+8 0x800009c -> CODE -> 0x8000034 section..text + section..text+24 0x800004c -> RETURN -> 0x8000060 reloc.target.function_0+12 + section..text+24 0x800004c -> RETURN -> 0x8000080 reloc.target.function_1+12 + section..text+24 0x800004c -> RETURN -> 0x80000a0 reloc.target.function_2+12 + reloc.target.function_0+8 0x800005c -> CALL -> 0x8000034 section..text + reloc.target.function_0+28 0x8000070 -> RETURN -> 0x80000e8 sym.main+52 + reloc.target.function_1+8 0x800007c -> CALL -> 0x8000034 section..text + reloc.target.function_1+28 0x8000090 -> RETURN -> 0x80000e8 sym.main+52 + reloc.target.function_2+8 0x800009c -> CALL -> 0x8000034 section..text sym.main+28 0x80000d0 -> CODE -> 0x8000110 sym.main+92 sym.main+32 0x80000d4 -> DATA -> 0x8000130 reloc..data + sym.main+40 0x80000dc -> DATA -> 0x8000130 reloc..data sym.main+40 0x80000dc -> DATA -> 0x8000134 section..data sym.main+40 0x80000dc -> DATA -> 0x8000138 reloc.function_1 - sym.main+48 0x80000e4 -> CODE -> 0x8000054 reloc.target.function_0 - sym.main+48 0x80000e4 -> CODE -> 0x8000074 reloc.target.function_1 + sym.main+48 0x80000e4 -> CALL -> 0x8000054 reloc.target.function_0 + sym.main+48 0x80000e4 -> CALL -> 0x8000074 reloc.target.function_1 sym.main+100 0x8000118 -> CODE -> 0x80000d4 sym.main+32 addr size traced ninstr jump fail fcns calls xrefs --------------------------------------------------------------------------------------- -0x8000034 28 0 0 0x08000060 0x08000080 0x08000034 +0x8000034 28 0 0 0x08000034 +0x8000050 4 0 0 0x08000054 0x8000054 12 0 0 0x08000060 0x08000054 0x08000034 0x08000034 -0x8000060 20 0 0 0x080000e8 0x08000054 +0x8000060 20 0 0 0x08000054 0x8000074 12 0 0 0x08000080 0x08000074 0x08000034 0x08000034 -0x8000080 20 0 0 0x080000e8 0x08000074 +0x8000080 20 0 0 0x08000074 0x8000094 12 0 0 0x080000a0 0x08000094 0x08000034 0x08000034 0x80000a0 20 0 0 0x08000094 0x80000b4 32 0 0 0x08000110 0x080000b4 -0x80000d4 20 0 0 0x080000e8 0x08000074 0x080000b4 +0x80000d4 20 0 0 0x080000e8 0x080000b4 0x80000e8 40 0 0 0x08000110 0x080000b4 0x8000110 12 0 0 0x080000d4 0x0800011c 0x080000b4 0x800011c 20 0 0 0x080000b4 +0x8000134 1 0 0 EOF EXPECT_ERR=< Date: Fri, 29 May 2026 21:52:22 +0200 Subject: [PATCH 279/334] Remvoe unused file --- librz/include/rz_inquiry/rz_state.h | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 librz/include/rz_inquiry/rz_state.h diff --git a/librz/include/rz_inquiry/rz_state.h b/librz/include/rz_inquiry/rz_state.h deleted file mode 100644 index 4f54f986502..00000000000 --- a/librz/include/rz_inquiry/rz_state.h +++ /dev/null @@ -1,10 +0,0 @@ - - -/** - * \file The header file for the RzInterp contains declarations for - * all RzIL based interpreters. - */ - -#ifndef RZ_INTERP_STATE -#define RZ_INTERP_STATE -#endif // RZ_STATE From 1114600a00042d905ce7362362930e1e830aaede Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 29 May 2026 21:56:08 +0200 Subject: [PATCH 280/334] Add new headers to install --- librz/include/meson.build | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/librz/include/meson.build b/librz/include/meson.build index 81623839184..c7713c418f1 100644 --- a/librz/include/meson.build +++ b/librz/include/meson.build @@ -143,10 +143,12 @@ rz_util_files = [ ] install_headers(rz_util_files, install_dir: join_paths(rizin_incdir, 'rz_util')) -rz_interpreter_files = [ +rz_inquiry_sub_files = [ 'rz_inquiry/rz_interpreter.h', + 'rz_inquiry/rz_il_cache.h', + 'rz_inquiry/rz_bcfg.h', ] -install_headers(rz_interpreter_files, install_dir: join_paths(rizin_incdir, 'rz_interpreter')) +install_headers(rz_inquiry_sub_files, install_dir: join_paths(rizin_incdir, 'rz_inquiry')) rz_il_definitions_files = [ 'rz_il/definitions/bool.h', From c9fec8eeb731f0b5b9fff11bfde58f2ecff5763f Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 29 May 2026 22:05:27 +0200 Subject: [PATCH 281/334] Rename branch_targets -> control flow targets --- librz/arch/xrefs.c | 16 ++++++++-------- librz/include/rz_analysis.h | 2 +- librz/inquiry/inquiry.c | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/librz/arch/xrefs.c b/librz/arch/xrefs.c index 38550a56702..7be05e6d11c 100644 --- a/librz/arch/xrefs.c +++ b/librz/arch/xrefs.c @@ -334,22 +334,22 @@ static bool read_up_to(RzAnalysis *analysis, ut64 addr, ut8 *buf, size_t buf_siz } /** - * \brief Returns all targets of call/jmp instructions within the \p sections. + * \brief Returns all control flow targets of call/jmp instructions within the \p sections. * NOTE: This function disassembles all instructions within the boundaries and checks for calls/jmps. * It DOES NOT use the existing xrefs. * * \param analysis The analysis plugin. * \param sections The RzBinSections to disassemble to find call/jmp instructions. - * \param branch_targets The found call targets of all disassemble calls. * \param include_call_return_pts If true, it will add addresses after a call instruction as well. + * \param cf_targets The found call targets of all disassemble calls. * * \return True in case of success, false otherwise. */ -RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, +RZ_API bool rz_analysis_get_all_cf_targets(RzAnalysis *analysis, const RzPVector /**/ *sections, bool include_call_return_pts, - RZ_NONNULL RZ_OUT RzSetU *branch_targets) { - rz_return_val_if_fail(analysis && analysis->cur && sections && branch_targets, false); + RZ_NONNULL RZ_OUT RzSetU *cf_targets) { + rz_return_val_if_fail(analysis && analysis->cur && sections && cf_targets, false); size_t buf_size = (analysis->cur->bits / 8) * 16; ut8 *buf = RZ_NEWS0(ut8, buf_size); if (!buf_size || !buf) { @@ -379,17 +379,17 @@ RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, bool op_is_call = rz_analysis_op_is_direct_call(&op); bool op_is_jump = rz_analysis_op_is_direct_jump(&op); if ((op_is_call || op_is_jump) && op.jump != UT64_MAX) { - rz_set_u_add(branch_targets, op.jump); + rz_set_u_add(cf_targets, op.jump); if (op.fail != UT64_MAX) { if (op_is_jump) { - rz_set_u_add(branch_targets, op.fail); + rz_set_u_add(cf_targets, op.fail); } } } if (include_call_return_pts && op_is_call) { // If it is a call, also add the following instruction as reference. // Because it is likely a return point. - rz_set_u_add(branch_targets, op.addr + op.size); + rz_set_u_add(cf_targets, op.addr + op.size); } addr += op.size; rz_analysis_op_fini(&op); diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 8233aef8190..be04f85d0ad 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -1641,7 +1641,7 @@ RZ_API bool rz_analysis_xrefs_set(RzAnalysis *analysis, ut64 from, ut64 to, RzAn RZ_API bool rz_analysis_xrefs_deln(RzAnalysis *analysis, ut64 from, ut64 to, RzAnalysisXRefType type); RZ_API bool rz_analysis_xref_del(RzAnalysis *analysis, ut64 from, ut64 to); -RZ_API bool rz_analysis_get_all_branch_targets(RzAnalysis *analysis, +RZ_API bool rz_analysis_get_all_cf_targets(RzAnalysis *analysis, const RzPVector /**/ *sections, bool include_call_return_pts, RZ_NONNULL RZ_OUT RzSetU *branch_targets); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 19a541eb0e7..8bdc4a35d34 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -277,7 +277,7 @@ static bool get_branch_targets(RzCore *core, RzSetU *branch_targets) { rz_pvector_remove_at(sections, *j); } rz_vector_free(non_x_idx); - if (!rz_analysis_get_all_branch_targets(core->analysis, sections, true, branch_targets)) { + if (!rz_analysis_get_all_cf_targets(core->analysis, sections, true, branch_targets)) { RZ_LOG_ERROR("Failed to get branch targets.\n"); return false; } From fa7f03718c694fd7439e44dd3b0ee049bece06ed Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 30 May 2026 18:53:16 +0200 Subject: [PATCH 282/334] Disassemble instructions for each BB added to RzAnalysis --- librz/arch/fcn.c | 2 + test/db/inquiry/interpreter/unmapped_fcn_loop | 30 ++-- test/db/inquiry/interpreter/xrefs | 166 +++++++++--------- 3 files changed, 100 insertions(+), 98 deletions(-) diff --git a/librz/arch/fcn.c b/librz/arch/fcn.c index 87aaa764a98..714c5f49d87 100644 --- a/librz/arch/fcn.c +++ b/librz/arch/fcn.c @@ -1856,6 +1856,7 @@ RZ_API RzAnalysisFunction *rz_analysis_get_function_byname(RzAnalysis *a, const /** * \brief Adds a basic block to the RzAnalysis. + * It disassembles it to determine the instructions and their offset. */ RZ_API bool rz_analysis_add_bb(RzAnalysis *a, ut64 addr, ut64 size) { rz_return_val_if_fail(a && size > 0, false); @@ -1873,6 +1874,7 @@ RZ_API bool rz_analysis_add_bb(RzAnalysis *a, ut64 addr, ut64 size) { if (!block) { return false; } + rz_analysis_block_analyze_ops(block); return true; } diff --git a/test/db/inquiry/interpreter/unmapped_fcn_loop b/test/db/inquiry/interpreter/unmapped_fcn_loop index bfc679979a6..358458ff044 100644 --- a/test/db/inquiry/interpreter/unmapped_fcn_loop +++ b/test/db/inquiry/interpreter/unmapped_fcn_loop @@ -131,21 +131,21 @@ EXPECT=< CODE -> 0x8000066 reloc.target.run+22 addr size traced ninstr jump fail fcns calls xrefs --------------------------------------------------------------------------------------- -0x8000040 9 0 0 0x08000049 0x08000040 0x08000050 0x08000050 -0x8000049 2 0 0 0x08000040 -0x800004b 5 0 0 0x08000050 -0x8000050 22 0 0 0x08000066 0x08000050 -0x8000066 6 0 0 0x0800009f 0x0800006c 0x08000050 -0x800006c 13 0 0 0x08000079 0x08000050 -0x8000079 10 0 0 0x08000088 0x08000083 0x08000050 -0x8000083 5 0 0 0x08000088 0x08000050 0x08000040 0x08000040 -0x8000088 23 0 0 0x08000066 0x08000050 -0x800009f 9 0 0 0x08000050 -0x80000a8 8 0 0 0x080000b0 -0x80000b0 14 0 0 0x080000be 0x080000b0 0x08000440 0x08000440 -0x80000be 2 0 0 0x080000b0 -0x80000c0 14 0 0 0x080000ce 0x080000c0 0x08000440 0x08000440 -0x80000ce 2 0 0 0x080000c0 +0x8000040 9 0 3 0x08000049 0x08000040 0x08000050 0x08000083 +0x8000049 2 0 2 0x08000040 +0x800004b 5 0 1 0x08000050 +0x8000050 22 0 5 0x08000066 0x08000050 +0x8000066 6 0 2 0x0800009f 0x0800006c 0x08000050 0x0800009d +0x800006c 13 0 3 0x08000079 0x08000050 0x080000b0 +0x8000079 10 0 3 0x08000088 0x08000083 0x08000050 +0x8000083 5 0 1 0x08000088 0x08000050 0x08000040 +0x8000088 23 0 8 0x08000066 0x08000050 +0x800009f 9 0 4 0x08000050 +0x80000a8 8 0 1 0x080000b0 +0x80000b0 14 0 4 0x080000be 0x080000b0 0x08000440 +0x80000be 2 0 2 0x080000b0 +0x80000c0 14 0 4 0x080000ce 0x080000c0 0x08000440 +0x80000ce 2 0 2 0x080000c0 0x8000440 1 0 0 0x08000440 EOF EXPECT_ERR=< CALL -> 0x80000b0 sym.some_ptr addr size traced ninstr jump fail fcns calls xrefs --------------------------------------------------------------------------------------- -0x8000040 29 0 0 0x0800005d 0x08000040 -0x800005d 6 0 0 0x0800008d 0x08000063 0x08000040 -0x8000063 14 0 0 0x08000071 0x08000040 -0x8000071 28 0 0 0x0800005d 0x08000040 -0x800008d 9 0 0 0x08000040 -0x8000096 10 0 0 0x080000a0 -0x80000a0 9 0 0 0x080000a9 0x080000a0 0x080000b0 0x080000b0 -0x80000a9 2 0 0 0x080000a0 -0x80000ab 5 0 0 -0x80000b0 16 0 0 0x080000b0 -0x80000c0 9 0 0 0x080000c9 0x080000c0 0x080000b0 0x080000b0 -0x80000c9 2 0 0 0x080000c0 -0x80000cb 5 0 0 0x080000d0 -0x80000d0 9 0 0 0x080000d9 0x080000d0 0x080000b0 0x080000b0 -0x80000d9 2 0 0 0x080000d0 +0x8000040 29 0 6 0x0800005d 0x08000040 +0x800005d 6 0 2 0x0800008d 0x08000063 0x08000040 0x0800008b +0x8000063 14 0 4 0x08000071 0x08000040 0x080000a0 +0x8000071 28 0 9 0x0800005d 0x08000040 +0x800008d 9 0 4 0x08000040 +0x8000096 10 0 1 0x080000a0 +0x80000a0 9 0 3 0x080000a9 0x080000a0 0x080000b0 0x0800006a +0x80000a9 2 0 2 0x080000a0 0x080000bf +0x80000ab 5 0 1 +0x80000b0 16 0 5 0x080000b0 +0x80000c0 9 0 3 0x080000c9 0x080000c0 0x080000b0 +0x80000c9 2 0 2 0x080000c0 0x080000bf +0x80000cb 5 0 1 0x080000d0 +0x80000d0 9 0 3 0x080000d9 0x080000d0 0x080000b0 +0x80000d9 2 0 2 0x080000d0 0x080000bf EOF RUN @@ -215,22 +215,22 @@ EXPECT=< RETURN -> 0x8000080 reloc..data.8000078+8 sym.function_2+4 0x80000e0 -> CALL -> 0x80000c0 sym.some_ptr sym.function_2+8 0x80000e4 -> RETURN -> 0x8000080 reloc..data.8000078+8 - addr size traced ninstr jump fail fcns calls xrefs ---------------------------------------------------------------------------------------- -0x8000040 32 0 0 0x08000060 0x08000040 -0x8000060 12 0 0 0x0800006c 0x080000ac 0x08000040 -0x800006c 4 0 0 0x08000070 0x08000040 -0x8000070 16 0 0 0x08000080 0x08000040 -0x8000080 28 0 0 0x0800009c 0x08000040 -0x800009c 16 0 0 0x08000060 0x08000040 -0x80000ac 8 0 0 0x08000040 -0x80000b4 8 0 0 0x080000bc 0x080000b4 0x080000c0 0x080000c0 -0x80000bc 4 0 0 0x080000b4 -0x80000c0 16 0 0 0x080000c0 -0x80000d0 8 0 0 0x080000d8 0x080000d0 0x080000c0 0x080000c0 -0x80000d8 4 0 0 0x080000d0 -0x80000dc 8 0 0 0x080000e4 0x080000dc 0x080000c0 0x080000c0 -0x80000e4 4 0 0 0x080000dc + addr size traced ninstr jump fail fcns calls xrefs +------------------------------------------------------------------------------------------------------------- +0x8000040 32 0 8 0x08000060 0x08000040 +0x8000060 12 0 3 0x0800006c 0x080000ac 0x08000040 0x080000a8 +0x800006c 4 0 1 0x08000070 0x08000040 +0x8000070 16 0 4 0x08000080 0x08000040 0x080000b4 0x080000d0 0x080000dc +0x8000080 28 0 7 0x0800009c 0x08000040 +0x800009c 16 0 4 0x08000060 0x08000040 +0x80000ac 8 0 2 0x08000040 0x08000068 +0x80000b4 8 0 2 0x080000bc 0x080000b4 0x080000c0 0x0800007c +0x80000bc 4 0 1 0x080000b4 0x080000cc +0x80000c0 16 0 4 0x080000c0 +0x80000d0 8 0 2 0x080000d8 0x080000d0 0x080000c0 0x0800007c +0x80000d8 4 0 1 0x080000d0 0x080000cc +0x80000dc 8 0 2 0x080000e4 0x080000dc 0x080000c0 0x0800007c +0x80000e4 4 0 1 0x080000dc 0x080000cc EOF EXPECT_ERR=< CALL -> 0x8000090 sym.function_2 reloc..data.80000c4+28 0x80000e0 -> CODE -> 0x8000114 reloc..data.80000c4+80 reloc..data.80000c4+80 0x8000114 -> CODE -> 0x80000c0 reloc..data - addr size traced ninstr jump fail fcns calls xrefs -------------------------------------------------------------------------------------------------------- -0x8000040 24 0 0 0x08000040 -0x8000058 12 0 0 0x08000064 0x08000058 0x08000040 0x08000040 -0x8000060 4 0 0 -0x8000064 16 0 0 0x08000058 -0x8000074 12 0 0 0x08000080 0x08000074 0x08000040 0x08000040 -0x800007c 4 0 0 -0x8000080 16 0 0 0x08000074 -0x8000090 12 0 0 0x0800009c 0x08000090 0x08000040 0x08000040 -0x8000098 4 0 0 -0x800009c 16 0 0 0x08000090 -0x80000ac 20 0 0 0x08000108 0x080000ac -0x80000c0 32 0 0 0x080000e0 0x080000ac 0xffffffffffffffff 0xffffffffffffffff -0x80000dc 4 0 0 0x080000e0 -0x80000e0 40 0 0 0x08000108 0x080000ac -0x8000108 16 0 0 0x080000c0 0x08000118 0x080000ac -0x8000114 4 0 0 -0x8000118 20 0 0 0x080000ac + addr size traced ninstr jump fail fcns calls xrefs +--------------------------------------------------------------------------------------- +0x8000040 24 0 6 0x08000040 +0x8000058 12 0 3 0x08000064 0x08000058 0x08000040 0x080000dc +0x8000060 4 0 1 +0x8000064 16 0 4 0x08000058 +0x8000074 12 0 3 0x08000080 0x08000074 0x08000040 0x080000dc +0x800007c 4 0 1 +0x8000080 16 0 4 0x08000074 +0x8000090 12 0 3 0x0800009c 0x08000090 0x08000040 0x080000dc +0x8000098 4 0 1 +0x800009c 16 0 4 0x08000090 +0x80000ac 20 0 5 0x08000108 0x080000ac +0x80000c0 32 0 8 0x080000e0 0x080000ac +0x80000dc 4 0 1 0x080000e0 +0x80000e0 40 0 10 0x08000108 0x080000ac +0x8000108 16 0 4 0x080000c0 0x08000118 0x080000ac 0x080000e0 +0x8000114 4 0 1 0x080000e0 +0x8000118 20 0 5 0x080000ac EOF EXPECT_ERR=< CODE -> 0x80000d4 sym.main+32 addr size traced ninstr jump fail fcns calls xrefs --------------------------------------------------------------------------------------- -0x8000034 28 0 0 0x08000034 -0x8000050 4 0 0 0x08000054 -0x8000054 12 0 0 0x08000060 0x08000054 0x08000034 0x08000034 -0x8000060 20 0 0 0x08000054 -0x8000074 12 0 0 0x08000080 0x08000074 0x08000034 0x08000034 -0x8000080 20 0 0 0x08000074 -0x8000094 12 0 0 0x080000a0 0x08000094 0x08000034 0x08000034 -0x80000a0 20 0 0 0x08000094 -0x80000b4 32 0 0 0x08000110 0x080000b4 -0x80000d4 20 0 0 0x080000e8 0x080000b4 -0x80000e8 40 0 0 0x08000110 0x080000b4 -0x8000110 12 0 0 0x080000d4 0x0800011c 0x080000b4 -0x800011c 20 0 0 0x080000b4 -0x8000134 1 0 0 +0x8000034 28 0 7 0x08000034 +0x8000050 4 0 1 0x08000054 0x0800003c +0x8000054 12 0 3 0x08000060 0x08000054 0x08000034 0x080000e4 +0x8000060 20 0 5 0x08000054 +0x8000074 12 0 3 0x08000080 0x08000074 0x08000034 0x080000e4 +0x8000080 20 0 5 0x08000074 +0x8000094 12 0 3 0x080000a0 0x08000094 0x08000034 +0x80000a0 20 0 5 0x08000094 +0x80000b4 32 0 8 0x08000110 0x080000b4 +0x80000d4 20 0 5 0x080000e8 0x080000b4 +0x80000e8 40 0 10 0x08000110 0x080000b4 +0x8000110 12 0 3 0x080000d4 0x0800011c 0x080000b4 0x080000d0 +0x800011c 20 0 5 0x080000b4 +0x8000134 1 0 1 0x080000dc EOF EXPECT_ERR=< CODE -> 0x80001dc reloc..data.rel.local.80001a0+60 reloc..data.rel.local.80001a0+68 0x80001e4 -> CODE -> 0x8000194 reloc..data.rel.local - addr size traced ninstr jump fail fcns calls xrefs ---------------------------------------------------------------------------- -0x8000040 48 0 0 0x08000040 -0x8000070 4 0 0 -0x8000074 48 0 0 0x08000074 -0x80000a4 28 0 0 -0x80000c0 4 0 0 -0x80000c4 48 0 0 0x080000c4 -0x80000f4 28 0 0 -0x8000110 4 0 0 -0x8000114 48 0 0 0x08000114 -0x8000144 28 0 0 -0x8000160 4 0 0 0x08000164 -0x8000164 44 0 0 0x080001dc 0x08000164 -0x8000190 4 0 0 -0x8000194 32 0 0 0x08000164 -0x80001b4 40 0 0 0x080001dc -0x80001dc 12 0 0 0x08000194 0x080001e8 0x08000164 -0x80001e8 32 0 0 0x08000164 + addr size traced ninstr jump fail fcns calls xrefs +--------------------------------------------------------------------------------- +0x8000040 48 0 12 0x08000040 +0x8000070 4 0 1 +0x8000074 48 0 12 0x08000074 +0x80000a4 28 0 7 +0x80000c0 4 0 1 +0x80000c4 48 0 12 0x080000c4 +0x80000f4 28 0 7 +0x8000110 4 0 1 +0x8000114 48 0 12 0x08000114 +0x8000144 28 0 7 +0x8000160 4 0 1 0x08000164 +0x8000164 44 0 11 0x080001dc 0x08000164 +0x8000190 4 0 1 +0x8000194 32 0 8 0x08000164 +0x80001b4 40 0 10 0x080001dc +0x80001dc 12 0 3 0x08000194 0x080001e8 0x08000164 0x0800018c +0x80001e8 32 0 8 0x08000164 EOF EXPECT_ERR=< Date: Mon, 1 Jun 2026 08:48:58 +0200 Subject: [PATCH 283/334] Reduce BV_STACK_MAX_SIZE to avoid stack overflows --- librz/inquiry/interpreter/prototype/eval.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 211957e66b1..2f604f0cbf1 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -60,8 +60,11 @@ typedef struct { /** * \brief In bytes + * + * TODO: find a sweet spot here where this size is as small is possible, + * but in practice only very few heap allocations have to happen. */ -#define BV_STACK_MAX_SIZE 0x1000 +#define BV_STACK_MAX_SIZE 0x100 /** * \brief Initializes an AbstractData object on the stack. From de19731ef2eb7f0774c5446fa80acf8025d980cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Mon, 1 Jun 2026 09:03:13 +0200 Subject: [PATCH 284/334] Fix some nomenclature * top represents the universal set, bottom the empty set * is_concrete -> is_const to denote more clearly that this is a single constant value (mapping to that value in the concrete domain) but we are still in the abstract domain. --- .../interpreter/p/interpreter_prototype.c | 18 +- librz/inquiry/interpreter/prototype/eval.c | 20 +-- librz/inquiry/interpreter/prototype/eval.h | 16 +- .../interpreter/prototype/eval_effect.c | 18 +- .../inquiry/interpreter/prototype/eval_pure.c | 154 +++++++++--------- 5 files changed, 114 insertions(+), 112 deletions(-) diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index abfb3eebb3f..39aca2abeb6 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -49,7 +49,7 @@ static bool eval(RZ_NONNULL RzInterpSet *iset, if (!interpreter_prototype_eval_effect(iset, pkt->effect, pkt->insn_pkt_size, plugin_data)) { return false; } - if (pc == rz_bv_to_ut64(apc->bv) && apc->is_concrete) { + if (pc == rz_bv_to_ut64(apc->bv) && apc->is_const) { // Instruction did not manipulate the PC. Set it to the next instruction (packet). set_pc(iset->astate, pc + pkt->insn_pkt_size, plugin_data); } @@ -63,7 +63,7 @@ bool successors(RZ_NONNULL const RzInterpAbstrState *state, rz_return_val_if_fail(state && successors, false); ProtoIntrprPluginData *pdata = plugin_data; ProtoIntrprAbstrData *apc = state->pc->abstr_data; - if (!apc->is_concrete) { + if (!apc->is_const) { // The PC is not a concrete value. // This prototype can't estimate a reasonable concretization for it. return true; @@ -85,7 +85,7 @@ static bool init_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { state->pc->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); ProtoIntrprAbstrData *apc = AD(state->pc->abstr_data); apc->bv = rz_bv_new_from_ut64(state->il_config->mem_key_size, 0); - apc->is_concrete = true; + apc->is_const = true; RzIterator *it = ht_up_as_iter_keys(state->globals); ut64 *k; @@ -102,7 +102,7 @@ static bool init_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { AD(av->abstr_data)->bv = rz_bv_new(state->il_config->mem_key_size); // TODO: This is debatable. It depends on the ABI what the default values are. // Some values must be concrete, otherwise the interpretation of the prototype end too early. - AD(av->abstr_data)->is_concrete = true; + AD(av->abstr_data)->is_const = true; if (state->il_config->init_state) { RzAnalysisILInitStateVar *il_var; rz_vector_foreach (&state->il_config->init_state->vars, il_var) { @@ -123,7 +123,7 @@ static bool init_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { static bool reset_state(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point, void *plugin_data) { ProtoIntrprAbstrData *apc = AD(state->pc->abstr_data); rz_bv_set_from_ut64(apc->bv, entry_point); - apc->is_concrete = true; + apc->is_const = true; RzIterator *it = ht_up_as_iter_keys(state->globals); ut64 *k; @@ -131,7 +131,7 @@ static bool reset_state(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point, v ut64 djb2_reg_name = *k; RzInterpAbstrVal *av = ht_up_find(state->globals, djb2_reg_name, NULL); rz_bv_set_from_ut64(AD(av->abstr_data)->bv, 0); - AD(av->abstr_data)->is_concrete = true; + AD(av->abstr_data)->is_const = true; if (state->il_config->init_state) { RzAnalysisILInitStateVar *il_var; rz_vector_foreach (&state->il_config->init_state->vars, il_var) { @@ -230,7 +230,7 @@ bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, ut64 hash = hash_state(state, plugin_data); rz_strbuf_appendf(sb, "hash = 0x%" PFMT64x "\n\n", hash); rz_strbuf_append(sb, "Globals\n\n"); - char *value = AD(state->pc->abstr_data)->is_concrete ? rz_bv_as_hex_string(AD(state->pc->abstr_data)->bv, true) : rz_str_dup("⊥"); + char *value = AD(state->pc->abstr_data)->is_const ? rz_bv_as_hex_string(AD(state->pc->abstr_data)->bv, true) : rz_str_dup("⊥"); rz_strbuf_appendf(sb, "\tpc = %s\n\n", value); free(value); @@ -240,7 +240,7 @@ bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, const char *gname = ht_up_find(state->var_name_hashes, *k, NULL); RzInterpAbstrVal *av = ht_up_find(state->globals, *k, NULL); ProtoIntrprAbstrData *ad = av->abstr_data; - value = ad->is_concrete ? rz_bv_as_hex_string(ad->bv, true) : rz_str_dup("⊥"); + value = ad->is_const ? rz_bv_as_hex_string(ad->bv, true) : rz_str_dup("⊥"); rz_strbuf_appendf(sb, "\t%s = %s\n", gname, value); free(value); } @@ -296,7 +296,7 @@ static RzInterpPlugin rz_interpreter_plugin_prototype = { .name = "abstr_int_prototype", .author = "Rot127", .version = "0.1p", - .desc = "A prototype interpreter for constant/bottom abstractions.", + .desc = "A prototype interpreter for constant/top abstractions.", .license = "LGPL-3.0-only", .supported_abstractions = RZ_INTERP_ABSTRACTION_CONST, .supported_yields = { RZ_INTERP_YIELD_KIND_XREF, RZ_INTERP_YIELD_KIND_CALL_CANDIDATE }, diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 474f4d1659d..21b9e840ded 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -16,7 +16,7 @@ bool report_yield_xref( ut64 from, const ProtoIntrprAbstrData *to, RzAnalysisXRefType type) { - if (!to->is_concrete || rz_bv_len(to->bv) > 64) { + if (!to->is_const || rz_bv_len(to->bv) > 64) { // Isn't reported return true; } @@ -72,7 +72,7 @@ void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) rz_return_if_fail(dst && src && dst->bv && src->bv); rz_bv_cast_inplace(dst->bv, rz_bv_len(src->bv), false); rz_bv_copy(dst->bv, src->bv); - dst->is_concrete = src->is_concrete; + dst->is_const = src->is_const; } void write_var_to_state(RzInterpSet *iset, @@ -146,7 +146,7 @@ bool read_var_from_state(RzInterpSet *iset, // It depends on the architecture and must be decided by the RzArch plugin. // State is passed due to this here as well. To make later refactoring easier. bool abstr_is_true(const RzInterpSet *iset, const ProtoIntrprAbstrData *data) { - if (!data->is_concrete) { + if (!data->is_const) { return false; } return !rz_bv_is_zero_vector(data->bv); @@ -157,7 +157,7 @@ bool store_abstr_data( RzILMemIndex mem_idx, const ProtoIntrprAbstrData *addr, const ProtoIntrprAbstrData *src) { - if (!src->is_concrete) { + if (!src->is_const) { // Really don't write? return true; } @@ -212,7 +212,7 @@ bool load_abstr_data( n_bits, rz_bv_len(out->bv)); return false; } - out->is_concrete = true; + out->is_const = true; char *bytes = rz_bv_as_hex_string(out->bv, true); RZ_LOG_DEBUG("prototype: READ @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); @@ -225,14 +225,14 @@ bool set_abstr_pc(RzInterpAbstrState *state, ProtoIntrprAbstrData *pc, rz_return_val_if_fail(state && pc, false); ProtoIntrprPluginData *pdata = plugin_data; ProtoIntrprAbstrData *apc = AD(state->pc->abstr_data); - if (!apc->is_concrete || rz_bv_len(apc->bv) > 64) { + if (!apc->is_const || rz_bv_len(apc->bv) > 64) { pdata->prev_pc = UT64_MAX; } else { pdata->prev_pc = rz_bv_to_ut64(apc->bv); } copy_abstr_data(state->pc->abstr_data, pc); RZ_LOG_DEBUG("prototype: set_abstr_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", - pdata->prev_pc, rz_bv_to_ut64(apc->bv), apc->is_concrete ? "Concrete" : "Abstract"); + pdata->prev_pc, rz_bv_to_ut64(apc->bv), apc->is_const ? "Constant" : "Top"); return true; } @@ -241,14 +241,14 @@ bool set_pc(RzInterpAbstrState *state, ut64 pc, rz_return_val_if_fail(state, false); ProtoIntrprPluginData *pdata = plugin_data; ProtoIntrprAbstrData *apc = AD(state->pc->abstr_data); - if (!apc->is_concrete || rz_bv_len(apc->bv) > 64) { + if (!apc->is_const || rz_bv_len(apc->bv) > 64) { pdata->prev_pc = UT64_MAX; } else { pdata->prev_pc = rz_bv_to_ut64(apc->bv); } - apc->is_concrete = true; - RZ_LOG_DEBUG("prototype: set_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (Concrete)\n", + apc->is_const = true; + RZ_LOG_DEBUG("prototype: set_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (Constant)\n", rz_bv_to_ut64(apc->bv), pc); return rz_bv_set_from_ut64(apc->bv, pc); } diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 2f604f0cbf1..01061337cec 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -17,12 +17,14 @@ typedef struct { /** - * \brief Set if the bit vector below is a valid concrete value. - * If unset it is a bottom value. + * \brief Set if the abstract value represents a single constant bitvector. + * If set, the bit vector below is a valid concrete value. + * If unset it is a top value, i.e. represents the set of all bitvectors. */ - bool is_concrete; + bool is_const; /** - * \brief The concrete value. If is_concrete is unset this might hold garbage. + * \brief The single constant value. + * If is_const is unset this might hold garbage. */ RzBitVector *bv; } ProtoIntrprAbstrData; @@ -75,21 +77,21 @@ typedef struct { #define STACK_ABSTR_DATA_OUT(name) \ ut8 _##name##_bv_large_buf[BV_STACK_MAX_SIZE] = { 0 }; \ RzBitVector _##name##_bv_large = { .len = BV_STACK_MAX_SIZE, ._elem_len = BV_STACK_MAX_SIZE, .bits.large_a = _##name##_bv_large_buf, .stack_alloc = true }; \ - ProtoIntrprAbstrData name = { .is_concrete = false, .bv = &_##name##_bv_large }; + ProtoIntrprAbstrData name = { .is_const = false, .bv = &_##name##_bv_large }; /** * \brief Creates abstract data on the heap with the given bit vector. */ static inline RZ_OWN ProtoIntrprAbstrData *adata_from_bv(const RzBitVector *bv) { ProtoIntrprAbstrData *ad = RZ_NEW0(ProtoIntrprAbstrData); - ad->is_concrete = true; + ad->is_const = true; ad->bv = rz_bv_dup(bv); return ad; } static inline RZ_OWN ProtoIntrprAbstrData *adata_new() { ProtoIntrprAbstrData *ad = RZ_NEW0(ProtoIntrprAbstrData); - ad->is_concrete = false; + ad->is_const = false; ad->bv = rz_bv_new(BV_STACK_MAX_SIZE); return ad; } diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 6bc68467898..596c1c81fe3 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -8,7 +8,7 @@ #include "rz_util/rz_str.h" static bool value_indicates_ret_addr_write(RzInterpSet *iset, ProtoIntrprAbstrData *val) { - return val->is_concrete && + return val->is_const && (rz_bv_to_ut64(val->bv) == iset->astate->bb_addr + iset->astate->bb_size || // Sparc stores the call instruction PC into o8. // The return instruction jumps then to o7+8. @@ -27,7 +27,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, case RZ_IL_OP_EMPTY: break; case RZ_IL_OP_NOP: { - if (!pc->is_concrete) { + if (!pc->is_const) { // The PC is no longer a concrete value. // This plugin has no addition for it defined. break; @@ -74,15 +74,15 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, if (!interpreter_prototype_eval_pure(iset, effect->op.jmp.dst, &eval_out, plugin_data)) { goto error; } - if (!eval_out.is_concrete) { + if (!eval_out.is_const) { RZ_LOG_DEBUG("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", rz_bv_to_ut64(pc->bv)); } ut64 target = rz_bv_to_ut64(eval_out.bv); RZ_LOG_DEBUG("prototype: JMP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", rz_bv_to_ut64(pc->bv), target, - eval_out.is_concrete ? "Concrete" : "Abstract"); + eval_out.is_const ? "Concrete" : "Abstract"); - if (eval_out.is_concrete) { + if (eval_out.is_const) { RzAnalysisXRefType xref_type = RZ_ANALYSIS_XREF_TYPE_CODE; if (plugin_data->call_cand.store_addr) { @@ -116,7 +116,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, memset(&plugin_data->call_cand, 0, sizeof(plugin_data->call_cand)); } - // Setting the PC to a bottom value is allowed here! + // Setting the PC to a top value is allowed here! // The successor function will handle this case. set_abstr_pc(iset->astate, &eval_out, plugin_data); break; @@ -125,7 +125,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, if (!interpreter_prototype_eval_pure(iset, effect->op.branch.condition, &eval_out, plugin_data)) { goto error; } - if (!eval_out.is_concrete) { + if (!eval_out.is_const) { // Bottom values means we can't make a // decision (in this prototype implementation). break; @@ -152,7 +152,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, rz_bv_fini(st_addr.bv); goto error; } - if (!st_addr.is_concrete) { + if (!st_addr.is_const) { rz_bv_fini(st_addr.bv); break; } @@ -172,7 +172,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, rz_bv_fini(st_addr.bv); goto error; } - if (!eval_out.is_concrete) { + if (!eval_out.is_const) { rz_bv_fini(st_addr.bv); break; } diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index c6868f7e57f..d364692e898 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -45,9 +45,9 @@ RZ_IPI bool interpreter_prototype_eval_pure( RZ_LOG_ERROR("prototype: ITE condition failed to evaluate.\n"); return false; } - if (!out->is_concrete) { + if (!out->is_const) { // Can't decide which pure to evaluate. - goto map_to_bottom; + goto map_to_top; } if (abstr_is_true(iset, out)) { @@ -68,31 +68,31 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_cast_inplace(out->bv, 1, false); } rz_bv_set(out->bv, 0, false); - out->is_concrete = true; + out->is_const = true; break; case RZ_IL_OP_B1: if (rz_bv_len(out->bv) != 1) { rz_bv_cast_inplace(out->bv, 1, false); } rz_bv_set(out->bv, 0, true); - out->is_concrete = true; + out->is_const = true; break; case RZ_IL_OP_CAST: { if (!interpreter_prototype_eval_pure(iset, pure->op.cast.val, out, plugin_data)) { RZ_LOG_ERROR("prototype: CAST val failed to evaluate.\n"); return false; } - if (!out->is_concrete) { - goto map_to_bottom; + if (!out->is_const) { + goto map_to_top; } STACK_ABSTR_DATA_OUT(fill_bit); if (!interpreter_prototype_eval_pure(iset, pure->op.cast.fill, &fill_bit, plugin_data)) { RZ_LOG_ERROR("prototype: CAST fill failed to evaluate.\n"); return false; } - if (!fill_bit.is_concrete) { + if (!fill_bit.is_const) { rz_bv_fini(fill_bit.bv); - goto map_to_bottom; + goto map_to_top; } rz_bv_cast_inplace(out->bv, pure->op.cast.length, abstr_is_true(iset, &fill_bit)); break; @@ -100,7 +100,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_BITV: rz_bv_cast_inplace(out->bv, rz_bv_len(pure->op.bitv.value), false); rz_bv_copy(out->bv, pure->op.bitv.value); - out->is_concrete = true; + out->is_const = true; break; case RZ_IL_OP_APPEND: { STACK_ABSTR_DATA_OUT(high); @@ -108,22 +108,22 @@ RZ_IPI bool interpreter_prototype_eval_pure( RZ_LOG_ERROR("prototype: APPEND high failed to evaluate.\n"); return false; } - if (!high.is_concrete) { + if (!high.is_const) { rz_bv_fini(high.bv); - goto map_to_bottom; + goto map_to_top; } if (!interpreter_prototype_eval_pure(iset, pure->op.append.low, out, plugin_data)) { RZ_LOG_ERROR("prototype: APPEND low failed to evaluate.\n"); rz_bv_fini(high.bv); return false; } - if (!out->is_concrete) { + if (!out->is_const) { rz_bv_fini(high.bv); - goto map_to_bottom; + goto map_to_top; } rz_bv_cast_inplace(out->bv, rz_bv_len(out->bv) + rz_bv_len(high.bv), false); rz_bv_copy_nbits(high.bv, 0, out->bv, rz_bv_len(out->bv), rz_bv_len(high.bv)); - out->is_concrete = true; + out->is_const = true; rz_bv_fini(high.bv); break; } @@ -134,7 +134,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RZ_LOG_ERROR("prototype: INV x failed to evaluate.\n"); return false; } - if (out->is_concrete) { + if (out->is_const) { rz_bv_not_inplace(out->bv); } break; @@ -147,21 +147,21 @@ RZ_IPI bool interpreter_prototype_eval_pure( RZ_LOG_ERROR("prototype: AND x failed to evaluate.\n"); return false; } - if (!out->is_concrete) { - goto map_to_bottom; + if (!out->is_const) { + goto map_to_top; } STACK_ABSTR_DATA_OUT(y); if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: AND y failed to evaluate.\n"); return false; } - if (!y.is_concrete) { + if (!y.is_const) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } if (!rz_bv_and_inplace(out->bv, y.bv)) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } rz_bv_fini(y.bv); break; @@ -174,21 +174,21 @@ RZ_IPI bool interpreter_prototype_eval_pure( RZ_LOG_ERROR("prototype: OR x failed to evaluate.\n"); return false; } - if (!out->is_concrete) { - goto map_to_bottom; + if (!out->is_const) { + goto map_to_top; } STACK_ABSTR_DATA_OUT(y); if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: OR y failed to evaluate.\n"); return false; } - if (!y.is_concrete) { + if (!y.is_const) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } if (!rz_bv_or_inplace(out->bv, y.bv)) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } rz_bv_fini(y.bv); break; @@ -201,21 +201,21 @@ RZ_IPI bool interpreter_prototype_eval_pure( RZ_LOG_ERROR("prototype: XOR x failed to evaluate.\n"); return false; } - if (!out->is_concrete) { - goto map_to_bottom; + if (!out->is_const) { + goto map_to_top; } STACK_ABSTR_DATA_OUT(y); if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: XOR y failed to evaluate.\n"); return false; } - if (!y.is_concrete) { + if (!y.is_const) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } if (!rz_bv_xor_inplace(out->bv, y.bv)) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } rz_bv_fini(y.bv); break; @@ -228,7 +228,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( switch (pure->code) { default: rz_warn_if_reached(); - goto map_to_bottom; + goto map_to_top; case RZ_IL_OP_IS_ZERO: bv = pure->op.is_zero.bv; truth_test = rz_bv_is_zero_vector; @@ -246,8 +246,8 @@ RZ_IPI bool interpreter_prototype_eval_pure( RZ_LOG_ERROR("prototype: MSB/LSB/IS_ZERO bv failed to evaluate.\n"); return false; } - if (!out->is_concrete) { - goto map_to_bottom; + if (!out->is_const) { + goto map_to_top; } bool truth = truth_test(out->bv); rz_bv_cast_inplace(out->bv, 1, false); @@ -260,7 +260,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RZ_LOG_ERROR("prototype: NEG bv failed to evaluate.\n"); return false; } - if (out->is_concrete) { + if (out->is_const) { rz_bv_neg_inplace(out->bv); } break; @@ -272,21 +272,21 @@ RZ_IPI bool interpreter_prototype_eval_pure( RZ_LOG_ERROR("prototype: ADD x failed to evaluate.\n"); return false; } - if (!out->is_concrete) { - goto map_to_bottom; + if (!out->is_const) { + goto map_to_top; } STACK_ABSTR_DATA_OUT(y); if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: ADD y failed to evaluate.\n"); return false; } - if (!y.is_concrete) { + if (!y.is_const) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } if (!rz_bv_add_inplace(out->bv, y.bv, NULL)) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } rz_bv_fini(y.bv); break; @@ -298,21 +298,21 @@ RZ_IPI bool interpreter_prototype_eval_pure( RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); return false; } - if (!out->is_concrete) { - goto map_to_bottom; + if (!out->is_const) { + goto map_to_top; } STACK_ABSTR_DATA_OUT(y); if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: SUB y failed to evaluate.\n"); return false; } - if (!y.is_concrete) { + if (!y.is_const) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } if (!rz_bv_sub_inplace(out->bv, y.bv, NULL)) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } rz_bv_fini(y.bv); break; @@ -326,34 +326,34 @@ RZ_IPI bool interpreter_prototype_eval_pure( RZ_LOG_ERROR("prototype: SHIFT(L/R) x failed to evaluate.\n"); return false; } - if (!out->is_concrete) { - goto map_to_bottom; + if (!out->is_const) { + goto map_to_top; } STACK_ABSTR_DATA_OUT(y); if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) y failed to evaluate.\n"); return false; } - if (!y.is_concrete) { + if (!y.is_const) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } STACK_ABSTR_DATA_OUT(fill_bit); if (!interpreter_prototype_eval_pure(iset, pfill_bit, &fill_bit, plugin_data)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) fill_bit failed to evaluate.\n"); return false; } - if (!fill_bit.is_concrete) { + if (!fill_bit.is_const) { rz_bv_fini(fill_bit.bv); rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } bool (*shift)(RzBitVector *bv, ut32 size, bool fill_bit); shift = pure->code == RZ_IL_OP_SHIFTR ? rz_bv_rshift_fill : rz_bv_lshift_fill; if (!shift(out->bv, rz_bv_to_ut64(y.bv), abstr_is_true(iset, &fill_bit))) { rz_bv_fini(fill_bit.bv); rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } rz_bv_fini(fill_bit.bv); rz_bv_fini(y.bv); @@ -367,7 +367,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzILOpPure *py; switch (pure->code) { default: - goto map_to_bottom; + goto map_to_top; case RZ_IL_OP_SLE: px = pure->op.sle.x; py = pure->op.sle.y; @@ -389,17 +389,17 @@ RZ_IPI bool interpreter_prototype_eval_pure( RZ_LOG_ERROR("prototype: CMP x failed to evaluate.\n"); return false; } - if (!out->is_concrete) { - goto map_to_bottom; + if (!out->is_const) { + goto map_to_top; } STACK_ABSTR_DATA_OUT(y); if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: CMP y failed to evaluate.\n"); return false; } - if (!y.is_concrete) { + if (!y.is_const) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } bool cmp_is_true = cmp(out->bv, y.bv); rz_bv_cast_inplace(out->bv, 1, false); @@ -417,9 +417,9 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(ld_addr.bv); return false; } - if (!ld_addr.is_concrete) { + if (!ld_addr.is_const) { rz_bv_fini(ld_addr.bv); - goto map_to_bottom; + goto map_to_top; } if (rz_bv_len(ld_addr.bv) == 64) { // TODO: Remove normalization. @@ -435,7 +435,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( size_t n_bits = pure->code == RZ_IL_OP_LOAD ? iset->astate->il_config->mem_key_size : pure->op.loadw.n_bits; if (!load_abstr_data(iset, mem_idx, &ld_addr, n_bits, out)) { rz_bv_fini(ld_addr.bv); - goto map_to_bottom; + goto map_to_top; } rz_bv_fini(ld_addr.bv); break; @@ -447,21 +447,21 @@ RZ_IPI bool interpreter_prototype_eval_pure( RZ_LOG_ERROR("prototype: MUL x failed to evaluate.\n"); return false; } - if (!out->is_concrete) { - goto map_to_bottom; + if (!out->is_const) { + goto map_to_top; } STACK_ABSTR_DATA_OUT(y); if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: MUL y failed to evaluate.\n"); return false; } - if (!y.is_concrete) { + if (!y.is_const) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } if (!rz_bv_mul_inplace(out->bv, y.bv)) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } rz_bv_fini(y.bv); break; @@ -473,21 +473,21 @@ RZ_IPI bool interpreter_prototype_eval_pure( RZ_LOG_ERROR("prototype: MOD x failed to evaluate.\n"); return false; } - if (!out->is_concrete) { - goto map_to_bottom; + if (!out->is_const) { + goto map_to_top; } STACK_ABSTR_DATA_OUT(y); if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: MOD y failed to evaluate.\n"); return false; } - if (!y.is_concrete) { + if (!y.is_const) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } if (!rz_bv_mod_inplace(out->bv, y.bv)) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } rz_bv_fini(y.bv); break; @@ -499,21 +499,21 @@ RZ_IPI bool interpreter_prototype_eval_pure( RZ_LOG_ERROR("prototype: DIV x failed to evaluate.\n"); return false; } - if (!out->is_concrete) { - goto map_to_bottom; + if (!out->is_const) { + goto map_to_top; } STACK_ABSTR_DATA_OUT(y); if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: DIV y failed to evaluate.\n"); return false; } - if (!y.is_concrete) { + if (!y.is_const) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } if (!rz_bv_div_inplace(out->bv, y.bv)) { rz_bv_fini(y.bv); - goto map_to_bottom; + goto map_to_top; } rz_bv_fini(y.bv); break; @@ -556,11 +556,11 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_FEXCEPT: RZ_LOG_ERROR("Unhandled pure %" PFMT32d "\n", pure->code); // Not implemented. - goto map_to_bottom; + goto map_to_top; } return true; -map_to_bottom: - out->is_concrete = false; +map_to_top: + out->is_const = false; return true; } From 149894b7de1cb857d8130453e7adfa43780e5afa Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 13 Jun 2026 17:44:06 +0200 Subject: [PATCH 285/334] Fix underflow --- librz/inquiry/bcfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librz/inquiry/bcfg.c b/librz/inquiry/bcfg.c index 45f513fcd3d..3be87c9b42d 100644 --- a/librz/inquiry/bcfg.c +++ b/librz/inquiry/bcfg.c @@ -302,7 +302,7 @@ RZ_IPI bool rz_inquiry_bcfg_reduce(RzInquiryBCFG *cfg) { rz_vector_reserve(&outedges, 8); size_t n_blocks = rz_pvector_len(&blocks); - for (size_t i = 0; i < n_blocks - 1; ++i) { + for (size_t i = 0; n_blocks && i < n_blocks - 1; ++i) { RzInquiryBlock *a = rz_pvector_at(&blocks, i); // Split of all blocks a overlaps with. From 63ac2b396eef6ebd223102c6dbac8653165d4c11 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 13 Jun 2026 17:45:42 +0200 Subject: [PATCH 286/334] Fix comparison of ut64 (compared ptrs. --- librz/inquiry/algorithms/revng_fcn_detection.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/librz/inquiry/algorithms/revng_fcn_detection.c b/librz/inquiry/algorithms/revng_fcn_detection.c index 774a24c173a..8d811bc02f0 100644 --- a/librz/inquiry/algorithms/revng_fcn_detection.c +++ b/librz/inquiry/algorithms/revng_fcn_detection.c @@ -53,10 +53,10 @@ #include #include -static int cmp(const void *a, const void *b, void *user) { - if ((ut64)a > (ut64)b) { +static int cmp(const ut64 *a, const ut64 *b, void *user) { + if (*a > *b) { return 1; - } else if ((ut64)a < (ut64)b) { + } else if (*a < *b) { return -1; } return 0; @@ -206,7 +206,7 @@ static void fill_candidate_fcn_entry_points( rz_iterator_free(predecessor); } rz_iterator_free(iter); - rz_vector_sort(cfep_addresses, cmp, false, NULL); + rz_vector_sort(cfep_addresses, (RzVectorComparator)cmp, false, NULL); } RZ_API bool rz_inquiry_algo_revng_fcn_detection( From 222f65b01fe9e4755bbd301fdcc0c95ab47a01b3 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 13 Jun 2026 17:47:11 +0200 Subject: [PATCH 287/334] Remove duplicate fini() call --- librz/inquiry/interpreter/interpreter.c | 1 - 1 file changed, 1 deletion(-) diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 2fcbd30a09e..eb4f716e2a5 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -549,7 +549,6 @@ TERM: { RZ_FREE_CUSTOM(tmp_succ_addr, rz_vector_free); RZ_FREE_CUSTOM(succ_states, rz_vector_free); RZ_FREE_CUSTOM(reachable_states, rz_set_u_free); - iset->plugin->fini_state(iset->astate, iset->intrpr_priv); if (iset->plugin->fini && iset->intrpr_priv) { RZ_FREE_CUSTOM(iset->intrpr_priv, iset->plugin->fini); } From 85e677062fd37e007145974068ccc79c571e5992 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 13 Jun 2026 17:47:58 +0200 Subject: [PATCH 288/334] Fix NULL check --- librz/inquiry/interpreter/interpreter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index eb4f716e2a5..2a06d121991 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -376,7 +376,7 @@ static bool reset_intrpr_state( *tmp_succ_addr = rz_vector_new(sizeof(RzInterpCtrlFlow), NULL, NULL); *succ_states = rz_vector_new(sizeof(SuccessorState), NULL, NULL); *reachable_states = rz_set_u_new(); - if (!tmp_succ_addr || !succ_states || !reachable_states) { + if (!*tmp_succ_addr || !*succ_states || !*reachable_states) { rz_warn_if_reached(); return false; } From a993a206439b88bbabe48d92ca8b3e0df308eb38 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 13 Jun 2026 17:51:19 +0200 Subject: [PATCH 289/334] Update change_cf flag if instruction si unlifted. --- librz/inquiry/il_cache.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/librz/inquiry/il_cache.c b/librz/inquiry/il_cache.c index d1b791ffc97..265eb5da72b 100644 --- a/librz/inquiry/il_cache.c +++ b/librz/inquiry/il_cache.c @@ -173,6 +173,8 @@ RZ_API RZ_OWN RzILCacheBlock *rz_il_cache_lift_il_block(const RzILCache *cache, if (lifted) { changes_cf = rz_analysis_op_changes_control_flow(&op); + } else { + changes_cf = false; } if (changes_cf && op.jump != UT64_MAX && cache->static_xrefs) { From 083d3524603bf7ad2fe893f8c8bf0151b8e030eb Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 13 Jun 2026 17:53:42 +0200 Subject: [PATCH 290/334] Fix leaks --- librz/inquiry/interpreter/interpreter.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 2a06d121991..fe1eb768e12 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -110,7 +110,9 @@ RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_new( RZ_LOG_ERROR("Failed to add %s to the global variable map. " "DJB2 hash collision of the register name. DJB2 hash = 0x%" PFMT64x "\n", rname, djb2_reg_hash); - return NULL; + ht_up_free(state->globals); + ht_up_free(state->var_name_hashes); + free(state); } } state->locals = ht_up_new(NULL, free); From 3ce7ab4a7ee0e2c0f9344b8231795c08d8baa27b Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 13 Jun 2026 17:55:04 +0200 Subject: [PATCH 291/334] Fix tests --- test/db/inquiry/interpreter/unmapped_fcn_loop | 6 +-- test/db/inquiry/interpreter/xrefs | 52 +++++++++---------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/test/db/inquiry/interpreter/unmapped_fcn_loop b/test/db/inquiry/interpreter/unmapped_fcn_loop index 358458ff044..bbc206b0945 100644 --- a/test/db/inquiry/interpreter/unmapped_fcn_loop +++ b/test/db/inquiry/interpreter/unmapped_fcn_loop @@ -134,15 +134,15 @@ EXPECT=< RETURN -> 0x80000d9 sym.function_2+9 sym.function_1+4 0x80000c4 -> CALL -> 0x80000b0 sym.some_ptr sym.function_2+4 0x80000d4 -> CALL -> 0x80000b0 sym.some_ptr - addr size traced ninstr jump fail fcns calls xrefs ---------------------------------------------------------------------------------------- + addr size traced ninstr jump fail fcns calls xrefs +------------------------------------------------------------------------------------------------------------- 0x8000040 29 0 6 0x0800005d 0x08000040 0x800005d 6 0 2 0x0800008d 0x08000063 0x08000040 0x0800008b 0x8000063 14 0 4 0x08000071 0x08000040 0x080000a0 -0x8000071 28 0 9 0x0800005d 0x08000040 +0x8000071 28 0 9 0x0800005d 0x08000040 0x080000aa 0x800008d 9 0 4 0x08000040 0x8000096 10 0 1 0x080000a0 0x80000a0 9 0 3 0x080000a9 0x080000a0 0x080000b0 0x0800006a 0x80000a9 2 0 2 0x080000a0 0x080000bf 0x80000ab 5 0 1 -0x80000b0 16 0 5 0x080000b0 +0x80000b0 16 0 5 0x080000b0 0x080000a4 0x080000d4 0x080000c4 0x80000c0 9 0 3 0x080000c9 0x080000c0 0x080000b0 0x80000c9 2 0 2 0x080000c0 0x080000bf 0x80000cb 5 0 1 0x080000d0 @@ -215,18 +215,18 @@ EXPECT=< RETURN -> 0x8000080 reloc..data.8000078+8 sym.function_2+4 0x80000e0 -> CALL -> 0x80000c0 sym.some_ptr sym.function_2+8 0x80000e4 -> RETURN -> 0x8000080 reloc..data.8000078+8 - addr size traced ninstr jump fail fcns calls xrefs -------------------------------------------------------------------------------------------------------------- + addr size traced ninstr jump fail fcns calls xrefs +----------------------------------------------------------------------------------------------------------------------------------- 0x8000040 32 0 8 0x08000060 0x08000040 0x8000060 12 0 3 0x0800006c 0x080000ac 0x08000040 0x080000a8 0x800006c 4 0 1 0x08000070 0x08000040 0x8000070 16 0 4 0x08000080 0x08000040 0x080000b4 0x080000d0 0x080000dc -0x8000080 28 0 7 0x0800009c 0x08000040 +0x8000080 28 0 7 0x0800009c 0x08000040 0x080000e4 0x080000d8 0x080000bc 0x800009c 16 0 4 0x08000060 0x08000040 0x80000ac 8 0 2 0x08000040 0x08000068 0x80000b4 8 0 2 0x080000bc 0x080000b4 0x080000c0 0x0800007c 0x80000bc 4 0 1 0x080000b4 0x080000cc -0x80000c0 16 0 4 0x080000c0 +0x80000c0 16 0 4 0x080000c0 0x080000b8 0x080000e0 0x080000d4 0x80000d0 8 0 2 0x080000d8 0x080000d0 0x080000c0 0x0800007c 0x80000d8 4 0 1 0x080000d0 0x080000cc 0x80000dc 8 0 2 0x080000e4 0x080000dc 0x080000c0 0x0800007c @@ -353,23 +353,23 @@ EXPECT=< CALL -> 0x8000090 sym.function_2 reloc..data.80000c4+28 0x80000e0 -> CODE -> 0x8000114 reloc..data.80000c4+80 reloc..data.80000c4+80 0x8000114 -> CODE -> 0x80000c0 reloc..data - addr size traced ninstr jump fail fcns calls xrefs ---------------------------------------------------------------------------------------- -0x8000040 24 0 6 0x08000040 + addr size traced ninstr jump fail fcns calls xrefs +------------------------------------------------------------------------------------------------------------- +0x8000040 24 0 6 0x08000040 0x08000098 0x08000060 0x0800007c 0x8000058 12 0 3 0x08000064 0x08000058 0x08000040 0x080000dc 0x8000060 4 0 1 -0x8000064 16 0 4 0x08000058 +0x8000064 16 0 4 0x08000058 0x08000054 0x8000074 12 0 3 0x08000080 0x08000074 0x08000040 0x080000dc 0x800007c 4 0 1 -0x8000080 16 0 4 0x08000074 +0x8000080 16 0 4 0x08000074 0x08000054 0x8000090 12 0 3 0x0800009c 0x08000090 0x08000040 0x080000dc 0x8000098 4 0 1 -0x800009c 16 0 4 0x08000090 +0x800009c 16 0 4 0x08000090 0x08000054 0x80000ac 20 0 5 0x08000108 0x080000ac -0x80000c0 32 0 8 0x080000e0 0x080000ac +0x80000c0 32 0 8 0x080000e0 0x080000ac 0x08000114 0x80000dc 4 0 1 0x080000e0 -0x80000e0 40 0 10 0x08000108 0x080000ac -0x8000108 16 0 4 0x080000c0 0x08000118 0x080000ac 0x080000e0 +0x80000e0 40 0 10 0x08000108 0x080000ac 0x0800008c 0x080000a8 0x08000070 +0x8000108 16 0 4 0x080000c0 0x08000118 0x080000ac 0x080000e0 0x080000bc 0x8000114 4 0 1 0x080000e0 0x8000118 20 0 5 0x080000ac EOF @@ -517,19 +517,19 @@ EXPECT=< CALL -> 0x8000054 reloc.target.function_0 sym.main+48 0x80000e4 -> CALL -> 0x8000074 reloc.target.function_1 sym.main+100 0x8000118 -> CODE -> 0x80000d4 sym.main+32 - addr size traced ninstr jump fail fcns calls xrefs ---------------------------------------------------------------------------------------- -0x8000034 28 0 7 0x08000034 + addr size traced ninstr jump fail fcns calls xrefs +------------------------------------------------------------------------------------------------------------- +0x8000034 28 0 7 0x08000034 0x0800005c 0x0800009c 0x0800007c 0x8000050 4 0 1 0x08000054 0x0800003c 0x8000054 12 0 3 0x08000060 0x08000054 0x08000034 0x080000e4 -0x8000060 20 0 5 0x08000054 +0x8000060 20 0 5 0x08000054 0x0800004c 0x8000074 12 0 3 0x08000080 0x08000074 0x08000034 0x080000e4 -0x8000080 20 0 5 0x08000074 +0x8000080 20 0 5 0x08000074 0x0800004c 0x8000094 12 0 3 0x080000a0 0x08000094 0x08000034 -0x80000a0 20 0 5 0x08000094 +0x80000a0 20 0 5 0x08000094 0x0800004c 0x80000b4 32 0 8 0x08000110 0x080000b4 -0x80000d4 20 0 5 0x080000e8 0x080000b4 -0x80000e8 40 0 10 0x08000110 0x080000b4 +0x80000d4 20 0 5 0x080000e8 0x080000b4 0x08000118 +0x80000e8 40 0 10 0x08000110 0x080000b4 0x08000090 0x08000070 0x8000110 12 0 3 0x080000d4 0x0800011c 0x080000b4 0x080000d0 0x800011c 20 0 5 0x080000b4 0x8000134 1 0 1 0x080000dc @@ -623,7 +623,7 @@ EXPECT=< Date: Mon, 15 Jun 2026 11:47:41 +0200 Subject: [PATCH 292/334] Reformat --- librz/bin/p/bin_elf.inc | 6 +++--- librz/inquiry/interpreter/interpreter.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/librz/bin/p/bin_elf.inc b/librz/bin/p/bin_elf.inc index 99eb26f2c90..181070c5756 100644 --- a/librz/bin/p/bin_elf.inc +++ b/librz/bin/p/bin_elf.inc @@ -858,9 +858,9 @@ static RzPVector /**/ *sections_obj(ELFOBJ *obj, size_t psize) { ptr->layout.count = section->size / sizeof(Elf_(Addr)); } if (section->type == SHT_RELA || - section->type == SHT_DYNAMIC || - section->type == SHT_DYNSYM || - (section->name && strstr(section->name, ".plt"))) { + section->type == SHT_DYNAMIC || + section->type == SHT_DYNSYM || + (section->name && strstr(section->name, ".plt"))) { ptr->layout.role = RZ_BIN_SECTION_ROLE_LINKING; } ptr->size = section->type != SHT_NOBITS ? section->size : 0; diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index fe1eb768e12..ddfad81ea4c 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -432,10 +432,10 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpSet *iset) { return false; } -// TODO: It is probably better to make the following stuff while-loops. -// Because otherwise it doesn't make sense without the docs. -// But while debugging and developing, I keep it this way to separate clearly -// what the interpreter does in each state. + // TODO: It is probably better to make the following stuff while-loops. + // Because otherwise it doesn't make sense without the docs. + // But while debugging and developing, I keep it this way to separate clearly + // what the interpreter does in each state. INIT: { RZ_LOG_DEBUG("interpreter: Enter INIT\n"); From 3d4ee195fe935cb4173604c95b210afab95fc5b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Mon, 15 Jun 2026 11:44:51 +0200 Subject: [PATCH 293/334] Add clone and join operations --- librz/include/rz_inquiry/rz_interpreter.h | 29 ++++-- librz/inquiry/interpreter/interpreter.c | 47 +++++++++ .../interpreter/p/interpreter_prototype.c | 99 +++++++++++++++++-- 3 files changed, 161 insertions(+), 14 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 23768133ca9..61a91734fc2 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -177,6 +177,12 @@ typedef struct { bool (*init)(void **plugin_data); bool (*reset)(void *plugin_data); bool (*fini)(void *plugin_data); + + /** + * \brief Clones the given abstract value. + */ + RZ_OWN RzInterpAbstrVal *(*clone_val)(const RzInterpAbstrVal *val, void *plugin_data); + /** * \brief Initializes the abstract state. */ @@ -189,15 +195,16 @@ typedef struct { * \brief Closes the abstract state and frees all its abstract data and sets the pointers to NULL. */ bool (*fini_state)(RZ_BORROW RzInterpAbstrState *state, void *plugin_data); - /** - * \brief Clones the abstract state. - */ - RZ_OWN RzInterpAbstrState *(*clone_state)(const RzInterpAbstrState *state, void *plugin_data); /** * \brief Hashes the state. */ ut64 (*hash_state)(RZ_NONNULL const RzInterpAbstrState *state, void *plugin_data); + /** + * \brief Performs the join operation on states (least upper bound, lattice theory) + * \return True if a was changed + */ + bool (*join_state)(RZ_BORROW RZ_INOUT RzInterpAbstrState *a, RZ_BORROW RZ_IN const RzInterpAbstrState *b, void *plugin_data); /** * \brief Evaluates an effect with the mutable state. */ @@ -214,15 +221,24 @@ typedef struct { RZ_NONNULL RZ_OUT RzVector /**/ *successors, void *plugin_data); + /** + * \brief Builds a string for printing an abstract value. + * + * \return Returns false in case of error, True otherwise. + */ + bool (*val_as_str)(RZ_NONNULL const RzInterpAbstrVal *state, + RZ_NONNULL RZ_OUT RzStrBuf *str_buf, + void *plugin_data); + /** * \brief Builds a string for printing the current state. * - * \return Returns false in case of error. The interpretation must abort. - * True otherwise. + * \return Returns false in case of error, True otherwise. */ bool (*state_as_str)(RZ_NONNULL const RzInterpAbstrState *state, RZ_NONNULL RZ_OUT RzStrBuf *str_buf, void *plugin_data); + /** * \brief Set the abstract PC to the given address. */ @@ -314,6 +330,7 @@ RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_new( RZ_OWN RZ_NONNULL RzAnalysisILConfig *il_config, RZ_NULLABLE const RzILRegBinding *reg_bindings); RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpAbstrState *state); +RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_clone(RZ_NONNULL RzInterpSet *iset, const RzInterpAbstrState *state); RZ_API RZ_OWN RzInterpYieldRBuf *rz_interpreter_yield_rbuf_new(RzInterpYieldKind kind, RzInterpYieldFilter filter, diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index ddfad81ea4c..a4ad61a820d 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -146,6 +146,53 @@ RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpAbstrStat free(state); } +static HtUP *var_set_clone(const RzInterpSet *iset, HtUP *vars) { + HtUP *r = ht_up_new(NULL, free); + if (!r) { + return NULL; + } + RzIterator *it = ht_up_as_iter_keys(vars); + ut64 *key; + rz_iterator_foreach(it, key) { + RzInterpAbstrVal *val = iset->plugin->clone_val(ht_up_find(vars, *key, NULL), iset->intrpr_priv); + if (!val) { + continue; + } + ht_up_insert(r, *key, val); + } + return r; +} + +RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_clone(RZ_NONNULL RzInterpSet *iset, const RzInterpAbstrState *state) { + RzInterpAbstrState *r = RZ_NEW0(RzInterpAbstrState); + if (!state) { + return NULL; + } + r->arch_name = state->arch_name; + r->kinds = state->kinds; + r->pc = iset->plugin->clone_val(state->pc, iset->intrpr_priv); + if (!state->pc) { + free(r); + return NULL; + } + r->var_name_hashes = ht_up_new(NULL, free); + RzIterator *it = ht_up_as_iter_keys(state->var_name_hashes); + ut64 *key; + rz_iterator_foreach(it, key) { + char *n = strdup(ht_up_find(state->var_name_hashes, *key, NULL)); + if (!n) { + continue; + } + ht_up_insert(r->var_name_hashes, *key, n); + } + + r->globals = var_set_clone(iset, state->globals); + r->locals = var_set_clone(iset, state->locals); + r->lets = var_set_clone(iset, state->lets); + r->il_config = state->il_config; + return r; +} + RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpSet *iset) { if (!iset) { return; diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 39aca2abeb6..7ce606e92a2 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -14,6 +14,22 @@ #define MAX_INVOCATIONS_PER_BLOCK 3 +bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, + RZ_NONNULL RZ_OUT RzStrBuf *sb, + void *plugin_data); + +RZ_OWN RzInterpAbstrVal *clone_val(const RzInterpAbstrVal *val, void *plugin_data) { + RzInterpAbstrVal *r = RZ_NEW0(RzInterpAbstrVal); + if (!r) { + return NULL; + } + r->kind = val->kind; + r->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); + AD(r->abstr_data)->is_const = AD(val->abstr_data)->is_const; + AD(r->abstr_data)->bv = rz_bv_dup(AD(val->abstr_data)->bv); + return r; +} + static bool eval(RZ_NONNULL RzInterpSet *iset, RZ_NONNULL const RzILCacheBlock *il_bb, void *plugin_data) { @@ -45,6 +61,11 @@ static bool eval(RZ_NONNULL RzInterpSet *iset, ProtoIntrprAbstrData *apc = AD(iset->astate->pc->abstr_data); ut64 pc = rz_bv_to_ut64(apc->bv); RZ_LOG_DEBUG("prototype: Eval PC = 0x%" PFMT64x "\n", pc); + RzStrBuf sb; + rz_strbuf_init(&sb); + state_as_str(iset->astate, &sb, plugin_data); + RZ_LOG_DEBUG("%s\n", rz_strbuf_get(&sb)); + rz_strbuf_fini(&sb); RzILCacheInsnPkt *pkt = *it; if (!interpreter_prototype_eval_effect(iset, pkt->effect, pkt->insn_pkt_size, plugin_data)) { return false; @@ -222,6 +243,67 @@ static ut64 hash_state(RZ_NONNULL const RzInterpAbstrState *state, void *plugin_ return h; } +/** + * \brief Join (least upper bound) on values + * \return True if a was changed + */ +static bool join_val(RZ_BORROW RZ_INOUT RzInterpAbstrVal *a, RZ_BORROW RZ_IN const RzInterpAbstrVal *b) { + ProtoIntrprAbstrData *ad = AD(a->abstr_data); + ProtoIntrprAbstrData *bd = AD(b->abstr_data); + if (ad->is_const && bd->is_const && rz_bv_eq(ad->bv, bd->bv)) { + // identical values, a already has the least upper bound + return false; + } + // for anything else, the least upper bound is top + bool changed = ad->is_const; + ad->is_const = false; + return changed; +} + +/** + * \brief Join (least upper bound) on var sets + * \return True if a was changed + */ +static bool join_vars(RZ_BORROW RZ_INOUT HtUP *a, RZ_BORROW RZ_IN HtUP *b) { + RzIterator *it = ht_up_as_iter_keys(a); + ut64 *k; + bool changed = false; + rz_iterator_foreach(it, k) { + RzInterpAbstrVal *av = ht_up_find(a, *k, NULL); + RzInterpAbstrVal *bv = ht_up_find(b, *k, NULL); + if (!av || !bv) { + continue; + } + if (join_val(av, bv)) { + changed = true; + } + } + return changed; +} + +bool join_state(RZ_BORROW RZ_INOUT RzInterpAbstrState *a, RZ_BORROW RZ_IN const RzInterpAbstrState *b, void *plugin_data) { + bool global_change = join_vars(a->globals, b->globals); + bool local_change = join_vars(a->locals, b->locals); + // lets are not be relevant here since they are immutable within their scope + return global_change || local_change; +} + +bool val_as_str(RZ_NONNULL const RzInterpAbstrVal *val, RZ_NONNULL RZ_OUT RzStrBuf *sb, void *plugin_data) { + rz_return_val_if_fail(val && sb, false); + ProtoIntrprAbstrData *av = AD(val->abstr_data); + if (av->is_const) { + char *s = rz_bv_as_hex_string(av->bv, false); + if (!s) { + return false; + } + rz_strbuf_append(sb, s); + free(s); + } else { + rz_strbuf_append(sb, "⊤"); + } + return true; +} + bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, RZ_NONNULL RZ_OUT RzStrBuf *sb, void *plugin_data) { @@ -230,19 +312,18 @@ bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, ut64 hash = hash_state(state, plugin_data); rz_strbuf_appendf(sb, "hash = 0x%" PFMT64x "\n\n", hash); rz_strbuf_append(sb, "Globals\n\n"); - char *value = AD(state->pc->abstr_data)->is_const ? rz_bv_as_hex_string(AD(state->pc->abstr_data)->bv, true) : rz_str_dup("⊥"); - rz_strbuf_appendf(sb, "\tpc = %s\n\n", value); - free(value); + rz_strbuf_append(sb, "\tpc = "); + val_as_str(state->pc, sb, plugin_data); + rz_strbuf_append(sb, "\n\n"); RzIterator *it = ht_up_as_iter_keys(state->globals); ut64 *k; rz_iterator_foreach(it, k) { const char *gname = ht_up_find(state->var_name_hashes, *k, NULL); + rz_strbuf_appendf(sb, "\t%s = ", gname); RzInterpAbstrVal *av = ht_up_find(state->globals, *k, NULL); - ProtoIntrprAbstrData *ad = av->abstr_data; - value = ad->is_const ? rz_bv_as_hex_string(ad->bv, true) : rz_str_dup("⊥"); - rz_strbuf_appendf(sb, "\t%s = %s\n", gname, value); - free(value); + val_as_str(av, sb, plugin_data); + rz_strbuf_append(sb, "\n"); } rz_iterator_free(it); return true; @@ -303,15 +384,17 @@ static RzInterpPlugin rz_interpreter_plugin_prototype = { .init = init, .reset = reset, .fini = fini, + .clone_val = clone_val, .eval = eval, .successors = successors, .init_state = init_state, .reset_state = reset_state, .fini_state = fini_state, .hash_state = hash_state, + .join_state = join_state, .set_pc = set_pc, .state_as_str = state_as_str, - .clone_state = NULL, + .val_as_str = val_as_str }; RZ_API RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype = { From 37ce0545c6c5aac8936f0f0ce524aaf892ba8959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Mon, 15 Jun 2026 12:17:46 +0200 Subject: [PATCH 294/334] Abstract interpretation with fixpoint iteration --- librz/include/rz_inquiry/rz_interpreter.h | 28 ++- librz/inquiry/inquiry.c | 2 +- librz/inquiry/interpreter/interpreter.c | 225 ++++++++++++++++-- .../interpreter/p/interpreter_prototype.c | 99 +++++--- librz/inquiry/interpreter/prototype/eval.c | 43 +++- librz/inquiry/interpreter/prototype/eval.h | 3 + .../interpreter/prototype/eval_effect.c | 58 +++-- .../inquiry/interpreter/prototype/eval_pure.c | 2 +- 8 files changed, 369 insertions(+), 91 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 61a91734fc2..94f40bea73a 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -102,13 +102,22 @@ typedef struct { RzInquiryBCFGEdgeType type; } RzInterpCtrlFlow; +typedef enum { + RZ_INTERP_PC_CONST, ///< Single known value + RZ_INTERP_PC_UNREACHABLE, ///< Bottom/unreachable state, if this is set, pc field is unused and undefined + RZ_INTERP_PC_ANY ///< Top state, if this is set, pc field is unused and undefined +} RzInterpPCState; + typedef struct { + ut64 pc; ///< Interpreter location in the code. This is not necessarily identical to the ISA's program counter register, but simply points to the instruction to execute. + RzInterpPCState pc_state; + bool uninterpreted; ///< True if this state has not yet been started to interpret, i.e. is part of RzInterpFunctionState.queue + RzInterpAbstraction kinds; ///< The abstractions of the state. HtUP *var_name_hashes; ///< Map of DJB2 hashes to variable names. HtUP /**/ *globals; ///< Global variables (mostly registers). Indexed by DJB2 hash of global name. HtUP /**/ *locals; ///< Local variables. Indexed by DJB2 hash of the local name. HtUP /**/ *lets; ///< Let variables. Indexed by DJB2 hash of the let name. - RzInterpAbstrVal *pc; ///< In our RzIL implementation the PC is not part of the register file. RzAnalysisILConfig *il_config; ///< The IL configuration of the RzArch plugin. const char *arch_name; ///< Name of architecture. Used by work-arounds until we have RzArch. ut64 bb_addr; @@ -266,14 +275,23 @@ typedef struct { bool req_ok; ///< Set to true if IO request succeeded. } RzInterpIOResult; +typedef struct { + RzList /**/ *queue; ///< States that have to be interpreted still. If this is empty, a fixpoint has been reached. + HtUP /**/ *pc_states; ///< Currently discovered states at the entries of blocks. +} RzInterpFunctionState; + /** * \brief The set of required objects for an interpreter to run. */ RZ_LIFETIME(RzInquiry) struct rz_interpreter_set { + RzAnalysis *a; ///< TODO: remove + // TODO: Move this one into each plugin? RzInterpAbstrState *astate; ///< The abstract state of the interpreter. + RzInterpFunctionState fcn_state; + RzIntpRunState *run_state; ///< The state the interpreter is currently in. /** * \brief The semaphore to sync RzInquiry and the interpreter between the Clean and Init run state. @@ -346,6 +364,14 @@ RZ_API RZ_OWN RzInterpSet *rz_interpreter_set_new( RZ_NONNULL const RzVector /**/ *ignored_code); RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpSet *iset); +/* + * \brief Register a newly discovered state + * + * This will join the state with the already known one at the same pc and add it to the + * queue for further interpretation if there were changes. + */ +RZ_API void rz_interp_set_push(RZ_BORROW RZ_NONNULL RzInterpSet *iset, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as); + RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpSet *iset); #endif // RZ_INTERPRETER diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 8bdc4a35d34..7cfef4de7d1 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -507,7 +507,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, goto error_free; } - collect_entry_points(core, entry_points, symbol_targets); + // collect_entry_points(core, entry_points, symbol_targets); if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { eprintf("Total branch targets in binary: %" PFMT32d "\n", rz_set_u_size(entry_points)); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index a4ad61a820d..572b41722a4 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -16,6 +16,8 @@ #include #include +#include "inquiry/interpreter/prototype/eval.h" + RZ_API void rz_interpreter_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yield_rbufs) { if (!yield_rbufs) { return; @@ -77,7 +79,7 @@ RZ_API RZ_OWN RzInterpYieldRBuf *rz_interpreter_yield_rbuf_new(RzInterpYieldKind RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_new( const char *arch_name, RzInterpAbstraction kinds, - RZ_OWN RZ_NONNULL RzAnalysisILConfig *il_config, + RZ_BORROW RZ_NONNULL RzAnalysisILConfig *il_config, RZ_NULLABLE const RzILRegBinding *reg_bindings) { rz_return_val_if_fail(il_config && reg_bindings, NULL); RzInterpAbstrState *state = RZ_NEW0(RzInterpAbstrState); @@ -86,10 +88,6 @@ RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_new( } state->arch_name = arch_name; state->kinds = kinds; - state->pc = RZ_NEW0(RzInterpAbstrVal); - if (!state->pc) { - return NULL; - } // Initialize the register file with uninitialized abstract values. state->var_name_hashes = ht_up_new(NULL, free); state->globals = ht_up_new(NULL, free); @@ -137,12 +135,6 @@ RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpAbstrStat if (state->lets) { ht_up_free(state->lets); } - if (state->pc) { - free(state->pc); - } - if (state->il_config) { - rz_analysis_il_config_free(state->il_config); - } free(state); } @@ -170,11 +162,9 @@ RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_clone(RZ_NONNULL Rz } r->arch_name = state->arch_name; r->kinds = state->kinds; - r->pc = iset->plugin->clone_val(state->pc, iset->intrpr_priv); - if (!state->pc) { - free(r); - return NULL; - } + r->pc = state->pc; + r->pc_state = state->pc_state; + r->uninterpreted = state->uninterpreted; r->var_name_hashes = ht_up_new(NULL, free); RzIterator *it = ht_up_as_iter_keys(state->var_name_hashes); ut64 *key; @@ -315,6 +305,7 @@ RZ_API RZ_OWN RzInterpSet *rz_interpreter_set_new( return NULL; } + iset->a = analysis; iset->plugin = plugin; iset->astate = state; iset->run_state = rz_intp_run_state_new(); @@ -329,9 +320,53 @@ RZ_API RZ_OWN RzInterpSet *rz_interpreter_set_new( iset->io_result_rbuf = io_result_rbuf; iset->run_state_sync = rz_th_sem_new(0); iset->ignored_code = ignored_code; + + iset->fcn_state.pc_states = ht_up_new(NULL, free); + iset->fcn_state.queue = rz_list_new(); + return iset; } +RZ_API void rz_interp_set_push(RZ_BORROW RZ_NONNULL RzInterpSet *iset, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as) { + if (as->pc_state == RZ_INTERP_PC_ANY) { + RZ_LOG_DEBUG("Encountered state with unknown/top pc\n"); + return; + } + if (as->pc_state != RZ_INTERP_PC_CONST) { + rz_warn_if_reached(); + return; + } + RzStrBuf sb; + rz_strbuf_init(&sb); + state_as_str_short(iset, &sb, as); + RZ_LOG_DEBUG("PUSH 0x%" PFMT64x ": %s\n", as->pc, rz_strbuf_get(&sb)); + rz_strbuf_fini(&sb); + RzInterpAbstrState *existing = ht_up_find(iset->fcn_state.pc_states, as->pc, NULL); + if (existing) { + if (iset->plugin->join_state(existing, as, iset->intrpr_priv) && !existing->uninterpreted) { + existing->uninterpreted = true; + rz_list_push(iset->fcn_state.queue, existing); + } + } else { + RzInterpAbstrState *c = rz_interpreter_abstr_state_clone(iset, as); + if (!c) { + return; + } + ht_up_insert(iset->fcn_state.pc_states, as->pc, c); + c->uninterpreted = true; + rz_list_push(iset->fcn_state.queue, c); + } +} + +RZ_API RZ_NULLABLE RZ_BORROW RzInterpAbstrState *rz_interp_set_pop(RZ_BORROW RZ_NONNULL RzInterpSet *iset) { + RzInterpAbstrState *r = rz_list_pop(iset->fcn_state.queue); + if (!r) { + return NULL; + } + r->uninterpreted = false; + return r; +} + static bool jumps_to_ignored_code(const RzVector *v, ut64 jump_target) { void *it; rz_vector_foreach (v, it) { @@ -435,7 +470,7 @@ static bool reset_intrpr_state( /** * Main interpretation. */ -RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpSet *iset) { +RZ_API bool rz_interpreter_run_rot127(RZ_NONNULL RZ_OWN RzInterpSet *iset) { rz_return_val_if_fail(iset && iset->astate && iset->il_request_rbuf && @@ -604,3 +639,159 @@ TERM: { return success; } } + +/** + * Main interpretation. + */ +RZ_API bool rz_interpreter_run_thestr4ng3r(RZ_NONNULL RZ_OWN RzInterpSet *iset) { + rz_return_val_if_fail(iset && + iset->astate && + iset->il_request_rbuf && + iset->il_queue && + iset->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] && + iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] && + iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] && + iset->run_state_sync && + iset->plugin && + iset->plugin->eval && + iset->plugin->successors && + iset->plugin->init_state && + iset->plugin->fini_state && + iset->plugin->hash_state, + false); + + bool success = true; + + RZ_LOG_DEBUG("interpreter: Main: Hello.\n"); + RzInterpPlugin *plugin = iset->plugin; + + // + // Start interpretation + // + const RzILCacheBlock *il_bb = NULL; + + if (iset->plugin->init) { + iset->plugin->init(&iset->intrpr_priv); + } + if (!iset->plugin->init_state(iset->astate, iset->intrpr_priv)) { + rz_warn_if_reached(); + return false; + } + + // TODO: It is probably better to make the following stuff while-loops. + // Because otherwise it doesn't make sense without the docs. + // But while debugging and developing, I keep it this way to separate clearly + // what the interpreter does in each state. + +INIT: { + // prepare state at the procedure entrypoint + RZ_LOG_DEBUG("interpreter: Enter INIT\n"); + rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_INIT); + + const RzAnalysisPlugin *cur = rz_analysis_plugin_current(iset->a); + RzAnalysisILConfig *config = cur->il_config(iset->a); + RzInterpAbstrState *estate = rz_interpreter_abstr_state_new( + cur->arch, + RZ_INTERP_ABSTRACTION_CONST, + config, + iset->il_vm->reg_binding); + + rz_list_purge(iset->fcn_state.queue); + ht_up_clear(iset->fcn_state.pc_states); + + ut64 entry_point; + if (rz_th_ring_buf_take_blocking(iset->entry_points, &entry_point) != RZ_THREAD_RING_BUF_OK) { + // No more entry points to interpret => Terminate. + // OR. + success = true; + goto TERM; + } + + if (iset->plugin->reset) { + iset->plugin->reset(iset->intrpr_priv); + } + + if (!iset->plugin->init_state(estate, iset->intrpr_priv) || !iset->plugin->reset_state(estate, entry_point, iset->intrpr_priv)) { + rz_warn_if_reached(); + return false; + } + + rz_interp_set_push(iset, estate); + rz_interpreter_abstr_state_free(estate); +} + + while (true) { + RZ_LOG_DEBUG("interpreter: Enter EMU\n"); + rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_EMU); + + RzInterpAbstrState *next = rz_interp_set_pop(iset); + if (!next) { + // No uninterpreted states left, fixpoint reached. + success = true; + goto TERM; + } + iset->astate = rz_interpreter_abstr_state_clone(iset, next); + + if (rz_th_ring_buf_put(iset->il_request_rbuf, &iset->astate->pc) != RZ_THREAD_RING_BUF_OK) { + // Can't request IL block => Cache closed => Terminate + success = false; + goto TERM; + } + if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || + il_bb == RZ_IL_CACHE_FAILED_LIFTING_PTR || !il_bb) { + success = false; + goto TERM; + } + + RzStrBuf sb; + rz_strbuf_init(&sb); + const char *old_cmt = rz_meta_get_string(iset->a, RZ_META_TYPE_COMMENT, il_bb->addr); + if (old_cmt) { + rz_strbuf_appendf(&sb, "%s; ", old_cmt); + } + rz_strbuf_append(&sb, "ENTRY "); + state_as_str_short(iset, &sb, iset->astate); + // rz_meta_set_string(iset->a, RZ_META_TYPE_COMMENT, il_bb->addr, rz_strbuf_get(&sb)); + rz_strbuf_fini(&sb); + + iset->astate->bb_addr = il_bb->addr; + iset->astate->bb_size = il_bb->size; + // Evaluate the effect on the abstract state. + if (!plugin->eval(iset, il_bb, iset->intrpr_priv)) { + RZ_LOG_DEBUG("interpreter: Eval failed\n"); + goto CLEAN; + } + + // Set effect and state for next evaluation. + il_bb = NULL; + } + +CLEAN: { + RZ_LOG_DEBUG("interpreter: Enter CLEAN\n"); + rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_CLEAN); + + // Wait until RzInquiry asks to start again. + rz_th_sem_wait(iset->run_state_sync); + + // Clean can only transition to Init. + goto INIT; +} + +TERM: { + RZ_LOG_DEBUG("interpreter: Enter TERM\n"); + rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_TERM); + iset->plugin->fini_state(iset->astate, iset->intrpr_priv); + if (iset->plugin->fini && iset->intrpr_priv) { + RZ_FREE_CUSTOM(iset->intrpr_priv, iset->plugin->fini); + } + + if (iset->plugin->fini && iset->intrpr_priv) { + RZ_FREE_CUSTOM(iset->intrpr_priv, iset->plugin->fini); + } + return success; +} +} + +RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpSet *iset) { + return rz_interpreter_run_thestr4ng3r(iset); +} diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 7ce606e92a2..66a1120a95e 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -56,25 +56,42 @@ static bool eval(RZ_NONNULL RzInterpSet *iset, memset(&pdata->call_cand, 0, sizeof(pdata->call_cand)); // Now execute the actual effects of the BLOCK. + RzInterpAbstrState *astate = iset->astate; void **it; rz_pvector_foreach (il_bb->il_ops, it) { - ProtoIntrprAbstrData *apc = AD(iset->astate->pc->abstr_data); - ut64 pc = rz_bv_to_ut64(apc->bv); + ut64 pc = astate->pc; RZ_LOG_DEBUG("prototype: Eval PC = 0x%" PFMT64x "\n", pc); RzStrBuf sb; rz_strbuf_init(&sb); state_as_str(iset->astate, &sb, plugin_data); RZ_LOG_DEBUG("%s\n", rz_strbuf_get(&sb)); rz_strbuf_fini(&sb); + + rz_strbuf_init(&sb); + state_as_str_short(iset, &sb, iset->astate); + rz_meta_set_string(iset->a, RZ_META_TYPE_COMMENT, pc, rz_strbuf_get(&sb)); + rz_strbuf_fini(&sb); + RzILCacheInsnPkt *pkt = *it; + + // Prepare next pc, the evalutation may overwrite this. + ut64 next_pc = pc + pkt->insn_pkt_size; + set_pc(iset->astate, next_pc, plugin_data); + if (!interpreter_prototype_eval_effect(iset, pkt->effect, pkt->insn_pkt_size, plugin_data)) { return false; } - if (pc == rz_bv_to_ut64(apc->bv) && apc->is_const) { - // Instruction did not manipulate the PC. Set it to the next instruction (packet). - set_pc(iset->astate, pc + pkt->insn_pkt_size, plugin_data); + if (astate->pc_state != RZ_INTERP_PC_CONST || astate->pc != next_pc) { + // Unreachable or a jump happened somewhere other than fallthrough, so we can't continue + // interpreting the block linearly, but have to push the new location + break; } } + + if (astate->pc_state != RZ_INTERP_PC_UNREACHABLE) { + rz_interp_set_push(iset, iset->astate); + } + return true; } @@ -83,18 +100,13 @@ bool successors(RZ_NONNULL const RzInterpAbstrState *state, void *plugin_data) { rz_return_val_if_fail(state && successors, false); ProtoIntrprPluginData *pdata = plugin_data; - ProtoIntrprAbstrData *apc = state->pc->abstr_data; - if (!apc->is_const) { + if (state->pc_state != RZ_INTERP_PC_CONST) { // The PC is not a concrete value. // This prototype can't estimate a reasonable concretization for it. return true; } - if (rz_bv_len(apc->bv) > 64) { - RZ_LOG_WARN("PC has a length of more than 64 bits!\n"); - return true; - } - ut64 next_pc = rz_bv_to_ut64(apc->bv); + ut64 next_pc = state->pc; RzInterpCtrlFlow branch = { 0 }; branch.target_addr = branch.actual_target = next_pc; branch.src_block_addr = pdata->prev_pc; @@ -103,10 +115,8 @@ bool successors(RZ_NONNULL const RzInterpAbstrState *state, } static bool init_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { - state->pc->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); - ProtoIntrprAbstrData *apc = AD(state->pc->abstr_data); - apc->bv = rz_bv_new_from_ut64(state->il_config->mem_key_size, 0); - apc->is_const = true; + state->pc = 0; + state->pc_state = RZ_INTERP_PC_UNREACHABLE; RzIterator *it = ht_up_as_iter_keys(state->globals); ut64 *k; @@ -123,8 +133,8 @@ static bool init_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { AD(av->abstr_data)->bv = rz_bv_new(state->il_config->mem_key_size); // TODO: This is debatable. It depends on the ABI what the default values are. // Some values must be concrete, otherwise the interpretation of the prototype end too early. - AD(av->abstr_data)->is_const = true; - if (state->il_config->init_state) { + AD(av->abstr_data)->is_const = false; + /*if (state->il_config->init_state) { RzAnalysisILInitStateVar *il_var; rz_vector_foreach (&state->il_config->init_state->vars, il_var) { if (rz_str_djb2_hash(il_var->name) != djb2_reg_name) { @@ -135,16 +145,15 @@ static bool init_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { rz_bv_copy(AD(av->abstr_data)->bv, default_val); rz_bv_free(default_val); } - } + }*/ } rz_iterator_free(it); return true; } static bool reset_state(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point, void *plugin_data) { - ProtoIntrprAbstrData *apc = AD(state->pc->abstr_data); - rz_bv_set_from_ut64(apc->bv, entry_point); - apc->is_const = true; + state->pc_state = RZ_INTERP_PC_CONST; + state->pc = entry_point; RzIterator *it = ht_up_as_iter_keys(state->globals); ut64 *k; @@ -152,8 +161,8 @@ static bool reset_state(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point, v ut64 djb2_reg_name = *k; RzInterpAbstrVal *av = ht_up_find(state->globals, djb2_reg_name, NULL); rz_bv_set_from_ut64(AD(av->abstr_data)->bv, 0); - AD(av->abstr_data)->is_const = true; - if (state->il_config->init_state) { + AD(av->abstr_data)->is_const = false; + /*if (state->il_config->init_state) { RzAnalysisILInitStateVar *il_var; rz_vector_foreach (&state->il_config->init_state->vars, il_var) { if (rz_str_djb2_hash(il_var->name) != djb2_reg_name) { @@ -164,7 +173,7 @@ static bool reset_state(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point, v rz_bv_copy(AD(av->abstr_data)->bv, default_val); rz_bv_free(default_val); } - } + }*/ } rz_iterator_free(it); state->bb_addr = 0; @@ -173,13 +182,6 @@ static bool reset_state(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point, v } static bool fini_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { - ProtoIntrprAbstrData *ad = state->pc->abstr_data; - if (ad && ad->bv) { - rz_bv_free(ad->bv); - } - free(ad); - state->pc->abstr_data = NULL; - RzIterator *it = ht_up_as_iter(state->globals); RzInterpAbstrVal **v; rz_iterator_foreach(it, v) { @@ -226,9 +228,9 @@ static bool fini_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { */ static ut64 hash_state(RZ_NONNULL const RzInterpAbstrState *state, void *plugin_data) { ut64 h = 5381; - ProtoIntrprAbstrData *ad = state->pc->abstr_data; - if (ad->bv) { - h = (h ^ (h << 5)) ^ rz_bv_to_ut64(ad->bv); + h = (h ^ (h << 5)) ^ (ut64)state->pc_state; + if (state->pc_state == RZ_INTERP_PC_CONST) { + h = (h ^ (h << 5)) ^ state->pc; } RzIterator *it = ht_up_as_iter(state->globals); RzInterpAbstrVal **v; @@ -313,7 +315,11 @@ bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, rz_strbuf_appendf(sb, "hash = 0x%" PFMT64x "\n\n", hash); rz_strbuf_append(sb, "Globals\n\n"); rz_strbuf_append(sb, "\tpc = "); - val_as_str(state->pc, sb, plugin_data); + if (state->pc_state == RZ_INTERP_PC_CONST) { + rz_strbuf_appendf(sb, "0x%" PFMT64x, state->pc); + } else { + rz_strbuf_append(sb, state->pc_state == RZ_INTERP_PC_ANY ? "⊤" : "⊥"); + } rz_strbuf_append(sb, "\n\n"); RzIterator *it = ht_up_as_iter_keys(state->globals); @@ -329,6 +335,27 @@ bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, return true; } +void state_as_str_short(RzInterpSet *iset, RZ_OUT RzStrBuf *out, RzInterpAbstrState *astate) { + bool first = true; + RzIterator *it = ht_up_as_iter_keys(astate->globals); + ut64 *k; + rz_iterator_foreach(it, k) { + ut64 djb2_reg_name = *k; + RzInterpAbstrVal *av = ht_up_find(astate->globals, djb2_reg_name, NULL); + ProtoIntrprAbstrData *val = av->abstr_data; + if (!val->is_const) { + continue; + } + if (!first) { + rz_strbuf_append(out, ", "); + } + first = false; + const char *varname = ht_up_find(astate->var_name_hashes, djb2_reg_name, NULL); + rz_strbuf_appendf(out, "%s = ", varname); + iset->plugin->val_as_str(av, out, iset->intrpr_priv); + } +} + bool init(void **plugin_data) { ProtoIntrprPluginData *pdata = RZ_NEW0(ProtoIntrprPluginData); if (!pdata) { diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 21b9e840ded..dd4ef34ee4a 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -152,6 +152,20 @@ bool abstr_is_true(const RzInterpSet *iset, const ProtoIntrprAbstrData *data) { return !rz_bv_is_zero_vector(data->bv); } +bool abstr_may_be_true(const RzInterpSet *iset, const ProtoIntrprAbstrData *data) { + if (!data->is_const) { + return true; + } + return !rz_bv_is_zero_vector(data->bv); +} + +bool abstr_may_be_false(const RzInterpSet *iset, const ProtoIntrprAbstrData *data) { + if (!data->is_const) { + return true; + } + return rz_bv_is_zero_vector(data->bv); +} + bool store_abstr_data( RzInterpSet *iset, RzILMemIndex mem_idx, @@ -224,15 +238,19 @@ bool set_abstr_pc(RzInterpAbstrState *state, ProtoIntrprAbstrData *pc, void *plugin_data) { rz_return_val_if_fail(state && pc, false); ProtoIntrprPluginData *pdata = plugin_data; - ProtoIntrprAbstrData *apc = AD(state->pc->abstr_data); - if (!apc->is_const || rz_bv_len(apc->bv) > 64) { + if (state->pc_state == RZ_INTERP_PC_CONST) { + pdata->prev_pc = state->pc; + } else { pdata->prev_pc = UT64_MAX; + } + if (pc->is_const) { + state->pc_state = RZ_INTERP_PC_CONST; + state->pc = rz_bv_to_ut64(pc->bv); } else { - pdata->prev_pc = rz_bv_to_ut64(apc->bv); + state->pc_state = RZ_INTERP_PC_ANY; } - copy_abstr_data(state->pc->abstr_data, pc); RZ_LOG_DEBUG("prototype: set_abstr_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", - pdata->prev_pc, rz_bv_to_ut64(apc->bv), apc->is_const ? "Constant" : "Top"); + pdata->prev_pc, state->pc, state->pc_state == RZ_INTERP_PC_CONST ? "Constant" : "Top"); return true; } @@ -240,17 +258,16 @@ bool set_pc(RzInterpAbstrState *state, ut64 pc, void *plugin_data) { rz_return_val_if_fail(state, false); ProtoIntrprPluginData *pdata = plugin_data; - ProtoIntrprAbstrData *apc = AD(state->pc->abstr_data); - if (!apc->is_const || rz_bv_len(apc->bv) > 64) { - pdata->prev_pc = UT64_MAX; + if (state->pc_state == RZ_INTERP_PC_CONST) { + pdata->prev_pc = state->pc; } else { - pdata->prev_pc = rz_bv_to_ut64(apc->bv); + pdata->prev_pc = UT64_MAX; } - - apc->is_const = true; + state->pc = pc; + state->pc_state = RZ_INTERP_PC_CONST; RZ_LOG_DEBUG("prototype: set_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (Constant)\n", - rz_bv_to_ut64(apc->bv), pc); - return rz_bv_set_from_ut64(apc->bv, pc); + pdata->prev_pc, pc); + return true; } void stack_frame_fini(ProtoInterprAbstrStackFrame *frame, void *unused) { diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 01061337cec..406eb652f19 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -100,6 +100,8 @@ void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) void write_var_to_state(RzInterpSet *iset, RzILVarKind kind, ut64 var_id, const ProtoIntrprAbstrData *data); bool read_var_from_state(RzInterpSet *iset, RzILVarKind kind, ut64 var_id, RZ_OUT ProtoIntrprAbstrData *data); bool abstr_is_true(const RzInterpSet *iset, const ProtoIntrprAbstrData *data); +bool abstr_may_be_true(const RzInterpSet *iset, const ProtoIntrprAbstrData *data); +bool abstr_may_be_false(const RzInterpSet *iset, const ProtoIntrprAbstrData *data); bool store_abstr_data( RzInterpSet *iset, RzILMemIndex mem_idx, @@ -143,5 +145,6 @@ void stack_frame_fini(ProtoInterprAbstrStackFrame *frame, void *unused); void stack_frame_push(ProtoIntrprPluginData *pdata, RzBitVector *entry_point, RzBitVector *return_addr, ut64 instance); void stack_frame_pop(ProtoIntrprPluginData *pdata, RZ_NULLABLE ProtoInterprAbstrStackFrame *frame); bool stack_frame_top_ret_addr_cmp(ProtoIntrprPluginData *pdata, RzBitVector *addr); +void state_as_str_short(RzInterpSet *iset, RZ_OUT RzStrBuf *out, RzInterpAbstrState *astate); #endif // PROTOYPE_EVAL_H diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 596c1c81fe3..c81f0073bc4 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -12,7 +12,7 @@ static bool value_indicates_ret_addr_write(RzInterpSet *iset, ProtoIntrprAbstrDa (rz_bv_to_ut64(val->bv) == iset->astate->bb_addr + iset->astate->bb_size || // Sparc stores the call instruction PC into o8. // The return instruction jumps then to o7+8. - (rz_str_startswith(iset->astate->arch_name, "sparc") && rz_bv_to_ut64(val->bv) == rz_bv_to_ut64(AD(iset->astate->pc->abstr_data)->bv))); + (rz_str_startswith(iset->astate->arch_name, "sparc") && rz_bv_to_ut64(val->bv) == iset->astate->pc)); } RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, @@ -20,18 +20,15 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, size_t insn_pkt_size, ProtoIntrprPluginData *plugin_data) { STACK_ABSTR_DATA_OUT(eval_out); - ProtoIntrprAbstrData *pc = AD(iset->astate->pc->abstr_data); + rz_return_val_if_fail(iset->astate->pc_state == RZ_INTERP_PC_CONST, false); + ut64 pc = iset->astate->pc; switch (effect->code) { default: case RZ_IL_OP_EMPTY: break; case RZ_IL_OP_NOP: { - if (!pc->is_const) { - // The PC is no longer a concrete value. - // This plugin has no addition for it defined. - break; - } +#if 0 STACK_ABSTR_DATA_OUT(npc); // First cast the bitvector, then set it. // This is performance critical. Since the stack allocated bv is >64 bit @@ -43,6 +40,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, goto error; } set_abstr_pc(iset->astate, &npc, plugin_data); +#endif break; } case RZ_IL_OP_SEQ: { @@ -63,7 +61,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, write_var_to_state(iset, kind, vhash, &eval_out); if (value_indicates_ret_addr_write(iset, &eval_out) && kind == RZ_IL_VAR_KIND_GLOBAL) { - plugin_data->call_cand.store_addr = rz_bv_to_ut64(pc->bv); + plugin_data->call_cand.store_addr = pc; plugin_data->call_cand.npc = iset->astate->bb_addr + iset->astate->bb_size; plugin_data->call_cand.bb_addr = iset->astate->bb_addr; plugin_data->call_cand.in_mem = false; @@ -75,11 +73,11 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, goto error; } if (!eval_out.is_const) { - RZ_LOG_DEBUG("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", rz_bv_to_ut64(pc->bv)); + RZ_LOG_DEBUG("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", pc); } ut64 target = rz_bv_to_ut64(eval_out.bv); RZ_LOG_DEBUG("prototype: JMP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", - rz_bv_to_ut64(pc->bv), target, + pc, target, eval_out.is_const ? "Concrete" : "Abstract"); if (eval_out.is_const) { @@ -88,7 +86,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, if (plugin_data->call_cand.store_addr) { // An instruction in this basic block stored the next PC. // Report a call candidate and assume this jump is a call. - plugin_data->call_cand.candidate_addr = rz_bv_to_ut64(pc->bv); + plugin_data->call_cand.candidate_addr = pc; plugin_data->call_cand.target = target; report_yield_call_candiate(iset, plugin_data); @@ -109,7 +107,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, xref_type = RZ_ANALYSIS_XREF_TYPE_RETURN; } - report_yield_xref(iset, insn_pkt_size, rz_bv_to_ut64(pc->bv), &eval_out, + report_yield_xref(iset, insn_pkt_size, pc, &eval_out, xref_type); // Clear the call candidate tracking variable. @@ -125,17 +123,33 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, if (!interpreter_prototype_eval_pure(iset, effect->op.branch.condition, &eval_out, plugin_data)) { goto error; } - if (!eval_out.is_const) { - // Bottom values means we can't make a - // decision (in this prototype implementation). - break; - } - - if (abstr_is_true(iset, &eval_out)) { + bool may_be_true = abstr_may_be_true(iset, &eval_out); + bool may_be_false = abstr_may_be_true(iset, &eval_out); + if (may_be_true && may_be_false) { + RzInterpAbstrState *true_state = rz_interpreter_abstr_state_clone(iset, iset->astate); + RzInterpAbstrState *false_state = iset->astate; + iset->astate = true_state; + if (!interpreter_prototype_eval_effect(iset, effect->op.branch.true_eff, insn_pkt_size, plugin_data)) { + goto error; + } + iset->astate = false_state; + if (!interpreter_prototype_eval_effect(iset, effect->op.branch.false_eff, insn_pkt_size, plugin_data)) { + goto error; + } + if (true_state->pc_state == false_state->pc_state && true_state->pc == false_state->pc) { + // identical target location, simply join the data and continue + iset->plugin->join_state(true_state, false_state, iset->intrpr_priv); + } else { + // different jump targets, branch rather than resorting to top pc + rz_interp_set_push(iset, true_state); + // true_state is already in iset->astate and will be continued automatically + } + rz_interpreter_abstr_state_free(true_state); + } else if (may_be_true) { if (!interpreter_prototype_eval_effect(iset, effect->op.branch.true_eff, insn_pkt_size, plugin_data)) { goto error; } - } else { + } else if (may_be_false) { if (!interpreter_prototype_eval_effect(iset, effect->op.branch.false_eff, insn_pkt_size, plugin_data)) { goto error; } @@ -177,12 +191,12 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, break; } if (value_indicates_ret_addr_write(iset, &eval_out)) { - plugin_data->call_cand.store_addr = rz_bv_to_ut64(pc->bv); + plugin_data->call_cand.store_addr = pc; plugin_data->call_cand.npc = iset->astate->bb_addr + iset->astate->bb_size; plugin_data->call_cand.bb_addr = iset->astate->bb_addr; plugin_data->call_cand.in_mem = true; } - report_yield_xref(iset, insn_pkt_size, rz_bv_to_ut64(pc->bv), &st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); + report_yield_xref(iset, insn_pkt_size, pc, &st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); if (!store_abstr_data(iset, mem_idx, &st_addr, &eval_out)) { rz_bv_fini(st_addr.bv); goto error; diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index d364692e898..ae310ed94ff 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -431,7 +431,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_and_inplace(ld_addr.bv, &mask); } - report_yield_xref(iset, 0, rz_bv_to_ut64(AD(iset->astate->pc->abstr_data)->bv), &ld_addr, RZ_ANALYSIS_XREF_TYPE_MEM_READ); + report_yield_xref(iset, 0, iset->astate->pc, &ld_addr, RZ_ANALYSIS_XREF_TYPE_MEM_READ); size_t n_bits = pure->code == RZ_IL_OP_LOAD ? iset->astate->il_config->mem_key_size : pure->op.loadw.n_bits; if (!load_abstr_data(iset, mem_idx, &ld_addr, n_bits, out)) { rz_bv_fini(ld_addr.bv); From f812ebc9422495f3e38f82db55cd7804dbedd131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Tue, 23 Jun 2026 13:29:08 +0200 Subject: [PATCH 295/334] Add comments for block entry/exit --- librz/inquiry/interpreter/p/interpreter_prototype.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 66a1120a95e..8fd16d68e11 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -68,6 +68,12 @@ static bool eval(RZ_NONNULL RzInterpSet *iset, rz_strbuf_fini(&sb); rz_strbuf_init(&sb); + if (pc == il_bb->addr) { + rz_strbuf_append(&sb, "ENTRY "); + } + if (rz_vector_index_ptr(&il_bb->il_ops->v, rz_pvector_len(il_bb->il_ops) - 1) == it) { + rz_strbuf_append(&sb, "EXIT "); + } state_as_str_short(iset, &sb, iset->astate); rz_meta_set_string(iset->a, RZ_META_TYPE_COMMENT, pc, rz_strbuf_get(&sb)); rz_strbuf_fini(&sb); From 38d2633ed43d20c3937a1dd0af8f009df72ef260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Tue, 23 Jun 2026 13:29:44 +0200 Subject: [PATCH 296/334] Fix handling calls --- librz/inquiry/interpreter/prototype/eval_effect.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index c81f0073bc4..b28f3a18e24 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -76,6 +76,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, RZ_LOG_DEBUG("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", pc); } ut64 target = rz_bv_to_ut64(eval_out.bv); + bool is_call = plugin_data->call_cand.store_addr; RZ_LOG_DEBUG("prototype: JMP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", pc, target, eval_out.is_const ? "Concrete" : "Abstract"); @@ -83,7 +84,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, if (eval_out.is_const) { RzAnalysisXRefType xref_type = RZ_ANALYSIS_XREF_TYPE_CODE; - if (plugin_data->call_cand.store_addr) { + if (is_call) { // An instruction in this basic block stored the next PC. // Report a call candidate and assume this jump is a call. plugin_data->call_cand.candidate_addr = pc; @@ -114,9 +115,12 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, memset(&plugin_data->call_cand, 0, sizeof(plugin_data->call_cand)); } - // Setting the PC to a top value is allowed here! - // The successor function will handle this case. - set_abstr_pc(iset->astate, &eval_out, plugin_data); + if (is_call) { + // For calls, assume control flow will continue like fallthrough. + // TODO: set data to top that may be changed by the call + } else { + set_abstr_pc(iset->astate, &eval_out, plugin_data); + } break; } case RZ_IL_OP_BRANCH: { From 87e4b4f92e6bec7841feeb7cfa0c0ef7bc0037c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Thu, 25 Jun 2026 13:45:33 +0200 Subject: [PATCH 297/334] Unify all interpreter abbrevs to interp --- librz/include/rz_inquiry/rz_interpreter.h | 32 ++++----- librz/inquiry/inquiry.c | 38 +++++----- librz/inquiry/interpreter/interpreter.c | 72 +++++++++---------- .../interpreter/p/interpreter_prototype.c | 2 +- .../interpreter/prototype/eval_effect.c | 2 +- librz/inquiry/interpreter/state.c | 36 +++++----- 6 files changed, 91 insertions(+), 91 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 94f40bea73a..bfb68de201f 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -24,19 +24,19 @@ #define RZ_INTERP_YIELD_RBUF_SIZE 128 #define RZ_INTERP_ENTRY_POINTS_RBUF_SIZE 4 -typedef struct rz_intp_run_state RzIntpRunState; +typedef struct rz_interp_run_state RzInterpRunState; -typedef enum rz_intp_state_flag { +typedef enum rz_interp_state_flag { /** * \brief Interpreter is still outside of its defined loop. * E.g. shortly after its thread was spawned. */ - RZ_INTP_RUN_STATE_OUT_OF_LOOP, - RZ_INTP_RUN_STATE_INIT, ///< Initialization state. - RZ_INTP_RUN_STATE_EMU, ///< Emulation state. - RZ_INTP_RUN_STATE_CLEAN, ///< Cleaning state. - RZ_INTP_RUN_STATE_TERM, ///< Termination state. -} RzIntpRunStateFlag; + RZ_INTERP_RUN_STATE_OUT_OF_LOOP, + RZ_INTERP_RUN_STATE_INIT, ///< Initialization state. + RZ_INTERP_RUN_STATE_EMU, ///< Emulation state. + RZ_INTERP_RUN_STATE_CLEAN, ///< Cleaning state. + RZ_INTERP_RUN_STATE_TERM, ///< Termination state. +} RzInterpRunStateFlag; /** * \brief The abstractions this module supports. @@ -292,7 +292,7 @@ struct rz_interpreter_set { RzInterpFunctionState fcn_state; - RzIntpRunState *run_state; ///< The state the interpreter is currently in. + RzInterpRunState *run_state; ///< The state the interpreter is currently in. /** * \brief The semaphore to sync RzInquiry and the interpreter between the Clean and Init run state. */ @@ -329,16 +329,16 @@ struct rz_interpreter_set { /** * \brief The private data of a single interpreter thread. */ - RZ_BORROW void *intrpr_priv; + RZ_BORROW void *interp_priv; }; -RZ_API RZ_OWN RzIntpRunState *rz_intp_run_state_new(); -RZ_API void rz_intp_run_state_free(RZ_OWN RZ_NULLABLE RzIntpRunState *state); -RZ_API RzIntpRunStateFlag rz_intp_run_state_get(RZ_BORROW RZ_NONNULL RzIntpRunState *state); -RZ_API RzIntpRunStateFlag rz_intp_run_state_get_unsafe(const RZ_NONNULL RzIntpRunState *state); -RZ_API const char *rz_intp_run_state_flag_str(RzIntpRunStateFlag flag); +RZ_API RZ_OWN RzInterpRunState *rz_interp_run_state_new(); +RZ_API void rz_interp_run_state_free(RZ_OWN RZ_NULLABLE RzInterpRunState *state); +RZ_API RzInterpRunStateFlag rz_interp_run_state_get(RZ_BORROW RZ_NONNULL RzInterpRunState *state); +RZ_API RzInterpRunStateFlag rz_interp_run_state_get_unsafe(const RZ_NONNULL RzInterpRunState *state); +RZ_API const char *rz_interp_run_state_flag_str(RzInterpRunStateFlag flag); -RZ_IPI void rz_intp_run_state_set(RZ_BORROW RZ_NONNULL RzIntpRunState *state, RzIntpRunStateFlag flag); +RZ_IPI void rz_interp_run_state_set(RZ_BORROW RZ_NONNULL RzInterpRunState *state, RzInterpRunStateFlag flag); RZ_API void rz_interpreter_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yield_rbuf); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 7cfef4de7d1..5aaadff1e87 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -476,7 +476,7 @@ static bool setup_yield_rbufs( struct ituple { RzThread *ithread; RzInterpSet *iset; - RzIntpRunStateFlag next_run_state; + RzInterpRunStateFlag next_run_state; }; /** @@ -574,7 +574,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RzThread *interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, intp_iset); iset_map[i].ithread = interpr_th; iset_map[i].iset = intp_iset; - iset_map[i].next_run_state = RZ_INTP_RUN_STATE_INIT; + iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_INIT; } // @@ -595,13 +595,13 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, break; } RzInterpSet *iset = iset_map[i].iset; - RzIntpRunStateFlag expected_rs = iset_map[i].next_run_state; + RzInterpRunStateFlag expected_rs = iset_map[i].next_run_state; - switch (rz_intp_run_state_get_unsafe(iset->run_state)) { - case RZ_INTP_RUN_STATE_OUT_OF_LOOP: + switch (rz_interp_run_state_get_unsafe(iset->run_state)) { + case RZ_INTERP_RUN_STATE_OUT_OF_LOOP: break; - case RZ_INTP_RUN_STATE_INIT: { - if (expected_rs != RZ_INTP_RUN_STATE_INIT) { + case RZ_INTERP_RUN_STATE_INIT: { + if (expected_rs != RZ_INTERP_RUN_STATE_INIT) { break; } // This interpreter is waiting for the next emulation task. @@ -612,7 +612,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // None left. // TODO Remove? rz_th_queue_close(iset->il_queue); - iset_map[i].next_run_state = RZ_INTP_RUN_STATE_TERM; + iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; intpr_terminated++; // RZ_LOG_DEBUG("Next: TERM\n"); continue; @@ -622,7 +622,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, case RZ_THREAD_RING_BUF_OK: // Successfully lifted and pushed the entry point's basic block into the queue. // Expect the interpreter to emulate now. - iset_map[i].next_run_state = RZ_INTP_RUN_STATE_EMU; + iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_EMU; // RZ_LOG_DEBUG("Next: EMU\n"); break; case RZ_THREAD_RING_BUF_FAIL: @@ -633,15 +633,15 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, case RZ_THREAD_RING_BUF_CLOSED: rz_warn_if_reached(); // Something went pretty wrong. - iset_map[i].next_run_state = RZ_INTP_RUN_STATE_TERM; + iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; intpr_terminated++; // RZ_LOG_DEBUG("Next: TERM\n"); continue; } break; } - case RZ_INTP_RUN_STATE_EMU: { - if (expected_rs != RZ_INTP_RUN_STATE_EMU) { + case RZ_INTERP_RUN_STATE_EMU: { + if (expected_rs != RZ_INTERP_RUN_STATE_EMU) { break; } // From here on, the code plays the role of the IO handler, @@ -685,26 +685,26 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // This part plays the role of a yield consumer. // In our prototype it only receives xrefs and call candidates. if (!handle_yields(core->inquiry, iset->yield_rbufs)) { - iset_map[i].next_run_state = RZ_INTP_RUN_STATE_TERM; + iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; intpr_terminated++; // RZ_LOG_DEBUG("Next: TERM\n"); } break; } - case RZ_INTP_RUN_STATE_CLEAN: { - if (!((expected_rs == RZ_INTP_RUN_STATE_CLEAN || expected_rs == RZ_INTP_RUN_STATE_EMU))) { + case RZ_INTERP_RUN_STATE_CLEAN: { + if (!((expected_rs == RZ_INTERP_RUN_STATE_CLEAN || expected_rs == RZ_INTERP_RUN_STATE_EMU))) { break; } close_reset_ipc_obj(iset); open_ipc_obj(iset); rz_th_sem_post(iset->run_state_sync); - iset_map[i].next_run_state = RZ_INTP_RUN_STATE_INIT; + iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_INIT; // RZ_LOG_DEBUG("Next: INIT\n"); break; } - case RZ_INTP_RUN_STATE_TERM: { - if (expected_rs != RZ_INTP_RUN_STATE_TERM) { - iset_map[i].next_run_state = RZ_INTP_RUN_STATE_TERM; + case RZ_INTERP_RUN_STATE_TERM: { + if (expected_rs != RZ_INTERP_RUN_STATE_TERM) { + iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; intpr_terminated++; } // RZ_LOG_DEBUG("Next: TERM\n"); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 572b41722a4..8bf142dafcc 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -146,7 +146,7 @@ static HtUP *var_set_clone(const RzInterpSet *iset, HtUP *vars) { RzIterator *it = ht_up_as_iter_keys(vars); ut64 *key; rz_iterator_foreach(it, key) { - RzInterpAbstrVal *val = iset->plugin->clone_val(ht_up_find(vars, *key, NULL), iset->intrpr_priv); + RzInterpAbstrVal *val = iset->plugin->clone_val(ht_up_find(vars, *key, NULL), iset->interp_priv); if (!val) { continue; } @@ -203,7 +203,7 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpSet *iset) { rz_interpreter_abstr_state_free(iset->astate); } if (iset->run_state) { - rz_intp_run_state_free(iset->run_state); + rz_interp_run_state_free(iset->run_state); } if (iset->il_vm) { rz_analysis_il_vm_free(iset->il_vm); @@ -308,7 +308,7 @@ RZ_API RZ_OWN RzInterpSet *rz_interpreter_set_new( iset->a = analysis; iset->plugin = plugin; iset->astate = state; - iset->run_state = rz_intp_run_state_new(); + iset->run_state = rz_interp_run_state_new(); iset->il_vm = il_vm; iset->il_queue = il_queue; iset->il_request_rbuf = il_request_rbuf; @@ -343,7 +343,7 @@ RZ_API void rz_interp_set_push(RZ_BORROW RZ_NONNULL RzInterpSet *iset, RZ_BORROW rz_strbuf_fini(&sb); RzInterpAbstrState *existing = ht_up_find(iset->fcn_state.pc_states, as->pc, NULL); if (existing) { - if (iset->plugin->join_state(existing, as, iset->intrpr_priv) && !existing->uninterpreted) { + if (iset->plugin->join_state(existing, as, iset->interp_priv) && !existing->uninterpreted) { existing->uninterpreted = true; rz_list_push(iset->fcn_state.queue, existing); } @@ -390,14 +390,14 @@ static bool choose_next_pc(RzInterpSet *iset, const RzILCacheBlock *il_bb) { // Debug printing whole state of VM. // - // plugin->state_as_str(out_state, state_str, iset->intrpr_priv); + // plugin->state_as_str(out_state, state_str, iset->interp_priv); // char *s = rz_strbuf_drain_nofree(state_str); // RZ_LOG_DEBUG("%s", s); // free(s); bool has_succsessor = true; // Determine successors and increase the reference counts for the current out state. - if (!iset->plugin->successors(iset->astate, tmp_succ_addr, iset->intrpr_priv)) { + if (!iset->plugin->successors(iset->astate, tmp_succ_addr, iset->interp_priv)) { rz_warn_if_reached(); return false; } @@ -449,10 +449,10 @@ static bool reset_intrpr_state( RzVector **succ_states) { if (iset->plugin->reset) { - iset->plugin->reset(iset->intrpr_priv); + iset->plugin->reset(iset->interp_priv); } - if (!iset->plugin->reset_state(iset->astate, entry_point, iset->intrpr_priv)) { + if (!iset->plugin->reset_state(iset->astate, entry_point, iset->interp_priv)) { rz_warn_if_reached(); return false; } @@ -507,9 +507,9 @@ RZ_API bool rz_interpreter_run_rot127(RZ_NONNULL RZ_OWN RzInterpSet *iset) { ut64 astate_hash = 0; if (iset->plugin->init) { - iset->plugin->init(&iset->intrpr_priv); + iset->plugin->init(&iset->interp_priv); } - if (!iset->plugin->init_state(iset->astate, iset->intrpr_priv)) { + if (!iset->plugin->init_state(iset->astate, iset->interp_priv)) { rz_warn_if_reached(); return false; } @@ -521,7 +521,7 @@ RZ_API bool rz_interpreter_run_rot127(RZ_NONNULL RZ_OWN RzInterpSet *iset) { INIT: { RZ_LOG_DEBUG("interpreter: Enter INIT\n"); - rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_INIT); + rz_interp_run_state_set(iset->run_state, RZ_INTERP_RUN_STATE_INIT); ut64 entry_point; if (rz_th_ring_buf_take_blocking(iset->entry_points, &entry_point) != RZ_THREAD_RING_BUF_OK || @@ -551,16 +551,16 @@ INIT: { EMU: { RZ_LOG_DEBUG("interpreter: Enter EMU\n"); - rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_EMU); + rz_interp_run_state_set(iset->run_state, RZ_INTERP_RUN_STATE_EMU); iset->astate->bb_addr = il_bb->addr; iset->astate->bb_size = il_bb->size; // Evaluate the effect on the abstract state. - if (!plugin->eval(iset, il_bb, iset->intrpr_priv)) { + if (!plugin->eval(iset, il_bb, iset->interp_priv)) { RZ_LOG_DEBUG("interpreter: Eval failed\n"); goto CLEAN; } - astate_hash = plugin->hash_state(iset->astate, iset->intrpr_priv); + astate_hash = plugin->hash_state(iset->astate, iset->interp_priv); // Add output state hash to the reachable states and // set a flag if it was a new state. @@ -598,7 +598,7 @@ EMU: { } RZ_LOG_DEBUG("interpreter: Received il_bb: 0x%" PFMT64x "\n", il_bb->addr); - if (!plugin->set_pc(iset->astate, next.ctrl_flow.actual_target, iset->intrpr_priv)) { + if (!plugin->set_pc(iset->astate, next.ctrl_flow.actual_target, iset->interp_priv)) { rz_warn_if_reached(); goto CLEAN; } @@ -609,7 +609,7 @@ EMU: { CLEAN: { RZ_LOG_DEBUG("interpreter: Enter CLEAN\n"); - rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_CLEAN); + rz_interp_run_state_set(iset->run_state, RZ_INTERP_RUN_STATE_CLEAN); RZ_FREE_CUSTOM(tmp_succ_addr, rz_vector_free); RZ_FREE_CUSTOM(succ_states, rz_vector_free); @@ -624,17 +624,17 @@ CLEAN: { TERM: { RZ_LOG_DEBUG("interpreter: Enter TERM\n"); - rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_TERM); - iset->plugin->fini_state(iset->astate, iset->intrpr_priv); - if (iset->plugin->fini && iset->intrpr_priv) { - RZ_FREE_CUSTOM(iset->intrpr_priv, iset->plugin->fini); + rz_interp_run_state_set(iset->run_state, RZ_INTERP_RUN_STATE_TERM); + iset->plugin->fini_state(iset->astate, iset->interp_priv); + if (iset->plugin->fini && iset->interp_priv) { + RZ_FREE_CUSTOM(iset->interp_priv, iset->plugin->fini); } RZ_FREE_CUSTOM(tmp_succ_addr, rz_vector_free); RZ_FREE_CUSTOM(succ_states, rz_vector_free); RZ_FREE_CUSTOM(reachable_states, rz_set_u_free); - if (iset->plugin->fini && iset->intrpr_priv) { - RZ_FREE_CUSTOM(iset->intrpr_priv, iset->plugin->fini); + if (iset->plugin->fini && iset->interp_priv) { + RZ_FREE_CUSTOM(iset->interp_priv, iset->plugin->fini); } return success; } @@ -671,9 +671,9 @@ RZ_API bool rz_interpreter_run_thestr4ng3r(RZ_NONNULL RZ_OWN RzInterpSet *iset) const RzILCacheBlock *il_bb = NULL; if (iset->plugin->init) { - iset->plugin->init(&iset->intrpr_priv); + iset->plugin->init(&iset->interp_priv); } - if (!iset->plugin->init_state(iset->astate, iset->intrpr_priv)) { + if (!iset->plugin->init_state(iset->astate, iset->interp_priv)) { rz_warn_if_reached(); return false; } @@ -686,7 +686,7 @@ RZ_API bool rz_interpreter_run_thestr4ng3r(RZ_NONNULL RZ_OWN RzInterpSet *iset) INIT: { // prepare state at the procedure entrypoint RZ_LOG_DEBUG("interpreter: Enter INIT\n"); - rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_INIT); + rz_interp_run_state_set(iset->run_state, RZ_INTERP_RUN_STATE_INIT); const RzAnalysisPlugin *cur = rz_analysis_plugin_current(iset->a); RzAnalysisILConfig *config = cur->il_config(iset->a); @@ -708,10 +708,10 @@ INIT: { } if (iset->plugin->reset) { - iset->plugin->reset(iset->intrpr_priv); + iset->plugin->reset(iset->interp_priv); } - if (!iset->plugin->init_state(estate, iset->intrpr_priv) || !iset->plugin->reset_state(estate, entry_point, iset->intrpr_priv)) { + if (!iset->plugin->init_state(estate, iset->interp_priv) || !iset->plugin->reset_state(estate, entry_point, iset->interp_priv)) { rz_warn_if_reached(); return false; } @@ -722,7 +722,7 @@ INIT: { while (true) { RZ_LOG_DEBUG("interpreter: Enter EMU\n"); - rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_EMU); + rz_interp_run_state_set(iset->run_state, RZ_INTERP_RUN_STATE_EMU); RzInterpAbstrState *next = rz_interp_set_pop(iset); if (!next) { @@ -757,7 +757,7 @@ INIT: { iset->astate->bb_addr = il_bb->addr; iset->astate->bb_size = il_bb->size; // Evaluate the effect on the abstract state. - if (!plugin->eval(iset, il_bb, iset->intrpr_priv)) { + if (!plugin->eval(iset, il_bb, iset->interp_priv)) { RZ_LOG_DEBUG("interpreter: Eval failed\n"); goto CLEAN; } @@ -768,7 +768,7 @@ INIT: { CLEAN: { RZ_LOG_DEBUG("interpreter: Enter CLEAN\n"); - rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_CLEAN); + rz_interp_run_state_set(iset->run_state, RZ_INTERP_RUN_STATE_CLEAN); // Wait until RzInquiry asks to start again. rz_th_sem_wait(iset->run_state_sync); @@ -779,14 +779,14 @@ CLEAN: { TERM: { RZ_LOG_DEBUG("interpreter: Enter TERM\n"); - rz_intp_run_state_set(iset->run_state, RZ_INTP_RUN_STATE_TERM); - iset->plugin->fini_state(iset->astate, iset->intrpr_priv); - if (iset->plugin->fini && iset->intrpr_priv) { - RZ_FREE_CUSTOM(iset->intrpr_priv, iset->plugin->fini); + rz_interp_run_state_set(iset->run_state, RZ_INTERP_RUN_STATE_TERM); + iset->plugin->fini_state(iset->astate, iset->interp_priv); + if (iset->plugin->fini && iset->interp_priv) { + RZ_FREE_CUSTOM(iset->interp_priv, iset->plugin->fini); } - if (iset->plugin->fini && iset->intrpr_priv) { - RZ_FREE_CUSTOM(iset->intrpr_priv, iset->plugin->fini); + if (iset->plugin->fini && iset->interp_priv) { + RZ_FREE_CUSTOM(iset->interp_priv, iset->plugin->fini); } return success; } diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 8fd16d68e11..88ce9aaf18f 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -358,7 +358,7 @@ void state_as_str_short(RzInterpSet *iset, RZ_OUT RzStrBuf *out, RzInterpAbstrSt first = false; const char *varname = ht_up_find(astate->var_name_hashes, djb2_reg_name, NULL); rz_strbuf_appendf(out, "%s = ", varname); - iset->plugin->val_as_str(av, out, iset->intrpr_priv); + iset->plugin->val_as_str(av, out, iset->interp_priv); } } diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index b28f3a18e24..8bd2b1bb3fb 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -142,7 +142,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, } if (true_state->pc_state == false_state->pc_state && true_state->pc == false_state->pc) { // identical target location, simply join the data and continue - iset->plugin->join_state(true_state, false_state, iset->intrpr_priv); + iset->plugin->join_state(true_state, false_state, iset->interp_priv); } else { // different jump targets, branch rather than resorting to top pc rz_interp_set_push(iset, true_state); diff --git a/librz/inquiry/interpreter/state.c b/librz/inquiry/interpreter/state.c index c5beeaa020d..e1c4c063606 100644 --- a/librz/inquiry/interpreter/state.c +++ b/librz/inquiry/interpreter/state.c @@ -3,34 +3,34 @@ #include "rz_util/rz_assert.h" #include -struct rz_intp_run_state { +struct rz_interp_run_state { RzThreadLock *lock; ///< The mutex around the state flag. - RzIntpRunStateFlag flag; ///< The current set state. + RzInterpRunStateFlag flag; ///< The current set state. }; -RZ_API const char *rz_intp_run_state_flag_str(RzIntpRunStateFlag flag) { +RZ_API const char *rz_interp_run_state_flag_str(RzInterpRunStateFlag flag) { switch (flag) { - case RZ_INTP_RUN_STATE_OUT_OF_LOOP: + case RZ_INTERP_RUN_STATE_OUT_OF_LOOP: return "-"; - case RZ_INTP_RUN_STATE_INIT: + case RZ_INTERP_RUN_STATE_INIT: return "I"; - case RZ_INTP_RUN_STATE_EMU: + case RZ_INTERP_RUN_STATE_EMU: return "O"; - case RZ_INTP_RUN_STATE_CLEAN: + case RZ_INTERP_RUN_STATE_CLEAN: return "C"; - case RZ_INTP_RUN_STATE_TERM: + case RZ_INTERP_RUN_STATE_TERM: return "T"; } rz_warn_if_reached(); return "-"; } -RZ_API RZ_OWN RzIntpRunState *rz_intp_run_state_new() { - RzIntpRunState *state = RZ_NEW0(RzIntpRunState); +RZ_API RZ_OWN RzInterpRunState *rz_interp_run_state_new() { + RzInterpRunState *state = RZ_NEW0(RzInterpRunState); if (!state) { return NULL; } - state->flag = RZ_INTP_RUN_STATE_OUT_OF_LOOP; + state->flag = RZ_INTERP_RUN_STATE_OUT_OF_LOOP; state->lock = rz_th_lock_new(false); if (!state->lock) { free(state); @@ -39,7 +39,7 @@ RZ_API RZ_OWN RzIntpRunState *rz_intp_run_state_new() { return state; } -RZ_API void rz_intp_run_state_free(RZ_OWN RZ_NULLABLE RzIntpRunState *state) { +RZ_API void rz_interp_run_state_free(RZ_OWN RZ_NULLABLE RzInterpRunState *state) { if (!state) { return; } @@ -47,16 +47,16 @@ RZ_API void rz_intp_run_state_free(RZ_OWN RZ_NULLABLE RzIntpRunState *state) { free(state); } -RZ_API RzIntpRunStateFlag rz_intp_run_state_get(RZ_BORROW RZ_NONNULL RzIntpRunState *state) { - rz_return_val_if_fail(state, RZ_INTP_RUN_STATE_TERM); +RZ_API RzInterpRunStateFlag rz_interp_run_state_get(RZ_BORROW RZ_NONNULL RzInterpRunState *state) { + rz_return_val_if_fail(state, RZ_INTERP_RUN_STATE_TERM); rz_th_lock_enter(state->lock); - RzIntpRunStateFlag flag = state->flag; + RzInterpRunStateFlag flag = state->flag; rz_th_lock_leave(state->lock); return flag; } -RZ_API RzIntpRunStateFlag rz_intp_run_state_get_unsafe(const RZ_NONNULL RzIntpRunState *state) { - rz_return_val_if_fail(state, RZ_INTP_RUN_STATE_TERM); +RZ_API RzInterpRunStateFlag rz_interp_run_state_get_unsafe(const RZ_NONNULL RzInterpRunState *state) { + rz_return_val_if_fail(state, RZ_INTERP_RUN_STATE_TERM); return state->flag; } @@ -64,7 +64,7 @@ RZ_API RzIntpRunStateFlag rz_intp_run_state_get_unsafe(const RZ_NONNULL RzIntpRu * \brief Sets the run state. * This function is declared IPI, so it is not used outside of the interpreter module! */ -RZ_IPI void rz_intp_run_state_set(RZ_BORROW RZ_NONNULL RzIntpRunState *state, RzIntpRunStateFlag flag) { +RZ_IPI void rz_interp_run_state_set(RZ_BORROW RZ_NONNULL RzInterpRunState *state, RzInterpRunStateFlag flag) { rz_th_lock_enter(state->lock); state->flag = flag; rz_th_lock_leave(state->lock); From ca6de4597245e7f417694e77d69be9b564e5aac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sat, 27 Jun 2026 15:37:14 +0200 Subject: [PATCH 298/334] Simplify IL cache requests Actual concurrent lifting is removed for now while developing the prototype. --- librz/include/rz_inquiry/rz_il_cache.h | 12 +- librz/include/rz_inquiry/rz_interpreter.h | 7 +- librz/inquiry/il_cache.c | 93 +++---- librz/inquiry/inquiry.c | 19 +- librz/inquiry/interpreter/interpreter.c | 299 +--------------------- librz/inquiry/interpreter/meson.build | 59 ----- librz/inquiry/meson.build | 10 +- 7 files changed, 81 insertions(+), 418 deletions(-) delete mode 100644 librz/inquiry/interpreter/meson.build diff --git a/librz/include/rz_inquiry/rz_il_cache.h b/librz/include/rz_inquiry/rz_il_cache.h index b971f17b040..f7408c8676f 100644 --- a/librz/include/rz_inquiry/rz_il_cache.h +++ b/librz/include/rz_inquiry/rz_il_cache.h @@ -77,10 +77,14 @@ RZ_API void rz_il_cache_stop_serving(RZ_BORROW RZ_NONNULL RzILCache *cache); RZ_API const RzVector /**/ *rz_il_cache_get_static_xrefs(const RzILCache *cache); RZ_API RzIterator /**/ *rz_il_cache_get_blocks(const RzILCache *cache); -RZ_API bool rz_il_cache_get_new_ring_buf( - RZ_BORROW RzILCache *cache, - RZ_NONNULL RZ_BORROW RZ_OUT RzThreadRingBuf **req_rbuf, - RZ_NONNULL RZ_BORROW RZ_OUT RzThreadQueue **il_queue); +typedef struct rz_il_cache_client_t { + RzThreadRingBuf *req_rbuf; + RzThreadQueue *il_queue; +} RzILCacheClient; + +RZ_API RZ_BORROW RzILCacheClient *rz_il_cache_new_client(RZ_NONNULL RZ_BORROW RzILCache *cache); +RZ_API RZ_NULLABLE RZ_BORROW const RzILCacheBlock *rz_il_cache_client_lift_il_block(RZ_NONNULL RZ_BORROW RzILCacheClient *client, ut64 addr); + RZ_API bool rz_il_cache_was_requested( RZ_BORROW RzILCache *cache, ut64 addr); diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index bfb68de201f..aa5fee3514f 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -306,9 +306,7 @@ struct rz_interpreter_set { RzAnalysisILVM *il_vm; ///< The RzAnalysisILVM for memory IO. RZ_LIFETIME(RzILCache) - RZ_BORROW RzThreadQueue /**/ *il_queue; ///< The queue to receive the IL effects. - RZ_LIFETIME(RzILCache) - RZ_BORROW RzThreadRingBuf /**/ *il_request_rbuf; ///< The ring buffer to send requests to the cache what address to get the next IL op from. + RZ_BORROW RzILCacheClient *il_cache_client; RzThreadRingBuf /**/ *io_request_rbuf; ///< The ring buffer for read/write requests to the IO layer. RzThreadRingBuf /**/ *io_result_rbuf; ///< The ring buffer for the read/write requests' answers. @@ -358,8 +356,7 @@ RZ_API RZ_OWN RzInterpSet *rz_interpreter_set_new( RzAnalysis *analysis, RZ_NONNULL RZ_OWN RzInterpPlugin *plugin, RzInterpAbstraction abstraction, - RZ_NONNULL RZ_BORROW RzThreadRingBuf *il_request_rbuf, - RZ_NONNULL RZ_BORROW RzThreadQueue *il_queue, + RZ_NONNULL RZ_BORROW RzILCacheClient *il_cache_client, RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM], RZ_NONNULL const RzVector /**/ *ignored_code); RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpSet *iset); diff --git a/librz/inquiry/il_cache.c b/librz/inquiry/il_cache.c index 265eb5da72b..eb6048c4ffa 100644 --- a/librz/inquiry/il_cache.c +++ b/librz/inquiry/il_cache.c @@ -45,9 +45,7 @@ struct rz_il_cache_t { HtUP /**/ *cache; - // The pair of ring_buffer + queue are always at the same offset. - RzPVector /**/ *req_rbufs; ///< The ring buffers the cache receives IL block requests. - RzPVector /**/ *il_queues; ///< The queues the cache serves IL blocks over. + RzPVector /**/ clients; RzThreadLock *skyline_lock; /** @@ -273,29 +271,28 @@ RZ_API bool rz_il_cache_serve(RZ_NONNULL RzILCache *cache) { bool success = true; rz_atomic_bool_set(cache->is_serving, true); - size_t clients = rz_pvector_len(cache->req_rbufs); + size_t clients = rz_pvector_len(&cache->clients); while (rz_atomic_bool_get(cache->is_serving)) { for (size_t i = 0; i < clients; ++i) { - RzThreadRingBuf *req_rbuf = rz_pvector_at(cache->req_rbufs, i); - RzThreadQueue *serve_queue = rz_pvector_at(cache->il_queues, i); + RzILCacheClient *client = rz_pvector_at(&cache->clients, i); // TODO: This unsafe check permits race conditions. // Not sure if it improve performance here. // Needs more benchmarks. - if (rz_th_ring_buf_is_empty_unsafe(req_rbuf)) { + if (rz_th_ring_buf_is_empty_unsafe(client->req_rbuf)) { continue; } ut64 req_addr = 0; - RzThreadRingBufResult r = rz_th_ring_buf_take(req_rbuf, &req_addr); + RzThreadRingBufResult r = rz_th_ring_buf_take(client->req_rbuf, &req_addr); if (r == RZ_THREAD_RING_BUF_CLOSED) { goto stop_serving; } else if (r == RZ_THREAD_RING_BUF_OK) { const RzILCacheBlock *block = lift_il_block(cache, req_addr); if (block) { - rz_th_queue_push(serve_queue, (void *)block, true); + rz_th_queue_push(client->il_queue, (void *)block, true); } else { - rz_th_queue_push(serve_queue, RZ_IL_CACHE_FAILED_LIFTING_PTR, true); + rz_th_queue_push(client->il_queue, RZ_IL_CACHE_FAILED_LIFTING_PTR, true); RZ_LOG_DEBUG("Failed to lift IL block at 0x%" PFMT64x "\n", req_addr); } } @@ -318,11 +315,10 @@ RZ_API void rz_il_cache_close(RZ_BORROW RZ_NONNULL RzILCache *cache) { rz_atomic_bool_set(cache->is_serving, false); - for (size_t i = 0; i < rz_pvector_len(cache->req_rbufs); ++i) { - RzThreadRingBuf *rb = rz_pvector_at(cache->req_rbufs, i); - rz_th_ring_buf_close(rb); - RzThreadQueue *il_queue = rz_pvector_at(cache->il_queues, i); - rz_th_queue_close(il_queue); + for (size_t i = 0; i < rz_pvector_len(&cache->clients); ++i) { + RzILCacheClient *client = rz_pvector_at(&cache->clients, i); + rz_th_ring_buf_close(client->req_rbuf); + rz_th_queue_close(client->il_queue); } } @@ -358,8 +354,7 @@ RZ_API void rz_il_cache_free(RZ_OWN RZ_NULLABLE RzILCache *cache) { rz_th_lock_leave(cache->n_serving_lock); } while (!all_stopped); - rz_pvector_free(cache->req_rbufs); - rz_pvector_free(cache->il_queues); + rz_pvector_fini(&cache->clients); ht_up_free(cache->cache); rz_vector_free(cache->static_xrefs); @@ -374,6 +369,13 @@ RZ_API void rz_il_cache_free(RZ_OWN RZ_NULLABLE RzILCache *cache) { free(cache); } +static void rz_il_cache_client_free(void *ptr) { + RzILCacheClient *client = ptr; + rz_th_ring_buf_free(client->req_rbuf); + rz_th_queue_free(client->il_queue); + free(client); +} + RZ_API RZ_OWN RzILCache *rz_il_cache_new( RZ_BORROW RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, @@ -404,11 +406,7 @@ RZ_API RZ_OWN RzILCache *rz_il_cache_new( } rz_skyline_init(cache->served_regions); - cache->il_queues = rz_pvector_new((RzPVectorFree)rz_th_queue_free); - cache->req_rbufs = rz_pvector_new((RzPVectorFree)rz_th_ring_buf_free); - if (!cache->il_queues || !cache->req_rbufs) { - goto err; - } + rz_pvector_init(&cache->clients, rz_il_cache_client_free); cache->config = config; cache->us_sleep = SLEEP_NONE_US; @@ -444,33 +442,40 @@ RZ_API bool rz_il_cache_was_requested( return r; } -RZ_API bool rz_il_cache_get_new_ring_buf( - RZ_BORROW RzILCache *cache, - RZ_NONNULL RZ_BORROW RZ_OUT RzThreadRingBuf **request_rbuf, - RZ_NONNULL RZ_BORROW RZ_OUT RzThreadQueue **il_queue) { - rz_return_val_if_fail(cache && request_rbuf && il_queue, false); +RZ_API RZ_BORROW RzILCacheClient *rz_il_cache_new_client(RZ_NONNULL RZ_BORROW RzILCache *cache) { + rz_return_val_if_fail(cache, false); - *request_rbuf = NULL; - *il_queue = NULL; // The queue to pass the Effects to the interpreter. - *il_queue = rz_th_queue_new(RZ_IL_OPS_CACHE_IL_QUEUE_SIZE, NULL); - if (!il_queue) { - rz_warn_if_reached(); + RzThreadQueue *il_queue = rz_th_queue_new(RZ_IL_OPS_CACHE_IL_QUEUE_SIZE, NULL); + // The ring buffer the interpreter can request new Effects over. + RzThreadRingBuf *request_rbuf = rz_th_ring_buf_new(RZ_IL_OPS_CACHE_ADDR_RBUF_SIZE, sizeof(ut64)); + if (!il_queue || !request_rbuf) { goto error_free; } - rz_pvector_push(cache->il_queues, *il_queue); - - // The ring buffer the interpreter can request new Effects over. - *request_rbuf = rz_th_ring_buf_new(RZ_IL_OPS_CACHE_ADDR_RBUF_SIZE, sizeof(ut64)); - if (!*request_rbuf) { - rz_warn_if_reached(); + RzILCacheClient *client = RZ_NEW0(RzILCacheClient); + if (!client) { goto error_free; } - rz_pvector_push(cache->req_rbufs, *request_rbuf); - - return true; + client->il_queue = il_queue; + client->req_rbuf = request_rbuf; + rz_pvector_push(&cache->clients, client); + return client; error_free: - rz_th_queue_free(*il_queue); - rz_th_ring_buf_free(*request_rbuf); - return false; + rz_th_queue_free(il_queue); + rz_th_ring_buf_free(request_rbuf); + return NULL; +} + +RZ_API RZ_NULLABLE RZ_BORROW const RzILCacheBlock *rz_il_cache_client_lift_il_block(RZ_NONNULL RZ_BORROW RzILCacheClient *client, ut64 addr) { + rz_return_val_if_fail(client, NULL); + if (rz_th_ring_buf_put(client->req_rbuf, &addr) != RZ_THREAD_RING_BUF_OK) { + // Can't request IL block => Cache closed => Terminate + return NULL; + } + const RzILCacheBlock *il_bb = NULL; + if (!rz_th_queue_pop(client->il_queue, false, (void **)&il_bb) || + il_bb == RZ_IL_CACHE_FAILED_LIFTING_PTR || !il_bb) { + return NULL; + } + return il_bb; } diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 5aaadff1e87..b75e93f6a00 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -396,10 +396,10 @@ static void close_reset_ipc_obj(RzInterpSet *iset) { // This also clears the buffer and queues rz_th_ring_buf_close(iset->io_request_rbuf); rz_th_ring_buf_close(iset->io_result_rbuf); - rz_th_ring_buf_close(iset->il_request_rbuf); rz_th_ring_buf_close(iset->entry_points); - rz_th_queue_close(iset->il_queue); - rz_list_free(rz_th_queue_pop_all(iset->il_queue)); + rz_th_ring_buf_close(iset->il_cache_client->req_rbuf); + rz_th_queue_close(iset->il_cache_client->il_queue); + rz_list_free(rz_th_queue_pop_all(iset->il_cache_client->il_queue)); } static void open_ipc_obj(RzInterpSet *iset) { @@ -407,9 +407,9 @@ static void open_ipc_obj(RzInterpSet *iset) { // jump target again. rz_th_ring_buf_open(iset->io_request_rbuf); rz_th_ring_buf_open(iset->io_result_rbuf); - rz_th_ring_buf_open(iset->il_request_rbuf); + rz_th_ring_buf_open(iset->il_cache_client->req_rbuf); + rz_th_queue_open(iset->il_cache_client->il_queue); rz_th_ring_buf_open(iset->entry_points); - rz_th_queue_open(iset->il_queue); } static bool collect_entry_points(RzCore *core, @@ -549,9 +549,8 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // Initialize and spawn the interpreters. // for (size_t i = 0; i < n_threads; ++i) { - RzThreadRingBuf *il_req = NULL; - RzThreadQueue *il_queue = NULL; - if (!rz_il_cache_get_new_ring_buf(il_cache, &il_req, &il_queue)) { + RzILCacheClient *cache_client = rz_il_cache_new_client(il_cache); + if (!cache_client) { return_code = false; rz_warn_if_reached(); goto error_free; @@ -560,7 +559,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, core->analysis, prototype->p_interpreter, RZ_INTERP_ABSTRACTION_CONST, - il_req, il_queue, + cache_client, yield_rbufs, ignored_code); if (!intp_iset) { @@ -611,7 +610,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, if (!reduce_get_entry_points(iset, il_cache, entry_points)) { // None left. // TODO Remove? - rz_th_queue_close(iset->il_queue); + rz_th_queue_close(iset->il_cache_client->il_queue); iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; intpr_terminated++; // RZ_LOG_DEBUG("Next: TERM\n"); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 8bf142dafcc..9c205229762 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -7,7 +7,6 @@ #include "rz_analysis.h" #include "rz_util/rz_assert.h" -#include "rz_util/rz_itv.h" #include "rz_util/rz_log.h" #include #include @@ -247,11 +246,10 @@ RZ_API RZ_OWN RzInterpSet *rz_interpreter_set_new( RzAnalysis *analysis, RZ_NONNULL RZ_OWN RzInterpPlugin *plugin, RzInterpAbstraction abstraction, - RZ_NONNULL RZ_BORROW RzThreadRingBuf *il_request_rbuf, - RZ_NONNULL RZ_BORROW RzThreadQueue *il_queue, + RZ_NONNULL RZ_BORROW RzILCacheClient *il_cache_client, RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM], RZ_NONNULL const RzVector /**/ *ignored_code) { - rz_return_val_if_fail(plugin && ignored_code && analysis && il_request_rbuf && il_queue, NULL); + rz_return_val_if_fail(plugin && ignored_code && analysis && il_cache_client, NULL); if (abstraction != (plugin->supported_abstractions & abstraction)) { RZ_LOG_ERROR("Plugin does not support all required abstractions.\n"); @@ -310,8 +308,7 @@ RZ_API RZ_OWN RzInterpSet *rz_interpreter_set_new( iset->astate = state; iset->run_state = rz_interp_run_state_new(); iset->il_vm = il_vm; - iset->il_queue = il_queue; - iset->il_request_rbuf = il_request_rbuf; + iset->il_cache_client = il_cache_client; iset->entry_points = entry_points; iset->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] = yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] = yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; @@ -367,287 +364,18 @@ RZ_API RZ_NULLABLE RZ_BORROW RzInterpAbstrState *rz_interp_set_pop(RZ_BORROW RZ_ return r; } -static bool jumps_to_ignored_code(const RzVector *v, ut64 jump_target) { - void *it; - rz_vector_foreach (v, it) { - RzInterval *itv = it; - if (rz_itv_contain(*itv, jump_target)) { - return true; - } - } - return false; -} - typedef struct { RzInterpCtrlFlow ctrl_flow; ut64 in_state_hash; } SuccessorState; -static bool choose_next_pc(RzInterpSet *iset, - ut64 out_hash, - RzVector *tmp_succ_addr, - RzVector *succ_states, - const RzILCacheBlock *il_bb) { - // Debug printing whole state of VM. - // - // plugin->state_as_str(out_state, state_str, iset->interp_priv); - // char *s = rz_strbuf_drain_nofree(state_str); - // RZ_LOG_DEBUG("%s", s); - // free(s); - bool has_succsessor = true; - - // Determine successors and increase the reference counts for the current out state. - if (!iset->plugin->successors(iset->astate, tmp_succ_addr, iset->interp_priv)) { - rz_warn_if_reached(); - return false; - } - - // It is possible that the successor function doesn't add successors. - // E.g. because the PC is an abstract value. - // In this case the state counts as invalid. - has_succsessor = !rz_vector_empty(tmp_succ_addr); - // Request the successor effects over the queue. - while (!rz_vector_empty(tmp_succ_addr)) { - RzInterpCtrlFlow cf = { 0 }; - rz_vector_pop_front(tmp_succ_addr, &cf); - if (cf.target_addr == UT64_MAX || cf.target_addr == 0) { - RZ_LOG_DEBUG("interpreter: Quit due to invalid PC.\n"); - // Obviously wrong address. - return false; - } - cf.src_block_addr = il_bb->addr; - if (jumps_to_ignored_code(iset->ignored_code, cf.target_addr) && !cf.alt_target) { - RZ_LOG_DEBUG("interpreter: tried to jump to ignored code region at 0x%" PFMT64x "\n", cf.target_addr); - // Ignored code is mostly dynamically linked functions. - // Skip to the next following address after the jump. - cf.alt_target = il_bb->addr + il_bb->size; - cf.actual_target = cf.alt_target; - } - - SuccessorState ss = { - .ctrl_flow = cf, - .in_state_hash = out_hash - }; - // The successors are pushed in the same order into the succ_states - // vector, as they are requested over the queue. - rz_vector_push(succ_states, &ss); - if (rz_th_ring_buf_put(iset->il_request_rbuf, &cf.actual_target) != RZ_THREAD_RING_BUF_OK) { - return false; - } - } - return has_succsessor; -} - -/** - * \brief Set entry point and reset (clean) the vectors and sets. - */ -static bool reset_intrpr_state( - RzInterpSet *iset, - ut64 entry_point, - RzVector **tmp_succ_addr, - RzSetU **reachable_states, - RzVector **succ_states) { - - if (iset->plugin->reset) { - iset->plugin->reset(iset->interp_priv); - } - - if (!iset->plugin->reset_state(iset->astate, entry_point, iset->interp_priv)) { - rz_warn_if_reached(); - return false; - } - - *tmp_succ_addr = rz_vector_new(sizeof(RzInterpCtrlFlow), NULL, NULL); - *succ_states = rz_vector_new(sizeof(SuccessorState), NULL, NULL); - *reachable_states = rz_set_u_new(); - if (!*tmp_succ_addr || !*succ_states || !*reachable_states) { - rz_warn_if_reached(); - return false; - } - return true; -} - -/** - * Main interpretation. - */ -RZ_API bool rz_interpreter_run_rot127(RZ_NONNULL RZ_OWN RzInterpSet *iset) { - rz_return_val_if_fail(iset && - iset->astate && - iset->il_request_rbuf && - iset->il_queue && - iset->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] && - iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] && - iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] && - iset->run_state_sync && - iset->plugin && - iset->plugin->eval && - iset->plugin->successors && - iset->plugin->init_state && - iset->plugin->fini_state && - iset->plugin->hash_state, - false); - - bool success = true; - - RZ_LOG_DEBUG("interpreter: Main: Hello.\n"); - RzInterpPlugin *plugin = iset->plugin; - - // - // Start interpretation - // - - // A vector for the plugin to push the determined successors into. - RzVector *tmp_succ_addr = NULL; - // The set of reachable states. - RzSetU *reachable_states = NULL; - // The successor states to evaluate. - // This vector must have the same order as the elements pushed into branch_queue. - RzVector *succ_states = NULL; - const RzILCacheBlock *il_bb = NULL; - ut64 astate_hash = 0; - - if (iset->plugin->init) { - iset->plugin->init(&iset->interp_priv); - } - if (!iset->plugin->init_state(iset->astate, iset->interp_priv)) { - rz_warn_if_reached(); - return false; - } - - // TODO: It is probably better to make the following stuff while-loops. - // Because otherwise it doesn't make sense without the docs. - // But while debugging and developing, I keep it this way to separate clearly - // what the interpreter does in each state. - -INIT: { - RZ_LOG_DEBUG("interpreter: Enter INIT\n"); - rz_interp_run_state_set(iset->run_state, RZ_INTERP_RUN_STATE_INIT); - - ut64 entry_point; - if (rz_th_ring_buf_take_blocking(iset->entry_points, &entry_point) != RZ_THREAD_RING_BUF_OK || - rz_th_ring_buf_put(iset->il_request_rbuf, &entry_point) != RZ_THREAD_RING_BUF_OK) { - // No more entry points to interpret => Terminate. - // OR - // Can't request IL block => Cache closed => Terminate. - success = true; - goto TERM; - } - - // Initializes the current interpreter's private data and its state. - if (!reset_intrpr_state(iset, entry_point, &tmp_succ_addr, &reachable_states, &succ_states)) { - success = false; - goto TERM; - } - - if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || - il_bb == RZ_IL_CACHE_FAILED_LIFTING_PTR || !il_bb) { - // No more BBs to interpret. Terminate. - success = true; - goto TERM; - } - - goto EMU; -} - -EMU: { - RZ_LOG_DEBUG("interpreter: Enter EMU\n"); - rz_interp_run_state_set(iset->run_state, RZ_INTERP_RUN_STATE_EMU); - - iset->astate->bb_addr = il_bb->addr; - iset->astate->bb_size = il_bb->size; - // Evaluate the effect on the abstract state. - if (!plugin->eval(iset, il_bb, iset->interp_priv)) { - RZ_LOG_DEBUG("interpreter: Eval failed\n"); - goto CLEAN; - } - astate_hash = plugin->hash_state(iset->astate, iset->interp_priv); - - // Add output state hash to the reachable states and - // set a flag if it was a new state. - size_t psize = rz_set_u_size(reachable_states); - rz_set_u_add(reachable_states, astate_hash); - bool new_state_reached = psize < rz_set_u_size(reachable_states); - - // Determine the successor effects to evaluate. - // Only newly reached states are allowed to add successors. - if (!(new_state_reached && choose_next_pc(iset, astate_hash, tmp_succ_addr, succ_states, il_bb))) { - // No new state or address means we can stop interpreting. - // Note, that we can't use the queues as cancel condition because they - // are asynchronous and checking them would introduces race conditions. - // TODO: This doesn't work if the interpreter can produce multiple out states. - goto CLEAN; - } - - // Set effect and state for next evaluation. - il_bb = NULL; - SuccessorState next = { 0 }; - rz_vector_pop_front(succ_states, &next); - if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || - il_bb == RZ_IL_CACHE_FAILED_LIFTING_PTR || !il_bb) { - RZ_LOG_DEBUG("interpreter: Getting il bb failed\n"); - // The il op lifting failed. Likely because the PC - // pointed to an unmapped region. - goto CLEAN; - } - // Now we know the size of the destination block. - // Set it and report the control flow change. - next.ctrl_flow.target_block_size = il_bb->size; - RzThreadRingBuf *cf_rbuf = iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW]->rbuf; - if (rz_th_ring_buf_put(cf_rbuf, &next.ctrl_flow) != RZ_THREAD_RING_BUF_OK) { - return false; - } - - RZ_LOG_DEBUG("interpreter: Received il_bb: 0x%" PFMT64x "\n", il_bb->addr); - if (!plugin->set_pc(iset->astate, next.ctrl_flow.actual_target, iset->interp_priv)) { - rz_warn_if_reached(); - goto CLEAN; - } - - // Loop back. Interpret next block. - goto EMU; -} - -CLEAN: { - RZ_LOG_DEBUG("interpreter: Enter CLEAN\n"); - rz_interp_run_state_set(iset->run_state, RZ_INTERP_RUN_STATE_CLEAN); - - RZ_FREE_CUSTOM(tmp_succ_addr, rz_vector_free); - RZ_FREE_CUSTOM(succ_states, rz_vector_free); - RZ_FREE_CUSTOM(reachable_states, rz_set_u_free); - - // Wait until RzInquiry asks to start again. - rz_th_sem_wait(iset->run_state_sync); - - // Clean can only transition to Init. - goto INIT; -} - -TERM: { - RZ_LOG_DEBUG("interpreter: Enter TERM\n"); - rz_interp_run_state_set(iset->run_state, RZ_INTERP_RUN_STATE_TERM); - iset->plugin->fini_state(iset->astate, iset->interp_priv); - if (iset->plugin->fini && iset->interp_priv) { - RZ_FREE_CUSTOM(iset->interp_priv, iset->plugin->fini); - } - - RZ_FREE_CUSTOM(tmp_succ_addr, rz_vector_free); - RZ_FREE_CUSTOM(succ_states, rz_vector_free); - RZ_FREE_CUSTOM(reachable_states, rz_set_u_free); - if (iset->plugin->fini && iset->interp_priv) { - RZ_FREE_CUSTOM(iset->interp_priv, iset->plugin->fini); - } - return success; -} -} - /** * Main interpretation. */ -RZ_API bool rz_interpreter_run_thestr4ng3r(RZ_NONNULL RZ_OWN RzInterpSet *iset) { +RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpSet *iset) { rz_return_val_if_fail(iset && iset->astate && - iset->il_request_rbuf && - iset->il_queue && + iset->il_cache_client && iset->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] && iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] && iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] && @@ -668,7 +396,6 @@ RZ_API bool rz_interpreter_run_thestr4ng3r(RZ_NONNULL RZ_OWN RzInterpSet *iset) // // Start interpretation // - const RzILCacheBlock *il_bb = NULL; if (iset->plugin->init) { iset->plugin->init(&iset->interp_priv); @@ -732,13 +459,8 @@ INIT: { } iset->astate = rz_interpreter_abstr_state_clone(iset, next); - if (rz_th_ring_buf_put(iset->il_request_rbuf, &iset->astate->pc) != RZ_THREAD_RING_BUF_OK) { - // Can't request IL block => Cache closed => Terminate - success = false; - goto TERM; - } - if (!rz_th_queue_pop(iset->il_queue, false, (void **)&il_bb) || - il_bb == RZ_IL_CACHE_FAILED_LIFTING_PTR || !il_bb) { + const RzILCacheBlock *il_bb = rz_il_cache_client_lift_il_block(iset->il_cache_client, iset->astate->pc); + if (!il_bb) { success = false; goto TERM; } @@ -761,9 +483,6 @@ INIT: { RZ_LOG_DEBUG("interpreter: Eval failed\n"); goto CLEAN; } - - // Set effect and state for next evaluation. - il_bb = NULL; } CLEAN: { @@ -791,7 +510,3 @@ TERM: { return success; } } - -RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpSet *iset) { - return rz_interpreter_run_thestr4ng3r(iset); -} diff --git a/librz/inquiry/interpreter/meson.build b/librz/inquiry/interpreter/meson.build deleted file mode 100644 index 3dda29212c8..00000000000 --- a/librz/inquiry/interpreter/meson.build +++ /dev/null @@ -1,59 +0,0 @@ -# SPDX-FileCopyrightText: 2025 RizinOrg -# SPDX-License-Identifier: LGPL-3.0-only - -interpreter_plugins_list = [ - 'prototype', -] - -interpreter_plugins = { - 'base_name': 'rz_interpreter', - 'base_struct': 'RzInterpreterPlugin', - 'list': interpreter_plugins_list, -} - -rz_interpreter_sources = [ - 'interpreter.c', - 'state.c', - 'p/interpreter_prototype.c', - 'prototype/eval_effect.c', - 'prototype/eval_pure.c', - 'prototype/eval.c', -] - -rz_interpreter_inc = [platform_inc] - -rz_interpreter = library('rz_interpreter', rz_interpreter_sources, - include_directories: rz_interpreter_inc, - dependencies: [ - rz_arch_dep, - rz_cons_dep, - rz_hash_dep, - rz_il_dep, - rz_type_dep, - rz_util_dep, - ], - install: true, - implicit_include_directories: false, - install_rpath: rpath_lib, - soversion: rizin_libversion, - version: rizin_version, - name_suffix: lib_name_suffix, - name_prefix: lib_name_prefix, -) - -rz_interpreter_dep = declare_dependency(link_with: rz_interpreter, - include_directories: rz_interpreter_inc) -meson.override_dependency('rz_interpreter', rz_interpreter_dep) - -modules += { 'rz_interpreter': { - 'target': rz_interpreter, - 'dependencies': [ - 'rz_arch', - 'rz_cons', - 'rz_hash', - 'rz_il', - 'rz_type', - 'rz_util' - ], - 'plugins': [interpreter_plugins] -}} diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index 2519484e928..f059fa4ef18 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -16,10 +16,14 @@ rz_inquiry_sources = [ 'bcfg.c', 'inquiry.c', 'il_cache.c', + 'interpreter/interpreter.c', + 'interpreter/state.c', + 'interpreter/p/interpreter_prototype.c', + 'interpreter/prototype/eval_effect.c', + 'interpreter/prototype/eval_pure.c', + 'interpreter/prototype/eval.c', ] -subdir('interpreter') - rz_inquiry_inc = [platform_inc] rz_inquiry = library('rz_inquiry', rz_inquiry_sources, @@ -31,7 +35,6 @@ rz_inquiry = library('rz_inquiry', rz_inquiry_sources, rz_cons_dep, rz_hash_dep, rz_il_dep, - rz_interpreter_dep, rz_io_dep, rz_magic_dep, rz_reg_dep, @@ -62,7 +65,6 @@ modules += { 'rz_inquiry': { 'rz_cons', 'rz_hash', 'rz_il', - 'rz_interpreter', 'rz_io', 'rz_magic', 'rz_reg', From e908b596396044ff7ebcd7cf948a58c0336c9a32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sun, 28 Jun 2026 16:09:15 +0200 Subject: [PATCH 299/334] Adapt to RzAnalysisILContext --- librz/il/definitions/mem.c | 11 ++-- librz/include/rz_il/definitions/mem.h | 10 ++++ librz/include/rz_inquiry/rz_interpreter.h | 6 +-- librz/inquiry/inquiry.c | 17 +++--- librz/inquiry/interpreter/interpreter.c | 53 +++++-------------- .../interpreter/p/interpreter_prototype.c | 2 +- librz/inquiry/interpreter/prototype/eval.c | 4 +- .../inquiry/interpreter/prototype/eval_pure.c | 2 +- 8 files changed, 46 insertions(+), 59 deletions(-) diff --git a/librz/il/definitions/mem.c b/librz/il/definitions/mem.c index b2ade7ae0ff..fbc0eb1cf4b 100644 --- a/librz/il/definitions/mem.c +++ b/librz/il/definitions/mem.c @@ -154,7 +154,8 @@ static RzBitVector *read_n_bits(RzBuffer *buf, ut32 n_bits, RzBitVector *key, bo return value; } -static bool read_n_bits_into(RzBuffer *mem_buf, RZ_OUT RzBitVector *out_bv, ut32 n_bits, const RzBitVector *key, bool big_endian) { +RZ_API bool rz_il_loadw_into(RZ_NONNULL RzBuffer *mem_buf, RZ_NONNULL RZ_OUT RzBitVector *out_bv, RZ_NONNULL const RzBitVector *key, ut32 n_bits, bool big_endian) { + rz_return_val_if_fail(mem_buf && out_bv && key, false); ut64 address = rz_bv_to_ut64(key); size_t n_bytes = rz_bv_len_bytes(out_bv); ut8 *data = calloc(n_bytes, 1); @@ -171,7 +172,9 @@ static bool read_n_bits_into(RzBuffer *mem_buf, RZ_OUT RzBitVector *out_bv, ut32 free(data); return true; } -static bool write_n_bits(RzBuffer *buf, const RzBitVector *key, const RzBitVector *value, bool big_endian) { + +RZ_API bool rz_il_storew(RZ_NONNULL RzBuffer *buf, RZ_NONNULL const RzBitVector *key, RZ_NONNULL const RzBitVector *value, bool big_endian) { + rz_return_val_if_fail(buf && key && value, false); ut64 address = rz_bv_to_ut64(key); ut32 n_bytes = rz_bv_len_bytes(value); @@ -218,7 +221,7 @@ RZ_API bool rz_il_mem_loadw_into(RZ_NONNULL RzILMem *mem, bool big_endian) { rz_return_val_if_fail(mem && key && n_bits, false); return_val_if_key_len_wrong(mem, key, NULL); - return read_n_bits_into(mem->buf, out_bv, n_bits, key, big_endian); + return rz_il_loadw_into(mem->buf, out_bv, key, n_bits, big_endian); } /** @@ -230,5 +233,5 @@ RZ_API bool rz_il_mem_loadw_into(RZ_NONNULL RzILMem *mem, RZ_API bool rz_il_mem_storew(RzILMem *mem, const RzBitVector *key, const RzBitVector *value, bool big_endian) { rz_return_val_if_fail(mem && key && value, false); return_val_if_key_len_wrong(mem, key, false); - return write_n_bits(mem->buf, key, value, big_endian); + return rz_il_storew(mem->buf, key, value, big_endian); } diff --git a/librz/include/rz_il/definitions/mem.h b/librz/include/rz_il/definitions/mem.h index cbd0a357d74..73a8228eec8 100644 --- a/librz/include/rz_il/definitions/mem.h +++ b/librz/include/rz_il/definitions/mem.h @@ -13,6 +13,16 @@ extern "C" { typedef ut32 RzILMemIndex; +/** + * \brief Low-level reading function used in rz_il_mem_loadw_into + */ +RZ_API bool rz_il_loadw_into(RZ_NONNULL RzBuffer *mem_buf, RZ_NONNULL RZ_OUT RzBitVector *out_bv, RZ_NONNULL const RzBitVector *key, ut32 n_bits, bool big_endian); + +/** + * \brief Low-level writing function used in rz_il_mem_storew + */ +RZ_API bool rz_il_storew(RZ_NONNULL RzBuffer *buf, RZ_NONNULL const RzBitVector *key, RZ_NONNULL const RzBitVector *value, bool big_endian); + /** * \brief A single memory as part of the RzIL VM. * diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index aa5fee3514f..014d6d53363 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -118,7 +118,6 @@ typedef struct { HtUP /**/ *globals; ///< Global variables (mostly registers). Indexed by DJB2 hash of global name. HtUP /**/ *locals; ///< Local variables. Indexed by DJB2 hash of the local name. HtUP /**/ *lets; ///< Let variables. Indexed by DJB2 hash of the let name. - RzAnalysisILConfig *il_config; ///< The IL configuration of the RzArch plugin. const char *arch_name; ///< Name of architecture. Used by work-arounds until we have RzArch. ut64 bb_addr; ut64 bb_size; @@ -303,7 +302,7 @@ struct rz_interpreter_set { */ RzThreadRingBuf *entry_points; - RzAnalysisILVM *il_vm; ///< The RzAnalysisILVM for memory IO. + RzAnalysisILContext *il_ctx; ///< Context about available global vars and memory RZ_LIFETIME(RzILCache) RZ_BORROW RzILCacheClient *il_cache_client; @@ -343,8 +342,7 @@ RZ_API void rz_interpreter_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_new( const char *arch_name, RzInterpAbstraction kinds, - RZ_OWN RZ_NONNULL RzAnalysisILConfig *il_config, - RZ_NULLABLE const RzILRegBinding *reg_bindings); + RZ_BORROW RZ_NONNULL RzAnalysisILContext *il_context); RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpAbstrState *state); RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_clone(RZ_NONNULL RzInterpSet *iset, const RzInterpAbstrState *state); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index b75e93f6a00..23045731fc1 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -196,14 +196,14 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(RZ_NONNULL const RzAnalysisXRef * return false; } -static void handle_io_request(RzPVector /**/ *il_mems, RzInterpIORequest *io_req, RZ_OUT RzInterpIOResult *io_res) { +static void handle_io_request(RzAnalysisILContext *il_ctx, RzInterpIORequest *io_req, RZ_OUT RzInterpIOResult *io_res) { RZ_LOG_DEBUG("inquiry: Received IO %s request: mem:%" PFMTSZd " 0x%" PFMT64x "\n", io_req->type == RZ_INTERP_IO_WRITE ? "write" : "read", io_req->mem_idx, rz_bv_to_ut64(io_req->addr)); io_res->req_ok = false; RzILMemIndex mem_idx = io_req->mem_idx; - if (rz_pvector_empty(il_mems) || rz_pvector_len(il_mems) <= mem_idx) { + if (mem_idx <= rz_vector_len(&il_ctx->memory)) { rz_warn_if_reached(); return; } @@ -212,11 +212,14 @@ static void handle_io_request(RzPVector /**/ *il_mems, RzInterpIORequ "63 bit set can't be addresses.\n"); return; } - RzILMem *mem = rz_pvector_at(il_mems, mem_idx); - if (io_req->type == RZ_INTERP_IO_READ) { - io_res->req_ok = rz_il_mem_loadw_into(mem, io_req->ld_data, io_req->addr, io_req->n_bits, io_req->big_endian); + RzAnalysisILMem *mem = rz_vector_index_ptr(&il_ctx->memory, mem_idx); + if (!mem->base_buf) { + io_res->req_ok = false; + } else if (io_req->type == RZ_INTERP_IO_READ) { + io_res->req_ok = rz_il_loadw_into(mem->base_buf, io_req->ld_data, io_req->addr, io_req->n_bits, io_req->big_endian); } else { - io_res->req_ok = rz_il_mem_storew(mem, io_req->addr, io_req->st_data, io_req->big_endian); + // TODO: stores from the interpreter should not be supported at all! + io_res->req_ok = rz_il_storew(mem->base_buf, io_req->addr, io_req->st_data, io_req->big_endian); } RZ_LOG_DEBUG("inquiry: Sent IO %s result. Success = %s.\n", io_req->type == RZ_INTERP_IO_WRITE ? "write" : "read", @@ -667,7 +670,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, goto fatal_error; } else if (r == RZ_THREAD_RING_BUF_OK) { RzInterpIOResult io_res = { 0 }; - handle_io_request(&iset->il_vm->vm->vm_memory, &io_req, &io_res); + handle_io_request(iset->il_ctx, &io_req, &io_res); if (rz_th_ring_buf_put(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { rz_warn_if_reached(); goto fatal_error; diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 9c205229762..2587ff144a9 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -78,9 +78,8 @@ RZ_API RZ_OWN RzInterpYieldRBuf *rz_interpreter_yield_rbuf_new(RzInterpYieldKind RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_new( const char *arch_name, RzInterpAbstraction kinds, - RZ_BORROW RZ_NONNULL RzAnalysisILConfig *il_config, - RZ_NULLABLE const RzILRegBinding *reg_bindings) { - rz_return_val_if_fail(il_config && reg_bindings, NULL); + RZ_BORROW RZ_NONNULL RzAnalysisILContext *il_context) { + rz_return_val_if_fail(il_context, NULL); RzInterpAbstrState *state = RZ_NEW0(RzInterpAbstrState); if (!state) { return NULL; @@ -90,8 +89,8 @@ RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_new( // Initialize the register file with uninitialized abstract values. state->var_name_hashes = ht_up_new(NULL, free); state->globals = ht_up_new(NULL, free); - for (size_t i = 0; i < reg_bindings->regs_count; i++) { - const char *rname = reg_bindings->regs[i].name; + for (size_t i = 0; i < il_context->reg_binding->regs_count; i++) { + const char *rname = il_context->reg_binding->regs[i].name; RzInterpAbstrVal *aval = RZ_NEW0(RzInterpAbstrVal); if (!aval) { ht_up_free(state->globals); @@ -114,7 +113,6 @@ RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_new( } state->locals = ht_up_new(NULL, free); state->lets = ht_up_new(NULL, free); - state->il_config = il_config; return state; } @@ -178,7 +176,6 @@ RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_clone(RZ_NONNULL Rz r->globals = var_set_clone(iset, state->globals); r->locals = var_set_clone(iset, state->locals); r->lets = var_set_clone(iset, state->lets); - r->il_config = state->il_config; return r; } @@ -204,9 +201,7 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpSet *iset) { if (iset->run_state) { rz_interp_run_state_free(iset->run_state); } - if (iset->il_vm) { - rz_analysis_il_vm_free(iset->il_vm); - } + rz_analysis_il_context_free(iset->il_ctx); free(iset); } @@ -261,35 +256,18 @@ RZ_API RZ_OWN RzInterpSet *rz_interpreter_set_new( return NULL; } - // Perform the RzAnalysisILVM and abstract state setup procedure. - // This prototype won't use the RzAnalysisILVM directly but its components. - // That is because the prototypes doesn't handle the VM tasks (track PC, handle IO) - // in one VM object, but in separated modules. - // So analysis_vm->vm->vm_memorys is used for handling IO requests and - // analysis_vm->reg_binding is used for the abstract state setup. - // - // TODO: Is it a good idea to separate these tasks into different modules? - // It allows the IO handler to buffer reads in r-- sections for multiple interpreters. - // Possibly allows to optimize the IO access, because there is only module accessing it (not every interpreter). - // But is there any other advantage? - RzAnalysisILVM *il_vm = rz_analysis_il_vm_new(analysis, rz_analysis_get_reg(analysis)); - if (!il_vm) { + RzAnalysisILContext *il_ctx = rz_analysis_il_context_resolve(analysis); + if (!il_ctx) { free(iset); - RZ_LOG_ERROR("Failed during RzAnalysisILVM setup.\n"); + RZ_LOG_ERROR("Failed to create analysis IL context.\n"); return NULL; } const RzAnalysisPlugin *cur = rz_analysis_plugin_current(analysis); - RzAnalysisILConfig *config = cur->il_config(analysis); - RzInterpAbstrState *state = rz_interpreter_abstr_state_new( - cur->arch, - abstraction, - config, - il_vm->reg_binding); + RzInterpAbstrState *state = rz_interpreter_abstr_state_new(cur->arch, abstraction, il_ctx); if (!state) { free(iset); - rz_analysis_il_vm_free(il_vm); - rz_analysis_il_config_free(config); + rz_analysis_il_context_free(il_ctx); return NULL; } @@ -298,7 +276,7 @@ RZ_API RZ_OWN RzInterpSet *rz_interpreter_set_new( RzThreadRingBuf *entry_points = NULL; if (!setup_ipc_objects(&io_request_rbuf, &io_result_rbuf, &entry_points)) { free(iset); - rz_analysis_il_vm_free(il_vm); + rz_analysis_il_context_free(il_ctx); rz_interpreter_abstr_state_free(state); return NULL; } @@ -307,7 +285,7 @@ RZ_API RZ_OWN RzInterpSet *rz_interpreter_set_new( iset->plugin = plugin; iset->astate = state; iset->run_state = rz_interp_run_state_new(); - iset->il_vm = il_vm; + iset->il_ctx = il_ctx; iset->il_cache_client = il_cache_client; iset->entry_points = entry_points; iset->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] = yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; @@ -416,12 +394,7 @@ INIT: { rz_interp_run_state_set(iset->run_state, RZ_INTERP_RUN_STATE_INIT); const RzAnalysisPlugin *cur = rz_analysis_plugin_current(iset->a); - RzAnalysisILConfig *config = cur->il_config(iset->a); - RzInterpAbstrState *estate = rz_interpreter_abstr_state_new( - cur->arch, - RZ_INTERP_ABSTRACTION_CONST, - config, - iset->il_vm->reg_binding); + RzInterpAbstrState *estate = rz_interpreter_abstr_state_new(cur->arch, RZ_INTERP_ABSTRACTION_CONST, iset->il_ctx); rz_list_purge(iset->fcn_state.queue); ht_up_clear(iset->fcn_state.pc_states); diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 88ce9aaf18f..7d8e15edd5e 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -136,7 +136,7 @@ static bool init_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { // TODO: Really a good idea to be so liberal? // Or should the length of the globals be enforced? // The bitvector arithmetic does enforce the length. - AD(av->abstr_data)->bv = rz_bv_new(state->il_config->mem_key_size); + AD(av->abstr_data)->bv = rz_bv_new(64); // TODO: This is debatable. It depends on the ABI what the default values are. // Some values must be concrete, otherwise the interpretation of the prototype end too early. AD(av->abstr_data)->is_const = false; diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index dd4ef34ee4a..9279d8d2916 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -178,7 +178,7 @@ bool store_abstr_data( RzInterpIORequest io_req = { 0 }; io_req.n_bits = rz_bv_len(src->bv); io_req.mem_idx = mem_idx; - io_req.big_endian = iset->astate->il_config->big_endian; + io_req.big_endian = iset->il_ctx->config->big_endian; io_req.type = RZ_INTERP_IO_WRITE; io_req.addr = addr->bv; @@ -212,7 +212,7 @@ bool load_abstr_data( io_req.ld_data = out->bv; io_req.mem_idx = mem_idx; io_req.n_bits = n_bits; - io_req.big_endian = iset->astate->il_config->big_endian; + io_req.big_endian = iset->il_ctx->config->big_endian; if (rz_th_ring_buf_put(iset->io_request_rbuf, &io_req) != RZ_THREAD_RING_BUF_OK) { return false; } diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index ae310ed94ff..546da06448c 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -432,7 +432,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( } report_yield_xref(iset, 0, iset->astate->pc, &ld_addr, RZ_ANALYSIS_XREF_TYPE_MEM_READ); - size_t n_bits = pure->code == RZ_IL_OP_LOAD ? iset->astate->il_config->mem_key_size : pure->op.loadw.n_bits; + size_t n_bits = pure->code == RZ_IL_OP_LOAD ? iset->il_ctx->config->mem_key_size : pure->op.loadw.n_bits; if (!load_abstr_data(iset, mem_idx, &ld_addr, n_bits, out)) { rz_bv_fini(ld_addr.bv); goto map_to_top; From 9708145f382895270650306fe431124e37ef327e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Mon, 29 Jun 2026 17:10:52 +0200 Subject: [PATCH 300/334] Remove io writes from interpreter --- librz/include/rz_inquiry/rz_interpreter.h | 8 +----- librz/inquiry/inquiry.c | 22 +++++---------- librz/inquiry/interpreter/interpreter.c | 7 +---- librz/inquiry/interpreter/prototype/eval.c | 31 +++------------------- 4 files changed, 11 insertions(+), 57 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 014d6d53363..d1128a02b71 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -255,20 +255,14 @@ typedef struct { void *plugin_data); } RzInterpPlugin; -typedef enum { - RZ_INTERP_IO_READ, - RZ_INTERP_IO_WRITE, -} RzInterpIOReqType; - typedef struct { - RzInterpIOReqType type; size_t mem_idx; ///< The memory space to read/write. bool big_endian; ///< Set if the data is big endian ordered. const RzBitVector *addr; ///< The address to read/write. const RzBitVector *st_data; ///< The data to store. RzBitVector *ld_data; ///< The bit vector to load into. It is BORROWED. size_t n_bits; ///< The number of bits to read/write. -} RzInterpIORequest; +} RzInterpIOReadRequest; typedef struct { bool req_ok; ///< Set to true if IO request succeeded. diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 23045731fc1..6e7a67a27d1 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -196,9 +196,8 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(RZ_NONNULL const RzAnalysisXRef * return false; } -static void handle_io_request(RzAnalysisILContext *il_ctx, RzInterpIORequest *io_req, RZ_OUT RzInterpIOResult *io_res) { - RZ_LOG_DEBUG("inquiry: Received IO %s request: mem:%" PFMTSZd " 0x%" PFMT64x "\n", - io_req->type == RZ_INTERP_IO_WRITE ? "write" : "read", +static void handle_io_request(RzAnalysisILContext *il_ctx, RzInterpIOReadRequest *io_req, RZ_OUT RzInterpIOResult *io_res) { + RZ_LOG_DEBUG("inquiry: Received IO read request: mem:%" PFMTSZd " 0x%" PFMT64x "\n", io_req->mem_idx, rz_bv_to_ut64(io_req->addr)); io_res->req_ok = false; @@ -215,14 +214,11 @@ static void handle_io_request(RzAnalysisILContext *il_ctx, RzInterpIORequest *io RzAnalysisILMem *mem = rz_vector_index_ptr(&il_ctx->memory, mem_idx); if (!mem->base_buf) { io_res->req_ok = false; - } else if (io_req->type == RZ_INTERP_IO_READ) { - io_res->req_ok = rz_il_loadw_into(mem->base_buf, io_req->ld_data, io_req->addr, io_req->n_bits, io_req->big_endian); } else { - // TODO: stores from the interpreter should not be supported at all! - io_res->req_ok = rz_il_storew(mem->base_buf, io_req->addr, io_req->st_data, io_req->big_endian); + // TODO: here only memory should be read that can be assumed to be constant! + io_res->req_ok = rz_il_loadw_into(mem->base_buf, io_req->ld_data, io_req->addr, io_req->n_bits, io_req->big_endian); } - RZ_LOG_DEBUG("inquiry: Sent IO %s result. Success = %s.\n", - io_req->type == RZ_INTERP_IO_WRITE ? "write" : "read", + RZ_LOG_DEBUG("inquiry: Sent IO read result. Success = %s.\n", rz_str_bool(io_res->req_ok)); } @@ -523,10 +519,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, goto error_free; } - RZ_LOG_DEBUG("inquiry: Enforce enabling IO cache.\n"); - const char *io_cache_opt = rz_config_get(core->config, "io.cache"); - rz_config_set(core->config, "io.cache", "true"); - // Bundle all the queues into one object to pass it to the thread. // Later we would pass a unique iset to each interpreter with // the required queues only. @@ -662,7 +654,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // But this requires multiple IO write caches // (one for each interpreter instance). // Because this is not yet implemented, there is only one interpreter thread for now. - RzInterpIORequest io_req = { 0 }; + RzInterpIOReadRequest io_req = { 0 }; if (!rz_th_ring_buf_is_empty_unsafe(iset->io_request_rbuf)) { RzThreadRingBufResult r = rz_th_ring_buf_take(iset->io_request_rbuf, &io_req); if (r == RZ_THREAD_RING_BUF_CLOSED) { @@ -780,8 +772,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_LOG_DEBUG("inquiry: inquiry: inquiry: Done\n"); - rz_config_set(core->config, "io.cache", io_cache_opt); - error_free: rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]); rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 2587ff144a9..3a3c8b3124e 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -216,7 +216,7 @@ static bool setup_ipc_objects( // Setup the IO queues. Each interpreter instance needs it's own queue at // for writing IO. Because the writing is done on the IO cache, and each // instance needs its own cache. - *io_request_rbuf = rz_th_ring_buf_new(RZ_INTERP_IO_RBUF_SIZE, sizeof(RzInterpIORequest)); + *io_request_rbuf = rz_th_ring_buf_new(RZ_INTERP_IO_RBUF_SIZE, sizeof(RzInterpIOReadRequest)); *io_result_rbuf = rz_th_ring_buf_new(RZ_INTERP_IO_RBUF_SIZE, sizeof(RzInterpIOResult)); *entry_points = rz_th_ring_buf_new(RZ_INTERP_ENTRY_POINTS_RBUF_SIZE, sizeof(ut64)); if (!*io_request_rbuf || !*io_result_rbuf || !*entry_points) { @@ -342,11 +342,6 @@ RZ_API RZ_NULLABLE RZ_BORROW RzInterpAbstrState *rz_interp_set_pop(RZ_BORROW RZ_ return r; } -typedef struct { - RzInterpCtrlFlow ctrl_flow; - ut64 in_state_hash; -} SuccessorState; - /** * Main interpretation. */ diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 9279d8d2916..5394529c9bd 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -171,32 +171,8 @@ bool store_abstr_data( RzILMemIndex mem_idx, const ProtoIntrprAbstrData *addr, const ProtoIntrprAbstrData *src) { - if (!src->is_const) { - // Really don't write? - return true; - } - RzInterpIORequest io_req = { 0 }; - io_req.n_bits = rz_bv_len(src->bv); - io_req.mem_idx = mem_idx; - io_req.big_endian = iset->il_ctx->config->big_endian; - - io_req.type = RZ_INTERP_IO_WRITE; - io_req.addr = addr->bv; - io_req.st_data = src->bv; - - char *bytes = rz_bv_as_hex_string(src->bv, true); - RZ_LOG_DEBUG("pprototype: ototype: STORE @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); - free(bytes); - - if (rz_th_ring_buf_put(iset->io_request_rbuf, &io_req) != RZ_THREAD_RING_BUF_OK) { - return false; - } - - RzInterpIOResult io_res = { 0 }; - if (rz_th_ring_buf_take_blocking(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { - return false; - } - return io_res.req_ok; + // TODO: handle with memory abstractions + return true; } bool load_abstr_data( @@ -205,9 +181,8 @@ bool load_abstr_data( const ProtoIntrprAbstrData *addr, size_t n_bits, RZ_OUT ProtoIntrprAbstrData *out) { - RzInterpIORequest io_req = { 0 }; + RzInterpIOReadRequest io_req = { 0 }; rz_bv_cast_inplace(out->bv, n_bits, 0); - io_req.type = RZ_INTERP_IO_READ; io_req.addr = addr->bv; io_req.ld_data = out->bv; io_req.mem_idx = mem_idx; From e0da8f361b9404f386a465aeb4a8dd517177bee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Tue, 30 Jun 2026 15:04:57 +0200 Subject: [PATCH 301/334] Split RzInterpSet into RzInterpInstance and RzInterpRunContext --- librz/include/rz_inquiry/rz_interpreter.h | 48 ++-- librz/inquiry/inquiry.c | 30 +-- librz/inquiry/interpreter/interpreter.c | 251 +++++++++--------- .../interpreter/p/interpreter_prototype.c | 20 +- librz/inquiry/interpreter/prototype/eval.c | 36 +-- librz/inquiry/interpreter/prototype/eval.h | 24 +- .../interpreter/prototype/eval_effect.c | 78 +++--- .../inquiry/interpreter/prototype/eval_pure.c | 86 +++--- 8 files changed, 289 insertions(+), 284 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index d1128a02b71..a2851a77fee 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -25,6 +25,7 @@ #define RZ_INTERP_ENTRY_POINTS_RBUF_SIZE 4 typedef struct rz_interp_run_state RzInterpRunState; +typedef struct rz_interp_run_context_t RzInterpRunContext; typedef enum rz_interp_state_flag { /** @@ -166,7 +167,7 @@ typedef struct { RzThreadRingBuf *rbuf; } RzInterpYieldRBuf; -typedef struct rz_interpreter_set RzInterpSet; +typedef struct rz_interp_instance_t RzInterpInstance; typedef struct { const char *name; @@ -216,7 +217,7 @@ typedef struct { /** * \brief Evaluates an effect with the mutable state. */ - bool (*eval)(RZ_NONNULL RzInterpSet *iset, + bool (*eval)(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzILCacheBlock *il_bb, void *plugin_data); /** @@ -268,23 +269,13 @@ typedef struct { bool req_ok; ///< Set to true if IO request succeeded. } RzInterpIOResult; -typedef struct { - RzList /**/ *queue; ///< States that have to be interpreted still. If this is empty, a fixpoint has been reached. - HtUP /**/ *pc_states; ///< Currently discovered states at the entries of blocks. -} RzInterpFunctionState; - /** - * \brief The set of required objects for an interpreter to run. + * \brief Root local data of a single interpreter thread */ RZ_LIFETIME(RzInquiry) -struct rz_interpreter_set { +struct rz_interp_instance_t { RzAnalysis *a; ///< TODO: remove - // TODO: Move this one into each plugin? - RzInterpAbstrState *astate; ///< The abstract state of the interpreter. - - RzInterpFunctionState fcn_state; - RzInterpRunState *run_state; ///< The state the interpreter is currently in. /** * \brief The semaphore to sync RzInquiry and the interpreter between the Clean and Init run state. @@ -331,27 +322,38 @@ RZ_API const char *rz_interp_run_state_flag_str(RzInterpRunStateFlag flag); RZ_IPI void rz_interp_run_state_set(RZ_BORROW RZ_NONNULL RzInterpRunState *state, RzInterpRunStateFlag flag); -RZ_API void rz_interpreter_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yield_rbuf); +RZ_API void rz_interp_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yield_rbuf); -RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_new( +RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_new( const char *arch_name, RzInterpAbstraction kinds, RZ_BORROW RZ_NONNULL RzAnalysisILContext *il_context); -RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpAbstrState *state); -RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_clone(RZ_NONNULL RzInterpSet *iset, const RzInterpAbstrState *state); +RZ_API void rz_interp_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpAbstrState *state); +RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_clone(RZ_NONNULL RzInterpInstance *iset, const RzInterpAbstrState *state); -RZ_API RZ_OWN RzInterpYieldRBuf *rz_interpreter_yield_rbuf_new(RzInterpYieldKind kind, +RZ_API RZ_OWN RzInterpYieldRBuf *rz_interp_yield_rbuf_new(RzInterpYieldKind kind, RzInterpYieldFilter filter, RZ_OWN RZ_NULLABLE void *filter_data); -RZ_API RZ_OWN RzInterpSet *rz_interpreter_set_new( +RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( RzAnalysis *analysis, RZ_NONNULL RZ_OWN RzInterpPlugin *plugin, RzInterpAbstraction abstraction, RZ_NONNULL RZ_BORROW RzILCacheClient *il_cache_client, RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM], RZ_NONNULL const RzVector /**/ *ignored_code); -RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpSet *iset); +RZ_API void rz_interp_instance_free(RZ_OWN RZ_NULLABLE RzInterpInstance *iset); + +/** + * \brief Local data during an interpreter run + */ +struct rz_interp_run_context_t { + RzInterpInstance *inst; //< parent interpreter thread + + RzInterpAbstrState *astate; ///< The abstract state of the interpreter. + RzList /**/ *queue; ///< States that have to be interpreted still. If this is empty, a fixpoint has been reached. + HtUP /**/ *pc_states; ///< Currently discovered states at the entries of blocks. +}; /* * \brief Register a newly discovered state @@ -359,8 +361,8 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpSet *iset); * This will join the state with the already known one at the same pc and add it to the * queue for further interpretation if there were changes. */ -RZ_API void rz_interp_set_push(RZ_BORROW RZ_NONNULL RzInterpSet *iset, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as); +RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as); -RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpSet *iset); +RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *iset); #endif // RZ_INTERPRETER diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 6e7a67a27d1..500598e18db 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -357,7 +357,7 @@ static bool handle_yields(RzInquiry *inquiry, RzInterpYieldRBuf *yield_rbufs[RZ_ */ // TODO: Optimize static bool reduce_get_entry_points( - RzInterpSet *iset, + RzInterpInstance *iset, RzILCache *il_cache, RZ_BORROW RzSetU /**/ *entry_points) { // Add the next entry point we need to check for executable regions the interpreters did not cover. @@ -390,7 +390,7 @@ static bool reduce_get_entry_points( return true; } -static void close_reset_ipc_obj(RzInterpSet *iset) { +static void close_reset_ipc_obj(RzInterpInstance *iset) { // Close and clear all the IPC objects of this interpreter. // This also clears the buffer and queues rz_th_ring_buf_close(iset->io_request_rbuf); @@ -401,7 +401,7 @@ static void close_reset_ipc_obj(RzInterpSet *iset) { rz_list_free(rz_th_queue_pop_all(iset->il_cache_client->il_queue)); } -static void open_ipc_obj(RzInterpSet *iset) { +static void open_ipc_obj(RzInterpInstance *iset) { // Open queue again, so the interpretation can start at another // jump target again. rz_th_ring_buf_open(iset->io_request_rbuf); @@ -443,7 +443,7 @@ static bool setup_yield_rbufs( RzInterpYieldKind yield_kind = RZ_INTERP_YIELD_KIND_CALL_CANDIDATE; RzInterpYieldRBuf *rbuf = NULL; - rbuf = rz_interpreter_yield_rbuf_new(yield_kind, NULL, NULL); + rbuf = rz_interp_yield_rbuf_new(yield_kind, NULL, NULL); if (!rbuf) { rz_warn_if_reached(); return false; @@ -452,7 +452,7 @@ static bool setup_yield_rbufs( yield_kind = RZ_INTERP_YIELD_KIND_CONTROL_FLOW; rbuf = NULL; - rbuf = rz_interpreter_yield_rbuf_new(yield_kind, NULL, NULL); + rbuf = rz_interp_yield_rbuf_new(yield_kind, NULL, NULL); if (!rbuf) { rz_warn_if_reached(); return false; @@ -460,7 +460,7 @@ static bool setup_yield_rbufs( yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] = rbuf; yield_kind = RZ_INTERP_YIELD_KIND_XREF; - rbuf = rz_interpreter_yield_rbuf_new( + rbuf = rz_interp_yield_rbuf_new( yield_kind, yield_filter, sections); @@ -474,7 +474,7 @@ static bool setup_yield_rbufs( struct ituple { RzThread *ithread; - RzInterpSet *iset; + RzInterpInstance *iset; RzInterpRunStateFlag next_run_state; }; @@ -487,7 +487,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_NONNULL const RzVector /**/ *ignored_code) { // All the things we need bool return_code = true; - RzInterpSet *intp_iset = NULL; + RzInterpInstance *intp_iset = NULL; RzBuffer *io_buf = rz_buf_new_with_io(rz_analysis_get_io_bind(core->analysis)); RzSetU *symbol_targets = rz_set_u_new(); @@ -550,7 +550,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_warn_if_reached(); goto error_free; } - intp_iset = rz_interpreter_set_new( + intp_iset = rz_interp_instance_new( core->analysis, prototype->p_interpreter, RZ_INTERP_ABSTRACTION_CONST, @@ -565,7 +565,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // Dispatch prototype interpreter into a thread. RZ_LOG_DEBUG("inquiry: Start main interpretation thread.\n"); - RzThread *interpr_th = rz_th_new((RzThreadFunction)rz_interpreter_run, intp_iset); + RzThread *interpr_th = rz_th_new((RzThreadFunction)rz_interp_instance_th, intp_iset); iset_map[i].ithread = interpr_th; iset_map[i].iset = intp_iset; iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_INIT; @@ -588,7 +588,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, user_sent_signal = true; break; } - RzInterpSet *iset = iset_map[i].iset; + RzInterpInstance *iset = iset_map[i].iset; RzInterpRunStateFlag expected_rs = iset_map[i].next_run_state; switch (rz_interp_run_state_get_unsafe(iset->run_state)) { @@ -733,7 +733,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_LOG_ERROR("User sent signal.\n"); } } - rz_interpreter_set_free(iset_map[i].iset); + rz_interp_instance_free(iset_map[i].iset); } rz_il_cache_stop_serving(il_cache); @@ -773,9 +773,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_LOG_DEBUG("inquiry: inquiry: inquiry: Done\n"); error_free: - rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]); - rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]); - rz_interpreter_yield_rbuf_free(yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW]); + rz_interp_yield_rbuf_free(yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]); + rz_interp_yield_rbuf_free(yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]); + rz_interp_yield_rbuf_free(yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW]); free(iset_map); rz_set_u_free(entry_points); rz_buf_free(io_buf); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 3a3c8b3124e..520c7e1b97d 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -17,7 +17,7 @@ #include "inquiry/interpreter/prototype/eval.h" -RZ_API void rz_interpreter_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yield_rbufs) { +RZ_API void rz_interp_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yield_rbufs) { if (!yield_rbufs) { return; } @@ -31,7 +31,7 @@ RZ_API void rz_interpreter_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf free(yield_rbufs); } -RZ_API RZ_OWN RzInterpYieldRBuf *rz_interpreter_yield_rbuf_new(RzInterpYieldKind kind, +RZ_API RZ_OWN RzInterpYieldRBuf *rz_interp_yield_rbuf_new(RzInterpYieldKind kind, RzInterpYieldFilter filter, RZ_OWN RZ_NULLABLE void *filter_data) { RzInterpYieldRBuf *yield_rbufs = RZ_NEW0(RzInterpYieldRBuf); @@ -75,7 +75,7 @@ RZ_API RZ_OWN RzInterpYieldRBuf *rz_interpreter_yield_rbuf_new(RzInterpYieldKind * \brief Initializes an abstract state for specified abstract kinds. Optionally with a list of registers. * The register name list should always be given if the architecture has some. */ -RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_new( +RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_new( const char *arch_name, RzInterpAbstraction kinds, RZ_BORROW RZ_NONNULL RzAnalysisILContext *il_context) { @@ -116,7 +116,7 @@ RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_new( return state; } -RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpAbstrState *state) { +RZ_API void rz_interp_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpAbstrState *state) { if (!state) { return; } @@ -135,7 +135,7 @@ RZ_API void rz_interpreter_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpAbstrStat free(state); } -static HtUP *var_set_clone(const RzInterpSet *iset, HtUP *vars) { +static HtUP *var_set_clone(const RzInterpInstance *iset, HtUP *vars) { HtUP *r = ht_up_new(NULL, free); if (!r) { return NULL; @@ -152,7 +152,7 @@ static HtUP *var_set_clone(const RzInterpSet *iset, HtUP *vars) { return r; } -RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_clone(RZ_NONNULL RzInterpSet *iset, const RzInterpAbstrState *state) { +RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_clone(RZ_NONNULL RzInterpInstance *iset, const RzInterpAbstrState *state) { RzInterpAbstrState *r = RZ_NEW0(RzInterpAbstrState); if (!state) { return NULL; @@ -179,7 +179,7 @@ RZ_API RZ_OWN RzInterpAbstrState *rz_interpreter_abstr_state_clone(RZ_NONNULL Rz return r; } -RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpSet *iset) { +RZ_API void rz_interp_instance_free(RZ_OWN RZ_NULLABLE RzInterpInstance *iset) { if (!iset) { return; } @@ -195,9 +195,6 @@ RZ_API void rz_interpreter_set_free(RZ_OWN RZ_NULLABLE RzInterpSet *iset) { if (iset->run_state_sync) { rz_th_sem_free(iset->run_state_sync); } - if (iset->astate) { - rz_interpreter_abstr_state_free(iset->astate); - } if (iset->run_state) { rz_interp_run_state_free(iset->run_state); } @@ -237,7 +234,7 @@ static bool setup_ipc_objects( * \brief Initializes a new RzInterpSet and returns it. * If it fails, all arguments are freed. */ -RZ_API RZ_OWN RzInterpSet *rz_interpreter_set_new( +RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( RzAnalysis *analysis, RZ_NONNULL RZ_OWN RzInterpPlugin *plugin, RzInterpAbstraction abstraction, @@ -251,7 +248,7 @@ RZ_API RZ_OWN RzInterpSet *rz_interpreter_set_new( return NULL; } - RzInterpSet *iset = RZ_NEW0(RzInterpSet); + RzInterpInstance *iset = RZ_NEW0(RzInterpInstance); if (!iset) { return NULL; } @@ -263,27 +260,17 @@ RZ_API RZ_OWN RzInterpSet *rz_interpreter_set_new( return NULL; } - const RzAnalysisPlugin *cur = rz_analysis_plugin_current(analysis); - RzInterpAbstrState *state = rz_interpreter_abstr_state_new(cur->arch, abstraction, il_ctx); - if (!state) { - free(iset); - rz_analysis_il_context_free(il_ctx); - return NULL; - } - RzThreadRingBuf *io_request_rbuf = NULL; RzThreadRingBuf *io_result_rbuf = NULL; RzThreadRingBuf *entry_points = NULL; if (!setup_ipc_objects(&io_request_rbuf, &io_result_rbuf, &entry_points)) { free(iset); rz_analysis_il_context_free(il_ctx); - rz_interpreter_abstr_state_free(state); return NULL; } iset->a = analysis; iset->plugin = plugin; - iset->astate = state; iset->run_state = rz_interp_run_state_new(); iset->il_ctx = il_ctx; iset->il_cache_client = il_cache_client; @@ -296,13 +283,10 @@ RZ_API RZ_OWN RzInterpSet *rz_interpreter_set_new( iset->run_state_sync = rz_th_sem_new(0); iset->ignored_code = ignored_code; - iset->fcn_state.pc_states = ht_up_new(NULL, free); - iset->fcn_state.queue = rz_list_new(); - return iset; } -RZ_API void rz_interp_set_push(RZ_BORROW RZ_NONNULL RzInterpSet *iset, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as) { +RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as) { if (as->pc_state == RZ_INTERP_PC_ANY) { RZ_LOG_DEBUG("Encountered state with unknown/top pc\n"); return; @@ -313,28 +297,28 @@ RZ_API void rz_interp_set_push(RZ_BORROW RZ_NONNULL RzInterpSet *iset, RZ_BORROW } RzStrBuf sb; rz_strbuf_init(&sb); - state_as_str_short(iset, &sb, as); + state_as_str_short(ctx->inst, &sb, as); RZ_LOG_DEBUG("PUSH 0x%" PFMT64x ": %s\n", as->pc, rz_strbuf_get(&sb)); rz_strbuf_fini(&sb); - RzInterpAbstrState *existing = ht_up_find(iset->fcn_state.pc_states, as->pc, NULL); + RzInterpAbstrState *existing = ht_up_find(ctx->pc_states, as->pc, NULL); if (existing) { - if (iset->plugin->join_state(existing, as, iset->interp_priv) && !existing->uninterpreted) { + if (ctx->inst->plugin->join_state(existing, as, ctx->inst->interp_priv) && !existing->uninterpreted) { existing->uninterpreted = true; - rz_list_push(iset->fcn_state.queue, existing); + rz_list_push(ctx->queue, existing); } } else { - RzInterpAbstrState *c = rz_interpreter_abstr_state_clone(iset, as); + RzInterpAbstrState *c = rz_interp_abstr_state_clone(ctx->inst, as); if (!c) { return; } - ht_up_insert(iset->fcn_state.pc_states, as->pc, c); + ht_up_insert(ctx->pc_states, as->pc, c); c->uninterpreted = true; - rz_list_push(iset->fcn_state.queue, c); + rz_list_push(ctx->queue, c); } } -RZ_API RZ_NULLABLE RZ_BORROW RzInterpAbstrState *rz_interp_set_pop(RZ_BORROW RZ_NONNULL RzInterpSet *iset) { - RzInterpAbstrState *r = rz_list_pop(iset->fcn_state.queue); +static RzInterpAbstrState *rz_interp_run_pop(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx) { + RzInterpAbstrState *r = rz_list_pop(ctx->queue); if (!r) { return NULL; } @@ -343,39 +327,109 @@ RZ_API RZ_NULLABLE RZ_BORROW RzInterpAbstrState *rz_interp_set_pop(RZ_BORROW RZ_ } /** - * Main interpretation. + * \brief Run the interpreter from a single entrypoint until a fixpoint is reached + */ +static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { + // Initialization + bool success = false; + RzInterpRunContext ctx = { + .inst = inst, + .astate = NULL, + .queue = rz_list_new(), + .pc_states = ht_up_new(NULL, free) + }; + if (!ctx.queue || !ctx.pc_states) { + goto cleanup; + } + + // Prepare the initial state from the given entry point + // Hint: nothing speaks against supporting multiple entry points in a single run + const RzAnalysisPlugin *cur = rz_analysis_plugin_current(inst->a); + RzInterpAbstrState *estate = rz_interp_abstr_state_new(cur->arch, RZ_INTERP_ABSTRACTION_CONST, inst->il_ctx); + if (inst->plugin->reset) { + // TODO: should rather be local to the RzInterpRunContext if it is reset every run + inst->plugin->reset(inst->interp_priv); + } + if (!inst->plugin->init_state(estate, inst->interp_priv) || !inst->plugin->reset_state(estate, entry_point, inst->interp_priv)) { + rz_warn_if_reached(); + rz_interp_abstr_state_free(estate); + goto cleanup; + } + rz_interp_run_push(&ctx, estate); + rz_interp_abstr_state_free(estate); + + // Loop and interpret until a fixpoint has been reached + while (true) { + RzInterpAbstrState *next = rz_interp_run_pop(&ctx); + if (!next) { + // No uninterpreted states left, fixpoint reached. + success = true; + break; + } + ctx.astate = rz_interp_abstr_state_clone(inst, next); + + const RzILCacheBlock *il_bb = rz_il_cache_client_lift_il_block(inst->il_cache_client, ctx.astate->pc); + if (!il_bb) { + // Lifting failed, TODO: handle this better + break; + } + + // DEBUG comments + RzStrBuf sb; + rz_strbuf_init(&sb); + const char *old_cmt = rz_meta_get_string(inst->a, RZ_META_TYPE_COMMENT, il_bb->addr); + if (old_cmt) { + rz_strbuf_appendf(&sb, "%s; ", old_cmt); + } + rz_strbuf_append(&sb, "ENTRY "); + state_as_str_short(inst, &sb, ctx.astate); + // rz_meta_set_string(iset->a, RZ_META_TYPE_COMMENT, il_bb->addr, rz_strbuf_get(&sb)); + rz_strbuf_fini(&sb); + + ctx.astate->bb_addr = il_bb->addr; + ctx.astate->bb_size = il_bb->size; + // Evaluate the effect on the abstract state. + if (!inst->plugin->eval(&ctx, il_bb, inst->interp_priv)) { + RZ_LOG_DEBUG("interpreter: Eval failed\n"); + success = false; + break; + } + } + +cleanup: + rz_list_free(ctx.queue); + ht_up_free(ctx.pc_states); + return success; +} + +/** + * \brief Interpreter thread */ -RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpSet *iset) { - rz_return_val_if_fail(iset && - iset->astate && - iset->il_cache_client && - iset->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] && - iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] && - iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] && - iset->run_state_sync && - iset->plugin && - iset->plugin->eval && - iset->plugin->successors && - iset->plugin->init_state && - iset->plugin->fini_state && - iset->plugin->hash_state, +RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *inst) { + rz_return_val_if_fail(inst && + inst->il_cache_client && + inst->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] && + inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] && + inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] && + inst->run_state_sync && + inst->plugin && + inst->plugin->eval && + inst->plugin->successors && + inst->plugin->init_state && + inst->plugin->fini_state && + inst->plugin->hash_state, false); bool success = true; RZ_LOG_DEBUG("interpreter: Main: Hello.\n"); - RzInterpPlugin *plugin = iset->plugin; // // Start interpretation // - if (iset->plugin->init) { - iset->plugin->init(&iset->interp_priv); - } - if (!iset->plugin->init_state(iset->astate, iset->interp_priv)) { - rz_warn_if_reached(); - return false; + if (inst->plugin->init) { + inst->plugin->init(&inst->interp_priv); } // TODO: It is probably better to make the following stuff while-loops. @@ -383,97 +437,46 @@ RZ_API bool rz_interpreter_run(RZ_NONNULL RZ_OWN RzInterpSet *iset) { // But while debugging and developing, I keep it this way to separate clearly // what the interpreter does in each state. -INIT: { +INIT: // prepare state at the procedure entrypoint RZ_LOG_DEBUG("interpreter: Enter INIT\n"); - rz_interp_run_state_set(iset->run_state, RZ_INTERP_RUN_STATE_INIT); - - const RzAnalysisPlugin *cur = rz_analysis_plugin_current(iset->a); - RzInterpAbstrState *estate = rz_interpreter_abstr_state_new(cur->arch, RZ_INTERP_ABSTRACTION_CONST, iset->il_ctx); - - rz_list_purge(iset->fcn_state.queue); - ht_up_clear(iset->fcn_state.pc_states); + rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_INIT); ut64 entry_point; - if (rz_th_ring_buf_take_blocking(iset->entry_points, &entry_point) != RZ_THREAD_RING_BUF_OK) { + if (rz_th_ring_buf_take_blocking(inst->entry_points, &entry_point) != RZ_THREAD_RING_BUF_OK) { // No more entry points to interpret => Terminate. // OR. success = true; goto TERM; } - if (iset->plugin->reset) { - iset->plugin->reset(iset->interp_priv); - } +// EMU + RZ_LOG_DEBUG("interpreter: Enter EMU\n"); + rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_EMU); - if (!iset->plugin->init_state(estate, iset->interp_priv) || !iset->plugin->reset_state(estate, entry_point, iset->interp_priv)) { - rz_warn_if_reached(); - return false; + if (!rz_interp_run(inst, entry_point)) { + RZ_LOG_ERROR("Interpreter run failed for entry point 0x%" PFMT64x "\n", entry_point); } - rz_interp_set_push(iset, estate); - rz_interpreter_abstr_state_free(estate); -} - - while (true) { - RZ_LOG_DEBUG("interpreter: Enter EMU\n"); - rz_interp_run_state_set(iset->run_state, RZ_INTERP_RUN_STATE_EMU); - - RzInterpAbstrState *next = rz_interp_set_pop(iset); - if (!next) { - // No uninterpreted states left, fixpoint reached. - success = true; - goto TERM; - } - iset->astate = rz_interpreter_abstr_state_clone(iset, next); - - const RzILCacheBlock *il_bb = rz_il_cache_client_lift_il_block(iset->il_cache_client, iset->astate->pc); - if (!il_bb) { - success = false; - goto TERM; - } - - RzStrBuf sb; - rz_strbuf_init(&sb); - const char *old_cmt = rz_meta_get_string(iset->a, RZ_META_TYPE_COMMENT, il_bb->addr); - if (old_cmt) { - rz_strbuf_appendf(&sb, "%s; ", old_cmt); - } - rz_strbuf_append(&sb, "ENTRY "); - state_as_str_short(iset, &sb, iset->astate); - // rz_meta_set_string(iset->a, RZ_META_TYPE_COMMENT, il_bb->addr, rz_strbuf_get(&sb)); - rz_strbuf_fini(&sb); - - iset->astate->bb_addr = il_bb->addr; - iset->astate->bb_size = il_bb->size; - // Evaluate the effect on the abstract state. - if (!plugin->eval(iset, il_bb, iset->interp_priv)) { - RZ_LOG_DEBUG("interpreter: Eval failed\n"); - goto CLEAN; - } - } - -CLEAN: { +// CLEAN RZ_LOG_DEBUG("interpreter: Enter CLEAN\n"); - rz_interp_run_state_set(iset->run_state, RZ_INTERP_RUN_STATE_CLEAN); + rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_CLEAN); // Wait until RzInquiry asks to start again. - rz_th_sem_wait(iset->run_state_sync); + rz_th_sem_wait(inst->run_state_sync); // Clean can only transition to Init. goto INIT; -} TERM: { RZ_LOG_DEBUG("interpreter: Enter TERM\n"); - rz_interp_run_state_set(iset->run_state, RZ_INTERP_RUN_STATE_TERM); - iset->plugin->fini_state(iset->astate, iset->interp_priv); - if (iset->plugin->fini && iset->interp_priv) { - RZ_FREE_CUSTOM(iset->interp_priv, iset->plugin->fini); + rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_TERM); + if (inst->plugin->fini && inst->interp_priv) { + RZ_FREE_CUSTOM(inst->interp_priv, inst->plugin->fini); } - if (iset->plugin->fini && iset->interp_priv) { - RZ_FREE_CUSTOM(iset->interp_priv, iset->plugin->fini); + if (inst->plugin->fini && inst->interp_priv) { + RZ_FREE_CUSTOM(inst->interp_priv, inst->plugin->fini); } return success; } diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 7d8e15edd5e..56edf4376ea 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -30,7 +30,7 @@ RZ_OWN RzInterpAbstrVal *clone_val(const RzInterpAbstrVal *val, void *plugin_dat return r; } -static bool eval(RZ_NONNULL RzInterpSet *iset, +static bool eval(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzILCacheBlock *il_bb, void *plugin_data) { ProtoIntrprPluginData *pdata = plugin_data; @@ -45,7 +45,7 @@ static bool eval(RZ_NONNULL RzInterpSet *iset, if (ic_pc->value > MAX_INVOCATIONS_PER_BLOCK) { // TODO: Make it configurable RZ_LOG_DEBUG("prototype: Reached maximum number of invocations of basic block at 0x%" PFMT64x ". Skipping it.\n", il_bb->addr) - set_pc(iset->astate, il_bb->addr + il_bb->size, plugin_data); + set_pc(ctx->astate, il_bb->addr + il_bb->size, plugin_data); return true; } } else { @@ -56,14 +56,14 @@ static bool eval(RZ_NONNULL RzInterpSet *iset, memset(&pdata->call_cand, 0, sizeof(pdata->call_cand)); // Now execute the actual effects of the BLOCK. - RzInterpAbstrState *astate = iset->astate; + RzInterpAbstrState *astate = ctx->astate; void **it; rz_pvector_foreach (il_bb->il_ops, it) { ut64 pc = astate->pc; RZ_LOG_DEBUG("prototype: Eval PC = 0x%" PFMT64x "\n", pc); RzStrBuf sb; rz_strbuf_init(&sb); - state_as_str(iset->astate, &sb, plugin_data); + state_as_str(ctx->astate, &sb, plugin_data); RZ_LOG_DEBUG("%s\n", rz_strbuf_get(&sb)); rz_strbuf_fini(&sb); @@ -74,17 +74,17 @@ static bool eval(RZ_NONNULL RzInterpSet *iset, if (rz_vector_index_ptr(&il_bb->il_ops->v, rz_pvector_len(il_bb->il_ops) - 1) == it) { rz_strbuf_append(&sb, "EXIT "); } - state_as_str_short(iset, &sb, iset->astate); - rz_meta_set_string(iset->a, RZ_META_TYPE_COMMENT, pc, rz_strbuf_get(&sb)); + state_as_str_short(ctx->inst, &sb, ctx->astate); + rz_meta_set_string(ctx->inst->a, RZ_META_TYPE_COMMENT, pc, rz_strbuf_get(&sb)); rz_strbuf_fini(&sb); RzILCacheInsnPkt *pkt = *it; // Prepare next pc, the evalutation may overwrite this. ut64 next_pc = pc + pkt->insn_pkt_size; - set_pc(iset->astate, next_pc, plugin_data); + set_pc(ctx->astate, next_pc, plugin_data); - if (!interpreter_prototype_eval_effect(iset, pkt->effect, pkt->insn_pkt_size, plugin_data)) { + if (!interpreter_prototype_eval_effect(ctx, pkt->effect, pkt->insn_pkt_size, plugin_data)) { return false; } if (astate->pc_state != RZ_INTERP_PC_CONST || astate->pc != next_pc) { @@ -95,7 +95,7 @@ static bool eval(RZ_NONNULL RzInterpSet *iset, } if (astate->pc_state != RZ_INTERP_PC_UNREACHABLE) { - rz_interp_set_push(iset, iset->astate); + rz_interp_run_push(ctx, ctx->astate); } return true; @@ -341,7 +341,7 @@ bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, return true; } -void state_as_str_short(RzInterpSet *iset, RZ_OUT RzStrBuf *out, RzInterpAbstrState *astate) { +void state_as_str_short(RzInterpInstance *iset, RZ_OUT RzStrBuf *out, RzInterpAbstrState *astate) { bool first = true; RzIterator *it = ht_up_as_iter_keys(astate->globals); ut64 *k; diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c index 5394529c9bd..e42a6ad0700 100644 --- a/librz/inquiry/interpreter/prototype/eval.c +++ b/librz/inquiry/interpreter/prototype/eval.c @@ -11,7 +11,7 @@ #include bool report_yield_xref( - RzInterpSet *iset, + RzInterpRunContext *ctx, size_t insn_pkt_size, ut64 from, const ProtoIntrprAbstrData *to, @@ -21,7 +21,7 @@ bool report_yield_xref( return true; } if (type == RZ_ANALYSIS_XREF_TYPE_CODE && - RZ_STR_EQ(iset->astate->arch_name, "hexagon") && + RZ_STR_EQ(ctx->astate->arch_name, "hexagon") && from + insn_pkt_size == rz_bv_to_ut64(to->bv)) { // Ugly work around. // Because we don't have RzArch yet the Hexagon plugin adds a JUMP at the @@ -33,12 +33,12 @@ bool report_yield_xref( return true; } - RzInterpYieldRBuf *yrbuf = iset->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; + RzInterpYieldRBuf *yrbuf = ctx->inst->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; rz_return_val_if_fail(yrbuf, false); ut64 to_addr = rz_bv_to_ut64(to->bv); RzAnalysisXRef xref = { 0 }; - xref.bb_addr = iset->astate->bb_addr; + xref.bb_addr = ctx->astate->bb_addr; xref.from = from; xref.to = to_addr; xref.type = type; @@ -55,7 +55,7 @@ bool report_yield_xref( * \brief Report the store of the next PC and report it as possible return point. */ bool report_yield_call_candiate( - RzInterpSet *iset, + RzInterpInstance *iset, ProtoIntrprPluginData *plugin_data) { RzInterpYieldRBuf *cc_rbuf = iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; rz_return_val_if_fail(cc_rbuf, false); @@ -75,7 +75,7 @@ void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) dst->is_const = src->is_const; } -void write_var_to_state(RzInterpSet *iset, +void write_var_to_state(RzInterpAbstrState *astate, RzILVarKind kind, ut64 var_id, const ProtoIntrprAbstrData *data) { @@ -85,13 +85,13 @@ void write_var_to_state(RzInterpSet *iset, rz_warn_if_reached(); return; case RZ_IL_VAR_KIND_GLOBAL: - ht_vals = iset->astate->globals; + ht_vals = astate->globals; break; case RZ_IL_VAR_KIND_LOCAL: - ht_vals = iset->astate->locals; + ht_vals = astate->locals; break; case RZ_IL_VAR_KIND_LOCAL_PURE: - ht_vals = iset->astate->lets; + ht_vals = astate->lets; break; } RzInterpAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); @@ -109,7 +109,7 @@ void write_var_to_state(RzInterpSet *iset, copy_abstr_data(av->abstr_data, data); } -bool read_var_from_state(RzInterpSet *iset, +bool read_var_from_state(RzInterpAbstrState *astate, RzILVarKind kind, ut64 var_id, RZ_OUT ProtoIntrprAbstrData *data) { @@ -119,13 +119,13 @@ bool read_var_from_state(RzInterpSet *iset, rz_warn_if_reached(); return false; case RZ_IL_VAR_KIND_GLOBAL: - ht_vals = iset->astate->globals; + ht_vals = astate->globals; break; case RZ_IL_VAR_KIND_LOCAL: - ht_vals = iset->astate->locals; + ht_vals = astate->locals; break; case RZ_IL_VAR_KIND_LOCAL_PURE: - ht_vals = iset->astate->lets; + ht_vals = astate->lets; break; } RzInterpAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); @@ -145,21 +145,21 @@ bool read_var_from_state(RzInterpSet *iset, // TODO: The assumption that true != 0 is invalid. // It depends on the architecture and must be decided by the RzArch plugin. // State is passed due to this here as well. To make later refactoring easier. -bool abstr_is_true(const RzInterpSet *iset, const ProtoIntrprAbstrData *data) { +bool abstr_is_true(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data) { if (!data->is_const) { return false; } return !rz_bv_is_zero_vector(data->bv); } -bool abstr_may_be_true(const RzInterpSet *iset, const ProtoIntrprAbstrData *data) { +bool abstr_may_be_true(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data) { if (!data->is_const) { return true; } return !rz_bv_is_zero_vector(data->bv); } -bool abstr_may_be_false(const RzInterpSet *iset, const ProtoIntrprAbstrData *data) { +bool abstr_may_be_false(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data) { if (!data->is_const) { return true; } @@ -167,7 +167,7 @@ bool abstr_may_be_false(const RzInterpSet *iset, const ProtoIntrprAbstrData *dat } bool store_abstr_data( - RzInterpSet *iset, + RzInterpInstance *iset, RzILMemIndex mem_idx, const ProtoIntrprAbstrData *addr, const ProtoIntrprAbstrData *src) { @@ -176,7 +176,7 @@ bool store_abstr_data( } bool load_abstr_data( - RzInterpSet *iset, + RzInterpInstance *iset, RzILMemIndex mem_idx, const ProtoIntrprAbstrData *addr, size_t n_bits, diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interpreter/prototype/eval.h index 406eb652f19..ed573b8c7b2 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interpreter/prototype/eval.h @@ -97,42 +97,42 @@ static inline RZ_OWN ProtoIntrprAbstrData *adata_new() { } void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src); -void write_var_to_state(RzInterpSet *iset, RzILVarKind kind, ut64 var_id, const ProtoIntrprAbstrData *data); -bool read_var_from_state(RzInterpSet *iset, RzILVarKind kind, ut64 var_id, RZ_OUT ProtoIntrprAbstrData *data); -bool abstr_is_true(const RzInterpSet *iset, const ProtoIntrprAbstrData *data); -bool abstr_may_be_true(const RzInterpSet *iset, const ProtoIntrprAbstrData *data); -bool abstr_may_be_false(const RzInterpSet *iset, const ProtoIntrprAbstrData *data); +void write_var_to_state(RzInterpAbstrState *astate, RzILVarKind kind, ut64 var_id, const ProtoIntrprAbstrData *data); +bool read_var_from_state(RzInterpAbstrState *astate, RzILVarKind kind, ut64 var_id, RZ_OUT ProtoIntrprAbstrData *data); +bool abstr_is_true(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data); +bool abstr_may_be_true(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data); +bool abstr_may_be_false(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data); bool store_abstr_data( - RzInterpSet *iset, + RzInterpInstance *iset, RzILMemIndex mem_idx, const ProtoIntrprAbstrData *addr, const ProtoIntrprAbstrData *src); bool load_abstr_data( - RzInterpSet *iset, + RzInterpInstance *iset, RzILMemIndex mem_idx, const ProtoIntrprAbstrData *addr, size_t n_bits, RZ_OUT ProtoIntrprAbstrData *out); -RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, +RZ_IPI bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, const RzILOpEffect *effect, size_t nop_pc_inc, ProtoIntrprPluginData *plugin_data); RZ_IPI bool interpreter_prototype_eval_pure( - RzInterpSet *iset, + RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT ProtoIntrprAbstrData *out, ProtoIntrprPluginData *plugin_data); bool report_yield_xref( - RzInterpSet *iset, + RzInterpRunContext *ctx, size_t insn_pkt_size, ut64 from, const ProtoIntrprAbstrData *to, RzAnalysisXRefType type); bool report_yield_call_candiate( - RzInterpSet *iset, + RzInterpInstance *iset, ProtoIntrprPluginData *plugin_data); bool set_pc(RzInterpAbstrState *state, ut64 pc, @@ -145,6 +145,6 @@ void stack_frame_fini(ProtoInterprAbstrStackFrame *frame, void *unused); void stack_frame_push(ProtoIntrprPluginData *pdata, RzBitVector *entry_point, RzBitVector *return_addr, ut64 instance); void stack_frame_pop(ProtoIntrprPluginData *pdata, RZ_NULLABLE ProtoInterprAbstrStackFrame *frame); bool stack_frame_top_ret_addr_cmp(ProtoIntrprPluginData *pdata, RzBitVector *addr); -void state_as_str_short(RzInterpSet *iset, RZ_OUT RzStrBuf *out, RzInterpAbstrState *astate); +void state_as_str_short(RzInterpInstance *iset, RZ_OUT RzStrBuf *out, RzInterpAbstrState *astate); #endif // PROTOYPE_EVAL_H diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interpreter/prototype/eval_effect.c index 8bd2b1bb3fb..8ec117e0540 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interpreter/prototype/eval_effect.c @@ -7,21 +7,21 @@ #include "rz_util/rz_bitvector.h" #include "rz_util/rz_str.h" -static bool value_indicates_ret_addr_write(RzInterpSet *iset, ProtoIntrprAbstrData *val) { +static bool value_indicates_ret_addr_write(RzInterpRunContext *ctx, ProtoIntrprAbstrData *val) { return val->is_const && - (rz_bv_to_ut64(val->bv) == iset->astate->bb_addr + iset->astate->bb_size || + (rz_bv_to_ut64(val->bv) == ctx->astate->bb_addr + ctx->astate->bb_size || // Sparc stores the call instruction PC into o8. // The return instruction jumps then to o7+8. - (rz_str_startswith(iset->astate->arch_name, "sparc") && rz_bv_to_ut64(val->bv) == iset->astate->pc)); + (rz_str_startswith(ctx->astate->arch_name, "sparc") && rz_bv_to_ut64(val->bv) == ctx->astate->pc)); } -RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, +RZ_IPI bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, const RzILOpEffect *effect, size_t insn_pkt_size, ProtoIntrprPluginData *plugin_data) { STACK_ABSTR_DATA_OUT(eval_out); - rz_return_val_if_fail(iset->astate->pc_state == RZ_INTERP_PC_CONST, false); - ut64 pc = iset->astate->pc; + rz_return_val_if_fail(ctx->astate->pc_state == RZ_INTERP_PC_CONST, false); + ut64 pc = ctx->astate->pc; switch (effect->code) { default: @@ -44,32 +44,32 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, break; } case RZ_IL_OP_SEQ: { - if (!interpreter_prototype_eval_effect(iset, effect->op.seq.x, insn_pkt_size, plugin_data)) { + if (!interpreter_prototype_eval_effect(ctx, effect->op.seq.x, insn_pkt_size, plugin_data)) { goto error; } - if (!interpreter_prototype_eval_effect(iset, effect->op.seq.y, insn_pkt_size, plugin_data)) { + if (!interpreter_prototype_eval_effect(ctx, effect->op.seq.y, insn_pkt_size, plugin_data)) { goto error; } break; } case RZ_IL_OP_SET: { ut64 vhash = effect->op.set.hash; - if (!interpreter_prototype_eval_pure(iset, effect->op.set.x, &eval_out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, effect->op.set.x, &eval_out, plugin_data)) { goto error; } RzILVarKind kind = effect->op.set.is_local ? RZ_IL_VAR_KIND_LOCAL : RZ_IL_VAR_KIND_GLOBAL; - write_var_to_state(iset, kind, vhash, &eval_out); - if (value_indicates_ret_addr_write(iset, &eval_out) && + write_var_to_state(ctx->astate, kind, vhash, &eval_out); + if (value_indicates_ret_addr_write(ctx, &eval_out) && kind == RZ_IL_VAR_KIND_GLOBAL) { plugin_data->call_cand.store_addr = pc; - plugin_data->call_cand.npc = iset->astate->bb_addr + iset->astate->bb_size; - plugin_data->call_cand.bb_addr = iset->astate->bb_addr; + plugin_data->call_cand.npc = ctx->astate->bb_addr + ctx->astate->bb_size; + plugin_data->call_cand.bb_addr = ctx->astate->bb_addr; plugin_data->call_cand.in_mem = false; } break; } case RZ_IL_OP_JMP: { - if (!interpreter_prototype_eval_pure(iset, effect->op.jmp.dst, &eval_out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, effect->op.jmp.dst, &eval_out, plugin_data)) { goto error; } if (!eval_out.is_const) { @@ -89,7 +89,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, // Report a call candidate and assume this jump is a call. plugin_data->call_cand.candidate_addr = pc; plugin_data->call_cand.target = target; - report_yield_call_candiate(iset, plugin_data); + report_yield_call_candiate(ctx->inst, plugin_data); // For a call, we need to push a new frame. RzBitVector ret_addr = { 0 }; @@ -108,7 +108,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, xref_type = RZ_ANALYSIS_XREF_TYPE_RETURN; } - report_yield_xref(iset, insn_pkt_size, pc, &eval_out, + report_yield_xref(ctx, insn_pkt_size, pc, &eval_out, xref_type); // Clear the call candidate tracking variable. @@ -119,42 +119,42 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, // For calls, assume control flow will continue like fallthrough. // TODO: set data to top that may be changed by the call } else { - set_abstr_pc(iset->astate, &eval_out, plugin_data); + set_abstr_pc(ctx->astate, &eval_out, plugin_data); } break; } case RZ_IL_OP_BRANCH: { - if (!interpreter_prototype_eval_pure(iset, effect->op.branch.condition, &eval_out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, effect->op.branch.condition, &eval_out, plugin_data)) { goto error; } - bool may_be_true = abstr_may_be_true(iset, &eval_out); - bool may_be_false = abstr_may_be_true(iset, &eval_out); + bool may_be_true = abstr_may_be_true(ctx->inst, &eval_out); + bool may_be_false = abstr_may_be_true(ctx->inst, &eval_out); if (may_be_true && may_be_false) { - RzInterpAbstrState *true_state = rz_interpreter_abstr_state_clone(iset, iset->astate); - RzInterpAbstrState *false_state = iset->astate; - iset->astate = true_state; - if (!interpreter_prototype_eval_effect(iset, effect->op.branch.true_eff, insn_pkt_size, plugin_data)) { + RzInterpAbstrState *true_state = rz_interp_abstr_state_clone(ctx->inst, ctx->astate); + RzInterpAbstrState *false_state = ctx->astate; + ctx->astate = true_state; + if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.true_eff, insn_pkt_size, plugin_data)) { goto error; } - iset->astate = false_state; - if (!interpreter_prototype_eval_effect(iset, effect->op.branch.false_eff, insn_pkt_size, plugin_data)) { + ctx->astate = false_state; + if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.false_eff, insn_pkt_size, plugin_data)) { goto error; } if (true_state->pc_state == false_state->pc_state && true_state->pc == false_state->pc) { // identical target location, simply join the data and continue - iset->plugin->join_state(true_state, false_state, iset->interp_priv); + ctx->inst->plugin->join_state(true_state, false_state, ctx->inst->interp_priv); } else { // different jump targets, branch rather than resorting to top pc - rz_interp_set_push(iset, true_state); - // true_state is already in iset->astate and will be continued automatically + rz_interp_run_push(ctx, true_state); + // true_state is already in ctx->inst->astate and will be continued automatically } - rz_interpreter_abstr_state_free(true_state); + rz_interp_abstr_state_free(true_state); } else if (may_be_true) { - if (!interpreter_prototype_eval_effect(iset, effect->op.branch.true_eff, insn_pkt_size, plugin_data)) { + if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.true_eff, insn_pkt_size, plugin_data)) { goto error; } } else if (may_be_false) { - if (!interpreter_prototype_eval_effect(iset, effect->op.branch.false_eff, insn_pkt_size, plugin_data)) { + if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.false_eff, insn_pkt_size, plugin_data)) { goto error; } } @@ -165,7 +165,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, STACK_ABSTR_DATA_OUT(st_addr); RzILOpPure *key = effect->code == RZ_IL_OP_STORE ? effect->op.store.key : effect->op.storew.key; RzILMemIndex mem_idx = effect->code == RZ_IL_OP_STORE ? 0 : effect->op.storew.mem; - if (!interpreter_prototype_eval_pure(iset, key, &st_addr, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, key, &st_addr, plugin_data)) { RZ_LOG_ERROR("prototype: STORE/STOREW key failed to evaluate.\n"); rz_bv_fini(st_addr.bv); goto error; @@ -185,7 +185,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, } RzILOpPure *pval = effect->code == RZ_IL_OP_STORE ? effect->op.store.value : effect->op.storew.value; - if (!interpreter_prototype_eval_pure(iset, pval, &eval_out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pval, &eval_out, plugin_data)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); rz_bv_fini(st_addr.bv); goto error; @@ -194,14 +194,14 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpSet *iset, rz_bv_fini(st_addr.bv); break; } - if (value_indicates_ret_addr_write(iset, &eval_out)) { + if (value_indicates_ret_addr_write(ctx, &eval_out)) { plugin_data->call_cand.store_addr = pc; - plugin_data->call_cand.npc = iset->astate->bb_addr + iset->astate->bb_size; - plugin_data->call_cand.bb_addr = iset->astate->bb_addr; + plugin_data->call_cand.npc = ctx->astate->bb_addr + ctx->astate->bb_size; + plugin_data->call_cand.bb_addr = ctx->astate->bb_addr; plugin_data->call_cand.in_mem = true; } - report_yield_xref(iset, insn_pkt_size, pc, &st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); - if (!store_abstr_data(iset, mem_idx, &st_addr, &eval_out)) { + report_yield_xref(ctx, insn_pkt_size, pc, &st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); + if (!store_abstr_data(ctx->inst, mem_idx, &st_addr, &eval_out)) { rz_bv_fini(st_addr.bv); goto error; } diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interpreter/prototype/eval_pure.c index 546da06448c..68b2f8b265d 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interpreter/prototype/eval_pure.c @@ -9,14 +9,14 @@ * \brief Evaluate a pure. */ RZ_IPI bool interpreter_prototype_eval_pure( - RzInterpSet *iset, + RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT ProtoIntrprAbstrData *out, ProtoIntrprPluginData *plugin_data) { switch (pure->code) { default: case RZ_IL_OP_VAR: { - if (!read_var_from_state(iset, pure->op.var.kind, pure->op.var.hash, out)) { + if (!read_var_from_state(ctx->astate, pure->op.var.kind, pure->op.var.hash, out)) { RZ_LOG_ERROR("prototype: VAR failed to evaluate. The %s '%s' doesn't exist.\n", rz_il_var_kind_name(pure->op.var.kind), pure->op.var.v); @@ -26,13 +26,13 @@ RZ_IPI bool interpreter_prototype_eval_pure( } case RZ_IL_OP_LET: { ut64 vhash = pure->op.let.hash; - if (!interpreter_prototype_eval_pure(iset, pure->op.let.exp, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.let.exp, out, plugin_data)) { RZ_LOG_ERROR("prototype: LET expression failed to evaluate.\n"); return false; } - write_var_to_state(iset, RZ_IL_VAR_KIND_LOCAL_PURE, vhash, out); + write_var_to_state(ctx->astate, RZ_IL_VAR_KIND_LOCAL_PURE, vhash, out); // Evaluate body - if (!interpreter_prototype_eval_pure(iset, pure->op.let.body, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.let.body, out, plugin_data)) { RZ_LOG_ERROR("prototype: LET body failed to evaluate.\n"); return false; } @@ -41,7 +41,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } case RZ_IL_OP_ITE: { - if (!interpreter_prototype_eval_pure(iset, pure->op.ite.condition, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.ite.condition, out, plugin_data)) { RZ_LOG_ERROR("prototype: ITE condition failed to evaluate.\n"); return false; } @@ -50,13 +50,13 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } - if (abstr_is_true(iset, out)) { - if (!interpreter_prototype_eval_pure(iset, pure->op.ite.x, out, plugin_data)) { + if (abstr_is_true(ctx->inst, out)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.ite.x, out, plugin_data)) { RZ_LOG_ERROR("prototype: ITE x failed to evaluate.\n"); return false; } } else { - if (!interpreter_prototype_eval_pure(iset, pure->op.ite.y, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.ite.y, out, plugin_data)) { RZ_LOG_ERROR("prototype: ITE y failed to evaluate.\n"); return false; } @@ -78,7 +78,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( out->is_const = true; break; case RZ_IL_OP_CAST: { - if (!interpreter_prototype_eval_pure(iset, pure->op.cast.val, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.cast.val, out, plugin_data)) { RZ_LOG_ERROR("prototype: CAST val failed to evaluate.\n"); return false; } @@ -86,7 +86,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(fill_bit); - if (!interpreter_prototype_eval_pure(iset, pure->op.cast.fill, &fill_bit, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.cast.fill, &fill_bit, plugin_data)) { RZ_LOG_ERROR("prototype: CAST fill failed to evaluate.\n"); return false; } @@ -94,7 +94,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(fill_bit.bv); goto map_to_top; } - rz_bv_cast_inplace(out->bv, pure->op.cast.length, abstr_is_true(iset, &fill_bit)); + rz_bv_cast_inplace(out->bv, pure->op.cast.length, abstr_is_true(ctx->inst, &fill_bit)); break; } case RZ_IL_OP_BITV: @@ -104,7 +104,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; case RZ_IL_OP_APPEND: { STACK_ABSTR_DATA_OUT(high); - if (!interpreter_prototype_eval_pure(iset, pure->op.append.high, &high, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.append.high, &high, plugin_data)) { RZ_LOG_ERROR("prototype: APPEND high failed to evaluate.\n"); return false; } @@ -112,7 +112,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(high.bv); goto map_to_top; } - if (!interpreter_prototype_eval_pure(iset, pure->op.append.low, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.append.low, out, plugin_data)) { RZ_LOG_ERROR("prototype: APPEND low failed to evaluate.\n"); rz_bv_fini(high.bv); return false; @@ -130,7 +130,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_LOGNOT: case RZ_IL_OP_INV: { RzILOpPure *x = pure->code == RZ_IL_OP_INV ? pure->op.boolinv.x : pure->op.lognot.bv; - if (!interpreter_prototype_eval_pure(iset, x, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, x, out, plugin_data)) { RZ_LOG_ERROR("prototype: INV x failed to evaluate.\n"); return false; } @@ -143,7 +143,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_AND: { RzILOpPure *px = pure->code == RZ_IL_OP_AND ? pure->op.booland.x : pure->op.logand.x; RzILOpPure *py = pure->code == RZ_IL_OP_AND ? pure->op.booland.y : pure->op.logand.y; - if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: AND x failed to evaluate.\n"); return false; } @@ -151,7 +151,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: AND y failed to evaluate.\n"); return false; } @@ -170,7 +170,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_OR: { RzILOpPure *px = pure->code == RZ_IL_OP_OR ? pure->op.boolor.x : pure->op.logor.x; RzILOpPure *py = pure->code == RZ_IL_OP_OR ? pure->op.boolor.y : pure->op.logor.y; - if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: OR x failed to evaluate.\n"); return false; } @@ -178,7 +178,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: OR y failed to evaluate.\n"); return false; } @@ -197,7 +197,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_XOR: { RzILOpPure *px = pure->code == RZ_IL_OP_XOR ? pure->op.boolxor.x : pure->op.logxor.x; RzILOpPure *py = pure->code == RZ_IL_OP_XOR ? pure->op.boolxor.y : pure->op.logxor.y; - if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: XOR x failed to evaluate.\n"); return false; } @@ -205,7 +205,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: XOR y failed to evaluate.\n"); return false; } @@ -242,7 +242,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( truth_test = rz_bv_msb; break; } - if (!interpreter_prototype_eval_pure(iset, bv, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, bv, out, plugin_data)) { RZ_LOG_ERROR("prototype: MSB/LSB/IS_ZERO bv failed to evaluate.\n"); return false; } @@ -256,7 +256,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } case RZ_IL_OP_NEG: { - if (!interpreter_prototype_eval_pure(iset, pure->op.neg.bv, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.neg.bv, out, plugin_data)) { RZ_LOG_ERROR("prototype: NEG bv failed to evaluate.\n"); return false; } @@ -268,7 +268,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_ADD: { RzILOpPure *px = pure->op.add.x; RzILOpPure *py = pure->op.add.y; - if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: ADD x failed to evaluate.\n"); return false; } @@ -276,7 +276,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: ADD y failed to evaluate.\n"); return false; } @@ -294,7 +294,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_SUB: { RzILOpPure *px = pure->op.sub.x; RzILOpPure *py = pure->op.sub.y; - if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); return false; } @@ -302,7 +302,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: SUB y failed to evaluate.\n"); return false; } @@ -322,7 +322,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzILOpPure *px = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.x : pure->op.shiftl.x; RzILOpPure *py = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.y : pure->op.shiftl.y; RzILOpPure *pfill_bit = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.fill_bit : pure->op.shiftl.fill_bit; - if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) x failed to evaluate.\n"); return false; } @@ -330,7 +330,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) y failed to evaluate.\n"); return false; } @@ -339,7 +339,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(fill_bit); - if (!interpreter_prototype_eval_pure(iset, pfill_bit, &fill_bit, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pfill_bit, &fill_bit, plugin_data)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) fill_bit failed to evaluate.\n"); return false; } @@ -350,7 +350,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( } bool (*shift)(RzBitVector *bv, ut32 size, bool fill_bit); shift = pure->code == RZ_IL_OP_SHIFTR ? rz_bv_rshift_fill : rz_bv_lshift_fill; - if (!shift(out->bv, rz_bv_to_ut64(y.bv), abstr_is_true(iset, &fill_bit))) { + if (!shift(out->bv, rz_bv_to_ut64(y.bv), abstr_is_true(ctx->inst, &fill_bit))) { rz_bv_fini(fill_bit.bv); rz_bv_fini(y.bv); goto map_to_top; @@ -385,7 +385,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } - if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: CMP x failed to evaluate.\n"); return false; } @@ -393,7 +393,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: CMP y failed to evaluate.\n"); return false; } @@ -412,7 +412,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( STACK_ABSTR_DATA_OUT(ld_addr); RzILOpPure *key = pure->code == RZ_IL_OP_LOAD ? pure->op.load.key : pure->op.loadw.key; RzILMemIndex mem_idx = pure->code == RZ_IL_OP_LOAD ? 0 : pure->op.loadw.mem; - if (!interpreter_prototype_eval_pure(iset, key, &ld_addr, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, key, &ld_addr, plugin_data)) { RZ_LOG_ERROR("prototype: LOAD/LOADW key failed to evaluate.\n"); rz_bv_fini(ld_addr.bv); return false; @@ -431,9 +431,9 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_and_inplace(ld_addr.bv, &mask); } - report_yield_xref(iset, 0, iset->astate->pc, &ld_addr, RZ_ANALYSIS_XREF_TYPE_MEM_READ); - size_t n_bits = pure->code == RZ_IL_OP_LOAD ? iset->il_ctx->config->mem_key_size : pure->op.loadw.n_bits; - if (!load_abstr_data(iset, mem_idx, &ld_addr, n_bits, out)) { + report_yield_xref(ctx, 0, ctx->astate->pc, &ld_addr, RZ_ANALYSIS_XREF_TYPE_MEM_READ); + size_t n_bits = pure->code == RZ_IL_OP_LOAD ? ctx->inst->il_ctx->config->mem_key_size : pure->op.loadw.n_bits; + if (!load_abstr_data(ctx->inst, mem_idx, &ld_addr, n_bits, out)) { rz_bv_fini(ld_addr.bv); goto map_to_top; } @@ -443,7 +443,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_MUL: { RzILOpPure *px = pure->op.mul.x; RzILOpPure *py = pure->op.mul.y; - if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: MUL x failed to evaluate.\n"); return false; } @@ -451,7 +451,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: MUL y failed to evaluate.\n"); return false; } @@ -469,7 +469,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_MOD: { RzILOpPure *px = pure->op.mod.x; RzILOpPure *py = pure->op.mod.y; - if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: MOD x failed to evaluate.\n"); return false; } @@ -477,7 +477,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: MOD y failed to evaluate.\n"); return false; } @@ -495,7 +495,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_DIV: { RzILOpPure *px = pure->op.div.x; RzILOpPure *py = pure->op.div.y; - if (!interpreter_prototype_eval_pure(iset, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { RZ_LOG_ERROR("prototype: DIV x failed to evaluate.\n"); return false; } @@ -503,7 +503,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(iset, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { RZ_LOG_ERROR("prototype: DIV y failed to evaluate.\n"); return false; } From b22e22e73b2345577e098d174fefd126aefcba9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Tue, 30 Jun 2026 15:45:15 +0200 Subject: [PATCH 302/334] Remove now unused RzInterpPlugin members --- librz/include/rz_inquiry/rz_interpreter.h | 21 ------ librz/inquiry/interpreter/interpreter.c | 4 +- .../interpreter/p/interpreter_prototype.c | 72 ------------------- 3 files changed, 1 insertion(+), 96 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index a2851a77fee..76bc9b06b55 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -204,11 +204,6 @@ typedef struct { * \brief Closes the abstract state and frees all its abstract data and sets the pointers to NULL. */ bool (*fini_state)(RZ_BORROW RzInterpAbstrState *state, void *plugin_data); - /** - * \brief Hashes the state. - */ - ut64 (*hash_state)(RZ_NONNULL const RzInterpAbstrState *state, - void *plugin_data); /** * \brief Performs the join operation on states (least upper bound, lattice theory) * \return True if a was changed @@ -220,15 +215,6 @@ typedef struct { bool (*eval)(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzILCacheBlock *il_bb, void *plugin_data); - /** - * \brief Determines the next successor addresses from state. - * - * \return Returns false in case of error. The interpretation must abort. - * True otherwise. - */ - bool (*successors)(RZ_NONNULL const RzInterpAbstrState *state, - RZ_NONNULL RZ_OUT RzVector /**/ *successors, - void *plugin_data); /** * \brief Builds a string for printing an abstract value. @@ -247,13 +233,6 @@ typedef struct { bool (*state_as_str)(RZ_NONNULL const RzInterpAbstrState *state, RZ_NONNULL RZ_OUT RzStrBuf *str_buf, void *plugin_data); - - /** - * \brief Set the abstract PC to the given address. - */ - bool (*set_pc)(RZ_NONNULL RzInterpAbstrState *state, - ut64 pc, - void *plugin_data); } RzInterpPlugin; typedef struct { diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interpreter/interpreter.c index 520c7e1b97d..88d601b5152 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interpreter/interpreter.c @@ -414,10 +414,8 @@ RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *inst) { inst->run_state_sync && inst->plugin && inst->plugin->eval && - inst->plugin->successors && inst->plugin->init_state && - inst->plugin->fini_state && - inst->plugin->hash_state, + inst->plugin->fini_state, false); bool success = true; diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c index 56edf4376ea..5c280fc6497 100644 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ b/librz/inquiry/interpreter/p/interpreter_prototype.c @@ -101,25 +101,6 @@ static bool eval(RZ_NONNULL RzInterpRunContext *ctx, return true; } -bool successors(RZ_NONNULL const RzInterpAbstrState *state, - RZ_NONNULL RZ_OUT RzVector /**/ *successors, - void *plugin_data) { - rz_return_val_if_fail(state && successors, false); - ProtoIntrprPluginData *pdata = plugin_data; - if (state->pc_state != RZ_INTERP_PC_CONST) { - // The PC is not a concrete value. - // This prototype can't estimate a reasonable concretization for it. - return true; - } - - ut64 next_pc = state->pc; - RzInterpCtrlFlow branch = { 0 }; - branch.target_addr = branch.actual_target = next_pc; - branch.src_block_addr = pdata->prev_pc; - rz_vector_push(successors, &branch); - return true; -} - static bool init_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { state->pc = 0; state->pc_state = RZ_INTERP_PC_UNREACHABLE; @@ -140,18 +121,6 @@ static bool init_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { // TODO: This is debatable. It depends on the ABI what the default values are. // Some values must be concrete, otherwise the interpretation of the prototype end too early. AD(av->abstr_data)->is_const = false; - /*if (state->il_config->init_state) { - RzAnalysisILInitStateVar *il_var; - rz_vector_foreach (&state->il_config->init_state->vars, il_var) { - if (rz_str_djb2_hash(il_var->name) != djb2_reg_name) { - continue; - } - // The RzArch plugin defined a default value for this global. - RzBitVector *default_val = rz_il_value_to_bv(il_var->val); - rz_bv_copy(AD(av->abstr_data)->bv, default_val); - rz_bv_free(default_val); - } - }*/ } rz_iterator_free(it); return true; @@ -168,18 +137,6 @@ static bool reset_state(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point, v RzInterpAbstrVal *av = ht_up_find(state->globals, djb2_reg_name, NULL); rz_bv_set_from_ut64(AD(av->abstr_data)->bv, 0); AD(av->abstr_data)->is_const = false; - /*if (state->il_config->init_state) { - RzAnalysisILInitStateVar *il_var; - rz_vector_foreach (&state->il_config->init_state->vars, il_var) { - if (rz_str_djb2_hash(il_var->name) != djb2_reg_name) { - continue; - } - // The RzArch plugin defined a default value for this global. - RzBitVector *default_val = rz_il_value_to_bv(il_var->val); - rz_bv_copy(AD(av->abstr_data)->bv, default_val); - rz_bv_free(default_val); - } - }*/ } rz_iterator_free(it); state->bb_addr = 0; @@ -227,30 +184,6 @@ static bool fini_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { return true; } -/** - * \brief This hash function is just an example implementation. - * It is likely not sufficient to prevent collisions. - * It is also slow. - */ -static ut64 hash_state(RZ_NONNULL const RzInterpAbstrState *state, void *plugin_data) { - ut64 h = 5381; - h = (h ^ (h << 5)) ^ (ut64)state->pc_state; - if (state->pc_state == RZ_INTERP_PC_CONST) { - h = (h ^ (h << 5)) ^ state->pc; - } - RzIterator *it = ht_up_as_iter(state->globals); - RzInterpAbstrVal **v; - rz_iterator_foreach(it, v) { - RzInterpAbstrVal *av = *v; - ProtoIntrprAbstrData *ad = av->abstr_data; - if (ad->bv) { - h = (h ^ (h << 5)) ^ rz_bv_to_ut64(ad->bv); - } - } - rz_iterator_free(it); - return h; -} - /** * \brief Join (least upper bound) on values * \return True if a was changed @@ -317,8 +250,6 @@ bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, void *plugin_data) { rz_return_val_if_fail(state && sb, false); - ut64 hash = hash_state(state, plugin_data); - rz_strbuf_appendf(sb, "hash = 0x%" PFMT64x "\n\n", hash); rz_strbuf_append(sb, "Globals\n\n"); rz_strbuf_append(sb, "\tpc = "); if (state->pc_state == RZ_INTERP_PC_CONST) { @@ -419,13 +350,10 @@ static RzInterpPlugin rz_interpreter_plugin_prototype = { .fini = fini, .clone_val = clone_val, .eval = eval, - .successors = successors, .init_state = init_state, .reset_state = reset_state, .fini_state = fini_state, - .hash_state = hash_state, .join_state = join_state, - .set_pc = set_pc, .state_as_str = state_as_str, .val_as_str = val_as_str }; From 16d0f85cc79c9e0a5c170681cd09c1c3d577bf5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Tue, 30 Jun 2026 17:33:07 +0200 Subject: [PATCH 303/334] Merge some files and prepare for finer value abstraction --- librz/include/rz_inquiry/rz_interpreter.h | 38 +- librz/inquiry/inquiry.c | 13 +- .../prototype/eval_effect.c => interp/eval.c} | 273 ++++++++++++- .../{interpreter/prototype => interp}/eval.h | 7 +- .../{interpreter => interp}/interpreter.c | 28 +- librz/inquiry/{interpreter => interp}/state.c | 0 .../eval_pure.c => interp/val_abs_constant.c} | 353 +++++++++++++++++ .../interpreter/p/interpreter_prototype.c | 370 ------------------ librz/inquiry/interpreter/prototype/eval.c | 276 ------------- librz/inquiry/meson.build | 23 +- 10 files changed, 652 insertions(+), 729 deletions(-) rename librz/inquiry/{interpreter/prototype/eval_effect.c => interp/eval.c} (50%) rename librz/inquiry/{interpreter/prototype => interp}/eval.h (96%) rename librz/inquiry/{interpreter => interp}/interpreter.c (95%) rename librz/inquiry/{interpreter => interp}/state.c (100%) rename librz/inquiry/{interpreter/prototype/eval_pure.c => interp/val_abs_constant.c} (61%) delete mode 100644 librz/inquiry/interpreter/p/interpreter_prototype.c delete mode 100644 librz/inquiry/interpreter/prototype/eval.c diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 76bc9b06b55..a1cae20d088 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -40,34 +40,11 @@ typedef enum rz_interp_state_flag { } RzInterpRunStateFlag; /** - * \brief The abstractions this module supports. - */ -typedef enum { - /** - * \brief An undefined abstracted value. - */ - RZ_INTERP_ABSTRACTION_UNDEF = 0, - /** - * \brief Value abstraction into constant and bottom values. - */ - RZ_INTERP_ABSTRACTION_CONST = 1 << 0, - /** - * \brief Value abstraction into Heap[base, offset] and bottom values. - */ - RZ_INTERP_ABSTRACTION_HEAP = 1 << 1, - /** - * \brief Value abstraction into Stack[base, offset] and bottom values. - */ - RZ_INTERP_ABSTRACTION_STACK = 1 << 2, -} RzInterpAbstraction; - -/** - * \brief An arbitrary abstract value. + * \brief An abstract value representing a set of RzILVal + * + * The actual abstraction and structure of this is defined by the plugin in use. */ -typedef struct { - RzInterpAbstraction kind; ///< The abstraction of the value. - void *abstr_data; ///< The abstract data. It is managed by individual interpreter. -} RzInterpAbstrVal; +typedef void RzInterpAbstrVal; typedef struct { RzInquiryBCFGEdgeType cf_type; ///< Control flow type. @@ -114,7 +91,6 @@ typedef struct { RzInterpPCState pc_state; bool uninterpreted; ///< True if this state has not yet been started to interpret, i.e. is part of RzInterpFunctionState.queue - RzInterpAbstraction kinds; ///< The abstractions of the state. HtUP *var_name_hashes; ///< Map of DJB2 hashes to variable names. HtUP /**/ *globals; ///< Global variables (mostly registers). Indexed by DJB2 hash of global name. HtUP /**/ *locals; ///< Local variables. Indexed by DJB2 hash of the local name. @@ -175,10 +151,6 @@ typedef struct { const char *version; const char *desc; const char *license; - /** - * \brief Supported abstractions. Multiple flags can be set. - */ - RzInterpAbstraction supported_abstractions; /** * \brief The yield type this interpreter generates. */ @@ -305,7 +277,6 @@ RZ_API void rz_interp_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yiel RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_new( const char *arch_name, - RzInterpAbstraction kinds, RZ_BORROW RZ_NONNULL RzAnalysisILContext *il_context); RZ_API void rz_interp_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpAbstrState *state); RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_clone(RZ_NONNULL RzInterpInstance *iset, const RzInterpAbstrState *state); @@ -317,7 +288,6 @@ RZ_API RZ_OWN RzInterpYieldRBuf *rz_interp_yield_rbuf_new(RzInterpYieldKind kind RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( RzAnalysis *analysis, RZ_NONNULL RZ_OWN RzInterpPlugin *plugin, - RzInterpAbstraction abstraction, RZ_NONNULL RZ_BORROW RzILCacheClient *il_cache_client, RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM], RZ_NONNULL const RzVector /**/ *ignored_code); diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 500598e18db..ccfb9858a45 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -9,11 +9,9 @@ #include "rz_bin.h" #include "rz_cons.h" #include "rz_il/definitions/mem.h" -#include "rz_il/rz_il_vm.h" #include "rz_inquiry/rz_bcfg.h" #include "rz_inquiry/rz_il_cache.h" #include "rz_inquiry/rz_interpreter.h" -#include "rz_inquiry_plugins.h" #include "rz_th.h" #include "rz_types.h" #include "rz_util/ht_pp.h" @@ -32,6 +30,7 @@ #include #include +#if 0 RZ_LIB_VERSION(rz_inquiry); static RzInquiryPlugin *inquiry_static_plugins[] = { RZ_INQUIRY_STATIC_PLUGINS }; @@ -46,6 +45,7 @@ RZ_API RZ_BORROW RzInquiryPlugin *rz_inquiry_get_plugin(size_t index) { } return inquiry_static_plugins[index]; } +#endif RZ_API bool rz_inquiry_plugin_add(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OWN RZ_NONNULL RzInquiryPlugin *plugin) { rz_return_val_if_fail(inquiry && plugin, false); @@ -142,9 +142,11 @@ RZ_API RZ_OWN RzInquiry *rz_inquiry_new(void) { return NULL; } +#if 0 for (size_t i = 0; i < RZ_ARRAY_SIZE(inquiry_static_plugins); ++i) { rz_inquiry_plugin_add(iq, inquiry_static_plugins[i]); } +#endif return iq; } @@ -152,9 +154,11 @@ RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *iq) { if (!iq) { return; } +#if 0 for (size_t i = 0; i < RZ_ARRAY_SIZE(inquiry_static_plugins); ++i) { rz_inquiry_plugin_del(iq, inquiry_static_plugins[i]); } +#endif ht_sp_free(iq->plugins); ht_sp_free(iq->plugins_data); ht_up_free(iq->call_candidates); @@ -478,6 +482,8 @@ struct ituple { RzInterpRunStateFlag next_run_state; }; +RZ_API extern RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype; + /** * A function to call the prototype interpreter. * Usually these tasks will be split between different caches and yield consumers. @@ -523,7 +529,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // Later we would pass a unique iset to each interpreter with // the required queues only. // But for the prototype we have only one iset with all queues. - RzInquiryPlugin *prototype = ht_sp_find(core->inquiry->plugins, "abstr_int_prototype", NULL); + RzInquiryPlugin *prototype = &rz_inquiry_plugin_interpreter_prototype; // ht_sp_find(core->inquiry->plugins, "abstr_int_prototype", NULL); if (!prototype) { return_code = false; rz_warn_if_reached(); @@ -553,7 +559,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, intp_iset = rz_interp_instance_new( core->analysis, prototype->p_interpreter, - RZ_INTERP_ABSTRACTION_CONST, cache_client, yield_rbufs, ignored_code); diff --git a/librz/inquiry/interpreter/prototype/eval_effect.c b/librz/inquiry/interp/eval.c similarity index 50% rename from librz/inquiry/interpreter/prototype/eval_effect.c rename to librz/inquiry/interp/eval.c index 8ec117e0540..7e8c34f6ecc 100644 --- a/librz/inquiry/interpreter/prototype/eval_effect.c +++ b/librz/inquiry/interp/eval.c @@ -4,8 +4,277 @@ #include "eval.h" #include "rz_analysis.h" #include "rz_inquiry/rz_interpreter.h" -#include "rz_util/rz_bitvector.h" -#include "rz_util/rz_str.h" +#include "rz_th.h" +#include "rz_types.h" +#include "rz_util/rz_assert.h" +#include "rz_util/rz_log.h" +#include + +bool report_yield_xref( + RzInterpRunContext *ctx, + size_t insn_pkt_size, + ut64 from, + const ProtoIntrprAbstrData *to, + RzAnalysisXRefType type) { + if (!to->is_const || rz_bv_len(to->bv) > 64) { + // Isn't reported + return true; + } + if (type == RZ_ANALYSIS_XREF_TYPE_CODE && + RZ_STR_EQ(ctx->astate->arch_name, "hexagon") && + from + insn_pkt_size == rz_bv_to_ut64(to->bv)) { + // Ugly work around. + // Because we don't have RzArch yet the Hexagon plugin adds a JUMP at the + // end of each and every instruction packet. + // This is necessary because the RzIL VM would otherwise just add 4 to the PC, + // which is too little for a packet with 2+ instructions. + // We don't want to report the code references to the next instruction + // packet. So skip them here. + return true; + } + + RzInterpYieldRBuf *yrbuf = ctx->inst->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; + rz_return_val_if_fail(yrbuf, false); + + ut64 to_addr = rz_bv_to_ut64(to->bv); + RzAnalysisXRef xref = { 0 }; + xref.bb_addr = ctx->astate->bb_addr; + xref.from = from; + xref.to = to_addr; + xref.type = type; + if (yrbuf->filter(&xref, yrbuf->filter_data->io_boundaries)) { + RZ_LOG_DEBUG("prototype: REPORT xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref.from, xref.to, rz_analysis_ref_type_tostring(xref.type)); + if (rz_th_ring_buf_put(yrbuf->rbuf, &xref) != RZ_THREAD_RING_BUF_OK) { + return false; + } + } + return true; +} + +/** + * \brief Report the store of the next PC and report it as possible return point. + */ +bool report_yield_call_candiate( + RzInterpInstance *iset, + ProtoIntrprPluginData *plugin_data) { + RzInterpYieldRBuf *cc_rbuf = iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; + rz_return_val_if_fail(cc_rbuf, false); + + RzAnalysisCallCandidate cc = { 0 }; + memcpy(&cc, &plugin_data->call_cand, sizeof(plugin_data->call_cand)); + if (rz_th_ring_buf_put(cc_rbuf->rbuf, &cc) != RZ_THREAD_RING_BUF_OK) { + return false; + } + return true; +} + +void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) { + rz_return_if_fail(dst && src && dst->bv && src->bv); + rz_bv_cast_inplace(dst->bv, rz_bv_len(src->bv), false); + rz_bv_copy(dst->bv, src->bv); + dst->is_const = src->is_const; +} + +void write_var_to_state(RzInterpAbstrState *astate, + RzILVarKind kind, + ut64 var_id, + const ProtoIntrprAbstrData *data) { + HtUP *ht_vals; + switch (kind) { + default: + rz_warn_if_reached(); + return; + case RZ_IL_VAR_KIND_GLOBAL: + ht_vals = astate->globals; + break; + case RZ_IL_VAR_KIND_LOCAL: + ht_vals = astate->locals; + break; + case RZ_IL_VAR_KIND_LOCAL_PURE: + ht_vals = astate->lets; + break; + } + ProtoIntrprAbstrData *av = ht_up_find(ht_vals, var_id, NULL); + if (!av) { + if (kind == RZ_IL_VAR_KIND_GLOBAL) { + RZ_LOG_WARN("New global variable created: 0x%" PFMT64x "\n", var_id) + return; + } + av = adata_new_top(); + if (!av) { + rz_warn_if_reached(); + return; + } + ht_up_insert(ht_vals, var_id, av); + } + copy_abstr_data(av, data); +} + +bool read_var_from_state(RzInterpAbstrState *astate, + RzILVarKind kind, + ut64 var_id, + RZ_OUT ProtoIntrprAbstrData *data) { + HtUP *ht_vals; + switch (kind) { + default: + rz_warn_if_reached(); + return false; + case RZ_IL_VAR_KIND_GLOBAL: + ht_vals = astate->globals; + break; + case RZ_IL_VAR_KIND_LOCAL: + ht_vals = astate->locals; + break; + case RZ_IL_VAR_KIND_LOCAL_PURE: + ht_vals = astate->lets; + break; + } + RzInterpAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); + if (!av) { + // Variable doesn't exist. + // This should never happen and is a bug. + rz_warn_if_reached(); + return false; + } + copy_abstr_data(data, av); + return true; +} + +// Returns true if the bit vector in \p data is not zero. If it is zero or +// the abstract data is not concrete it returns false. +// +// TODO: The assumption that true != 0 is invalid. +// It depends on the architecture and must be decided by the RzArch plugin. +// State is passed due to this here as well. To make later refactoring easier. +bool abstr_is_true(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data) { + if (!data->is_const) { + return false; + } + return !rz_bv_is_zero_vector(data->bv); +} + +bool abstr_may_be_true(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data) { + if (!data->is_const) { + return true; + } + return !rz_bv_is_zero_vector(data->bv); +} + +bool abstr_may_be_false(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data) { + if (!data->is_const) { + return true; + } + return rz_bv_is_zero_vector(data->bv); +} + +bool store_abstr_data( + RzInterpInstance *iset, + RzILMemIndex mem_idx, + const ProtoIntrprAbstrData *addr, + const ProtoIntrprAbstrData *src) { + // TODO: handle with memory abstractions + return true; +} + +bool load_abstr_data( + RzInterpInstance *iset, + RzILMemIndex mem_idx, + const ProtoIntrprAbstrData *addr, + size_t n_bits, + RZ_OUT ProtoIntrprAbstrData *out) { + RzInterpIOReadRequest io_req = { 0 }; + rz_bv_cast_inplace(out->bv, n_bits, 0); + io_req.addr = addr->bv; + io_req.ld_data = out->bv; + io_req.mem_idx = mem_idx; + io_req.n_bits = n_bits; + io_req.big_endian = iset->il_ctx->config->big_endian; + if (rz_th_ring_buf_put(iset->io_request_rbuf, &io_req) != RZ_THREAD_RING_BUF_OK) { + return false; + } + RzInterpIOResult io_res = { 0 }; + if (rz_th_ring_buf_take_blocking(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { + return false; + } + if (!io_res.req_ok) { + RZ_LOG_WARN("prototype: Failed to read correct number of bytes. Requested: 0x%" PFMTSZx + " Received: 0x%" PFMT32x " bits.\n", + n_bits, rz_bv_len(out->bv)); + return false; + } + out->is_const = true; + + char *bytes = rz_bv_as_hex_string(out->bv, true); + RZ_LOG_DEBUG("prototype: READ @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); + free(bytes); + return true; +} + +bool set_abstr_pc(RzInterpAbstrState *state, ProtoIntrprAbstrData *pc, + void *plugin_data) { + rz_return_val_if_fail(state && pc, false); + ProtoIntrprPluginData *pdata = plugin_data; + if (state->pc_state == RZ_INTERP_PC_CONST) { + pdata->prev_pc = state->pc; + } else { + pdata->prev_pc = UT64_MAX; + } + if (pc->is_const) { + state->pc_state = RZ_INTERP_PC_CONST; + state->pc = rz_bv_to_ut64(pc->bv); + } else { + state->pc_state = RZ_INTERP_PC_ANY; + } + RZ_LOG_DEBUG("prototype: set_abstr_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", + pdata->prev_pc, state->pc, state->pc_state == RZ_INTERP_PC_CONST ? "Constant" : "Top"); + return true; +} + +bool set_pc(RzInterpAbstrState *state, ut64 pc, + void *plugin_data) { + rz_return_val_if_fail(state, false); + ProtoIntrprPluginData *pdata = plugin_data; + if (state->pc_state == RZ_INTERP_PC_CONST) { + pdata->prev_pc = state->pc; + } else { + pdata->prev_pc = UT64_MAX; + } + state->pc = pc; + state->pc_state = RZ_INTERP_PC_CONST; + RZ_LOG_DEBUG("prototype: set_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (Constant)\n", + pdata->prev_pc, pc); + return true; +} + +void stack_frame_fini(ProtoInterprAbstrStackFrame *frame, void *unused) { + if (!frame) { + return; + } + rz_bv_fini(&frame->return_addr); + rz_bv_fini(&frame->entry_point); +} + +void stack_frame_push(ProtoIntrprPluginData *pdata, RzBitVector *entry_point, RzBitVector *return_addr, ut64 instance) { + ProtoInterprAbstrStackFrame frame = { 0 }; + rz_bv_init(&frame.return_addr, rz_bv_len(return_addr)); + rz_bv_copy(&frame.return_addr, return_addr); + rz_bv_init(&frame.entry_point, rz_bv_len(entry_point)); + rz_bv_copy(&frame.entry_point, entry_point); + frame.instance = instance; + rz_vector_push(&pdata->stack, &frame); +} + +void stack_frame_pop(ProtoIntrprPluginData *pdata, RZ_NULLABLE ProtoInterprAbstrStackFrame *frame) { + rz_vector_pop(&pdata->stack, frame); +} + +bool stack_frame_top_ret_addr_cmp(ProtoIntrprPluginData *pdata, RzBitVector *addr) { + ProtoInterprAbstrStackFrame *frame = rz_vector_tail(&pdata->stack); + if (!frame) { + return false; + } + return rz_bv_eq(&frame->return_addr, addr); +} static bool value_indicates_ret_addr_write(RzInterpRunContext *ctx, ProtoIntrprAbstrData *val) { return val->is_const && diff --git a/librz/inquiry/interpreter/prototype/eval.h b/librz/inquiry/interp/eval.h similarity index 96% rename from librz/inquiry/interpreter/prototype/eval.h rename to librz/inquiry/interp/eval.h index ed573b8c7b2..caa4dac6b43 100644 --- a/librz/inquiry/interpreter/prototype/eval.h +++ b/librz/inquiry/interp/eval.h @@ -10,11 +10,6 @@ #include #include -/** - * \brief Abstract data getter from the RzInterpAbstrVal - */ -#define AD(av) (((ProtoIntrprAbstrData *)av)) - typedef struct { /** * \brief Set if the abstract value represents a single constant bitvector. @@ -89,7 +84,7 @@ static inline RZ_OWN ProtoIntrprAbstrData *adata_from_bv(const RzBitVector *bv) return ad; } -static inline RZ_OWN ProtoIntrprAbstrData *adata_new() { +static inline RZ_OWN ProtoIntrprAbstrData *adata_new_top() { ProtoIntrprAbstrData *ad = RZ_NEW0(ProtoIntrprAbstrData); ad->is_const = false; ad->bv = rz_bv_new(BV_STACK_MAX_SIZE); diff --git a/librz/inquiry/interpreter/interpreter.c b/librz/inquiry/interp/interpreter.c similarity index 95% rename from librz/inquiry/interpreter/interpreter.c rename to librz/inquiry/interp/interpreter.c index 88d601b5152..466c4fc2b0a 100644 --- a/librz/inquiry/interpreter/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -15,7 +15,7 @@ #include #include -#include "inquiry/interpreter/prototype/eval.h" +#include "eval.h" RZ_API void rz_interp_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yield_rbufs) { if (!yield_rbufs) { @@ -77,7 +77,6 @@ RZ_API RZ_OWN RzInterpYieldRBuf *rz_interp_yield_rbuf_new(RzInterpYieldKind kind */ RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_new( const char *arch_name, - RzInterpAbstraction kinds, RZ_BORROW RZ_NONNULL RzAnalysisILContext *il_context) { rz_return_val_if_fail(il_context, NULL); RzInterpAbstrState *state = RZ_NEW0(RzInterpAbstrState); @@ -85,21 +84,19 @@ RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_new( return NULL; } state->arch_name = arch_name; - state->kinds = kinds; // Initialize the register file with uninitialized abstract values. state->var_name_hashes = ht_up_new(NULL, free); state->globals = ht_up_new(NULL, free); for (size_t i = 0; i < il_context->reg_binding->regs_count; i++) { const char *rname = il_context->reg_binding->regs[i].name; - RzInterpAbstrVal *aval = RZ_NEW0(RzInterpAbstrVal); - if (!aval) { - ht_up_free(state->globals); - ht_up_free(state->var_name_hashes); - free(state); - return NULL; - } + RzInterpAbstrVal *aval = NULL; // RZ_NEW0(RzInterpAbstrVal); + // if (!aval) { + // ht_up_free(state->globals); + // ht_up_free(state->var_name_hashes); + // free(state); + // return NULL; + // } - aval->kind = RZ_INTERP_ABSTRACTION_UNDEF; ut64 djb2_reg_hash = rz_str_djb2_hash(rname); if (!ht_up_insert(state->globals, djb2_reg_hash, aval) || !ht_up_insert(state->var_name_hashes, djb2_reg_hash, rz_str_dup(rname))) { @@ -158,7 +155,6 @@ RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_clone(RZ_NONNULL RzInter return NULL; } r->arch_name = state->arch_name; - r->kinds = state->kinds; r->pc = state->pc; r->pc_state = state->pc_state; r->uninterpreted = state->uninterpreted; @@ -237,17 +233,11 @@ static bool setup_ipc_objects( RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( RzAnalysis *analysis, RZ_NONNULL RZ_OWN RzInterpPlugin *plugin, - RzInterpAbstraction abstraction, RZ_NONNULL RZ_BORROW RzILCacheClient *il_cache_client, RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM], RZ_NONNULL const RzVector /**/ *ignored_code) { rz_return_val_if_fail(plugin && ignored_code && analysis && il_cache_client, NULL); - if (abstraction != (plugin->supported_abstractions & abstraction)) { - RZ_LOG_ERROR("Plugin does not support all required abstractions.\n"); - return NULL; - } - RzInterpInstance *iset = RZ_NEW0(RzInterpInstance); if (!iset) { return NULL; @@ -345,7 +335,7 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { // Prepare the initial state from the given entry point // Hint: nothing speaks against supporting multiple entry points in a single run const RzAnalysisPlugin *cur = rz_analysis_plugin_current(inst->a); - RzInterpAbstrState *estate = rz_interp_abstr_state_new(cur->arch, RZ_INTERP_ABSTRACTION_CONST, inst->il_ctx); + RzInterpAbstrState *estate = rz_interp_abstr_state_new(cur->arch, inst->il_ctx); if (inst->plugin->reset) { // TODO: should rather be local to the RzInterpRunContext if it is reset every run inst->plugin->reset(inst->interp_priv); diff --git a/librz/inquiry/interpreter/state.c b/librz/inquiry/interp/state.c similarity index 100% rename from librz/inquiry/interpreter/state.c rename to librz/inquiry/interp/state.c diff --git a/librz/inquiry/interpreter/prototype/eval_pure.c b/librz/inquiry/interp/val_abs_constant.c similarity index 61% rename from librz/inquiry/interpreter/prototype/eval_pure.c rename to librz/inquiry/interp/val_abs_constant.c index 68b2f8b265d..20988aa8de0 100644 --- a/librz/inquiry/interpreter/prototype/eval_pure.c +++ b/librz/inquiry/interp/val_abs_constant.c @@ -5,6 +5,27 @@ #include "rz_util/rz_assert.h" #include +#include +#include +#include +#include + +#include "rz_util/ht_uu.h" +#include "rz_util/rz_bitvector.h" + +static void adata_free(ProtoIntrprAbstrData *adata) { + if (!adata) { + return; + } + rz_bv_free(adata->bv); + free(adata); +} + +/** + * \brief Abstract data getter from the RzInterpAbstrVal + */ +#define AD(av) ((ProtoIntrprAbstrData *)av) + /** * \brief Evaluate a pure. */ @@ -564,3 +585,335 @@ RZ_IPI bool interpreter_prototype_eval_pure( out->is_const = false; return true; } + + +#define INITIAL_STACK_CAPACITY 8 + +#define MAX_INVOCATIONS_PER_BLOCK 3 + +bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, + RZ_NONNULL RZ_OUT RzStrBuf *sb, + void *plugin_data); + +RZ_OWN RzInterpAbstrVal *clone_val(const RzInterpAbstrVal *val, void *plugin_data) { + ProtoIntrprAbstrData *r = RZ_NEW0(ProtoIntrprAbstrData); + if (!r) { + return NULL; + } + r->is_const = AD(val)->is_const; + r->bv = rz_bv_dup(AD(val)->bv); + return r; +} + +static bool eval(RZ_NONNULL RzInterpRunContext *ctx, + RZ_NONNULL const RzILCacheBlock *il_bb, + void *plugin_data) { + ProtoIntrprPluginData *pdata = plugin_data; + + // Check invocation count of the current address. + // Never execute the same address more than MAX_INVOCATIONS_PER_BLOCK times. + bool found = false; + HtUUKv *ic_pc = ht_uu_find_kv(pdata->bb_invocation_count, il_bb->addr, &found); + if (found) { + ic_pc->value++; + RZ_LOG_DEBUG("prototype: Eval BLOCK (ic: %" PFMT64d ") = 0x%" PFMT64x "\n", ic_pc->value, il_bb->addr); + if (ic_pc->value > MAX_INVOCATIONS_PER_BLOCK) { + // TODO: Make it configurable + RZ_LOG_DEBUG("prototype: Reached maximum number of invocations of basic block at 0x%" PFMT64x ". Skipping it.\n", il_bb->addr) + set_pc(ctx->astate, il_bb->addr + il_bb->size, plugin_data); + return true; + } + } else { + ht_uu_update(pdata->bb_invocation_count, il_bb->addr, 1); + } + + // Reset call candidate tracking for each basic block. + memset(&pdata->call_cand, 0, sizeof(pdata->call_cand)); + + // Now execute the actual effects of the BLOCK. + RzInterpAbstrState *astate = ctx->astate; + void **it; + rz_pvector_foreach (il_bb->il_ops, it) { + ut64 pc = astate->pc; + RZ_LOG_DEBUG("prototype: Eval PC = 0x%" PFMT64x "\n", pc); + RzStrBuf sb; + rz_strbuf_init(&sb); + state_as_str(ctx->astate, &sb, plugin_data); + RZ_LOG_DEBUG("%s\n", rz_strbuf_get(&sb)); + rz_strbuf_fini(&sb); + + rz_strbuf_init(&sb); + if (pc == il_bb->addr) { + rz_strbuf_append(&sb, "ENTRY "); + } + if (rz_vector_index_ptr(&il_bb->il_ops->v, rz_pvector_len(il_bb->il_ops) - 1) == it) { + rz_strbuf_append(&sb, "EXIT "); + } + state_as_str_short(ctx->inst, &sb, ctx->astate); + rz_meta_set_string(ctx->inst->a, RZ_META_TYPE_COMMENT, pc, rz_strbuf_get(&sb)); + rz_strbuf_fini(&sb); + + RzILCacheInsnPkt *pkt = *it; + + // Prepare next pc, the evalutation may overwrite this. + ut64 next_pc = pc + pkt->insn_pkt_size; + set_pc(ctx->astate, next_pc, plugin_data); + + if (!interpreter_prototype_eval_effect(ctx, pkt->effect, pkt->insn_pkt_size, plugin_data)) { + return false; + } + if (astate->pc_state != RZ_INTERP_PC_CONST || astate->pc != next_pc) { + // Unreachable or a jump happened somewhere other than fallthrough, so we can't continue + // interpreting the block linearly, but have to push the new location + break; + } + } + + if (astate->pc_state != RZ_INTERP_PC_UNREACHABLE) { + rz_interp_run_push(ctx, ctx->astate); + } + + return true; +} + +static bool init_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { + state->pc = 0; + state->pc_state = RZ_INTERP_PC_UNREACHABLE; + + RzIterator *it = ht_up_as_iter_keys(state->globals); + ut64 *k; + rz_iterator_foreach(it, k) { + ut64 djb2_reg_name = *k; + ProtoIntrprAbstrData *av = adata_new_top(); + if (!av) { + break; + } + ht_up_update(state->globals, djb2_reg_name, av); + } + rz_iterator_free(it); + return true; +} + +static bool reset_state(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point, void *plugin_data) { + state->pc_state = RZ_INTERP_PC_CONST; + state->pc = entry_point; + + RzIterator *it = ht_up_as_iter_keys(state->globals); + ut64 *k; + rz_iterator_foreach(it, k) { + ut64 djb2_reg_name = *k; + RzInterpAbstrVal *av = ht_up_find(state->globals, djb2_reg_name, NULL); + rz_bv_set_from_ut64(AD(av)->bv, 0); + AD(av)->is_const = false; + } + rz_iterator_free(it); + state->bb_addr = 0; + state->bb_size = 0; + return true; +} + +static bool fini_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { + RzIterator *it = ht_up_as_iter(state->globals); + RzInterpAbstrVal **v; + rz_iterator_foreach(it, v) { + adata_free(*v); + } + rz_iterator_free(it); + + it = ht_up_as_iter(state->locals); + rz_iterator_foreach(it, v) { + adata_free(*v); + } + rz_iterator_free(it); + + it = ht_up_as_iter(state->lets); + rz_iterator_foreach(it, v) { + adata_free(*v); + } + rz_iterator_free(it); + return true; +} + +/** + * \brief Join (least upper bound) on values + * \return True if a was changed + */ +static bool join_val(RZ_BORROW RZ_INOUT RzInterpAbstrVal *a, RZ_BORROW RZ_IN const RzInterpAbstrVal *b) { + ProtoIntrprAbstrData *ad = AD(a); + ProtoIntrprAbstrData *bd = AD(b); + if (ad->is_const && bd->is_const && rz_bv_eq(ad->bv, bd->bv)) { + // identical values, a already has the least upper bound + return false; + } + // for anything else, the least upper bound is top + bool changed = ad->is_const; + ad->is_const = false; + return changed; +} + +/** + * \brief Join (least upper bound) on var sets + * \return True if a was changed + */ +static bool join_vars(RZ_BORROW RZ_INOUT HtUP *a, RZ_BORROW RZ_IN HtUP *b) { + RzIterator *it = ht_up_as_iter_keys(a); + ut64 *k; + bool changed = false; + rz_iterator_foreach(it, k) { + RzInterpAbstrVal *av = ht_up_find(a, *k, NULL); + RzInterpAbstrVal *bv = ht_up_find(b, *k, NULL); + if (!av || !bv) { + continue; + } + if (join_val(av, bv)) { + changed = true; + } + } + return changed; +} + +bool join_state(RZ_BORROW RZ_INOUT RzInterpAbstrState *a, RZ_BORROW RZ_IN const RzInterpAbstrState *b, void *plugin_data) { + bool global_change = join_vars(a->globals, b->globals); + bool local_change = join_vars(a->locals, b->locals); + // lets are not be relevant here since they are immutable within their scope + return global_change || local_change; +} + +bool val_as_str(RZ_NONNULL const RzInterpAbstrVal *val, RZ_NONNULL RZ_OUT RzStrBuf *sb, void *plugin_data) { + rz_return_val_if_fail(val && sb, false); + ProtoIntrprAbstrData *av = AD(val); + if (av->is_const) { + char *s = rz_bv_as_hex_string(av->bv, false); + if (!s) { + return false; + } + rz_strbuf_append(sb, s); + free(s); + } else { + rz_strbuf_append(sb, "⊤"); + } + return true; +} + +bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, + RZ_NONNULL RZ_OUT RzStrBuf *sb, + void *plugin_data) { + rz_return_val_if_fail(state && sb, false); + + rz_strbuf_append(sb, "Globals\n\n"); + rz_strbuf_append(sb, "\tpc = "); + if (state->pc_state == RZ_INTERP_PC_CONST) { + rz_strbuf_appendf(sb, "0x%" PFMT64x, state->pc); + } else { + rz_strbuf_append(sb, state->pc_state == RZ_INTERP_PC_ANY ? "⊤" : "⊥"); + } + rz_strbuf_append(sb, "\n\n"); + + RzIterator *it = ht_up_as_iter_keys(state->globals); + ut64 *k; + rz_iterator_foreach(it, k) { + const char *gname = ht_up_find(state->var_name_hashes, *k, NULL); + rz_strbuf_appendf(sb, "\t%s = ", gname); + RzInterpAbstrVal *av = ht_up_find(state->globals, *k, NULL); + val_as_str(av, sb, plugin_data); + rz_strbuf_append(sb, "\n"); + } + rz_iterator_free(it); + return true; +} + +void state_as_str_short(RzInterpInstance *iset, RZ_OUT RzStrBuf *out, RzInterpAbstrState *astate) { + bool first = true; + RzIterator *it = ht_up_as_iter_keys(astate->globals); + ut64 *k; + rz_iterator_foreach(it, k) { + ut64 djb2_reg_name = *k; + RzInterpAbstrVal *av = ht_up_find(astate->globals, djb2_reg_name, NULL); + ProtoIntrprAbstrData *val = AD(av); + if (!val->is_const) { + continue; + } + if (!first) { + rz_strbuf_append(out, ", "); + } + first = false; + const char *varname = ht_up_find(astate->var_name_hashes, djb2_reg_name, NULL); + rz_strbuf_appendf(out, "%s = ", varname); + iset->plugin->val_as_str(av, out, iset->interp_priv); + } +} + +bool init(void **plugin_data) { + ProtoIntrprPluginData *pdata = RZ_NEW0(ProtoIntrprPluginData); + if (!pdata) { + return NULL; + } + RZ_LOG_DEBUG("prototype: init()\n"); + pdata->bb_invocation_count = ht_uu_new(); + if (!pdata->bb_invocation_count) { + free(pdata); + return false; + } + rz_vector_init(&pdata->stack, + sizeof(ProtoInterprAbstrStackFrame), + (RzVectorFree)stack_frame_fini, NULL); + rz_vector_reserve(&pdata->stack, INITIAL_STACK_CAPACITY); + *plugin_data = pdata; + return true; +} + +bool fini(void *plugin_data) { + if (!plugin_data) { + return true; + } + RZ_LOG_DEBUG("prototype: fini()\n"); + ProtoIntrprPluginData *pdata = plugin_data; + ht_uu_free(pdata->bb_invocation_count); + rz_vector_fini(&pdata->stack); + free(pdata); + return true; +} + +bool reset(void *plugin_data) { + if (!plugin_data) { + return true; + } + RZ_LOG_DEBUG("prototype: reset()\n"); + ProtoIntrprPluginData *pdata = plugin_data; + pdata->prev_pc = UT64_MAX; + ht_uu_clear(pdata->bb_invocation_count); + memset(&pdata->call_cand, 0, sizeof(RzAnalysisCallCandidate)); + rz_vector_purge(&pdata->stack); + return true; +} + +static RzInterpPlugin rz_interpreter_plugin_prototype = { + .name = "abstr_int_prototype", + .author = "Rot127", + .version = "0.1p", + .desc = "A prototype interpreter for constant/top abstractions.", + .license = "LGPL-3.0-only", + .supported_yields = { RZ_INTERP_YIELD_KIND_XREF, RZ_INTERP_YIELD_KIND_CALL_CANDIDATE }, + .init = init, + .reset = reset, + .fini = fini, + .clone_val = clone_val, + .eval = eval, + .init_state = init_state, + .reset_state = reset_state, + .fini_state = fini_state, + .join_state = join_state, + .state_as_str = state_as_str, + .val_as_str = val_as_str +}; + +RZ_API RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype = { + .p_interpreter = &rz_interpreter_plugin_prototype, +}; + +#ifndef RZ_PLUGIN_INCORE +RZ_API RzLibStruct rizin_plugin = { + .type = RZ_LIB_TYPE_INTERPRETER, + .data = &interpreter_prototype +}; +#endif diff --git a/librz/inquiry/interpreter/p/interpreter_prototype.c b/librz/inquiry/interpreter/p/interpreter_prototype.c deleted file mode 100644 index 5c280fc6497..00000000000 --- a/librz/inquiry/interpreter/p/interpreter_prototype.c +++ /dev/null @@ -1,370 +0,0 @@ -// SPDX-FileCopyrightText: 2025 RizinOrg -// SPDX-License-Identifier: LGPL-3.0-only - -#include -#include -#include -#include - -#include "../prototype/eval.h" -#include "rz_util/ht_uu.h" -#include "rz_util/rz_bitvector.h" - -#define INITIAL_STACK_CAPACITY 8 - -#define MAX_INVOCATIONS_PER_BLOCK 3 - -bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, - RZ_NONNULL RZ_OUT RzStrBuf *sb, - void *plugin_data); - -RZ_OWN RzInterpAbstrVal *clone_val(const RzInterpAbstrVal *val, void *plugin_data) { - RzInterpAbstrVal *r = RZ_NEW0(RzInterpAbstrVal); - if (!r) { - return NULL; - } - r->kind = val->kind; - r->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); - AD(r->abstr_data)->is_const = AD(val->abstr_data)->is_const; - AD(r->abstr_data)->bv = rz_bv_dup(AD(val->abstr_data)->bv); - return r; -} - -static bool eval(RZ_NONNULL RzInterpRunContext *ctx, - RZ_NONNULL const RzILCacheBlock *il_bb, - void *plugin_data) { - ProtoIntrprPluginData *pdata = plugin_data; - - // Check invocation count of the current address. - // Never execute the same address more than MAX_INVOCATIONS_PER_BLOCK times. - bool found = false; - HtUUKv *ic_pc = ht_uu_find_kv(pdata->bb_invocation_count, il_bb->addr, &found); - if (found) { - ic_pc->value++; - RZ_LOG_DEBUG("prototype: Eval BLOCK (ic: %" PFMT64d ") = 0x%" PFMT64x "\n", ic_pc->value, il_bb->addr); - if (ic_pc->value > MAX_INVOCATIONS_PER_BLOCK) { - // TODO: Make it configurable - RZ_LOG_DEBUG("prototype: Reached maximum number of invocations of basic block at 0x%" PFMT64x ". Skipping it.\n", il_bb->addr) - set_pc(ctx->astate, il_bb->addr + il_bb->size, plugin_data); - return true; - } - } else { - ht_uu_update(pdata->bb_invocation_count, il_bb->addr, 1); - } - - // Reset call candidate tracking for each basic block. - memset(&pdata->call_cand, 0, sizeof(pdata->call_cand)); - - // Now execute the actual effects of the BLOCK. - RzInterpAbstrState *astate = ctx->astate; - void **it; - rz_pvector_foreach (il_bb->il_ops, it) { - ut64 pc = astate->pc; - RZ_LOG_DEBUG("prototype: Eval PC = 0x%" PFMT64x "\n", pc); - RzStrBuf sb; - rz_strbuf_init(&sb); - state_as_str(ctx->astate, &sb, plugin_data); - RZ_LOG_DEBUG("%s\n", rz_strbuf_get(&sb)); - rz_strbuf_fini(&sb); - - rz_strbuf_init(&sb); - if (pc == il_bb->addr) { - rz_strbuf_append(&sb, "ENTRY "); - } - if (rz_vector_index_ptr(&il_bb->il_ops->v, rz_pvector_len(il_bb->il_ops) - 1) == it) { - rz_strbuf_append(&sb, "EXIT "); - } - state_as_str_short(ctx->inst, &sb, ctx->astate); - rz_meta_set_string(ctx->inst->a, RZ_META_TYPE_COMMENT, pc, rz_strbuf_get(&sb)); - rz_strbuf_fini(&sb); - - RzILCacheInsnPkt *pkt = *it; - - // Prepare next pc, the evalutation may overwrite this. - ut64 next_pc = pc + pkt->insn_pkt_size; - set_pc(ctx->astate, next_pc, plugin_data); - - if (!interpreter_prototype_eval_effect(ctx, pkt->effect, pkt->insn_pkt_size, plugin_data)) { - return false; - } - if (astate->pc_state != RZ_INTERP_PC_CONST || astate->pc != next_pc) { - // Unreachable or a jump happened somewhere other than fallthrough, so we can't continue - // interpreting the block linearly, but have to push the new location - break; - } - } - - if (astate->pc_state != RZ_INTERP_PC_UNREACHABLE) { - rz_interp_run_push(ctx, ctx->astate); - } - - return true; -} - -static bool init_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { - state->pc = 0; - state->pc_state = RZ_INTERP_PC_UNREACHABLE; - - RzIterator *it = ht_up_as_iter_keys(state->globals); - ut64 *k; - rz_iterator_foreach(it, k) { - ut64 djb2_reg_name = *k; - RzInterpAbstrVal *av = ht_up_find(state->globals, djb2_reg_name, NULL); - rz_return_val_if_fail(av, false); - av->abstr_data = RZ_NEW0(ProtoIntrprAbstrData); - // Length doesn't matter here. Because the destination is always - // set to the length of the src. - // TODO: Really a good idea to be so liberal? - // Or should the length of the globals be enforced? - // The bitvector arithmetic does enforce the length. - AD(av->abstr_data)->bv = rz_bv_new(64); - // TODO: This is debatable. It depends on the ABI what the default values are. - // Some values must be concrete, otherwise the interpretation of the prototype end too early. - AD(av->abstr_data)->is_const = false; - } - rz_iterator_free(it); - return true; -} - -static bool reset_state(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point, void *plugin_data) { - state->pc_state = RZ_INTERP_PC_CONST; - state->pc = entry_point; - - RzIterator *it = ht_up_as_iter_keys(state->globals); - ut64 *k; - rz_iterator_foreach(it, k) { - ut64 djb2_reg_name = *k; - RzInterpAbstrVal *av = ht_up_find(state->globals, djb2_reg_name, NULL); - rz_bv_set_from_ut64(AD(av->abstr_data)->bv, 0); - AD(av->abstr_data)->is_const = false; - } - rz_iterator_free(it); - state->bb_addr = 0; - state->bb_size = 0; - return true; -} - -static bool fini_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { - RzIterator *it = ht_up_as_iter(state->globals); - RzInterpAbstrVal **v; - rz_iterator_foreach(it, v) { - RzInterpAbstrVal *av = *v; - ProtoIntrprAbstrData *ad = av->abstr_data; - if (ad && ad->bv) { - rz_bv_free(ad->bv); - } - free(ad); - av->abstr_data = NULL; - } - rz_iterator_free(it); - - it = ht_up_as_iter(state->locals); - rz_iterator_foreach(it, v) { - RzInterpAbstrVal *av = *v; - ProtoIntrprAbstrData *ad = av->abstr_data; - if (ad && ad->bv) { - rz_bv_free(ad->bv); - } - free(ad); - av->abstr_data = NULL; - } - rz_iterator_free(it); - - it = ht_up_as_iter(state->lets); - rz_iterator_foreach(it, v) { - RzInterpAbstrVal *av = *v; - ProtoIntrprAbstrData *ad = av->abstr_data; - if (ad && ad->bv) { - rz_bv_free(ad->bv); - } - free(ad); - av->abstr_data = NULL; - } - rz_iterator_free(it); - return true; -} - -/** - * \brief Join (least upper bound) on values - * \return True if a was changed - */ -static bool join_val(RZ_BORROW RZ_INOUT RzInterpAbstrVal *a, RZ_BORROW RZ_IN const RzInterpAbstrVal *b) { - ProtoIntrprAbstrData *ad = AD(a->abstr_data); - ProtoIntrprAbstrData *bd = AD(b->abstr_data); - if (ad->is_const && bd->is_const && rz_bv_eq(ad->bv, bd->bv)) { - // identical values, a already has the least upper bound - return false; - } - // for anything else, the least upper bound is top - bool changed = ad->is_const; - ad->is_const = false; - return changed; -} - -/** - * \brief Join (least upper bound) on var sets - * \return True if a was changed - */ -static bool join_vars(RZ_BORROW RZ_INOUT HtUP *a, RZ_BORROW RZ_IN HtUP *b) { - RzIterator *it = ht_up_as_iter_keys(a); - ut64 *k; - bool changed = false; - rz_iterator_foreach(it, k) { - RzInterpAbstrVal *av = ht_up_find(a, *k, NULL); - RzInterpAbstrVal *bv = ht_up_find(b, *k, NULL); - if (!av || !bv) { - continue; - } - if (join_val(av, bv)) { - changed = true; - } - } - return changed; -} - -bool join_state(RZ_BORROW RZ_INOUT RzInterpAbstrState *a, RZ_BORROW RZ_IN const RzInterpAbstrState *b, void *plugin_data) { - bool global_change = join_vars(a->globals, b->globals); - bool local_change = join_vars(a->locals, b->locals); - // lets are not be relevant here since they are immutable within their scope - return global_change || local_change; -} - -bool val_as_str(RZ_NONNULL const RzInterpAbstrVal *val, RZ_NONNULL RZ_OUT RzStrBuf *sb, void *plugin_data) { - rz_return_val_if_fail(val && sb, false); - ProtoIntrprAbstrData *av = AD(val->abstr_data); - if (av->is_const) { - char *s = rz_bv_as_hex_string(av->bv, false); - if (!s) { - return false; - } - rz_strbuf_append(sb, s); - free(s); - } else { - rz_strbuf_append(sb, "⊤"); - } - return true; -} - -bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, - RZ_NONNULL RZ_OUT RzStrBuf *sb, - void *plugin_data) { - rz_return_val_if_fail(state && sb, false); - - rz_strbuf_append(sb, "Globals\n\n"); - rz_strbuf_append(sb, "\tpc = "); - if (state->pc_state == RZ_INTERP_PC_CONST) { - rz_strbuf_appendf(sb, "0x%" PFMT64x, state->pc); - } else { - rz_strbuf_append(sb, state->pc_state == RZ_INTERP_PC_ANY ? "⊤" : "⊥"); - } - rz_strbuf_append(sb, "\n\n"); - - RzIterator *it = ht_up_as_iter_keys(state->globals); - ut64 *k; - rz_iterator_foreach(it, k) { - const char *gname = ht_up_find(state->var_name_hashes, *k, NULL); - rz_strbuf_appendf(sb, "\t%s = ", gname); - RzInterpAbstrVal *av = ht_up_find(state->globals, *k, NULL); - val_as_str(av, sb, plugin_data); - rz_strbuf_append(sb, "\n"); - } - rz_iterator_free(it); - return true; -} - -void state_as_str_short(RzInterpInstance *iset, RZ_OUT RzStrBuf *out, RzInterpAbstrState *astate) { - bool first = true; - RzIterator *it = ht_up_as_iter_keys(astate->globals); - ut64 *k; - rz_iterator_foreach(it, k) { - ut64 djb2_reg_name = *k; - RzInterpAbstrVal *av = ht_up_find(astate->globals, djb2_reg_name, NULL); - ProtoIntrprAbstrData *val = av->abstr_data; - if (!val->is_const) { - continue; - } - if (!first) { - rz_strbuf_append(out, ", "); - } - first = false; - const char *varname = ht_up_find(astate->var_name_hashes, djb2_reg_name, NULL); - rz_strbuf_appendf(out, "%s = ", varname); - iset->plugin->val_as_str(av, out, iset->interp_priv); - } -} - -bool init(void **plugin_data) { - ProtoIntrprPluginData *pdata = RZ_NEW0(ProtoIntrprPluginData); - if (!pdata) { - return NULL; - } - RZ_LOG_DEBUG("prototype: init()\n"); - pdata->bb_invocation_count = ht_uu_new(); - if (!pdata->bb_invocation_count) { - free(pdata); - return false; - } - rz_vector_init(&pdata->stack, - sizeof(ProtoInterprAbstrStackFrame), - (RzVectorFree)stack_frame_fini, NULL); - rz_vector_reserve(&pdata->stack, INITIAL_STACK_CAPACITY); - *plugin_data = pdata; - return true; -} - -bool fini(void *plugin_data) { - if (!plugin_data) { - return true; - } - RZ_LOG_DEBUG("prototype: fini()\n"); - ProtoIntrprPluginData *pdata = plugin_data; - ht_uu_free(pdata->bb_invocation_count); - rz_vector_fini(&pdata->stack); - free(pdata); - return true; -} - -bool reset(void *plugin_data) { - if (!plugin_data) { - return true; - } - RZ_LOG_DEBUG("prototype: reset()\n"); - ProtoIntrprPluginData *pdata = plugin_data; - pdata->prev_pc = UT64_MAX; - ht_uu_clear(pdata->bb_invocation_count); - memset(&pdata->call_cand, 0, sizeof(RzAnalysisCallCandidate)); - rz_vector_purge(&pdata->stack); - return true; -} - -static RzInterpPlugin rz_interpreter_plugin_prototype = { - .name = "abstr_int_prototype", - .author = "Rot127", - .version = "0.1p", - .desc = "A prototype interpreter for constant/top abstractions.", - .license = "LGPL-3.0-only", - .supported_abstractions = RZ_INTERP_ABSTRACTION_CONST, - .supported_yields = { RZ_INTERP_YIELD_KIND_XREF, RZ_INTERP_YIELD_KIND_CALL_CANDIDATE }, - .init = init, - .reset = reset, - .fini = fini, - .clone_val = clone_val, - .eval = eval, - .init_state = init_state, - .reset_state = reset_state, - .fini_state = fini_state, - .join_state = join_state, - .state_as_str = state_as_str, - .val_as_str = val_as_str -}; - -RZ_API RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype = { - .p_interpreter = &rz_interpreter_plugin_prototype, -}; - -#ifndef RZ_PLUGIN_INCORE -RZ_API RzLibStruct rizin_plugin = { - .type = RZ_LIB_TYPE_INTERPRETER, - .data = &interpreter_prototype -}; -#endif diff --git a/librz/inquiry/interpreter/prototype/eval.c b/librz/inquiry/interpreter/prototype/eval.c deleted file mode 100644 index e42a6ad0700..00000000000 --- a/librz/inquiry/interpreter/prototype/eval.c +++ /dev/null @@ -1,276 +0,0 @@ -// SPDX-FileCopyrightText: 2025 RizinOrg -// SPDX-License-Identifier: LGPL-3.0-only - -#include "eval.h" -#include "rz_analysis.h" -#include "rz_inquiry/rz_interpreter.h" -#include "rz_th.h" -#include "rz_types.h" -#include "rz_util/rz_assert.h" -#include "rz_util/rz_log.h" -#include - -bool report_yield_xref( - RzInterpRunContext *ctx, - size_t insn_pkt_size, - ut64 from, - const ProtoIntrprAbstrData *to, - RzAnalysisXRefType type) { - if (!to->is_const || rz_bv_len(to->bv) > 64) { - // Isn't reported - return true; - } - if (type == RZ_ANALYSIS_XREF_TYPE_CODE && - RZ_STR_EQ(ctx->astate->arch_name, "hexagon") && - from + insn_pkt_size == rz_bv_to_ut64(to->bv)) { - // Ugly work around. - // Because we don't have RzArch yet the Hexagon plugin adds a JUMP at the - // end of each and every instruction packet. - // This is necessary because the RzIL VM would otherwise just add 4 to the PC, - // which is too little for a packet with 2+ instructions. - // We don't want to report the code references to the next instruction - // packet. So skip them here. - return true; - } - - RzInterpYieldRBuf *yrbuf = ctx->inst->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; - rz_return_val_if_fail(yrbuf, false); - - ut64 to_addr = rz_bv_to_ut64(to->bv); - RzAnalysisXRef xref = { 0 }; - xref.bb_addr = ctx->astate->bb_addr; - xref.from = from; - xref.to = to_addr; - xref.type = type; - if (yrbuf->filter(&xref, yrbuf->filter_data->io_boundaries)) { - RZ_LOG_DEBUG("prototype: REPORT xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref.from, xref.to, rz_analysis_ref_type_tostring(xref.type)); - if (rz_th_ring_buf_put(yrbuf->rbuf, &xref) != RZ_THREAD_RING_BUF_OK) { - return false; - } - } - return true; -} - -/** - * \brief Report the store of the next PC and report it as possible return point. - */ -bool report_yield_call_candiate( - RzInterpInstance *iset, - ProtoIntrprPluginData *plugin_data) { - RzInterpYieldRBuf *cc_rbuf = iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; - rz_return_val_if_fail(cc_rbuf, false); - - RzAnalysisCallCandidate cc = { 0 }; - memcpy(&cc, &plugin_data->call_cand, sizeof(plugin_data->call_cand)); - if (rz_th_ring_buf_put(cc_rbuf->rbuf, &cc) != RZ_THREAD_RING_BUF_OK) { - return false; - } - return true; -} - -void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) { - rz_return_if_fail(dst && src && dst->bv && src->bv); - rz_bv_cast_inplace(dst->bv, rz_bv_len(src->bv), false); - rz_bv_copy(dst->bv, src->bv); - dst->is_const = src->is_const; -} - -void write_var_to_state(RzInterpAbstrState *astate, - RzILVarKind kind, - ut64 var_id, - const ProtoIntrprAbstrData *data) { - HtUP *ht_vals; - switch (kind) { - default: - rz_warn_if_reached(); - return; - case RZ_IL_VAR_KIND_GLOBAL: - ht_vals = astate->globals; - break; - case RZ_IL_VAR_KIND_LOCAL: - ht_vals = astate->locals; - break; - case RZ_IL_VAR_KIND_LOCAL_PURE: - ht_vals = astate->lets; - break; - } - RzInterpAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); - if (!av) { - if (kind == RZ_IL_VAR_KIND_GLOBAL) { - RZ_LOG_WARN("New global variable created: 0x%" PFMT64x "\n", var_id) - } - av = RZ_NEW0(RzInterpAbstrVal); - ht_up_insert(ht_vals, var_id, av); - } - if (!av->abstr_data) { - av->kind = RZ_INTERP_ABSTRACTION_CONST; - av->abstr_data = adata_new(); - } - copy_abstr_data(av->abstr_data, data); -} - -bool read_var_from_state(RzInterpAbstrState *astate, - RzILVarKind kind, - ut64 var_id, - RZ_OUT ProtoIntrprAbstrData *data) { - HtUP *ht_vals; - switch (kind) { - default: - rz_warn_if_reached(); - return false; - case RZ_IL_VAR_KIND_GLOBAL: - ht_vals = astate->globals; - break; - case RZ_IL_VAR_KIND_LOCAL: - ht_vals = astate->locals; - break; - case RZ_IL_VAR_KIND_LOCAL_PURE: - ht_vals = astate->lets; - break; - } - RzInterpAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); - if (!av || !av->abstr_data) { - // Variable doesn't exist. - // This should never happen and is a bug. - rz_warn_if_reached(); - return false; - } - copy_abstr_data(data, av->abstr_data); - return true; -} - -// Returns true if the bit vector in \p data is not zero. If it is zero or -// the abstract data is not concrete it returns false. -// -// TODO: The assumption that true != 0 is invalid. -// It depends on the architecture and must be decided by the RzArch plugin. -// State is passed due to this here as well. To make later refactoring easier. -bool abstr_is_true(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data) { - if (!data->is_const) { - return false; - } - return !rz_bv_is_zero_vector(data->bv); -} - -bool abstr_may_be_true(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data) { - if (!data->is_const) { - return true; - } - return !rz_bv_is_zero_vector(data->bv); -} - -bool abstr_may_be_false(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data) { - if (!data->is_const) { - return true; - } - return rz_bv_is_zero_vector(data->bv); -} - -bool store_abstr_data( - RzInterpInstance *iset, - RzILMemIndex mem_idx, - const ProtoIntrprAbstrData *addr, - const ProtoIntrprAbstrData *src) { - // TODO: handle with memory abstractions - return true; -} - -bool load_abstr_data( - RzInterpInstance *iset, - RzILMemIndex mem_idx, - const ProtoIntrprAbstrData *addr, - size_t n_bits, - RZ_OUT ProtoIntrprAbstrData *out) { - RzInterpIOReadRequest io_req = { 0 }; - rz_bv_cast_inplace(out->bv, n_bits, 0); - io_req.addr = addr->bv; - io_req.ld_data = out->bv; - io_req.mem_idx = mem_idx; - io_req.n_bits = n_bits; - io_req.big_endian = iset->il_ctx->config->big_endian; - if (rz_th_ring_buf_put(iset->io_request_rbuf, &io_req) != RZ_THREAD_RING_BUF_OK) { - return false; - } - RzInterpIOResult io_res = { 0 }; - if (rz_th_ring_buf_take_blocking(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { - return false; - } - if (!io_res.req_ok) { - RZ_LOG_WARN("prototype: Failed to read correct number of bytes. Requested: 0x%" PFMTSZx - " Received: 0x%" PFMT32x " bits.\n", - n_bits, rz_bv_len(out->bv)); - return false; - } - out->is_const = true; - - char *bytes = rz_bv_as_hex_string(out->bv, true); - RZ_LOG_DEBUG("prototype: READ @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); - free(bytes); - return true; -} - -bool set_abstr_pc(RzInterpAbstrState *state, ProtoIntrprAbstrData *pc, - void *plugin_data) { - rz_return_val_if_fail(state && pc, false); - ProtoIntrprPluginData *pdata = plugin_data; - if (state->pc_state == RZ_INTERP_PC_CONST) { - pdata->prev_pc = state->pc; - } else { - pdata->prev_pc = UT64_MAX; - } - if (pc->is_const) { - state->pc_state = RZ_INTERP_PC_CONST; - state->pc = rz_bv_to_ut64(pc->bv); - } else { - state->pc_state = RZ_INTERP_PC_ANY; - } - RZ_LOG_DEBUG("prototype: set_abstr_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", - pdata->prev_pc, state->pc, state->pc_state == RZ_INTERP_PC_CONST ? "Constant" : "Top"); - return true; -} - -bool set_pc(RzInterpAbstrState *state, ut64 pc, - void *plugin_data) { - rz_return_val_if_fail(state, false); - ProtoIntrprPluginData *pdata = plugin_data; - if (state->pc_state == RZ_INTERP_PC_CONST) { - pdata->prev_pc = state->pc; - } else { - pdata->prev_pc = UT64_MAX; - } - state->pc = pc; - state->pc_state = RZ_INTERP_PC_CONST; - RZ_LOG_DEBUG("prototype: set_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (Constant)\n", - pdata->prev_pc, pc); - return true; -} - -void stack_frame_fini(ProtoInterprAbstrStackFrame *frame, void *unused) { - if (!frame) { - return; - } - rz_bv_fini(&frame->return_addr); - rz_bv_fini(&frame->entry_point); -} - -void stack_frame_push(ProtoIntrprPluginData *pdata, RzBitVector *entry_point, RzBitVector *return_addr, ut64 instance) { - ProtoInterprAbstrStackFrame frame = { 0 }; - rz_bv_init(&frame.return_addr, rz_bv_len(return_addr)); - rz_bv_copy(&frame.return_addr, return_addr); - rz_bv_init(&frame.entry_point, rz_bv_len(entry_point)); - rz_bv_copy(&frame.entry_point, entry_point); - frame.instance = instance; - rz_vector_push(&pdata->stack, &frame); -} - -void stack_frame_pop(ProtoIntrprPluginData *pdata, RZ_NULLABLE ProtoInterprAbstrStackFrame *frame) { - rz_vector_pop(&pdata->stack, frame); -} - -bool stack_frame_top_ret_addr_cmp(ProtoIntrprPluginData *pdata, RzBitVector *addr) { - ProtoInterprAbstrStackFrame *frame = rz_vector_tail(&pdata->stack); - if (!frame) { - return false; - } - return rz_bv_eq(&frame->return_addr, addr); -} diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index f059fa4ef18..84bf9ca560d 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -1,27 +1,15 @@ # SPDX-FileCopyrightText: 2025 RizinOrg # SPDX-License-Identifier: LGPL-3.0-only -inquiry_plugins_list = [ - 'interpreter_prototype', -] - -inquiry_plugins = { - 'base_name': 'rz_inquiry', - 'base_struct': 'RzInquiryPlugin', - 'list': inquiry_plugins_list, -} - rz_inquiry_sources = [ 'algorithms/revng_fcn_detection.c', 'bcfg.c', 'inquiry.c', 'il_cache.c', - 'interpreter/interpreter.c', - 'interpreter/state.c', - 'interpreter/p/interpreter_prototype.c', - 'interpreter/prototype/eval_effect.c', - 'interpreter/prototype/eval_pure.c', - 'interpreter/prototype/eval.c', + 'interp/interpreter.c', + 'interp/state.c', + 'interp/val_abs_constant.c', + 'interp/eval.c', ] rz_inquiry_inc = [platform_inc] @@ -72,6 +60,5 @@ modules += { 'rz_inquiry': { 'rz_syscall', 'rz_type', 'rz_util' - ], - 'plugins': [inquiry_plugins] + ] }} From b4677f1e6cc89e9a9307b790a79294c12384626c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Tue, 30 Jun 2026 21:02:25 +0200 Subject: [PATCH 304/334] Remove some features before reafactoring --- librz/inquiry/interp/eval.c | 53 ++++--------------------- librz/inquiry/interp/eval.h | 31 --------------- librz/inquiry/interp/val_abs_constant.c | 34 ---------------- 3 files changed, 7 insertions(+), 111 deletions(-) diff --git a/librz/inquiry/interp/eval.c b/librz/inquiry/interp/eval.c index 7e8c34f6ecc..7b89445f56d 100644 --- a/librz/inquiry/interp/eval.c +++ b/librz/inquiry/interp/eval.c @@ -213,69 +213,26 @@ bool load_abstr_data( bool set_abstr_pc(RzInterpAbstrState *state, ProtoIntrprAbstrData *pc, void *plugin_data) { rz_return_val_if_fail(state && pc, false); - ProtoIntrprPluginData *pdata = plugin_data; - if (state->pc_state == RZ_INTERP_PC_CONST) { - pdata->prev_pc = state->pc; - } else { - pdata->prev_pc = UT64_MAX; - } if (pc->is_const) { state->pc_state = RZ_INTERP_PC_CONST; state->pc = rz_bv_to_ut64(pc->bv); } else { state->pc_state = RZ_INTERP_PC_ANY; } - RZ_LOG_DEBUG("prototype: set_abstr_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", - pdata->prev_pc, state->pc, state->pc_state == RZ_INTERP_PC_CONST ? "Constant" : "Top"); + RZ_LOG_DEBUG("prototype: set_abstr_pc() - Set PC: 0x%" PFMT64x " (%s)\n", + state->pc, state->pc_state == RZ_INTERP_PC_CONST ? "Constant" : "Top"); return true; } bool set_pc(RzInterpAbstrState *state, ut64 pc, void *plugin_data) { rz_return_val_if_fail(state, false); - ProtoIntrprPluginData *pdata = plugin_data; - if (state->pc_state == RZ_INTERP_PC_CONST) { - pdata->prev_pc = state->pc; - } else { - pdata->prev_pc = UT64_MAX; - } state->pc = pc; state->pc_state = RZ_INTERP_PC_CONST; - RZ_LOG_DEBUG("prototype: set_pc() - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (Constant)\n", - pdata->prev_pc, pc); + RZ_LOG_DEBUG("prototype: set_pc() - Set PC: 0x%" PFMT64x " (Constant)\n", pc); return true; } -void stack_frame_fini(ProtoInterprAbstrStackFrame *frame, void *unused) { - if (!frame) { - return; - } - rz_bv_fini(&frame->return_addr); - rz_bv_fini(&frame->entry_point); -} - -void stack_frame_push(ProtoIntrprPluginData *pdata, RzBitVector *entry_point, RzBitVector *return_addr, ut64 instance) { - ProtoInterprAbstrStackFrame frame = { 0 }; - rz_bv_init(&frame.return_addr, rz_bv_len(return_addr)); - rz_bv_copy(&frame.return_addr, return_addr); - rz_bv_init(&frame.entry_point, rz_bv_len(entry_point)); - rz_bv_copy(&frame.entry_point, entry_point); - frame.instance = instance; - rz_vector_push(&pdata->stack, &frame); -} - -void stack_frame_pop(ProtoIntrprPluginData *pdata, RZ_NULLABLE ProtoInterprAbstrStackFrame *frame) { - rz_vector_pop(&pdata->stack, frame); -} - -bool stack_frame_top_ret_addr_cmp(ProtoIntrprPluginData *pdata, RzBitVector *addr) { - ProtoInterprAbstrStackFrame *frame = rz_vector_tail(&pdata->stack); - if (!frame) { - return false; - } - return rz_bv_eq(&frame->return_addr, addr); -} - static bool value_indicates_ret_addr_write(RzInterpRunContext *ctx, ProtoIntrprAbstrData *val) { return val->is_const && (rz_bv_to_ut64(val->bv) == ctx->astate->bb_addr + ctx->astate->bb_size || @@ -360,6 +317,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, plugin_data->call_cand.target = target; report_yield_call_candiate(ctx->inst, plugin_data); +#if 0 // For a call, we need to push a new frame. RzBitVector ret_addr = { 0 }; rz_bv_init(&ret_addr, rz_bv_len(eval_out.bv)); @@ -369,13 +327,16 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, ut64 ic = ht_uu_find(plugin_data->bb_invocation_count, plugin_data->call_cand.target, &found); stack_frame_push(plugin_data, eval_out.bv, &ret_addr, !found ? 0 : ic); rz_bv_fini(&ret_addr); +#endif xref_type = RZ_ANALYSIS_XREF_TYPE_CALL; } +#if 0 if (xref_type == RZ_ANALYSIS_XREF_TYPE_CODE && stack_frame_top_ret_addr_cmp(plugin_data, eval_out.bv)) { stack_frame_pop(plugin_data, NULL); xref_type = RZ_ANALYSIS_XREF_TYPE_RETURN; } +#endif report_yield_xref(ctx, insn_pkt_size, pc, &eval_out, xref_type); diff --git a/librz/inquiry/interp/eval.h b/librz/inquiry/interp/eval.h index caa4dac6b43..2214608f9d5 100644 --- a/librz/inquiry/interp/eval.h +++ b/librz/inquiry/interp/eval.h @@ -25,34 +25,7 @@ typedef struct { } ProtoIntrprAbstrData; typedef struct { - /** - * \brief The procedure's entry point this frame was initialized at. - */ - RzBitVector entry_point; - /** - * \brief The number of times that frame was initialized at the entry point. - * This is equivalent to the number of times the function was called at this entry point. - */ - ut64 instance; - /** - * \brief The return address of the procedure. - * TODO: The return address might be wrong in case of tail calls. - * The prototype doesn't really check what address was stored in the link register - * or on the stack (due to missing abstraction of archs calling convention). - * Instead, it simply stores the instruction address which comes after the call - * in memory. - */ - RzBitVector return_addr; - - // TODO: The abstract stack pointer at the point of procedure entry should be tracked here. - // But this needs to wait until we have a proper memory model implemented. -} ProtoInterprAbstrStackFrame; - -typedef struct { - HtUU *bb_invocation_count; RzAnalysisCallCandidate call_cand; ///< Data of a call candidate. - RzVector /**/ stack; ///< The call frame stack. - ut64 prev_pc; ///< Previous PC. Set to UT64_MAX if it was invalid. } ProtoIntrprPluginData; /** @@ -136,10 +109,6 @@ bool set_pc(RzInterpAbstrState *state, ut64 pc, bool set_abstr_pc(RzInterpAbstrState *state, ProtoIntrprAbstrData *pc, void *plugin_data); -void stack_frame_fini(ProtoInterprAbstrStackFrame *frame, void *unused); -void stack_frame_push(ProtoIntrprPluginData *pdata, RzBitVector *entry_point, RzBitVector *return_addr, ut64 instance); -void stack_frame_pop(ProtoIntrprPluginData *pdata, RZ_NULLABLE ProtoInterprAbstrStackFrame *frame); -bool stack_frame_top_ret_addr_cmp(ProtoIntrprPluginData *pdata, RzBitVector *addr); void state_as_str_short(RzInterpInstance *iset, RZ_OUT RzStrBuf *out, RzInterpAbstrState *astate); #endif // PROTOYPE_EVAL_H diff --git a/librz/inquiry/interp/val_abs_constant.c b/librz/inquiry/interp/val_abs_constant.c index 20988aa8de0..5d14aef5370 100644 --- a/librz/inquiry/interp/val_abs_constant.c +++ b/librz/inquiry/interp/val_abs_constant.c @@ -10,7 +10,6 @@ #include #include -#include "rz_util/ht_uu.h" #include "rz_util/rz_bitvector.h" static void adata_free(ProtoIntrprAbstrData *adata) { @@ -587,8 +586,6 @@ RZ_IPI bool interpreter_prototype_eval_pure( } -#define INITIAL_STACK_CAPACITY 8 - #define MAX_INVOCATIONS_PER_BLOCK 3 bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, @@ -610,23 +607,6 @@ static bool eval(RZ_NONNULL RzInterpRunContext *ctx, void *plugin_data) { ProtoIntrprPluginData *pdata = plugin_data; - // Check invocation count of the current address. - // Never execute the same address more than MAX_INVOCATIONS_PER_BLOCK times. - bool found = false; - HtUUKv *ic_pc = ht_uu_find_kv(pdata->bb_invocation_count, il_bb->addr, &found); - if (found) { - ic_pc->value++; - RZ_LOG_DEBUG("prototype: Eval BLOCK (ic: %" PFMT64d ") = 0x%" PFMT64x "\n", ic_pc->value, il_bb->addr); - if (ic_pc->value > MAX_INVOCATIONS_PER_BLOCK) { - // TODO: Make it configurable - RZ_LOG_DEBUG("prototype: Reached maximum number of invocations of basic block at 0x%" PFMT64x ". Skipping it.\n", il_bb->addr) - set_pc(ctx->astate, il_bb->addr + il_bb->size, plugin_data); - return true; - } - } else { - ht_uu_update(pdata->bb_invocation_count, il_bb->addr, 1); - } - // Reset call candidate tracking for each basic block. memset(&pdata->call_cand, 0, sizeof(pdata->call_cand)); @@ -849,15 +829,6 @@ bool init(void **plugin_data) { return NULL; } RZ_LOG_DEBUG("prototype: init()\n"); - pdata->bb_invocation_count = ht_uu_new(); - if (!pdata->bb_invocation_count) { - free(pdata); - return false; - } - rz_vector_init(&pdata->stack, - sizeof(ProtoInterprAbstrStackFrame), - (RzVectorFree)stack_frame_fini, NULL); - rz_vector_reserve(&pdata->stack, INITIAL_STACK_CAPACITY); *plugin_data = pdata; return true; } @@ -868,8 +839,6 @@ bool fini(void *plugin_data) { } RZ_LOG_DEBUG("prototype: fini()\n"); ProtoIntrprPluginData *pdata = plugin_data; - ht_uu_free(pdata->bb_invocation_count); - rz_vector_fini(&pdata->stack); free(pdata); return true; } @@ -880,10 +849,7 @@ bool reset(void *plugin_data) { } RZ_LOG_DEBUG("prototype: reset()\n"); ProtoIntrprPluginData *pdata = plugin_data; - pdata->prev_pc = UT64_MAX; - ht_uu_clear(pdata->bb_invocation_count); memset(&pdata->call_cand, 0, sizeof(RzAnalysisCallCandidate)); - rz_vector_purge(&pdata->stack); return true; } From e4beb36bd43511f4b31023d4db7dad9bf643f8fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Tue, 30 Jun 2026 21:17:20 +0200 Subject: [PATCH 305/334] Move plugin data out of plugin --- librz/include/rz_inquiry/rz_interpreter.h | 28 ++--- librz/inquiry/interp/eval.c | 74 ++++++------ librz/inquiry/interp/eval.h | 19 +-- librz/inquiry/interp/interpreter.c | 21 +--- librz/inquiry/interp/val_abs_constant.c | 139 ++++++++-------------- 5 files changed, 108 insertions(+), 173 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index a1cae20d088..e556159f53f 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -162,31 +162,30 @@ typedef struct { /** * \brief Clones the given abstract value. */ - RZ_OWN RzInterpAbstrVal *(*clone_val)(const RzInterpAbstrVal *val, void *plugin_data); + RZ_OWN RzInterpAbstrVal *(*clone_val)(const RzInterpAbstrVal *val); /** * \brief Initializes the abstract state. */ - bool (*init_state)(RZ_BORROW RzInterpAbstrState *state, void *plugin_data); + bool (*init_state)(RZ_BORROW RzInterpAbstrState *state); /** * \brief Reset the abstract state. */ - bool (*reset_state)(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point, void *plugin_data); + bool (*reset_state)(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point); /** * \brief Closes the abstract state and frees all its abstract data and sets the pointers to NULL. */ - bool (*fini_state)(RZ_BORROW RzInterpAbstrState *state, void *plugin_data); + bool (*fini_state)(RZ_BORROW RzInterpAbstrState *state); /** * \brief Performs the join operation on states (least upper bound, lattice theory) * \return True if a was changed */ - bool (*join_state)(RZ_BORROW RZ_INOUT RzInterpAbstrState *a, RZ_BORROW RZ_IN const RzInterpAbstrState *b, void *plugin_data); + bool (*join_state)(RZ_BORROW RZ_INOUT RzInterpAbstrState *a, RZ_BORROW RZ_IN const RzInterpAbstrState *b); /** * \brief Evaluates an effect with the mutable state. */ bool (*eval)(RZ_NONNULL RzInterpRunContext *ctx, - RZ_NONNULL const RzILCacheBlock *il_bb, - void *plugin_data); + RZ_NONNULL const RzILCacheBlock *il_bb); /** * \brief Builds a string for printing an abstract value. @@ -194,8 +193,7 @@ typedef struct { * \return Returns false in case of error, True otherwise. */ bool (*val_as_str)(RZ_NONNULL const RzInterpAbstrVal *state, - RZ_NONNULL RZ_OUT RzStrBuf *str_buf, - void *plugin_data); + RZ_NONNULL RZ_OUT RzStrBuf *str_buf); /** * \brief Builds a string for printing the current state. @@ -203,8 +201,7 @@ typedef struct { * \return Returns false in case of error, True otherwise. */ bool (*state_as_str)(RZ_NONNULL const RzInterpAbstrState *state, - RZ_NONNULL RZ_OUT RzStrBuf *str_buf, - void *plugin_data); + RZ_NONNULL RZ_OUT RzStrBuf *str_buf); } RzInterpPlugin; typedef struct { @@ -259,10 +256,6 @@ struct rz_interp_instance_t { * \brief The interpreter plugin. */ RzInterpPlugin *plugin; - /** - * \brief The private data of a single interpreter thread. - */ - RZ_BORROW void *interp_priv; }; RZ_API RZ_OWN RzInterpRunState *rz_interp_run_state_new(); @@ -299,9 +292,12 @@ RZ_API void rz_interp_instance_free(RZ_OWN RZ_NULLABLE RzInterpInstance *iset); struct rz_interp_run_context_t { RzInterpInstance *inst; //< parent interpreter thread - RzInterpAbstrState *astate; ///< The abstract state of the interpreter. RzList /**/ *queue; ///< States that have to be interpreted still. If this is empty, a fixpoint has been reached. HtUP /**/ *pc_states; ///< Currently discovered states at the entries of blocks. + + // Tracking data local to a single block interpretation + RzInterpAbstrState *astate; ///< The abstract state of the interpreter. + RzAnalysisCallCandidate call_cand; ///< Data of a call candidate. }; /* diff --git a/librz/inquiry/interp/eval.c b/librz/inquiry/interp/eval.c index 7b89445f56d..4b3de3d77a6 100644 --- a/librz/inquiry/interp/eval.c +++ b/librz/inquiry/interp/eval.c @@ -55,13 +55,12 @@ bool report_yield_xref( * \brief Report the store of the next PC and report it as possible return point. */ bool report_yield_call_candiate( - RzInterpInstance *iset, - ProtoIntrprPluginData *plugin_data) { - RzInterpYieldRBuf *cc_rbuf = iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; + RzInterpRunContext *ctx) { + RzInterpYieldRBuf *cc_rbuf = ctx->inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; rz_return_val_if_fail(cc_rbuf, false); RzAnalysisCallCandidate cc = { 0 }; - memcpy(&cc, &plugin_data->call_cand, sizeof(plugin_data->call_cand)); + memcpy(&cc, &ctx->call_cand, sizeof(ctx->call_cand)); if (rz_th_ring_buf_put(cc_rbuf->rbuf, &cc) != RZ_THREAD_RING_BUF_OK) { return false; } @@ -210,8 +209,7 @@ bool load_abstr_data( return true; } -bool set_abstr_pc(RzInterpAbstrState *state, ProtoIntrprAbstrData *pc, - void *plugin_data) { +bool set_abstr_pc(RzInterpAbstrState *state, ProtoIntrprAbstrData *pc) { rz_return_val_if_fail(state && pc, false); if (pc->is_const) { state->pc_state = RZ_INTERP_PC_CONST; @@ -224,8 +222,7 @@ bool set_abstr_pc(RzInterpAbstrState *state, ProtoIntrprAbstrData *pc, return true; } -bool set_pc(RzInterpAbstrState *state, ut64 pc, - void *plugin_data) { +bool set_pc(RzInterpAbstrState *state, ut64 pc) { rz_return_val_if_fail(state, false); state->pc = pc; state->pc_state = RZ_INTERP_PC_CONST; @@ -243,8 +240,7 @@ static bool value_indicates_ret_addr_write(RzInterpRunContext *ctx, ProtoIntrprA RZ_IPI bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, const RzILOpEffect *effect, - size_t insn_pkt_size, - ProtoIntrprPluginData *plugin_data) { + size_t insn_pkt_size) { STACK_ABSTR_DATA_OUT(eval_out); rz_return_val_if_fail(ctx->astate->pc_state == RZ_INTERP_PC_CONST, false); ut64 pc = ctx->astate->pc; @@ -265,44 +261,44 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, if (!rz_bv_add_inplace(npc.bv, pc->bv, NULL)) { goto error; } - set_abstr_pc(iset->astate, &npc, plugin_data); + set_abstr_pc(iset->astate, &npc); #endif break; } case RZ_IL_OP_SEQ: { - if (!interpreter_prototype_eval_effect(ctx, effect->op.seq.x, insn_pkt_size, plugin_data)) { + if (!interpreter_prototype_eval_effect(ctx, effect->op.seq.x, insn_pkt_size)) { goto error; } - if (!interpreter_prototype_eval_effect(ctx, effect->op.seq.y, insn_pkt_size, plugin_data)) { + if (!interpreter_prototype_eval_effect(ctx, effect->op.seq.y, insn_pkt_size)) { goto error; } break; } case RZ_IL_OP_SET: { ut64 vhash = effect->op.set.hash; - if (!interpreter_prototype_eval_pure(ctx, effect->op.set.x, &eval_out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, effect->op.set.x, &eval_out)) { goto error; } RzILVarKind kind = effect->op.set.is_local ? RZ_IL_VAR_KIND_LOCAL : RZ_IL_VAR_KIND_GLOBAL; write_var_to_state(ctx->astate, kind, vhash, &eval_out); if (value_indicates_ret_addr_write(ctx, &eval_out) && kind == RZ_IL_VAR_KIND_GLOBAL) { - plugin_data->call_cand.store_addr = pc; - plugin_data->call_cand.npc = ctx->astate->bb_addr + ctx->astate->bb_size; - plugin_data->call_cand.bb_addr = ctx->astate->bb_addr; - plugin_data->call_cand.in_mem = false; + ctx->call_cand.store_addr = pc; + ctx->call_cand.npc = ctx->astate->bb_addr + ctx->astate->bb_size; + ctx->call_cand.bb_addr = ctx->astate->bb_addr; + ctx->call_cand.in_mem = false; } break; } case RZ_IL_OP_JMP: { - if (!interpreter_prototype_eval_pure(ctx, effect->op.jmp.dst, &eval_out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, effect->op.jmp.dst, &eval_out)) { goto error; } if (!eval_out.is_const) { RZ_LOG_DEBUG("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", pc); } ut64 target = rz_bv_to_ut64(eval_out.bv); - bool is_call = plugin_data->call_cand.store_addr; + bool is_call = !!ctx->call_cand.store_addr; RZ_LOG_DEBUG("prototype: JMP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", pc, target, eval_out.is_const ? "Concrete" : "Abstract"); @@ -313,18 +309,18 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, if (is_call) { // An instruction in this basic block stored the next PC. // Report a call candidate and assume this jump is a call. - plugin_data->call_cand.candidate_addr = pc; - plugin_data->call_cand.target = target; - report_yield_call_candiate(ctx->inst, plugin_data); + ctx->call_cand.candidate_addr = pc; + ctx->call_cand.target = target; + report_yield_call_candiate(ctx); #if 0 // For a call, we need to push a new frame. RzBitVector ret_addr = { 0 }; rz_bv_init(&ret_addr, rz_bv_len(eval_out.bv)); - rz_bv_set_from_ut64(&ret_addr, plugin_data->call_cand.npc); + rz_bv_set_from_ut64(&ret_addr->call_cand.npc); bool found = false; - ut64 ic = ht_uu_find(plugin_data->bb_invocation_count, plugin_data->call_cand.target, &found); + ut64 ic = ht_uu_find(plugin_data->bb_invocation_count->call_cand.target, &found); stack_frame_push(plugin_data, eval_out.bv, &ret_addr, !found ? 0 : ic); rz_bv_fini(&ret_addr); #endif @@ -342,19 +338,19 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, xref_type); // Clear the call candidate tracking variable. - memset(&plugin_data->call_cand, 0, sizeof(plugin_data->call_cand)); + memset(&ctx->call_cand, 0, sizeof(ctx->call_cand)); } if (is_call) { // For calls, assume control flow will continue like fallthrough. // TODO: set data to top that may be changed by the call } else { - set_abstr_pc(ctx->astate, &eval_out, plugin_data); + set_abstr_pc(ctx->astate, &eval_out); } break; } case RZ_IL_OP_BRANCH: { - if (!interpreter_prototype_eval_pure(ctx, effect->op.branch.condition, &eval_out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, effect->op.branch.condition, &eval_out)) { goto error; } bool may_be_true = abstr_may_be_true(ctx->inst, &eval_out); @@ -363,16 +359,16 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, RzInterpAbstrState *true_state = rz_interp_abstr_state_clone(ctx->inst, ctx->astate); RzInterpAbstrState *false_state = ctx->astate; ctx->astate = true_state; - if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.true_eff, insn_pkt_size, plugin_data)) { + if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.true_eff, insn_pkt_size)) { goto error; } ctx->astate = false_state; - if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.false_eff, insn_pkt_size, plugin_data)) { + if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.false_eff, insn_pkt_size)) { goto error; } if (true_state->pc_state == false_state->pc_state && true_state->pc == false_state->pc) { // identical target location, simply join the data and continue - ctx->inst->plugin->join_state(true_state, false_state, ctx->inst->interp_priv); + ctx->inst->plugin->join_state(true_state, false_state); } else { // different jump targets, branch rather than resorting to top pc rz_interp_run_push(ctx, true_state); @@ -380,11 +376,11 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, } rz_interp_abstr_state_free(true_state); } else if (may_be_true) { - if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.true_eff, insn_pkt_size, plugin_data)) { + if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.true_eff, insn_pkt_size)) { goto error; } } else if (may_be_false) { - if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.false_eff, insn_pkt_size, plugin_data)) { + if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.false_eff, insn_pkt_size)) { goto error; } } @@ -395,7 +391,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, STACK_ABSTR_DATA_OUT(st_addr); RzILOpPure *key = effect->code == RZ_IL_OP_STORE ? effect->op.store.key : effect->op.storew.key; RzILMemIndex mem_idx = effect->code == RZ_IL_OP_STORE ? 0 : effect->op.storew.mem; - if (!interpreter_prototype_eval_pure(ctx, key, &st_addr, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, key, &st_addr)) { RZ_LOG_ERROR("prototype: STORE/STOREW key failed to evaluate.\n"); rz_bv_fini(st_addr.bv); goto error; @@ -415,7 +411,7 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, } RzILOpPure *pval = effect->code == RZ_IL_OP_STORE ? effect->op.store.value : effect->op.storew.value; - if (!interpreter_prototype_eval_pure(ctx, pval, &eval_out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pval, &eval_out)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); rz_bv_fini(st_addr.bv); goto error; @@ -425,10 +421,10 @@ RZ_IPI bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, break; } if (value_indicates_ret_addr_write(ctx, &eval_out)) { - plugin_data->call_cand.store_addr = pc; - plugin_data->call_cand.npc = ctx->astate->bb_addr + ctx->astate->bb_size; - plugin_data->call_cand.bb_addr = ctx->astate->bb_addr; - plugin_data->call_cand.in_mem = true; + ctx->call_cand.store_addr = pc; + ctx->call_cand.npc = ctx->astate->bb_addr + ctx->astate->bb_size; + ctx->call_cand.bb_addr = ctx->astate->bb_addr; + ctx->call_cand.in_mem = true; } report_yield_xref(ctx, insn_pkt_size, pc, &st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); if (!store_abstr_data(ctx->inst, mem_idx, &st_addr, &eval_out)) { diff --git a/librz/inquiry/interp/eval.h b/librz/inquiry/interp/eval.h index 2214608f9d5..799c4381e4c 100644 --- a/librz/inquiry/interp/eval.h +++ b/librz/inquiry/interp/eval.h @@ -24,10 +24,6 @@ typedef struct { RzBitVector *bv; } ProtoIntrprAbstrData; -typedef struct { - RzAnalysisCallCandidate call_cand; ///< Data of a call candidate. -} ProtoIntrprPluginData; - /** * \brief In bytes * @@ -84,13 +80,11 @@ bool load_abstr_data( RZ_IPI bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, const RzILOpEffect *effect, - size_t nop_pc_inc, - ProtoIntrprPluginData *plugin_data); + size_t nop_pc_inc); RZ_IPI bool interpreter_prototype_eval_pure( RzInterpRunContext *ctx, const RzILOpPure *pure, - RZ_OUT ProtoIntrprAbstrData *out, - ProtoIntrprPluginData *plugin_data); + RZ_OUT ProtoIntrprAbstrData *out); bool report_yield_xref( RzInterpRunContext *ctx, @@ -100,14 +94,11 @@ bool report_yield_xref( RzAnalysisXRefType type); bool report_yield_call_candiate( - RzInterpInstance *iset, - ProtoIntrprPluginData *plugin_data); + RzInterpRunContext *ctx); -bool set_pc(RzInterpAbstrState *state, ut64 pc, - void *plugin_data); +bool set_pc(RzInterpAbstrState *state, ut64 pc); -bool set_abstr_pc(RzInterpAbstrState *state, ProtoIntrprAbstrData *pc, - void *plugin_data); +bool set_abstr_pc(RzInterpAbstrState *state, ProtoIntrprAbstrData *pc); void state_as_str_short(RzInterpInstance *iset, RZ_OUT RzStrBuf *out, RzInterpAbstrState *astate); diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 466c4fc2b0a..a86c893422a 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -140,7 +140,7 @@ static HtUP *var_set_clone(const RzInterpInstance *iset, HtUP *vars) { RzIterator *it = ht_up_as_iter_keys(vars); ut64 *key; rz_iterator_foreach(it, key) { - RzInterpAbstrVal *val = iset->plugin->clone_val(ht_up_find(vars, *key, NULL), iset->interp_priv); + RzInterpAbstrVal *val = iset->plugin->clone_val(ht_up_find(vars, *key, NULL)); if (!val) { continue; } @@ -292,7 +292,7 @@ RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_ rz_strbuf_fini(&sb); RzInterpAbstrState *existing = ht_up_find(ctx->pc_states, as->pc, NULL); if (existing) { - if (ctx->inst->plugin->join_state(existing, as, ctx->inst->interp_priv) && !existing->uninterpreted) { + if (ctx->inst->plugin->join_state(existing, as) && !existing->uninterpreted) { existing->uninterpreted = true; rz_list_push(ctx->queue, existing); } @@ -338,9 +338,10 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { RzInterpAbstrState *estate = rz_interp_abstr_state_new(cur->arch, inst->il_ctx); if (inst->plugin->reset) { // TODO: should rather be local to the RzInterpRunContext if it is reset every run - inst->plugin->reset(inst->interp_priv); + // inst->plugin->reset(inst->interp_priv); + memset(&ctx.call_cand, 0, sizeof(ctx.call_cand)); } - if (!inst->plugin->init_state(estate, inst->interp_priv) || !inst->plugin->reset_state(estate, entry_point, inst->interp_priv)) { + if (!inst->plugin->init_state(estate) || !inst->plugin->reset_state(estate, entry_point)) { rz_warn_if_reached(); rz_interp_abstr_state_free(estate); goto cleanup; @@ -379,7 +380,7 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { ctx.astate->bb_addr = il_bb->addr; ctx.astate->bb_size = il_bb->size; // Evaluate the effect on the abstract state. - if (!inst->plugin->eval(&ctx, il_bb, inst->interp_priv)) { + if (!inst->plugin->eval(&ctx, il_bb)) { RZ_LOG_DEBUG("interpreter: Eval failed\n"); success = false; break; @@ -416,9 +417,6 @@ RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *inst) { // Start interpretation // - if (inst->plugin->init) { - inst->plugin->init(&inst->interp_priv); - } // TODO: It is probably better to make the following stuff while-loops. // Because otherwise it doesn't make sense without the docs. @@ -459,13 +457,6 @@ RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *inst) { TERM: { RZ_LOG_DEBUG("interpreter: Enter TERM\n"); rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_TERM); - if (inst->plugin->fini && inst->interp_priv) { - RZ_FREE_CUSTOM(inst->interp_priv, inst->plugin->fini); - } - - if (inst->plugin->fini && inst->interp_priv) { - RZ_FREE_CUSTOM(inst->interp_priv, inst->plugin->fini); - } return success; } } diff --git a/librz/inquiry/interp/val_abs_constant.c b/librz/inquiry/interp/val_abs_constant.c index 5d14aef5370..9c84c716746 100644 --- a/librz/inquiry/interp/val_abs_constant.c +++ b/librz/inquiry/interp/val_abs_constant.c @@ -31,8 +31,7 @@ static void adata_free(ProtoIntrprAbstrData *adata) { RZ_IPI bool interpreter_prototype_eval_pure( RzInterpRunContext *ctx, const RzILOpPure *pure, - RZ_OUT ProtoIntrprAbstrData *out, - ProtoIntrprPluginData *plugin_data) { + RZ_OUT ProtoIntrprAbstrData *out) { switch (pure->code) { default: case RZ_IL_OP_VAR: { @@ -46,13 +45,13 @@ RZ_IPI bool interpreter_prototype_eval_pure( } case RZ_IL_OP_LET: { ut64 vhash = pure->op.let.hash; - if (!interpreter_prototype_eval_pure(ctx, pure->op.let.exp, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.let.exp, out)) { RZ_LOG_ERROR("prototype: LET expression failed to evaluate.\n"); return false; } write_var_to_state(ctx->astate, RZ_IL_VAR_KIND_LOCAL_PURE, vhash, out); // Evaluate body - if (!interpreter_prototype_eval_pure(ctx, pure->op.let.body, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.let.body, out)) { RZ_LOG_ERROR("prototype: LET body failed to evaluate.\n"); return false; } @@ -61,7 +60,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } case RZ_IL_OP_ITE: { - if (!interpreter_prototype_eval_pure(ctx, pure->op.ite.condition, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.ite.condition, out)) { RZ_LOG_ERROR("prototype: ITE condition failed to evaluate.\n"); return false; } @@ -71,12 +70,12 @@ RZ_IPI bool interpreter_prototype_eval_pure( } if (abstr_is_true(ctx->inst, out)) { - if (!interpreter_prototype_eval_pure(ctx, pure->op.ite.x, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.ite.x, out)) { RZ_LOG_ERROR("prototype: ITE x failed to evaluate.\n"); return false; } } else { - if (!interpreter_prototype_eval_pure(ctx, pure->op.ite.y, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.ite.y, out)) { RZ_LOG_ERROR("prototype: ITE y failed to evaluate.\n"); return false; } @@ -98,7 +97,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( out->is_const = true; break; case RZ_IL_OP_CAST: { - if (!interpreter_prototype_eval_pure(ctx, pure->op.cast.val, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.cast.val, out)) { RZ_LOG_ERROR("prototype: CAST val failed to evaluate.\n"); return false; } @@ -106,7 +105,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(fill_bit); - if (!interpreter_prototype_eval_pure(ctx, pure->op.cast.fill, &fill_bit, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.cast.fill, &fill_bit)) { RZ_LOG_ERROR("prototype: CAST fill failed to evaluate.\n"); return false; } @@ -124,7 +123,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; case RZ_IL_OP_APPEND: { STACK_ABSTR_DATA_OUT(high); - if (!interpreter_prototype_eval_pure(ctx, pure->op.append.high, &high, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.append.high, &high)) { RZ_LOG_ERROR("prototype: APPEND high failed to evaluate.\n"); return false; } @@ -132,7 +131,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(high.bv); goto map_to_top; } - if (!interpreter_prototype_eval_pure(ctx, pure->op.append.low, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.append.low, out)) { RZ_LOG_ERROR("prototype: APPEND low failed to evaluate.\n"); rz_bv_fini(high.bv); return false; @@ -150,7 +149,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_LOGNOT: case RZ_IL_OP_INV: { RzILOpPure *x = pure->code == RZ_IL_OP_INV ? pure->op.boolinv.x : pure->op.lognot.bv; - if (!interpreter_prototype_eval_pure(ctx, x, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, x, out)) { RZ_LOG_ERROR("prototype: INV x failed to evaluate.\n"); return false; } @@ -163,7 +162,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_AND: { RzILOpPure *px = pure->code == RZ_IL_OP_AND ? pure->op.booland.x : pure->op.logand.x; RzILOpPure *py = pure->code == RZ_IL_OP_AND ? pure->op.booland.y : pure->op.logand.y; - if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out)) { RZ_LOG_ERROR("prototype: AND x failed to evaluate.\n"); return false; } @@ -171,7 +170,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y)) { RZ_LOG_ERROR("prototype: AND y failed to evaluate.\n"); return false; } @@ -190,7 +189,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_OR: { RzILOpPure *px = pure->code == RZ_IL_OP_OR ? pure->op.boolor.x : pure->op.logor.x; RzILOpPure *py = pure->code == RZ_IL_OP_OR ? pure->op.boolor.y : pure->op.logor.y; - if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out)) { RZ_LOG_ERROR("prototype: OR x failed to evaluate.\n"); return false; } @@ -198,7 +197,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y)) { RZ_LOG_ERROR("prototype: OR y failed to evaluate.\n"); return false; } @@ -217,7 +216,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_XOR: { RzILOpPure *px = pure->code == RZ_IL_OP_XOR ? pure->op.boolxor.x : pure->op.logxor.x; RzILOpPure *py = pure->code == RZ_IL_OP_XOR ? pure->op.boolxor.y : pure->op.logxor.y; - if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out)) { RZ_LOG_ERROR("prototype: XOR x failed to evaluate.\n"); return false; } @@ -225,7 +224,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y)) { RZ_LOG_ERROR("prototype: XOR y failed to evaluate.\n"); return false; } @@ -262,7 +261,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( truth_test = rz_bv_msb; break; } - if (!interpreter_prototype_eval_pure(ctx, bv, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, bv, out)) { RZ_LOG_ERROR("prototype: MSB/LSB/IS_ZERO bv failed to evaluate.\n"); return false; } @@ -276,7 +275,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } case RZ_IL_OP_NEG: { - if (!interpreter_prototype_eval_pure(ctx, pure->op.neg.bv, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pure->op.neg.bv, out)) { RZ_LOG_ERROR("prototype: NEG bv failed to evaluate.\n"); return false; } @@ -288,7 +287,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_ADD: { RzILOpPure *px = pure->op.add.x; RzILOpPure *py = pure->op.add.y; - if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out)) { RZ_LOG_ERROR("prototype: ADD x failed to evaluate.\n"); return false; } @@ -296,7 +295,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y)) { RZ_LOG_ERROR("prototype: ADD y failed to evaluate.\n"); return false; } @@ -314,7 +313,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_SUB: { RzILOpPure *px = pure->op.sub.x; RzILOpPure *py = pure->op.sub.y; - if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); return false; } @@ -322,7 +321,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y)) { RZ_LOG_ERROR("prototype: SUB y failed to evaluate.\n"); return false; } @@ -342,7 +341,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzILOpPure *px = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.x : pure->op.shiftl.x; RzILOpPure *py = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.y : pure->op.shiftl.y; RzILOpPure *pfill_bit = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.fill_bit : pure->op.shiftl.fill_bit; - if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) x failed to evaluate.\n"); return false; } @@ -350,7 +349,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) y failed to evaluate.\n"); return false; } @@ -359,7 +358,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(fill_bit); - if (!interpreter_prototype_eval_pure(ctx, pfill_bit, &fill_bit, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, pfill_bit, &fill_bit)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) fill_bit failed to evaluate.\n"); return false; } @@ -405,7 +404,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } - if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out)) { RZ_LOG_ERROR("prototype: CMP x failed to evaluate.\n"); return false; } @@ -413,7 +412,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y)) { RZ_LOG_ERROR("prototype: CMP y failed to evaluate.\n"); return false; } @@ -432,7 +431,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( STACK_ABSTR_DATA_OUT(ld_addr); RzILOpPure *key = pure->code == RZ_IL_OP_LOAD ? pure->op.load.key : pure->op.loadw.key; RzILMemIndex mem_idx = pure->code == RZ_IL_OP_LOAD ? 0 : pure->op.loadw.mem; - if (!interpreter_prototype_eval_pure(ctx, key, &ld_addr, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, key, &ld_addr)) { RZ_LOG_ERROR("prototype: LOAD/LOADW key failed to evaluate.\n"); rz_bv_fini(ld_addr.bv); return false; @@ -463,7 +462,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_MUL: { RzILOpPure *px = pure->op.mul.x; RzILOpPure *py = pure->op.mul.y; - if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out)) { RZ_LOG_ERROR("prototype: MUL x failed to evaluate.\n"); return false; } @@ -471,7 +470,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y)) { RZ_LOG_ERROR("prototype: MUL y failed to evaluate.\n"); return false; } @@ -489,7 +488,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_MOD: { RzILOpPure *px = pure->op.mod.x; RzILOpPure *py = pure->op.mod.y; - if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out)) { RZ_LOG_ERROR("prototype: MOD x failed to evaluate.\n"); return false; } @@ -497,7 +496,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y)) { RZ_LOG_ERROR("prototype: MOD y failed to evaluate.\n"); return false; } @@ -515,7 +514,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_DIV: { RzILOpPure *px = pure->op.div.x; RzILOpPure *py = pure->op.div.y; - if (!interpreter_prototype_eval_pure(ctx, px, out, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, px, out)) { RZ_LOG_ERROR("prototype: DIV x failed to evaluate.\n"); return false; } @@ -523,7 +522,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y, plugin_data)) { + if (!interpreter_prototype_eval_pure(ctx, py, &y)) { RZ_LOG_ERROR("prototype: DIV y failed to evaluate.\n"); return false; } @@ -589,10 +588,9 @@ RZ_IPI bool interpreter_prototype_eval_pure( #define MAX_INVOCATIONS_PER_BLOCK 3 bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, - RZ_NONNULL RZ_OUT RzStrBuf *sb, - void *plugin_data); + RZ_NONNULL RZ_OUT RzStrBuf *sb); -RZ_OWN RzInterpAbstrVal *clone_val(const RzInterpAbstrVal *val, void *plugin_data) { +RZ_OWN RzInterpAbstrVal *clone_val(const RzInterpAbstrVal *val) { ProtoIntrprAbstrData *r = RZ_NEW0(ProtoIntrprAbstrData); if (!r) { return NULL; @@ -602,13 +600,10 @@ RZ_OWN RzInterpAbstrVal *clone_val(const RzInterpAbstrVal *val, void *plugin_dat return r; } -static bool eval(RZ_NONNULL RzInterpRunContext *ctx, - RZ_NONNULL const RzILCacheBlock *il_bb, - void *plugin_data) { - ProtoIntrprPluginData *pdata = plugin_data; +static bool eval(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzILCacheBlock *il_bb) { // Reset call candidate tracking for each basic block. - memset(&pdata->call_cand, 0, sizeof(pdata->call_cand)); + memset(&ctx->call_cand, 0, sizeof(ctx->call_cand)); // Now execute the actual effects of the BLOCK. RzInterpAbstrState *astate = ctx->astate; @@ -618,7 +613,7 @@ static bool eval(RZ_NONNULL RzInterpRunContext *ctx, RZ_LOG_DEBUG("prototype: Eval PC = 0x%" PFMT64x "\n", pc); RzStrBuf sb; rz_strbuf_init(&sb); - state_as_str(ctx->astate, &sb, plugin_data); + state_as_str(ctx->astate, &sb); RZ_LOG_DEBUG("%s\n", rz_strbuf_get(&sb)); rz_strbuf_fini(&sb); @@ -637,9 +632,9 @@ static bool eval(RZ_NONNULL RzInterpRunContext *ctx, // Prepare next pc, the evalutation may overwrite this. ut64 next_pc = pc + pkt->insn_pkt_size; - set_pc(ctx->astate, next_pc, plugin_data); + set_pc(ctx->astate, next_pc); - if (!interpreter_prototype_eval_effect(ctx, pkt->effect, pkt->insn_pkt_size, plugin_data)) { + if (!interpreter_prototype_eval_effect(ctx, pkt->effect, pkt->insn_pkt_size)) { return false; } if (astate->pc_state != RZ_INTERP_PC_CONST || astate->pc != next_pc) { @@ -656,7 +651,7 @@ static bool eval(RZ_NONNULL RzInterpRunContext *ctx, return true; } -static bool init_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { +static bool init_state(RZ_BORROW RzInterpAbstrState *state) { state->pc = 0; state->pc_state = RZ_INTERP_PC_UNREACHABLE; @@ -674,7 +669,7 @@ static bool init_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { return true; } -static bool reset_state(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point, void *plugin_data) { +static bool reset_state(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point) { state->pc_state = RZ_INTERP_PC_CONST; state->pc = entry_point; @@ -692,7 +687,7 @@ static bool reset_state(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point, v return true; } -static bool fini_state(RZ_BORROW RzInterpAbstrState *state, void *plugin_data) { +static bool fini_state(RZ_BORROW RzInterpAbstrState *state) { RzIterator *it = ht_up_as_iter(state->globals); RzInterpAbstrVal **v; rz_iterator_foreach(it, v) { @@ -752,14 +747,14 @@ static bool join_vars(RZ_BORROW RZ_INOUT HtUP *a, RZ_BORROW RZ_IN HtUP *b) { return changed; } -bool join_state(RZ_BORROW RZ_INOUT RzInterpAbstrState *a, RZ_BORROW RZ_IN const RzInterpAbstrState *b, void *plugin_data) { +bool join_state(RZ_BORROW RZ_INOUT RzInterpAbstrState *a, RZ_BORROW RZ_IN const RzInterpAbstrState *b) { bool global_change = join_vars(a->globals, b->globals); bool local_change = join_vars(a->locals, b->locals); // lets are not be relevant here since they are immutable within their scope return global_change || local_change; } -bool val_as_str(RZ_NONNULL const RzInterpAbstrVal *val, RZ_NONNULL RZ_OUT RzStrBuf *sb, void *plugin_data) { +bool val_as_str(RZ_NONNULL const RzInterpAbstrVal *val, RZ_NONNULL RZ_OUT RzStrBuf *sb) { rz_return_val_if_fail(val && sb, false); ProtoIntrprAbstrData *av = AD(val); if (av->is_const) { @@ -776,8 +771,7 @@ bool val_as_str(RZ_NONNULL const RzInterpAbstrVal *val, RZ_NONNULL RZ_OUT RzStrB } bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, - RZ_NONNULL RZ_OUT RzStrBuf *sb, - void *plugin_data) { + RZ_NONNULL RZ_OUT RzStrBuf *sb) { rz_return_val_if_fail(state && sb, false); rz_strbuf_append(sb, "Globals\n\n"); @@ -795,7 +789,7 @@ bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, const char *gname = ht_up_find(state->var_name_hashes, *k, NULL); rz_strbuf_appendf(sb, "\t%s = ", gname); RzInterpAbstrVal *av = ht_up_find(state->globals, *k, NULL); - val_as_str(av, sb, plugin_data); + val_as_str(av, sb); rz_strbuf_append(sb, "\n"); } rz_iterator_free(it); @@ -819,40 +813,10 @@ void state_as_str_short(RzInterpInstance *iset, RZ_OUT RzStrBuf *out, RzInterpAb first = false; const char *varname = ht_up_find(astate->var_name_hashes, djb2_reg_name, NULL); rz_strbuf_appendf(out, "%s = ", varname); - iset->plugin->val_as_str(av, out, iset->interp_priv); + iset->plugin->val_as_str(av, out); } } -bool init(void **plugin_data) { - ProtoIntrprPluginData *pdata = RZ_NEW0(ProtoIntrprPluginData); - if (!pdata) { - return NULL; - } - RZ_LOG_DEBUG("prototype: init()\n"); - *plugin_data = pdata; - return true; -} - -bool fini(void *plugin_data) { - if (!plugin_data) { - return true; - } - RZ_LOG_DEBUG("prototype: fini()\n"); - ProtoIntrprPluginData *pdata = plugin_data; - free(pdata); - return true; -} - -bool reset(void *plugin_data) { - if (!plugin_data) { - return true; - } - RZ_LOG_DEBUG("prototype: reset()\n"); - ProtoIntrprPluginData *pdata = plugin_data; - memset(&pdata->call_cand, 0, sizeof(RzAnalysisCallCandidate)); - return true; -} - static RzInterpPlugin rz_interpreter_plugin_prototype = { .name = "abstr_int_prototype", .author = "Rot127", @@ -860,9 +824,6 @@ static RzInterpPlugin rz_interpreter_plugin_prototype = { .desc = "A prototype interpreter for constant/top abstractions.", .license = "LGPL-3.0-only", .supported_yields = { RZ_INTERP_YIELD_KIND_XREF, RZ_INTERP_YIELD_KIND_CALL_CANDIDATE }, - .init = init, - .reset = reset, - .fini = fini, .clone_val = clone_val, .eval = eval, .init_state = init_state, From 0959a306a6592086e508398b5eab686c7e122f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Wed, 1 Jul 2026 08:45:57 +0200 Subject: [PATCH 306/334] Move effect evaluation out of plugin and leave pure --- librz/include/rz_inquiry.h | 5 + librz/include/rz_inquiry/rz_interpreter.h | 87 +-- librz/inquiry/inquiry.c | 12 +- librz/inquiry/interp/eval.c | 449 --------------- librz/inquiry/interp/eval.h | 96 +--- librz/inquiry/interp/interpreter.c | 655 ++++++++++++++++++++-- librz/inquiry/interp/val_abs_constant.c | 396 +++++-------- librz/inquiry/meson.build | 1 - 8 files changed, 820 insertions(+), 881 deletions(-) delete mode 100644 librz/inquiry/interp/eval.c diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index c6ffdd209a6..1e128e6ad3c 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -24,6 +24,11 @@ extern "C" { #define RZ_INQUIRY_CHECK_USER_SIGNAL_ITC 1000 typedef struct rz_inquiry_plugin_t { + const char *name; + const char *author; + const char *version; + const char *desc; + const char *license; RzInterpPlugin *p_interpreter; } RzInquiryPlugin; diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index e556159f53f..7c88d87332b 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -43,8 +43,24 @@ typedef enum rz_interp_state_flag { * \brief An abstract value representing a set of RzILVal * * The actual abstraction and structure of this is defined by the plugin in use. + * `struct rz_interp_opaque_abstr_val_t` is defined nowhere. Is is used to ensure + * type-checking of passing this opaque pointer. */ -typedef void RzInterpAbstrVal; +typedef struct rz_interp_opaque_abstr_val_t RzInterpAbstrVal; + +/** + * \brief Helper to explicitly cast a plugin-defined abstract value to an opaque RzInterpAbstrVal + */ +static inline RzInterpAbstrVal *rz_interp_abstr_val_pack(void *val) { + return val; +} + +/** + * \brief Helper to explicitly cast an opaque RzInterpAbstrVal to a plugin-defined abstract value + */ +static inline void *rz_interp_abstr_val_unpack(const RzInterpAbstrVal *val) { + return (void *)val; +} typedef struct { RzInquiryBCFGEdgeType cf_type; ///< Control flow type. @@ -147,45 +163,39 @@ typedef struct rz_interp_instance_t RzInterpInstance; typedef struct { const char *name; - const char *author; - const char *version; - const char *desc; - const char *license; - /** - * \brief The yield type this interpreter generates. - */ - RzInterpYieldKind supported_yields[RZ_INTERP_YIELD_KIND_NUM]; - bool (*init)(void **plugin_data); - bool (*reset)(void *plugin_data); - bool (*fini)(void *plugin_data); - /** - * \brief Clones the given abstract value. - */ - RZ_OWN RzInterpAbstrVal *(*clone_val)(const RzInterpAbstrVal *val); + RZ_OWN RzInterpAbstrVal *(*val_new_top)(void); ///< allocate a new abstract value and initialize it as top + void (*val_free)(RzInterpAbstrVal *val); + bool (*is_top)(RZ_NONNULL const RzInterpAbstrVal *val); ///< return whether the given value is top + bool (*may_be_bool)(RZ_NONNULL const RzInterpAbstrVal *val, bool value); ///< return whether the given value's concrete set contains \p value + void (*set_top)(RZ_OUT RZ_NONNULL RzInterpAbstrVal *dst); ///< Set \p val to be top + void (*set_const)(RZ_OUT RZ_NONNULL RzInterpAbstrVal *dst, RZ_IN RZ_NONNULL RzBitVector *src); ///< set \p dst to the least value that includes \p src /** - * \brief Initializes the abstract state. - */ - bool (*init_state)(RZ_BORROW RzInterpAbstrState *state); - /** - * \brief Reset the abstract state. - */ - bool (*reset_state)(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point); - /** - * \brief Closes the abstract state and frees all its abstract data and sets the pointers to NULL. + * \brief Concretize an abstract value to a single bit vector, if possible + * + * If \p val represents exactly one concrete value, this returns true. + * Additionally, the concrete value is written to \p out if passed. */ - bool (*fini_state)(RZ_BORROW RzInterpAbstrState *state); + bool (*to_concrete_const)(RZ_NONNULL const RzInterpAbstrVal *val, RZ_NULLABLE RZ_OUT RzBitVector *out); + + RZ_OWN void (*copy)(RzInterpAbstrVal *dst, const RzInterpAbstrVal *src); + /** - * \brief Performs the join operation on states (least upper bound, lattice theory) + * \brief Performs the join operation on two abstract values (least upper bound, lattice theory) + * + * This is the least upper bound (lattice theory). In detail, it means \p a should be set to the least + * abstract value whose corresponding concrete value set is a superset of both the concrete value sets + * of \p a and \p b. + * * \return True if a was changed */ - bool (*join_state)(RZ_BORROW RZ_INOUT RzInterpAbstrState *a, RZ_BORROW RZ_IN const RzInterpAbstrState *b); + bool (*join)(RZ_BORROW RZ_INOUT RzInterpAbstrVal *a, RZ_BORROW RZ_IN const RzInterpAbstrVal *b); + /** - * \brief Evaluates an effect with the mutable state. + * \brief Evaluate \p pure and return the result in \p out */ - bool (*eval)(RZ_NONNULL RzInterpRunContext *ctx, - RZ_NONNULL const RzILCacheBlock *il_bb); + bool (*eval_pure)(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT RzInterpAbstrVal *out); /** * \brief Builds a string for printing an abstract value. @@ -195,13 +205,6 @@ typedef struct { bool (*val_as_str)(RZ_NONNULL const RzInterpAbstrVal *state, RZ_NONNULL RZ_OUT RzStrBuf *str_buf); - /** - * \brief Builds a string for printing the current state. - * - * \return Returns false in case of error, True otherwise. - */ - bool (*state_as_str)(RZ_NONNULL const RzInterpAbstrState *state, - RZ_NONNULL RZ_OUT RzStrBuf *str_buf); } RzInterpPlugin; typedef struct { @@ -269,10 +272,12 @@ RZ_IPI void rz_interp_run_state_set(RZ_BORROW RZ_NONNULL RzInterpRunState *state RZ_API void rz_interp_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yield_rbuf); RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_new( - const char *arch_name, - RZ_BORROW RZ_NONNULL RzAnalysisILContext *il_context); -RZ_API void rz_interp_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpAbstrState *state); + RZ_NONNULL RzInterpInstance *inst, + const char *arch_name); +RZ_API void rz_interp_abstr_state_free(RzInterpInstance *inst, RZ_OWN RZ_NULLABLE RzInterpAbstrState *state); RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_clone(RZ_NONNULL RzInterpInstance *iset, const RzInterpAbstrState *state); +RZ_API bool rz_interp_abstr_state_as_str(RZ_NONNULL RzInterpInstance *inst, RZ_NONNULL const RzInterpAbstrState *state, RZ_NONNULL RZ_OUT RzStrBuf *sb); +RZ_API void rz_interp_abstr_state_as_str_short(RZ_NONNULL RzInterpInstance *inst, RZ_NONNULL const RzInterpAbstrState *astate, RZ_NONNULL RZ_OUT RzStrBuf *sb); RZ_API RZ_OWN RzInterpYieldRBuf *rz_interp_yield_rbuf_new(RzInterpYieldKind kind, RzInterpYieldFilter filter, diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index ccfb9858a45..183b986507a 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -54,13 +54,13 @@ RZ_API bool rz_inquiry_plugin_add(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OW return false; } - if (!ht_sp_insert(inquiry->plugins, plugin->p_interpreter->name, plugin)) { - RZ_LOG_WARN("Plugin '%s' was already added.\n", plugin->p_interpreter->name); + if (!ht_sp_insert(inquiry->plugins, plugin->name, plugin)) { + RZ_LOG_WARN("Plugin '%s' was already added.\n", plugin->name); return true; } void **p_data = RZ_NEW0(void *); - if (!ht_sp_insert(inquiry->plugins_data, plugin->p_interpreter->name, p_data)) { + if (!ht_sp_insert(inquiry->plugins_data, plugin->name, p_data)) { rz_warn_if_reached(); return false; } @@ -70,13 +70,15 @@ RZ_API bool rz_inquiry_plugin_add(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OW RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OWN RZ_NONNULL RzInquiryPlugin *plugin) { rz_return_val_if_fail(inquiry && plugin, false); - void **p_data = ht_sp_find(inquiry->plugins_data, plugin->p_interpreter->name, NULL); + void **p_data = ht_sp_find(inquiry->plugins_data, plugin->name, NULL); +#if 0 if (plugin->p_interpreter->fini) { plugin->p_interpreter->fini(p_data ? *p_data : NULL); } +#endif free(p_data); if (plugin->p_interpreter) { - return ht_sp_delete(inquiry->plugins, plugin->p_interpreter->name); + return ht_sp_delete(inquiry->plugins, plugin->name); } rz_warn_if_reached(); return false; diff --git a/librz/inquiry/interp/eval.c b/librz/inquiry/interp/eval.c deleted file mode 100644 index 4b3de3d77a6..00000000000 --- a/librz/inquiry/interp/eval.c +++ /dev/null @@ -1,449 +0,0 @@ -// SPDX-FileCopyrightText: 2025 RizinOrg -// SPDX-License-Identifier: LGPL-3.0-only - -#include "eval.h" -#include "rz_analysis.h" -#include "rz_inquiry/rz_interpreter.h" -#include "rz_th.h" -#include "rz_types.h" -#include "rz_util/rz_assert.h" -#include "rz_util/rz_log.h" -#include - -bool report_yield_xref( - RzInterpRunContext *ctx, - size_t insn_pkt_size, - ut64 from, - const ProtoIntrprAbstrData *to, - RzAnalysisXRefType type) { - if (!to->is_const || rz_bv_len(to->bv) > 64) { - // Isn't reported - return true; - } - if (type == RZ_ANALYSIS_XREF_TYPE_CODE && - RZ_STR_EQ(ctx->astate->arch_name, "hexagon") && - from + insn_pkt_size == rz_bv_to_ut64(to->bv)) { - // Ugly work around. - // Because we don't have RzArch yet the Hexagon plugin adds a JUMP at the - // end of each and every instruction packet. - // This is necessary because the RzIL VM would otherwise just add 4 to the PC, - // which is too little for a packet with 2+ instructions. - // We don't want to report the code references to the next instruction - // packet. So skip them here. - return true; - } - - RzInterpYieldRBuf *yrbuf = ctx->inst->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; - rz_return_val_if_fail(yrbuf, false); - - ut64 to_addr = rz_bv_to_ut64(to->bv); - RzAnalysisXRef xref = { 0 }; - xref.bb_addr = ctx->astate->bb_addr; - xref.from = from; - xref.to = to_addr; - xref.type = type; - if (yrbuf->filter(&xref, yrbuf->filter_data->io_boundaries)) { - RZ_LOG_DEBUG("prototype: REPORT xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref.from, xref.to, rz_analysis_ref_type_tostring(xref.type)); - if (rz_th_ring_buf_put(yrbuf->rbuf, &xref) != RZ_THREAD_RING_BUF_OK) { - return false; - } - } - return true; -} - -/** - * \brief Report the store of the next PC and report it as possible return point. - */ -bool report_yield_call_candiate( - RzInterpRunContext *ctx) { - RzInterpYieldRBuf *cc_rbuf = ctx->inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; - rz_return_val_if_fail(cc_rbuf, false); - - RzAnalysisCallCandidate cc = { 0 }; - memcpy(&cc, &ctx->call_cand, sizeof(ctx->call_cand)); - if (rz_th_ring_buf_put(cc_rbuf->rbuf, &cc) != RZ_THREAD_RING_BUF_OK) { - return false; - } - return true; -} - -void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src) { - rz_return_if_fail(dst && src && dst->bv && src->bv); - rz_bv_cast_inplace(dst->bv, rz_bv_len(src->bv), false); - rz_bv_copy(dst->bv, src->bv); - dst->is_const = src->is_const; -} - -void write_var_to_state(RzInterpAbstrState *astate, - RzILVarKind kind, - ut64 var_id, - const ProtoIntrprAbstrData *data) { - HtUP *ht_vals; - switch (kind) { - default: - rz_warn_if_reached(); - return; - case RZ_IL_VAR_KIND_GLOBAL: - ht_vals = astate->globals; - break; - case RZ_IL_VAR_KIND_LOCAL: - ht_vals = astate->locals; - break; - case RZ_IL_VAR_KIND_LOCAL_PURE: - ht_vals = astate->lets; - break; - } - ProtoIntrprAbstrData *av = ht_up_find(ht_vals, var_id, NULL); - if (!av) { - if (kind == RZ_IL_VAR_KIND_GLOBAL) { - RZ_LOG_WARN("New global variable created: 0x%" PFMT64x "\n", var_id) - return; - } - av = adata_new_top(); - if (!av) { - rz_warn_if_reached(); - return; - } - ht_up_insert(ht_vals, var_id, av); - } - copy_abstr_data(av, data); -} - -bool read_var_from_state(RzInterpAbstrState *astate, - RzILVarKind kind, - ut64 var_id, - RZ_OUT ProtoIntrprAbstrData *data) { - HtUP *ht_vals; - switch (kind) { - default: - rz_warn_if_reached(); - return false; - case RZ_IL_VAR_KIND_GLOBAL: - ht_vals = astate->globals; - break; - case RZ_IL_VAR_KIND_LOCAL: - ht_vals = astate->locals; - break; - case RZ_IL_VAR_KIND_LOCAL_PURE: - ht_vals = astate->lets; - break; - } - RzInterpAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); - if (!av) { - // Variable doesn't exist. - // This should never happen and is a bug. - rz_warn_if_reached(); - return false; - } - copy_abstr_data(data, av); - return true; -} - -// Returns true if the bit vector in \p data is not zero. If it is zero or -// the abstract data is not concrete it returns false. -// -// TODO: The assumption that true != 0 is invalid. -// It depends on the architecture and must be decided by the RzArch plugin. -// State is passed due to this here as well. To make later refactoring easier. -bool abstr_is_true(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data) { - if (!data->is_const) { - return false; - } - return !rz_bv_is_zero_vector(data->bv); -} - -bool abstr_may_be_true(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data) { - if (!data->is_const) { - return true; - } - return !rz_bv_is_zero_vector(data->bv); -} - -bool abstr_may_be_false(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data) { - if (!data->is_const) { - return true; - } - return rz_bv_is_zero_vector(data->bv); -} - -bool store_abstr_data( - RzInterpInstance *iset, - RzILMemIndex mem_idx, - const ProtoIntrprAbstrData *addr, - const ProtoIntrprAbstrData *src) { - // TODO: handle with memory abstractions - return true; -} - -bool load_abstr_data( - RzInterpInstance *iset, - RzILMemIndex mem_idx, - const ProtoIntrprAbstrData *addr, - size_t n_bits, - RZ_OUT ProtoIntrprAbstrData *out) { - RzInterpIOReadRequest io_req = { 0 }; - rz_bv_cast_inplace(out->bv, n_bits, 0); - io_req.addr = addr->bv; - io_req.ld_data = out->bv; - io_req.mem_idx = mem_idx; - io_req.n_bits = n_bits; - io_req.big_endian = iset->il_ctx->config->big_endian; - if (rz_th_ring_buf_put(iset->io_request_rbuf, &io_req) != RZ_THREAD_RING_BUF_OK) { - return false; - } - RzInterpIOResult io_res = { 0 }; - if (rz_th_ring_buf_take_blocking(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { - return false; - } - if (!io_res.req_ok) { - RZ_LOG_WARN("prototype: Failed to read correct number of bytes. Requested: 0x%" PFMTSZx - " Received: 0x%" PFMT32x " bits.\n", - n_bits, rz_bv_len(out->bv)); - return false; - } - out->is_const = true; - - char *bytes = rz_bv_as_hex_string(out->bv, true); - RZ_LOG_DEBUG("prototype: READ @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); - free(bytes); - return true; -} - -bool set_abstr_pc(RzInterpAbstrState *state, ProtoIntrprAbstrData *pc) { - rz_return_val_if_fail(state && pc, false); - if (pc->is_const) { - state->pc_state = RZ_INTERP_PC_CONST; - state->pc = rz_bv_to_ut64(pc->bv); - } else { - state->pc_state = RZ_INTERP_PC_ANY; - } - RZ_LOG_DEBUG("prototype: set_abstr_pc() - Set PC: 0x%" PFMT64x " (%s)\n", - state->pc, state->pc_state == RZ_INTERP_PC_CONST ? "Constant" : "Top"); - return true; -} - -bool set_pc(RzInterpAbstrState *state, ut64 pc) { - rz_return_val_if_fail(state, false); - state->pc = pc; - state->pc_state = RZ_INTERP_PC_CONST; - RZ_LOG_DEBUG("prototype: set_pc() - Set PC: 0x%" PFMT64x " (Constant)\n", pc); - return true; -} - -static bool value_indicates_ret_addr_write(RzInterpRunContext *ctx, ProtoIntrprAbstrData *val) { - return val->is_const && - (rz_bv_to_ut64(val->bv) == ctx->astate->bb_addr + ctx->astate->bb_size || - // Sparc stores the call instruction PC into o8. - // The return instruction jumps then to o7+8. - (rz_str_startswith(ctx->astate->arch_name, "sparc") && rz_bv_to_ut64(val->bv) == ctx->astate->pc)); -} - -RZ_IPI bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, - const RzILOpEffect *effect, - size_t insn_pkt_size) { - STACK_ABSTR_DATA_OUT(eval_out); - rz_return_val_if_fail(ctx->astate->pc_state == RZ_INTERP_PC_CONST, false); - ut64 pc = ctx->astate->pc; - - switch (effect->code) { - default: - case RZ_IL_OP_EMPTY: - break; - case RZ_IL_OP_NOP: { -#if 0 - STACK_ABSTR_DATA_OUT(npc); - // First cast the bitvector, then set it. - // This is performance critical. Since the stack allocated bv is >64 bit - // the rz_bv_set_from_ut64() will set its whole memory, eating a lot of runtime. - // If we cast before, it is simply an assignment to bv->small_bits. - rz_bv_cast_inplace(npc.bv, rz_bv_len(pc->bv), false); - rz_bv_set_from_ut64(npc.bv, insn_pkt_size); - if (!rz_bv_add_inplace(npc.bv, pc->bv, NULL)) { - goto error; - } - set_abstr_pc(iset->astate, &npc); -#endif - break; - } - case RZ_IL_OP_SEQ: { - if (!interpreter_prototype_eval_effect(ctx, effect->op.seq.x, insn_pkt_size)) { - goto error; - } - if (!interpreter_prototype_eval_effect(ctx, effect->op.seq.y, insn_pkt_size)) { - goto error; - } - break; - } - case RZ_IL_OP_SET: { - ut64 vhash = effect->op.set.hash; - if (!interpreter_prototype_eval_pure(ctx, effect->op.set.x, &eval_out)) { - goto error; - } - RzILVarKind kind = effect->op.set.is_local ? RZ_IL_VAR_KIND_LOCAL : RZ_IL_VAR_KIND_GLOBAL; - write_var_to_state(ctx->astate, kind, vhash, &eval_out); - if (value_indicates_ret_addr_write(ctx, &eval_out) && - kind == RZ_IL_VAR_KIND_GLOBAL) { - ctx->call_cand.store_addr = pc; - ctx->call_cand.npc = ctx->astate->bb_addr + ctx->astate->bb_size; - ctx->call_cand.bb_addr = ctx->astate->bb_addr; - ctx->call_cand.in_mem = false; - } - break; - } - case RZ_IL_OP_JMP: { - if (!interpreter_prototype_eval_pure(ctx, effect->op.jmp.dst, &eval_out)) { - goto error; - } - if (!eval_out.is_const) { - RZ_LOG_DEBUG("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", pc); - } - ut64 target = rz_bv_to_ut64(eval_out.bv); - bool is_call = !!ctx->call_cand.store_addr; - RZ_LOG_DEBUG("prototype: JMP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", - pc, target, - eval_out.is_const ? "Concrete" : "Abstract"); - - if (eval_out.is_const) { - RzAnalysisXRefType xref_type = RZ_ANALYSIS_XREF_TYPE_CODE; - - if (is_call) { - // An instruction in this basic block stored the next PC. - // Report a call candidate and assume this jump is a call. - ctx->call_cand.candidate_addr = pc; - ctx->call_cand.target = target; - report_yield_call_candiate(ctx); - -#if 0 - // For a call, we need to push a new frame. - RzBitVector ret_addr = { 0 }; - rz_bv_init(&ret_addr, rz_bv_len(eval_out.bv)); - rz_bv_set_from_ut64(&ret_addr->call_cand.npc); - - bool found = false; - ut64 ic = ht_uu_find(plugin_data->bb_invocation_count->call_cand.target, &found); - stack_frame_push(plugin_data, eval_out.bv, &ret_addr, !found ? 0 : ic); - rz_bv_fini(&ret_addr); -#endif - - xref_type = RZ_ANALYSIS_XREF_TYPE_CALL; - } -#if 0 - if (xref_type == RZ_ANALYSIS_XREF_TYPE_CODE && stack_frame_top_ret_addr_cmp(plugin_data, eval_out.bv)) { - stack_frame_pop(plugin_data, NULL); - xref_type = RZ_ANALYSIS_XREF_TYPE_RETURN; - } -#endif - - report_yield_xref(ctx, insn_pkt_size, pc, &eval_out, - xref_type); - - // Clear the call candidate tracking variable. - memset(&ctx->call_cand, 0, sizeof(ctx->call_cand)); - } - - if (is_call) { - // For calls, assume control flow will continue like fallthrough. - // TODO: set data to top that may be changed by the call - } else { - set_abstr_pc(ctx->astate, &eval_out); - } - break; - } - case RZ_IL_OP_BRANCH: { - if (!interpreter_prototype_eval_pure(ctx, effect->op.branch.condition, &eval_out)) { - goto error; - } - bool may_be_true = abstr_may_be_true(ctx->inst, &eval_out); - bool may_be_false = abstr_may_be_true(ctx->inst, &eval_out); - if (may_be_true && may_be_false) { - RzInterpAbstrState *true_state = rz_interp_abstr_state_clone(ctx->inst, ctx->astate); - RzInterpAbstrState *false_state = ctx->astate; - ctx->astate = true_state; - if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.true_eff, insn_pkt_size)) { - goto error; - } - ctx->astate = false_state; - if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.false_eff, insn_pkt_size)) { - goto error; - } - if (true_state->pc_state == false_state->pc_state && true_state->pc == false_state->pc) { - // identical target location, simply join the data and continue - ctx->inst->plugin->join_state(true_state, false_state); - } else { - // different jump targets, branch rather than resorting to top pc - rz_interp_run_push(ctx, true_state); - // true_state is already in ctx->inst->astate and will be continued automatically - } - rz_interp_abstr_state_free(true_state); - } else if (may_be_true) { - if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.true_eff, insn_pkt_size)) { - goto error; - } - } else if (may_be_false) { - if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.false_eff, insn_pkt_size)) { - goto error; - } - } - break; - } - case RZ_IL_OP_STORE: - case RZ_IL_OP_STOREW: { - STACK_ABSTR_DATA_OUT(st_addr); - RzILOpPure *key = effect->code == RZ_IL_OP_STORE ? effect->op.store.key : effect->op.storew.key; - RzILMemIndex mem_idx = effect->code == RZ_IL_OP_STORE ? 0 : effect->op.storew.mem; - if (!interpreter_prototype_eval_pure(ctx, key, &st_addr)) { - RZ_LOG_ERROR("prototype: STORE/STOREW key failed to evaluate.\n"); - rz_bv_fini(st_addr.bv); - goto error; - } - if (!st_addr.is_const) { - rz_bv_fini(st_addr.bv); - break; - } - if (rz_bv_len(st_addr.bv) == 64) { - // TODO: Remove normalization. - // Unset bit 63 is required, because the RzBuffer API only supports - // st64 addresses. - RzBitVector mask = { 0 }; - rz_bv_init(&mask, 64); - rz_bv_set_from_ut64(&mask, 0x7fffffffffffffff); - rz_bv_and_inplace(st_addr.bv, &mask); - } - - RzILOpPure *pval = effect->code == RZ_IL_OP_STORE ? effect->op.store.value : effect->op.storew.value; - if (!interpreter_prototype_eval_pure(ctx, pval, &eval_out)) { - RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); - rz_bv_fini(st_addr.bv); - goto error; - } - if (!eval_out.is_const) { - rz_bv_fini(st_addr.bv); - break; - } - if (value_indicates_ret_addr_write(ctx, &eval_out)) { - ctx->call_cand.store_addr = pc; - ctx->call_cand.npc = ctx->astate->bb_addr + ctx->astate->bb_size; - ctx->call_cand.bb_addr = ctx->astate->bb_addr; - ctx->call_cand.in_mem = true; - } - report_yield_xref(ctx, insn_pkt_size, pc, &st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); - if (!store_abstr_data(ctx->inst, mem_idx, &st_addr, &eval_out)) { - rz_bv_fini(st_addr.bv); - goto error; - } - rz_bv_fini(st_addr.bv); - break; - } - case RZ_IL_OP_GOTO: - case RZ_IL_OP_BLK: - case RZ_IL_OP_REPEAT: - RZ_LOG_ERROR("Unhandled effect %" PFMT32d "\n", effect->code); - // Ignore for now. - break; - } - rz_bv_fini(eval_out.bv); - return true; -error: - rz_bv_fini(eval_out.bv); - return false; -} diff --git a/librz/inquiry/interp/eval.h b/librz/inquiry/interp/eval.h index 799c4381e4c..d1c1e0103ad 100644 --- a/librz/inquiry/interp/eval.h +++ b/librz/inquiry/interp/eval.h @@ -10,96 +10,28 @@ #include #include -typedef struct { - /** - * \brief Set if the abstract value represents a single constant bitvector. - * If set, the bit vector below is a valid concrete value. - * If unset it is a top value, i.e. represents the set of all bitvectors. - */ - bool is_const; - /** - * \brief The single constant value. - * If is_const is unset this might hold garbage. - */ - RzBitVector *bv; -} ProtoIntrprAbstrData; - -/** - * \brief In bytes - * - * TODO: find a sweet spot here where this size is as small is possible, - * but in practice only very few heap allocations have to happen. - */ -#define BV_STACK_MAX_SIZE 0x100 - -/** - * \brief Initializes an AbstractData object on the stack. - * The bitvector pre-allocates BV_STACK_MAX_SIZE bytes on the stack for large bit vectors. - * Any value larger than these bits will be stored in heap allocated memory. - * Because of this the bit vector should always be passed to rz_bv_fini() after usage. - */ -#define STACK_ABSTR_DATA_OUT(name) \ - ut8 _##name##_bv_large_buf[BV_STACK_MAX_SIZE] = { 0 }; \ - RzBitVector _##name##_bv_large = { .len = BV_STACK_MAX_SIZE, ._elem_len = BV_STACK_MAX_SIZE, .bits.large_a = _##name##_bv_large_buf, .stack_alloc = true }; \ - ProtoIntrprAbstrData name = { .is_const = false, .bv = &_##name##_bv_large }; - -/** - * \brief Creates abstract data on the heap with the given bit vector. - */ -static inline RZ_OWN ProtoIntrprAbstrData *adata_from_bv(const RzBitVector *bv) { - ProtoIntrprAbstrData *ad = RZ_NEW0(ProtoIntrprAbstrData); - ad->is_const = true; - ad->bv = rz_bv_dup(bv); - return ad; -} - -static inline RZ_OWN ProtoIntrprAbstrData *adata_new_top() { - ProtoIntrprAbstrData *ad = RZ_NEW0(ProtoIntrprAbstrData); - ad->is_const = false; - ad->bv = rz_bv_new(BV_STACK_MAX_SIZE); - return ad; -} - -void copy_abstr_data(ProtoIntrprAbstrData *dst, const ProtoIntrprAbstrData *src); -void write_var_to_state(RzInterpAbstrState *astate, RzILVarKind kind, ut64 var_id, const ProtoIntrprAbstrData *data); -bool read_var_from_state(RzInterpAbstrState *astate, RzILVarKind kind, ut64 var_id, RZ_OUT ProtoIntrprAbstrData *data); -bool abstr_is_true(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data); -bool abstr_may_be_true(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data); -bool abstr_may_be_false(const RzInterpInstance *iset, const ProtoIntrprAbstrData *data); -bool store_abstr_data( - RzInterpInstance *iset, - RzILMemIndex mem_idx, - const ProtoIntrprAbstrData *addr, - const ProtoIntrprAbstrData *src); +void write_var_to_state(RzInterpInstance *inst, + RzInterpAbstrState *astate, + RzILVarKind kind, + ut64 var_id, + const RzInterpAbstrVal *data); +bool read_var_from_state(RzInterpInstance *inst, + RzInterpAbstrState *astate, + RzILVarKind kind, + ut64 var_id, + RZ_OUT RzInterpAbstrVal *data); bool load_abstr_data( - RzInterpInstance *iset, + RzInterpInstance *inst, RzILMemIndex mem_idx, - const ProtoIntrprAbstrData *addr, + const RzBitVector *addr, size_t n_bits, - RZ_OUT ProtoIntrprAbstrData *out); - -RZ_IPI bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, - const RzILOpEffect *effect, - size_t nop_pc_inc); -RZ_IPI bool interpreter_prototype_eval_pure( - RzInterpRunContext *ctx, - const RzILOpPure *pure, - RZ_OUT ProtoIntrprAbstrData *out); + RZ_OUT RzInterpAbstrVal *out); bool report_yield_xref( RzInterpRunContext *ctx, size_t insn_pkt_size, ut64 from, - const ProtoIntrprAbstrData *to, + const RzInterpAbstrVal *to, RzAnalysisXRefType type); -bool report_yield_call_candiate( - RzInterpRunContext *ctx); - -bool set_pc(RzInterpAbstrState *state, ut64 pc); - -bool set_abstr_pc(RzInterpAbstrState *state, ProtoIntrprAbstrData *pc); - -void state_as_str_short(RzInterpInstance *iset, RZ_OUT RzStrBuf *out, RzInterpAbstrState *astate); - #endif // PROTOYPE_EVAL_H diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index a86c893422a..6fc900e258d 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -76,26 +76,28 @@ RZ_API RZ_OWN RzInterpYieldRBuf *rz_interp_yield_rbuf_new(RzInterpYieldKind kind * The register name list should always be given if the architecture has some. */ RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_new( - const char *arch_name, - RZ_BORROW RZ_NONNULL RzAnalysisILContext *il_context) { - rz_return_val_if_fail(il_context, NULL); + RZ_NONNULL RzInterpInstance *inst, + const char *arch_name) { + rz_return_val_if_fail(inst, NULL); RzInterpAbstrState *state = RZ_NEW0(RzInterpAbstrState); if (!state) { return NULL; } + state->pc_state = RZ_INTERP_PC_UNREACHABLE; state->arch_name = arch_name; // Initialize the register file with uninitialized abstract values. state->var_name_hashes = ht_up_new(NULL, free); - state->globals = ht_up_new(NULL, free); - for (size_t i = 0; i < il_context->reg_binding->regs_count; i++) { - const char *rname = il_context->reg_binding->regs[i].name; - RzInterpAbstrVal *aval = NULL; // RZ_NEW0(RzInterpAbstrVal); - // if (!aval) { - // ht_up_free(state->globals); - // ht_up_free(state->var_name_hashes); - // free(state); - // return NULL; - // } + state->globals = ht_up_new(NULL, NULL); + for (size_t i = 0; i < inst->il_ctx->reg_binding->regs_count; i++) { + const char *rname = inst->il_ctx->reg_binding->regs[i].name; + RzInterpAbstrVal *aval = inst->plugin->val_new_top(); + if (!aval) { + rz_warn_if_reached(); + ht_up_free(state->globals); + ht_up_free(state->var_name_hashes); + free(state); + return NULL; + } ut64 djb2_reg_hash = rz_str_djb2_hash(rname); if (!ht_up_insert(state->globals, djb2_reg_hash, aval) || @@ -108,42 +110,117 @@ RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_new( free(state); } } - state->locals = ht_up_new(NULL, free); - state->lets = ht_up_new(NULL, free); + state->locals = ht_up_new(NULL, NULL); + state->lets = ht_up_new(NULL, NULL); return state; } -RZ_API void rz_interp_abstr_state_free(RZ_OWN RZ_NULLABLE RzInterpAbstrState *state) { +static void var_set_free(RzInterpInstance *inst, HtUP *vars) { + if (!vars) { + return; + } + RzIterator *it = ht_up_as_iter(vars); + RzInterpAbstrVal **v; + rz_iterator_foreach(it, v) { + inst->plugin->val_free(*v); + } + rz_iterator_free(it); + ht_up_free(vars); +} + +RZ_API void rz_interp_abstr_state_free(RzInterpInstance *inst, RZ_OWN RZ_NULLABLE RzInterpAbstrState *state) { if (!state) { return; } if (state->var_name_hashes) { ht_up_free(state->var_name_hashes); } - if (state->globals) { - ht_up_free(state->globals); + var_set_free(inst, state->globals); + var_set_free(inst, state->locals); + var_set_free(inst, state->lets); + free(state); +} + +static bool reset_state(RzInterpInstance *inst, RZ_BORROW RzInterpAbstrState *state, ut64 entry_point) { + state->pc_state = RZ_INTERP_PC_CONST; + state->pc = entry_point; + + RzIterator *it = ht_up_as_iter_keys(state->globals); + ut64 *k; + rz_iterator_foreach(it, k) { + ut64 djb2_reg_name = *k; + RzInterpAbstrVal *av = ht_up_find(state->globals, djb2_reg_name, NULL); + if (av) { + inst->plugin->set_top(av); + } } - if (state->locals) { - ht_up_free(state->locals); + rz_iterator_free(it); + state->bb_addr = 0; + state->bb_size = 0; + return true; +} + +#define STR_TOP "⊤" +#define STR_BOTTOM "⊥" + +RZ_API bool rz_interp_abstr_state_as_str(RZ_NONNULL RzInterpInstance *inst, RZ_NONNULL const RzInterpAbstrState *state, RZ_NONNULL RZ_OUT RzStrBuf *sb) { + rz_return_val_if_fail(state && sb, false); + + rz_strbuf_append(sb, "Globals\n\n"); + rz_strbuf_append(sb, "\tpc = "); + if (state->pc_state == RZ_INTERP_PC_CONST) { + rz_strbuf_appendf(sb, "0x%" PFMT64x, state->pc); + } else { + rz_strbuf_append(sb, state->pc_state == RZ_INTERP_PC_ANY ? STR_TOP : STR_BOTTOM); } - if (state->lets) { - ht_up_free(state->lets); + rz_strbuf_append(sb, "\n\n"); + + RzIterator *it = ht_up_as_iter_keys(state->globals); + ut64 *k; + rz_iterator_foreach(it, k) { + const char *gname = ht_up_find(state->var_name_hashes, *k, NULL); + rz_strbuf_appendf(sb, "\t%s = ", gname); + RzInterpAbstrVal *av = ht_up_find(state->globals, *k, NULL); + inst->plugin->val_as_str(av, sb); + rz_strbuf_append(sb, "\n"); + } + rz_iterator_free(it); + return true; +} + +RZ_API void rz_interp_abstr_state_as_str_short(RZ_NONNULL RzInterpInstance *inst, RZ_NONNULL const RzInterpAbstrState *astate, RZ_NONNULL RZ_OUT RzStrBuf *sb) { + bool first = true; + RzIterator *it = ht_up_as_iter_keys(astate->globals); + ut64 *k; + rz_iterator_foreach(it, k) { + ut64 djb2_reg_name = *k; + RzInterpAbstrVal *av = ht_up_find(astate->globals, djb2_reg_name, NULL); + if (!av || inst->plugin->is_top(av)) { + continue; + } + if (!first) { + rz_strbuf_append(sb, ", "); + } + first = false; + const char *varname = ht_up_find(astate->var_name_hashes, djb2_reg_name, NULL); + rz_strbuf_appendf(sb, "%s = ", varname); + inst->plugin->val_as_str(av, sb); } - free(state); } static HtUP *var_set_clone(const RzInterpInstance *iset, HtUP *vars) { - HtUP *r = ht_up_new(NULL, free); + HtUP *r = ht_up_new(NULL, NULL); if (!r) { return NULL; } RzIterator *it = ht_up_as_iter_keys(vars); ut64 *key; rz_iterator_foreach(it, key) { - RzInterpAbstrVal *val = iset->plugin->clone_val(ht_up_find(vars, *key, NULL)); + RzInterpAbstrVal *val = iset->plugin->val_new_top(); if (!val) { - continue; + break; } + iset->plugin->copy(val, ht_up_find(vars, *key, NULL)); ht_up_insert(r, *key, val); } return r; @@ -226,6 +303,34 @@ static bool setup_ipc_objects( return false; } +/** + * \brief Join (least upper bound) on var sets + * \return True if a was changed + */ +static bool join_vars(RzInterpInstance *inst, RZ_BORROW RZ_INOUT HtUP *a, RZ_BORROW RZ_IN HtUP *b) { + RzIterator *it = ht_up_as_iter_keys(a); + ut64 *k; + bool changed = false; + rz_iterator_foreach(it, k) { + RzInterpAbstrVal *av = ht_up_find(a, *k, NULL); + RzInterpAbstrVal *bv = ht_up_find(b, *k, NULL); + if (!av || !bv) { + continue; + } + if (inst->plugin->join(av, bv)) { + changed = true; + } + } + return changed; +} + +bool join_state(RzInterpInstance *inst, RZ_BORROW RZ_INOUT RzInterpAbstrState *a, RZ_BORROW RZ_IN const RzInterpAbstrState *b) { + bool global_change = join_vars(inst, a->globals, b->globals); + bool local_change = join_vars(inst, a->locals, b->locals); + // lets are not be relevant here since they are immutable within their scope + return global_change || local_change; +} + /** * \brief Initializes a new RzInterpSet and returns it. * If it fails, all arguments are freed. @@ -287,12 +392,12 @@ RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_ } RzStrBuf sb; rz_strbuf_init(&sb); - state_as_str_short(ctx->inst, &sb, as); + rz_interp_abstr_state_as_str_short(ctx->inst, as, &sb); RZ_LOG_DEBUG("PUSH 0x%" PFMT64x ": %s\n", as->pc, rz_strbuf_get(&sb)); rz_strbuf_fini(&sb); RzInterpAbstrState *existing = ht_up_find(ctx->pc_states, as->pc, NULL); if (existing) { - if (ctx->inst->plugin->join_state(existing, as) && !existing->uninterpreted) { + if (join_state(ctx->inst, existing, as) && !existing->uninterpreted) { existing->uninterpreted = true; rz_list_push(ctx->queue, existing); } @@ -316,6 +421,476 @@ static RzInterpAbstrState *rz_interp_run_pop(RZ_BORROW RZ_NONNULL RzInterpRunCon return r; } +bool report_yield_xref( + RzInterpRunContext *ctx, + size_t insn_pkt_size, + ut64 from, + const RzInterpAbstrVal *to, + RzAnalysisXRefType type) { + RzBitVector to_bv; + rz_bv_init(&to_bv, 64); + bool success = true; + if (!ctx->inst->plugin->to_concrete_const(to, &to_bv) || rz_bv_len(&to_bv) > 64) { + // Isn't reported + goto cleanup; + } + if (type == RZ_ANALYSIS_XREF_TYPE_CODE && + RZ_STR_EQ(ctx->astate->arch_name, "hexagon") && + from + insn_pkt_size == rz_bv_to_ut64(&to_bv)) { + // Ugly work around. + // Because we don't have RzArch yet the Hexagon plugin adds a JUMP at the + // end of each and every instruction packet. + // This is necessary because the RzIL VM would otherwise just add 4 to the PC, + // which is too little for a packet with 2+ instructions. + // We don't want to report the code references to the next instruction + // packet. So skip them here. + goto cleanup; + } + + RzInterpYieldRBuf *yrbuf = ctx->inst->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; + rz_return_val_if_fail(yrbuf, false); + + ut64 to_addr = rz_bv_to_ut64(&to_bv); + RzAnalysisXRef xref = { 0 }; + xref.bb_addr = ctx->astate->bb_addr; + xref.from = from; + xref.to = to_addr; + xref.type = type; + if (yrbuf->filter(&xref, yrbuf->filter_data->io_boundaries)) { + RZ_LOG_DEBUG("prototype: REPORT xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref.from, xref.to, rz_analysis_ref_type_tostring(xref.type)); + if (rz_th_ring_buf_put(yrbuf->rbuf, &xref) != RZ_THREAD_RING_BUF_OK) { + success = false; + goto cleanup; + } + } +cleanup: + rz_bv_fini(&to_bv); + return success; +} + +/** + * \brief Report the store of the next PC and report it as possible return point. + */ +static bool report_yield_call_candiate( + RzInterpRunContext *ctx) { + RzInterpYieldRBuf *cc_rbuf = ctx->inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; + rz_return_val_if_fail(cc_rbuf, false); + + RzAnalysisCallCandidate cc = { 0 }; + memcpy(&cc, &ctx->call_cand, sizeof(ctx->call_cand)); + if (rz_th_ring_buf_put(cc_rbuf->rbuf, &cc) != RZ_THREAD_RING_BUF_OK) { + return false; + } + return true; +} + +void write_var_to_state(RzInterpInstance *inst, + RzInterpAbstrState *astate, + RzILVarKind kind, + ut64 var_id, + const RzInterpAbstrVal *data) { + HtUP *ht_vals; + switch (kind) { + default: + rz_warn_if_reached(); + return; + case RZ_IL_VAR_KIND_GLOBAL: + ht_vals = astate->globals; + break; + case RZ_IL_VAR_KIND_LOCAL: + ht_vals = astate->locals; + break; + case RZ_IL_VAR_KIND_LOCAL_PURE: + ht_vals = astate->lets; + break; + } + RzInterpAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); + if (!av) { + if (kind == RZ_IL_VAR_KIND_GLOBAL) { + RZ_LOG_WARN("New global variable created: 0x%" PFMT64x "\n", var_id) + return; + } + av = inst->plugin->val_new_top(); + if (!av) { + rz_warn_if_reached(); + return; + } + ht_up_insert(ht_vals, var_id, av); + } + inst->plugin->copy(av, data); +} + +bool read_var_from_state(RzInterpInstance *inst, + RzInterpAbstrState *astate, + RzILVarKind kind, + ut64 var_id, + RZ_OUT RzInterpAbstrVal *data) { + HtUP *ht_vals; + switch (kind) { + default: + rz_warn_if_reached(); + return false; + case RZ_IL_VAR_KIND_GLOBAL: + ht_vals = astate->globals; + break; + case RZ_IL_VAR_KIND_LOCAL: + ht_vals = astate->locals; + break; + case RZ_IL_VAR_KIND_LOCAL_PURE: + ht_vals = astate->lets; + break; + } + RzInterpAbstrVal *av = ht_up_find(ht_vals, var_id, NULL); + if (!av) { + // Variable doesn't exist. + // This should never happen and is a bug. + rz_warn_if_reached(); + return false; + } + inst->plugin->copy(data, av); + return true; +} + +static bool store_abstr_data( + RzInterpInstance *iset, + RzILMemIndex mem_idx, + const RzInterpAbstrVal *addr, + const RzInterpAbstrVal *src) { + // TODO: handle with memory abstractions + return true; +} + +bool load_abstr_data( + RzInterpInstance *inst, + RzILMemIndex mem_idx, + const RzBitVector *addr, + size_t n_bits, + RZ_OUT RzInterpAbstrVal *out) { + RzInterpIOReadRequest io_req = { 0 }; + + RzBitVector out_bv; + rz_bv_init(&out_bv, n_bits); + + io_req.addr = addr; + io_req.ld_data = &out_bv; + io_req.mem_idx = mem_idx; + io_req.n_bits = n_bits; + io_req.big_endian = inst->il_ctx->config->big_endian; + if (rz_th_ring_buf_put(inst->io_request_rbuf, &io_req) != RZ_THREAD_RING_BUF_OK) { + return false; + } + RzInterpIOResult io_res = { 0 }; + if (rz_th_ring_buf_take_blocking(inst->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { + return false; + } + if (!io_res.req_ok) { + RZ_LOG_WARN("prototype: Failed to read correct number of bytes. Requested: 0x%" PFMTSZx + " Received: 0x%" PFMT32x " bits.\n", + n_bits, rz_bv_len(&out_bv)); + inst->plugin->set_top(out); + return false; + } + inst->plugin->set_const(out, &out_bv); + + char *bytes = rz_bv_as_hex_string(&out_bv, true); + RZ_LOG_DEBUG("prototype: READ @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); + free(bytes); + return true; +} + +static bool set_abstr_pc(RzInterpInstance *inst, RzInterpAbstrState *state, RzInterpAbstrVal *pc) { + rz_return_val_if_fail(state && pc, false); + RzBitVector pc_bv; + rz_bv_init(&pc_bv, 64); + if (inst->plugin->to_concrete_const(pc, &pc_bv)) { + state->pc_state = RZ_INTERP_PC_CONST; + state->pc = rz_bv_to_ut64(&pc_bv); + } else { + state->pc_state = RZ_INTERP_PC_ANY; + } + rz_bv_fini(&pc_bv); + RZ_LOG_DEBUG("prototype: set_abstr_pc() - Set PC: 0x%" PFMT64x " (%s)\n", + state->pc, state->pc_state == RZ_INTERP_PC_CONST ? "Constant" : "Top"); + return true; +} + +static bool value_indicates_ret_addr_write(RzInterpRunContext *ctx, RzInterpAbstrVal *val) { + RzBitVector bv; + rz_bv_init(&bv, 64); + bool ret = ctx->inst->plugin->to_concrete_const(val, &bv) && + (rz_bv_to_ut64(&bv) == ctx->astate->bb_addr + ctx->astate->bb_size || + // Sparc stores the call instruction PC into o8. + // The return instruction jumps then to o7+8. + (rz_str_startswith(ctx->astate->arch_name, "sparc") && rz_bv_to_ut64(&bv) == ctx->astate->pc)); + rz_bv_fini(&bv); + return ret; +} + +static bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, + const RzILOpEffect *effect, + size_t insn_pkt_size) { + rz_return_val_if_fail(ctx->astate->pc_state == RZ_INTERP_PC_CONST, false); + ut64 pc = ctx->astate->pc; + RzInterpAbstrVal *eval_out = NULL; + + switch (effect->code) { + default: + case RZ_IL_OP_EMPTY: + break; + case RZ_IL_OP_NOP: { + break; + } + case RZ_IL_OP_SEQ: { + if (!interpreter_prototype_eval_effect(ctx, effect->op.seq.x, insn_pkt_size)) { + goto error; + } + if (!interpreter_prototype_eval_effect(ctx, effect->op.seq.y, insn_pkt_size)) { + goto error; + } + break; + } + case RZ_IL_OP_SET: { + eval_out = ctx->inst->plugin->val_new_top(); + ut64 vhash = effect->op.set.hash; + if (!eval_out || !ctx->inst->plugin->eval_pure(ctx, effect->op.set.x, eval_out)) { + goto error; + } + RzILVarKind kind = effect->op.set.is_local ? RZ_IL_VAR_KIND_LOCAL : RZ_IL_VAR_KIND_GLOBAL; + write_var_to_state(ctx->inst, ctx->astate, kind, vhash, eval_out); + if (value_indicates_ret_addr_write(ctx, eval_out) && + kind == RZ_IL_VAR_KIND_GLOBAL) { + ctx->call_cand.store_addr = pc; + ctx->call_cand.npc = ctx->astate->bb_addr + ctx->astate->bb_size; + ctx->call_cand.bb_addr = ctx->astate->bb_addr; + ctx->call_cand.in_mem = false; + } + break; + } + case RZ_IL_OP_JMP: { + eval_out = ctx->inst->plugin->val_new_top(); + if (!eval_out || !ctx->inst->plugin->eval_pure(ctx, effect->op.jmp.dst, eval_out)) { + goto error; + } + RzBitVector eval_out_bv; + rz_bv_init(&eval_out_bv, 64); + bool is_const = ctx->inst->plugin->to_concrete_const(eval_out, &eval_out_bv); + if (!is_const) { + RZ_LOG_DEBUG("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", pc); + } + bool is_call = !!ctx->call_cand.store_addr; + + if (is_const) { + ut64 target = rz_bv_to_ut64(&eval_out_bv); + RZ_LOG_DEBUG("prototype: JMP - Set PC: 0x%" PFMT64x " -> 0x%" PFMT64x "\n", pc, target); + RzAnalysisXRefType xref_type = RZ_ANALYSIS_XREF_TYPE_CODE; + + if (is_call) { + // An instruction in this basic block stored the next PC. + // Report a call candidate and assume this jump is a call. + ctx->call_cand.candidate_addr = pc; + ctx->call_cand.target = target; + report_yield_call_candiate(ctx); + +#if 0 + // For a call, we need to push a new frame. + RzBitVector ret_addr = { 0 }; + rz_bv_init(&ret_addr, rz_bv_len(eval_out.bv)); + rz_bv_set_from_ut64(&ret_addr->call_cand.npc); + + bool found = false; + ut64 ic = ht_uu_find(plugin_data->bb_invocation_count->call_cand.target, &found); + stack_frame_push(plugin_data, eval_out.bv, &ret_addr, !found ? 0 : ic); + rz_bv_fini(&ret_addr); +#endif + + xref_type = RZ_ANALYSIS_XREF_TYPE_CALL; + } +#if 0 + if (xref_type == RZ_ANALYSIS_XREF_TYPE_CODE && stack_frame_top_ret_addr_cmp(plugin_data, eval_out.bv)) { + stack_frame_pop(plugin_data, NULL); + xref_type = RZ_ANALYSIS_XREF_TYPE_RETURN; + } +#endif + + report_yield_xref(ctx, insn_pkt_size, pc, eval_out, + xref_type); + + // Clear the call candidate tracking variable. + memset(&ctx->call_cand, 0, sizeof(ctx->call_cand)); + } + + if (is_call) { + // For calls, assume control flow will continue like fallthrough. + // TODO: set data to top that may be changed by the call + } else { + set_abstr_pc(ctx->inst, ctx->astate, eval_out); + } + rz_bv_fini(&eval_out_bv); + break; + } + case RZ_IL_OP_BRANCH: { + eval_out = ctx->inst->plugin->val_new_top(); + if (!eval_out || !ctx->inst->plugin->eval_pure(ctx, effect->op.branch.condition, eval_out)) { + goto error; + } + bool may_be_true = ctx->inst->plugin->may_be_bool(eval_out, true); + bool may_be_false = ctx->inst->plugin->may_be_bool(eval_out, false); + if (may_be_true && may_be_false) { + RzInterpAbstrState *true_state = rz_interp_abstr_state_clone(ctx->inst, ctx->astate); + RzInterpAbstrState *false_state = ctx->astate; + ctx->astate = true_state; + if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.true_eff, insn_pkt_size)) { + goto error; + } + ctx->astate = false_state; + if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.false_eff, insn_pkt_size)) { + goto error; + } + if (true_state->pc_state == false_state->pc_state && true_state->pc == false_state->pc) { + // identical target location, simply join the data and continue + join_state(ctx->inst, true_state, false_state); + } else { + // different jump targets, branch rather than resorting to top pc + rz_interp_run_push(ctx, true_state); + // true_state is already in ctx->inst->astate and will be continued automatically + } + rz_interp_abstr_state_free(ctx->inst, true_state); + } else if (may_be_true) { + if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.true_eff, insn_pkt_size)) { + goto error; + } + } else if (may_be_false) { + if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.false_eff, insn_pkt_size)) { + goto error; + } + } + break; + } + case RZ_IL_OP_STORE: + case RZ_IL_OP_STOREW: { + RzInterpAbstrVal *st_addr = ctx->inst->plugin->val_new_top(); + RzILOpPure *key = effect->code == RZ_IL_OP_STORE ? effect->op.store.key : effect->op.storew.key; + RzILMemIndex mem_idx = effect->code == RZ_IL_OP_STORE ? 0 : effect->op.storew.mem; + if (!ctx->inst->plugin->eval_pure(ctx, key, st_addr)) { + RZ_LOG_ERROR("prototype: STORE/STOREW key failed to evaluate.\n"); + ctx->inst->plugin->val_free(st_addr); + goto error; + } + RzBitVector st_addr_bv; + rz_bv_init(&st_addr_bv, 64); + bool st_addr_is_const = st_addr && ctx->inst->plugin->to_concrete_const(st_addr, &st_addr_bv); + ctx->inst->plugin->val_free(st_addr); + if (!st_addr_is_const) { + rz_bv_fini(&st_addr_bv); + break; + } + if (rz_bv_len(&st_addr_bv) == 64) { + // TODO: Remove normalization. + // Unset bit 63 is required, because the RzBuffer API only supports + // st64 addresses. + RzBitVector mask = { 0 }; + rz_bv_init(&mask, 64); + rz_bv_set_from_ut64(&mask, 0x7fffffffffffffff); + rz_bv_and_inplace(&st_addr_bv, &mask); + } + + RzILOpPure *pval = effect->code == RZ_IL_OP_STORE ? effect->op.store.value : effect->op.storew.value; + if (!ctx->inst->plugin->eval_pure(ctx, pval, eval_out)) { + RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); + rz_bv_fini(&st_addr_bv); + goto error; + } + if (!eval_out || !ctx->inst->plugin->eval_pure(ctx, effect->op.branch.condition, eval_out)) { + rz_bv_fini(&st_addr_bv); + break; + } + if (value_indicates_ret_addr_write(ctx, eval_out)) { + ctx->call_cand.store_addr = pc; + ctx->call_cand.npc = ctx->astate->bb_addr + ctx->astate->bb_size; + ctx->call_cand.bb_addr = ctx->astate->bb_addr; + ctx->call_cand.in_mem = true; + } + report_yield_xref(ctx, insn_pkt_size, pc, st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); + if (!store_abstr_data(ctx->inst, mem_idx, st_addr, eval_out)) { + rz_bv_fini(&st_addr_bv); + goto error; + } + rz_bv_fini(&st_addr_bv); + break; + } + case RZ_IL_OP_GOTO: + case RZ_IL_OP_BLK: + case RZ_IL_OP_REPEAT: + RZ_LOG_ERROR("Unhandled effect %" PFMT32d "\n", effect->code); + // Ignore for now. + break; + } + ctx->inst->plugin->val_free(eval_out); + return true; +error: + ctx->inst->plugin->val_free(eval_out); + return false; +} + + +static bool set_pc(RzInterpAbstrState *state, ut64 pc) { + rz_return_val_if_fail(state, false); + state->pc = pc; + state->pc_state = RZ_INTERP_PC_CONST; + RZ_LOG_DEBUG("prototype: set_pc() - Set PC: 0x%" PFMT64x " (Constant)\n", pc); + return true; +} + +static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzILCacheBlock *il_bb) { + // Reset call candidate tracking for each basic block. + memset(&ctx->call_cand, 0, sizeof(ctx->call_cand)); + + // Now execute the actual effects of the BLOCK. + RzInterpAbstrState *astate = ctx->astate; + void **it; + rz_pvector_foreach (il_bb->il_ops, it) { + ut64 pc = astate->pc; + RZ_LOG_DEBUG("prototype: Eval PC = 0x%" PFMT64x "\n", pc); + RzStrBuf sb; + rz_strbuf_init(&sb); + rz_interp_abstr_state_as_str(ctx->inst, ctx->astate, &sb); + RZ_LOG_DEBUG("%s\n", rz_strbuf_get(&sb)); + rz_strbuf_fini(&sb); + + rz_strbuf_init(&sb); + if (pc == il_bb->addr) { + rz_strbuf_append(&sb, "ENTRY "); + } + if (rz_vector_index_ptr(&il_bb->il_ops->v, rz_pvector_len(il_bb->il_ops) - 1) == it) { + rz_strbuf_append(&sb, "EXIT "); + } + rz_interp_abstr_state_as_str_short(ctx->inst, ctx->astate, &sb); + rz_meta_set_string(ctx->inst->a, RZ_META_TYPE_COMMENT, pc, rz_strbuf_get(&sb)); + rz_strbuf_fini(&sb); + + RzILCacheInsnPkt *pkt = *it; + + // Prepare next pc, the evalutation may overwrite this. + ut64 next_pc = pc + pkt->insn_pkt_size; + set_pc(ctx->astate, next_pc); + + if (!interpreter_prototype_eval_effect(ctx, pkt->effect, pkt->insn_pkt_size)) { + return false; + } + if (astate->pc_state != RZ_INTERP_PC_CONST || astate->pc != next_pc) { + // Unreachable or a jump happened somewhere other than fallthrough, so we can't continue + // interpreting the block linearly, but have to push the new location + break; + } + } + + if (astate->pc_state != RZ_INTERP_PC_UNREACHABLE) { + rz_interp_run_push(ctx, ctx->astate); + } + + return true; +} + /** * \brief Run the interpreter from a single entrypoint until a fixpoint is reached */ @@ -335,19 +910,15 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { // Prepare the initial state from the given entry point // Hint: nothing speaks against supporting multiple entry points in a single run const RzAnalysisPlugin *cur = rz_analysis_plugin_current(inst->a); - RzInterpAbstrState *estate = rz_interp_abstr_state_new(cur->arch, inst->il_ctx); - if (inst->plugin->reset) { - // TODO: should rather be local to the RzInterpRunContext if it is reset every run - // inst->plugin->reset(inst->interp_priv); - memset(&ctx.call_cand, 0, sizeof(ctx.call_cand)); - } - if (!inst->plugin->init_state(estate) || !inst->plugin->reset_state(estate, entry_point)) { + RzInterpAbstrState *estate = rz_interp_abstr_state_new(inst, cur->arch); + memset(&ctx.call_cand, 0, sizeof(ctx.call_cand)); + if (!reset_state(inst, estate, entry_point)) { rz_warn_if_reached(); - rz_interp_abstr_state_free(estate); + rz_interp_abstr_state_free(inst, estate); goto cleanup; } rz_interp_run_push(&ctx, estate); - rz_interp_abstr_state_free(estate); + rz_interp_abstr_state_free(inst, estate); // Loop and interpret until a fixpoint has been reached while (true) { @@ -373,14 +944,14 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { rz_strbuf_appendf(&sb, "%s; ", old_cmt); } rz_strbuf_append(&sb, "ENTRY "); - state_as_str_short(inst, &sb, ctx.astate); + rz_interp_abstr_state_as_str_short(inst, ctx.astate, &sb); // rz_meta_set_string(iset->a, RZ_META_TYPE_COMMENT, il_bb->addr, rz_strbuf_get(&sb)); rz_strbuf_fini(&sb); ctx.astate->bb_addr = il_bb->addr; ctx.astate->bb_size = il_bb->size; // Evaluate the effect on the abstract state. - if (!inst->plugin->eval(&ctx, il_bb)) { + if (!eval_block(&ctx, il_bb)) { RZ_LOG_DEBUG("interpreter: Eval failed\n"); success = false; break; @@ -403,10 +974,7 @@ RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *inst) { inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] && inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] && inst->run_state_sync && - inst->plugin && - inst->plugin->eval && - inst->plugin->init_state && - inst->plugin->fini_state, + inst->plugin, false); bool success = true; @@ -417,7 +985,6 @@ RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *inst) { // Start interpretation // - // TODO: It is probably better to make the following stuff while-loops. // Because otherwise it doesn't make sense without the docs. // But while debugging and developing, I keep it this way to separate clearly diff --git a/librz/inquiry/interp/val_abs_constant.c b/librz/inquiry/interp/val_abs_constant.c index 9c84c716746..20c63e450e1 100644 --- a/librz/inquiry/interp/val_abs_constant.c +++ b/librz/inquiry/interp/val_abs_constant.c @@ -12,30 +12,85 @@ #include "rz_util/rz_bitvector.h" -static void adata_free(ProtoIntrprAbstrData *adata) { - if (!adata) { - return; - } - rz_bv_free(adata->bv); - free(adata); -} +typedef struct { + /** + * \brief Set if the abstract value represents a single constant bitvector. + * If set, the bit vector below is a valid concrete value. + * If unset it is a top value, i.e. represents the set of all bitvectors. + */ + bool is_const; + /** + * \brief The single constant value. + * If is_const is unset this might hold garbage. + */ + RzBitVector *bv; +} ProtoIntrprAbstrData; /** * \brief Abstract data getter from the RzInterpAbstrVal */ -#define AD(av) ((ProtoIntrprAbstrData *)av) +#define AD(av) ((ProtoIntrprAbstrData *)rz_interp_abstr_val_unpack(av)) + +static RzInterpAbstrVal *pack(ProtoIntrprAbstrData *val) { + return rz_interp_abstr_val_pack(val); +} + +/** + * \brief In bytes + * + * TODO: find a sweet spot here where this size is as small is possible, + * but in practice only very few heap allocations have to happen. + */ +#define BV_STACK_MAX_SIZE 0x100 /** - * \brief Evaluate a pure. + * \brief Initializes an AbstractData object on the stack. + * The bitvector pre-allocates BV_STACK_MAX_SIZE bytes on the stack for large bit vectors. + * Any value larger than these bits will be stored in heap allocated memory. + * Because of this the bit vector should always be passed to rz_bv_fini() after usage. */ -RZ_IPI bool interpreter_prototype_eval_pure( - RzInterpRunContext *ctx, - const RzILOpPure *pure, - RZ_OUT ProtoIntrprAbstrData *out) { +#define STACK_ABSTR_DATA_OUT(name) \ + ut8 _##name##_bv_large_buf[BV_STACK_MAX_SIZE] = { 0 }; \ + RzBitVector _##name##_bv_large = { .len = BV_STACK_MAX_SIZE, ._elem_len = BV_STACK_MAX_SIZE, .bits.large_a = _##name##_bv_large_buf, .stack_alloc = true }; \ + ProtoIntrprAbstrData name = { .is_const = false, .bv = &_##name##_bv_large }; + +static RZ_OWN RzInterpAbstrVal *val_new_top() { + ProtoIntrprAbstrData *ad = RZ_NEW0(ProtoIntrprAbstrData); + ad->is_const = false; + ad->bv = rz_bv_new(BV_STACK_MAX_SIZE); + return pack(ad); +} + +static void val_free(RzInterpAbstrVal *val) { + if (!val) { + return; + } + ProtoIntrprAbstrData *adata = AD(val); + rz_bv_free(adata->bv); + free(adata); +} + +static bool val_is_top(RZ_NONNULL const RzInterpAbstrVal *val) { + return !AD(val)->is_const; +} + +bool static val_may_be_bool(RZ_NONNULL const RzInterpAbstrVal *val, bool value) { + if (!AD(val)->is_const) { + return true; + } + return value != rz_bv_is_zero_vector(AD(val)->bv); +} + +static void val_set_top(RZ_NONNULL RzInterpAbstrVal *val) { + AD(val)->is_const = false; +} + +static bool eval_pure(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT RzInterpAbstrVal *out_val) { + ProtoIntrprAbstrData *out = AD(out_val); switch (pure->code) { default: case RZ_IL_OP_VAR: { - if (!read_var_from_state(ctx->astate, pure->op.var.kind, pure->op.var.hash, out)) { + if (!read_var_from_state(ctx->inst, ctx->astate, pure->op.var.kind, pure->op.var.hash, out_val)) { RZ_LOG_ERROR("prototype: VAR failed to evaluate. The %s '%s' doesn't exist.\n", rz_il_var_kind_name(pure->op.var.kind), pure->op.var.v); @@ -45,13 +100,13 @@ RZ_IPI bool interpreter_prototype_eval_pure( } case RZ_IL_OP_LET: { ut64 vhash = pure->op.let.hash; - if (!interpreter_prototype_eval_pure(ctx, pure->op.let.exp, out)) { + if (!eval_pure(ctx, pure->op.let.exp, out_val)) { RZ_LOG_ERROR("prototype: LET expression failed to evaluate.\n"); return false; } - write_var_to_state(ctx->astate, RZ_IL_VAR_KIND_LOCAL_PURE, vhash, out); + write_var_to_state(ctx->inst, ctx->astate, RZ_IL_VAR_KIND_LOCAL_PURE, vhash, out_val); // Evaluate body - if (!interpreter_prototype_eval_pure(ctx, pure->op.let.body, out)) { + if (!eval_pure(ctx, pure->op.let.body, out_val)) { RZ_LOG_ERROR("prototype: LET body failed to evaluate.\n"); return false; } @@ -60,7 +115,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } case RZ_IL_OP_ITE: { - if (!interpreter_prototype_eval_pure(ctx, pure->op.ite.condition, out)) { + if (!eval_pure(ctx, pure->op.ite.condition, out_val)) { RZ_LOG_ERROR("prototype: ITE condition failed to evaluate.\n"); return false; } @@ -69,13 +124,14 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } - if (abstr_is_true(ctx->inst, out)) { - if (!interpreter_prototype_eval_pure(ctx, pure->op.ite.x, out)) { + // TODO: eval both if top + if (!val_may_be_bool(out_val, false)) { + if (!eval_pure(ctx, pure->op.ite.x, out_val)) { RZ_LOG_ERROR("prototype: ITE x failed to evaluate.\n"); return false; } } else { - if (!interpreter_prototype_eval_pure(ctx, pure->op.ite.y, out)) { + if (!eval_pure(ctx, pure->op.ite.y, out_val)) { RZ_LOG_ERROR("prototype: ITE y failed to evaluate.\n"); return false; } @@ -97,7 +153,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( out->is_const = true; break; case RZ_IL_OP_CAST: { - if (!interpreter_prototype_eval_pure(ctx, pure->op.cast.val, out)) { + if (!eval_pure(ctx, pure->op.cast.val, out_val)) { RZ_LOG_ERROR("prototype: CAST val failed to evaluate.\n"); return false; } @@ -105,7 +161,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(fill_bit); - if (!interpreter_prototype_eval_pure(ctx, pure->op.cast.fill, &fill_bit)) { + if (!eval_pure(ctx, pure->op.cast.fill, pack(&fill_bit))) { RZ_LOG_ERROR("prototype: CAST fill failed to evaluate.\n"); return false; } @@ -113,7 +169,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(fill_bit.bv); goto map_to_top; } - rz_bv_cast_inplace(out->bv, pure->op.cast.length, abstr_is_true(ctx->inst, &fill_bit)); + rz_bv_cast_inplace(out->bv, pure->op.cast.length, !rz_bv_is_zero_vector(fill_bit.bv)); break; } case RZ_IL_OP_BITV: @@ -123,7 +179,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; case RZ_IL_OP_APPEND: { STACK_ABSTR_DATA_OUT(high); - if (!interpreter_prototype_eval_pure(ctx, pure->op.append.high, &high)) { + if (!eval_pure(ctx, pure->op.append.high, pack(&high))) { RZ_LOG_ERROR("prototype: APPEND high failed to evaluate.\n"); return false; } @@ -131,7 +187,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_fini(high.bv); goto map_to_top; } - if (!interpreter_prototype_eval_pure(ctx, pure->op.append.low, out)) { + if (!eval_pure(ctx, pure->op.append.low, pack(out))) { RZ_LOG_ERROR("prototype: APPEND low failed to evaluate.\n"); rz_bv_fini(high.bv); return false; @@ -149,7 +205,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_LOGNOT: case RZ_IL_OP_INV: { RzILOpPure *x = pure->code == RZ_IL_OP_INV ? pure->op.boolinv.x : pure->op.lognot.bv; - if (!interpreter_prototype_eval_pure(ctx, x, out)) { + if (!eval_pure(ctx, x, out_val)) { RZ_LOG_ERROR("prototype: INV x failed to evaluate.\n"); return false; } @@ -162,7 +218,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_AND: { RzILOpPure *px = pure->code == RZ_IL_OP_AND ? pure->op.booland.x : pure->op.logand.x; RzILOpPure *py = pure->code == RZ_IL_OP_AND ? pure->op.booland.y : pure->op.logand.y; - if (!interpreter_prototype_eval_pure(ctx, px, out)) { + if (!eval_pure(ctx, px, out_val)) { RZ_LOG_ERROR("prototype: AND x failed to evaluate.\n"); return false; } @@ -170,7 +226,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y)) { + if (!eval_pure(ctx, py, pack(&y))) { RZ_LOG_ERROR("prototype: AND y failed to evaluate.\n"); return false; } @@ -189,7 +245,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_OR: { RzILOpPure *px = pure->code == RZ_IL_OP_OR ? pure->op.boolor.x : pure->op.logor.x; RzILOpPure *py = pure->code == RZ_IL_OP_OR ? pure->op.boolor.y : pure->op.logor.y; - if (!interpreter_prototype_eval_pure(ctx, px, out)) { + if (!eval_pure(ctx, px, out_val)) { RZ_LOG_ERROR("prototype: OR x failed to evaluate.\n"); return false; } @@ -197,7 +253,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y)) { + if (!eval_pure(ctx, py, pack(&y))) { RZ_LOG_ERROR("prototype: OR y failed to evaluate.\n"); return false; } @@ -216,7 +272,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_XOR: { RzILOpPure *px = pure->code == RZ_IL_OP_XOR ? pure->op.boolxor.x : pure->op.logxor.x; RzILOpPure *py = pure->code == RZ_IL_OP_XOR ? pure->op.boolxor.y : pure->op.logxor.y; - if (!interpreter_prototype_eval_pure(ctx, px, out)) { + if (!eval_pure(ctx, px, out_val)) { RZ_LOG_ERROR("prototype: XOR x failed to evaluate.\n"); return false; } @@ -224,7 +280,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y)) { + if (!eval_pure(ctx, py, pack(&y))) { RZ_LOG_ERROR("prototype: XOR y failed to evaluate.\n"); return false; } @@ -261,7 +317,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( truth_test = rz_bv_msb; break; } - if (!interpreter_prototype_eval_pure(ctx, bv, out)) { + if (!eval_pure(ctx, bv, out_val)) { RZ_LOG_ERROR("prototype: MSB/LSB/IS_ZERO bv failed to evaluate.\n"); return false; } @@ -275,7 +331,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } case RZ_IL_OP_NEG: { - if (!interpreter_prototype_eval_pure(ctx, pure->op.neg.bv, out)) { + if (!eval_pure(ctx, pure->op.neg.bv, out_val)) { RZ_LOG_ERROR("prototype: NEG bv failed to evaluate.\n"); return false; } @@ -287,7 +343,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_ADD: { RzILOpPure *px = pure->op.add.x; RzILOpPure *py = pure->op.add.y; - if (!interpreter_prototype_eval_pure(ctx, px, out)) { + if (!eval_pure(ctx, px, out_val)) { RZ_LOG_ERROR("prototype: ADD x failed to evaluate.\n"); return false; } @@ -295,7 +351,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y)) { + if (!eval_pure(ctx, py, pack(&y))) { RZ_LOG_ERROR("prototype: ADD y failed to evaluate.\n"); return false; } @@ -313,7 +369,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_SUB: { RzILOpPure *px = pure->op.sub.x; RzILOpPure *py = pure->op.sub.y; - if (!interpreter_prototype_eval_pure(ctx, px, out)) { + if (!eval_pure(ctx, px, out_val)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); return false; } @@ -321,7 +377,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y)) { + if (!eval_pure(ctx, py, pack(&y))) { RZ_LOG_ERROR("prototype: SUB y failed to evaluate.\n"); return false; } @@ -341,7 +397,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( RzILOpPure *px = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.x : pure->op.shiftl.x; RzILOpPure *py = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.y : pure->op.shiftl.y; RzILOpPure *pfill_bit = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.fill_bit : pure->op.shiftl.fill_bit; - if (!interpreter_prototype_eval_pure(ctx, px, out)) { + if (!eval_pure(ctx, px, out_val)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) x failed to evaluate.\n"); return false; } @@ -349,7 +405,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y)) { + if (!eval_pure(ctx, py, pack(&y))) { RZ_LOG_ERROR("prototype: SHIFT(L/R) y failed to evaluate.\n"); return false; } @@ -358,7 +414,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(fill_bit); - if (!interpreter_prototype_eval_pure(ctx, pfill_bit, &fill_bit)) { + if (!eval_pure(ctx, pfill_bit, pack(&fill_bit))) { RZ_LOG_ERROR("prototype: SHIFT(L/R) fill_bit failed to evaluate.\n"); return false; } @@ -369,7 +425,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( } bool (*shift)(RzBitVector *bv, ut32 size, bool fill_bit); shift = pure->code == RZ_IL_OP_SHIFTR ? rz_bv_rshift_fill : rz_bv_lshift_fill; - if (!shift(out->bv, rz_bv_to_ut64(y.bv), abstr_is_true(ctx->inst, &fill_bit))) { + if (!shift(out->bv, rz_bv_to_ut64(y.bv), !rz_bv_is_zero_vector(fill_bit.bv))) { rz_bv_fini(fill_bit.bv); rz_bv_fini(y.bv); goto map_to_top; @@ -404,7 +460,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( break; } - if (!interpreter_prototype_eval_pure(ctx, px, out)) { + if (!eval_pure(ctx, px, out_val)) { RZ_LOG_ERROR("prototype: CMP x failed to evaluate.\n"); return false; } @@ -412,7 +468,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y)) { + if (!eval_pure(ctx, py, pack(&y))) { RZ_LOG_ERROR("prototype: CMP y failed to evaluate.\n"); return false; } @@ -431,7 +487,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( STACK_ABSTR_DATA_OUT(ld_addr); RzILOpPure *key = pure->code == RZ_IL_OP_LOAD ? pure->op.load.key : pure->op.loadw.key; RzILMemIndex mem_idx = pure->code == RZ_IL_OP_LOAD ? 0 : pure->op.loadw.mem; - if (!interpreter_prototype_eval_pure(ctx, key, &ld_addr)) { + if (!eval_pure(ctx, key, pack(&ld_addr))) { RZ_LOG_ERROR("prototype: LOAD/LOADW key failed to evaluate.\n"); rz_bv_fini(ld_addr.bv); return false; @@ -450,9 +506,9 @@ RZ_IPI bool interpreter_prototype_eval_pure( rz_bv_and_inplace(ld_addr.bv, &mask); } - report_yield_xref(ctx, 0, ctx->astate->pc, &ld_addr, RZ_ANALYSIS_XREF_TYPE_MEM_READ); + report_yield_xref(ctx, 0, ctx->astate->pc, pack(&ld_addr), RZ_ANALYSIS_XREF_TYPE_MEM_READ); size_t n_bits = pure->code == RZ_IL_OP_LOAD ? ctx->inst->il_ctx->config->mem_key_size : pure->op.loadw.n_bits; - if (!load_abstr_data(ctx->inst, mem_idx, &ld_addr, n_bits, out)) { + if (!load_abstr_data(ctx->inst, mem_idx, ld_addr.bv, n_bits, out_val)) { rz_bv_fini(ld_addr.bv); goto map_to_top; } @@ -462,7 +518,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_MUL: { RzILOpPure *px = pure->op.mul.x; RzILOpPure *py = pure->op.mul.y; - if (!interpreter_prototype_eval_pure(ctx, px, out)) { + if (!eval_pure(ctx, px, out_val)) { RZ_LOG_ERROR("prototype: MUL x failed to evaluate.\n"); return false; } @@ -470,7 +526,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y)) { + if (!eval_pure(ctx, py, pack(&y))) { RZ_LOG_ERROR("prototype: MUL y failed to evaluate.\n"); return false; } @@ -488,7 +544,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_MOD: { RzILOpPure *px = pure->op.mod.x; RzILOpPure *py = pure->op.mod.y; - if (!interpreter_prototype_eval_pure(ctx, px, out)) { + if (!eval_pure(ctx, px, out_val)) { RZ_LOG_ERROR("prototype: MOD x failed to evaluate.\n"); return false; } @@ -496,7 +552,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y)) { + if (!eval_pure(ctx, py, pack(&y))) { RZ_LOG_ERROR("prototype: MOD y failed to evaluate.\n"); return false; } @@ -514,7 +570,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( case RZ_IL_OP_DIV: { RzILOpPure *px = pure->op.div.x; RzILOpPure *py = pure->op.div.y; - if (!interpreter_prototype_eval_pure(ctx, px, out)) { + if (!eval_pure(ctx, px, out_val)) { RZ_LOG_ERROR("prototype: DIV x failed to evaluate.\n"); return false; } @@ -522,7 +578,7 @@ RZ_IPI bool interpreter_prototype_eval_pure( goto map_to_top; } STACK_ABSTR_DATA_OUT(y); - if (!interpreter_prototype_eval_pure(ctx, py, &y)) { + if (!eval_pure(ctx, py, pack(&y))) { RZ_LOG_ERROR("prototype: DIV y failed to evaluate.\n"); return false; } @@ -584,129 +640,13 @@ RZ_IPI bool interpreter_prototype_eval_pure( return true; } - -#define MAX_INVOCATIONS_PER_BLOCK 3 - -bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, - RZ_NONNULL RZ_OUT RzStrBuf *sb); - -RZ_OWN RzInterpAbstrVal *clone_val(const RzInterpAbstrVal *val) { - ProtoIntrprAbstrData *r = RZ_NEW0(ProtoIntrprAbstrData); - if (!r) { - return NULL; - } - r->is_const = AD(val)->is_const; - r->bv = rz_bv_dup(AD(val)->bv); - return r; -} - -static bool eval(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzILCacheBlock *il_bb) { - - // Reset call candidate tracking for each basic block. - memset(&ctx->call_cand, 0, sizeof(ctx->call_cand)); - - // Now execute the actual effects of the BLOCK. - RzInterpAbstrState *astate = ctx->astate; - void **it; - rz_pvector_foreach (il_bb->il_ops, it) { - ut64 pc = astate->pc; - RZ_LOG_DEBUG("prototype: Eval PC = 0x%" PFMT64x "\n", pc); - RzStrBuf sb; - rz_strbuf_init(&sb); - state_as_str(ctx->astate, &sb); - RZ_LOG_DEBUG("%s\n", rz_strbuf_get(&sb)); - rz_strbuf_fini(&sb); - - rz_strbuf_init(&sb); - if (pc == il_bb->addr) { - rz_strbuf_append(&sb, "ENTRY "); - } - if (rz_vector_index_ptr(&il_bb->il_ops->v, rz_pvector_len(il_bb->il_ops) - 1) == it) { - rz_strbuf_append(&sb, "EXIT "); - } - state_as_str_short(ctx->inst, &sb, ctx->astate); - rz_meta_set_string(ctx->inst->a, RZ_META_TYPE_COMMENT, pc, rz_strbuf_get(&sb)); - rz_strbuf_fini(&sb); - - RzILCacheInsnPkt *pkt = *it; - - // Prepare next pc, the evalutation may overwrite this. - ut64 next_pc = pc + pkt->insn_pkt_size; - set_pc(ctx->astate, next_pc); - - if (!interpreter_prototype_eval_effect(ctx, pkt->effect, pkt->insn_pkt_size)) { - return false; - } - if (astate->pc_state != RZ_INTERP_PC_CONST || astate->pc != next_pc) { - // Unreachable or a jump happened somewhere other than fallthrough, so we can't continue - // interpreting the block linearly, but have to push the new location - break; - } - } - - if (astate->pc_state != RZ_INTERP_PC_UNREACHABLE) { - rz_interp_run_push(ctx, ctx->astate); - } - - return true; -} - -static bool init_state(RZ_BORROW RzInterpAbstrState *state) { - state->pc = 0; - state->pc_state = RZ_INTERP_PC_UNREACHABLE; - - RzIterator *it = ht_up_as_iter_keys(state->globals); - ut64 *k; - rz_iterator_foreach(it, k) { - ut64 djb2_reg_name = *k; - ProtoIntrprAbstrData *av = adata_new_top(); - if (!av) { - break; - } - ht_up_update(state->globals, djb2_reg_name, av); - } - rz_iterator_free(it); - return true; -} - -static bool reset_state(RZ_BORROW RzInterpAbstrState *state, ut64 entry_point) { - state->pc_state = RZ_INTERP_PC_CONST; - state->pc = entry_point; - - RzIterator *it = ht_up_as_iter_keys(state->globals); - ut64 *k; - rz_iterator_foreach(it, k) { - ut64 djb2_reg_name = *k; - RzInterpAbstrVal *av = ht_up_find(state->globals, djb2_reg_name, NULL); - rz_bv_set_from_ut64(AD(av)->bv, 0); - AD(av)->is_const = false; - } - rz_iterator_free(it); - state->bb_addr = 0; - state->bb_size = 0; - return true; -} - -static bool fini_state(RZ_BORROW RzInterpAbstrState *state) { - RzIterator *it = ht_up_as_iter(state->globals); - RzInterpAbstrVal **v; - rz_iterator_foreach(it, v) { - adata_free(*v); - } - rz_iterator_free(it); - - it = ht_up_as_iter(state->locals); - rz_iterator_foreach(it, v) { - adata_free(*v); - } - rz_iterator_free(it); - - it = ht_up_as_iter(state->lets); - rz_iterator_foreach(it, v) { - adata_free(*v); - } - rz_iterator_free(it); - return true; +void val_copy(RzInterpAbstrVal *dst_val, const RzInterpAbstrVal *src_val) { + ProtoIntrprAbstrData *dst = AD(dst_val); + ProtoIntrprAbstrData *src = AD(src_val); + rz_return_if_fail(dst && src && dst->bv && src->bv); + rz_bv_cast_inplace(dst->bv, rz_bv_len(src->bv), false); + rz_bv_copy(dst->bv, src->bv); + dst->is_const = src->is_const; } /** @@ -726,34 +666,6 @@ static bool join_val(RZ_BORROW RZ_INOUT RzInterpAbstrVal *a, RZ_BORROW RZ_IN con return changed; } -/** - * \brief Join (least upper bound) on var sets - * \return True if a was changed - */ -static bool join_vars(RZ_BORROW RZ_INOUT HtUP *a, RZ_BORROW RZ_IN HtUP *b) { - RzIterator *it = ht_up_as_iter_keys(a); - ut64 *k; - bool changed = false; - rz_iterator_foreach(it, k) { - RzInterpAbstrVal *av = ht_up_find(a, *k, NULL); - RzInterpAbstrVal *bv = ht_up_find(b, *k, NULL); - if (!av || !bv) { - continue; - } - if (join_val(av, bv)) { - changed = true; - } - } - return changed; -} - -bool join_state(RZ_BORROW RZ_INOUT RzInterpAbstrState *a, RZ_BORROW RZ_IN const RzInterpAbstrState *b) { - bool global_change = join_vars(a->globals, b->globals); - bool local_change = join_vars(a->locals, b->locals); - // lets are not be relevant here since they are immutable within their scope - return global_change || local_change; -} - bool val_as_str(RZ_NONNULL const RzInterpAbstrVal *val, RZ_NONNULL RZ_OUT RzStrBuf *sb) { rz_return_val_if_fail(val && sb, false); ProtoIntrprAbstrData *av = AD(val); @@ -770,71 +682,37 @@ bool val_as_str(RZ_NONNULL const RzInterpAbstrVal *val, RZ_NONNULL RZ_OUT RzStrB return true; } -bool state_as_str(RZ_NONNULL const RzInterpAbstrState *state, - RZ_NONNULL RZ_OUT RzStrBuf *sb) { - rz_return_val_if_fail(state && sb, false); - - rz_strbuf_append(sb, "Globals\n\n"); - rz_strbuf_append(sb, "\tpc = "); - if (state->pc_state == RZ_INTERP_PC_CONST) { - rz_strbuf_appendf(sb, "0x%" PFMT64x, state->pc); - } else { - rz_strbuf_append(sb, state->pc_state == RZ_INTERP_PC_ANY ? "⊤" : "⊥"); +static bool to_concrete_const(RZ_NONNULL const RzInterpAbstrVal *val, RZ_NULLABLE RZ_OUT RzBitVector *out) { + if (!AD(val)->is_const) { + return false; } - rz_strbuf_append(sb, "\n\n"); - - RzIterator *it = ht_up_as_iter_keys(state->globals); - ut64 *k; - rz_iterator_foreach(it, k) { - const char *gname = ht_up_find(state->var_name_hashes, *k, NULL); - rz_strbuf_appendf(sb, "\t%s = ", gname); - RzInterpAbstrVal *av = ht_up_find(state->globals, *k, NULL); - val_as_str(av, sb); - rz_strbuf_append(sb, "\n"); + if (out) { + rz_bv_cast_inplace(out, rz_bv_len(AD(val)->bv), false); + rz_bv_copy(out, AD(val)->bv); } - rz_iterator_free(it); return true; } -void state_as_str_short(RzInterpInstance *iset, RZ_OUT RzStrBuf *out, RzInterpAbstrState *astate) { - bool first = true; - RzIterator *it = ht_up_as_iter_keys(astate->globals); - ut64 *k; - rz_iterator_foreach(it, k) { - ut64 djb2_reg_name = *k; - RzInterpAbstrVal *av = ht_up_find(astate->globals, djb2_reg_name, NULL); - ProtoIntrprAbstrData *val = AD(av); - if (!val->is_const) { - continue; - } - if (!first) { - rz_strbuf_append(out, ", "); - } - first = false; - const char *varname = ht_up_find(astate->var_name_hashes, djb2_reg_name, NULL); - rz_strbuf_appendf(out, "%s = ", varname); - iset->plugin->val_as_str(av, out); - } -} - static RzInterpPlugin rz_interpreter_plugin_prototype = { + .name = "constant", + .val_new_top = val_new_top, + .val_free = val_free, + .set_top = val_set_top, + .is_top = val_is_top, + .may_be_bool = val_may_be_bool, + .to_concrete_const = to_concrete_const, + .copy = val_copy, + .eval_pure = eval_pure, + .join = join_val, + .val_as_str = val_as_str, +}; + +RZ_API RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype = { .name = "abstr_int_prototype", .author = "Rot127", .version = "0.1p", .desc = "A prototype interpreter for constant/top abstractions.", .license = "LGPL-3.0-only", - .supported_yields = { RZ_INTERP_YIELD_KIND_XREF, RZ_INTERP_YIELD_KIND_CALL_CANDIDATE }, - .clone_val = clone_val, - .eval = eval, - .init_state = init_state, - .reset_state = reset_state, - .fini_state = fini_state, - .join_state = join_state, - .state_as_str = state_as_str, - .val_as_str = val_as_str -}; - -RZ_API RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype = { .p_interpreter = &rz_interpreter_plugin_prototype, }; diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index 84bf9ca560d..fb3e44384c0 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -9,7 +9,6 @@ rz_inquiry_sources = [ 'interp/interpreter.c', 'interp/state.c', 'interp/val_abs_constant.c', - 'interp/eval.c', ] rz_inquiry_inc = [platform_inc] From 5adc36708df190dd2ff4df755892613e61255a72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Fri, 3 Jul 2026 14:35:46 +0200 Subject: [PATCH 307/334] Make interp plugin only handle actual abstract value evaluation --- librz/include/rz_inquiry.h | 2 +- librz/include/rz_inquiry/rz_interpreter.h | 34 +- librz/inquiry/README.md | 28 +- librz/inquiry/inquiry.c | 6 +- librz/inquiry/interp/eval.h | 37 -- librz/inquiry/interp/interpreter.c | 378 +++++++++++- librz/inquiry/interp/val_abs_constant.c | 698 ++++------------------ 7 files changed, 532 insertions(+), 651 deletions(-) delete mode 100644 librz/inquiry/interp/eval.h diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 1e128e6ad3c..432cd91e61c 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -29,7 +29,7 @@ typedef struct rz_inquiry_plugin_t { const char *version; const char *desc; const char *license; - RzInterpPlugin *p_interpreter; + RzInterpValueAbstraction *value_abstraction; } RzInquiryPlugin; typedef struct { diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 7c88d87332b..39ba421edbd 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -169,7 +169,8 @@ typedef struct { bool (*is_top)(RZ_NONNULL const RzInterpAbstrVal *val); ///< return whether the given value is top bool (*may_be_bool)(RZ_NONNULL const RzInterpAbstrVal *val, bool value); ///< return whether the given value's concrete set contains \p value void (*set_top)(RZ_OUT RZ_NONNULL RzInterpAbstrVal *dst); ///< Set \p val to be top - void (*set_const)(RZ_OUT RZ_NONNULL RzInterpAbstrVal *dst, RZ_IN RZ_NONNULL RzBitVector *src); ///< set \p dst to the least value that includes \p src + void (*set_const_bool)(RZ_OUT RZ_NONNULL RzInterpAbstrVal *dst, bool src); ///< set \p dst to the least value that includes \p src + void (*set_const_bv)(RZ_OUT RZ_NONNULL RzInterpAbstrVal *dst, RZ_IN RZ_NONNULL RzBitVector *src); ///< set \p dst to the least value that includes \p src /** * \brief Concretize an abstract value to a single bit vector, if possible @@ -192,11 +193,6 @@ typedef struct { */ bool (*join)(RZ_BORROW RZ_INOUT RzInterpAbstrVal *a, RZ_BORROW RZ_IN const RzInterpAbstrVal *b); - /** - * \brief Evaluate \p pure and return the result in \p out - */ - bool (*eval_pure)(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT RzInterpAbstrVal *out); - /** * \brief Builds a string for printing an abstract value. * @@ -205,7 +201,27 @@ typedef struct { bool (*val_as_str)(RZ_NONNULL const RzInterpAbstrVal *state, RZ_NONNULL RZ_OUT RzStrBuf *str_buf); -} RzInterpPlugin; + void (*eval_cast)(ut32 length, RZ_NONNULL const RzInterpAbstrVal *fill, RZ_INOUT RZ_NONNULL RzInterpAbstrVal *val); ///< RZ_IL_OP_CAST + void (*eval_shift)(bool right, RZ_NONNULL RZ_INOUT RzInterpAbstrVal *x, RZ_NONNULL const RzInterpAbstrVal *y, RZ_NONNULL const RzInterpAbstrVal *fill_bit); ///< RZ_IL_OP_SHIFTL, RZ_IL_OP_SHIFTR + + /** + * \brief Evaluate a binary operation on two abstract values + * + * \param code The operation to evaluate. May be any IL op that takes exactly two bitvector or boolean operands. + * \param x Output, as well as `x` (or `low`) operand + * \param y `y` (or `low`) operand + */ + void (*eval_binop)(RzILOpPureCode code, RZ_NONNULL RZ_INOUT RzInterpAbstrVal *x, RZ_NONNULL const RzInterpAbstrVal *y); + + /** + * \brief Evaluate a unary operation on an abstract value + * + * \param code The operation to evaluate. May be any IL op that takes exactly one bitvector or boolean operand. + * \param val Output, as well as operand + */ + void (*eval_unop)(RzILOpPureCode code, RZ_NONNULL RZ_INOUT RzInterpAbstrVal *val); + +} RzInterpValueAbstraction; typedef struct { size_t mem_idx; ///< The memory space to read/write. @@ -258,7 +274,7 @@ struct rz_interp_instance_t { /** * \brief The interpreter plugin. */ - RzInterpPlugin *plugin; + RzInterpValueAbstraction *plugin; }; RZ_API RZ_OWN RzInterpRunState *rz_interp_run_state_new(); @@ -285,7 +301,7 @@ RZ_API RZ_OWN RzInterpYieldRBuf *rz_interp_yield_rbuf_new(RzInterpYieldKind kind RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( RzAnalysis *analysis, - RZ_NONNULL RZ_OWN RzInterpPlugin *plugin, + RZ_NONNULL RZ_OWN RzInterpValueAbstraction *plugin, RZ_NONNULL RZ_BORROW RzILCacheClient *il_cache_client, RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM], RZ_NONNULL const RzVector /**/ *ignored_code); diff --git a/librz/inquiry/README.md b/librz/inquiry/README.md index 61f041c5ed4..2f517673e4f 100644 --- a/librz/inquiry/README.md +++ b/librz/inquiry/README.md @@ -5,10 +5,26 @@ Module implementing basic and advanced binary analysis. -## Directory structure +## Interpreter Notes -```c -. // API implementation -└── p // Plugin implementations - └── interpreter_prototype // The prototype abstract interpreter for basic analysis. -``` +Ideas for optimization (do not implement any of these without profiling first): +* evaluation (`eval_pure` in `interpreter.c` and possibly also `eval_effect`) + often temporarily allocates temporary abstract values if there is more than + one operand in an op. + E.g. for `(add x y)`, the output RzAbstractVal can be reused to evaluate `x` + into first, but `y` is allocated dynamically on the heap. + Because the size of dynamic values depends on the plugin, we can't use + regular stack memory. However we could build our own stack to the side and + re-use allocations that way. + For example an `RzVector` where the element size is given by the plugin. +* In evaluation of an op, one could often short-circuit if one operand is + already known to be top. + E.g. for `(add x y)` with constant/top abstraction, if `x` evaluates to top, + we don't have to evaluate `y` anymore because the result will be top anyway. + However, this may not always be the case, e.g. for `(logand x y)`, if `x` is + top, `y` may evaluate to 0 and the result will be a constant again. And if + the plugin defines a more fine-grained abstraction then even more cases will + result in non-top results. + So the plugin must decide whether to short-circuit or not. It has to be + profiled if the cost of asking the plugin all the time is really compensated + by avoided evalutations in practical code. diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 183b986507a..3e9e1021a84 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -49,7 +49,7 @@ RZ_API RZ_BORROW RzInquiryPlugin *rz_inquiry_get_plugin(size_t index) { RZ_API bool rz_inquiry_plugin_add(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OWN RZ_NONNULL RzInquiryPlugin *plugin) { rz_return_val_if_fail(inquiry && plugin, false); - if (!plugin->p_interpreter) { + if (!plugin->value_abstraction) { rz_warn_if_reached(); return false; } @@ -77,7 +77,7 @@ RZ_API bool rz_inquiry_plugin_del(RZ_BORROW RZ_NONNULL RzInquiry *inquiry, RZ_OW } #endif free(p_data); - if (plugin->p_interpreter) { + if (plugin->value_abstraction) { return ht_sp_delete(inquiry->plugins, plugin->name); } rz_warn_if_reached(); @@ -560,7 +560,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, } intp_iset = rz_interp_instance_new( core->analysis, - prototype->p_interpreter, + prototype->value_abstraction, cache_client, yield_rbufs, ignored_code); diff --git a/librz/inquiry/interp/eval.h b/librz/inquiry/interp/eval.h deleted file mode 100644 index d1c1e0103ad..00000000000 --- a/librz/inquiry/interp/eval.h +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-FileCopyrightText: 2025 RizinOrg -// SPDX-License-Identifier: LGPL-3.0-only - -#ifndef PROTOYPE_EVAL_H -#define PROTOYPE_EVAL_H - -#include "rz_analysis.h" -#include -#include -#include -#include - -void write_var_to_state(RzInterpInstance *inst, - RzInterpAbstrState *astate, - RzILVarKind kind, - ut64 var_id, - const RzInterpAbstrVal *data); -bool read_var_from_state(RzInterpInstance *inst, - RzInterpAbstrState *astate, - RzILVarKind kind, - ut64 var_id, - RZ_OUT RzInterpAbstrVal *data); -bool load_abstr_data( - RzInterpInstance *inst, - RzILMemIndex mem_idx, - const RzBitVector *addr, - size_t n_bits, - RZ_OUT RzInterpAbstrVal *out); - -bool report_yield_xref( - RzInterpRunContext *ctx, - size_t insn_pkt_size, - ut64 from, - const RzInterpAbstrVal *to, - RzAnalysisXRefType type); - -#endif // PROTOYPE_EVAL_H diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 6fc900e258d..147151b2dac 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -15,8 +15,6 @@ #include #include -#include "eval.h" - RZ_API void rz_interp_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yield_rbufs) { if (!yield_rbufs) { return; @@ -337,7 +335,7 @@ bool join_state(RzInterpInstance *inst, RZ_BORROW RZ_INOUT RzInterpAbstrState *a */ RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( RzAnalysis *analysis, - RZ_NONNULL RZ_OWN RzInterpPlugin *plugin, + RZ_NONNULL RZ_OWN RzInterpValueAbstraction *plugin, RZ_NONNULL RZ_BORROW RzILCacheClient *il_cache_client, RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM], RZ_NONNULL const RzVector /**/ *ignored_code) { @@ -590,7 +588,7 @@ bool load_abstr_data( inst->plugin->set_top(out); return false; } - inst->plugin->set_const(out, &out_bv); + inst->plugin->set_const_bv(out, &out_bv); char *bytes = rz_bv_as_hex_string(&out_bv, true); RZ_LOG_DEBUG("prototype: READ @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); @@ -626,7 +624,350 @@ static bool value_indicates_ret_addr_write(RzInterpRunContext *ctx, RzInterpAbst return ret; } -static bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, +static bool eval_pure(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT RzInterpAbstrVal *out) { + switch (pure->code) { + default: + case RZ_IL_OP_VAR: { + if (!read_var_from_state(ctx->inst, ctx->astate, pure->op.var.kind, pure->op.var.hash, out)) { + RZ_LOG_ERROR("prototype: VAR failed to evaluate. The %s '%s' doesn't exist.\n", + rz_il_var_kind_name(pure->op.var.kind), + pure->op.var.v); + return false; + } + break; + } + case RZ_IL_OP_LET: { + ut64 vhash = pure->op.let.hash; + if (!eval_pure(ctx, pure->op.let.exp, out)) { + RZ_LOG_ERROR("prototype: LET expression failed to evaluate.\n"); + return false; + } + write_var_to_state(ctx->inst, ctx->astate, RZ_IL_VAR_KIND_LOCAL_PURE, vhash, out); + // Evaluate body + if (!eval_pure(ctx, pure->op.let.body, out)) { + RZ_LOG_ERROR("prototype: LET body failed to evaluate.\n"); + return false; + } + // No need to free the LET variable. + // It is simply overwritten next time. + break; + } + case RZ_IL_OP_ITE: { + if (!eval_pure(ctx, pure->op.ite.condition, out)) { + RZ_LOG_ERROR("prototype: ITE condition failed to evaluate.\n"); + return false; + } + + RzBitVector cond_bv; + rz_bv_init(&cond_bv, 64); + if (!ctx->inst->plugin->to_concrete_const(out, &cond_bv)) { + // Can't decide which pure to evaluate. + rz_bv_fini(&cond_bv); + goto map_to_top; + } + bool cond_bool = !rz_bv_is_zero_vector(&cond_bv); + + // TODO: eval both if top + if (cond_bool) { + if (!eval_pure(ctx, pure->op.ite.x, out)) { + RZ_LOG_ERROR("prototype: ITE x failed to evaluate.\n"); + return false; + } + } else { + if (!eval_pure(ctx, pure->op.ite.y, out)) { + RZ_LOG_ERROR("prototype: ITE y failed to evaluate.\n"); + return false; + } + } + break; + } + case RZ_IL_OP_B0: + ctx->inst->plugin->set_const_bool(out, false); + break; + case RZ_IL_OP_B1: + ctx->inst->plugin->set_const_bool(out, false); + break; + case RZ_IL_OP_CAST: { + if (!eval_pure(ctx, pure->op.cast.val, out)) { + RZ_LOG_ERROR("prototype: CAST val failed to evaluate.\n"); + return false; + } + RzInterpAbstrVal *fill_bit = ctx->inst->plugin->val_new_top(); + if (!fill_bit) { + return false; + } + if (!eval_pure(ctx, pure->op.cast.fill, fill_bit)) { + RZ_LOG_ERROR("prototype: CAST fill failed to evaluate.\n"); + return false; + } + ctx->inst->plugin->eval_cast(pure->op.cast.length, fill_bit, out); + ctx->inst->plugin->val_free(fill_bit); + break; + } + case RZ_IL_OP_BITV: + ctx->inst->plugin->set_const_bv(out, pure->op.bitv.value); + break; + case RZ_IL_OP_APPEND: + case RZ_IL_OP_LOGAND: + case RZ_IL_OP_AND: + case RZ_IL_OP_LOGOR: + case RZ_IL_OP_OR: + case RZ_IL_OP_LOGXOR: + case RZ_IL_OP_XOR: + case RZ_IL_OP_ADD: + case RZ_IL_OP_SUB: + case RZ_IL_OP_SLE: + case RZ_IL_OP_ULE: + case RZ_IL_OP_EQ: + case RZ_IL_OP_MUL: + case RZ_IL_OP_MOD: + case RZ_IL_OP_DIV: { + RzILOpPure *px; + RzILOpPure *py; + switch (pure->code) { + case RZ_IL_OP_APPEND: + // we use low as the x/out value because in the case of constant operands, + // appending high bits to a bitvector is more efficient than prepending + // low bits in place. + px = pure->op.append.low; + py = pure->op.append.high; + break; + case RZ_IL_OP_LOGAND: + px = pure->op.logand.x; + py = pure->op.logand.y; + break; + case RZ_IL_OP_AND: + px = pure->op.booland.x; + py = pure->op.booland.y; + break; + case RZ_IL_OP_LOGOR: + px = pure->op.logor.x; + py = pure->op.logor.y; + break; + case RZ_IL_OP_OR: + px = pure->op.boolor.x; + py = pure->op.boolor.y; + break; + case RZ_IL_OP_LOGXOR: + px = pure->op.logxor.x; + py = pure->op.logxor.y; + break; + case RZ_IL_OP_XOR: + px = pure->op.boolxor.x; + py = pure->op.boolxor.y; + break; + case RZ_IL_OP_ADD: + px = pure->op.add.x; + py = pure->op.add.y; + break; + case RZ_IL_OP_SUB: + px = pure->op.sub.x; + py = pure->op.sub.y; + break; + case RZ_IL_OP_SLE: + px = pure->op.sle.x; + py = pure->op.sle.y; + break; + case RZ_IL_OP_ULE: + px = pure->op.ule.x; + py = pure->op.ule.y; + break; + case RZ_IL_OP_EQ: + px = pure->op.eq.x; + py = pure->op.eq.y; + break; + case RZ_IL_OP_MUL: + px = pure->op.mul.x; + py = pure->op.mul.y; + break; + case RZ_IL_OP_MOD: + px = pure->op.mod.x; + py = pure->op.mod.y; + break; + case RZ_IL_OP_DIV: + px = pure->op.div.x; + py = pure->op.div.y; + break; + default: + // this switch should be in sync with outer cases + rz_warn_if_reached(); + return false; + } + if (!eval_pure(ctx, px, out)) { + RZ_LOG_ERROR("prototype: binop x failed to evaluate.\n"); + return false; + } + // Hint: As an optimization, we could short-circuit if out is top here. + // However it entirely depends on the plugin whether this is possible, or we lose a lot of precision by doing so. + RzInterpAbstrVal *y = ctx->inst->plugin->val_new_top(); + if (!y) { + return false; + } + if (!eval_pure(ctx, py, y)) { + RZ_LOG_ERROR("prototype: binop y failed to evaluate.\n"); + return false; + } + ctx->inst->plugin->eval_binop(pure->code, out, y); + ctx->inst->plugin->val_free(y); + break; + } + case RZ_IL_OP_LOGNOT: + case RZ_IL_OP_INV: + case RZ_IL_OP_IS_ZERO: + case RZ_IL_OP_LSB: + case RZ_IL_OP_MSB: + case RZ_IL_OP_NEG: { + RzILOpPure *x; + switch (pure->code) { + case RZ_IL_OP_LOGNOT: + x = pure->op.lognot.bv; + break; + case RZ_IL_OP_INV: + x = pure->op.boolinv.x; + break; + case RZ_IL_OP_IS_ZERO: + x = pure->op.is_zero.bv; + break; + case RZ_IL_OP_LSB: + x = pure->op.lsb.bv; + break; + case RZ_IL_OP_MSB: + x = pure->op.msb.bv; + break; + case RZ_IL_OP_NEG: + x = pure->op.neg.bv; + break; + default: + // this switch should be in sync with outer cases + rz_warn_if_reached(); + return false; + } + if (!eval_pure(ctx, x, out)) { + RZ_LOG_ERROR("prototype: unop x failed to evaluate.\n"); + return false; + } + ctx->inst->plugin->eval_unop(pure->code, out); + break; + } + case RZ_IL_OP_SHIFTL: + case RZ_IL_OP_SHIFTR: { + RzILOpPure *px = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.x : pure->op.shiftl.x; + RzILOpPure *py = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.y : pure->op.shiftl.y; + RzILOpPure *pfill_bit = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.fill_bit : pure->op.shiftl.fill_bit; + if (!eval_pure(ctx, px, out)) { + RZ_LOG_ERROR("prototype: SHIFT(L/R) x failed to evaluate.\n"); + return false; + } + // Hint: As an optimization, we could short-circuit if out is top here. + // However it entirely depends on the plugin whether this is possible, or we lose a lot of precision by doing so. + RzInterpAbstrVal *y = ctx->inst->plugin->val_new_top(); + if (!y) { + return false; + } + if (!eval_pure(ctx, py, y)) { + RZ_LOG_ERROR("prototype: SHIFT(L/R) y failed to evaluate.\n"); + return false; + } + RzInterpAbstrVal *fill_bit = ctx->inst->plugin->val_new_top(); + if (!fill_bit) { + ctx->inst->plugin->val_free(y); + return false; + } + if (!eval_pure(ctx, pfill_bit, fill_bit)) { + RZ_LOG_ERROR("prototype: SHIFT(L/R) fill_bit failed to evaluate.\n"); + ctx->inst->plugin->val_free(y); + return false; + } + ctx->inst->plugin->eval_shift(pure->code == RZ_IL_OP_SHIFTR, out, y, fill_bit); + ctx->inst->plugin->val_free(y); + ctx->inst->plugin->val_free(fill_bit); + break; + } + case RZ_IL_OP_LOADW: + case RZ_IL_OP_LOAD: { + RzILOpPure *key = pure->code == RZ_IL_OP_LOAD ? pure->op.load.key : pure->op.loadw.key; + RzILMemIndex mem_idx = pure->code == RZ_IL_OP_LOAD ? 0 : pure->op.loadw.mem; + if (!eval_pure(ctx, key, out)) { + RZ_LOG_ERROR("prototype: LOAD/LOADW key failed to evaluate.\n"); + return false; + } + + // Hint: Instead of supporting only a single constant load addr and mapping all other + // loads to top, if the concrete set of the address is reasonably small, we could load + // from all possible addresses and join the results. + RzBitVector ld_addr; + rz_bv_init(&ld_addr, 64); + if (!ctx->inst->plugin->to_concrete_const(out, &ld_addr)) { + rz_bv_fini(&ld_addr); + goto map_to_top; + } + if (rz_bv_len(&ld_addr) == 64) { + // TODO: Remove normalization. + // Unset bit 63 is required, because the RzBuffer API only supports + // st64 addresses. + RzBitVector mask = { 0 }; + rz_bv_init(&mask, 64); + rz_bv_set_from_ut64(&mask, 0x7fffffffffffffff); + rz_bv_and_inplace(&ld_addr, &mask); + } + + report_yield_xref(ctx, 0, ctx->astate->pc, out, RZ_ANALYSIS_XREF_TYPE_MEM_READ); + size_t n_bits = pure->code == RZ_IL_OP_LOAD ? ctx->inst->il_ctx->config->mem_key_size : pure->op.loadw.n_bits; + if (!load_abstr_data(ctx->inst, mem_idx, &ld_addr, n_bits, out)) { + rz_bv_fini(&ld_addr); + goto map_to_top; + } + rz_bv_fini(&ld_addr); + break; + } + case RZ_IL_OP_SDIV: + case RZ_IL_OP_SMOD: + case RZ_IL_OP_FLOAT: + case RZ_IL_OP_FBITS: + case RZ_IL_OP_IS_FINITE: + case RZ_IL_OP_IS_NAN: + case RZ_IL_OP_IS_INF: + case RZ_IL_OP_IS_FZERO: + case RZ_IL_OP_IS_FNEG: + case RZ_IL_OP_IS_FPOS: + case RZ_IL_OP_FNEG: + case RZ_IL_OP_FABS: + case RZ_IL_OP_FCAST_INT: + case RZ_IL_OP_FCAST_SINT: + case RZ_IL_OP_FCAST_FLOAT: + case RZ_IL_OP_FCAST_SFLOAT: + case RZ_IL_OP_FCONVERT: + case RZ_IL_OP_FREQUAL: + case RZ_IL_OP_FSUCC: + case RZ_IL_OP_FPRED: + case RZ_IL_OP_FORDER: + case RZ_IL_OP_FROUND: + case RZ_IL_OP_FSQRT: + case RZ_IL_OP_FRSQRT: + case RZ_IL_OP_FADD: + case RZ_IL_OP_FSUB: + case RZ_IL_OP_FMUL: + case RZ_IL_OP_FDIV: + case RZ_IL_OP_FMOD: + case RZ_IL_OP_FHYPOT: + case RZ_IL_OP_FPOW: + case RZ_IL_OP_FMAD: + case RZ_IL_OP_FROOTN: + case RZ_IL_OP_FPOWN: + case RZ_IL_OP_FCOMPOUND: + case RZ_IL_OP_FEXCEPT: + RZ_LOG_ERROR("Unhandled pure %" PFMT32d "\n", pure->code); + // Not implemented. + goto map_to_top; + } + return true; + +map_to_top: + ctx->inst->plugin->set_top(out); + return true; +} + +static bool eval_effect(RzInterpRunContext *ctx, const RzILOpEffect *effect, size_t insn_pkt_size) { rz_return_val_if_fail(ctx->astate->pc_state == RZ_INTERP_PC_CONST, false); @@ -641,10 +982,10 @@ static bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, break; } case RZ_IL_OP_SEQ: { - if (!interpreter_prototype_eval_effect(ctx, effect->op.seq.x, insn_pkt_size)) { + if (!eval_effect(ctx, effect->op.seq.x, insn_pkt_size)) { goto error; } - if (!interpreter_prototype_eval_effect(ctx, effect->op.seq.y, insn_pkt_size)) { + if (!eval_effect(ctx, effect->op.seq.y, insn_pkt_size)) { goto error; } break; @@ -652,7 +993,7 @@ static bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, case RZ_IL_OP_SET: { eval_out = ctx->inst->plugin->val_new_top(); ut64 vhash = effect->op.set.hash; - if (!eval_out || !ctx->inst->plugin->eval_pure(ctx, effect->op.set.x, eval_out)) { + if (!eval_out || !eval_pure(ctx, effect->op.set.x, eval_out)) { goto error; } RzILVarKind kind = effect->op.set.is_local ? RZ_IL_VAR_KIND_LOCAL : RZ_IL_VAR_KIND_GLOBAL; @@ -668,7 +1009,7 @@ static bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, } case RZ_IL_OP_JMP: { eval_out = ctx->inst->plugin->val_new_top(); - if (!eval_out || !ctx->inst->plugin->eval_pure(ctx, effect->op.jmp.dst, eval_out)) { + if (!eval_out || !eval_pure(ctx, effect->op.jmp.dst, eval_out)) { goto error; } RzBitVector eval_out_bv; @@ -730,7 +1071,7 @@ static bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, } case RZ_IL_OP_BRANCH: { eval_out = ctx->inst->plugin->val_new_top(); - if (!eval_out || !ctx->inst->plugin->eval_pure(ctx, effect->op.branch.condition, eval_out)) { + if (!eval_out || !eval_pure(ctx, effect->op.branch.condition, eval_out)) { goto error; } bool may_be_true = ctx->inst->plugin->may_be_bool(eval_out, true); @@ -739,11 +1080,11 @@ static bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, RzInterpAbstrState *true_state = rz_interp_abstr_state_clone(ctx->inst, ctx->astate); RzInterpAbstrState *false_state = ctx->astate; ctx->astate = true_state; - if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.true_eff, insn_pkt_size)) { + if (!eval_effect(ctx, effect->op.branch.true_eff, insn_pkt_size)) { goto error; } ctx->astate = false_state; - if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.false_eff, insn_pkt_size)) { + if (!eval_effect(ctx, effect->op.branch.false_eff, insn_pkt_size)) { goto error; } if (true_state->pc_state == false_state->pc_state && true_state->pc == false_state->pc) { @@ -756,11 +1097,11 @@ static bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, } rz_interp_abstr_state_free(ctx->inst, true_state); } else if (may_be_true) { - if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.true_eff, insn_pkt_size)) { + if (!eval_effect(ctx, effect->op.branch.true_eff, insn_pkt_size)) { goto error; } } else if (may_be_false) { - if (!interpreter_prototype_eval_effect(ctx, effect->op.branch.false_eff, insn_pkt_size)) { + if (!eval_effect(ctx, effect->op.branch.false_eff, insn_pkt_size)) { goto error; } } @@ -771,7 +1112,7 @@ static bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, RzInterpAbstrVal *st_addr = ctx->inst->plugin->val_new_top(); RzILOpPure *key = effect->code == RZ_IL_OP_STORE ? effect->op.store.key : effect->op.storew.key; RzILMemIndex mem_idx = effect->code == RZ_IL_OP_STORE ? 0 : effect->op.storew.mem; - if (!ctx->inst->plugin->eval_pure(ctx, key, st_addr)) { + if (!eval_pure(ctx, key, st_addr)) { RZ_LOG_ERROR("prototype: STORE/STOREW key failed to evaluate.\n"); ctx->inst->plugin->val_free(st_addr); goto error; @@ -795,12 +1136,12 @@ static bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, } RzILOpPure *pval = effect->code == RZ_IL_OP_STORE ? effect->op.store.value : effect->op.storew.value; - if (!ctx->inst->plugin->eval_pure(ctx, pval, eval_out)) { + if (!eval_pure(ctx, pval, eval_out)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); rz_bv_fini(&st_addr_bv); goto error; } - if (!eval_out || !ctx->inst->plugin->eval_pure(ctx, effect->op.branch.condition, eval_out)) { + if (!eval_out || !eval_pure(ctx, effect->op.branch.condition, eval_out)) { rz_bv_fini(&st_addr_bv); break; } @@ -832,7 +1173,6 @@ static bool interpreter_prototype_eval_effect(RzInterpRunContext *ctx, return false; } - static bool set_pc(RzInterpAbstrState *state, ut64 pc) { rz_return_val_if_fail(state, false); state->pc = pc; @@ -874,7 +1214,7 @@ static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzIL ut64 next_pc = pc + pkt->insn_pkt_size; set_pc(ctx->astate, next_pc); - if (!interpreter_prototype_eval_effect(ctx, pkt->effect, pkt->insn_pkt_size)) { + if (!eval_effect(ctx, pkt->effect, pkt->insn_pkt_size)) { return false; } if (astate->pc_state != RZ_INTERP_PC_CONST || astate->pc != next_pc) { diff --git a/librz/inquiry/interp/val_abs_constant.c b/librz/inquiry/interp/val_abs_constant.c index 20c63e450e1..db0b63ab137 100644 --- a/librz/inquiry/interp/val_abs_constant.c +++ b/librz/inquiry/interp/val_abs_constant.c @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: 2025 RizinOrg // SPDX-License-Identifier: LGPL-3.0-only -#include "eval.h" #include "rz_util/rz_assert.h" #include @@ -35,29 +34,10 @@ static RzInterpAbstrVal *pack(ProtoIntrprAbstrData *val) { return rz_interp_abstr_val_pack(val); } -/** - * \brief In bytes - * - * TODO: find a sweet spot here where this size is as small is possible, - * but in practice only very few heap allocations have to happen. - */ -#define BV_STACK_MAX_SIZE 0x100 - -/** - * \brief Initializes an AbstractData object on the stack. - * The bitvector pre-allocates BV_STACK_MAX_SIZE bytes on the stack for large bit vectors. - * Any value larger than these bits will be stored in heap allocated memory. - * Because of this the bit vector should always be passed to rz_bv_fini() after usage. - */ -#define STACK_ABSTR_DATA_OUT(name) \ - ut8 _##name##_bv_large_buf[BV_STACK_MAX_SIZE] = { 0 }; \ - RzBitVector _##name##_bv_large = { .len = BV_STACK_MAX_SIZE, ._elem_len = BV_STACK_MAX_SIZE, .bits.large_a = _##name##_bv_large_buf, .stack_alloc = true }; \ - ProtoIntrprAbstrData name = { .is_const = false, .bv = &_##name##_bv_large }; - static RZ_OWN RzInterpAbstrVal *val_new_top() { ProtoIntrprAbstrData *ad = RZ_NEW0(ProtoIntrprAbstrData); ad->is_const = false; - ad->bv = rz_bv_new(BV_STACK_MAX_SIZE); + ad->bv = rz_bv_new(64); return pack(ad); } @@ -85,559 +65,16 @@ static void val_set_top(RZ_NONNULL RzInterpAbstrVal *val) { AD(val)->is_const = false; } -static bool eval_pure(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT RzInterpAbstrVal *out_val) { - ProtoIntrprAbstrData *out = AD(out_val); - switch (pure->code) { - default: - case RZ_IL_OP_VAR: { - if (!read_var_from_state(ctx->inst, ctx->astate, pure->op.var.kind, pure->op.var.hash, out_val)) { - RZ_LOG_ERROR("prototype: VAR failed to evaluate. The %s '%s' doesn't exist.\n", - rz_il_var_kind_name(pure->op.var.kind), - pure->op.var.v); - return false; - } - break; - } - case RZ_IL_OP_LET: { - ut64 vhash = pure->op.let.hash; - if (!eval_pure(ctx, pure->op.let.exp, out_val)) { - RZ_LOG_ERROR("prototype: LET expression failed to evaluate.\n"); - return false; - } - write_var_to_state(ctx->inst, ctx->astate, RZ_IL_VAR_KIND_LOCAL_PURE, vhash, out_val); - // Evaluate body - if (!eval_pure(ctx, pure->op.let.body, out_val)) { - RZ_LOG_ERROR("prototype: LET body failed to evaluate.\n"); - return false; - } - // No need to free the LET variable. - // It is simply overwritten next time. - break; - } - case RZ_IL_OP_ITE: { - if (!eval_pure(ctx, pure->op.ite.condition, out_val)) { - RZ_LOG_ERROR("prototype: ITE condition failed to evaluate.\n"); - return false; - } - if (!out->is_const) { - // Can't decide which pure to evaluate. - goto map_to_top; - } - - // TODO: eval both if top - if (!val_may_be_bool(out_val, false)) { - if (!eval_pure(ctx, pure->op.ite.x, out_val)) { - RZ_LOG_ERROR("prototype: ITE x failed to evaluate.\n"); - return false; - } - } else { - if (!eval_pure(ctx, pure->op.ite.y, out_val)) { - RZ_LOG_ERROR("prototype: ITE y failed to evaluate.\n"); - return false; - } - } - break; - } - case RZ_IL_OP_B0: - if (rz_bv_len(out->bv) != 1) { - rz_bv_cast_inplace(out->bv, 1, false); - } - rz_bv_set(out->bv, 0, false); - out->is_const = true; - break; - case RZ_IL_OP_B1: - if (rz_bv_len(out->bv) != 1) { - rz_bv_cast_inplace(out->bv, 1, false); - } - rz_bv_set(out->bv, 0, true); - out->is_const = true; - break; - case RZ_IL_OP_CAST: { - if (!eval_pure(ctx, pure->op.cast.val, out_val)) { - RZ_LOG_ERROR("prototype: CAST val failed to evaluate.\n"); - return false; - } - if (!out->is_const) { - goto map_to_top; - } - STACK_ABSTR_DATA_OUT(fill_bit); - if (!eval_pure(ctx, pure->op.cast.fill, pack(&fill_bit))) { - RZ_LOG_ERROR("prototype: CAST fill failed to evaluate.\n"); - return false; - } - if (!fill_bit.is_const) { - rz_bv_fini(fill_bit.bv); - goto map_to_top; - } - rz_bv_cast_inplace(out->bv, pure->op.cast.length, !rz_bv_is_zero_vector(fill_bit.bv)); - break; - } - case RZ_IL_OP_BITV: - rz_bv_cast_inplace(out->bv, rz_bv_len(pure->op.bitv.value), false); - rz_bv_copy(out->bv, pure->op.bitv.value); - out->is_const = true; - break; - case RZ_IL_OP_APPEND: { - STACK_ABSTR_DATA_OUT(high); - if (!eval_pure(ctx, pure->op.append.high, pack(&high))) { - RZ_LOG_ERROR("prototype: APPEND high failed to evaluate.\n"); - return false; - } - if (!high.is_const) { - rz_bv_fini(high.bv); - goto map_to_top; - } - if (!eval_pure(ctx, pure->op.append.low, pack(out))) { - RZ_LOG_ERROR("prototype: APPEND low failed to evaluate.\n"); - rz_bv_fini(high.bv); - return false; - } - if (!out->is_const) { - rz_bv_fini(high.bv); - goto map_to_top; - } - rz_bv_cast_inplace(out->bv, rz_bv_len(out->bv) + rz_bv_len(high.bv), false); - rz_bv_copy_nbits(high.bv, 0, out->bv, rz_bv_len(out->bv), rz_bv_len(high.bv)); - out->is_const = true; - rz_bv_fini(high.bv); - break; - } - case RZ_IL_OP_LOGNOT: - case RZ_IL_OP_INV: { - RzILOpPure *x = pure->code == RZ_IL_OP_INV ? pure->op.boolinv.x : pure->op.lognot.bv; - if (!eval_pure(ctx, x, out_val)) { - RZ_LOG_ERROR("prototype: INV x failed to evaluate.\n"); - return false; - } - if (out->is_const) { - rz_bv_not_inplace(out->bv); - } - break; - } - case RZ_IL_OP_LOGAND: - case RZ_IL_OP_AND: { - RzILOpPure *px = pure->code == RZ_IL_OP_AND ? pure->op.booland.x : pure->op.logand.x; - RzILOpPure *py = pure->code == RZ_IL_OP_AND ? pure->op.booland.y : pure->op.logand.y; - if (!eval_pure(ctx, px, out_val)) { - RZ_LOG_ERROR("prototype: AND x failed to evaluate.\n"); - return false; - } - if (!out->is_const) { - goto map_to_top; - } - STACK_ABSTR_DATA_OUT(y); - if (!eval_pure(ctx, py, pack(&y))) { - RZ_LOG_ERROR("prototype: AND y failed to evaluate.\n"); - return false; - } - if (!y.is_const) { - rz_bv_fini(y.bv); - goto map_to_top; - } - if (!rz_bv_and_inplace(out->bv, y.bv)) { - rz_bv_fini(y.bv); - goto map_to_top; - } - rz_bv_fini(y.bv); - break; - } - case RZ_IL_OP_LOGOR: - case RZ_IL_OP_OR: { - RzILOpPure *px = pure->code == RZ_IL_OP_OR ? pure->op.boolor.x : pure->op.logor.x; - RzILOpPure *py = pure->code == RZ_IL_OP_OR ? pure->op.boolor.y : pure->op.logor.y; - if (!eval_pure(ctx, px, out_val)) { - RZ_LOG_ERROR("prototype: OR x failed to evaluate.\n"); - return false; - } - if (!out->is_const) { - goto map_to_top; - } - STACK_ABSTR_DATA_OUT(y); - if (!eval_pure(ctx, py, pack(&y))) { - RZ_LOG_ERROR("prototype: OR y failed to evaluate.\n"); - return false; - } - if (!y.is_const) { - rz_bv_fini(y.bv); - goto map_to_top; - } - if (!rz_bv_or_inplace(out->bv, y.bv)) { - rz_bv_fini(y.bv); - goto map_to_top; - } - rz_bv_fini(y.bv); - break; - } - case RZ_IL_OP_LOGXOR: - case RZ_IL_OP_XOR: { - RzILOpPure *px = pure->code == RZ_IL_OP_XOR ? pure->op.boolxor.x : pure->op.logxor.x; - RzILOpPure *py = pure->code == RZ_IL_OP_XOR ? pure->op.boolxor.y : pure->op.logxor.y; - if (!eval_pure(ctx, px, out_val)) { - RZ_LOG_ERROR("prototype: XOR x failed to evaluate.\n"); - return false; - } - if (!out->is_const) { - goto map_to_top; - } - STACK_ABSTR_DATA_OUT(y); - if (!eval_pure(ctx, py, pack(&y))) { - RZ_LOG_ERROR("prototype: XOR y failed to evaluate.\n"); - return false; - } - if (!y.is_const) { - rz_bv_fini(y.bv); - goto map_to_top; - } - if (!rz_bv_xor_inplace(out->bv, y.bv)) { - rz_bv_fini(y.bv); - goto map_to_top; - } - rz_bv_fini(y.bv); - break; - } - case RZ_IL_OP_IS_ZERO: - case RZ_IL_OP_LSB: - case RZ_IL_OP_MSB: { - bool (*truth_test)(const RzBitVector *bv); - RzILOpBitVector *bv; - switch (pure->code) { - default: - rz_warn_if_reached(); - goto map_to_top; - case RZ_IL_OP_IS_ZERO: - bv = pure->op.is_zero.bv; - truth_test = rz_bv_is_zero_vector; - break; - case RZ_IL_OP_LSB: - bv = pure->op.lsb.bv; - truth_test = rz_bv_lsb; - break; - case RZ_IL_OP_MSB: - bv = pure->op.msb.bv; - truth_test = rz_bv_msb; - break; - } - if (!eval_pure(ctx, bv, out_val)) { - RZ_LOG_ERROR("prototype: MSB/LSB/IS_ZERO bv failed to evaluate.\n"); - return false; - } - if (!out->is_const) { - goto map_to_top; - } - bool truth = truth_test(out->bv); - rz_bv_cast_inplace(out->bv, 1, false); - // TODO: Truth bit. - rz_bv_set(out->bv, 0, truth); - break; - } - case RZ_IL_OP_NEG: { - if (!eval_pure(ctx, pure->op.neg.bv, out_val)) { - RZ_LOG_ERROR("prototype: NEG bv failed to evaluate.\n"); - return false; - } - if (out->is_const) { - rz_bv_neg_inplace(out->bv); - } - break; - } - case RZ_IL_OP_ADD: { - RzILOpPure *px = pure->op.add.x; - RzILOpPure *py = pure->op.add.y; - if (!eval_pure(ctx, px, out_val)) { - RZ_LOG_ERROR("prototype: ADD x failed to evaluate.\n"); - return false; - } - if (!out->is_const) { - goto map_to_top; - } - STACK_ABSTR_DATA_OUT(y); - if (!eval_pure(ctx, py, pack(&y))) { - RZ_LOG_ERROR("prototype: ADD y failed to evaluate.\n"); - return false; - } - if (!y.is_const) { - rz_bv_fini(y.bv); - goto map_to_top; - } - if (!rz_bv_add_inplace(out->bv, y.bv, NULL)) { - rz_bv_fini(y.bv); - goto map_to_top; - } - rz_bv_fini(y.bv); - break; - } - case RZ_IL_OP_SUB: { - RzILOpPure *px = pure->op.sub.x; - RzILOpPure *py = pure->op.sub.y; - if (!eval_pure(ctx, px, out_val)) { - RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); - return false; - } - if (!out->is_const) { - goto map_to_top; - } - STACK_ABSTR_DATA_OUT(y); - if (!eval_pure(ctx, py, pack(&y))) { - RZ_LOG_ERROR("prototype: SUB y failed to evaluate.\n"); - return false; - } - if (!y.is_const) { - rz_bv_fini(y.bv); - goto map_to_top; - } - if (!rz_bv_sub_inplace(out->bv, y.bv, NULL)) { - rz_bv_fini(y.bv); - goto map_to_top; - } - rz_bv_fini(y.bv); - break; - } - case RZ_IL_OP_SHIFTL: - case RZ_IL_OP_SHIFTR: { - RzILOpPure *px = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.x : pure->op.shiftl.x; - RzILOpPure *py = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.y : pure->op.shiftl.y; - RzILOpPure *pfill_bit = pure->code == RZ_IL_OP_SHIFTR ? pure->op.shiftr.fill_bit : pure->op.shiftl.fill_bit; - if (!eval_pure(ctx, px, out_val)) { - RZ_LOG_ERROR("prototype: SHIFT(L/R) x failed to evaluate.\n"); - return false; - } - if (!out->is_const) { - goto map_to_top; - } - STACK_ABSTR_DATA_OUT(y); - if (!eval_pure(ctx, py, pack(&y))) { - RZ_LOG_ERROR("prototype: SHIFT(L/R) y failed to evaluate.\n"); - return false; - } - if (!y.is_const) { - rz_bv_fini(y.bv); - goto map_to_top; - } - STACK_ABSTR_DATA_OUT(fill_bit); - if (!eval_pure(ctx, pfill_bit, pack(&fill_bit))) { - RZ_LOG_ERROR("prototype: SHIFT(L/R) fill_bit failed to evaluate.\n"); - return false; - } - if (!fill_bit.is_const) { - rz_bv_fini(fill_bit.bv); - rz_bv_fini(y.bv); - goto map_to_top; - } - bool (*shift)(RzBitVector *bv, ut32 size, bool fill_bit); - shift = pure->code == RZ_IL_OP_SHIFTR ? rz_bv_rshift_fill : rz_bv_lshift_fill; - if (!shift(out->bv, rz_bv_to_ut64(y.bv), !rz_bv_is_zero_vector(fill_bit.bv))) { - rz_bv_fini(fill_bit.bv); - rz_bv_fini(y.bv); - goto map_to_top; - } - rz_bv_fini(fill_bit.bv); - rz_bv_fini(y.bv); - break; - } - case RZ_IL_OP_SLE: - case RZ_IL_OP_ULE: - case RZ_IL_OP_EQ: { - bool (*cmp)(RzBitVector *x, RzBitVector *y); - RzILOpPure *px; - RzILOpPure *py; - switch (pure->code) { - default: - goto map_to_top; - case RZ_IL_OP_SLE: - px = pure->op.sle.x; - py = pure->op.sle.y; - cmp = rz_bv_sle; - break; - case RZ_IL_OP_ULE: - px = pure->op.ule.x; - py = pure->op.ule.y; - cmp = rz_bv_ule; - break; - case RZ_IL_OP_EQ: - px = pure->op.eq.x; - py = pure->op.eq.y; - cmp = rz_bv_eq; - break; - } - - if (!eval_pure(ctx, px, out_val)) { - RZ_LOG_ERROR("prototype: CMP x failed to evaluate.\n"); - return false; - } - if (!out->is_const) { - goto map_to_top; - } - STACK_ABSTR_DATA_OUT(y); - if (!eval_pure(ctx, py, pack(&y))) { - RZ_LOG_ERROR("prototype: CMP y failed to evaluate.\n"); - return false; - } - if (!y.is_const) { - rz_bv_fini(y.bv); - goto map_to_top; - } - bool cmp_is_true = cmp(out->bv, y.bv); - rz_bv_cast_inplace(out->bv, 1, false); - rz_bv_set(out->bv, 0, cmp_is_true); - rz_bv_fini(y.bv); - break; - } - case RZ_IL_OP_LOADW: - case RZ_IL_OP_LOAD: { - STACK_ABSTR_DATA_OUT(ld_addr); - RzILOpPure *key = pure->code == RZ_IL_OP_LOAD ? pure->op.load.key : pure->op.loadw.key; - RzILMemIndex mem_idx = pure->code == RZ_IL_OP_LOAD ? 0 : pure->op.loadw.mem; - if (!eval_pure(ctx, key, pack(&ld_addr))) { - RZ_LOG_ERROR("prototype: LOAD/LOADW key failed to evaluate.\n"); - rz_bv_fini(ld_addr.bv); - return false; - } - if (!ld_addr.is_const) { - rz_bv_fini(ld_addr.bv); - goto map_to_top; - } - if (rz_bv_len(ld_addr.bv) == 64) { - // TODO: Remove normalization. - // Unset bit 63 is required, because the RzBuffer API only supports - // st64 addresses. - RzBitVector mask = { 0 }; - rz_bv_init(&mask, 64); - rz_bv_set_from_ut64(&mask, 0x7fffffffffffffff); - rz_bv_and_inplace(ld_addr.bv, &mask); - } - - report_yield_xref(ctx, 0, ctx->astate->pc, pack(&ld_addr), RZ_ANALYSIS_XREF_TYPE_MEM_READ); - size_t n_bits = pure->code == RZ_IL_OP_LOAD ? ctx->inst->il_ctx->config->mem_key_size : pure->op.loadw.n_bits; - if (!load_abstr_data(ctx->inst, mem_idx, ld_addr.bv, n_bits, out_val)) { - rz_bv_fini(ld_addr.bv); - goto map_to_top; - } - rz_bv_fini(ld_addr.bv); - break; - } - case RZ_IL_OP_MUL: { - RzILOpPure *px = pure->op.mul.x; - RzILOpPure *py = pure->op.mul.y; - if (!eval_pure(ctx, px, out_val)) { - RZ_LOG_ERROR("prototype: MUL x failed to evaluate.\n"); - return false; - } - if (!out->is_const) { - goto map_to_top; - } - STACK_ABSTR_DATA_OUT(y); - if (!eval_pure(ctx, py, pack(&y))) { - RZ_LOG_ERROR("prototype: MUL y failed to evaluate.\n"); - return false; - } - if (!y.is_const) { - rz_bv_fini(y.bv); - goto map_to_top; - } - if (!rz_bv_mul_inplace(out->bv, y.bv)) { - rz_bv_fini(y.bv); - goto map_to_top; - } - rz_bv_fini(y.bv); - break; - } - case RZ_IL_OP_MOD: { - RzILOpPure *px = pure->op.mod.x; - RzILOpPure *py = pure->op.mod.y; - if (!eval_pure(ctx, px, out_val)) { - RZ_LOG_ERROR("prototype: MOD x failed to evaluate.\n"); - return false; - } - if (!out->is_const) { - goto map_to_top; - } - STACK_ABSTR_DATA_OUT(y); - if (!eval_pure(ctx, py, pack(&y))) { - RZ_LOG_ERROR("prototype: MOD y failed to evaluate.\n"); - return false; - } - if (!y.is_const) { - rz_bv_fini(y.bv); - goto map_to_top; - } - if (!rz_bv_mod_inplace(out->bv, y.bv)) { - rz_bv_fini(y.bv); - goto map_to_top; - } - rz_bv_fini(y.bv); - break; - } - case RZ_IL_OP_DIV: { - RzILOpPure *px = pure->op.div.x; - RzILOpPure *py = pure->op.div.y; - if (!eval_pure(ctx, px, out_val)) { - RZ_LOG_ERROR("prototype: DIV x failed to evaluate.\n"); - return false; - } - if (!out->is_const) { - goto map_to_top; - } - STACK_ABSTR_DATA_OUT(y); - if (!eval_pure(ctx, py, pack(&y))) { - RZ_LOG_ERROR("prototype: DIV y failed to evaluate.\n"); - return false; - } - if (!y.is_const) { - rz_bv_fini(y.bv); - goto map_to_top; - } - if (!rz_bv_div_inplace(out->bv, y.bv)) { - rz_bv_fini(y.bv); - goto map_to_top; - } - rz_bv_fini(y.bv); - break; - } - case RZ_IL_OP_SDIV: - case RZ_IL_OP_SMOD: - case RZ_IL_OP_FLOAT: - case RZ_IL_OP_FBITS: - case RZ_IL_OP_IS_FINITE: - case RZ_IL_OP_IS_NAN: - case RZ_IL_OP_IS_INF: - case RZ_IL_OP_IS_FZERO: - case RZ_IL_OP_IS_FNEG: - case RZ_IL_OP_IS_FPOS: - case RZ_IL_OP_FNEG: - case RZ_IL_OP_FABS: - case RZ_IL_OP_FCAST_INT: - case RZ_IL_OP_FCAST_SINT: - case RZ_IL_OP_FCAST_FLOAT: - case RZ_IL_OP_FCAST_SFLOAT: - case RZ_IL_OP_FCONVERT: - case RZ_IL_OP_FREQUAL: - case RZ_IL_OP_FSUCC: - case RZ_IL_OP_FPRED: - case RZ_IL_OP_FORDER: - case RZ_IL_OP_FROUND: - case RZ_IL_OP_FSQRT: - case RZ_IL_OP_FRSQRT: - case RZ_IL_OP_FADD: - case RZ_IL_OP_FSUB: - case RZ_IL_OP_FMUL: - case RZ_IL_OP_FDIV: - case RZ_IL_OP_FMOD: - case RZ_IL_OP_FHYPOT: - case RZ_IL_OP_FPOW: - case RZ_IL_OP_FMAD: - case RZ_IL_OP_FROOTN: - case RZ_IL_OP_FPOWN: - case RZ_IL_OP_FCOMPOUND: - case RZ_IL_OP_FEXCEPT: - RZ_LOG_ERROR("Unhandled pure %" PFMT32d "\n", pure->code); - // Not implemented. - goto map_to_top; - } - return true; +static void val_set_const_bool(RZ_OUT RZ_NONNULL RzInterpAbstrVal *dst, bool src) { + AD(dst)->is_const = true; + rz_bv_cast_inplace(AD(dst)->bv, 1, false); + rz_bv_set_from_ut64(AD(dst)->bv, src ? 1 : 0); +} -map_to_top: - out->is_const = false; - return true; +static void val_set_const_bv(RZ_OUT RZ_NONNULL RzInterpAbstrVal *dst, RZ_IN RZ_NONNULL RzBitVector *src) { + AD(dst)->is_const = true; + rz_bv_cast_inplace(AD(dst)->bv, rz_bv_len(src), false); + rz_bv_copy(AD(dst)->bv, src); } void val_copy(RzInterpAbstrVal *dst_val, const RzInterpAbstrVal *src_val) { @@ -693,18 +130,127 @@ static bool to_concrete_const(RZ_NONNULL const RzInterpAbstrVal *val, RZ_NULLABL return true; } -static RzInterpPlugin rz_interpreter_plugin_prototype = { +static void eval_cast(ut32 length, RZ_NONNULL const RzInterpAbstrVal *fill, RZ_INOUT RZ_NONNULL RzInterpAbstrVal *val) { + if (!AD(val)->is_const || !AD(fill)->is_const) { + val_set_top(val); + return; + } + rz_bv_cast_inplace(AD(val)->bv, length, !rz_bv_is_zero_vector(AD(fill)->bv)); +} + +static void eval_shift(bool right, RZ_NONNULL RZ_INOUT RzInterpAbstrVal *x, RZ_NONNULL const RzInterpAbstrVal *y, RZ_NONNULL const RzInterpAbstrVal *fill_bit) { + if (!AD(x)->is_const || !AD(y)->is_const || !AD(fill_bit)->is_const) { + // Hint: there are some more cases that could be handled better: + // - x is either all 1 or all 0 => any shift will produce the same result + // - y is 0 => fill_bit may be top and the result is still known to be just x + // but not sure if these are worth the extra control flow for practical purposes + return; + } + bool (*shift)(RzBitVector *bv, ut32 size, bool fill_bit); + shift = right ? rz_bv_rshift_fill : rz_bv_lshift_fill; + shift(AD(x)->bv, rz_bv_to_ut64(AD(y)->bv), !rz_bv_is_zero_vector(AD(fill_bit)->bv)); +} + +static void eval_binop(RzILOpPureCode code, RZ_NONNULL RZ_INOUT RzInterpAbstrVal *x, RZ_NONNULL const RzInterpAbstrVal *y) { + if (!AD(x)->is_const || !AD(y)->is_const) { + val_set_top(x); + return; + } + RzBitVector *xv = AD(x)->bv; + RzBitVector *yv = AD(y)->bv; + switch (code) { + case RZ_IL_OP_APPEND: + rz_bv_append_inplace(xv, yv); + break; + case RZ_IL_OP_LOGAND: + case RZ_IL_OP_AND: + rz_bv_and_inplace(xv, yv); + break; + case RZ_IL_OP_LOGOR: + case RZ_IL_OP_OR: + rz_bv_or_inplace(xv, yv); + break; + case RZ_IL_OP_LOGXOR: + case RZ_IL_OP_XOR: + rz_bv_xor_inplace(xv, yv); + break; + case RZ_IL_OP_ADD: + rz_bv_add_inplace(xv, yv, NULL); + break; + case RZ_IL_OP_SUB: + rz_bv_sub_inplace(xv, yv, NULL); + break; + case RZ_IL_OP_SLE: + val_set_const_bool(x, rz_bv_sle(xv, yv)); + break; + case RZ_IL_OP_ULE: + val_set_const_bool(x, rz_bv_ule(xv, yv)); + break; + case RZ_IL_OP_EQ: + val_set_const_bool(x, rz_bv_eq(xv, yv)); + break; + case RZ_IL_OP_MUL: + rz_bv_mul_inplace(xv, yv); + break; + case RZ_IL_OP_MOD: + rz_bv_mod_inplace(xv, yv); + break; + case RZ_IL_OP_DIV: + rz_bv_div_inplace(xv, yv); + break; + default: + // unimplemented + val_set_top(x); + break; + } +} + +static void eval_unop(RzILOpPureCode code, RZ_NONNULL RZ_INOUT RzInterpAbstrVal *val) { + if (!AD(val)->is_const) { + return; + } + RzBitVector *bv = AD(val)->bv; + switch (code) { + case RZ_IL_OP_LOGNOT: + case RZ_IL_OP_INV: + rz_bv_not_inplace(bv); + break; + case RZ_IL_OP_IS_ZERO: + val_set_const_bool(val, rz_bv_is_zero_vector(bv)); + break; + case RZ_IL_OP_LSB: + val_set_const_bool(val, rz_bv_lsb(bv)); + break; + case RZ_IL_OP_MSB: + val_set_const_bool(val, rz_bv_msb(bv)); + break; + case RZ_IL_OP_NEG: + rz_bv_neg_inplace(bv); + break; + default: + // unimplemented + val_set_top(val); + break; + } +} + +static RzInterpValueAbstraction rz_interpreter_plugin_prototype = { .name = "constant", .val_new_top = val_new_top, .val_free = val_free, .set_top = val_set_top, + .set_const_bool = val_set_const_bool, + .set_const_bv = val_set_const_bv, .is_top = val_is_top, .may_be_bool = val_may_be_bool, .to_concrete_const = to_concrete_const, .copy = val_copy, - .eval_pure = eval_pure, .join = join_val, .val_as_str = val_as_str, + .eval_cast = eval_cast, + .eval_shift = eval_shift, + .eval_binop = eval_binop, + .eval_unop = eval_unop }; RZ_API RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype = { @@ -713,7 +259,7 @@ RZ_API RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype = { .version = "0.1p", .desc = "A prototype interpreter for constant/top abstractions.", .license = "LGPL-3.0-only", - .p_interpreter = &rz_interpreter_plugin_prototype, + .value_abstraction = &rz_interpreter_plugin_prototype, }; #ifndef RZ_PLUGIN_INCORE From 54cdab09b96b6b524b73b6113bdc82d7e13c6490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Fri, 3 Jul 2026 16:23:28 +0200 Subject: [PATCH 308/334] Minor interp loop refactor --- librz/inquiry/interp/interpreter.c | 59 ++++++++++++------------------ 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 147151b2dac..abc2ad78849 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -1321,49 +1321,36 @@ RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *inst) { RZ_LOG_DEBUG("interpreter: Main: Hello.\n"); - // - // Start interpretation - // - - // TODO: It is probably better to make the following stuff while-loops. - // Because otherwise it doesn't make sense without the docs. - // But while debugging and developing, I keep it this way to separate clearly - // what the interpreter does in each state. + while (true) { + // INIT + RZ_LOG_DEBUG("interpreter: Enter INIT\n"); + rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_INIT); + + ut64 entry_point; + if (rz_th_ring_buf_take_blocking(inst->entry_points, &entry_point) != RZ_THREAD_RING_BUF_OK) { + // No more entry points to interpret => Terminate. + // OR. + success = true; + break; + } -INIT: - // prepare state at the procedure entrypoint - RZ_LOG_DEBUG("interpreter: Enter INIT\n"); - rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_INIT); + // EMU + RZ_LOG_DEBUG("interpreter: Enter EMU\n"); + rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_EMU); - ut64 entry_point; - if (rz_th_ring_buf_take_blocking(inst->entry_points, &entry_point) != RZ_THREAD_RING_BUF_OK) { - // No more entry points to interpret => Terminate. - // OR. - success = true; - goto TERM; - } + if (!rz_interp_run(inst, entry_point)) { + RZ_LOG_ERROR("Interpreter run failed for entry point 0x%" PFMT64x "\n", entry_point); + } -// EMU - RZ_LOG_DEBUG("interpreter: Enter EMU\n"); - rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_EMU); + // CLEAN + RZ_LOG_DEBUG("interpreter: Enter CLEAN\n"); + rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_CLEAN); - if (!rz_interp_run(inst, entry_point)) { - RZ_LOG_ERROR("Interpreter run failed for entry point 0x%" PFMT64x "\n", entry_point); + // Wait until RzInquiry asks to start again. + rz_th_sem_wait(inst->run_state_sync); } -// CLEAN - RZ_LOG_DEBUG("interpreter: Enter CLEAN\n"); - rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_CLEAN); - - // Wait until RzInquiry asks to start again. - rz_th_sem_wait(inst->run_state_sync); - - // Clean can only transition to Init. - goto INIT; - -TERM: { RZ_LOG_DEBUG("interpreter: Enter TERM\n"); rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_TERM); return success; } -} From a05730643b6c5917b75e8bc6364126b9964d20f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Mon, 6 Jul 2026 19:02:53 +0200 Subject: [PATCH 309/334] Adjust to RzIL refinements --- librz/inquiry/interp/interpreter.c | 93 ++---------------------------- 1 file changed, 5 insertions(+), 88 deletions(-) diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index abc2ad78849..007767d331c 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -724,74 +724,15 @@ static bool eval_pure(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT Rz case RZ_IL_OP_DIV: { RzILOpPure *px; RzILOpPure *py; - switch (pure->code) { - case RZ_IL_OP_APPEND: + if (pure->code == RZ_IL_OP_APPEND) { // we use low as the x/out value because in the case of constant operands, // appending high bits to a bitvector is more efficient than prepending // low bits in place. px = pure->op.append.low; py = pure->op.append.high; - break; - case RZ_IL_OP_LOGAND: - px = pure->op.logand.x; - py = pure->op.logand.y; - break; - case RZ_IL_OP_AND: - px = pure->op.booland.x; - py = pure->op.booland.y; - break; - case RZ_IL_OP_LOGOR: - px = pure->op.logor.x; - py = pure->op.logor.y; - break; - case RZ_IL_OP_OR: - px = pure->op.boolor.x; - py = pure->op.boolor.y; - break; - case RZ_IL_OP_LOGXOR: - px = pure->op.logxor.x; - py = pure->op.logxor.y; - break; - case RZ_IL_OP_XOR: - px = pure->op.boolxor.x; - py = pure->op.boolxor.y; - break; - case RZ_IL_OP_ADD: - px = pure->op.add.x; - py = pure->op.add.y; - break; - case RZ_IL_OP_SUB: - px = pure->op.sub.x; - py = pure->op.sub.y; - break; - case RZ_IL_OP_SLE: - px = pure->op.sle.x; - py = pure->op.sle.y; - break; - case RZ_IL_OP_ULE: - px = pure->op.ule.x; - py = pure->op.ule.y; - break; - case RZ_IL_OP_EQ: - px = pure->op.eq.x; - py = pure->op.eq.y; - break; - case RZ_IL_OP_MUL: - px = pure->op.mul.x; - py = pure->op.mul.y; - break; - case RZ_IL_OP_MOD: - px = pure->op.mod.x; - py = pure->op.mod.y; - break; - case RZ_IL_OP_DIV: - px = pure->op.div.x; - py = pure->op.div.y; - break; - default: - // this switch should be in sync with outer cases - rz_warn_if_reached(); - return false; + } else { + px = pure->op.binop.x; + py = pure->op.binop.y; } if (!eval_pure(ctx, px, out)) { RZ_LOG_ERROR("prototype: binop x failed to evaluate.\n"); @@ -817,31 +758,7 @@ static bool eval_pure(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT Rz case RZ_IL_OP_LSB: case RZ_IL_OP_MSB: case RZ_IL_OP_NEG: { - RzILOpPure *x; - switch (pure->code) { - case RZ_IL_OP_LOGNOT: - x = pure->op.lognot.bv; - break; - case RZ_IL_OP_INV: - x = pure->op.boolinv.x; - break; - case RZ_IL_OP_IS_ZERO: - x = pure->op.is_zero.bv; - break; - case RZ_IL_OP_LSB: - x = pure->op.lsb.bv; - break; - case RZ_IL_OP_MSB: - x = pure->op.msb.bv; - break; - case RZ_IL_OP_NEG: - x = pure->op.neg.bv; - break; - default: - // this switch should be in sync with outer cases - rz_warn_if_reached(); - return false; - } + RzILOpPure *x = pure->op.unop.x; if (!eval_pure(ctx, x, out)) { RZ_LOG_ERROR("prototype: unop x failed to evaluate.\n"); return false; From acfa2d878999f48b0417c48a96aaee92505a292d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Wed, 8 Jul 2026 13:16:43 +0200 Subject: [PATCH 310/334] Move non-state out of abstract state and other minor changes --- librz/include/rz_analysis.h | 2 +- librz/include/rz_inquiry/rz_interpreter.h | 12 +- librz/inquiry/README.md | 95 +++++++++++ librz/inquiry/il_cache.c | 4 - librz/inquiry/interp/interpreter.c | 187 +++++++++++----------- 5 files changed, 198 insertions(+), 102 deletions(-) diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index be04f85d0ad..d645cef2545 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -988,7 +988,7 @@ typedef struct rz_analysis_ref_t { * a call instruction. */ typedef struct { - ut64 bb_addr; ///< The address of the basic block which stores the NPC. + ut64 bb_addr; ///< The address of the basic block which stores the NPC, WARNING: may not actually be filled yet! ut64 store_addr; ///< The address of the instruction packet storing the NPC. Might be 0, if it was not added by the interpreter. ut64 candidate_addr; ///< Address of the call candidate instruction packet. Might be 0, if it was not added by the interpreter. ut64 npc; ///< The NPC stored. Should point after a basic block. diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 39ba421edbd..bd993a53a41 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -103,17 +103,13 @@ typedef enum { } RzInterpPCState; typedef struct { - ut64 pc; ///< Interpreter location in the code. This is not necessarily identical to the ISA's program counter register, but simply points to the instruction to execute. + ut64 pc; ///< Interpreter location in the code. This is not necessarily identical to the ISA's program counter register, but simply points to the instruction to execute next. RzInterpPCState pc_state; bool uninterpreted; ///< True if this state has not yet been started to interpret, i.e. is part of RzInterpFunctionState.queue - HtUP *var_name_hashes; ///< Map of DJB2 hashes to variable names. HtUP /**/ *globals; ///< Global variables (mostly registers). Indexed by DJB2 hash of global name. HtUP /**/ *locals; ///< Local variables. Indexed by DJB2 hash of the local name. HtUP /**/ *lets; ///< Let variables. Indexed by DJB2 hash of the let name. - const char *arch_name; ///< Name of architecture. Used by work-arounds until we have RzArch. - ut64 bb_addr; - ut64 bb_size; } RzInterpAbstrState; typedef enum { @@ -255,6 +251,8 @@ struct rz_interp_instance_t { RzThreadRingBuf *entry_points; RzAnalysisILContext *il_ctx; ///< Context about available global vars and memory + HtUP *var_name_hashes; ///< Map of DJB2 hashes to variable names. + RZ_DEPRECATE const char *arch_name; ///< Name of architecture. Used only by work-arounds until we have RzArch. RZ_LIFETIME(RzILCache) RZ_BORROW RzILCacheClient *il_cache_client; @@ -288,8 +286,7 @@ RZ_IPI void rz_interp_run_state_set(RZ_BORROW RZ_NONNULL RzInterpRunState *state RZ_API void rz_interp_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yield_rbuf); RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_new( - RZ_NONNULL RzInterpInstance *inst, - const char *arch_name); + RZ_NONNULL RzInterpInstance *inst); RZ_API void rz_interp_abstr_state_free(RzInterpInstance *inst, RZ_OWN RZ_NULLABLE RzInterpAbstrState *state); RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_clone(RZ_NONNULL RzInterpInstance *iset, const RzInterpAbstrState *state); RZ_API bool rz_interp_abstr_state_as_str(RZ_NONNULL RzInterpInstance *inst, RZ_NONNULL const RzInterpAbstrState *state, RZ_NONNULL RZ_OUT RzStrBuf *sb); @@ -317,6 +314,7 @@ struct rz_interp_run_context_t { HtUP /**/ *pc_states; ///< Currently discovered states at the entries of blocks. // Tracking data local to a single block interpretation + ut64 il_block_end; ///< The address directly after the last instruction of the currently interpreted IL block RzInterpAbstrState *astate; ///< The abstract state of the interpreter. RzAnalysisCallCandidate call_cand; ///< Data of a call candidate. }; diff --git a/librz/inquiry/README.md b/librz/inquiry/README.md index 2f517673e4f..e8486b76b81 100644 --- a/librz/inquiry/README.md +++ b/librz/inquiry/README.md @@ -28,3 +28,98 @@ Ideas for optimization (do not implement any of these without profiling first): So the plugin must decide whether to short-circuit or not. It has to be profiled if the cost of asking the plugin all the time is really compensated by avoided evalutations in practical code. + +## CFG recovery irregularities TODO: + +Code: +B> fallthrough + fallthrough +A> ... + +Case 1: + A was already discovered. + B is discovered later. + => It must extend only until the start of A. + +Case 2: + B was already discovered. + A is discovered later. + => B must be split. + +Special cases: +Jump into the middle of an instruction. Perhaps when an instruction meets again +with one from another block, this should be merged. + +### Problem with blocks that do not end in a jump, or ones that have more prepended instructions to be discovered later: +Call detection is based on a store of the block end addr before the jump. +Consider the following ARMv4 code for an indirect call (blx was introduced in ARMv5): + +A> mov lr, pc +B> mov pc, r0 +C> ... + +both A and B are block entries. + +1. If A is discovered before B, the call is recognized and C is also added as a fallthrough entry. +2. If B is discovered before A, the call is not recognized because A and B are two separate blocks already. + +The ideal outcome is probably that this is **not** recognized as a call since +it is unlikely to be meant to be one in this pattern, but this is not practical +as explained in detail in the section below. +Since for this special case, we will not decide whether the jump is a call, we +have to simply interpret it as both a call and a direct jump. + +Idea: have some flags per block, something like: +- Fallthrough +- Stores bb-end, WARNING: bb-end must be detected from the IL cache block that definitely ends with an explicit jmp + +#### Detailed reasoning why avoiding call detection is not possible + +Consider patterns like this, where there is an in-edge to A and to B. + +A> mov lr, pc +B> jmp 1324 +C> ... + +For all such patterns across an entire interpretation, we now want to **not** +consider the jmp as a call, but only because of the in-edge B. However: + +* To make the decision whether a block from entry A is calling, it is necessary + to know that there is no entry B into the block splitting pc storage and + jump. +* This is only possible to know once a fixpoint has been reached already. But + reaching the final fixpoint also depends on whether A is calling, so there is + a circular dependency. + +One might try to approach this by postponing adding any successor states of A +if it is detected that it might be calling, then loop until a temporary +fixpoint is reached, then continue from A, again postponing deciding on +successors of potentially calling blocks and do this until a final fixpoint has +been reached. +But if wethen detect an edge B, we have violated our condition that we want +this pattern to always not be recognized as calling. But if from this we might +rewind and conclude that A is not calling, that edge B would disappear, so A +should be calling again and it is a circular dependency again. + +We could weaken our initial condition to allow for A to be calling while B is +there if B is detected only because of A later on, but then consider this +example: + +A> mov lr, pc +B> jmp 1234 +C> jmp B' +... +A'> mov lr, pc +B'> jmp 4321 +C'> jmp B + +There is no unique solution for minimizing calling blocks with edges into them +anymore. Either A is considered calling and A' is not, or the other way around. + +In conclusion, it is impossible to guarantee that blocks that have a +call-splitting in-edge are never treated as calling, and it is also impossible +to minimize the number of such occurrences deterministically (avoiding +arbitrary heuristics). +And even if we tried to minimize these cases and use some heuristics to decide +between multiple solutions, the added complexity to the algorithm would likely +be justified by how little practical usefulness this has. diff --git a/librz/inquiry/il_cache.c b/librz/inquiry/il_cache.c index eb6048c4ffa..885a5b03224 100644 --- a/librz/inquiry/il_cache.c +++ b/librz/inquiry/il_cache.c @@ -139,7 +139,6 @@ RZ_API RZ_OWN RzILCacheBlock *rz_il_cache_lift_il_block(const RzILCache *cache, goto fail; } il_block->addr = addr; - RZ_LOG_DEBUG("ILCache: Gen block:\n"); bool sparc_add_delayed_insn = false; bool changes_cf = true; do { @@ -196,7 +195,6 @@ RZ_API RZ_OWN RzILCacheBlock *rz_il_cache_lift_il_block(const RzILCache *cache, } } - RZ_LOG_DEBUG("ILCache: \t0x%" PFMT64x "\n", addr); addr += op.size; rz_analysis_op_fini(&op); rz_mem_memzero(buf, max_read_size); @@ -226,11 +224,9 @@ static const RzILCacheBlock *lift_il_block(RzILCache *cache, ut64 addr) { RzILCacheBlock *block = ht_up_find(cache->cache, addr, NULL); if (block) { char *bstr = rz_il_cache_block_str(block); - RZ_LOG_DEBUG("ILCache: Serve block %s from cache\n", bstr); free(bstr); return block; } - RZ_LOG_DEBUG("ILCache: Lift new block\n"); block = rz_il_cache_lift_il_block(cache, addr); if (!block) { RZ_LOG_DEBUG("ILCache: Failed to lift block at 0x%" PFMT64x "\n", addr); diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 007767d331c..ee9fc0bc4b1 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -74,17 +74,14 @@ RZ_API RZ_OWN RzInterpYieldRBuf *rz_interp_yield_rbuf_new(RzInterpYieldKind kind * The register name list should always be given if the architecture has some. */ RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_new( - RZ_NONNULL RzInterpInstance *inst, - const char *arch_name) { + RZ_NONNULL RzInterpInstance *inst) { rz_return_val_if_fail(inst, NULL); RzInterpAbstrState *state = RZ_NEW0(RzInterpAbstrState); if (!state) { return NULL; } state->pc_state = RZ_INTERP_PC_UNREACHABLE; - state->arch_name = arch_name; // Initialize the register file with uninitialized abstract values. - state->var_name_hashes = ht_up_new(NULL, free); state->globals = ht_up_new(NULL, NULL); for (size_t i = 0; i < inst->il_ctx->reg_binding->regs_count; i++) { const char *rname = inst->il_ctx->reg_binding->regs[i].name; @@ -92,19 +89,13 @@ RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_new( if (!aval) { rz_warn_if_reached(); ht_up_free(state->globals); - ht_up_free(state->var_name_hashes); free(state); return NULL; } - ut64 djb2_reg_hash = rz_str_djb2_hash(rname); - if (!ht_up_insert(state->globals, djb2_reg_hash, aval) || - !ht_up_insert(state->var_name_hashes, djb2_reg_hash, rz_str_dup(rname))) { - RZ_LOG_ERROR("Failed to add %s to the global variable map. " - "DJB2 hash collision of the register name. DJB2 hash = 0x%" PFMT64x "\n", - rname, djb2_reg_hash); + if (!ht_up_insert(state->globals, djb2_reg_hash, aval)) { + RZ_LOG_ERROR("Failed to add %s to the global variable map.", rname); ht_up_free(state->globals); - ht_up_free(state->var_name_hashes); free(state); } } @@ -130,9 +121,6 @@ RZ_API void rz_interp_abstr_state_free(RzInterpInstance *inst, RZ_OWN RZ_NULLABL if (!state) { return; } - if (state->var_name_hashes) { - ht_up_free(state->var_name_hashes); - } var_set_free(inst, state->globals); var_set_free(inst, state->locals); var_set_free(inst, state->lets); @@ -153,8 +141,6 @@ static bool reset_state(RzInterpInstance *inst, RZ_BORROW RzInterpAbstrState *st } } rz_iterator_free(it); - state->bb_addr = 0; - state->bb_size = 0; return true; } @@ -176,7 +162,7 @@ RZ_API bool rz_interp_abstr_state_as_str(RZ_NONNULL RzInterpInstance *inst, RZ_N RzIterator *it = ht_up_as_iter_keys(state->globals); ut64 *k; rz_iterator_foreach(it, k) { - const char *gname = ht_up_find(state->var_name_hashes, *k, NULL); + const char *gname = ht_up_find(inst->var_name_hashes, *k, NULL); rz_strbuf_appendf(sb, "\t%s = ", gname); RzInterpAbstrVal *av = ht_up_find(state->globals, *k, NULL); inst->plugin->val_as_str(av, sb); @@ -200,12 +186,36 @@ RZ_API void rz_interp_abstr_state_as_str_short(RZ_NONNULL RzInterpInstance *inst rz_strbuf_append(sb, ", "); } first = false; - const char *varname = ht_up_find(astate->var_name_hashes, djb2_reg_name, NULL); + const char *varname = ht_up_find(inst->var_name_hashes, djb2_reg_name, NULL); rz_strbuf_appendf(sb, "%s = ", varname); inst->plugin->val_as_str(av, sb); } } +RZ_API void rz_interp_instance_free(RZ_OWN RZ_NULLABLE RzInterpInstance *iset) { + if (!iset) { + return; + } + ht_up_free(iset->var_name_hashes); + if (iset->io_request_rbuf) { + rz_th_ring_buf_free(iset->io_request_rbuf); + } + if (iset->io_result_rbuf) { + rz_th_ring_buf_free(iset->io_result_rbuf); + } + if (iset->entry_points) { + rz_th_ring_buf_free(iset->entry_points); + } + if (iset->run_state_sync) { + rz_th_sem_free(iset->run_state_sync); + } + if (iset->run_state) { + rz_interp_run_state_free(iset->run_state); + } + rz_analysis_il_context_free(iset->il_ctx); + free(iset); +} + static HtUP *var_set_clone(const RzInterpInstance *iset, HtUP *vars) { HtUP *r = ht_up_new(NULL, NULL); if (!r) { @@ -229,50 +239,15 @@ RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_clone(RZ_NONNULL RzInter if (!state) { return NULL; } - r->arch_name = state->arch_name; r->pc = state->pc; r->pc_state = state->pc_state; r->uninterpreted = state->uninterpreted; - r->var_name_hashes = ht_up_new(NULL, free); - RzIterator *it = ht_up_as_iter_keys(state->var_name_hashes); - ut64 *key; - rz_iterator_foreach(it, key) { - char *n = strdup(ht_up_find(state->var_name_hashes, *key, NULL)); - if (!n) { - continue; - } - ht_up_insert(r->var_name_hashes, *key, n); - } - r->globals = var_set_clone(iset, state->globals); r->locals = var_set_clone(iset, state->locals); r->lets = var_set_clone(iset, state->lets); return r; } -RZ_API void rz_interp_instance_free(RZ_OWN RZ_NULLABLE RzInterpInstance *iset) { - if (!iset) { - return; - } - if (iset->io_request_rbuf) { - rz_th_ring_buf_free(iset->io_request_rbuf); - } - if (iset->io_result_rbuf) { - rz_th_ring_buf_free(iset->io_result_rbuf); - } - if (iset->entry_points) { - rz_th_ring_buf_free(iset->entry_points); - } - if (iset->run_state_sync) { - rz_th_sem_free(iset->run_state_sync); - } - if (iset->run_state) { - rz_interp_run_state_free(iset->run_state); - } - rz_analysis_il_context_free(iset->il_ctx); - free(iset); -} - static bool setup_ipc_objects( RZ_OUT RzThreadRingBuf **io_request_rbuf, RZ_OUT RzThreadRingBuf **io_result_rbuf, @@ -341,42 +316,64 @@ RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( RZ_NONNULL const RzVector /**/ *ignored_code) { rz_return_val_if_fail(plugin && ignored_code && analysis && il_cache_client, NULL); - RzInterpInstance *iset = RZ_NEW0(RzInterpInstance); - if (!iset) { + RzInterpInstance *inst = RZ_NEW0(RzInterpInstance); + if (!inst) { return NULL; } + const RzAnalysisPlugin *cur = rz_analysis_plugin_current(analysis); + if (!cur || !cur->arch) { + goto err_inst; + } + inst->arch_name = cur->arch; + RzAnalysisILContext *il_ctx = rz_analysis_il_context_resolve(analysis); if (!il_ctx) { - free(iset); RZ_LOG_ERROR("Failed to create analysis IL context.\n"); - return NULL; + goto err_inst; } RzThreadRingBuf *io_request_rbuf = NULL; RzThreadRingBuf *io_result_rbuf = NULL; RzThreadRingBuf *entry_points = NULL; if (!setup_ipc_objects(&io_request_rbuf, &io_result_rbuf, &entry_points)) { - free(iset); - rz_analysis_il_context_free(il_ctx); - return NULL; + goto err_il_ctx; } - iset->a = analysis; - iset->plugin = plugin; - iset->run_state = rz_interp_run_state_new(); - iset->il_ctx = il_ctx; - iset->il_cache_client = il_cache_client; - iset->entry_points = entry_points; - iset->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] = yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; - iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] = yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; - iset->yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] = yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW]; - iset->io_request_rbuf = io_request_rbuf; - iset->io_result_rbuf = io_result_rbuf; - iset->run_state_sync = rz_th_sem_new(0); - iset->ignored_code = ignored_code; - - return iset; + inst->a = analysis; + inst->plugin = plugin; + inst->run_state = rz_interp_run_state_new(); + inst->il_ctx = il_ctx; + + inst->var_name_hashes = ht_up_new(NULL, free); + for (size_t i = 0; i < il_ctx->reg_binding->regs_count; i++) { + const char *rname = il_ctx->reg_binding->regs[i].name; + ut64 djb2_reg_hash = rz_str_djb2_hash(rname); + if (!ht_up_insert(inst->var_name_hashes, djb2_reg_hash, rz_str_dup(rname))) { + RZ_LOG_ERROR("DJB2 hash collision of the register name %s. DJB2 hash = 0x%" PFMT64x "\n", + rname, djb2_reg_hash); + goto err_var_name_hashes; + } + } + + inst->il_cache_client = il_cache_client; + inst->entry_points = entry_points; + inst->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] = yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; + inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] = yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; + inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] = yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW]; + inst->io_request_rbuf = io_request_rbuf; + inst->io_result_rbuf = io_result_rbuf; + inst->run_state_sync = rz_th_sem_new(0); + inst->ignored_code = ignored_code; + + return inst; +err_var_name_hashes: + ht_up_free(inst->var_name_hashes); +err_il_ctx: + rz_analysis_il_context_free(il_ctx); +err_inst: + free(inst); + return NULL; } RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as) { @@ -433,7 +430,7 @@ bool report_yield_xref( goto cleanup; } if (type == RZ_ANALYSIS_XREF_TYPE_CODE && - RZ_STR_EQ(ctx->astate->arch_name, "hexagon") && + RZ_STR_EQ(ctx->inst->arch_name, "hexagon") && from + insn_pkt_size == rz_bv_to_ut64(&to_bv)) { // Ugly work around. // Because we don't have RzArch yet the Hexagon plugin adds a JUMP at the @@ -450,7 +447,7 @@ bool report_yield_xref( ut64 to_addr = rz_bv_to_ut64(&to_bv); RzAnalysisXRef xref = { 0 }; - xref.bb_addr = ctx->astate->bb_addr; + xref.bb_addr = 0; // TODO? ctx->astate->bb_addr; xref.from = from; xref.to = to_addr; xref.type = type; @@ -475,6 +472,7 @@ static bool report_yield_call_candiate( rz_return_val_if_fail(cc_rbuf, false); RzAnalysisCallCandidate cc = { 0 }; + // TODO? put the bb addr into the call candidate? Currently we do not know it. memcpy(&cc, &ctx->call_cand, sizeof(ctx->call_cand)); if (rz_th_ring_buf_put(cc_rbuf->rbuf, &cc) != RZ_THREAD_RING_BUF_OK) { return false; @@ -615,11 +613,23 @@ static bool set_abstr_pc(RzInterpInstance *inst, RzInterpAbstrState *state, RzIn static bool value_indicates_ret_addr_write(RzInterpRunContext *ctx, RzInterpAbstrVal *val) { RzBitVector bv; rz_bv_init(&bv, 64); + // Hint: pc addrs coming from the lifters are currently just opaque bitvectors. + // So we do not know whether the constant contents of val are actually taken from the architecture's + // pc register or match the instruction address by chance only. + // We could add a flag to RzILOpArgsBV that would be set by lifters to indicate that the constant value + // originates from the pc and use that here. + // This may also help with the sparc workaround. + // The downside is that a pattern like this in non-relocatable code would not be detected as call: + // 0x42 mov lr, 0x4a + // 0x46 mov pc, r0 + // 0x4a ... + // but it is questionable whether that should be even detected at all, since there is no way to know + // if it is intended as call or jump. bool ret = ctx->inst->plugin->to_concrete_const(val, &bv) && - (rz_bv_to_ut64(&bv) == ctx->astate->bb_addr + ctx->astate->bb_size || + (rz_bv_to_ut64(&bv) == ctx->il_block_end || // Sparc stores the call instruction PC into o8. // The return instruction jumps then to o7+8. - (rz_str_startswith(ctx->astate->arch_name, "sparc") && rz_bv_to_ut64(&bv) == ctx->astate->pc)); + (rz_str_startswith(ctx->inst->arch_name, "sparc") && rz_bv_to_ut64(&bv) == ctx->astate->pc)); rz_bv_fini(&bv); return ret; } @@ -918,8 +928,7 @@ static bool eval_effect(RzInterpRunContext *ctx, if (value_indicates_ret_addr_write(ctx, eval_out) && kind == RZ_IL_VAR_KIND_GLOBAL) { ctx->call_cand.store_addr = pc; - ctx->call_cand.npc = ctx->astate->bb_addr + ctx->astate->bb_size; - ctx->call_cand.bb_addr = ctx->astate->bb_addr; + ctx->call_cand.npc = ctx->il_block_end; ctx->call_cand.in_mem = false; } break; @@ -1064,8 +1073,7 @@ static bool eval_effect(RzInterpRunContext *ctx, } if (value_indicates_ret_addr_write(ctx, eval_out)) { ctx->call_cand.store_addr = pc; - ctx->call_cand.npc = ctx->astate->bb_addr + ctx->astate->bb_size; - ctx->call_cand.bb_addr = ctx->astate->bb_addr; + ctx->call_cand.npc = ctx->il_block_end; ctx->call_cand.in_mem = true; } report_yield_xref(ctx, insn_pkt_size, pc, st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); @@ -1166,8 +1174,7 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { // Prepare the initial state from the given entry point // Hint: nothing speaks against supporting multiple entry points in a single run - const RzAnalysisPlugin *cur = rz_analysis_plugin_current(inst->a); - RzInterpAbstrState *estate = rz_interp_abstr_state_new(inst, cur->arch); + RzInterpAbstrState *estate = rz_interp_abstr_state_new(inst); memset(&ctx.call_cand, 0, sizeof(ctx.call_cand)); if (!reset_state(inst, estate, entry_point)) { rz_warn_if_reached(); @@ -1189,7 +1196,8 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { const RzILCacheBlock *il_bb = rz_il_cache_client_lift_il_block(inst->il_cache_client, ctx.astate->pc); if (!il_bb) { - // Lifting failed, TODO: handle this better + RZ_LOG_ERROR("interpreter: Lifting failed\n"); + // TODO: handle this better break; } @@ -1205,11 +1213,10 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { // rz_meta_set_string(iset->a, RZ_META_TYPE_COMMENT, il_bb->addr, rz_strbuf_get(&sb)); rz_strbuf_fini(&sb); - ctx.astate->bb_addr = il_bb->addr; - ctx.astate->bb_size = il_bb->size; + ctx.il_block_end = il_bb->addr + il_bb->size; // Evaluate the effect on the abstract state. if (!eval_block(&ctx, il_bb)) { - RZ_LOG_DEBUG("interpreter: Eval failed\n"); + RZ_LOG_ERROR("interpreter: Eval failed\n"); success = false; break; } From 7971c640c312017c33b66b725197540c0b4cfdbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Wed, 8 Jul 2026 16:45:19 +0200 Subject: [PATCH 311/334] Introduce RzInterpBlock --- librz/include/rz_inquiry/rz_interpreter.h | 21 ++++++- librz/inquiry/README.md | 6 ++ librz/inquiry/interp/interpreter.c | 76 +++++++++++++++-------- 3 files changed, 75 insertions(+), 28 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index bd993a53a41..98dd9e89d74 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -105,13 +105,28 @@ typedef enum { typedef struct { ut64 pc; ///< Interpreter location in the code. This is not necessarily identical to the ISA's program counter register, but simply points to the instruction to execute next. RzInterpPCState pc_state; - bool uninterpreted; ///< True if this state has not yet been started to interpret, i.e. is part of RzInterpFunctionState.queue HtUP /**/ *globals; ///< Global variables (mostly registers). Indexed by DJB2 hash of global name. HtUP /**/ *locals; ///< Local variables. Indexed by DJB2 hash of the local name. HtUP /**/ *lets; ///< Let variables. Indexed by DJB2 hash of the let name. } RzInterpAbstrState; +/** + * \brief Basic Block as part of the abstract interpretation loop + * Represents a block of instructions of which only the last may have a control effect. + * Unlike IL blocks, the last instruction may also not have a control effect, which is + * the case when the instruction directly following this block has an in-edge. + * And unlike regular basic blocks, a call instruction also terminates an interpreter block. + */ +typedef struct { + /** + * Least upper bound of all states discovered at the entry of the block. + * pc_state of this state must be RZ_INTERP_PC_CONST and pc points to the first instruction of the block. + */ + RzInterpAbstrState *entry_state; // TODO: flatten to remove indirection + bool uninterpreted; ///< True if the entry state has not yet been started to interpret, i.e. the block is part of RzInterpFunctionState.queue +} RzInterpBlock; + typedef enum { /** * \brief The yield is an cross reference. @@ -310,8 +325,8 @@ RZ_API void rz_interp_instance_free(RZ_OWN RZ_NULLABLE RzInterpInstance *iset); struct rz_interp_run_context_t { RzInterpInstance *inst; //< parent interpreter thread - RzList /**/ *queue; ///< States that have to be interpreted still. If this is empty, a fixpoint has been reached. - HtUP /**/ *pc_states; ///< Currently discovered states at the entries of blocks. + RzList /**/ *queue; ///< States that have to be interpreted still. If this is empty, a fixpoint has been reached. + HtUP /**/ *pc_blocks; ///< Currently discovered blocks. // Tracking data local to a single block interpretation ut64 il_block_end; ///< The address directly after the last instruction of the currently interpreted IL block diff --git a/librz/inquiry/README.md b/librz/inquiry/README.md index e8486b76b81..d13e4296507 100644 --- a/librz/inquiry/README.md +++ b/librz/inquiry/README.md @@ -54,9 +54,11 @@ with one from another block, this should be merged. Call detection is based on a store of the block end addr before the jump. Consider the following ARMv4 code for an indirect call (blx was introduced in ARMv5): +``` A> mov lr, pc B> mov pc, r0 C> ... +``` both A and B are block entries. @@ -77,9 +79,11 @@ Idea: have some flags per block, something like: Consider patterns like this, where there is an in-edge to A and to B. +``` A> mov lr, pc B> jmp 1324 C> ... +``` For all such patterns across an entire interpretation, we now want to **not** consider the jmp as a call, but only because of the in-edge B. However: @@ -105,6 +109,7 @@ We could weaken our initial condition to allow for A to be calling while B is there if B is detected only because of A later on, but then consider this example: +``` A> mov lr, pc B> jmp 1234 C> jmp B' @@ -112,6 +117,7 @@ C> jmp B' A'> mov lr, pc B'> jmp 4321 C'> jmp B +``` There is no unique solution for minimizing calling blocks with edges into them anymore. Either A is considered calling and A' is not, or the other way around. diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index ee9fc0bc4b1..dcb3ad515a2 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -241,7 +241,6 @@ RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_clone(RZ_NONNULL RzInter } r->pc = state->pc; r->pc_state = state->pc_state; - r->uninterpreted = state->uninterpreted; r->globals = var_set_clone(iset, state->globals); r->locals = var_set_clone(iset, state->locals); r->lets = var_set_clone(iset, state->lets); @@ -376,6 +375,27 @@ RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( return NULL; } +static RzInterpBlock *interp_block_new(RzInterpInstance *inst, RZ_BORROW RZ_NONNULL RzInterpAbstrState *entry_state) { + RzInterpBlock *block = RZ_NEW(RzInterpBlock); + if (!block) { + return NULL; + } + block->entry_state = rz_interp_abstr_state_clone(inst, entry_state); + if (!block->entry_state) { + free(block); + return NULL; + } + return block; +} + +static void interp_block_free(RzInterpInstance *inst, RzInterpBlock *block) { + if (!block) { + return; + } + rz_interp_abstr_state_free(inst, block->entry_state); + free(block); +} + RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as) { if (as->pc_state == RZ_INTERP_PC_ANY) { RZ_LOG_DEBUG("Encountered state with unknown/top pc\n"); @@ -390,25 +410,25 @@ RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_ rz_interp_abstr_state_as_str_short(ctx->inst, as, &sb); RZ_LOG_DEBUG("PUSH 0x%" PFMT64x ": %s\n", as->pc, rz_strbuf_get(&sb)); rz_strbuf_fini(&sb); - RzInterpAbstrState *existing = ht_up_find(ctx->pc_states, as->pc, NULL); - if (existing) { - if (join_state(ctx->inst, existing, as) && !existing->uninterpreted) { - existing->uninterpreted = true; - rz_list_push(ctx->queue, existing); + RzInterpBlock *block = ht_up_find(ctx->pc_blocks, as->pc, NULL); + if (block) { + if (join_state(ctx->inst, block->entry_state, as) && !block->uninterpreted) { + block->uninterpreted = true; + rz_list_push(ctx->queue, block); } } else { - RzInterpAbstrState *c = rz_interp_abstr_state_clone(ctx->inst, as); - if (!c) { + block = interp_block_new(ctx->inst, as); + if (!block) { return; } - ht_up_insert(ctx->pc_states, as->pc, c); - c->uninterpreted = true; - rz_list_push(ctx->queue, c); + ht_up_insert(ctx->pc_blocks, as->pc, block); + block->uninterpreted = true; + rz_list_push(ctx->queue, block); } } -static RzInterpAbstrState *rz_interp_run_pop(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx) { - RzInterpAbstrState *r = rz_list_pop(ctx->queue); +static RzInterpBlock *rz_interp_run_pop(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx) { + RzInterpBlock *r = rz_list_pop(ctx->queue); if (!r) { return NULL; } @@ -1019,7 +1039,7 @@ static bool eval_effect(RzInterpRunContext *ctx, } else { // different jump targets, branch rather than resorting to top pc rz_interp_run_push(ctx, true_state); - // true_state is already in ctx->inst->astate and will be continued automatically + // false_state is already in ctx->inst->astate and will be continued automatically } rz_interp_abstr_state_free(ctx->inst, true_state); } else if (may_be_true) { @@ -1166,9 +1186,9 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { .inst = inst, .astate = NULL, .queue = rz_list_new(), - .pc_states = ht_up_new(NULL, free) + .pc_blocks = ht_up_new(NULL, NULL) }; - if (!ctx.queue || !ctx.pc_states) { + if (!ctx.queue || !ctx.pc_blocks) { goto cleanup; } @@ -1186,16 +1206,16 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { // Loop and interpret until a fixpoint has been reached while (true) { - RzInterpAbstrState *next = rz_interp_run_pop(&ctx); - if (!next) { + RzInterpBlock *interp_block = rz_interp_run_pop(&ctx); + if (!interp_block) { // No uninterpreted states left, fixpoint reached. success = true; break; } - ctx.astate = rz_interp_abstr_state_clone(inst, next); + ctx.astate = rz_interp_abstr_state_clone(inst, interp_block->entry_state); - const RzILCacheBlock *il_bb = rz_il_cache_client_lift_il_block(inst->il_cache_client, ctx.astate->pc); - if (!il_bb) { + const RzILCacheBlock *il_block = rz_il_cache_client_lift_il_block(inst->il_cache_client, ctx.astate->pc); + if (!il_block) { RZ_LOG_ERROR("interpreter: Lifting failed\n"); // TODO: handle this better break; @@ -1204,7 +1224,7 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { // DEBUG comments RzStrBuf sb; rz_strbuf_init(&sb); - const char *old_cmt = rz_meta_get_string(inst->a, RZ_META_TYPE_COMMENT, il_bb->addr); + const char *old_cmt = rz_meta_get_string(inst->a, RZ_META_TYPE_COMMENT, il_block->addr); if (old_cmt) { rz_strbuf_appendf(&sb, "%s; ", old_cmt); } @@ -1213,9 +1233,9 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { // rz_meta_set_string(iset->a, RZ_META_TYPE_COMMENT, il_bb->addr, rz_strbuf_get(&sb)); rz_strbuf_fini(&sb); - ctx.il_block_end = il_bb->addr + il_bb->size; + ctx.il_block_end = il_block->addr + il_block->size; // Evaluate the effect on the abstract state. - if (!eval_block(&ctx, il_bb)) { + if (!eval_block(&ctx, il_block)) { RZ_LOG_ERROR("interpreter: Eval failed\n"); success = false; break; @@ -1224,7 +1244,13 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { cleanup: rz_list_free(ctx.queue); - ht_up_free(ctx.pc_states); + RzIterator *it = ht_up_as_iter(ctx.pc_blocks); + RzInterpBlock **b; + rz_iterator_foreach(it, b) { + interp_block_free(inst, *b); + } + rz_iterator_free(it); + ht_up_free(ctx.pc_blocks); return success; } From d63589e7e0116f1f08b84d3e195edeb98bf16ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Wed, 8 Jul 2026 18:14:04 +0200 Subject: [PATCH 312/334] Reorder interpreter.c --- librz/include/rz_inquiry/rz_interpreter.h | 2 +- librz/inquiry/interp/interpreter.c | 334 ++++++++++++---------- 2 files changed, 177 insertions(+), 159 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 98dd9e89d74..ec3117faf2a 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -116,7 +116,7 @@ typedef struct { * Represents a block of instructions of which only the last may have a control effect. * Unlike IL blocks, the last instruction may also not have a control effect, which is * the case when the instruction directly following this block has an in-edge. - * And unlike regular basic blocks, a call instruction also terminates an interpreter block. + * And unlike in RzAnalysisBlock, a call instruction also terminates an interpreter block. */ typedef struct { /** diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index dcb3ad515a2..3fbd22e994e 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -15,59 +15,11 @@ #include #include -RZ_API void rz_interp_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yield_rbufs) { - if (!yield_rbufs) { - return; - } - if (yield_rbufs->rbuf) { - rz_th_ring_buf_free(yield_rbufs->rbuf); - } - if (yield_rbufs->filter_data && yield_rbufs->filter_data->io_boundaries) { - rz_pvector_free(yield_rbufs->filter_data->io_boundaries); - } - free(yield_rbufs->filter_data); - free(yield_rbufs); -} - -RZ_API RZ_OWN RzInterpYieldRBuf *rz_interp_yield_rbuf_new(RzInterpYieldKind kind, - RzInterpYieldFilter filter, - RZ_OWN RZ_NULLABLE void *filter_data) { - RzInterpYieldRBuf *yield_rbufs = RZ_NEW0(RzInterpYieldRBuf); - if (!yield_rbufs) { - return NULL; - } - RzThreadRingBuf *rbuf = NULL; - switch (kind) { - default: - rz_warn_if_reached(); - return NULL; - case RZ_INTERP_YIELD_KIND_CALL_CANDIDATE: - rbuf = rz_th_ring_buf_new(RZ_INTERP_YIELD_RBUF_SIZE, sizeof(RzAnalysisCallCandidate)); - break; - case RZ_INTERP_YIELD_KIND_CONTROL_FLOW: - rbuf = rz_th_ring_buf_new(RZ_INTERP_YIELD_RBUF_SIZE, sizeof(RzInterpCtrlFlow)); - break; - case RZ_INTERP_YIELD_KIND_XREF: - if (filter_data) { - yield_rbufs->filter_data = RZ_NEW0(RzInterpYieldFilterData); - yield_rbufs->filter_data->io_boundaries = filter_data; - } - rbuf = rz_th_ring_buf_new(RZ_INTERP_YIELD_RBUF_SIZE, sizeof(RzAnalysisXRef)); - if (!rbuf) { - rz_pvector_free(filter_data); - return NULL; - } - break; - } - if (!rbuf) { - free(yield_rbufs); - return NULL; - } - yield_rbufs->kind = kind; - yield_rbufs->rbuf = rbuf; - yield_rbufs->filter = filter; - return yield_rbufs; -} +///////////////////////////////////////////////////////// +/** + * \name RzInterpAbstrState + * @{ + */ /** * \brief Initializes an abstract state for specified abstract kinds. Optionally with a list of registers. @@ -192,30 +144,6 @@ RZ_API void rz_interp_abstr_state_as_str_short(RZ_NONNULL RzInterpInstance *inst } } -RZ_API void rz_interp_instance_free(RZ_OWN RZ_NULLABLE RzInterpInstance *iset) { - if (!iset) { - return; - } - ht_up_free(iset->var_name_hashes); - if (iset->io_request_rbuf) { - rz_th_ring_buf_free(iset->io_request_rbuf); - } - if (iset->io_result_rbuf) { - rz_th_ring_buf_free(iset->io_result_rbuf); - } - if (iset->entry_points) { - rz_th_ring_buf_free(iset->entry_points); - } - if (iset->run_state_sync) { - rz_th_sem_free(iset->run_state_sync); - } - if (iset->run_state) { - rz_interp_run_state_free(iset->run_state); - } - rz_analysis_il_context_free(iset->il_ctx); - free(iset); -} - static HtUP *var_set_clone(const RzInterpInstance *iset, HtUP *vars) { HtUP *r = ht_up_new(NULL, NULL); if (!r) { @@ -247,34 +175,6 @@ RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_clone(RZ_NONNULL RzInter return r; } -static bool setup_ipc_objects( - RZ_OUT RzThreadRingBuf **io_request_rbuf, - RZ_OUT RzThreadRingBuf **io_result_rbuf, - RZ_OUT RzThreadRingBuf **entry_points) { - *io_request_rbuf = NULL; - *io_result_rbuf = NULL; - *entry_points = NULL; - - // Setup the IO queues. Each interpreter instance needs it's own queue at - // for writing IO. Because the writing is done on the IO cache, and each - // instance needs its own cache. - *io_request_rbuf = rz_th_ring_buf_new(RZ_INTERP_IO_RBUF_SIZE, sizeof(RzInterpIOReadRequest)); - *io_result_rbuf = rz_th_ring_buf_new(RZ_INTERP_IO_RBUF_SIZE, sizeof(RzInterpIOResult)); - *entry_points = rz_th_ring_buf_new(RZ_INTERP_ENTRY_POINTS_RBUF_SIZE, sizeof(ut64)); - if (!*io_request_rbuf || !*io_result_rbuf || !*entry_points) { - rz_warn_if_reached(); - goto error_free; - } - - return true; - -error_free: - rz_th_ring_buf_free(*io_request_rbuf); - rz_th_ring_buf_free(*io_result_rbuf); - rz_th_ring_buf_free(*entry_points); - return false; -} - /** * \brief Join (least upper bound) on var sets * \return True if a was changed @@ -303,6 +203,161 @@ bool join_state(RzInterpInstance *inst, RZ_BORROW RZ_INOUT RzInterpAbstrState *a return global_change || local_change; } +/// @} + +///////////////////////////////////////////////////////// +/** + * \name Interpreter Blocks + * @{ + */ + +static RzInterpBlock *interp_block_new(RzInterpInstance *inst, RZ_BORROW RZ_NONNULL RzInterpAbstrState *entry_state) { + RzInterpBlock *block = RZ_NEW0(RzInterpBlock); + if (!block) { + return NULL; + } + block->entry_state = rz_interp_abstr_state_clone(inst, entry_state); + if (!block->entry_state) { + free(block); + return NULL; + } + return block; +} + +static void interp_block_free(RzInterpInstance *inst, RzInterpBlock *block) { + if (!block) { + return; + } + rz_interp_abstr_state_free(inst, block->entry_state); + free(block); +} + +RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as) { + if (as->pc_state == RZ_INTERP_PC_ANY) { + RZ_LOG_DEBUG("Encountered state with unknown/top pc\n"); + return; + } + if (as->pc_state != RZ_INTERP_PC_CONST) { + rz_warn_if_reached(); + return; + } + RzStrBuf sb; + rz_strbuf_init(&sb); + rz_interp_abstr_state_as_str_short(ctx->inst, as, &sb); + RZ_LOG_DEBUG("PUSH 0x%" PFMT64x ": %s\n", as->pc, rz_strbuf_get(&sb)); + rz_strbuf_fini(&sb); + RzInterpBlock *block = ht_up_find(ctx->pc_blocks, as->pc, NULL); + if (block) { + if (join_state(ctx->inst, block->entry_state, as) && !block->uninterpreted) { + block->uninterpreted = true; + rz_list_push(ctx->queue, block); + } + } else { + block = interp_block_new(ctx->inst, as); + if (!block) { + return; + } + ht_up_insert(ctx->pc_blocks, as->pc, block); + block->uninterpreted = true; + rz_list_push(ctx->queue, block); + } +} + +static RzInterpBlock *rz_interp_run_pop(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx) { + RzInterpBlock *r = rz_list_pop(ctx->queue); + if (!r) { + return NULL; + } + r->uninterpreted = false; + return r; +} + +/// @} + +///////////////////////////////////////////////////////// + +RZ_API void rz_interp_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yield_rbufs) { + if (!yield_rbufs) { + return; + } + if (yield_rbufs->rbuf) { + rz_th_ring_buf_free(yield_rbufs->rbuf); + } + if (yield_rbufs->filter_data && yield_rbufs->filter_data->io_boundaries) { + rz_pvector_free(yield_rbufs->filter_data->io_boundaries); + } + free(yield_rbufs->filter_data); + free(yield_rbufs); +} + +RZ_API RZ_OWN RzInterpYieldRBuf *rz_interp_yield_rbuf_new(RzInterpYieldKind kind, + RzInterpYieldFilter filter, + RZ_OWN RZ_NULLABLE void *filter_data) { + RzInterpYieldRBuf *yield_rbufs = RZ_NEW0(RzInterpYieldRBuf); + if (!yield_rbufs) { + return NULL; + } + RzThreadRingBuf *rbuf = NULL; + switch (kind) { + default: + rz_warn_if_reached(); + return NULL; + case RZ_INTERP_YIELD_KIND_CALL_CANDIDATE: + rbuf = rz_th_ring_buf_new(RZ_INTERP_YIELD_RBUF_SIZE, sizeof(RzAnalysisCallCandidate)); + break; + case RZ_INTERP_YIELD_KIND_CONTROL_FLOW: + rbuf = rz_th_ring_buf_new(RZ_INTERP_YIELD_RBUF_SIZE, sizeof(RzInterpCtrlFlow)); + break; + case RZ_INTERP_YIELD_KIND_XREF: + if (filter_data) { + yield_rbufs->filter_data = RZ_NEW0(RzInterpYieldFilterData); + yield_rbufs->filter_data->io_boundaries = filter_data; + } + rbuf = rz_th_ring_buf_new(RZ_INTERP_YIELD_RBUF_SIZE, sizeof(RzAnalysisXRef)); + if (!rbuf) { + rz_pvector_free(filter_data); + return NULL; + } + break; + } + if (!rbuf) { + free(yield_rbufs); + return NULL; + } + yield_rbufs->kind = kind; + yield_rbufs->rbuf = rbuf; + yield_rbufs->filter = filter; + return yield_rbufs; +} + +static bool setup_ipc_objects( + RZ_OUT RzThreadRingBuf **io_request_rbuf, + RZ_OUT RzThreadRingBuf **io_result_rbuf, + RZ_OUT RzThreadRingBuf **entry_points) { + *io_request_rbuf = NULL; + *io_result_rbuf = NULL; + *entry_points = NULL; + + // Setup the IO queues. Each interpreter instance needs it's own queue at + // for writing IO. Because the writing is done on the IO cache, and each + // instance needs its own cache. + *io_request_rbuf = rz_th_ring_buf_new(RZ_INTERP_IO_RBUF_SIZE, sizeof(RzInterpIOReadRequest)); + *io_result_rbuf = rz_th_ring_buf_new(RZ_INTERP_IO_RBUF_SIZE, sizeof(RzInterpIOResult)); + *entry_points = rz_th_ring_buf_new(RZ_INTERP_ENTRY_POINTS_RBUF_SIZE, sizeof(ut64)); + if (!*io_request_rbuf || !*io_result_rbuf || !*entry_points) { + rz_warn_if_reached(); + goto error_free; + } + + return true; + +error_free: + rz_th_ring_buf_free(*io_request_rbuf); + rz_th_ring_buf_free(*io_result_rbuf); + rz_th_ring_buf_free(*entry_points); + return false; +} + /** * \brief Initializes a new RzInterpSet and returns it. * If it fails, all arguments are freed. @@ -375,65 +430,28 @@ RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( return NULL; } -static RzInterpBlock *interp_block_new(RzInterpInstance *inst, RZ_BORROW RZ_NONNULL RzInterpAbstrState *entry_state) { - RzInterpBlock *block = RZ_NEW(RzInterpBlock); - if (!block) { - return NULL; - } - block->entry_state = rz_interp_abstr_state_clone(inst, entry_state); - if (!block->entry_state) { - free(block); - return NULL; - } - return block; -} - -static void interp_block_free(RzInterpInstance *inst, RzInterpBlock *block) { - if (!block) { +RZ_API void rz_interp_instance_free(RZ_OWN RZ_NULLABLE RzInterpInstance *iset) { + if (!iset) { return; } - rz_interp_abstr_state_free(inst, block->entry_state); - free(block); -} - -RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as) { - if (as->pc_state == RZ_INTERP_PC_ANY) { - RZ_LOG_DEBUG("Encountered state with unknown/top pc\n"); - return; + ht_up_free(iset->var_name_hashes); + if (iset->io_request_rbuf) { + rz_th_ring_buf_free(iset->io_request_rbuf); } - if (as->pc_state != RZ_INTERP_PC_CONST) { - rz_warn_if_reached(); - return; + if (iset->io_result_rbuf) { + rz_th_ring_buf_free(iset->io_result_rbuf); } - RzStrBuf sb; - rz_strbuf_init(&sb); - rz_interp_abstr_state_as_str_short(ctx->inst, as, &sb); - RZ_LOG_DEBUG("PUSH 0x%" PFMT64x ": %s\n", as->pc, rz_strbuf_get(&sb)); - rz_strbuf_fini(&sb); - RzInterpBlock *block = ht_up_find(ctx->pc_blocks, as->pc, NULL); - if (block) { - if (join_state(ctx->inst, block->entry_state, as) && !block->uninterpreted) { - block->uninterpreted = true; - rz_list_push(ctx->queue, block); - } - } else { - block = interp_block_new(ctx->inst, as); - if (!block) { - return; - } - ht_up_insert(ctx->pc_blocks, as->pc, block); - block->uninterpreted = true; - rz_list_push(ctx->queue, block); + if (iset->entry_points) { + rz_th_ring_buf_free(iset->entry_points); } -} - -static RzInterpBlock *rz_interp_run_pop(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx) { - RzInterpBlock *r = rz_list_pop(ctx->queue); - if (!r) { - return NULL; + if (iset->run_state_sync) { + rz_th_sem_free(iset->run_state_sync); } - r->uninterpreted = false; - return r; + if (iset->run_state) { + rz_interp_run_state_free(iset->run_state); + } + rz_analysis_il_context_free(iset->il_ctx); + free(iset); } bool report_yield_xref( From 72a62b906f1c35f1ca46f32ad5b33795898dfd05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Thu, 9 Jul 2026 12:18:33 +0200 Subject: [PATCH 313/334] Store blocks in rbtree --- librz/include/rz_inquiry/rz_interpreter.h | 5 +- librz/inquiry/interp/interpreter.c | 75 +++++++++++++++++++---- 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index ec3117faf2a..3e309356811 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -119,6 +119,9 @@ typedef struct { * And unlike in RzAnalysisBlock, a call instruction also terminates an interpreter block. */ typedef struct { + RBNode _rb; // private, node in the RBTree. Key is entry_state->p. + ut64 _max_end; // private, augmented value for RBTree. + /** * Least upper bound of all states discovered at the entry of the block. * pc_state of this state must be RZ_INTERP_PC_CONST and pc points to the first instruction of the block. @@ -326,7 +329,7 @@ struct rz_interp_run_context_t { RzInterpInstance *inst; //< parent interpreter thread RzList /**/ *queue; ///< States that have to be interpreted still. If this is empty, a fixpoint has been reached. - HtUP /**/ *pc_blocks; ///< Currently discovered blocks. + RBTree /**/ blocks; // All currently discovered blocks by address. They can overlap each other, but must never start at the same address. // Tracking data local to a single block interpretation ut64 il_block_end; ///< The address directly after the last instruction of the currently interpreted IL block diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 3fbd22e994e..4e378bae8bc 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -211,6 +211,8 @@ bool join_state(RzInterpInstance *inst, RZ_BORROW RZ_INOUT RzInterpAbstrState *a * @{ */ +#define UNWRAP_BLOCK(rbnode) ((rbnode) ? container_of(rbnode, RzInterpBlock, _rb) : NULL) + static RzInterpBlock *interp_block_new(RzInterpInstance *inst, RZ_BORROW RZ_NONNULL RzInterpAbstrState *entry_state) { RzInterpBlock *block = RZ_NEW0(RzInterpBlock); if (!block) { @@ -232,6 +234,62 @@ static void interp_block_free(RzInterpInstance *inst, RzInterpBlock *block) { free(block); } +static void interp_block_free_rb(RBNode *node, void *user) { + RzInterpBlock *block = UNWRAP_BLOCK(node); + interp_block_free(user, block); +} + +static void interp_blocks_free(RzInterpInstance *inst, RBTree root) { + rz_rbtree_free(root, interp_block_free_rb, inst); +} + +/** cmp function for RzInterpRunContext.blocks */ +static int interp_block_addr_cmp(const void *incoming, const RBNode *in_tree, void *user) { + ut64 incoming_addr = *(ut64 *)incoming; + const RzInterpBlock *in_tree_block = container_of(in_tree, const RzInterpBlock, _rb); + if (incoming_addr < in_tree_block->entry_state->pc) { + return -1; + } + if (incoming_addr > in_tree_block->entry_state->pc) { + return 1; + } + return 0; +} + +/** sum function for RzInterpRunContext.blocks */ +static void interp_block_max_end(RBNode *node) { + RzInterpBlock *block = UNWRAP_BLOCK(node); + block->_max_end = block->entry_state->pc + 0; // TODO: + block->size; + int i; + for (i = 0; i < 2; i++) { + if (node->child[i]) { + ut64 end = UNWRAP_BLOCK(node->child[i])->_max_end; + if (end > block->_max_end) { + block->_max_end = end; + } + } + } +} + +static RzInterpBlock *interp_block_create(RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as) { + rz_return_val_if_fail(ctx && as && as->pc_state == RZ_INTERP_PC_CONST, NULL); + RzInterpBlock *block = interp_block_new(ctx->inst, as); + if (!block) { + return NULL; + } + bool succ = rz_rbtree_aug_insert(&ctx->blocks, &block->entry_state->pc, &block->_rb, interp_block_addr_cmp, NULL, interp_block_max_end); + if (!succ) { + rz_warn_if_reached(); + return NULL; + } + return block; +} + +static RzInterpBlock *interp_block_at(RzInterpRunContext *ctx, ut64 addr) { + RBNode *node = rz_rbtree_find(ctx->blocks, &addr, interp_block_addr_cmp, NULL); + return UNWRAP_BLOCK(node); +} + RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as) { if (as->pc_state == RZ_INTERP_PC_ANY) { RZ_LOG_DEBUG("Encountered state with unknown/top pc\n"); @@ -246,18 +304,17 @@ RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_ rz_interp_abstr_state_as_str_short(ctx->inst, as, &sb); RZ_LOG_DEBUG("PUSH 0x%" PFMT64x ": %s\n", as->pc, rz_strbuf_get(&sb)); rz_strbuf_fini(&sb); - RzInterpBlock *block = ht_up_find(ctx->pc_blocks, as->pc, NULL); + RzInterpBlock *block = interp_block_at(ctx, as->pc); if (block) { if (join_state(ctx->inst, block->entry_state, as) && !block->uninterpreted) { block->uninterpreted = true; rz_list_push(ctx->queue, block); } } else { - block = interp_block_new(ctx->inst, as); + block = interp_block_create(ctx, as); if (!block) { return; } - ht_up_insert(ctx->pc_blocks, as->pc, block); block->uninterpreted = true; rz_list_push(ctx->queue, block); } @@ -1204,9 +1261,9 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { .inst = inst, .astate = NULL, .queue = rz_list_new(), - .pc_blocks = ht_up_new(NULL, NULL) + .blocks = NULL }; - if (!ctx.queue || !ctx.pc_blocks) { + if (!ctx.queue) { goto cleanup; } @@ -1262,13 +1319,7 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { cleanup: rz_list_free(ctx.queue); - RzIterator *it = ht_up_as_iter(ctx.pc_blocks); - RzInterpBlock **b; - rz_iterator_foreach(it, b) { - interp_block_free(inst, *b); - } - rz_iterator_free(it); - ht_up_free(ctx.pc_blocks); + interp_blocks_free(inst, ctx.blocks); return success; } From 6d1fcd3c9f399061ff4f354e21d2188d9c3903c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Fri, 10 Jul 2026 16:55:51 +0200 Subject: [PATCH 314/334] Apply interpreter blocks to analysis --- librz/include/rz_inquiry/rz_interpreter.h | 29 ++- librz/inquiry/README.md | 37 +++- librz/inquiry/inquiry.c | 25 ++- librz/inquiry/interp/interpreter.c | 218 +++++++++++++++++----- 4 files changed, 251 insertions(+), 58 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 3e309356811..f2f66e5f9ab 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -119,15 +119,19 @@ typedef struct { * And unlike in RzAnalysisBlock, a call instruction also terminates an interpreter block. */ typedef struct { - RBNode _rb; // private, node in the RBTree. Key is entry_state->p. - ut64 _max_end; // private, augmented value for RBTree. - + RzIntervalNode *node; ///< Backref to the node containing this block. end value is inclusive. TODO: remove this if RBTree is used directly /** * Least upper bound of all states discovered at the entry of the block. * pc_state of this state must be RZ_INTERP_PC_CONST and pc points to the first instruction of the block. */ RzInterpAbstrState *entry_state; // TODO: flatten to remove indirection + RzVector /**/ insn_offsets; ///< starting at the second instruction in the block (since first is always 0), offsets from the start of the block + bool insns_resolved; ///< Set to true once instruction_offsets and node->end are filled. bool uninterpreted; ///< True if the entry state has not yet been started to interpret, i.e. the block is part of RzInterpFunctionState.queue + + // Out-edges + bool fallthrough; ///< if true, there is an edge to the block after the end of this one + RzVector /**/ jump_targets; ///< Explicit jump targets, does not contain fallthrough address } RzInterpBlock; typedef enum { @@ -283,6 +287,9 @@ struct rz_interp_instance_t { * These ring buffers are shared with other interpreter sets. */ RZ_BORROW RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM]; + + RzPVector /**/ results; ///< TODO: replace this by a queue/rbuf/... to handle interp and results concurrently + /** * \brief Ignored address ranges. */ @@ -329,14 +336,25 @@ struct rz_interp_run_context_t { RzInterpInstance *inst; //< parent interpreter thread RzList /**/ *queue; ///< States that have to be interpreted still. If this is empty, a fixpoint has been reached. - RBTree /**/ blocks; // All currently discovered blocks by address. They can overlap each other, but must never start at the same address. + /** + * \brief All currently discovered blocks by address. + * TODO: If the interval tree concept is kept, this should eventually be refactored to use RBTree directly and embed RBNode + * inside RzInterpBlock to remove the additional indirection. + */ + RzIntervalTree /**/ blocks; // Tracking data local to a single block interpretation - ut64 il_block_end; ///< The address directly after the last instruction of the currently interpreted IL block + RzInterpBlock *block; ///< The currently interpreted interp block + ut64 il_block_end; ///< The address directly after the last instruction of the currently interpreted IL block, may be further than the last instruction of the interp block! RzInterpAbstrState *astate; ///< The abstract state of the interpreter. RzAnalysisCallCandidate call_cand; ///< Data of a call candidate. }; +typedef struct rz_interp_run_result_t { + ut64 entry; + RzIntervalTree /**/ blocks; +} RzInterpResult; + /* * \brief Register a newly discovered state * @@ -346,5 +364,6 @@ struct rz_interp_run_context_t { RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as); RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *iset); +RZ_API void rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, RZ_NONNULL RzAnalysis *analysis); #endif // RZ_INTERPRETER diff --git a/librz/inquiry/README.md b/librz/inquiry/README.md index d13e4296507..ddc4c868b13 100644 --- a/librz/inquiry/README.md +++ b/librz/inquiry/README.md @@ -29,12 +29,37 @@ Ideas for optimization (do not implement any of these without profiling first): profiled if the cost of asking the plugin all the time is really compensated by avoided evalutations in practical code. -## CFG recovery irregularities TODO: +## Multiple entrypoints in one block Code: +``` B> fallthrough fallthrough -A> ... +A> fallthrough + jmp +``` + +B and A are in-edges. When interpreting, we first only get the full IL blocks +where B extends until the jmp and A does not know there may be instructions +before. +Desired outcome is to have blocks like this without overlap: + +``` +------------------ +| B> fallthrough | +| fallthrough | +------------------ + | +------------------ +| A> fallthrough | +| jmp | +------------------ +``` + +rot127 solved it by just interpreting the IL blocks as they come and at the end +reducing the overlapping blocks. + +thestr4ng3r's approach is to handle the reduction as part of the interpreter loop: Case 1: A was already discovered. @@ -47,10 +72,12 @@ Case 2: => B must be split. Special cases: -Jump into the middle of an instruction. Perhaps when an instruction meets again -with one from another block, this should be merged. +Jump into the middle of an instruction. In such cases, overlaps between blocks +are permitted. Perhaps when an instruction meets again with one from another +block, this should be merged. + +### Call detection issues -### Problem with blocks that do not end in a jump, or ones that have more prepended instructions to be discovered later: Call detection is based on a store of the block end addr before the jump. Consider the following ARMv4 code for an indirect call (blx was introduced in ARMv5): diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 3e9e1021a84..1ddb3185552 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -495,7 +495,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_NONNULL const RzVector /**/ *ignored_code) { // All the things we need bool return_code = true; - RzInterpInstance *intp_iset = NULL; + RzInterpInstance *inst = NULL; RzBuffer *io_buf = rz_buf_new_with_io(rz_analysis_get_io_bind(core->analysis)); RzSetU *symbol_targets = rz_set_u_new(); @@ -558,13 +558,13 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, rz_warn_if_reached(); goto error_free; } - intp_iset = rz_interp_instance_new( + inst = rz_interp_instance_new( core->analysis, prototype->value_abstraction, cache_client, yield_rbufs, ignored_code); - if (!intp_iset) { + if (!inst) { return_code = false; rz_warn_if_reached(); goto error_free; @@ -572,9 +572,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // Dispatch prototype interpreter into a thread. RZ_LOG_DEBUG("inquiry: Start main interpretation thread.\n"); - RzThread *interpr_th = rz_th_new((RzThreadFunction)rz_interp_instance_th, intp_iset); + RzThread *interpr_th = rz_th_new((RzThreadFunction)rz_interp_instance_th, inst); iset_map[i].ithread = interpr_th; - iset_map[i].iset = intp_iset; + iset_map[i].iset = inst; iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_INIT; } @@ -740,13 +740,25 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_LOG_ERROR("User sent signal.\n"); } } - rz_interp_instance_free(iset_map[i].iset); } rz_il_cache_stop_serving(il_cache); if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { eprintf("\n"); } + + // Apply results + void **it; + rz_pvector_foreach (&inst->results, it) { + RzInterpResult *res = *it; + rz_interp_result_apply_to_analysis(res, core->analysis); + } + + for (size_t i = 0; i < n_threads; i++) { + rz_interp_instance_free(iset_map[i].iset); + } + +#if 0 RzIterator *iter = rz_il_cache_get_blocks(il_cache); if (!iter) { rz_warn_if_reached(); @@ -776,6 +788,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // g = rz_inquiry_bcfg_as_dot(core->inquiry->bcfg, "reduced"); // printf("%s\n", g); // free(g); +#endif RZ_LOG_DEBUG("inquiry: inquiry: inquiry: Done\n"); diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 4e378bae8bc..3f47cd2a5f2 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -223,6 +223,8 @@ static RzInterpBlock *interp_block_new(RzInterpInstance *inst, RZ_BORROW RZ_NONN free(block); return NULL; } + rz_vector_init(&block->insn_offsets, sizeof(ut16), NULL, NULL); + rz_vector_init(&block->jump_targets, sizeof(ut64), NULL, NULL); return block; } @@ -231,44 +233,22 @@ static void interp_block_free(RzInterpInstance *inst, RzInterpBlock *block) { return; } rz_interp_abstr_state_free(inst, block->entry_state); + rz_vector_fini(&block->insn_offsets); + rz_vector_fini(&block->jump_targets); free(block); } -static void interp_block_free_rb(RBNode *node, void *user) { - RzInterpBlock *block = UNWRAP_BLOCK(node); - interp_block_free(user, block); +static void interp_blocks_init(RzInterpRunContext *ctx) { + rz_interval_tree_init(&ctx->blocks, NULL); } -static void interp_blocks_free(RzInterpInstance *inst, RBTree root) { - rz_rbtree_free(root, interp_block_free_rb, inst); -} - -/** cmp function for RzInterpRunContext.blocks */ -static int interp_block_addr_cmp(const void *incoming, const RBNode *in_tree, void *user) { - ut64 incoming_addr = *(ut64 *)incoming; - const RzInterpBlock *in_tree_block = container_of(in_tree, const RzInterpBlock, _rb); - if (incoming_addr < in_tree_block->entry_state->pc) { - return -1; - } - if (incoming_addr > in_tree_block->entry_state->pc) { - return 1; - } - return 0; -} - -/** sum function for RzInterpRunContext.blocks */ -static void interp_block_max_end(RBNode *node) { - RzInterpBlock *block = UNWRAP_BLOCK(node); - block->_max_end = block->entry_state->pc + 0; // TODO: + block->size; - int i; - for (i = 0; i < 2; i++) { - if (node->child[i]) { - ut64 end = UNWRAP_BLOCK(node->child[i])->_max_end; - if (end > block->_max_end) { - block->_max_end = end; - } - } +static void interp_blocks_free(RzInterpRunContext *ctx) { + RzIntervalTreeIter it; + RzInterpBlock *block; + rz_interval_tree_foreach(&ctx->blocks, it, block) { + interp_block_free(ctx->inst, block); } + rz_interval_tree_fini(&ctx->blocks); } static RzInterpBlock *interp_block_create(RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as) { @@ -277,17 +257,101 @@ static RzInterpBlock *interp_block_create(RzInterpRunContext *ctx, RZ_BORROW RZ_ if (!block) { return NULL; } - bool succ = rz_rbtree_aug_insert(&ctx->blocks, &block->entry_state->pc, &block->_rb, interp_block_addr_cmp, NULL, interp_block_max_end); - if (!succ) { + RzIntervalNode *node = rz_interval_tree_insert(&ctx->blocks, block->entry_state->pc, block->entry_state->pc, block); + if (!node) { rz_warn_if_reached(); return NULL; } + block->node = node; return block; } +static ut64 interp_block_get_start(RzInterpBlock *block) { + return block->entry_state->pc; +} + +/** end is inclusive */ +static ut64 interp_block_get_end(RzInterpBlock *block) { + return block->node->end; +} + +static ut64 interp_block_get_size(RzInterpBlock *block) { + return interp_block_get_end(block) - interp_block_get_start(block) + 1; +} + +static void interp_block_add_non_fallthrough_target(RzInterpBlock *block, ut64 target) { + // linear search may be inefficient, but practically the number of targets is often small + if (rz_vector_contains(&block->jump_targets, &target)) { + return; + } + rz_vector_push(&block->jump_targets, &target); +} + static RzInterpBlock *interp_block_at(RzInterpRunContext *ctx, ut64 addr) { - RBNode *node = rz_rbtree_find(ctx->blocks, &addr, interp_block_addr_cmp, NULL); - return UNWRAP_BLOCK(node); + return rz_interval_tree_at(&ctx->blocks, addr); +} + +static int interp_block_addr_cmp(const void *incoming, const RBNode *in_tree, void *user) { + ut64 incoming_start = *(ut64 *)incoming; + ut64 other_start = container_of(in_tree, const RzIntervalNode, node)->start; + if (incoming_start < other_start) { + return -1; + } + if (incoming_start > other_start) { + return 1; + } + return 0; +} + +/** + * Resize the block to cover the instructions, or until the following block + * and fill instruction offsets. + */ +static void interp_block_apply_instructions(RzInterpRunContext *ctx, RzInterpBlock *interp_block, const RzILCacheBlock *il_block) { + if (interp_block->insns_resolved) { + return; + } + interp_block->insns_resolved = true; + // Blocks may overlap, but another block must not start at one of our instruction addresses. + // So we close our block once any of our instructions hit exactly the start of another block. + // (There is also the case where two non-start instructions hit, but we ignore this for now since results will + // still be correct) + // The next candidate for hitting is always the first whose address is greater than or equal to the instruction + // address (excluding the block start itself, since that would find our own block) so we search forward + // from a lower bound. + ut64 search_next_addr = interp_block->node->start + 1; + RBIter next_it = rz_rbtree_lower_bound_forward(&ctx->blocks.root->node, &search_next_addr, interp_block_addr_cmp, NULL); + + ut64 block_start = interp_block->node->start; + size_t insns_count = rz_pvector_len(il_block->il_ops); + rz_return_if_fail(insns_count > 0); + // interp_block->instruction_offsets is assumed to be empty here + ut64 cur = block_start; + rz_vector_reserve(&interp_block->insn_offsets, insns_count); + for (size_t i = 0; i < rz_pvector_len(il_block->il_ops); i++) { + RzILCacheInsnPkt *insn = rz_pvector_at(il_block->il_ops, i); + if (i > 0) { + // Close block if hitting another block's start address + while (rz_rbtree_iter_has(&next_it)) { + RzIntervalNode *next_node = rz_interval_tree_iter_get(&next_it); + if (next_node->start > cur) { + break; + } + if (next_node->start == cur) { + // hit found, the block will not include this instruction anymore. + goto close; + } + } + + ut16 off = cur - block_start; + rz_vector_push(&interp_block->insn_offsets, &off); + } + cur += insn->insn_pkt_size; + } +close: + // Warning: the resize operation may invalidate the node pointer! But in reality, it only does so + // if the start address has changed, so it is ok to leave the reference in interp_block->node as-is. + rz_interval_tree_resize(&ctx->blocks, interp_block->node, block_start, cur - 1); } RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as) { @@ -472,6 +536,7 @@ RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( inst->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] = yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] = yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] = yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW]; + rz_pvector_init(&inst->results, NULL); inst->io_request_rbuf = io_request_rbuf; inst->io_result_rbuf = io_result_rbuf; inst->run_state_sync = rz_th_sem_new(0); @@ -1114,6 +1179,7 @@ static bool eval_effect(RzInterpRunContext *ctx, } else { // different jump targets, branch rather than resorting to top pc rz_interp_run_push(ctx, true_state); + rz_vector_push(&ctx->block->jump_targets, &true_state->pc); // false_state is already in ctx->inst->astate and will be continued automatically } rz_interp_abstr_state_free(ctx->inst, true_state); @@ -1205,11 +1271,19 @@ static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzIL // Reset call candidate tracking for each basic block. memset(&ctx->call_cand, 0, sizeof(ctx->call_cand)); + ut64 interp_block_end = interp_block_get_end(ctx->block); + // Now execute the actual effects of the BLOCK. RzInterpAbstrState *astate = ctx->astate; void **it; rz_pvector_foreach (il_bb->il_ops, it) { ut64 pc = astate->pc; + + if (pc > interp_block_end) { + // block is truncated + break; + } + RZ_LOG_DEBUG("prototype: Eval PC = 0x%" PFMT64x "\n", pc); RzStrBuf sb; rz_strbuf_init(&sb); @@ -1237,14 +1311,21 @@ static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzIL if (!eval_effect(ctx, pkt->effect, pkt->insn_pkt_size)) { return false; } - if (astate->pc_state != RZ_INTERP_PC_CONST || astate->pc != next_pc) { - // Unreachable or a jump happened somewhere other than fallthrough, so we can't continue - // interpreting the block linearly, but have to push the new location + if (astate->pc_state != RZ_INTERP_PC_CONST) { + // unreachable or unknown jump + break; + } + if (astate->pc != next_pc) { + // Constant jump other than fallthrough, meaning interpretation will continue in another block + interp_block_add_non_fallthrough_target(ctx->block, astate->pc); break; } } if (astate->pc_state != RZ_INTERP_PC_UNREACHABLE) { + if (astate->pc_state == RZ_INTERP_PC_CONST && astate->pc == interp_block_end + 1) { + ctx->block->fallthrough = true; + } rz_interp_run_push(ctx, ctx->astate); } @@ -1260,12 +1341,12 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { RzInterpRunContext ctx = { .inst = inst, .astate = NULL, - .queue = rz_list_new(), - .blocks = NULL + .queue = rz_list_new() }; if (!ctx.queue) { goto cleanup; } + interp_blocks_init(&ctx); // Prepare the initial state from the given entry point // Hint: nothing speaks against supporting multiple entry points in a single run @@ -1296,6 +1377,8 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { break; } + interp_block_apply_instructions(&ctx, interp_block, il_block); + // DEBUG comments RzStrBuf sb; rz_strbuf_init(&sb); @@ -1308,6 +1391,7 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { // rz_meta_set_string(iset->a, RZ_META_TYPE_COMMENT, il_bb->addr, rz_strbuf_get(&sb)); rz_strbuf_fini(&sb); + ctx.block = interp_block; ctx.il_block_end = il_block->addr + il_block->size; // Evaluate the effect on the abstract state. if (!eval_block(&ctx, il_block)) { @@ -1317,9 +1401,15 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { } } + RzInterpResult *res = RZ_NEW0(RzInterpResult); + res->entry = entry_point; + memmove(&res->blocks, &ctx.blocks, sizeof(ctx.blocks)); + memset(&ctx.blocks, 0, sizeof(ctx.blocks)); + rz_pvector_push(&inst->results, res); + cleanup: rz_list_free(ctx.queue); - interp_blocks_free(inst, ctx.blocks); + interp_blocks_free(&ctx); return success; } @@ -1373,3 +1463,47 @@ RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *inst) { rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_TERM); return success; } + +static void bb_add_target(RzAnalysisBlock *abb, ut64 target) { + if (abb->jump == UT64_MAX && abb->fail != target) { + abb->jump = target; + } else if (abb->fail == UT64_MAX && abb->jump != target) { + abb->fail = target; + } else if (abb->fail != target && abb->jump != target) { + RZ_LOG_WARN("The basic block at 0x%" PFMT64x " has more than two outgoing edges.\n" + "\t\tHas jump = 0x%" PFMT64x " fail = 0x%" PFMT64x ". Will miss = 0x%" PFMT64x "\n", + abb->addr, abb->jump, abb->fail, + target); + } +} + +RZ_API void rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, RZ_NONNULL RzAnalysis *analysis) { + rz_return_if_fail(res && analysis); + // partially copied from rz_inquiry_convert_and_add_to_analysis() + char name[128]; + RzAnalysisFunction *func = rz_analysis_create_function(analysis, rz_strf(name, "inquiry.0x%" PFMT64x, res->entry), res->entry, RZ_ANALYSIS_FCN_TYPE_FCN); + RzIntervalTreeIter it; + RzInterpBlock *block; + rz_interval_tree_foreach (&res->blocks, it, block) { + ut64 start = interp_block_get_start(block); + ut64 size = interp_block_get_size(block); + // TODO: add_bb should eventually not be used here since it does its own analysis. + // Instead, we should create the block by hand and apply our analysis info to it. + rz_analysis_add_bb(analysis, start, size); + RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, interp_block_get_start(block)); + rz_analysis_function_add_block(func, abb); + + // TODO: check if this is a calling block (thus fallthrough) and the following block + // has no in-edge from somewhere else. If so, merge them. + + abb->jump = UT64_MAX; + abb->fail = UT64_MAX; + ut64 *target; + rz_vector_foreach(&block->jump_targets, target) { + bb_add_target(abb, *target); + } + if (block->fallthrough) { + bb_add_target(abb, start + size); + } + } +} From fb2c3cc55d60b9870e5b9eaef4cc8158660b633d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sat, 11 Jul 2026 12:19:07 +0200 Subject: [PATCH 315/334] Merge consecutive blocks if possible --- librz/include/rz_inquiry/rz_interpreter.h | 4 +- librz/inquiry/interp/interpreter.c | 64 ++++++++++++++++++----- 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index f2f66e5f9ab..d9687bfa5df 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -128,6 +128,8 @@ typedef struct { RzVector /**/ insn_offsets; ///< starting at the second instruction in the block (since first is always 0), offsets from the start of the block bool insns_resolved; ///< Set to true once instruction_offsets and node->end are filled. bool uninterpreted; ///< True if the entry state has not yet been started to interpret, i.e. the block is part of RzInterpFunctionState.queue + bool non_fallthrough_in; ///< True if there is an in-edge to this block that is not only a fallthrough. Used when merging consecutive blocks after interpretation. + bool added_to_analysis; ///< Only used after interpretation, when adding to analysis. Marks blocks that have been merged with the previous. // Out-edges bool fallthrough; ///< if true, there is an edge to the block after the end of this one @@ -361,7 +363,7 @@ typedef struct rz_interp_run_result_t { * This will join the state with the already known one at the same pc and add it to the * queue for further interpretation if there were changes. */ -RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as); +RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as, bool is_fallthrough); RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *iset); RZ_API void rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, RZ_NONNULL RzAnalysis *analysis); diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 3f47cd2a5f2..50994653a0f 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -275,10 +275,6 @@ static ut64 interp_block_get_end(RzInterpBlock *block) { return block->node->end; } -static ut64 interp_block_get_size(RzInterpBlock *block) { - return interp_block_get_end(block) - interp_block_get_start(block) + 1; -} - static void interp_block_add_non_fallthrough_target(RzInterpBlock *block, ut64 target) { // linear search may be inefficient, but practically the number of targets is often small if (rz_vector_contains(&block->jump_targets, &target)) { @@ -354,7 +350,7 @@ static void interp_block_apply_instructions(RzInterpRunContext *ctx, RzInterpBlo rz_interval_tree_resize(&ctx->blocks, interp_block->node, block_start, cur - 1); } -RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as) { +RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as, bool is_fallthrough) { if (as->pc_state == RZ_INTERP_PC_ANY) { RZ_LOG_DEBUG("Encountered state with unknown/top pc\n"); return; @@ -382,6 +378,9 @@ RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_ block->uninterpreted = true; rz_list_push(ctx->queue, block); } + if (!is_fallthrough) { + block->non_fallthrough_in = true; + } } static RzInterpBlock *rz_interp_run_pop(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx) { @@ -1162,6 +1161,7 @@ static bool eval_effect(RzInterpRunContext *ctx, } bool may_be_true = ctx->inst->plugin->may_be_bool(eval_out, true); bool may_be_false = ctx->inst->plugin->may_be_bool(eval_out, false); + ut64 fallthrough_pc = ctx->astate->pc; if (may_be_true && may_be_false) { RzInterpAbstrState *true_state = rz_interp_abstr_state_clone(ctx->inst, ctx->astate); RzInterpAbstrState *false_state = ctx->astate; @@ -1178,8 +1178,10 @@ static bool eval_effect(RzInterpRunContext *ctx, join_state(ctx->inst, true_state, false_state); } else { // different jump targets, branch rather than resorting to top pc - rz_interp_run_push(ctx, true_state); - rz_vector_push(&ctx->block->jump_targets, &true_state->pc); + rz_interp_run_push(ctx, true_state, true_state->pc_state == RZ_INTERP_PC_CONST && true_state->pc == fallthrough_pc); + if (true_state->pc_state == RZ_INTERP_PC_CONST) { + rz_vector_push(&ctx->block->jump_targets, &true_state->pc); + } // false_state is already in ctx->inst->astate and will be continued automatically } rz_interp_abstr_state_free(ctx->inst, true_state); @@ -1323,10 +1325,12 @@ static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzIL } if (astate->pc_state != RZ_INTERP_PC_UNREACHABLE) { + bool fallthrough = false; if (astate->pc_state == RZ_INTERP_PC_CONST && astate->pc == interp_block_end + 1) { + fallthrough = true; ctx->block->fallthrough = true; } - rz_interp_run_push(ctx, ctx->astate); + rz_interp_run_push(ctx, ctx->astate, fallthrough); } return true; @@ -1357,7 +1361,7 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { rz_interp_abstr_state_free(inst, estate); goto cleanup; } - rz_interp_run_push(&ctx, estate); + rz_interp_run_push(&ctx, estate, false); rz_interp_abstr_state_free(inst, estate); // Loop and interpret until a fixpoint has been reached @@ -1485,12 +1489,46 @@ RZ_API void rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, R RzIntervalTreeIter it; RzInterpBlock *block; rz_interval_tree_foreach (&res->blocks, it, block) { + if (block->added_to_analysis) { + // has been merged into the previous already + continue; + } ut64 start = interp_block_get_start(block); - ut64 size = interp_block_get_size(block); + ut64 end_excl = interp_block_get_end(block) + 1; + + if (block->fallthrough && rz_vector_empty(&block->jump_targets)) { + // Merge consecutive blocks if there is no in-edge between them. + // Splits like this happen in the first place because interp blocks only reach until the + // first jump. This may be a call however, which just falls through. + RzIntervalTreeIter next_it = it; + while (true) { + rz_rbtree_iter_next(&next_it); + RzIntervalNode *next_node = NULL; + while (rz_rbtree_iter_has(&next_it)) { + RzIntervalNode *n = rz_interval_tree_iter_get(&next_it); + if (n->start >= end_excl) { + if (n->start == end_excl) { + next_node = n; + } + break; + } + } + RzInterpBlock *next_block; + if (!next_node || (next_block = next_node->data)->non_fallthrough_in) { + // no consecutive block or there is a consecutive block, but is has an in-edge from somewhere else + break; + } + next_block->added_to_analysis = true; + end_excl = next_node->end + 1; + block = next_block; + } + } + // TODO: add_bb should eventually not be used here since it does its own analysis. // Instead, we should create the block by hand and apply our analysis info to it. - rz_analysis_add_bb(analysis, start, size); - RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, interp_block_get_start(block)); + // Keep in mind we might have to add info from multiple merged blocks here if (see merging above) + rz_analysis_add_bb(analysis, start, end_excl - start); + RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, start); rz_analysis_function_add_block(func, abb); // TODO: check if this is a calling block (thus fallthrough) and the following block @@ -1503,7 +1541,7 @@ RZ_API void rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, R bb_add_target(abb, *target); } if (block->fallthrough) { - bb_add_target(abb, start + size); + bb_add_target(abb, end_excl); } } } From ac4c1f09d625ab082797df4c3f63af168de46aa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sat, 11 Jul 2026 14:25:32 +0200 Subject: [PATCH 316/334] Begin testing of cfg results --- librz/core/cmd/cmd_inquiry.c | 14 +- librz/include/rz_inquiry.h | 2 +- librz/include/rz_inquiry/rz_il_cache.h | 8 +- librz/include/rz_inquiry/rz_interpreter.h | 8 +- librz/inquiry/il_cache.c | 50 ++++--- librz/inquiry/inquiry.c | 18 +-- librz/inquiry/interp/interpreter.c | 37 +++--- librz/inquiry/interp/val_abs_constant.c | 4 +- test/unit/meson.build | 2 + test/unit/test_inquiry_interp.c | 152 ++++++++++++++++++++++ 10 files changed, 226 insertions(+), 69 deletions(-) create mode 100644 test/unit/test_inquiry_interp.c diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index a1505f05f4f..8af664b07b2 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -64,20 +64,14 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar rz_set_u_add(entry_points, entry_point); } } - RzVector *ignored_code_regions = get_ignored_code_regions( - rz_bin_object_get_symbols(core->bin->cur->o), - rz_bin_object_get_sections(core->bin->cur->o), - rz_io_maps(core->io)); - bool success = rz_inquiry_interpreter(core, entry_points, ignored_code_regions); + bool success = rz_inquiry_interpreter(core, entry_points); eprintf("Finished reference recovery: %s\n", success ? "OK" : "FAIL"); if (!success) { - rz_vector_free(ignored_code_regions); return RZ_CMD_STATUS_ERROR; } RzSetU *symbol_addresses = rz_set_u_new(); if (!symbol_addresses || !rz_inquiry_get_fcn_symbol_addr(core, symbol_addresses)) { - rz_vector_free(ignored_code_regions); rz_warn_if_reached(); return RZ_CMD_STATUS_ERROR; } @@ -88,12 +82,16 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar return RZ_CMD_STATUS_ERROR; } if (run_fcn_detection) { + RzVector *ignored_code_regions = get_ignored_code_regions( + rz_bin_object_get_symbols(core->bin->cur->o), + rz_bin_object_get_sections(core->bin->cur->o), + rz_io_maps(core->io)); eprintf("Perform function deduction: "); success &= rz_inquiry_function_deduction(core->analysis, core->inquiry, symbol_addresses, symbols, ignored_code_regions, fcns); + rz_vector_free(ignored_code_regions); eprintf("%s\n", success ? "OK" : "FAIL"); } rz_set_u_free(symbol_addresses); - rz_vector_free(ignored_code_regions); success &= rz_inquiry_convert_and_add_to_analysis(core->analysis, core->inquiry, fcns, symbols); rz_pvector_free(fcns); diff --git a/librz/include/rz_inquiry.h b/librz/include/rz_inquiry.h index 432cd91e61c..f6f40ed9f39 100644 --- a/librz/include/rz_inquiry.h +++ b/librz/include/rz_inquiry.h @@ -56,7 +56,7 @@ RZ_API void rz_inquiry_free(RZ_OWN RZ_NULLABLE RzInquiry *q); RZ_API bool rz_inquiry_xref_interpreter_filter(RZ_NONNULL const RzAnalysisXRef *xref, RZ_NONNULL const RzPVector /**/ *allowed_segments); -RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzSetU /**/ *entry_points, RZ_NONNULL const RzVector /**/ *ignored_code); +RZ_API bool rz_inquiry_interpreter(RzCore *core, RZ_OWN RzSetU /**/ *entry_points); RZ_API bool rz_inquiry_function_deduction( RZ_NONNULL RZ_BORROW RzAnalysis *analysis, diff --git a/librz/include/rz_inquiry/rz_il_cache.h b/librz/include/rz_inquiry/rz_il_cache.h index f7408c8676f..bcc67b13f16 100644 --- a/librz/include/rz_inquiry/rz_il_cache.h +++ b/librz/include/rz_inquiry/rz_il_cache.h @@ -78,11 +78,13 @@ RZ_API const RzVector /**/ *rz_il_cache_get_static_xrefs(const R RZ_API RzIterator /**/ *rz_il_cache_get_blocks(const RzILCache *cache); typedef struct rz_il_cache_client_t { - RzThreadRingBuf *req_rbuf; - RzThreadQueue *il_queue; + RzILCache *cache; + RzThreadRingBuf *req_rbuf; ///< used only if async + RzThreadQueue *il_queue; ///< used only if async + bool async; } RzILCacheClient; -RZ_API RZ_BORROW RzILCacheClient *rz_il_cache_new_client(RZ_NONNULL RZ_BORROW RzILCache *cache); +RZ_API RZ_BORROW RzILCacheClient *rz_il_cache_new_client(RZ_NONNULL RZ_BORROW RzILCache *cache, bool async); RZ_API RZ_NULLABLE RZ_BORROW const RzILCacheBlock *rz_il_cache_client_lift_il_block(RZ_NONNULL RZ_BORROW RzILCacheClient *client, ut64 addr); RZ_API bool rz_il_cache_was_requested( diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index d9687bfa5df..c460a12d1b8 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -288,7 +288,7 @@ struct rz_interp_instance_t { * \brief The ring buffers to push the yield of interpretation into. * These ring buffers are shared with other interpreter sets. */ - RZ_BORROW RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM]; + RZ_BORROW RZ_NULLABLE RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM]; RzPVector /**/ results; ///< TODO: replace this by a queue/rbuf/... to handle interp and results concurrently @@ -327,8 +327,7 @@ RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( RzAnalysis *analysis, RZ_NONNULL RZ_OWN RzInterpValueAbstraction *plugin, RZ_NONNULL RZ_BORROW RzILCacheClient *il_cache_client, - RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM], - RZ_NONNULL const RzVector /**/ *ignored_code); + RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM]); RZ_API void rz_interp_instance_free(RZ_OWN RZ_NULLABLE RzInterpInstance *iset); /** @@ -365,7 +364,10 @@ typedef struct rz_interp_run_result_t { */ RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as, bool is_fallthrough); +RZ_API bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point); RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *iset); RZ_API void rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, RZ_NONNULL RzAnalysis *analysis); +extern RZ_API RzInterpValueAbstraction rz_interp_value_domain_const; + #endif // RZ_INTERPRETER diff --git a/librz/inquiry/il_cache.c b/librz/inquiry/il_cache.c index 885a5b03224..cef55ed70e0 100644 --- a/librz/inquiry/il_cache.c +++ b/librz/inquiry/il_cache.c @@ -30,7 +30,7 @@ struct rz_il_cache_t { RZ_BORROW RzAnalysis *analysis; RZ_BORROW RzIO *io; - RzPVector /**/ *bin_sections; + RZ_NULLABLE RzPVector /**/ *bin_sections; size_t n_serving; ///< Number of caches serving currently. RzThreadLock *n_serving_lock; @@ -313,8 +313,10 @@ RZ_API void rz_il_cache_close(RZ_BORROW RZ_NONNULL RzILCache *cache) { for (size_t i = 0; i < rz_pvector_len(&cache->clients); ++i) { RzILCacheClient *client = rz_pvector_at(&cache->clients, i); - rz_th_ring_buf_close(client->req_rbuf); - rz_th_queue_close(client->il_queue); + if (client->async) { + rz_th_ring_buf_close(client->req_rbuf); + rz_th_queue_close(client->il_queue); + } } } @@ -375,9 +377,9 @@ static void rz_il_cache_client_free(void *ptr) { RZ_API RZ_OWN RzILCache *rz_il_cache_new( RZ_BORROW RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, - RZ_OWN RZ_NONNULL RzPVector /**/ *bin_sections, + RZ_OWN RZ_NULLABLE RzPVector /**/ *bin_sections, RzILCacheConfig config) { - rz_return_val_if_fail(analysis && io && bin_sections, NULL); + rz_return_val_if_fail(analysis && io, NULL); RzILCache *cache = RZ_NEW0(RzILCache); if (!cache) { rz_warn_if_reached(); @@ -438,32 +440,42 @@ RZ_API bool rz_il_cache_was_requested( return r; } -RZ_API RZ_BORROW RzILCacheClient *rz_il_cache_new_client(RZ_NONNULL RZ_BORROW RzILCache *cache) { +/** + * \brief Create a new client to access the IL cache + * \param async If false, the client will directly call into the IL cache, for single-threaded applications only. + * If true, the client requires another thread to run rz_il_cache_serve() when it requests data, for concurrent applications. + */ +RZ_API RZ_BORROW RzILCacheClient *rz_il_cache_new_client(RZ_NONNULL RZ_BORROW RzILCache *cache, bool async) { rz_return_val_if_fail(cache, false); - - // The queue to pass the Effects to the interpreter. - RzThreadQueue *il_queue = rz_th_queue_new(RZ_IL_OPS_CACHE_IL_QUEUE_SIZE, NULL); - // The ring buffer the interpreter can request new Effects over. - RzThreadRingBuf *request_rbuf = rz_th_ring_buf_new(RZ_IL_OPS_CACHE_ADDR_RBUF_SIZE, sizeof(ut64)); - if (!il_queue || !request_rbuf) { - goto error_free; - } RzILCacheClient *client = RZ_NEW0(RzILCacheClient); if (!client) { - goto error_free; + return NULL; + } + client->cache = cache; + if (async) { + client->async = true; + // The queue to pass the Effects to the interpreter. + client->il_queue = rz_th_queue_new(RZ_IL_OPS_CACHE_IL_QUEUE_SIZE, NULL); + // The ring buffer the interpreter can request new Effects over. + client->req_rbuf = rz_th_ring_buf_new(RZ_IL_OPS_CACHE_ADDR_RBUF_SIZE, sizeof(ut64)); + if (!client->il_queue || !client->req_rbuf) { + goto error_free; + } } - client->il_queue = il_queue; - client->req_rbuf = request_rbuf; rz_pvector_push(&cache->clients, client); return client; error_free: - rz_th_queue_free(il_queue); - rz_th_ring_buf_free(request_rbuf); + rz_th_queue_free(client->il_queue); + rz_th_ring_buf_free(client->req_rbuf); + free(client); return NULL; } RZ_API RZ_NULLABLE RZ_BORROW const RzILCacheBlock *rz_il_cache_client_lift_il_block(RZ_NONNULL RZ_BORROW RzILCacheClient *client, ut64 addr) { rz_return_val_if_fail(client, NULL); + if (!client->async) { + return lift_il_block(client->cache, addr); + } if (rz_th_ring_buf_put(client->req_rbuf, &addr) != RZ_THREAD_RING_BUF_OK) { // Can't request IL block => Cache closed => Terminate return NULL; diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 1ddb3185552..b6d2d303604 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -484,15 +484,12 @@ struct ituple { RzInterpRunStateFlag next_run_state; }; -RZ_API extern RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype; - /** * A function to call the prototype interpreter. * Usually these tasks will be split between different caches and yield consumers. */ RZ_API bool rz_inquiry_interpreter(RzCore *core, - RZ_OWN RzSetU *entry_points, - RZ_NONNULL const RzVector /**/ *ignored_code) { + RZ_OWN RzSetU *entry_points) { // All the things we need bool return_code = true; RzInterpInstance *inst = NULL; @@ -531,12 +528,6 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // Later we would pass a unique iset to each interpreter with // the required queues only. // But for the prototype we have only one iset with all queues. - RzInquiryPlugin *prototype = &rz_inquiry_plugin_interpreter_prototype; // ht_sp_find(core->inquiry->plugins, "abstr_int_prototype", NULL); - if (!prototype) { - return_code = false; - rz_warn_if_reached(); - goto error_free; - } size_t n_threads = 1; iset_map = RZ_NEWS0(struct ituple, n_threads); @@ -552,7 +543,7 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, // Initialize and spawn the interpreters. // for (size_t i = 0; i < n_threads; ++i) { - RzILCacheClient *cache_client = rz_il_cache_new_client(il_cache); + RzILCacheClient *cache_client = rz_il_cache_new_client(il_cache, true); if (!cache_client) { return_code = false; rz_warn_if_reached(); @@ -560,10 +551,9 @@ RZ_API bool rz_inquiry_interpreter(RzCore *core, } inst = rz_interp_instance_new( core->analysis, - prototype->value_abstraction, + &rz_interp_value_domain_const, cache_client, - yield_rbufs, - ignored_code); + yield_rbufs); if (!inst) { return_code = false; rz_warn_if_reached(); diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 50994653a0f..51c360395b5 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -486,9 +486,8 @@ RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( RzAnalysis *analysis, RZ_NONNULL RZ_OWN RzInterpValueAbstraction *plugin, RZ_NONNULL RZ_BORROW RzILCacheClient *il_cache_client, - RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM], - RZ_NONNULL const RzVector /**/ *ignored_code) { - rz_return_val_if_fail(plugin && ignored_code && analysis && il_cache_client, NULL); + RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM]) { + rz_return_val_if_fail(plugin && analysis && il_cache_client, NULL); RzInterpInstance *inst = RZ_NEW0(RzInterpInstance); if (!inst) { @@ -532,14 +531,15 @@ RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( inst->il_cache_client = il_cache_client; inst->entry_points = entry_points; - inst->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] = yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; - inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] = yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; - inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] = yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW]; + if (yield_rbufs) { + inst->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] = yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; + inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] = yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; + inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] = yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW]; + } rz_pvector_init(&inst->results, NULL); inst->io_request_rbuf = io_request_rbuf; inst->io_result_rbuf = io_result_rbuf; inst->run_state_sync = rz_th_sem_new(0); - inst->ignored_code = ignored_code; return inst; err_var_name_hashes: @@ -581,6 +581,11 @@ bool report_yield_xref( ut64 from, const RzInterpAbstrVal *to, RzAnalysisXRefType type) { + RzInterpYieldRBuf *yrbuf = ctx->inst->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; + if (!yrbuf) { + return true; + } + RzBitVector to_bv; rz_bv_init(&to_bv, 64); bool success = true; @@ -601,9 +606,6 @@ bool report_yield_xref( goto cleanup; } - RzInterpYieldRBuf *yrbuf = ctx->inst->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; - rz_return_val_if_fail(yrbuf, false); - ut64 to_addr = rz_bv_to_ut64(&to_bv); RzAnalysisXRef xref = { 0 }; xref.bb_addr = 0; // TODO? ctx->astate->bb_addr; @@ -628,7 +630,9 @@ bool report_yield_xref( static bool report_yield_call_candiate( RzInterpRunContext *ctx) { RzInterpYieldRBuf *cc_rbuf = ctx->inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; - rz_return_val_if_fail(cc_rbuf, false); + if (!cc_rbuf) { + return true; + } RzAnalysisCallCandidate cc = { 0 }; // TODO? put the bb addr into the call candidate? Currently we do not know it. @@ -1339,7 +1343,9 @@ static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzIL /** * \brief Run the interpreter from a single entrypoint until a fixpoint is reached */ -static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { +RZ_API bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { + rz_return_val_if_fail(inst, false); + // Initialization bool success = false; RzInterpRunContext ctx = { @@ -1423,9 +1429,6 @@ static bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *inst) { rz_return_val_if_fail(inst && inst->il_cache_client && - inst->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] && - inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] && - inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] && inst->run_state_sync && inst->plugin, false); @@ -1530,10 +1533,6 @@ RZ_API void rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, R rz_analysis_add_bb(analysis, start, end_excl - start); RzAnalysisBlock *abb = rz_analysis_get_block_at(analysis, start); rz_analysis_function_add_block(func, abb); - - // TODO: check if this is a calling block (thus fallthrough) and the following block - // has no in-edge from somewhere else. If so, merge them. - abb->jump = UT64_MAX; abb->fail = UT64_MAX; ut64 *target; diff --git a/librz/inquiry/interp/val_abs_constant.c b/librz/inquiry/interp/val_abs_constant.c index db0b63ab137..3503d8ec085 100644 --- a/librz/inquiry/interp/val_abs_constant.c +++ b/librz/inquiry/interp/val_abs_constant.c @@ -234,7 +234,7 @@ static void eval_unop(RzILOpPureCode code, RZ_NONNULL RZ_INOUT RzInterpAbstrVal } } -static RzInterpValueAbstraction rz_interpreter_plugin_prototype = { +RZ_API RzInterpValueAbstraction rz_interp_value_domain_const = { .name = "constant", .val_new_top = val_new_top, .val_free = val_free, @@ -259,7 +259,7 @@ RZ_API RzInquiryPlugin rz_inquiry_plugin_interpreter_prototype = { .version = "0.1p", .desc = "A prototype interpreter for constant/top abstractions.", .license = "LGPL-3.0-only", - .value_abstraction = &rz_interpreter_plugin_prototype, + .value_abstraction = &rz_interp_value_domain_const, }; #ifndef RZ_PLUGIN_INCORE diff --git a/test/unit/meson.build b/test/unit/meson.build index 30fb1f70c27..c7deb55eb66 100644 --- a/test/unit/meson.build +++ b/test/unit/meson.build @@ -87,6 +87,7 @@ if get_option('enable_tests') 'il_vm', 'il_helpers', 'intervaltree', + 'inquiry_interp', 'io', 'io_ihex', 'itv', @@ -161,6 +162,7 @@ if get_option('enable_tests') rz_main_dep, rz_socket_dep, rz_core_dep, + rz_inquiry_dep, rz_io_dep, rz_bin_dep, rz_flag_dep, diff --git a/test/unit/test_inquiry_interp.c b/test/unit/test_inquiry_interp.c new file mode 100644 index 00000000000..7e9e2e00791 --- /dev/null +++ b/test/unit/test_inquiry_interp.c @@ -0,0 +1,152 @@ +// SPDX-FileCopyrightText: 2026 Florian Märkl +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "minunit.h" + +typedef struct test_interp_t { + RzAnalysis *analysis; + RzIO *io; + RzILCache *il_cache; + RzILCacheClient *il_cache_client; + RzInterpInstance *inst; +} TestInterp; + +static TestInterp *interp_new(const char *arch, int bits, ut64 baddr, const char *url) { + // for debugging, uncomment: + // eprintf("rz -a %s -b %d -m 0x%" PFMT64x " %s\n", arch, bits, baddr, url); + TestInterp *interp = RZ_NEW(TestInterp); + interp->analysis = rz_analysis_new(NULL); + rz_analysis_use(interp->analysis, arch); + rz_analysis_set_bits(interp->analysis, bits); + interp->io = rz_io_new(); + interp->io->va = 1; + interp->il_cache = rz_il_cache_new(interp->analysis, interp->io, NULL, RZ_IL_CACHE_CONFIG_NOP_UNLIFTED | RZ_IL_CACHE_CONFIG_NO_SLEEP); + interp->il_cache_client = rz_il_cache_new_client(interp->il_cache, false); + interp->inst = rz_interp_instance_new(interp->analysis, &rz_interp_value_domain_const, interp->il_cache_client, NULL); + RzIODesc *desc = rz_io_open_at(interp->io, url, RZ_PERM_RX, 0644, baddr, NULL); + if (!desc) { + mu_perror("load code"); + return NULL; + } + return interp; +} + +static void interp_free(TestInterp *interp) { + rz_interp_instance_free(interp->inst); + rz_il_cache_free(interp->il_cache); + rz_io_free(interp->io); + rz_analysis_free(interp->analysis); + free(interp); +} + +static size_t do_extract_blocks(RzInterpResult *res, RzInterpBlock *blocks[], size_t count) { + size_t r = 0; + RzIntervalTreeIter it; + RzInterpBlock *block; + rz_interval_tree_foreach(&res->blocks, it, block) { + if (r < count) { + blocks[r] = block; + } + r++; + } + return r; +} + +static ut64 block_start(RzInterpBlock *block) { + return block->entry_state->pc; +} + +static ut64 block_end(RzInterpBlock *block) { + return block->node->end + 1; +} + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +/** Extract single result from interp as well as its blocks into local vars for easy assertion */ +#define EXTRACT_RESULT(interp, blocks_count) \ + mu_assert_eq(rz_pvector_len(&interp->inst->results), 1, "results len"); \ + RzInterpResult *res = rz_pvector_at(&interp->inst->results, 0); \ + RzInterpBlock *blocks[blocks_count]; \ + mu_assert_eq(do_extract_blocks(res, blocks, blocks_count), blocks_count, "blocks count") + +#define ASSERT_BLOCK_BOUNDS(i, start, end) do { \ + mu_assert_eq(block_start(blocks[i]), start, "block " STR(i) " start"); \ + mu_assert_eq(block_end(blocks[i]), end, "block " STR(i) " end"); \ + } while (0); + +bool test_interp_cfg_single_block(void) { + TestInterp *interp = interp_new("arm", 64, 0x10000, "hex://" + "600880d2" // 0x00 mov x0, 0x43 + "c0035fd6" // 0x04 ret + ); + mu_assert_notnull(interp, "init"); + bool succ = rz_interp_run(interp->inst, 0x10000); + mu_assert_true(succ, "run success"); + + EXTRACT_RESULT(interp, 1); + mu_assert_eq(res->entry, 0x10000, "result entry"); + ASSERT_BLOCK_BOUNDS(0, 0x10000, 0x10008); + + interp_free(interp); + mu_end; +} + +bool test_interp_cfg_direct_jmp(void) { + TestInterp *interp = interp_new("arm", 64, 0x10000, "hex://" + "600880d2" // 0x00 mov x0, 0x43 + "03000014" // 0x04 b 0x10 --- + "1f2003d5" // 0x08 nop | + "1f2003d5" // 0x0c nop | + "400880d2" // 0x10 mov x0, 0x42 <-- + "c0035fd6" // 0x14 ret + ); + mu_assert_notnull(interp, "init"); + bool succ = rz_interp_run(interp->inst, 0x10000); + mu_assert_true(succ, "run success"); + + EXTRACT_RESULT(interp, 2); + mu_assert_eq(res->entry, 0x10000, "result entry"); + ASSERT_BLOCK_BOUNDS(0, 0x10000, 0x10008); + ASSERT_BLOCK_BOUNDS(1, 0x10010, 0x10018); + + interp_free(interp); + mu_end; +} + +bool test_interp_cfg_branch_join(void) { + TestInterp *interp = interp_new("arm", 64, 0x10000, "hex://" + "1f8c04f1" // 0x00 cmp x0, 0x123 + "69000054" // 0x04 b.ls 0x10 --- + // | + "600880d2" // 0x08 mov x0, 0x43 | + "02000014" // 0x0c b 0x14 -- | -- + // | | + "400880d2" // 0x10 mov x0, 0x42 <-- | + // | + "c0035fd6" // 0x14 ret <------ + ); + mu_assert_notnull(interp, "init"); + bool succ = rz_interp_run(interp->inst, 0x10000); + mu_assert_true(succ, "run success"); + + EXTRACT_RESULT(interp, 4); + mu_assert_eq(res->entry, 0x10000, "result entry"); + ASSERT_BLOCK_BOUNDS(0, 0x10000, 0x10008); + ASSERT_BLOCK_BOUNDS(1, 0x10008, 0x10010); + ASSERT_BLOCK_BOUNDS(2, 0x10010, 0x10014); + ASSERT_BLOCK_BOUNDS(3, 0x10014, 0x10018); + + interp_free(interp); + mu_end; +} + +bool all_tests() { + mu_run_test(test_interp_cfg_single_block); + mu_run_test(test_interp_cfg_direct_jmp); + mu_run_test(test_interp_cfg_branch_join); + return tests_passed != tests_run; +} + +mu_main(all_tests) From 2af6ee360d50e598b67e799a87d7125b699f5a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sun, 12 Jul 2026 16:00:05 +0200 Subject: [PATCH 317/334] Split blocks --- librz/include/rz_inquiry/rz_interpreter.h | 17 +- librz/inquiry/README.md | 6 + librz/inquiry/interp/interp_priv.h | 32 ++ librz/inquiry/interp/interpreter.c | 191 +++++++--- test/unit/test_inquiry_interp.c | 435 +++++++++++++++++++++- 5 files changed, 606 insertions(+), 75 deletions(-) create mode 100644 librz/inquiry/interp/interp_priv.h diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index c460a12d1b8..2ad8e8e326a 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -126,7 +126,7 @@ typedef struct { */ RzInterpAbstrState *entry_state; // TODO: flatten to remove indirection RzVector /**/ insn_offsets; ///< starting at the second instruction in the block (since first is always 0), offsets from the start of the block - bool insns_resolved; ///< Set to true once instruction_offsets and node->end are filled. + bool bounds_resolved; ///< Set to true once insn_offsets and node->end are filled. bool uninterpreted; ///< True if the entry state has not yet been started to interpret, i.e. the block is part of RzInterpFunctionState.queue bool non_fallthrough_in; ///< True if there is an in-edge to this block that is not only a fallthrough. Used when merging consecutive blocks after interpretation. bool added_to_analysis; ///< Only used after interpretation, when adding to analysis. Marks blocks that have been merged with the previous. @@ -312,13 +312,6 @@ RZ_IPI void rz_interp_run_state_set(RZ_BORROW RZ_NONNULL RzInterpRunState *state RZ_API void rz_interp_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yield_rbuf); -RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_new( - RZ_NONNULL RzInterpInstance *inst); -RZ_API void rz_interp_abstr_state_free(RzInterpInstance *inst, RZ_OWN RZ_NULLABLE RzInterpAbstrState *state); -RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_clone(RZ_NONNULL RzInterpInstance *iset, const RzInterpAbstrState *state); -RZ_API bool rz_interp_abstr_state_as_str(RZ_NONNULL RzInterpInstance *inst, RZ_NONNULL const RzInterpAbstrState *state, RZ_NONNULL RZ_OUT RzStrBuf *sb); -RZ_API void rz_interp_abstr_state_as_str_short(RZ_NONNULL RzInterpInstance *inst, RZ_NONNULL const RzInterpAbstrState *astate, RZ_NONNULL RZ_OUT RzStrBuf *sb); - RZ_API RZ_OWN RzInterpYieldRBuf *rz_interp_yield_rbuf_new(RzInterpYieldKind kind, RzInterpYieldFilter filter, RZ_OWN RZ_NULLABLE void *filter_data); @@ -356,14 +349,6 @@ typedef struct rz_interp_run_result_t { RzIntervalTree /**/ blocks; } RzInterpResult; -/* - * \brief Register a newly discovered state - * - * This will join the state with the already known one at the same pc and add it to the - * queue for further interpretation if there were changes. - */ -RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as, bool is_fallthrough); - RZ_API bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point); RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *iset); RZ_API void rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, RZ_NONNULL RzAnalysis *analysis); diff --git a/librz/inquiry/README.md b/librz/inquiry/README.md index ddc4c868b13..478a07238d9 100644 --- a/librz/inquiry/README.md +++ b/librz/inquiry/README.md @@ -5,6 +5,12 @@ Module implementing basic and advanced binary analysis. +## TODO + +* Finish and test cfg recovery for all cases +* Also make sure to test fallthrough and jump target fields of interp blocks +* Add final pass for analysis and integrate xrefs and other yield info in result + ## Interpreter Notes Ideas for optimization (do not implement any of these without profiling first): diff --git a/librz/inquiry/interp/interp_priv.h b/librz/inquiry/interp/interp_priv.h new file mode 100644 index 00000000000..4fff938f500 --- /dev/null +++ b/librz/inquiry/interp/interp_priv.h @@ -0,0 +1,32 @@ + +#ifndef RZ_INTERP_PRIV_H +#define RZ_INTERP_PRIV_H + +#include + +// These functions are actually internal, but need RZ_API to be called from unit tests +RZ_API bool rz_interp_run_context_init(RzInterpRunContext *ctx, RzInterpInstance *inst); +RZ_API void rz_interp_run_context_fini(RzInterpRunContext *ctx); +RZ_API RzInterpBlock *rz_interp_block_at(RzInterpRunContext *ctx, ut64 addr); +RZ_API void rz_interp_block_resolve_bounds(RzInterpRunContext *ctx, RzInterpBlock *interp_block, const RzILCacheBlock *il_block); + +static inline ut64 rz_interp_block_get_start(RzInterpBlock *block) { + return block->entry_state->pc; +} + +/** end is inclusive */ +static inline ut64 rz_interp_block_get_end(RzInterpBlock *block) { + return block->node->end; +} + +RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as, bool is_fallthrough); + +RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_new( + RZ_NONNULL RzInterpInstance *inst); +RZ_API void rz_interp_abstr_state_free(RzInterpInstance *inst, RZ_OWN RZ_NULLABLE RzInterpAbstrState *state); +RZ_API void rz_interp_abstr_state_set_pc_const(RzInterpAbstrState *state, ut64 pc); +RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_clone(RZ_NONNULL RzInterpInstance *iset, const RzInterpAbstrState *state); +RZ_API bool rz_interp_abstr_state_as_str(RZ_NONNULL RzInterpInstance *inst, RZ_NONNULL const RzInterpAbstrState *state, RZ_NONNULL RZ_OUT RzStrBuf *sb); +RZ_API void rz_interp_abstr_state_as_str_short(RZ_NONNULL RzInterpInstance *inst, RZ_NONNULL const RzInterpAbstrState *astate, RZ_NONNULL RZ_OUT RzStrBuf *sb); + +#endif diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 51c360395b5..3a83c50cc16 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -9,7 +9,7 @@ #include "rz_util/rz_assert.h" #include "rz_util/rz_log.h" #include -#include +#include "interp_priv.h" #include #include #include @@ -79,6 +79,12 @@ RZ_API void rz_interp_abstr_state_free(RzInterpInstance *inst, RZ_OWN RZ_NULLABL free(state); } +RZ_API void rz_interp_abstr_state_set_pc_const(RzInterpAbstrState *state, ut64 pc) { + rz_return_if_fail(state); + state->pc = pc; + state->pc_state = RZ_INTERP_PC_CONST; +} + static bool reset_state(RzInterpInstance *inst, RZ_BORROW RzInterpAbstrState *state, ut64 entry_point) { state->pc_state = RZ_INTERP_PC_CONST; state->pc = entry_point; @@ -266,15 +272,6 @@ static RzInterpBlock *interp_block_create(RzInterpRunContext *ctx, RZ_BORROW RZ_ return block; } -static ut64 interp_block_get_start(RzInterpBlock *block) { - return block->entry_state->pc; -} - -/** end is inclusive */ -static ut64 interp_block_get_end(RzInterpBlock *block) { - return block->node->end; -} - static void interp_block_add_non_fallthrough_target(RzInterpBlock *block, ut64 target) { // linear search may be inefficient, but practically the number of targets is often small if (rz_vector_contains(&block->jump_targets, &target)) { @@ -283,10 +280,60 @@ static void interp_block_add_non_fallthrough_target(RzInterpBlock *block, ut64 t rz_vector_push(&block->jump_targets, &target); } -static RzInterpBlock *interp_block_at(RzInterpRunContext *ctx, ut64 addr) { +RZ_API RzInterpBlock *rz_interp_block_at(RzInterpRunContext *ctx, ut64 addr) { return rz_interval_tree_at(&ctx->blocks, addr); } +/** Mark a block that its current entry_state has not been explored fully yet */ +static void interp_block_mark_uninterpreted(RzInterpRunContext *ctx, RzInterpBlock *block) { + if (block->uninterpreted) { + return; + } + block->uninterpreted = true; + rz_list_push(ctx->queue, block); +} + +typedef struct interp_block_with_op_at_ctx_t { + ut64 addr; + RzInterpBlock *found; + size_t *hit_op_idx; +} InterpBlockWithOpAtCtx; + +static int interp_block_with_op_at_cmp(const void *a, const void *b, void *user) { + const ut16 *av = a; + const ut16 *bv = b; + if (*av > *bv) { + return 1; + } else if (*av < *bv) { + return -1; + } + return 0; +} + +static bool interp_block_with_op_at_cb(RzIntervalNode *node, void *user) { + InterpBlockWithOpAtCtx *lctx = user; + RzInterpBlock *block = node->data; + ut16 off = (ut16)(lctx->addr - rz_interp_block_get_start(block)); + // insn_offsets does not contain the first instruction, which is just fine here since we are not looking for that + size_t hit_op_idx = rz_vector_find_sorted(&block->insn_offsets, &off, interp_block_with_op_at_cmp, NULL); + if (hit_op_idx != SZT_MAX) { + lctx->found = block; + *lctx->hit_op_idx = hit_op_idx + 1; // + 1 because insn_offsets omits the first + return false; + } + return true; +} + +static RzInterpBlock *interp_block_with_op_at(RzInterpRunContext *ctx, ut64 addr, size_t *hit_op_idx) { + InterpBlockWithOpAtCtx lctx = { + .addr = addr, + .found = NULL, + .hit_op_idx = hit_op_idx + }; + rz_interval_tree_all_in(&ctx->blocks, addr, true, interp_block_with_op_at_cb, &lctx); + return lctx.found; +} + static int interp_block_addr_cmp(const void *incoming, const RBNode *in_tree, void *user) { ut64 incoming_start = *(ut64 *)incoming; ut64 other_start = container_of(in_tree, const RzIntervalNode, node)->start; @@ -299,16 +346,59 @@ static int interp_block_addr_cmp(const void *incoming, const RBNode *in_tree, vo return 0; } +static void interp_block_resize(RzInterpRunContext *ctx, RzInterpBlock *block, ut64 new_end) { + // Warning: the resize operation may invalidate the node pointer! But in reality, it only does so + // if the start address has changed, so it is ok to leave the reference in interp_block->node as-is. + rz_interval_tree_resize(&ctx->blocks, block->node, rz_interp_block_get_start(block), new_end); +} + /** * Resize the block to cover the instructions, or until the following block * and fill instruction offsets. + * It may also split another block if \p interp_block starts at one of its instruction addresses. */ -static void interp_block_apply_instructions(RzInterpRunContext *ctx, RzInterpBlock *interp_block, const RzILCacheBlock *il_block) { - if (interp_block->insns_resolved) { +RZ_API void rz_interp_block_resolve_bounds(RzInterpRunContext *ctx, RzInterpBlock *interp_block, const RzILCacheBlock *il_block) { + if (interp_block->bounds_resolved) { + return; + } + interp_block->bounds_resolved = true; + ut64 block_start = rz_interp_block_get_start(interp_block); + + // Blocks may overlap, but one block must not start at an instruction start of another. + // We have to consider two cases here, depending on the order in which blocks have been discovered. + + // Case A: Our block would start at an instruction start of another block that starts before us and falls through. + // We can move the instruction information from the preceding block in that case. This way, case B is already handled as well. + size_t hit_op_idx = 0; + RzInterpBlock *preceding = interp_block_with_op_at(ctx, rz_interp_block_get_start(interp_block), &hit_op_idx); + if (preceding) { + size_t total_ops_count = rz_vector_len(&preceding->insn_offsets) + 1; + size_t our_ops_count = total_ops_count - hit_op_idx; + rz_vector_reserve(&interp_block->insn_offsets, our_ops_count - 1); + for (size_t i = hit_op_idx + 1; i < total_ops_count; i++) { + ut64 addr = *(ut16 *)rz_vector_index_ptr(&preceding->insn_offsets, i - 1); + addr += rz_interp_block_get_start(preceding); + addr -= block_start; + ut16 off = (ut16)addr; + rz_vector_push(&interp_block->insn_offsets, &off); + } + rz_vector_remove_range(&preceding->insn_offsets, hit_op_idx - 1, rz_vector_len(&preceding->insn_offsets) - (hit_op_idx -1), NULL); + rz_vector_shrink(&preceding->insn_offsets); + interp_block_resize(ctx, interp_block, rz_interp_block_get_end(preceding)); + interp_block_resize(ctx, preceding, block_start - 1); + preceding->fallthrough = true; + rz_vector_fini(&interp_block->jump_targets); + memmove(&interp_block->jump_targets, &preceding->jump_targets, sizeof(interp_block->jump_targets)); + rz_vector_init(&preceding->jump_targets, interp_block->jump_targets.elem_size, interp_block->jump_targets.free, interp_block->jump_targets.free_user); + + // The state reachable from the preceding block reaching our start address must be joined into our block's entry state. + // Hint: For performance, it would actually be better to reinterpret the preceding block before our block, otherwise + // ours will likely be interperted twice. + interp_block_mark_uninterpreted(ctx, preceding); return; } - interp_block->insns_resolved = true; - // Blocks may overlap, but another block must not start at one of our instruction addresses. + + // Case B: Our block will fall through until one instruction start hits exactly another existing block. // So we close our block once any of our instructions hit exactly the start of another block. // (There is also the case where two non-start instructions hit, but we ignore this for now since results will // still be correct) @@ -318,7 +408,6 @@ static void interp_block_apply_instructions(RzInterpRunContext *ctx, RzInterpBlo ut64 search_next_addr = interp_block->node->start + 1; RBIter next_it = rz_rbtree_lower_bound_forward(&ctx->blocks.root->node, &search_next_addr, interp_block_addr_cmp, NULL); - ut64 block_start = interp_block->node->start; size_t insns_count = rz_pvector_len(il_block->il_ops); rz_return_if_fail(insns_count > 0); // interp_block->instruction_offsets is assumed to be empty here @@ -345,11 +434,15 @@ static void interp_block_apply_instructions(RzInterpRunContext *ctx, RzInterpBlo cur += insn->insn_pkt_size; } close: - // Warning: the resize operation may invalidate the node pointer! But in reality, it only does so - // if the start address has changed, so it is ok to leave the reference in interp_block->node as-is. - rz_interval_tree_resize(&ctx->blocks, interp_block->node, block_start, cur - 1); + interp_block_resize(ctx, interp_block, cur - 1); } +/* + * \brief Register a newly discovered state + * + * This will join the state with the already known one at the same pc and add it to the + * queue for further interpretation if there were changes. + */ RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as, bool is_fallthrough) { if (as->pc_state == RZ_INTERP_PC_ANY) { RZ_LOG_DEBUG("Encountered state with unknown/top pc\n"); @@ -364,19 +457,17 @@ RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_ rz_interp_abstr_state_as_str_short(ctx->inst, as, &sb); RZ_LOG_DEBUG("PUSH 0x%" PFMT64x ": %s\n", as->pc, rz_strbuf_get(&sb)); rz_strbuf_fini(&sb); - RzInterpBlock *block = interp_block_at(ctx, as->pc); + RzInterpBlock *block = rz_interp_block_at(ctx, as->pc); if (block) { - if (join_state(ctx->inst, block->entry_state, as) && !block->uninterpreted) { - block->uninterpreted = true; - rz_list_push(ctx->queue, block); + if (join_state(ctx->inst, block->entry_state, as)) { + interp_block_mark_uninterpreted(ctx, block); } } else { block = interp_block_create(ctx, as); if (!block) { return; } - block->uninterpreted = true; - rz_list_push(ctx->queue, block); + interp_block_mark_uninterpreted(ctx, block); } if (!is_fallthrough) { block->non_fallthrough_in = true; @@ -1183,7 +1274,7 @@ static bool eval_effect(RzInterpRunContext *ctx, } else { // different jump targets, branch rather than resorting to top pc rz_interp_run_push(ctx, true_state, true_state->pc_state == RZ_INTERP_PC_CONST && true_state->pc == fallthrough_pc); - if (true_state->pc_state == RZ_INTERP_PC_CONST) { + if (true_state->pc_state == RZ_INTERP_PC_CONST && !rz_vector_contains(&ctx->block->jump_targets, &true_state->pc)) { rz_vector_push(&ctx->block->jump_targets, &true_state->pc); } // false_state is already in ctx->inst->astate and will be continued automatically @@ -1265,19 +1356,11 @@ static bool eval_effect(RzInterpRunContext *ctx, return false; } -static bool set_pc(RzInterpAbstrState *state, ut64 pc) { - rz_return_val_if_fail(state, false); - state->pc = pc; - state->pc_state = RZ_INTERP_PC_CONST; - RZ_LOG_DEBUG("prototype: set_pc() - Set PC: 0x%" PFMT64x " (Constant)\n", pc); - return true; -} - static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzILCacheBlock *il_bb) { // Reset call candidate tracking for each basic block. memset(&ctx->call_cand, 0, sizeof(ctx->call_cand)); - ut64 interp_block_end = interp_block_get_end(ctx->block); + ut64 interp_block_end = rz_interp_block_get_end(ctx->block); // Now execute the actual effects of the BLOCK. RzInterpAbstrState *astate = ctx->astate; @@ -1312,7 +1395,7 @@ static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzIL // Prepare next pc, the evalutation may overwrite this. ut64 next_pc = pc + pkt->insn_pkt_size; - set_pc(ctx->astate, next_pc); + rz_interp_abstr_state_set_pc_const(ctx->astate, next_pc); if (!eval_effect(ctx, pkt->effect, pkt->insn_pkt_size)) { return false; @@ -1340,6 +1423,22 @@ static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzIL return true; } +RZ_API bool rz_interp_run_context_init(RzInterpRunContext *ctx, RzInterpInstance *inst) { + ctx->inst = inst; + ctx->astate = NULL; + ctx->queue = rz_list_new(); + if (!ctx->queue) { + return false; + } + interp_blocks_init(ctx); + return true; +} + +RZ_API void rz_interp_run_context_fini(RzInterpRunContext *ctx) { + rz_list_free(ctx->queue); + interp_blocks_free(ctx); +} + /** * \brief Run the interpreter from a single entrypoint until a fixpoint is reached */ @@ -1348,15 +1447,10 @@ RZ_API bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { // Initialization bool success = false; - RzInterpRunContext ctx = { - .inst = inst, - .astate = NULL, - .queue = rz_list_new() - }; - if (!ctx.queue) { - goto cleanup; + RzInterpRunContext ctx; + if (!rz_interp_run_context_init(&ctx, inst)) { + return false; } - interp_blocks_init(&ctx); // Prepare the initial state from the given entry point // Hint: nothing speaks against supporting multiple entry points in a single run @@ -1387,7 +1481,7 @@ RZ_API bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { break; } - interp_block_apply_instructions(&ctx, interp_block, il_block); + rz_interp_block_resolve_bounds(&ctx, interp_block, il_block); // DEBUG comments RzStrBuf sb; @@ -1418,8 +1512,7 @@ RZ_API bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { rz_pvector_push(&inst->results, res); cleanup: - rz_list_free(ctx.queue); - interp_blocks_free(&ctx); + rz_interp_run_context_fini(&ctx); return success; } @@ -1496,8 +1589,8 @@ RZ_API void rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, R // has been merged into the previous already continue; } - ut64 start = interp_block_get_start(block); - ut64 end_excl = interp_block_get_end(block) + 1; + ut64 start = rz_interp_block_get_start(block); + ut64 end_excl = rz_interp_block_get_end(block) + 1; if (block->fallthrough && rz_vector_empty(&block->jump_targets)) { // Merge consecutive blocks if there is no in-edge between them. diff --git a/test/unit/test_inquiry_interp.c b/test/unit/test_inquiry_interp.c index 7e9e2e00791..5dfd590d026 100644 --- a/test/unit/test_inquiry_interp.c +++ b/test/unit/test_inquiry_interp.c @@ -1,7 +1,8 @@ // SPDX-FileCopyrightText: 2026 Florian Märkl // SPDX-License-Identifier: LGPL-3.0-only -#include +#include "../../librz/inquiry/interp/interp_priv.h" + #include "minunit.h" typedef struct test_interp_t { @@ -71,10 +72,218 @@ static ut64 block_end(RzInterpBlock *block) { RzInterpBlock *blocks[blocks_count]; \ mu_assert_eq(do_extract_blocks(res, blocks, blocks_count), blocks_count, "blocks count") -#define ASSERT_BLOCK_BOUNDS(i, start, end) do { \ +#define ASSERT_BLOCK(i, start, end, is_fallthrough, jump) do { \ mu_assert_eq(block_start(blocks[i]), start, "block " STR(i) " start"); \ mu_assert_eq(block_end(blocks[i]), end, "block " STR(i) " end"); \ - } while (0); + mu_assert_eq(blocks[i]->fallthrough, (is_fallthrough), "fallthrough"); \ + if ((jump) != UT64_MAX) { \ + mu_assert_eq(rz_vector_len(&blocks[i]->jump_targets), 1, "jump targets count"); \ + mu_assert_eq(*(ut64 *)rz_vector_index_ptr(&blocks[i]->jump_targets, 0), (jump), "jump"); \ + } else { \ + mu_assert_eq(rz_vector_len(&blocks[i]->jump_targets), 0, "jump targets count"); \ + } \ + } while (0) + +#define ASSERT_ANALYSIS_BLOCK(block, start, end, jumpv, failv) do { \ + mu_assert_eq(((RzAnalysisBlock *)block)->addr, (start), "analysis block start"); \ + mu_assert_eq(((RzAnalysisBlock *)block)->size, (end) - (start), "analysis block size"); \ + mu_assert_eq(((RzAnalysisBlock *)block)->jump, jumpv, "analysis block jump"); \ + mu_assert_eq(((RzAnalysisBlock *)block)->fail, failv, "analysis block fail"); \ + } while (0) + +static size_t blocks_count(RzInterpRunContext *ctx) { + size_t r = 0; + RzIntervalTreeIter it; + RzInterpBlock *block; + rz_interval_tree_foreach(&ctx->blocks, it, block) { + r++; + } + return r; +} + +bool test_interp_block_resolve_bounds_single(void) { + TestInterp *interp = interp_new("arm", 64, 0x10000, "hex://" + "000080d2" // 0x00 mov x0, 0 + // --- + "200080d2" // 0x04 mov x0, 1 <- entry + "400080d2" // 0x08 mov x0, 2 + "600080d2" // 0x0c mov x0, 3 + "800080d2" // 0x10 mov x0, 4 + "a00080d2" // 0x14 mov x0, 5 + "c0035fd6" // 0x18 ret + // --- + ); + + RzInterpRunContext ctx; + mu_assert_true(rz_interp_run_context_init(&ctx, interp->inst), "init run context"); + + RzInterpAbstrState *as = rz_interp_abstr_state_new(interp->inst); + rz_interp_abstr_state_set_pc_const(as, 0x10008); + rz_interp_run_push(&ctx, as, false); + rz_interp_abstr_state_free(interp->inst, as); + + mu_assert_eq(blocks_count(&ctx), 1, "blocks count"); + RzInterpBlock *block = rz_interp_block_at(&ctx, 0x10008); + mu_assert_notnull(block, "block"); + + RzILCacheBlock *il_block = rz_il_cache_lift_il_block(interp->il_cache, 0x10008); + rz_interp_block_resolve_bounds(&ctx, block, il_block); + mu_assert_true(block->bounds_resolved, "bounds resolved"); + mu_assert_eq(rz_interp_block_get_start(block), 0x10008, "block start"); + mu_assert_eq(rz_interp_block_get_end(block), 0x1001b, "block end"); + mu_assert_eq(rz_vector_len(&block->insn_offsets), 4, "insn count"); + mu_assert_eq(*(ut16 *)rz_vector_index_ptr(&block->insn_offsets, 0), 0x04, "insn offset"); + mu_assert_eq(*(ut16 *)rz_vector_index_ptr(&block->insn_offsets, 1), 0x08, "insn offset"); + mu_assert_eq(*(ut16 *)rz_vector_index_ptr(&block->insn_offsets, 2), 0x0c, "insn offset"); + mu_assert_eq(*(ut16 *)rz_vector_index_ptr(&block->insn_offsets, 3), 0x10, "insn offset"); + + rz_interp_run_context_fini(&ctx); + interp_free(interp); + mu_end; +} + +bool test_interp_block_resolve_bounds_prepend(bool single_op_existing_block, bool single_op_prepend) { + // single_op_existing_block and single_op_prepend are for testing for potential bugs in insn offset handling + + TestInterp *interp = interp_new("arm", 64, 0x10000, "hex://" + // --- blocks for single_op_existing_block == false + "000080d2" // 0x00 mov x0, 0 <- prepended block + "200080d2" // 0x04 mov x0, 1 <- alternative entry if single_op_prepend + // --- + "400080d2" // 0x08 mov x0, 2 <- existing block + "600080d2" // 0x0c mov x0, 3 + "800080d2" // 0x10 mov x0, 4 + "a00080d2" // 0x14 mov x0, 5 + "c0035fd6" // 0x18 ret + // --- + + // --- blocks for single_op_existing_block == true + // 0x10 mov x0, 4 <- prepended block + // 0x14 mov x0, 5 <- alternative entry if single_op_prepend + // --- + // 0x18 ret <- existing block + // --- + ); + + RzInterpRunContext ctx; + mu_assert_true(rz_interp_run_context_init(&ctx, interp->inst), "init run context"); + + ut64 existing_block_start = single_op_existing_block ? 0x10018 : 0x10008; + ut64 prepended_block_start = existing_block_start - (single_op_prepend ? 4 : 8); + + RzInterpAbstrState *as = rz_interp_abstr_state_new(interp->inst); + rz_interp_abstr_state_set_pc_const(as, existing_block_start); + rz_interp_run_push(&ctx, as, false); + rz_interp_abstr_state_free(interp->inst, as); + + as = rz_interp_abstr_state_new(interp->inst); + rz_interp_abstr_state_set_pc_const(as, prepended_block_start); + rz_interp_run_push(&ctx, as, false); + rz_interp_abstr_state_free(interp->inst, as); + + mu_assert_eq(blocks_count(&ctx), 2, "blocks count"); + RzInterpBlock *block = rz_interp_block_at(&ctx, prepended_block_start); + mu_assert_notnull(block, "block"); + + RzILCacheBlock *il_block = rz_il_cache_lift_il_block(interp->il_cache, prepended_block_start); + rz_interp_block_resolve_bounds(&ctx, block, il_block); + + mu_assert_true(block->bounds_resolved, "bounds resolved"); + mu_assert_eq(rz_interp_block_get_start(block), prepended_block_start, "block start"); + mu_assert_eq(rz_interp_block_get_end(block), existing_block_start - 1, "block end"); + mu_assert_eq(rz_vector_len(&block->insn_offsets), single_op_prepend ? 0 : 1, "insn count"); + if (!single_op_prepend) { + mu_assert_eq(*(ut16 *)rz_vector_index_ptr(&block->insn_offsets, 0), 0x04, "insn offset"); + } + + rz_interp_run_context_fini(&ctx); + interp_free(interp); + mu_end; +} + +bool test_interp_block_resolve_bounds_split(bool single_op_existing_block, bool single_op_split) { + // single_op_existing_block and single_op_prepend are for testing for potential bugs in insn offset handling + + TestInterp *interp = interp_new("arm", 64, 0x10000, "hex://" + // --- blocks for single_op_split == false + "000080d2" // 0x00 mov x0, 0 <- existing block + "200080d2" // 0x04 mov x0, 1 <- alternative entry if single_op_existing_block + // --- + "400080d2" // 0x08 mov x0, 2 <- splitting block + "c0035fd6" // 0x0c ret + // --- + + // --- blocks for single_op_split == false + // 0x00 mov x0, 0 + // 0x04 mov x0, 1 <- existing block + // 0x08 mov x0, 2 <- alternative entry if single_op_existing_block + // --- + // 0x0c ret <- splitting block + // --- + ); + + RzInterpRunContext ctx; + mu_assert_true(rz_interp_run_context_init(&ctx, interp->inst), "init run context"); + + ut64 splitting_block_start = single_op_split ? 0x1000c : 0x10008; + ut64 existing_block_start = splitting_block_start - (single_op_existing_block ? 4 : 8); + + RzInterpAbstrState *as = rz_interp_abstr_state_new(interp->inst); + rz_interp_abstr_state_set_pc_const(as, existing_block_start); + rz_interp_run_push(&ctx, as, false); + rz_interp_abstr_state_free(interp->inst, as); + RzInterpBlock *existing_block = rz_interp_block_at(&ctx, existing_block_start); + mu_assert_notnull(existing_block, "block"); + + RzILCacheBlock *il_block = rz_il_cache_lift_il_block(interp->il_cache, existing_block_start); + rz_interp_block_resolve_bounds(&ctx, existing_block, il_block); + mu_assert_true(existing_block->bounds_resolved, "bounds resolved"); + mu_assert_eq(rz_interp_block_get_start(existing_block), existing_block_start, "block start"); + mu_assert_eq(rz_interp_block_get_end(existing_block), splitting_block_start + (single_op_split ? 3 : 7), "block end"); + size_t off_count_expect = (single_op_existing_block ? 0 : 1) + (single_op_split ? 1 : 2); + mu_assert_eq(rz_vector_len(&existing_block->insn_offsets), off_count_expect, "insn count"); + for (size_t i = 0; i < off_count_expect; i++) { + mu_assert_eq(*(ut16 *)rz_vector_index_ptr(&existing_block->insn_offsets, i), 4 + 4 * i, "insn offset"); + } + existing_block->fallthrough = false; + ut64 target = 0x12345; + rz_vector_push(&existing_block->jump_targets, &target); + + as = rz_interp_abstr_state_new(interp->inst); + rz_interp_abstr_state_set_pc_const(as, splitting_block_start); + rz_interp_run_push(&ctx, as, false); + rz_interp_abstr_state_free(interp->inst, as); + + mu_assert_eq(blocks_count(&ctx), 2, "blocks count"); + RzInterpBlock *splitting_block = rz_interp_block_at(&ctx, splitting_block_start); + mu_assert_notnull(splitting_block, "block"); + + il_block = rz_il_cache_lift_il_block(interp->il_cache, splitting_block_start); + rz_interp_block_resolve_bounds(&ctx, splitting_block, il_block); + + mu_assert_true(splitting_block->bounds_resolved, "bounds resolved"); + mu_assert_eq(rz_interp_block_get_start(splitting_block), splitting_block_start, "block start"); + mu_assert_eq(rz_interp_block_get_end(splitting_block), splitting_block_start + (single_op_split ? 3 : 7), "block end"); + mu_assert_eq(rz_vector_len(&splitting_block->insn_offsets), single_op_split ? 0 : 1, "insn count"); + if (!single_op_split) { + mu_assert_eq(*(ut16 *)rz_vector_index_ptr(&splitting_block->insn_offsets, 0), 0x04, "insn offset"); + } + + // existing has been modified to go only until the splitting block + mu_assert_true(existing_block->bounds_resolved, "bounds resolved"); + mu_assert_eq(rz_interp_block_get_start(existing_block), existing_block_start, "block start"); + mu_assert_eq(rz_interp_block_get_end(existing_block), splitting_block_start - 1, "block end"); + mu_assert_eq(rz_vector_len(&existing_block->insn_offsets), single_op_existing_block ? 0 : 1, "insn count"); + if (!single_op_existing_block) { + mu_assert_eq(*(ut16 *)rz_vector_index_ptr(&existing_block->insn_offsets, 0), 0x04, "insn offset"); + } + mu_assert_true(existing_block->fallthrough, "existing fallthrough"); + mu_assert_eq(rz_vector_len(&existing_block->jump_targets), 0, "existing jump targets"); + + rz_interp_run_context_fini(&ctx); + interp_free(interp); + mu_end; +} bool test_interp_cfg_single_block(void) { TestInterp *interp = interp_new("arm", 64, 0x10000, "hex://" @@ -87,7 +296,13 @@ bool test_interp_cfg_single_block(void) { EXTRACT_RESULT(interp, 1); mu_assert_eq(res->entry, 0x10000, "result entry"); - ASSERT_BLOCK_BOUNDS(0, 0x10000, 0x10008); + ASSERT_BLOCK(0, 0x10000, 0x10008, false, UT64_MAX); + + rz_interp_result_apply_to_analysis(res, interp->analysis); + RzAnalysisFunction *fcn = rz_analysis_get_function_at(interp->analysis, 0x10000); + mu_assert_notnull(fcn, "analysis function"); + mu_assert_eq(rz_pvector_len(fcn->bbs), 1, "analysis block count"); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 0), 0x10000, 0x10008, UT64_MAX, UT64_MAX); interp_free(interp); mu_end; @@ -108,8 +323,46 @@ bool test_interp_cfg_direct_jmp(void) { EXTRACT_RESULT(interp, 2); mu_assert_eq(res->entry, 0x10000, "result entry"); - ASSERT_BLOCK_BOUNDS(0, 0x10000, 0x10008); - ASSERT_BLOCK_BOUNDS(1, 0x10010, 0x10018); + ASSERT_BLOCK(0, 0x10000, 0x10008, false, 0x10010); + ASSERT_BLOCK(1, 0x10010, 0x10018, false, UT64_MAX); + + rz_interp_result_apply_to_analysis(res, interp->analysis); + RzAnalysisFunction *fcn = rz_analysis_get_function_at(interp->analysis, 0x10000); + mu_assert_notnull(fcn, "analysis function"); + mu_assert_eq(rz_pvector_len(fcn->bbs), 2, "analysis block count"); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 0), 0x10000, 0x10008, 0x10010, UT64_MAX); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 1), 0x10010, 0x10018, UT64_MAX, UT64_MAX); + + interp_free(interp); + mu_end; +} + +bool test_interp_cfg_branch(void) { + TestInterp *interp = interp_new("arm", 64, 0x10000, "hex://" + "600880d2" // 0x00 mov x0, 0x43 + "69000054" // 0x04 b.ls 0x10 --- + "1f2003d5" // 0x08 nop | + "c0035fd6" // 0x0c ret | + "400880d2" // 0x10 mov x0, 0x42 <-- + "c0035fd6" // 0x14 ret + ); + mu_assert_notnull(interp, "init"); + bool succ = rz_interp_run(interp->inst, 0x10000); + mu_assert_true(succ, "run success"); + + EXTRACT_RESULT(interp, 3); + mu_assert_eq(res->entry, 0x10000, "result entry"); + ASSERT_BLOCK(0, 0x10000, 0x10008, true, 0x10010); + ASSERT_BLOCK(1, 0x10008, 0x10010, false, UT64_MAX); + ASSERT_BLOCK(2, 0x10010, 0x10018, false, UT64_MAX); + + rz_interp_result_apply_to_analysis(res, interp->analysis); + RzAnalysisFunction *fcn = rz_analysis_get_function_at(interp->analysis, 0x10000); + mu_assert_notnull(fcn, "analysis function"); + mu_assert_eq(rz_pvector_len(fcn->bbs), 3, "analysis block count"); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 0), 0x10000, 0x10008, 0x10010, 0x10008); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 1), 0x10008, 0x10010, UT64_MAX, UT64_MAX); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 2), 0x10010, 0x10018, UT64_MAX, UT64_MAX); interp_free(interp); mu_end; @@ -133,19 +386,181 @@ bool test_interp_cfg_branch_join(void) { EXTRACT_RESULT(interp, 4); mu_assert_eq(res->entry, 0x10000, "result entry"); - ASSERT_BLOCK_BOUNDS(0, 0x10000, 0x10008); - ASSERT_BLOCK_BOUNDS(1, 0x10008, 0x10010); - ASSERT_BLOCK_BOUNDS(2, 0x10010, 0x10014); - ASSERT_BLOCK_BOUNDS(3, 0x10014, 0x10018); + ASSERT_BLOCK(0, 0x10000, 0x10008, true, 0x10010); + ASSERT_BLOCK(1, 0x10008, 0x10010, false, 0x10014); + ASSERT_BLOCK(2, 0x10010, 0x10014, true, UT64_MAX); + ASSERT_BLOCK(3, 0x10014, 0x10018, false, UT64_MAX); + + rz_interp_result_apply_to_analysis(res, interp->analysis); + RzAnalysisFunction *fcn = rz_analysis_get_function_at(interp->analysis, 0x10000); + mu_assert_notnull(fcn, "analysis function"); + mu_assert_eq(rz_pvector_len(fcn->bbs), 4, "analysis block count"); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 0), 0x10000, 0x10008, 0x10010, 0x10008); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 1), 0x10008, 0x10010, 0x10014, UT64_MAX); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 2), 0x10010, 0x10014, 0x10014, UT64_MAX); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 3), 0x10014, 0x10018, UT64_MAX, UT64_MAX); + + interp_free(interp); + mu_end; +} + +bool test_interp_cfg_multi_entry_fallthrough_jmp(bool swap) { + // This is about a sequence of instructions that ends with some control flow instruction, + // but has two entrypoints into it, where one entrypoint falls through into the other. + // The swapping of the branches is there because potential bugs in the interpreter may + // depend on the order of block addresses added. + TestInterp *interp = interp_new("arm", 64, 0x10000, + !swap ? "hex://" + "a9000054" // 0x00 b.ls 0x14 ------ + "02000014" // 0x04 b 0xc --- | + "1f2003d5" // 0x08 nop | | + "400880d2" // 0x0c mov x0, 0x42 <-- | + "1f2003d5" // 0x10 nop | + "610880d2" // 0x14 mov x1, 0x43 <----- + "c0035fd6" // 0x18 ret + : "hex://" + "69000054" // 0x00 b.ls 0xc --- + "04000014" // 0x04 b 0x14 -- | -- + "1f2003d5" // 0x08 nop | | + "400880d2" // 0x0c mov x0, 0x42 <-- | + "1f2003d5" // 0x10 nop | + "610880d2" // 0x14 mov x1, 0x43 <------ + "c0035fd6" // 0x18 ret + ); + mu_assert_notnull(interp, "init"); + bool succ = rz_interp_run(interp->inst, 0x10000); + mu_assert_true(succ, "run success"); + + EXTRACT_RESULT(interp, 4); + mu_assert_eq(res->entry, 0x10000, "result entry"); + ASSERT_BLOCK(0, 0x10000, 0x10004, true, !swap ? 0x10014 : 0x1000c); + ASSERT_BLOCK(1, 0x10004, 0x10008, false, !swap ? 0x1000c : 0x10014); + ASSERT_BLOCK(2, 0x1000c, 0x10014, true, UT64_MAX); + ASSERT_BLOCK(3, 0x10014, 0x1001c, false, UT64_MAX); + + rz_interp_result_apply_to_analysis(res, interp->analysis); + RzAnalysisFunction *fcn = rz_analysis_get_function_at(interp->analysis, 0x10000); + mu_assert_notnull(fcn, "analysis function"); + mu_assert_eq(rz_pvector_len(fcn->bbs), 4, "analysis block count"); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 0), 0x10000, 0x10004, !swap ? 0x10014 : 0x1000c, 0x10004); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 1), 0x10004, 0x10008, !swap ? 0x1000c : 0x10014, UT64_MAX); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 2), 0x1000c, 0x10014, 0x10014, UT64_MAX); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 3), 0x10014, 0x1001c, UT64_MAX, UT64_MAX); + + interp_free(interp); + mu_end; +} + +bool test_interp_cfg_multi_entry_fallthrough_jmp_before() { + TestInterp *interp = interp_new("arm", 64, 0x10000, "hex://" + "400880d2" // 0x00 mov x0, 0x42 <-- + "1f8c04f1" // 0x04 cmp x0, 0x123 | + "c9ffff54" // 0x08 b.ls 0 --- + "c0035fd6" // 0x0c ret + ); + mu_assert_notnull(interp, "init"); + bool succ = rz_interp_run(interp->inst, 0x10008); + mu_assert_true(succ, "run success"); + + EXTRACT_RESULT(interp, 3); + mu_assert_eq(res->entry, 0x10008, "result entry"); + ASSERT_BLOCK(0, 0x10000, 0x10008, true, UT64_MAX); + ASSERT_BLOCK(1, 0x10008, 0x1000c, true, 0x10000); + ASSERT_BLOCK(2, 0x1000c, 0x10010, false, UT64_MAX); + + rz_interp_result_apply_to_analysis(res, interp->analysis); + RzAnalysisFunction *fcn = rz_analysis_get_function_at(interp->analysis, 0x10008); + mu_assert_notnull(fcn, "analysis function"); + mu_assert_eq(rz_pvector_len(fcn->bbs), 3, "analysis block count"); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 0), 0x10000, 0x10008, 0x10008, UT64_MAX); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 1), 0x10008, 0x1000c, 0x10000, 0x1000c); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 2), 0x1000c, 0x10010, UT64_MAX, UT64_MAX); + + interp_free(interp); + mu_end; +} + +bool test_interp_cfg_multi_entry_fallthrough_jmp_inside_self() { + TestInterp *interp = interp_new("arm", 64, 0x10000, "hex://" + "600080d2" // 0x00 mov x0, 3 + "000400d1" // 0x04 sub x0, x0, 1 <-- + "e1ffff54" // 0x08 b.ne 4 --- + "c0035fd6" // 0x0c ret + ); + mu_assert_notnull(interp, "init"); + bool succ = rz_interp_run(interp->inst, 0x10000); + mu_assert_true(succ, "run success"); + + EXTRACT_RESULT(interp, 3); + mu_assert_eq(res->entry, 0x10000, "result entry"); + ASSERT_BLOCK(0, 0x10000, 0x10004, true, UT64_MAX); + ASSERT_BLOCK(1, 0x10004, 0x1000c, true, 0X10004); + ASSERT_BLOCK(2, 0x1000c, 0x10010, false, UT64_MAX); + + rz_interp_result_apply_to_analysis(res, interp->analysis); + RzAnalysisFunction *fcn = rz_analysis_get_function_at(interp->analysis, 0x10000); + mu_assert_notnull(fcn, "analysis function"); + mu_assert_eq(rz_pvector_len(fcn->bbs), 3, "analysis block count"); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 0), 0x10000, 0x10004, 0x10004, UT64_MAX); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 1), 0x10004, 0x1000c, 0x10004, 0x1000c); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 2), 0x1000c, 0x10010, UT64_MAX, UT64_MAX); + + interp_free(interp); + mu_end; +} + +bool test_interp_cfg_multi_entry_fallthrough_jmp_inside_other() { + TestInterp *interp = interp_new("arm", 64, 0x10000, "hex://" + "600080d2" // 0x00 mov x0, 3 + "000400d1" // 0x04 sub x0, x0, 1 <-------- + "02000014" // 0x08 b 0x10 --- | + "1f2003d5" // 0x0c nop | | + "a1ffff54" // 0x10 b.ne 4 <-- --- + "c0035fd6" // 0x14 ret + ); + mu_assert_notnull(interp, "init"); + bool succ = rz_interp_run(interp->inst, 0x10000); + mu_assert_true(succ, "run success"); + + EXTRACT_RESULT(interp, 4); + mu_assert_eq(res->entry, 0x10000, "result entry"); + ASSERT_BLOCK(0, 0x10000, 0x10004, true, UT64_MAX); + ASSERT_BLOCK(1, 0x10004, 0x1000c, false, 0x10010); + ASSERT_BLOCK(2, 0x10010, 0x10014, true, 0x10004); + ASSERT_BLOCK(3, 0x10014, 0x10018, false, UT64_MAX); + + rz_interp_result_apply_to_analysis(res, interp->analysis); + RzAnalysisFunction *fcn = rz_analysis_get_function_at(interp->analysis, 0x10000); + mu_assert_notnull(fcn, "analysis function"); + mu_assert_eq(rz_pvector_len(fcn->bbs), 4, "analysis block count"); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 0), 0x10000, 0x10004, 0x10004, UT64_MAX); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 1), 0x10004, 0x1000c, 0x10010, UT64_MAX); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 2), 0x10010, 0x10014, 0x10004, 0x10014); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 3), 0x10014, 0x10018, UT64_MAX, UT64_MAX); interp_free(interp); mu_end; } bool all_tests() { + mu_run_test(test_interp_block_resolve_bounds_single); + mu_run_test(test_interp_block_resolve_bounds_prepend, false, false); + mu_run_test(test_interp_block_resolve_bounds_prepend, false, true); + mu_run_test(test_interp_block_resolve_bounds_prepend, true, false); + mu_run_test(test_interp_block_resolve_bounds_prepend, true, true); + mu_run_test(test_interp_block_resolve_bounds_split, false, false); + mu_run_test(test_interp_block_resolve_bounds_split, false, true); + mu_run_test(test_interp_block_resolve_bounds_split, true, false); + mu_run_test(test_interp_block_resolve_bounds_split, true, true); mu_run_test(test_interp_cfg_single_block); mu_run_test(test_interp_cfg_direct_jmp); + mu_run_test(test_interp_cfg_branch); mu_run_test(test_interp_cfg_branch_join); + mu_run_test(test_interp_cfg_multi_entry_fallthrough_jmp, false); + mu_run_test(test_interp_cfg_multi_entry_fallthrough_jmp, true); + mu_run_test(test_interp_cfg_multi_entry_fallthrough_jmp_before); + mu_run_test(test_interp_cfg_multi_entry_fallthrough_jmp_inside_self); + mu_run_test(test_interp_cfg_multi_entry_fallthrough_jmp_inside_other); return tests_passed != tests_run; } From 1c555aeaa9cd8f2e2ade98cc5f2782ca9a91b82a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Mon, 13 Jul 2026 19:43:15 +0200 Subject: [PATCH 318/334] Simplify interp_block_with_op_at_cmp --- librz/inquiry/interp/interpreter.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 3a83c50cc16..10955ecece7 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -302,12 +302,7 @@ typedef struct interp_block_with_op_at_ctx_t { static int interp_block_with_op_at_cmp(const void *a, const void *b, void *user) { const ut16 *av = a; const ut16 *bv = b; - if (*av > *bv) { - return 1; - } else if (*av < *bv) { - return -1; - } - return 0; + return (st32)*av - (st32)*bv; } static bool interp_block_with_op_at_cb(RzIntervalNode *node, void *user) { From f061c68a12251f6ad96e4ab84e2e02a4337d56a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Mon, 13 Jul 2026 20:35:58 +0200 Subject: [PATCH 319/334] Reset data after calls and test --- librz/inquiry/interp/interpreter.c | 14 ++++++-- test/unit/test_inquiry_interp.c | 54 ++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 10955ecece7..23267ea20ce 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -1143,6 +1143,17 @@ static bool eval_pure(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT Rz return true; } +static void eval_call(RzInterpRunContext *ctx) { + // For calls, assume control flow will continue like fallthrough. + // But any data that may be modified by the callee must be set to top. + // TODO: this should depend on the ABI, some data may be preserved. + RzIterator *it = ht_up_as_iter(ctx->astate->globals); + RzInterpAbstrVal **av; + rz_iterator_foreach(it, av) { + ctx->inst->plugin->set_top(*av); + } +} + static bool eval_effect(RzInterpRunContext *ctx, const RzILOpEffect *effect, size_t insn_pkt_size) { @@ -1236,8 +1247,7 @@ static bool eval_effect(RzInterpRunContext *ctx, } if (is_call) { - // For calls, assume control flow will continue like fallthrough. - // TODO: set data to top that may be changed by the call + eval_call(ctx); } else { set_abstr_pc(ctx->inst, ctx->astate, eval_out); } diff --git a/test/unit/test_inquiry_interp.c b/test/unit/test_inquiry_interp.c index 5dfd590d026..c945b1dbda5 100644 --- a/test/unit/test_inquiry_interp.c +++ b/test/unit/test_inquiry_interp.c @@ -542,6 +542,58 @@ bool test_interp_cfg_multi_entry_fallthrough_jmp_inside_other() { mu_end; } +bool test_interp_cfg_call(void) { + TestInterp *interp = interp_new("arm", 64, 0x10000, "hex://" + "600880d2" // 0x00 mov x0, 0x43 + "ff430094" // 0x04 bl 0x11000 + "c0035fd6" // 0x08 ret + ); + mu_assert_notnull(interp, "init"); + bool succ = rz_interp_run(interp->inst, 0x10000); + mu_assert_true(succ, "run success"); + + EXTRACT_RESULT(interp, 2); + mu_assert_eq(res->entry, 0x10000, "result entry"); + ASSERT_BLOCK(0, 0x10000, 0x10008, true, UT64_MAX); + ASSERT_BLOCK(1, 0x10008, 0x1000c, false, UT64_MAX); + + rz_interp_result_apply_to_analysis(res, interp->analysis); + RzAnalysisFunction *fcn = rz_analysis_get_function_at(interp->analysis, 0x10000); + mu_assert_notnull(fcn, "analysis function"); + mu_assert_eq(rz_pvector_len(fcn->bbs), 1, "analysis block count"); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 0), 0x10000, 0x1000c, UT64_MAX, UT64_MAX); + + interp_free(interp); + mu_end; +} + +bool test_interp_cfg_call_multi_insn(void) { + TestInterp *interp = interp_new("arm", 32, 0x10000, "hex://" + // this pattern is common on ARMv4 to perform an indirect call + "110aa0e3" // 0x00 mov r0, 0xff000 + "0fe0a0e1" // 0x04 mov lr, pc + "00f0a0e1" // 0x08 mov pc, r0 + "1eff2fe1" // 0x0c bx lr + ); + mu_assert_notnull(interp, "init"); + bool succ = rz_interp_run(interp->inst, 0x10000); + mu_assert_true(succ, "run success"); + + EXTRACT_RESULT(interp, 2); + mu_assert_eq(res->entry, 0x10000, "result entry"); + ASSERT_BLOCK(0, 0x10000, 0x1000c, true, UT64_MAX); + ASSERT_BLOCK(1, 0x1000c, 0x10010, false, UT64_MAX); + + rz_interp_result_apply_to_analysis(res, interp->analysis); + RzAnalysisFunction *fcn = rz_analysis_get_function_at(interp->analysis, 0x10000); + mu_assert_notnull(fcn, "analysis function"); + mu_assert_eq(rz_pvector_len(fcn->bbs), 1, "analysis block count"); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 0), 0x10000, 0x10010, UT64_MAX, UT64_MAX); + + interp_free(interp); + mu_end; +} + bool all_tests() { mu_run_test(test_interp_block_resolve_bounds_single); mu_run_test(test_interp_block_resolve_bounds_prepend, false, false); @@ -561,6 +613,8 @@ bool all_tests() { mu_run_test(test_interp_cfg_multi_entry_fallthrough_jmp_before); mu_run_test(test_interp_cfg_multi_entry_fallthrough_jmp_inside_self); mu_run_test(test_interp_cfg_multi_entry_fallthrough_jmp_inside_other); + mu_run_test(test_interp_cfg_call); + mu_run_test(test_interp_cfg_call_multi_insn); return tests_passed != tests_run; } From d5cc23f781054a11766f45a4750c8d58fb34ee0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Tue, 14 Jul 2026 16:13:02 +0200 Subject: [PATCH 320/334] Add xrefs to result --- librz/include/rz_inquiry/rz_interpreter.h | 9 +- librz/inquiry/README.md | 2 - librz/inquiry/inquiry.c | 2 +- librz/inquiry/interp/interpreter.c | 121 +++++++++++++++++----- 4 files changed, 104 insertions(+), 30 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 2ad8e8e326a..8bedc497afd 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -26,6 +26,7 @@ typedef struct rz_interp_run_state RzInterpRunState; typedef struct rz_interp_run_context_t RzInterpRunContext; +typedef struct rz_interp_result_t RzInterpResult; typedef enum rz_interp_state_flag { /** @@ -171,6 +172,7 @@ typedef struct { /** * \brief A ring buffer to push interpretation yields into. + * TODO: remove */ typedef struct { RzInterpYieldKind kind; @@ -288,6 +290,7 @@ struct rz_interp_instance_t { * \brief The ring buffers to push the yield of interpretation into. * These ring buffers are shared with other interpreter sets. */ + // TODO: remove RZ_BORROW RZ_NULLABLE RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM]; RzPVector /**/ results; ///< TODO: replace this by a queue/rbuf/... to handle interp and results concurrently @@ -336,6 +339,7 @@ struct rz_interp_run_context_t { * inside RzInterpBlock to remove the additional indirection. */ RzIntervalTree /**/ blocks; + RzInterpResult *res; ///< If not NULL, a fixpoint has been reached already and we are now collecting results // Tracking data local to a single block interpretation RzInterpBlock *block; ///< The currently interpreted interp block @@ -344,10 +348,11 @@ struct rz_interp_run_context_t { RzAnalysisCallCandidate call_cand; ///< Data of a call candidate. }; -typedef struct rz_interp_run_result_t { +struct rz_interp_result_t { ut64 entry; RzIntervalTree /**/ blocks; -} RzInterpResult; + RzVector /**/ xrefs; +} /*RzInterpResult*/; RZ_API bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point); RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *iset); diff --git a/librz/inquiry/README.md b/librz/inquiry/README.md index 478a07238d9..b538cc9d518 100644 --- a/librz/inquiry/README.md +++ b/librz/inquiry/README.md @@ -7,8 +7,6 @@ Module implementing basic and advanced binary analysis. ## TODO -* Finish and test cfg recovery for all cases -* Also make sure to test fallthrough and jump target fields of interp blocks * Add final pass for analysis and integrate xrefs and other yield info in result ## Interpreter Notes diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index b6d2d303604..015a2839b38 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -208,7 +208,7 @@ static void handle_io_request(RzAnalysisILContext *il_ctx, RzInterpIOReadRequest rz_bv_to_ut64(io_req->addr)); io_res->req_ok = false; RzILMemIndex mem_idx = io_req->mem_idx; - if (mem_idx <= rz_vector_len(&il_ctx->memory)) { + if (mem_idx > rz_vector_len(&il_ctx->memory)) { rz_warn_if_reached(); return; } diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 23267ea20ce..1c974e382eb 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -211,6 +211,24 @@ bool join_state(RzInterpInstance *inst, RZ_BORROW RZ_INOUT RzInterpAbstrState *a /// @} +// Our analysis loop works by first performing abstract interpretation until a fixpoint is reached, +// and only in a second pass collecting analysis information from it such as xrefs. +// That is because before the fixpoint, abstract states will not yet represent all possible concrete states. +// +// Other approaches are conceivable, such as doing analysis every time a block is evaluated and if +// it is evaluated again, throwing away the previous results, so we encapsulate the logic for when to +// do what in these functions: + +/** Whether during evaluation, analysis results should be collected */ +static inline bool interp_is_analyzing(RzInterpRunContext *ctx) { + return ctx->res != NULL; +} + +/** Whether during evaluation, new states may be discoveres */ +static inline bool interp_is_collecting_states(RzInterpRunContext *ctx) { + return ctx->res == NULL; +} + ///////////////////////////////////////////////////////// /** * \name Interpreter Blocks @@ -361,7 +379,7 @@ RZ_API void rz_interp_block_resolve_bounds(RzInterpRunContext *ctx, RzInterpBloc // Blocks may overlap, but one block must not start at an instruction start of another. // We have to consider two cases here, depending on the order in which blocks have been discovered. - + // Case A: Our block would start at an instruction start of another block that starts before us and falls through. // We can move the instruction information from the preceding block in that case. This way, case B is already handled as well. size_t hit_op_idx = 0; @@ -439,6 +457,7 @@ RZ_API void rz_interp_block_resolve_bounds(RzInterpRunContext *ctx, RzInterpBloc * queue for further interpretation if there were changes. */ RZ_API void rz_interp_run_push(RZ_BORROW RZ_NONNULL RzInterpRunContext *ctx, RZ_BORROW RZ_NONNULL RzInterpAbstrState *as, bool is_fallthrough) { + rz_return_if_fail(interp_is_collecting_states(ctx)); if (as->pc_state == RZ_INTERP_PC_ANY) { RZ_LOG_DEBUG("Encountered state with unknown/top pc\n"); return; @@ -661,22 +680,20 @@ RZ_API void rz_interp_instance_free(RZ_OWN RZ_NULLABLE RzInterpInstance *iset) { free(iset); } -bool report_yield_xref( +static void report_yield_xref( RzInterpRunContext *ctx, size_t insn_pkt_size, ut64 from, const RzInterpAbstrVal *to, RzAnalysisXRefType type) { - RzInterpYieldRBuf *yrbuf = ctx->inst->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; - if (!yrbuf) { - return true; + if (!interp_is_analyzing(ctx)) { + return; } - RzBitVector to_bv; rz_bv_init(&to_bv, 64); - bool success = true; if (!ctx->inst->plugin->to_concrete_const(to, &to_bv) || rz_bv_len(&to_bv) > 64) { // Isn't reported + // TODO: we might also want to report multiple values here depending on the value domain goto cleanup; } if (type == RZ_ANALYSIS_XREF_TYPE_CODE && @@ -693,21 +710,16 @@ bool report_yield_xref( } ut64 to_addr = rz_bv_to_ut64(&to_bv); - RzAnalysisXRef xref = { 0 }; - xref.bb_addr = 0; // TODO? ctx->astate->bb_addr; - xref.from = from; - xref.to = to_addr; - xref.type = type; - if (yrbuf->filter(&xref, yrbuf->filter_data->io_boundaries)) { - RZ_LOG_DEBUG("prototype: REPORT xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref.from, xref.to, rz_analysis_ref_type_tostring(xref.type)); - if (rz_th_ring_buf_put(yrbuf->rbuf, &xref) != RZ_THREAD_RING_BUF_OK) { - success = false; - goto cleanup; - } + RzAnalysisXRef *xref = rz_vector_push(&ctx->res->xrefs, NULL); + if (!xref) { + goto cleanup; } + xref->bb_addr = 0; // TODO? ctx->astate->bb_addr; + xref->from = from; + xref->to = to_addr; + xref->type = type; cleanup: rz_bv_fini(&to_bv); - return success; } /** @@ -1187,6 +1199,27 @@ static bool eval_effect(RzInterpRunContext *ctx, write_var_to_state(ctx->inst, ctx->astate, kind, vhash, eval_out); if (value_indicates_ret_addr_write(ctx, eval_out) && kind == RZ_IL_VAR_KIND_GLOBAL) { + // Hint: this ret-addr store detection currently only works across a single interp block. + // Consider the following ARMv4 code for an indirect call (blx was introduced in ARMv5): + // ``` + // A> mov lr, pc + // B> mov pc, r0 + // C> ... + // ``` + // + // both A and B are block entries. + // + // 1. If A is discovered before B, the call is recognized at that point. Once B is detected, C will not be + // reached by fallthrough anymore. + // 2. If B is discovered before A, the call is not recognized in the first place because A and B are two + // separate blocks already. + // + // It is inconvenient that we may have a fixpoint where C could be considered only partially evaluated, + // but as long as we don't find a practical example where this happens and thus also don't have a good + // example for the expected analysis outcome, we leave it as-is. + // + // Making C reachable even with the in-edge at B could work by for example by marking A as ret-addr-storing + // and using that information when evaluating B. ctx->call_cand.store_addr = pc; ctx->call_cand.npc = ctx->il_block_end; ctx->call_cand.in_mem = false; @@ -1276,7 +1309,7 @@ static bool eval_effect(RzInterpRunContext *ctx, if (true_state->pc_state == false_state->pc_state && true_state->pc == false_state->pc) { // identical target location, simply join the data and continue join_state(ctx->inst, true_state, false_state); - } else { + } else if (interp_is_collecting_states(ctx)) { // different jump targets, branch rather than resorting to top pc rz_interp_run_push(ctx, true_state, true_state->pc_state == RZ_INTERP_PC_CONST && true_state->pc == fallthrough_pc); if (true_state->pc_state == RZ_INTERP_PC_CONST && !rz_vector_contains(&ctx->block->jump_targets, &true_state->pc)) { @@ -1416,7 +1449,7 @@ static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzIL } } - if (astate->pc_state != RZ_INTERP_PC_UNREACHABLE) { + if (interp_is_collecting_states(ctx) && astate->pc_state != RZ_INTERP_PC_UNREACHABLE) { bool fallthrough = false; if (astate->pc_state == RZ_INTERP_PC_CONST && astate->pc == interp_block_end + 1) { fallthrough = true; @@ -1431,6 +1464,7 @@ static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzIL RZ_API bool rz_interp_run_context_init(RzInterpRunContext *ctx, RzInterpInstance *inst) { ctx->inst = inst; ctx->astate = NULL; + ctx->res = NULL; ctx->queue = rz_list_new(); if (!ctx->queue) { return false; @@ -1504,17 +1538,47 @@ RZ_API bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { ctx.il_block_end = il_block->addr + il_block->size; // Evaluate the effect on the abstract state. if (!eval_block(&ctx, il_block)) { + // TODO: should this even be able to fail at all? + RZ_LOG_ERROR("interpreter: Eval failed\n"); + success = false; + goto cleanup; + } + } + + // Fixpoint reached, evaluate all blocks again once to collect analysis information. + // We do this in an additional pass because until now, the collected abstract states + // did not fully represent all reachable concrete states. + + ctx.res = RZ_NEW0(RzInterpResult); + if (!ctx.res) { + success = false; + goto cleanup; + } + ctx.res->entry = entry_point; + rz_vector_init(&ctx.res->xrefs, sizeof(RzAnalysisXRef), NULL, NULL); + + RzIntervalTreeIter it; + RzInterpBlock *interp_block; + rz_interval_tree_foreach (&ctx.blocks, it, interp_block) { + ctx.astate = rz_interp_abstr_state_clone(inst, interp_block->entry_state); + const RzILCacheBlock *il_block = rz_il_cache_client_lift_il_block(inst->il_cache_client, ctx.astate->pc); + if (!il_block) { + RZ_LOG_ERROR("interpreter: Lifting failed\n"); + // TODO: handle this better + break; + } + if (!eval_block(&ctx, il_block)) { + // TODO: should this even be able to fail at all? RZ_LOG_ERROR("interpreter: Eval failed\n"); success = false; break; } } - RzInterpResult *res = RZ_NEW0(RzInterpResult); - res->entry = entry_point; - memmove(&res->blocks, &ctx.blocks, sizeof(ctx.blocks)); + memmove(&ctx.res->blocks, &ctx.blocks, sizeof(ctx.blocks)); memset(&ctx.blocks, 0, sizeof(ctx.blocks)); - rz_pvector_push(&inst->results, res); + rz_pvector_push(&inst->results, ctx.res); + ctx.res = NULL; cleanup: rz_interp_run_context_fini(&ctx); @@ -1641,4 +1705,11 @@ RZ_API void rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, R bb_add_target(abb, end_excl); } } + + RzAnalysisXRef *xref; + rz_vector_foreach (&res->xrefs, xref) { + if (!rz_analysis_xrefs_set(analysis, xref->from, xref->to, xref->type)) { + RZ_LOG_ERROR("failed to set xref\n"); + } + } } From fe59424294d60db7618c655d51b078c674cb7f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Wed, 15 Jul 2026 12:59:50 +0200 Subject: [PATCH 321/334] Fix xrefs from addr --- librz/include/rz_inquiry/rz_interpreter.h | 1 + librz/inquiry/interp/interpreter.c | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 8bedc497afd..d3eb4ccc046 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -345,6 +345,7 @@ struct rz_interp_run_context_t { RzInterpBlock *block; ///< The currently interpreted interp block ut64 il_block_end; ///< The address directly after the last instruction of the currently interpreted IL block, may be further than the last instruction of the interp block! RzInterpAbstrState *astate; ///< The abstract state of the interpreter. + ut64 insn_addr; ///< The address of the currently evaluated instruction. This is not equal to astate->pc since that is already advanced by the instruction size. RzAnalysisCallCandidate call_cand; ///< Data of a call candidate. }; diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 1c974e382eb..e62eade27c9 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -1099,7 +1099,7 @@ static bool eval_pure(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT Rz rz_bv_and_inplace(&ld_addr, &mask); } - report_yield_xref(ctx, 0, ctx->astate->pc, out, RZ_ANALYSIS_XREF_TYPE_MEM_READ); + report_yield_xref(ctx, 0, ctx->insn_addr, out, RZ_ANALYSIS_XREF_TYPE_MEM_READ); size_t n_bits = pure->code == RZ_IL_OP_LOAD ? ctx->inst->il_ctx->config->mem_key_size : pure->op.loadw.n_bits; if (!load_abstr_data(ctx->inst, mem_idx, &ld_addr, n_bits, out)) { rz_bv_fini(&ld_addr); @@ -1272,8 +1272,7 @@ static bool eval_effect(RzInterpRunContext *ctx, } #endif - report_yield_xref(ctx, insn_pkt_size, pc, eval_out, - xref_type); + report_yield_xref(ctx, insn_pkt_size, ctx->insn_addr, eval_out, xref_type); // Clear the call candidate tracking variable. memset(&ctx->call_cand, 0, sizeof(ctx->call_cand)); @@ -1372,7 +1371,7 @@ static bool eval_effect(RzInterpRunContext *ctx, ctx->call_cand.npc = ctx->il_block_end; ctx->call_cand.in_mem = true; } - report_yield_xref(ctx, insn_pkt_size, pc, st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); + report_yield_xref(ctx, insn_pkt_size, ctx->insn_addr, st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); if (!store_abstr_data(ctx->inst, mem_idx, st_addr, eval_out)) { rz_bv_fini(&st_addr_bv); goto error; @@ -1431,6 +1430,8 @@ static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzIL RzILCacheInsnPkt *pkt = *it; + ctx->insn_addr = pc; + // Prepare next pc, the evalutation may overwrite this. ut64 next_pc = pc + pkt->insn_pkt_size; rz_interp_abstr_state_set_pc_const(ctx->astate, next_pc); From af406628777d2fad4af0fbdddef1d1a1a0d0ca47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Wed, 15 Jul 2026 13:21:31 +0200 Subject: [PATCH 322/334] Fix runaway block mutations --- librz/inquiry/interp/interpreter.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index e62eade27c9..3a48949439b 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -1393,7 +1393,9 @@ static bool eval_effect(RzInterpRunContext *ctx, return false; } -static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzILCacheBlock *il_bb) { +static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL RzInterpBlock *interp_block, RZ_NONNULL const RzILCacheBlock *il_block) { + ctx->block = interp_block; + ctx->il_block_end = il_block->addr + il_block->size; // Reset call candidate tracking for each basic block. memset(&ctx->call_cand, 0, sizeof(ctx->call_cand)); @@ -1402,7 +1404,7 @@ static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzIL // Now execute the actual effects of the BLOCK. RzInterpAbstrState *astate = ctx->astate; void **it; - rz_pvector_foreach (il_bb->il_ops, it) { + rz_pvector_foreach (il_block->il_ops, it) { ut64 pc = astate->pc; if (pc > interp_block_end) { @@ -1418,10 +1420,10 @@ static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzIL rz_strbuf_fini(&sb); rz_strbuf_init(&sb); - if (pc == il_bb->addr) { + if (pc == il_block->addr) { rz_strbuf_append(&sb, "ENTRY "); } - if (rz_vector_index_ptr(&il_bb->il_ops->v, rz_pvector_len(il_bb->il_ops) - 1) == it) { + if (rz_vector_index_ptr(&il_block->il_ops->v, rz_pvector_len(il_block->il_ops) - 1) == it) { rz_strbuf_append(&sb, "EXIT "); } rz_interp_abstr_state_as_str_short(ctx->inst, ctx->astate, &sb); @@ -1443,7 +1445,7 @@ static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL const RzIL // unreachable or unknown jump break; } - if (astate->pc != next_pc) { + if (interp_is_collecting_states(ctx) && astate->pc != next_pc) { // Constant jump other than fallthrough, meaning interpretation will continue in another block interp_block_add_non_fallthrough_target(ctx->block, astate->pc); break; @@ -1534,11 +1536,8 @@ RZ_API bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { rz_interp_abstr_state_as_str_short(inst, ctx.astate, &sb); // rz_meta_set_string(iset->a, RZ_META_TYPE_COMMENT, il_bb->addr, rz_strbuf_get(&sb)); rz_strbuf_fini(&sb); - - ctx.block = interp_block; - ctx.il_block_end = il_block->addr + il_block->size; // Evaluate the effect on the abstract state. - if (!eval_block(&ctx, il_block)) { + if (!eval_block(&ctx, interp_block, il_block)) { // TODO: should this even be able to fail at all? RZ_LOG_ERROR("interpreter: Eval failed\n"); success = false; @@ -1568,7 +1567,7 @@ RZ_API bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { // TODO: handle this better break; } - if (!eval_block(&ctx, il_block)) { + if (!eval_block(&ctx, interp_block, il_block)) { // TODO: should this even be able to fail at all? RZ_LOG_ERROR("interpreter: Eval failed\n"); success = false; From 6df4130bc84c5493e9cffe7950f23c1af0dc8686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Thu, 16 Jul 2026 12:52:57 +0200 Subject: [PATCH 323/334] Add result dimensions and move comments out --- librz/include/rz_inquiry/rz_interpreter.h | 23 ++- librz/inquiry/README.md | 4 - librz/inquiry/interp/interpreter.c | 180 ++++++++++++--------- test/unit/test_inquiry_interp.c | 181 ++++++++++++++++++---- 4 files changed, 270 insertions(+), 118 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index d3eb4ccc046..9aa1e45b1f8 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -326,6 +326,19 @@ RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM]); RZ_API void rz_interp_instance_free(RZ_OWN RZ_NULLABLE RzInterpInstance *iset); +/** + * \brief Dimensions describing what kind of information should be retrieved from the interpreter run + */ +typedef enum rz_interp_result_dimen_t { + /** + * \brief The basic information that is always included in every run + * This includes block boundaries, control flow edges and entry states for each block. + */ + RZ_INTERP_RESULT_DIMEN_BASE = 0, + RZ_INTERP_RESULT_DIMEN_XREFS = 1 << 0, ///< fills RzInterpResult.xrefs + RZ_INTERP_RESULT_DIMEN_COMMENTS = 1 << 1 ///< per-address textual state description for debugging, fills RzInterpResult.comments +} RzInterpResultDimen; + /** * \brief Local data during an interpreter run */ @@ -339,6 +352,7 @@ struct rz_interp_run_context_t { * inside RzInterpBlock to remove the additional indirection. */ RzIntervalTree /**/ blocks; + RzInterpResultDimen res_dimen; RzInterpResult *res; ///< If not NULL, a fixpoint has been reached already and we are now collecting results // Tracking data local to a single block interpretation @@ -350,12 +364,13 @@ struct rz_interp_run_context_t { }; struct rz_interp_result_t { - ut64 entry; - RzIntervalTree /**/ blocks; - RzVector /**/ xrefs; + ut64 entry; ///< always filled + RzIntervalTree /**/ blocks; ///< always filled + RzVector /**/ xrefs; ///< filled if RZ_INTERP_RESULT_DIMEN_XREFS is requested + HtUP /**/ *comments; ///< filled if RZ_INTERP_RESULT_DIMEN_COMMENTS } /*RzInterpResult*/; -RZ_API bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point); +RZ_API RzInterpResult *rz_interp_run(RzInterpInstance *inst, ut64 entry_point, RzInterpResultDimen dimen); RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *iset); RZ_API void rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, RZ_NONNULL RzAnalysis *analysis); diff --git a/librz/inquiry/README.md b/librz/inquiry/README.md index b538cc9d518..ddc4c868b13 100644 --- a/librz/inquiry/README.md +++ b/librz/inquiry/README.md @@ -5,10 +5,6 @@ Module implementing basic and advanced binary analysis. -## TODO - -* Add final pass for analysis and integrate xrefs and other yield info in result - ## Interpreter Notes Ideas for optimization (do not implement any of these without profiling first): diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 3a48949439b..8fee9f2d813 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -134,12 +134,14 @@ RZ_API void rz_interp_abstr_state_as_str_short(RZ_NONNULL RzInterpInstance *inst bool first = true; RzIterator *it = ht_up_as_iter_keys(astate->globals); ut64 *k; + bool all_top = true; rz_iterator_foreach(it, k) { ut64 djb2_reg_name = *k; RzInterpAbstrVal *av = ht_up_find(astate->globals, djb2_reg_name, NULL); if (!av || inst->plugin->is_top(av)) { continue; } + all_top = false; if (!first) { rz_strbuf_append(sb, ", "); } @@ -148,6 +150,9 @@ RZ_API void rz_interp_abstr_state_as_str_short(RZ_NONNULL RzInterpInstance *inst rz_strbuf_appendf(sb, "%s = ", varname); inst->plugin->val_as_str(av, sb); } + if (all_top) { + rz_strbuf_append(sb, STR_TOP); + } } static HtUP *var_set_clone(const RzInterpInstance *iset, HtUP *vars) { @@ -229,6 +234,23 @@ static inline bool interp_is_collecting_states(RzInterpRunContext *ctx) { return ctx->res == NULL; } +static void interp_add_comment(RzInterpRunContext *ctx, ut64 addr, const char *cmt) { + // building the commment string passed to this function is expensive, so assert that it is only called + // when actually requested. + rz_return_if_fail(interp_is_analyzing(ctx) && (ctx->res_dimen & RZ_INTERP_RESULT_DIMEN_COMMENTS)); + RzStrBuf sb; + rz_strbuf_init(&sb); + char *existing = ht_up_find(ctx->res->comments, addr, NULL); + if (existing) { + rz_strbuf_appendf(&sb, "%s; ", existing); + } + rz_strbuf_append(&sb, cmt); + char *val = rz_strbuf_drain_nofree(&sb); + if (!ht_up_update(ctx->res->comments, addr, val)) { + free(val); + } +} + ///////////////////////////////////////////////////////// /** * \name Interpreter Blocks @@ -686,7 +708,7 @@ static void report_yield_xref( ut64 from, const RzInterpAbstrVal *to, RzAnalysisXRefType type) { - if (!interp_is_analyzing(ctx)) { + if (!interp_is_analyzing(ctx) || !(ctx->res_dimen & RZ_INTERP_RESULT_DIMEN_XREFS)) { return; } RzBitVector to_bv; @@ -841,11 +863,8 @@ bool load_abstr_data( return false; } if (!io_res.req_ok) { - RZ_LOG_WARN("prototype: Failed to read correct number of bytes. Requested: 0x%" PFMTSZx - " Received: 0x%" PFMT32x " bits.\n", - n_bits, rz_bv_len(&out_bv)); inst->plugin->set_top(out); - return false; + return true; } inst->plugin->set_const_bv(out, &out_bv); @@ -1341,9 +1360,9 @@ static bool eval_effect(RzInterpRunContext *ctx, RzBitVector st_addr_bv; rz_bv_init(&st_addr_bv, 64); bool st_addr_is_const = st_addr && ctx->inst->plugin->to_concrete_const(st_addr, &st_addr_bv); - ctx->inst->plugin->val_free(st_addr); if (!st_addr_is_const) { rz_bv_fini(&st_addr_bv); + ctx->inst->plugin->val_free(st_addr); break; } if (rz_bv_len(&st_addr_bv) == 64) { @@ -1357,21 +1376,20 @@ static bool eval_effect(RzInterpRunContext *ctx, } RzILOpPure *pval = effect->code == RZ_IL_OP_STORE ? effect->op.store.value : effect->op.storew.value; - if (!eval_pure(ctx, pval, eval_out)) { + eval_out = ctx->inst->plugin->val_new_top(); + if (!eval_out || !eval_pure(ctx, pval, eval_out)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); rz_bv_fini(&st_addr_bv); + ctx->inst->plugin->val_free(st_addr); goto error; } - if (!eval_out || !eval_pure(ctx, effect->op.branch.condition, eval_out)) { - rz_bv_fini(&st_addr_bv); - break; - } if (value_indicates_ret_addr_write(ctx, eval_out)) { ctx->call_cand.store_addr = pc; ctx->call_cand.npc = ctx->il_block_end; ctx->call_cand.in_mem = true; } report_yield_xref(ctx, insn_pkt_size, ctx->insn_addr, st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); + ctx->inst->plugin->val_free(st_addr); if (!store_abstr_data(ctx->inst, mem_idx, st_addr, eval_out)) { rz_bv_fini(&st_addr_bv); goto error; @@ -1412,32 +1430,27 @@ static bool eval_block(RZ_NONNULL RzInterpRunContext *ctx, RZ_NONNULL RzInterpBl break; } - RZ_LOG_DEBUG("prototype: Eval PC = 0x%" PFMT64x "\n", pc); - RzStrBuf sb; - rz_strbuf_init(&sb); - rz_interp_abstr_state_as_str(ctx->inst, ctx->astate, &sb); - RZ_LOG_DEBUG("%s\n", rz_strbuf_get(&sb)); - rz_strbuf_fini(&sb); - - rz_strbuf_init(&sb); - if (pc == il_block->addr) { - rz_strbuf_append(&sb, "ENTRY "); - } - if (rz_vector_index_ptr(&il_block->il_ops->v, rz_pvector_len(il_block->il_ops) - 1) == it) { - rz_strbuf_append(&sb, "EXIT "); - } - rz_interp_abstr_state_as_str_short(ctx->inst, ctx->astate, &sb); - rz_meta_set_string(ctx->inst->a, RZ_META_TYPE_COMMENT, pc, rz_strbuf_get(&sb)); - rz_strbuf_fini(&sb); - RzILCacheInsnPkt *pkt = *it; - ctx->insn_addr = pc; // Prepare next pc, the evalutation may overwrite this. ut64 next_pc = pc + pkt->insn_pkt_size; rz_interp_abstr_state_set_pc_const(ctx->astate, next_pc); + if (interp_is_analyzing(ctx) && (ctx->res_dimen & RZ_INTERP_RESULT_DIMEN_COMMENTS)) { + RzStrBuf sb; + rz_strbuf_init(&sb); + rz_interp_abstr_state_as_str_short(ctx->inst, ctx->astate, &sb); + interp_add_comment(ctx, ctx->insn_addr, rz_strbuf_get(&sb)); + rz_strbuf_fini(&sb); + if (pc == il_block->addr) { + interp_add_comment(ctx, ctx->insn_addr, "<-"); + } + if (rz_vector_index_ptr(&il_block->il_ops->v, rz_pvector_len(il_block->il_ops) - 1) == it) { + interp_add_comment(ctx, ctx->insn_addr, "->"); + } + } + if (!eval_effect(ctx, pkt->effect, pkt->insn_pkt_size)) { return false; } @@ -1484,14 +1497,14 @@ RZ_API void rz_interp_run_context_fini(RzInterpRunContext *ctx) { /** * \brief Run the interpreter from a single entrypoint until a fixpoint is reached */ -RZ_API bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { - rz_return_val_if_fail(inst, false); +RZ_API RzInterpResult *rz_interp_run(RzInterpInstance *inst, ut64 entry_point, RzInterpResultDimen dimen) { + rz_return_val_if_fail(inst, NULL); // Initialization - bool success = false; - RzInterpRunContext ctx; + RzInterpResult *res = NULL; + RzInterpRunContext ctx = { 0 }; if (!rz_interp_run_context_init(&ctx, inst)) { - return false; + return NULL; } // Prepare the initial state from the given entry point @@ -1511,7 +1524,6 @@ RZ_API bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { RzInterpBlock *interp_block = rz_interp_run_pop(&ctx); if (!interp_block) { // No uninterpreted states left, fixpoint reached. - success = true; break; } ctx.astate = rz_interp_abstr_state_clone(inst, interp_block->entry_state); @@ -1525,64 +1537,61 @@ RZ_API bool rz_interp_run(RzInterpInstance *inst, ut64 entry_point) { rz_interp_block_resolve_bounds(&ctx, interp_block, il_block); - // DEBUG comments - RzStrBuf sb; - rz_strbuf_init(&sb); - const char *old_cmt = rz_meta_get_string(inst->a, RZ_META_TYPE_COMMENT, il_block->addr); - if (old_cmt) { - rz_strbuf_appendf(&sb, "%s; ", old_cmt); - } - rz_strbuf_append(&sb, "ENTRY "); - rz_interp_abstr_state_as_str_short(inst, ctx.astate, &sb); - // rz_meta_set_string(iset->a, RZ_META_TYPE_COMMENT, il_bb->addr, rz_strbuf_get(&sb)); - rz_strbuf_fini(&sb); // Evaluate the effect on the abstract state. if (!eval_block(&ctx, interp_block, il_block)) { // TODO: should this even be able to fail at all? RZ_LOG_ERROR("interpreter: Eval failed\n"); - success = false; goto cleanup; } } - // Fixpoint reached, evaluate all blocks again once to collect analysis information. - // We do this in an additional pass because until now, the collected abstract states - // did not fully represent all reachable concrete states. - - ctx.res = RZ_NEW0(RzInterpResult); - if (!ctx.res) { - success = false; + // Fixpoint reached, collect results. + res = RZ_NEW0(RzInterpResult); + if (!res) { goto cleanup; } - ctx.res->entry = entry_point; - rz_vector_init(&ctx.res->xrefs, sizeof(RzAnalysisXRef), NULL, NULL); + res->entry = entry_point; - RzIntervalTreeIter it; - RzInterpBlock *interp_block; - rz_interval_tree_foreach (&ctx.blocks, it, interp_block) { - ctx.astate = rz_interp_abstr_state_clone(inst, interp_block->entry_state); - const RzILCacheBlock *il_block = rz_il_cache_client_lift_il_block(inst->il_cache_client, ctx.astate->pc); - if (!il_block) { - RZ_LOG_ERROR("interpreter: Lifting failed\n"); - // TODO: handle this better - break; + if (dimen != RZ_INTERP_RESULT_DIMEN_BASE) { + // Evaluate all blocks again once to collect analysis information. + // We do this in an additional pass because until now, the collected abstract states + // did not fully represent all reachable concrete states. + if (dimen & RZ_INTERP_RESULT_DIMEN_XREFS) { + rz_vector_init(&res->xrefs, sizeof(RzAnalysisXRef), NULL, NULL); } - if (!eval_block(&ctx, interp_block, il_block)) { - // TODO: should this even be able to fail at all? - RZ_LOG_ERROR("interpreter: Eval failed\n"); - success = false; - break; + if (dimen & RZ_INTERP_RESULT_DIMEN_COMMENTS) { + res->comments = ht_up_new(NULL, free); + if (!res->comments) { + free(res); + goto cleanup; + } + } + ctx.res = res; + ctx.res_dimen = dimen; + RzIntervalTreeIter it; + RzInterpBlock *interp_block; + rz_interval_tree_foreach (&ctx.blocks, it, interp_block) { + ctx.astate = rz_interp_abstr_state_clone(inst, interp_block->entry_state); + const RzILCacheBlock *il_block = rz_il_cache_client_lift_il_block(inst->il_cache_client, ctx.astate->pc); + if (!il_block) { + RZ_LOG_ERROR("interpreter: Lifting failed\n"); + // TODO: handle this better + break; + } + if (!eval_block(&ctx, interp_block, il_block)) { + // TODO: should this even be able to fail at all? + RZ_LOG_ERROR("interpreter: Eval failed\n"); + break; + } } } - memmove(&ctx.res->blocks, &ctx.blocks, sizeof(ctx.blocks)); + memmove(&res->blocks, &ctx.blocks, sizeof(ctx.blocks)); memset(&ctx.blocks, 0, sizeof(ctx.blocks)); - rz_pvector_push(&inst->results, ctx.res); - ctx.res = NULL; cleanup: rz_interp_run_context_fini(&ctx); - return success; + return res; } /** @@ -1616,7 +1625,11 @@ RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *inst) { RZ_LOG_DEBUG("interpreter: Enter EMU\n"); rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_EMU); - if (!rz_interp_run(inst, entry_point)) { + RzInterpResult *res = rz_interp_run(inst, entry_point, RZ_INTERP_RESULT_DIMEN_XREFS | RZ_INTERP_RESULT_DIMEN_COMMENTS); // TODO: make dimensions configurable + if (res) { + // TODO: use some sort of channel for delivering results + rz_pvector_push(&inst->results, res); + } else { RZ_LOG_ERROR("Interpreter run failed for entry point 0x%" PFMT64x "\n", entry_point); } @@ -1706,10 +1719,25 @@ RZ_API void rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, R } } + // Hint: Some of the xrefs, specifically code and call ones, could be determined from + // the information in blocks as well. So an optimization could be to make the interpreter + // only emit explicit xref info for all remaining events, e.g. mem read/write. RzAnalysisXRef *xref; rz_vector_foreach (&res->xrefs, xref) { if (!rz_analysis_xrefs_set(analysis, xref->from, xref->to, xref->type)) { RZ_LOG_ERROR("failed to set xref\n"); } } + + if (res->comments) { + RzIterator *it = ht_up_as_iter_keys(res->comments); + ut64 *k; + rz_iterator_foreach(it, k) { + const char *cmt = ht_up_find(res->comments, *k, NULL); + if (cmt) { + rz_meta_set_string(analysis, RZ_META_TYPE_COMMENT, *k, cmt); + } + } + rz_iterator_free(it); + } } diff --git a/test/unit/test_inquiry_interp.c b/test/unit/test_inquiry_interp.c index c945b1dbda5..56508f339a1 100644 --- a/test/unit/test_inquiry_interp.c +++ b/test/unit/test_inquiry_interp.c @@ -11,8 +11,28 @@ typedef struct test_interp_t { RzILCache *il_cache; RzILCacheClient *il_cache_client; RzInterpInstance *inst; + RzThread *io_server; } TestInterp; +static void *io_server_th(void *user) { + TestInterp *interp = user; + RzInterpIOReadRequest io_req = { 0 }; + while (true) { + RzThreadRingBufResult r = rz_th_ring_buf_take_blocking(interp->inst->io_request_rbuf, &io_req); + if (r == RZ_THREAD_RING_BUF_CLOSED) { + return NULL; + } else if (r == RZ_THREAD_RING_BUF_OK) { + RzInterpIOResult io_res = { 0 }; + io_res.req_ok = false; // Currently we do not care about contents, just make the interpreter continue + if (rz_th_ring_buf_put(interp->inst->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { + rz_warn_if_reached(); + } + } else { + rz_warn_if_reached(); + } + } +} + static TestInterp *interp_new(const char *arch, int bits, ut64 baddr, const char *url) { // for debugging, uncomment: // eprintf("rz -a %s -b %d -m 0x%" PFMT64x " %s\n", arch, bits, baddr, url); @@ -30,10 +50,14 @@ static TestInterp *interp_new(const char *arch, int bits, ut64 baddr, const char mu_perror("load code"); return NULL; } + interp->io_server = rz_th_new(io_server_th, interp); return interp; } static void interp_free(TestInterp *interp) { + rz_th_ring_buf_close(interp->inst->io_request_rbuf); + rz_th_wait(interp->io_server); + rz_th_free(interp->io_server); rz_interp_instance_free(interp->inst); rz_il_cache_free(interp->il_cache); rz_io_free(interp->io); @@ -66,9 +90,8 @@ static ut64 block_end(RzInterpBlock *block) { #define STR(x) STR_HELPER(x) /** Extract single result from interp as well as its blocks into local vars for easy assertion */ -#define EXTRACT_RESULT(interp, blocks_count) \ - mu_assert_eq(rz_pvector_len(&interp->inst->results), 1, "results len"); \ - RzInterpResult *res = rz_pvector_at(&interp->inst->results, 0); \ +#define EXTRACT_RESULT(res, blocks_count) \ + mu_assert_notnull(res, "result"); \ RzInterpBlock *blocks[blocks_count]; \ mu_assert_eq(do_extract_blocks(res, blocks, blocks_count), blocks_count, "blocks count") @@ -84,6 +107,13 @@ static ut64 block_end(RzInterpBlock *block) { } \ } while (0) +#define ASSERT_XREF(i, from_v, to_v, type_v) do { \ + RzAnalysisXRef *xref = rz_vector_index_ptr(&res->xrefs, i); \ + mu_assert_eq(xref->from, from_v, "xref " STR(i) " from"); \ + mu_assert_eq(xref->to, to_v, "xref " STR(i) " to"); \ + mu_assert_eq(xref->type, type_v, "xref " STR(i) " type"); \ + } while (0) + #define ASSERT_ANALYSIS_BLOCK(block, start, end, jumpv, failv) do { \ mu_assert_eq(((RzAnalysisBlock *)block)->addr, (start), "analysis block start"); \ mu_assert_eq(((RzAnalysisBlock *)block)->size, (end) - (start), "analysis block size"); \ @@ -106,7 +136,7 @@ bool test_interp_block_resolve_bounds_single(void) { "000080d2" // 0x00 mov x0, 0 // --- "200080d2" // 0x04 mov x0, 1 <- entry - "400080d2" // 0x08 mov x0, 2 + "400080d2" // 0x08 mov x0, 2 "600080d2" // 0x0c mov x0, 3 "800080d2" // 0x10 mov x0, 4 "a00080d2" // 0x14 mov x0, 5 @@ -291,10 +321,9 @@ bool test_interp_cfg_single_block(void) { "c0035fd6" // 0x04 ret ); mu_assert_notnull(interp, "init"); - bool succ = rz_interp_run(interp->inst, 0x10000); - mu_assert_true(succ, "run success"); + RzInterpResult *res = rz_interp_run(interp->inst, 0x10000, RZ_INTERP_RESULT_DIMEN_BASE); - EXTRACT_RESULT(interp, 1); + EXTRACT_RESULT(res, 1); mu_assert_eq(res->entry, 0x10000, "result entry"); ASSERT_BLOCK(0, 0x10000, 0x10008, false, UT64_MAX); @@ -318,10 +347,9 @@ bool test_interp_cfg_direct_jmp(void) { "c0035fd6" // 0x14 ret ); mu_assert_notnull(interp, "init"); - bool succ = rz_interp_run(interp->inst, 0x10000); - mu_assert_true(succ, "run success"); + RzInterpResult *res = rz_interp_run(interp->inst, 0x10000, RZ_INTERP_RESULT_DIMEN_BASE); - EXTRACT_RESULT(interp, 2); + EXTRACT_RESULT(res, 2); mu_assert_eq(res->entry, 0x10000, "result entry"); ASSERT_BLOCK(0, 0x10000, 0x10008, false, 0x10010); ASSERT_BLOCK(1, 0x10010, 0x10018, false, UT64_MAX); @@ -347,10 +375,9 @@ bool test_interp_cfg_branch(void) { "c0035fd6" // 0x14 ret ); mu_assert_notnull(interp, "init"); - bool succ = rz_interp_run(interp->inst, 0x10000); - mu_assert_true(succ, "run success"); + RzInterpResult *res = rz_interp_run(interp->inst, 0x10000, RZ_INTERP_RESULT_DIMEN_BASE); - EXTRACT_RESULT(interp, 3); + EXTRACT_RESULT(res, 3); mu_assert_eq(res->entry, 0x10000, "result entry"); ASSERT_BLOCK(0, 0x10000, 0x10008, true, 0x10010); ASSERT_BLOCK(1, 0x10008, 0x10010, false, UT64_MAX); @@ -381,10 +408,9 @@ bool test_interp_cfg_branch_join(void) { "c0035fd6" // 0x14 ret <------ ); mu_assert_notnull(interp, "init"); - bool succ = rz_interp_run(interp->inst, 0x10000); - mu_assert_true(succ, "run success"); + RzInterpResult *res = rz_interp_run(interp->inst, 0x10000, RZ_INTERP_RESULT_DIMEN_BASE); - EXTRACT_RESULT(interp, 4); + EXTRACT_RESULT(res, 4); mu_assert_eq(res->entry, 0x10000, "result entry"); ASSERT_BLOCK(0, 0x10000, 0x10008, true, 0x10010); ASSERT_BLOCK(1, 0x10008, 0x10010, false, 0x10014); @@ -428,10 +454,9 @@ bool test_interp_cfg_multi_entry_fallthrough_jmp(bool swap) { "c0035fd6" // 0x18 ret ); mu_assert_notnull(interp, "init"); - bool succ = rz_interp_run(interp->inst, 0x10000); - mu_assert_true(succ, "run success"); + RzInterpResult *res = rz_interp_run(interp->inst, 0x10000, RZ_INTERP_RESULT_DIMEN_BASE); - EXTRACT_RESULT(interp, 4); + EXTRACT_RESULT(res, 4); mu_assert_eq(res->entry, 0x10000, "result entry"); ASSERT_BLOCK(0, 0x10000, 0x10004, true, !swap ? 0x10014 : 0x1000c); ASSERT_BLOCK(1, 0x10004, 0x10008, false, !swap ? 0x1000c : 0x10014); @@ -459,10 +484,9 @@ bool test_interp_cfg_multi_entry_fallthrough_jmp_before() { "c0035fd6" // 0x0c ret ); mu_assert_notnull(interp, "init"); - bool succ = rz_interp_run(interp->inst, 0x10008); - mu_assert_true(succ, "run success"); + RzInterpResult *res = rz_interp_run(interp->inst, 0x10008, RZ_INTERP_RESULT_DIMEN_BASE); - EXTRACT_RESULT(interp, 3); + EXTRACT_RESULT(res, 3); mu_assert_eq(res->entry, 0x10008, "result entry"); ASSERT_BLOCK(0, 0x10000, 0x10008, true, UT64_MAX); ASSERT_BLOCK(1, 0x10008, 0x1000c, true, 0x10000); @@ -488,10 +512,9 @@ bool test_interp_cfg_multi_entry_fallthrough_jmp_inside_self() { "c0035fd6" // 0x0c ret ); mu_assert_notnull(interp, "init"); - bool succ = rz_interp_run(interp->inst, 0x10000); - mu_assert_true(succ, "run success"); + RzInterpResult *res = rz_interp_run(interp->inst, 0x10000, RZ_INTERP_RESULT_DIMEN_BASE); - EXTRACT_RESULT(interp, 3); + EXTRACT_RESULT(res, 3); mu_assert_eq(res->entry, 0x10000, "result entry"); ASSERT_BLOCK(0, 0x10000, 0x10004, true, UT64_MAX); ASSERT_BLOCK(1, 0x10004, 0x1000c, true, 0X10004); @@ -519,10 +542,9 @@ bool test_interp_cfg_multi_entry_fallthrough_jmp_inside_other() { "c0035fd6" // 0x14 ret ); mu_assert_notnull(interp, "init"); - bool succ = rz_interp_run(interp->inst, 0x10000); - mu_assert_true(succ, "run success"); + RzInterpResult *res = rz_interp_run(interp->inst, 0x10000, RZ_INTERP_RESULT_DIMEN_BASE); - EXTRACT_RESULT(interp, 4); + EXTRACT_RESULT(res, 4); mu_assert_eq(res->entry, 0x10000, "result entry"); ASSERT_BLOCK(0, 0x10000, 0x10004, true, UT64_MAX); ASSERT_BLOCK(1, 0x10004, 0x1000c, false, 0x10010); @@ -549,10 +571,9 @@ bool test_interp_cfg_call(void) { "c0035fd6" // 0x08 ret ); mu_assert_notnull(interp, "init"); - bool succ = rz_interp_run(interp->inst, 0x10000); - mu_assert_true(succ, "run success"); + RzInterpResult *res = rz_interp_run(interp->inst, 0x10000, RZ_INTERP_RESULT_DIMEN_BASE); - EXTRACT_RESULT(interp, 2); + EXTRACT_RESULT(res, 2); mu_assert_eq(res->entry, 0x10000, "result entry"); ASSERT_BLOCK(0, 0x10000, 0x10008, true, UT64_MAX); ASSERT_BLOCK(1, 0x10008, 0x1000c, false, UT64_MAX); @@ -576,10 +597,9 @@ bool test_interp_cfg_call_multi_insn(void) { "1eff2fe1" // 0x0c bx lr ); mu_assert_notnull(interp, "init"); - bool succ = rz_interp_run(interp->inst, 0x10000); - mu_assert_true(succ, "run success"); + RzInterpResult *res = rz_interp_run(interp->inst, 0x10000, RZ_INTERP_RESULT_DIMEN_BASE); - EXTRACT_RESULT(interp, 2); + EXTRACT_RESULT(res, 2); mu_assert_eq(res->entry, 0x10000, "result entry"); ASSERT_BLOCK(0, 0x10000, 0x1000c, true, UT64_MAX); ASSERT_BLOCK(1, 0x1000c, 0x10010, false, UT64_MAX); @@ -594,6 +614,97 @@ bool test_interp_cfg_call_multi_insn(void) { mu_end; } +static int xref_cmp(const void *a, const void *b, void *user) { + const RzAnalysisXRef *ax = a; + const RzAnalysisXRef *bx = b; + return (st64)ax->from - (st64)bx->from; // addrs in our tests are small enough that this is fine +} + +bool test_interp_xrefs(void) { + TestInterp *interp = interp_new("arm", 64, 0x10000, "hex://" + "600880d2" // 0x00 mov x0, 0x43 + "a9000054" // 0x04 b.ls 0x18 ------ + "81000090" // 0x08 adrp x1, 0x20000 | + "230440f9" // 0x0c ldr x3, [x1, 8] | + "200800f9" // 0x10 str x0, [x1, 0x10] | + "fb030094" // 0x14 bl 0x1000 | + "400880d2" // 0x18 mov x0, 0x42 <----- + "c0035fd6" // 0x1c ret + ); + mu_assert_notnull(interp, "init"); + RzIODesc *data_desc = rz_io_open_at(interp->io, "malloc://0x100", RZ_PERM_RW, 0644, 0x20000, NULL); + mu_assert_notnull(data_desc, "load data"); + RzInterpResult *res = rz_interp_run(interp->inst, 0x10000, RZ_INTERP_RESULT_DIMEN_XREFS); + + EXTRACT_RESULT(res, 3); + mu_assert_eq(res->entry, 0x10000, "result entry"); + ASSERT_BLOCK(0, 0x10000, 0x10008, true, 0x10018); + ASSERT_BLOCK(1, 0x10008, 0x10018, true, UT64_MAX); + ASSERT_BLOCK(2, 0x10018, 0x10020, false, UT64_MAX); + + mu_assert_eq(rz_vector_len(&res->xrefs), 4, "xrefs count"); + rz_vector_sort(&res->xrefs, xref_cmp, false, NULL); + ASSERT_XREF(0, 0x10004, 0x10018, RZ_ANALYSIS_XREF_TYPE_CODE); + ASSERT_XREF(1, 0x1000c, 0x20008, RZ_ANALYSIS_XREF_TYPE_MEM_READ); + ASSERT_XREF(2, 0x10010, 0x20010, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); + ASSERT_XREF(3, 0x10014, 0x11000, RZ_ANALYSIS_XREF_TYPE_CALL); + + rz_interp_result_apply_to_analysis(res, interp->analysis); + RzList *l = rz_analysis_xrefs_get_from(interp->analysis, 0x1000c); + mu_assert_eq(rz_list_length(l), 1, "analysis xref"); + RzAnalysisXRef *x = rz_list_first_val(l); + mu_assert_eq(x->from, 0x1000c, "analysis xref from"); + mu_assert_eq(x->to, 0x20008, "analysis xref to"); + mu_assert_eq(x->type, RZ_ANALYSIS_XREF_TYPE_MEM_READ, "analysis xref type"); + + interp_free(interp); + mu_end; +} + +bool test_interp_comments(void) { + TestInterp *interp = interp_new("arm", 64, 0x10000, "hex://" + "600880d2" // 0x00 mov x0, 0x43 + "a9000054" // 0x04 b.ls 0x18 ------ + "81000090" // 0x08 adrp x1, 0x20000 | + "230440f9" // 0x0c ldr x3, [x1, 8] | + "200800f9" // 0x10 str x0, [x1, 0x10] | + "fb030094" // 0x14 bl 0x1000 | + "400880d2" // 0x18 mov x0, 0x42 <----- + "c0035fd6" // 0x1c ret + ); + mu_assert_notnull(interp, "init"); + RzIODesc *data_desc = rz_io_open_at(interp->io, "malloc://0x100", RZ_PERM_RW, 0644, 0x20000, NULL); + mu_assert_notnull(data_desc, "load data"); + RzInterpResult *res = rz_interp_run(interp->inst, 0x10000, RZ_INTERP_RESULT_DIMEN_COMMENTS); + + EXTRACT_RESULT(res, 3); + mu_assert_eq(res->entry, 0x10000, "result entry"); + ASSERT_BLOCK(0, 0x10000, 0x10008, true, 0x10018); + ASSERT_BLOCK(1, 0x10008, 0x10018, true, UT64_MAX); + ASSERT_BLOCK(2, 0x10018, 0x10020, false, UT64_MAX); + + mu_assert_eq(ht_up_size(res->comments), 8, "comments count"); + mu_assert_streq(ht_up_find(res->comments, 0x10000, NULL), "⊤; <-", "comment"); + mu_assert_streq(ht_up_find(res->comments, 0x10004, NULL), "x0 = 0x43; ->", "comment"); + mu_assert_streq(ht_up_find(res->comments, 0x10008, NULL), "x0 = 0x43; <-", "comment"); + mu_assert_streq(ht_up_find(res->comments, 0x1000c, NULL), "x1 = 0x20000, x0 = 0x43", "comment"); + + // contents from here could become different when mem is implemented + mu_assert_streq(ht_up_find(res->comments, 0x10010, NULL), "x1 = 0x20000, x0 = 0x43", "comment"); + mu_assert_streq(ht_up_find(res->comments, 0x10014, NULL), "x1 = 0x20000, x0 = 0x43; ->", "comment"); + mu_assert_streq(ht_up_find(res->comments, 0x10018, NULL), "⊤; <-", "comment"); + mu_assert_streq(ht_up_find(res->comments, 0x10010, NULL), "x1 = 0x20000, x0 = 0x43", "comment"); + mu_assert_streq(ht_up_find(res->comments, 0x1001c, NULL), "x0 = 0x42; ->", "comment"); + + rz_interp_result_apply_to_analysis(res, interp->analysis); + + const char *acmt = rz_meta_get_string(interp->analysis, RZ_META_TYPE_COMMENT, 0x10008); + mu_assert_streq(acmt, "x0 = 0x43; <-", "analysis comment"); + + interp_free(interp); + mu_end; +} + bool all_tests() { mu_run_test(test_interp_block_resolve_bounds_single); mu_run_test(test_interp_block_resolve_bounds_prepend, false, false); @@ -615,6 +726,8 @@ bool all_tests() { mu_run_test(test_interp_cfg_multi_entry_fallthrough_jmp_inside_other); mu_run_test(test_interp_cfg_call); mu_run_test(test_interp_cfg_call_multi_insn); + mu_run_test(test_interp_xrefs); + mu_run_test(test_interp_comments); return tests_passed != tests_run; } From 3b753559f8cc4fe7d276e826460b60247df1c1b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Thu, 16 Jul 2026 14:59:32 +0200 Subject: [PATCH 324/334] wip commands --- librz/core/cmd/cmd_inquiry.c | 15 +++ librz/core/cmd_descs/cmd_analysis.yaml | 67 +++++------ librz/core/cmd_descs/cmd_descs.c | 105 ++++++++++-------- librz/core/cmd_descs/cmd_descs.h | 6 +- test/db/cmd/cmd_help | 12 +- test/db/inquiry/interpreter/unmapped_fcn_loop | 2 +- test/db/inquiry/interpreter/xrefs | 10 +- 7 files changed, 121 insertions(+), 96 deletions(-) diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 8af664b07b2..eb59ba6cad0 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -11,6 +11,21 @@ #include #include +RZ_IPI RzCmdStatus rz_inquiry_function_handler(RzCore *core, int argc, const char **argv) { + rz_return_val_if_fail(core->analysis && core->io && core->bin->cur && core->bin->cur->o, RZ_CMD_STATUS_ERROR); + RzSetU *entry_points = rz_set_u_new(); + if (!entry_points) { + return RZ_CMD_STATUS_ERROR; + } + rz_set_u_add(entry_points, core->offset); + bool success = rz_inquiry_interpreter(core, entry_points); + if (!success) { + RZ_LOG_ERROR("Analysis failed.\n"); + return RZ_CMD_STATUS_ERROR; + } + return RZ_CMD_STATUS_OK; +} + static RzVector /**/ *get_ignored_code_regions( const RzPVector /**/ *symbols, RzPVector /**/ *sections, diff --git a/librz/core/cmd_descs/cmd_analysis.yaml b/librz/core/cmd_descs/cmd_analysis.yaml index 7ab3203c31b..f9776939a46 100644 --- a/librz/core/cmd_descs/cmd_analysis.yaml +++ b/librz/core/cmd_descs/cmd_analysis.yaml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021 RizinOrg +# SPDX-FileCopyrightTiext: 2021 RizinOrg # SPDX-License-Identifier: LGPL-3.0-only --- name: cmd_analysis @@ -16,38 +16,8 @@ commands: args: [] - name: aaaa summary: Experimental analysis - subcommands: - - name: aaaa - summary: Legacy experimental analysis - cname: analyze_everything_experimental - args: [] - - name: aaaaI - summary: New RzInquiry analysis. - subcommands: - - name: aaaaIp - summary: Abstract Interpreter Prototype - cname: inquiry_interpreter_prototype - args: - - name: f - type: RZ_CMD_ARG_TYPE_OPTION - flags: RZ_CMD_ARG_FLAG_OPTION - default_value: 0 - - name: entry_points - type: RZ_CMD_ARG_TYPE_RZNUM - flags: RZ_CMD_ARG_FLAG_ARRAY - optional: true - details: - - name: Examples - entries: - - text: "aaaaIp" - arg_str: "" - comment: "Run prototype RzIL analysis detecting cross references." - - text: "aaaaIp" - arg_str: " 0x1000" - comment: "Run prototype RzIL analysis starting at address 0x1000." - - text: "aaaaIp" - arg_str: " -f" - comment: "Run prototype RzIL analysis with function detection." + cname: analyze_everything_experimental + args: [] - name: aac summary: Analysis function calls commands subcommands: @@ -2649,3 +2619,34 @@ commands: comment: "memwrites" - text: "NOTE:" comment: "mem{reads,writes} with PIC only fetch the offset" + - name: aI + summary: New experimental RzInquiry analysis + subcommands: + - name: aIf + summary: analyze function at current seek + cname: inquiry_function + args: [] + - name: aIp + summary: Abstract Interpreter Prototype + cname: inquiry_interpreter_prototype + args: + - name: f + type: RZ_CMD_ARG_TYPE_OPTION + flags: RZ_CMD_ARG_FLAG_OPTION + default_value: 0 + - name: entry_points + type: RZ_CMD_ARG_TYPE_RZNUM + flags: RZ_CMD_ARG_FLAG_ARRAY + optional: true + details: + - name: Examples + entries: + - text: "aIp" + arg_str: "" + comment: "Run prototype RzIL analysis detecting cross references." + - text: "aIp" + arg_str: " 0x1000" + comment: "Run prototype RzIL analysis starting at address 0x1000." + - text: "aIp" + arg_str: " -f" + comment: "Run prototype RzIL analysis with function detection." diff --git a/librz/core/cmd_descs/cmd_descs.c b/librz/core/cmd_descs/cmd_descs.c index b7a20549476..559dc9db070 100644 --- a/librz/core/cmd_descs/cmd_descs.c +++ b/librz/core/cmd_descs/cmd_descs.c @@ -43,7 +43,6 @@ static const RzCmdDescDetail compare_and_set_core_num_value_details[2]; static const RzCmdDescDetail exec_cmd_if_core_num_value_positive_details[2]; static const RzCmdDescDetail exec_cmd_if_core_num_value_negative_details[2]; static const RzCmdDescDetail push_escaped_details[3]; -static const RzCmdDescDetail inquiry_interpreter_prototype_details[2]; static const RzCmdDescDetail analysis_all_esil_details[2]; static const RzCmdDescDetail analyze_all_preludes_details[2]; static const RzCmdDescDetail analysis_functions_merge_details[2]; @@ -67,6 +66,7 @@ static const RzCmdDescDetail analysis_hint_set_optype_details[2]; static const RzCmdDescDetail analysis_hint_set_immbase_details[3]; static const RzCmdDescDetail analysis_hint_set_offset_details[2]; static const RzCmdDescDetail analyze_esil_insn_access_details[4]; +static const RzCmdDescDetail inquiry_interpreter_prototype_details[2]; static const RzCmdDescDetail basefind_compute_details[2]; static const RzCmdDescDetail cmd_cmp_unified_details[2]; static const RzCmdDescDetail cw_details[2]; @@ -252,7 +252,6 @@ static const RzCmdDescArg input_msg_args[2]; static const RzCmdDescArg input_conditional_args[2]; static const RzCmdDescArg get_addr_references_args[2]; static const RzCmdDescArg push_escaped_args[2]; -static const RzCmdDescArg inquiry_interpreter_prototype_args[3]; static const RzCmdDescArg analysis_all_esil_args[2]; static const RzCmdDescArg analyze_all_consecutive_functions_in_section_args[2]; static const RzCmdDescArg analyze_xrefs_section_bytes_args[2]; @@ -430,6 +429,7 @@ static const RzCmdDescArg analyze_esil_emu_fcn_find_args_args[2]; static const RzCmdDescArg analyze_esil_int_list_load_args[2]; static const RzCmdDescArg analyze_esil_int_remove_args[2]; static const RzCmdDescArg analyze_esil_insn_access_args[4]; +static const RzCmdDescArg inquiry_interpreter_prototype_args[3]; static const RzCmdDescArg block_args[2]; static const RzCmdDescArg block_decrease_args[2]; static const RzCmdDescArg block_increase_args[2]; @@ -4040,53 +4040,14 @@ static const RzCmdDescHelp analyze_everything_help = { .args = analyze_everything_args, }; -static const RzCmdDescHelp aaaa_help = { - .summary = "Experimental analysis", -}; static const RzCmdDescArg analyze_everything_experimental_args[] = { { 0 }, }; static const RzCmdDescHelp analyze_everything_experimental_help = { - .summary = "Legacy experimental analysis", + .summary = "Experimental analysis", .args = analyze_everything_experimental_args, }; -static const RzCmdDescHelp aaaaI_help = { - .summary = "New RzInquiry analysis.", -}; -static const RzCmdDescDetailEntry inquiry_interpreter_prototype_Examples_detail_entries[] = { - { .text = "aaaaIp", .arg_str = "", .comment = "Run prototype RzIL analysis detecting cross references." }, - { .text = "aaaaIp", .arg_str = " 0x1000", .comment = "Run prototype RzIL analysis starting at address 0x1000." }, - { .text = "aaaaIp", .arg_str = " -f", .comment = "Run prototype RzIL analysis with function detection." }, - { 0 }, -}; -static const RzCmdDescDetail inquiry_interpreter_prototype_details[] = { - { .name = "Examples", .entries = inquiry_interpreter_prototype_Examples_detail_entries }, - { 0 }, -}; -static const RzCmdDescArg inquiry_interpreter_prototype_args[] = { - { - .name = "f", - .type = RZ_CMD_ARG_TYPE_OPTION, - .flags = RZ_CMD_ARG_FLAG_OPTION, - .default_value = "0", - - }, - { - .name = "entry_points", - .type = RZ_CMD_ARG_TYPE_RZNUM, - .flags = RZ_CMD_ARG_FLAG_ARRAY, - .optional = true, - - }, - { 0 }, -}; -static const RzCmdDescHelp inquiry_interpreter_prototype_help = { - .summary = "Abstract Interpreter Prototype", - .details = inquiry_interpreter_prototype_details, - .args = inquiry_interpreter_prototype_args, -}; - static const RzCmdDescHelp aac_help = { .summary = "Analysis function calls commands", }; @@ -8490,6 +8451,50 @@ static const RzCmdDescHelp analyze_esil_insn_access_help = { .args = analyze_esil_insn_access_args, }; +static const RzCmdDescHelp aI_help = { + .summary = "New experimental RzInquiry analysis", +}; +static const RzCmdDescArg inquiry_function_args[] = { + { 0 }, +}; +static const RzCmdDescHelp inquiry_function_help = { + .summary = "analyze function at current seek", + .args = inquiry_function_args, +}; + +static const RzCmdDescDetailEntry inquiry_interpreter_prototype_Examples_detail_entries[] = { + { .text = "aIp", .arg_str = "", .comment = "Run prototype RzIL analysis detecting cross references." }, + { .text = "aIp", .arg_str = " 0x1000", .comment = "Run prototype RzIL analysis starting at address 0x1000." }, + { .text = "aIp", .arg_str = " -f", .comment = "Run prototype RzIL analysis with function detection." }, + { 0 }, +}; +static const RzCmdDescDetail inquiry_interpreter_prototype_details[] = { + { .name = "Examples", .entries = inquiry_interpreter_prototype_Examples_detail_entries }, + { 0 }, +}; +static const RzCmdDescArg inquiry_interpreter_prototype_args[] = { + { + .name = "f", + .type = RZ_CMD_ARG_TYPE_OPTION, + .flags = RZ_CMD_ARG_FLAG_OPTION, + .default_value = "0", + + }, + { + .name = "entry_points", + .type = RZ_CMD_ARG_TYPE_RZNUM, + .flags = RZ_CMD_ARG_FLAG_ARRAY, + .optional = true, + + }, + { 0 }, +}; +static const RzCmdDescHelp inquiry_interpreter_prototype_help = { + .summary = "Abstract Interpreter Prototype", + .details = inquiry_interpreter_prototype_details, + .args = inquiry_interpreter_prototype_args, +}; + static const RzCmdDescHelp b_help = { .summary = "Display or change the block size", }; @@ -23036,12 +23041,8 @@ RZ_IPI void rzshell_cmddescs_init(RzCore *core) { RzCmdDesc *analyze_everything_cd = rz_cmd_desc_argv_new(core->rcmd, aa_cd, "aaa", rz_analyze_everything_handler, &analyze_everything_help); rz_warn_if_fail(analyze_everything_cd); - RzCmdDesc *aaaa_cd = rz_cmd_desc_group_new(core->rcmd, aa_cd, "aaaa", rz_analyze_everything_experimental_handler, &analyze_everything_experimental_help, &aaaa_help); - rz_warn_if_fail(aaaa_cd); - RzCmdDesc *aaaaI_cd = rz_cmd_desc_group_new(core->rcmd, aaaa_cd, "aaaaI", NULL, NULL, &aaaaI_help); - rz_warn_if_fail(aaaaI_cd); - RzCmdDesc *inquiry_interpreter_prototype_cd = rz_cmd_desc_argv_new(core->rcmd, aaaaI_cd, "aaaaIp", rz_inquiry_interpreter_prototype_handler, &inquiry_interpreter_prototype_help); - rz_warn_if_fail(inquiry_interpreter_prototype_cd); + RzCmdDesc *analyze_everything_experimental_cd = rz_cmd_desc_argv_new(core->rcmd, aa_cd, "aaaa", rz_analyze_everything_experimental_handler, &analyze_everything_experimental_help); + rz_warn_if_fail(analyze_everything_experimental_cd); RzCmdDesc *aac_cd = rz_cmd_desc_group_new(core->rcmd, aa_cd, "aac", rz_analyze_all_function_calls_handler, &analyze_all_function_calls_help, &aac_help); rz_warn_if_fail(aac_cd); @@ -23952,6 +23953,14 @@ RZ_IPI void rzshell_cmddescs_init(RzCore *core) { RzCmdDesc *aea_cd = rz_cmd_desc_group_modes_new(core->rcmd, ae_cd, "aea", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_JSON, rz_analyze_esil_insn_access_handler, &analyze_esil_insn_access_help, &aea_help); rz_warn_if_fail(aea_cd); + RzCmdDesc *aI_cd = rz_cmd_desc_group_new(core->rcmd, ae_cd, "aI", NULL, NULL, &aI_help); + rz_warn_if_fail(aI_cd); + RzCmdDesc *inquiry_function_cd = rz_cmd_desc_argv_new(core->rcmd, aI_cd, "aIf", rz_inquiry_function_handler, &inquiry_function_help); + rz_warn_if_fail(inquiry_function_cd); + + RzCmdDesc *inquiry_interpreter_prototype_cd = rz_cmd_desc_argv_new(core->rcmd, aI_cd, "aIp", rz_inquiry_interpreter_prototype_handler, &inquiry_interpreter_prototype_help); + rz_warn_if_fail(inquiry_interpreter_prototype_cd); + RzCmdDesc *b_cd = rz_cmd_desc_group_state_new(core->rcmd, root_cd, "b", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_JSON, rz_block_handler, &block_help, &b_help); rz_warn_if_fail(b_cd); RzCmdDesc *block_decrease_cd = rz_cmd_desc_argv_new(core->rcmd, b_cd, "b-", rz_block_decrease_handler, &block_decrease_help); diff --git a/librz/core/cmd_descs/cmd_descs.h b/librz/core/cmd_descs/cmd_descs.h index c9a03d15684..1b5f37e2966 100644 --- a/librz/core/cmd_descs/cmd_descs.h +++ b/librz/core/cmd_descs/cmd_descs.h @@ -342,8 +342,6 @@ RZ_IPI RzCmdStatus rz_analyze_simple_handler(RzCore *core, int argc, const char RZ_IPI RzCmdStatus rz_analyze_everything_handler(RzCore *core, int argc, const char **argv); // "aaaa" RZ_IPI RzCmdStatus rz_analyze_everything_experimental_handler(RzCore *core, int argc, const char **argv); -// "aaaaIp" -RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int argc, const char **argv); // "aac" RZ_IPI RzCmdStatus rz_analyze_all_function_calls_handler(RzCore *core, int argc, const char **argv); // "aaci" @@ -967,6 +965,10 @@ RZ_IPI RzCmdStatus rz_analyze_esil_int_list_load_handler(RzCore *core, int argc, RZ_IPI RzCmdStatus rz_analyze_esil_int_remove_handler(RzCore *core, int argc, const char **argv); // "aea" RZ_IPI RzCmdStatus rz_analyze_esil_insn_access_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode); +// "aIf" +RZ_IPI RzCmdStatus rz_inquiry_function_handler(RzCore *core, int argc, const char **argv); +// "aIp" +RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int argc, const char **argv); // "b" RZ_IPI RzCmdStatus rz_block_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state); // "b-" diff --git a/test/db/cmd/cmd_help b/test/db/cmd/cmd_help index e0a76033375..0c15c9b3f69 100644 --- a/test/db/cmd/cmd_help +++ b/test/db/cmd/cmd_help @@ -18,8 +18,7 @@ EOF EXPECT=< ...] # Abstract Interpreter Prototype +| aaaa # Experimental analysis | aac # Analyze function calls | aaci # Analyze all function calls to imports | aaC # Analysis classes from RzBin @@ -65,7 +64,7 @@ CMDS=< ...]","args":[{"type":"option","name":"f","required":true,"is_option":true,"default":"0"},{"type":"expression","name":"entry_points","is_array":true}],"description":"","summary":"Abstract Interpreter Prototype","details":[{"name":"Examples","entries":[{"text":"aaaaIp","comment":"Run prototype RzIL analysis detecting cross references.","arg_str":""},{"text":"aaaaIp","comment":"Run prototype RzIL analysis starting at address 0x1000.","arg_str":" 0x1000"},{"text":"aaaaIp","comment":"Run prototype RzIL analysis with function detection.","arg_str":" -f"}]}]},"aac":{"cmd":"aac","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze function calls","details":[]},"aaci":{"cmd":"aaci","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all function calls to imports","details":[]},"aaC":{"cmd":"aaC","type":"argv","args_str":"","args":[],"description":"","summary":"Analysis classes from RzBin","details":[]},"aad":{"cmd":"aad","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze data references to code","details":[]},"aae":{"cmd":"aae","type":"argv","args_str":" []","args":[{"type":"expression","name":"len","is_last":true}],"description":"","summary":"Analyze references with ESIL","details":[{"name":"Examples","entries":[{"text":"aae","comment":"analyze ranges given by analysis.in","arg_str":""},{"text":"aae","comment":"analyze the whole section","arg_str":" $SS @ $S"}]}]},"aaef":{"cmd":"aaef","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze references with ESIL in all functions","details":[]},"aaf":{"cmd":"aaf","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions","details":[]},"aafe":{"cmd":"aafe","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions using ESIL","details":[]},"aafr":{"cmd":"aafr","type":"argv","args_str":" ","args":[{"type":"number","name":"length","required":true}],"description":"","summary":"Analyze all consecutive functions in section","details":[]},"aaft":{"cmd":"aaft","type":"argv","args_str":"","args":[],"description":"","summary":"Performs recursive type matching in all functions","details":[]},"aai":{"cmd":"aai","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details","details":[]},"aaij":{"cmd":"aaij","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details (JSON mode)","details":[]},"aaj":{"cmd":"aaj","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all unresolved jumps","details":[]},"aalg":{"cmd":"aalg","type":"argv","args_str":"","args":[],"description":"","summary":"Recover and analyze all Golang functions and strings","details":[]},"aalor":{"cmd":"aalor","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all Objective-C references from selector usages to their implementations","details":[]},"aalos":{"cmd":"aalos","type":"argv","args_str":"","args":[],"description":"","summary":"Recover all Objective-C selector stub names (__objc_stubs section contents)","details":[]},"aan":{"cmd":"aan","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions based on their strings or calls","details":[]},"aanr":{"cmd":"aanr","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions which does not return","details":[]},"aap":{"cmd":"aap","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all preludes","details":[{"name":"Search a custom prelude","entries":[{"text":"e analysis.prelude='90AEF630'","comment":"Set new prelude","arg_str":""},{"text":"aap","comment":"Search for 90AEF630 and create a new function","arg_str":""}]}]},"aar":{"cmd":"aar","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Analyze xrefs in current section or by n_bytes","details":[]},"aas":{"cmd":"aas","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the symbols","details":[]},"aaS":{"cmd":"aaS","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the flags starting as sym.* and entry*","details":[]},"aat":{"cmd":"aat","type":"argv","args_str":" []","args":[{"type":"function","name":"func_name"}],"description":"","summary":"Analyze all/given function to convert immediate to linked structure offsets","details":[]},"aaT":{"cmd":"aaT","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Prints commands to create functions after a trap call","details":[]},"aau":{"cmd":"aau","type":"argv","args_str":" []","args":[{"type":"number","name":"min_len"}],"description":"","summary":"Print memory areas not covered by functions","details":[]},"aav":{"cmd":"aav","type":"argv_state","args_str":"","args":[],"description":"","summary":"Analyze values referencing a specific section or map","details":[]}} +{"aa":{"cmd":"aa","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all flags starting with sym. and entry","details":[]},"aaa":{"cmd":"aaa","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all calls, references, emulation and applies signatures","details":[]},"aaaa":{"cmd":"aaaa","type":"argv","args_str":"","args":[],"description":"","summary":"Experimental analysis","details":[]},"aac":{"cmd":"aac","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze function calls","details":[]},"aaci":{"cmd":"aaci","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all function calls to imports","details":[]},"aaC":{"cmd":"aaC","type":"argv","args_str":"","args":[],"description":"","summary":"Analysis classes from RzBin","details":[]},"aad":{"cmd":"aad","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze data references to code","details":[]},"aae":{"cmd":"aae","type":"argv","args_str":" []","args":[{"type":"expression","name":"len","is_last":true}],"description":"","summary":"Analyze references with ESIL","details":[{"name":"Examples","entries":[{"text":"aae","comment":"analyze ranges given by analysis.in","arg_str":""},{"text":"aae","comment":"analyze the whole section","arg_str":" $SS @ $S"}]}]},"aaef":{"cmd":"aaef","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze references with ESIL in all functions","details":[]},"aaf":{"cmd":"aaf","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions","details":[]},"aafe":{"cmd":"aafe","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions using ESIL","details":[]},"aafr":{"cmd":"aafr","type":"argv","args_str":" ","args":[{"type":"number","name":"length","required":true}],"description":"","summary":"Analyze all consecutive functions in section","details":[]},"aaft":{"cmd":"aaft","type":"argv","args_str":"","args":[],"description":"","summary":"Performs recursive type matching in all functions","details":[]},"aai":{"cmd":"aai","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details","details":[]},"aaij":{"cmd":"aaij","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details (JSON mode)","details":[]},"aaj":{"cmd":"aaj","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all unresolved jumps","details":[]},"aalg":{"cmd":"aalg","type":"argv","args_str":"","args":[],"description":"","summary":"Recover and analyze all Golang functions and strings","details":[]},"aalor":{"cmd":"aalor","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all Objective-C references from selector usages to their implementations","details":[]},"aalos":{"cmd":"aalos","type":"argv","args_str":"","args":[],"description":"","summary":"Recover all Objective-C selector stub names (__objc_stubs section contents)","details":[]},"aan":{"cmd":"aan","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions based on their strings or calls","details":[]},"aanr":{"cmd":"aanr","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions which does not return","details":[]},"aap":{"cmd":"aap","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all preludes","details":[{"name":"Search a custom prelude","entries":[{"text":"e analysis.prelude='90AEF630'","comment":"Set new prelude","arg_str":""},{"text":"aap","comment":"Search for 90AEF630 and create a new function","arg_str":""}]}]},"aar":{"cmd":"aar","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Analyze xrefs in current section or by n_bytes","details":[]},"aas":{"cmd":"aas","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the symbols","details":[]},"aaS":{"cmd":"aaS","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the flags starting as sym.* and entry*","details":[]},"aat":{"cmd":"aat","type":"argv","args_str":" []","args":[{"type":"function","name":"func_name"}],"description":"","summary":"Analyze all/given function to convert immediate to linked structure offsets","details":[]},"aaT":{"cmd":"aaT","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Prints commands to create functions after a trap call","details":[]},"aau":{"cmd":"aau","type":"argv","args_str":" []","args":[{"type":"number","name":"min_len"}],"description":"","summary":"Print memory areas not covered by functions","details":[]},"aav":{"cmd":"aav","type":"argv_state","args_str":"","args":[],"description":"","summary":"Analyze values referencing a specific section or map","details":[]}} EOF RUN @@ -77,8 +76,7 @@ EOF EXPECT=< ...] # Abstract Interpreter Prototype +| aaaa # Experimental analysis | aac # Analyze function calls | aaci # Analyze all function calls to imports | aaC # Analysis classes from RzBin @@ -115,7 +113,7 @@ EXPECT=< ...]","args":[{"type":"option","name":"f","required":true,"is_option":true,"default":"0"},{"type":"expression","name":"entry_points","is_array":true}],"description":"","summary":"Abstract Interpreter Prototype","details":[{"name":"Examples","entries":[{"text":"aaaaIp","comment":"Run prototype RzIL analysis detecting cross references.","arg_str":""},{"text":"aaaaIp","comment":"Run prototype RzIL analysis starting at address 0x1000.","arg_str":" 0x1000"},{"text":"aaaaIp","comment":"Run prototype RzIL analysis with function detection.","arg_str":" -f"}]}]},"aac":{"cmd":"aac","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze function calls","details":[]},"aaci":{"cmd":"aaci","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all function calls to imports","details":[]},"aaC":{"cmd":"aaC","type":"argv","args_str":"","args":[],"description":"","summary":"Analysis classes from RzBin","details":[]},"aad":{"cmd":"aad","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze data references to code","details":[]},"aae":{"cmd":"aae","type":"argv","args_str":" []","args":[{"type":"expression","name":"len","is_last":true}],"description":"","summary":"Analyze references with ESIL","details":[{"name":"Examples","entries":[{"text":"aae","comment":"analyze ranges given by analysis.in","arg_str":""},{"text":"aae","comment":"analyze the whole section","arg_str":" $SS @ $S"}]}]},"aaef":{"cmd":"aaef","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze references with ESIL in all functions","details":[]},"aaf":{"cmd":"aaf","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions","details":[]},"aafe":{"cmd":"aafe","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions using ESIL","details":[]},"aafr":{"cmd":"aafr","type":"argv","args_str":" ","args":[{"type":"number","name":"length","required":true}],"description":"","summary":"Analyze all consecutive functions in section","details":[]},"aaft":{"cmd":"aaft","type":"argv","args_str":"","args":[],"description":"","summary":"Performs recursive type matching in all functions","details":[]},"aai":{"cmd":"aai","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details","details":[]},"aaij":{"cmd":"aaij","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details (JSON mode)","details":[]},"aaj":{"cmd":"aaj","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all unresolved jumps","details":[]},"aalg":{"cmd":"aalg","type":"argv","args_str":"","args":[],"description":"","summary":"Recover and analyze all Golang functions and strings","details":[]},"aalor":{"cmd":"aalor","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all Objective-C references from selector usages to their implementations","details":[]},"aalos":{"cmd":"aalos","type":"argv","args_str":"","args":[],"description":"","summary":"Recover all Objective-C selector stub names (__objc_stubs section contents)","details":[]},"aan":{"cmd":"aan","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions based on their strings or calls","details":[]},"aanr":{"cmd":"aanr","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions which does not return","details":[]},"aap":{"cmd":"aap","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all preludes","details":[{"name":"Search a custom prelude","entries":[{"text":"e analysis.prelude='90AEF630'","comment":"Set new prelude","arg_str":""},{"text":"aap","comment":"Search for 90AEF630 and create a new function","arg_str":""}]}]},"aar":{"cmd":"aar","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Analyze xrefs in current section or by n_bytes","details":[]},"aas":{"cmd":"aas","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the symbols","details":[]},"aaS":{"cmd":"aaS","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the flags starting as sym.* and entry*","details":[]},"aat":{"cmd":"aat","type":"argv","args_str":" []","args":[{"type":"function","name":"func_name"}],"description":"","summary":"Analyze all/given function to convert immediate to linked structure offsets","details":[]},"aaT":{"cmd":"aaT","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Prints commands to create functions after a trap call","details":[]},"aau":{"cmd":"aau","type":"argv","args_str":" []","args":[{"type":"number","name":"min_len"}],"description":"","summary":"Print memory areas not covered by functions","details":[]},"aav":{"cmd":"aav","type":"argv_state","args_str":"","args":[],"description":"","summary":"Analyze values referencing a specific section or map","details":[]}} +{"aa":{"cmd":"aa","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all flags starting with sym. and entry","details":[]},"aaa":{"cmd":"aaa","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all calls, references, emulation and applies signatures","details":[]},"aaaa":{"cmd":"aaaa","type":"argv","args_str":"","args":[],"description":"","summary":"Experimental analysis","details":[]},"aac":{"cmd":"aac","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze function calls","details":[]},"aaci":{"cmd":"aaci","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all function calls to imports","details":[]},"aaC":{"cmd":"aaC","type":"argv","args_str":"","args":[],"description":"","summary":"Analysis classes from RzBin","details":[]},"aad":{"cmd":"aad","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze data references to code","details":[]},"aae":{"cmd":"aae","type":"argv","args_str":" []","args":[{"type":"expression","name":"len","is_last":true}],"description":"","summary":"Analyze references with ESIL","details":[{"name":"Examples","entries":[{"text":"aae","comment":"analyze ranges given by analysis.in","arg_str":""},{"text":"aae","comment":"analyze the whole section","arg_str":" $SS @ $S"}]}]},"aaef":{"cmd":"aaef","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze references with ESIL in all functions","details":[]},"aaf":{"cmd":"aaf","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions","details":[]},"aafe":{"cmd":"aafe","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all functions using ESIL","details":[]},"aafr":{"cmd":"aafr","type":"argv","args_str":" ","args":[{"type":"number","name":"length","required":true}],"description":"","summary":"Analyze all consecutive functions in section","details":[]},"aaft":{"cmd":"aaft","type":"argv","args_str":"","args":[],"description":"","summary":"Performs recursive type matching in all functions","details":[]},"aai":{"cmd":"aai","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details","details":[]},"aaij":{"cmd":"aaij","type":"argv_state","args_str":"","args":[],"description":"","summary":"Print preformed analysis details (JSON mode)","details":[]},"aaj":{"cmd":"aaj","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all unresolved jumps","details":[]},"aalg":{"cmd":"aalg","type":"argv","args_str":"","args":[],"description":"","summary":"Recover and analyze all Golang functions and strings","details":[]},"aalor":{"cmd":"aalor","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all Objective-C references from selector usages to their implementations","details":[]},"aalos":{"cmd":"aalos","type":"argv","args_str":"","args":[],"description":"","summary":"Recover all Objective-C selector stub names (__objc_stubs section contents)","details":[]},"aan":{"cmd":"aan","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions based on their strings or calls","details":[]},"aanr":{"cmd":"aanr","type":"argv","args_str":"","args":[],"description":"","summary":"Renames all functions which does not return","details":[]},"aap":{"cmd":"aap","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze all preludes","details":[{"name":"Search a custom prelude","entries":[{"text":"e analysis.prelude='90AEF630'","comment":"Set new prelude","arg_str":""},{"text":"aap","comment":"Search for 90AEF630 and create a new function","arg_str":""}]}]},"aar":{"cmd":"aar","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Analyze xrefs in current section or by n_bytes","details":[]},"aas":{"cmd":"aas","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the symbols","details":[]},"aaS":{"cmd":"aaS","type":"argv","args_str":"","args":[],"description":"","summary":"Analyze only the flags starting as sym.* and entry*","details":[]},"aat":{"cmd":"aat","type":"argv","args_str":" []","args":[{"type":"function","name":"func_name"}],"description":"","summary":"Analyze all/given function to convert immediate to linked structure offsets","details":[]},"aaT":{"cmd":"aaT","type":"argv","args_str":" []","args":[{"type":"number","name":"n_bytes"}],"description":"","summary":"Prints commands to create functions after a trap call","details":[]},"aau":{"cmd":"aau","type":"argv","args_str":" []","args":[{"type":"number","name":"min_len"}],"description":"","summary":"Print memory areas not covered by functions","details":[]},"aav":{"cmd":"aav","type":"argv_state","args_str":"","args":[],"description":"","summary":"Analyze values referencing a specific section or map","details":[]}} EOF RUN diff --git a/test/db/inquiry/interpreter/unmapped_fcn_loop b/test/db/inquiry/interpreter/unmapped_fcn_loop index bbc206b0945..041fdd62217 100644 --- a/test/db/inquiry/interpreter/unmapped_fcn_loop +++ b/test/db/inquiry/interpreter/unmapped_fcn_loop @@ -1,7 +1,7 @@ NAME=Indirect call x86 FILE=bins/inquiry/interpreter/prototype/x86_unmapped_fcn_in_loop CMDS=< Date: Fri, 17 Jul 2026 11:48:43 +0200 Subject: [PATCH 325/334] Isolate interpreter driver and restructure commands --- librz/core/cmd/cmd_inquiry.c | 6 +- librz/core/cmd_descs/cmd_analysis.yaml | 4 +- librz/core/cmd_descs/cmd_descs.c | 10 +- librz/core/cmd_descs/cmd_descs.h | 2 +- librz/include/rz_inquiry/rz_interpreter.h | 2 + librz/inquiry/README.md | 6 + librz/inquiry/inquiry.c | 570 ---------------------- librz/inquiry/interp/driver.c | 316 ++++++++++++ librz/inquiry/interp/interpreter.c | 3 +- librz/inquiry/meson.build | 1 + 10 files changed, 338 insertions(+), 582 deletions(-) create mode 100644 librz/inquiry/interp/driver.c diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index eb59ba6cad0..03c3dc65ac8 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -11,14 +11,14 @@ #include #include -RZ_IPI RzCmdStatus rz_inquiry_function_handler(RzCore *core, int argc, const char **argv) { +RZ_IPI RzCmdStatus rz_inquiry_analyze_function_handler(RzCore *core, int argc, const char **argv) { rz_return_val_if_fail(core->analysis && core->io && core->bin->cur && core->bin->cur->o, RZ_CMD_STATUS_ERROR); RzSetU *entry_points = rz_set_u_new(); if (!entry_points) { return RZ_CMD_STATUS_ERROR; } rz_set_u_add(entry_points, core->offset); - bool success = rz_inquiry_interpreter(core, entry_points); + bool success = rz_interp_driver_run(core, entry_points); if (!success) { RZ_LOG_ERROR("Analysis failed.\n"); return RZ_CMD_STATUS_ERROR; @@ -79,7 +79,7 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar rz_set_u_add(entry_points, entry_point); } } - bool success = rz_inquiry_interpreter(core, entry_points); + bool success = rz_interp_driver_run(core, entry_points); eprintf("Finished reference recovery: %s\n", success ? "OK" : "FAIL"); if (!success) { return RZ_CMD_STATUS_ERROR; diff --git a/librz/core/cmd_descs/cmd_analysis.yaml b/librz/core/cmd_descs/cmd_analysis.yaml index f9776939a46..9f23cf3319a 100644 --- a/librz/core/cmd_descs/cmd_analysis.yaml +++ b/librz/core/cmd_descs/cmd_analysis.yaml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightTiext: 2021 RizinOrg +# SPDX-FileCopyrightText: 2021 RizinOrg # SPDX-License-Identifier: LGPL-3.0-only --- name: cmd_analysis @@ -2624,7 +2624,7 @@ commands: subcommands: - name: aIf summary: analyze function at current seek - cname: inquiry_function + cname: inquiry_analyze_function args: [] - name: aIp summary: Abstract Interpreter Prototype diff --git a/librz/core/cmd_descs/cmd_descs.c b/librz/core/cmd_descs/cmd_descs.c index 559dc9db070..a7d33237f30 100644 --- a/librz/core/cmd_descs/cmd_descs.c +++ b/librz/core/cmd_descs/cmd_descs.c @@ -8454,12 +8454,12 @@ static const RzCmdDescHelp analyze_esil_insn_access_help = { static const RzCmdDescHelp aI_help = { .summary = "New experimental RzInquiry analysis", }; -static const RzCmdDescArg inquiry_function_args[] = { +static const RzCmdDescArg inquiry_analyze_function_args[] = { { 0 }, }; -static const RzCmdDescHelp inquiry_function_help = { +static const RzCmdDescHelp inquiry_analyze_function_help = { .summary = "analyze function at current seek", - .args = inquiry_function_args, + .args = inquiry_analyze_function_args, }; static const RzCmdDescDetailEntry inquiry_interpreter_prototype_Examples_detail_entries[] = { @@ -23955,8 +23955,8 @@ RZ_IPI void rzshell_cmddescs_init(RzCore *core) { RzCmdDesc *aI_cd = rz_cmd_desc_group_new(core->rcmd, ae_cd, "aI", NULL, NULL, &aI_help); rz_warn_if_fail(aI_cd); - RzCmdDesc *inquiry_function_cd = rz_cmd_desc_argv_new(core->rcmd, aI_cd, "aIf", rz_inquiry_function_handler, &inquiry_function_help); - rz_warn_if_fail(inquiry_function_cd); + RzCmdDesc *inquiry_analyze_function_cd = rz_cmd_desc_argv_new(core->rcmd, aI_cd, "aIf", rz_inquiry_analyze_function_handler, &inquiry_analyze_function_help); + rz_warn_if_fail(inquiry_analyze_function_cd); RzCmdDesc *inquiry_interpreter_prototype_cd = rz_cmd_desc_argv_new(core->rcmd, aI_cd, "aIp", rz_inquiry_interpreter_prototype_handler, &inquiry_interpreter_prototype_help); rz_warn_if_fail(inquiry_interpreter_prototype_cd); diff --git a/librz/core/cmd_descs/cmd_descs.h b/librz/core/cmd_descs/cmd_descs.h index 1b5f37e2966..0a3f106570e 100644 --- a/librz/core/cmd_descs/cmd_descs.h +++ b/librz/core/cmd_descs/cmd_descs.h @@ -966,7 +966,7 @@ RZ_IPI RzCmdStatus rz_analyze_esil_int_remove_handler(RzCore *core, int argc, co // "aea" RZ_IPI RzCmdStatus rz_analyze_esil_insn_access_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode); // "aIf" -RZ_IPI RzCmdStatus rz_inquiry_function_handler(RzCore *core, int argc, const char **argv); +RZ_IPI RzCmdStatus rz_inquiry_analyze_function_handler(RzCore *core, int argc, const char **argv); // "aIp" RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int argc, const char **argv); // "b" diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 9aa1e45b1f8..ac3b1059827 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -376,4 +376,6 @@ RZ_API void rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, R extern RZ_API RzInterpValueAbstraction rz_interp_value_domain_const; +RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points); + #endif // RZ_INTERPRETER diff --git a/librz/inquiry/README.md b/librz/inquiry/README.md index ddc4c868b13..92ad293a1e4 100644 --- a/librz/inquiry/README.md +++ b/librz/inquiry/README.md @@ -5,6 +5,12 @@ Module implementing basic and advanced binary analysis. +## TODO + +* remove yield bufs +* rename interp to absint +* rename valeabstraction to valuedomain + ## Interpreter Notes Ideas for optimization (do not implement any of these without profiling first): diff --git a/librz/inquiry/inquiry.c b/librz/inquiry/inquiry.c index 015a2839b38..5d77d9bc4ed 100644 --- a/librz/inquiry/inquiry.c +++ b/librz/inquiry/inquiry.c @@ -7,18 +7,11 @@ #include "rz_analysis.h" #include "rz_bin.h" -#include "rz_cons.h" -#include "rz_il/definitions/mem.h" #include "rz_inquiry/rz_bcfg.h" -#include "rz_inquiry/rz_il_cache.h" -#include "rz_inquiry/rz_interpreter.h" -#include "rz_th.h" #include "rz_types.h" #include "rz_util/ht_pp.h" #include "rz_util/ht_sp.h" #include "rz_util/ht_up.h" -#include "rz_util/rz_bitvector.h" -#include "rz_util/rz_buf.h" #include "rz_util/rz_graph.h" #include "rz_util/rz_iterator.h" #include "rz_util/rz_log.h" @@ -202,32 +195,6 @@ RZ_API bool rz_inquiry_xref_interpreter_filter(RZ_NONNULL const RzAnalysisXRef * return false; } -static void handle_io_request(RzAnalysisILContext *il_ctx, RzInterpIOReadRequest *io_req, RZ_OUT RzInterpIOResult *io_res) { - RZ_LOG_DEBUG("inquiry: Received IO read request: mem:%" PFMTSZd " 0x%" PFMT64x "\n", - io_req->mem_idx, - rz_bv_to_ut64(io_req->addr)); - io_res->req_ok = false; - RzILMemIndex mem_idx = io_req->mem_idx; - if (mem_idx > rz_vector_len(&il_ctx->memory)) { - rz_warn_if_reached(); - return; - } - if (rz_bv_len(io_req->addr) == 64 && rz_bv_msb(io_req->addr)) { - RZ_LOG_ERROR("Due to the Unix seek() implementation, addresses with the " - "63 bit set can't be addresses.\n"); - return; - } - RzAnalysisILMem *mem = rz_vector_index_ptr(&il_ctx->memory, mem_idx); - if (!mem->base_buf) { - io_res->req_ok = false; - } else { - // TODO: here only memory should be read that can be assumed to be constant! - io_res->req_ok = rz_il_loadw_into(mem->base_buf, io_req->ld_data, io_req->addr, io_req->n_bits, io_req->big_endian); - } - RZ_LOG_DEBUG("inquiry: Sent IO read result. Success = %s.\n", - rz_str_bool(io_res->req_ok)); -} - RZ_API bool rz_inquiry_get_fcn_symbol_addr(RzCore *core, RZ_OUT RzSetU *symbol_targets) { rz_return_val_if_fail(core && symbol_targets, false); RzPVector /**/ *sections = rz_bin_object_get_sections(core->bin->cur->o); @@ -262,543 +229,6 @@ RZ_API bool rz_inquiry_get_fcn_symbol_addr(RzCore *core, RZ_OUT RzSetU *symbol_t return true; } -static bool get_branch_targets(RzCore *core, RzSetU *branch_targets) { - RzPVector /**/ *sections = rz_bin_object_get_sections(core->bin->cur->o); - if (!sections) { - rz_warn_if_reached(); - return false; - } - RzVector *non_x_idx = rz_vector_new(sizeof(size_t), NULL, NULL); - void **it; - size_t i; - rz_pvector_enumerate (sections, it, i) { - RzBinSection *sec = *it; - if (!(sec->perm & RZ_PERM_X)) { - rz_vector_push(non_x_idx, &i); - } - } - size_t *j; - rz_vector_foreach_prev (non_x_idx, j) { - rz_pvector_remove_at(sections, *j); - } - rz_vector_free(non_x_idx); - if (!rz_analysis_get_all_cf_targets(core->analysis, sections, true, branch_targets)) { - RZ_LOG_ERROR("Failed to get branch targets.\n"); - return false; - } - rz_pvector_free(sections); - return true; -} - -static bool log_control_flow(RzInquiry *inquiry, RzInterpCtrlFlow *cf) { - RZ_LOG_DEBUG("inquiry: Received control flow: 0x%" PFMT64x " size: %" PFMTSZu " (alt: 0x%" PFMT64x ")\n", - cf->target_addr, cf->target_block_size, cf->alt_target); - rz_inquiry_bcfg_add_block(inquiry->bcfg, cf->actual_target, cf->target_block_size); - if (cf->alt_target) { - // Add a dummy basic block at the address the call originally jumped to. - // This is the basic block for the imported function. - rz_inquiry_bcfg_add_block(inquiry->bcfg, cf->target_addr, 1); - } - // Add a simple control flow edge here. - // It gets later updated to another type if a reported xref has it. - rz_inquiry_bcfg_add_edge(inquiry->bcfg, cf->src_block_addr, cf->actual_target, cf->type); - return true; -} - -static bool handle_yields(RzInquiry *inquiry, RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM]) { - RzInterpYieldRBuf *rbuf_xrefs = yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; - rz_return_val_if_fail(rbuf_xrefs, false); - - RzAnalysisXRef xref = { 0 }; - if (!rz_th_ring_buf_is_empty_unsafe(rbuf_xrefs->rbuf)) { - RzThreadRingBufResult r = rz_th_ring_buf_take(rbuf_xrefs->rbuf, &xref); - if (r == RZ_THREAD_RING_BUF_CLOSED) { - rz_warn_if_reached(); - return false; - } else if (r == RZ_THREAD_RING_BUF_OK) { - rz_inquiry_add_xref(inquiry, &xref); - RZ_LOG_DEBUG("inquiry: Added xref: 0x%" PFMT64x " -> 0x%" PFMT64x " (%s)\n", xref.from, xref.to, rz_analysis_ref_type_tostring(xref.type)); - } - } - - RzInterpYieldRBuf *rbuf_cf = yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW]; - if (!rz_th_ring_buf_is_empty_unsafe(rbuf_cf->rbuf)) { - RzInterpCtrlFlow cf = { 0 }; - RzThreadRingBufResult r = rz_th_ring_buf_take(rbuf_cf->rbuf, &cf); - if (r == RZ_THREAD_RING_BUF_CLOSED) { - rz_warn_if_reached(); - return false; - } else if (r == RZ_THREAD_RING_BUF_OK) { - log_control_flow(inquiry, &cf); - } - } - - RzInterpYieldRBuf *rbuf_calls = yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; - rz_return_val_if_fail(rbuf_calls, false); - - RzAnalysisCallCandidate cc = { 0 }; - if (!rz_th_ring_buf_is_empty_unsafe(rbuf_calls->rbuf)) { - RzThreadRingBufResult r = rz_th_ring_buf_take(rbuf_calls->rbuf, &cc); - if (r == RZ_THREAD_RING_BUF_CLOSED) { - rz_warn_if_reached(); - return false; - } else if (r == RZ_THREAD_RING_BUF_OK) { - RzAnalysisCallCandidate *cc_clone = RZ_NEW0(RzAnalysisCallCandidate); - memcpy(cc_clone, &cc, sizeof(RzAnalysisCallCandidate)); - if (ht_up_update(inquiry->call_candidates, cc_clone->bb_addr, cc_clone)) { - RZ_LOG_DEBUG("inquiry: Overwrote a call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); - } else { - RZ_LOG_DEBUG("inquiry: Added call candidate located at 0x%" PFMT64x "\n", cc_clone->candidate_addr); - } - } - } - return true; -} - -/** - * \brief Removes entry points which were requested from the IL cache - * (and hence have been interpreted). - * Returns false if no entry points are left to interpret. - * True otherwise. - */ -// TODO: Optimize -static bool reduce_get_entry_points( - RzInterpInstance *iset, - RzILCache *il_cache, - RZ_BORROW RzSetU /**/ *entry_points) { - // Add the next entry point we need to check for executable regions the interpreters did not cover. - // For this we simply delete all entry points which point - // into the already handled basic blocks. - // Then add a few addresses as new entry point. - // The addresses we add are jump targets from jump/call instructions in the binary. - - RzVector to_del = { 0 }; - rz_vector_init(&to_del, sizeof(ut64), NULL, NULL); - - RzIterator *entries = rz_set_u_as_iter(entry_points); - ut64 *ct; - rz_iterator_foreach(entries, ct) { - // This call target was interpreted before (hence is in the IL cache). - if (rz_il_cache_was_requested(il_cache, *ct)) { - rz_vector_push(&to_del, ct); - } - } - rz_iterator_free(entries); - - rz_vector_foreach (&to_del, ct) { - rz_set_u_delete(entry_points, *ct); - } - rz_vector_fini(&to_del); - - if (rz_set_u_size(entry_points) == 0) { - return false; - } - return true; -} - -static void close_reset_ipc_obj(RzInterpInstance *iset) { - // Close and clear all the IPC objects of this interpreter. - // This also clears the buffer and queues - rz_th_ring_buf_close(iset->io_request_rbuf); - rz_th_ring_buf_close(iset->io_result_rbuf); - rz_th_ring_buf_close(iset->entry_points); - rz_th_ring_buf_close(iset->il_cache_client->req_rbuf); - rz_th_queue_close(iset->il_cache_client->il_queue); - rz_list_free(rz_th_queue_pop_all(iset->il_cache_client->il_queue)); -} - -static void open_ipc_obj(RzInterpInstance *iset) { - // Open queue again, so the interpretation can start at another - // jump target again. - rz_th_ring_buf_open(iset->io_request_rbuf); - rz_th_ring_buf_open(iset->io_result_rbuf); - rz_th_ring_buf_open(iset->il_cache_client->req_rbuf); - rz_th_queue_open(iset->il_cache_client->il_queue); - rz_th_ring_buf_open(iset->entry_points); -} - -static bool collect_entry_points(RzCore *core, - RzSetU *entry_points, - RzSetU *symbol_targets) { - - if (!get_branch_targets(core, entry_points) || - !rz_inquiry_get_fcn_symbol_addr(core, symbol_targets)) { - rz_warn_if_reached(); - return false; - } - RzIterator *iter = rz_set_u_as_iter(symbol_targets); - ut64 *sym_addr; - rz_iterator_foreach(iter, sym_addr) { - rz_set_u_add(entry_points, *sym_addr); - } - rz_iterator_free(iter); - rz_set_u_free(symbol_targets); - return true; -} - -static bool setup_yield_rbufs( - RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM], - RZ_OWN RzPVector /**/ *sections, - RzInterpYieldFilter yield_filter) { - // A single interpreter can produce different yields. - // E.g. if the interpreter has a complex abstract memory model - // for stack, heap and constant values. - // Then it can produce three kind of yields. - // These yield queues can be shared between different interpreters. - // So we have one yield queue for each yield type. - - RzInterpYieldKind yield_kind = RZ_INTERP_YIELD_KIND_CALL_CANDIDATE; - RzInterpYieldRBuf *rbuf = NULL; - rbuf = rz_interp_yield_rbuf_new(yield_kind, NULL, NULL); - if (!rbuf) { - rz_warn_if_reached(); - return false; - } - yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] = rbuf; - - yield_kind = RZ_INTERP_YIELD_KIND_CONTROL_FLOW; - rbuf = NULL; - rbuf = rz_interp_yield_rbuf_new(yield_kind, NULL, NULL); - if (!rbuf) { - rz_warn_if_reached(); - return false; - } - yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] = rbuf; - - yield_kind = RZ_INTERP_YIELD_KIND_XREF; - rbuf = rz_interp_yield_rbuf_new( - yield_kind, - yield_filter, - sections); - if (!rbuf) { - rz_warn_if_reached(); - return false; - } - yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] = rbuf; - return true; -} - -struct ituple { - RzThread *ithread; - RzInterpInstance *iset; - RzInterpRunStateFlag next_run_state; -}; - -/** - * A function to call the prototype interpreter. - * Usually these tasks will be split between different caches and yield consumers. - */ -RZ_API bool rz_inquiry_interpreter(RzCore *core, - RZ_OWN RzSetU *entry_points) { - // All the things we need - bool return_code = true; - RzInterpInstance *inst = NULL; - - RzBuffer *io_buf = rz_buf_new_with_io(rz_analysis_get_io_bind(core->analysis)); - RzSetU *symbol_targets = rz_set_u_new(); - bool user_sent_signal = false; - struct ituple *iset_map = NULL; - RzThread *il_cache_th = NULL; - - rz_cons_push(); - - RZ_LOG_DEBUG("inquiry: Create IL Cache"); - RzILCache *il_cache = rz_il_cache_new(core->analysis, core->io, - rz_bin_object_get_sections(core->bin->cur->o), - RZ_IL_CACHE_CONFIG_NOP_UNLIFTED | RZ_IL_CACHE_CONFIG_NO_SLEEP); - if (!il_cache) { - return_code = false; - goto error_free; - } - - // collect_entry_points(core, entry_points, symbol_targets); - - if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { - eprintf("Total branch targets in binary: %" PFMT32d "\n", rz_set_u_size(entry_points)); - } - - // Initialize the abstract state with the architecture's registers. - if (!rz_analysis_plugin_current(core->analysis)->il_config) { - RZ_LOG_ERROR("The RzArch plugin doesn't have il_config() implemented.\n"); - return_code = false; - goto error_free; - } - - // Bundle all the queues into one object to pass it to the thread. - // Later we would pass a unique iset to each interpreter with - // the required queues only. - // But for the prototype we have only one iset with all queues. - size_t n_threads = 1; - iset_map = RZ_NEWS0(struct ituple, n_threads); - - RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM] = { 0 }; - if (!setup_yield_rbufs(yield_rbufs, rz_bin_object_get_sections(core->bin->cur->o), - (RzInterpYieldFilter)rz_inquiry_xref_interpreter_filter)) { - return_code = false; - rz_warn_if_reached(); - goto error_free; - } - - // - // Initialize and spawn the interpreters. - // - for (size_t i = 0; i < n_threads; ++i) { - RzILCacheClient *cache_client = rz_il_cache_new_client(il_cache, true); - if (!cache_client) { - return_code = false; - rz_warn_if_reached(); - goto error_free; - } - inst = rz_interp_instance_new( - core->analysis, - &rz_interp_value_domain_const, - cache_client, - yield_rbufs); - if (!inst) { - return_code = false; - rz_warn_if_reached(); - goto error_free; - } - - // Dispatch prototype interpreter into a thread. - RZ_LOG_DEBUG("inquiry: Start main interpretation thread.\n"); - RzThread *interpr_th = rz_th_new((RzThreadFunction)rz_interp_instance_th, inst); - iset_map[i].ithread = interpr_th; - iset_map[i].iset = inst; - iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_INIT; - } - - // - // Spawn the IL cache. - // - RZ_LOG_DEBUG("inquiry: Spawn IL Cache"); - il_cache_th = rz_th_new((RzThreadFunction)rz_il_cache_serve, il_cache); - - ut64 intpr_terminated = 0; - ut64 check_signal = 0; - - // - // Enter the loop to serve the interpreters. - // - for (ut64 i = 0;; check_signal++, i = (i + 1) % n_threads) { - if (check_signal % RZ_INQUIRY_CHECK_USER_SIGNAL_ITC == 0 && rz_cons_is_breaked()) { - user_sent_signal = true; - break; - } - RzInterpInstance *iset = iset_map[i].iset; - RzInterpRunStateFlag expected_rs = iset_map[i].next_run_state; - - switch (rz_interp_run_state_get_unsafe(iset->run_state)) { - case RZ_INTERP_RUN_STATE_OUT_OF_LOOP: - break; - case RZ_INTERP_RUN_STATE_INIT: { - if (expected_rs != RZ_INTERP_RUN_STATE_INIT) { - break; - } - // This interpreter is waiting for the next emulation task. - // TODO: Really reduce the entry points each and every time? - // This eats too much runtime I think. - // Better live with some duplicate emulation and reduce less often? - if (!reduce_get_entry_points(iset, il_cache, entry_points)) { - // None left. - // TODO Remove? - rz_th_queue_close(iset->il_cache_client->il_queue); - iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; - intpr_terminated++; - // RZ_LOG_DEBUG("Next: TERM\n"); - continue; - } - ut64 next_entry_point = rz_set_u_take(entry_points); - switch (rz_th_ring_buf_put(iset->entry_points, &next_entry_point)) { - case RZ_THREAD_RING_BUF_OK: - // Successfully lifted and pushed the entry point's basic block into the queue. - // Expect the interpreter to emulate now. - iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_EMU; - // RZ_LOG_DEBUG("Next: EMU\n"); - break; - case RZ_THREAD_RING_BUF_FAIL: - // The entry point buffer is full. - // Insert the entry point to the todo set again. - rz_set_u_add(entry_points, next_entry_point); - break; - case RZ_THREAD_RING_BUF_CLOSED: - rz_warn_if_reached(); - // Something went pretty wrong. - iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; - intpr_terminated++; - // RZ_LOG_DEBUG("Next: TERM\n"); - continue; - } - break; - } - case RZ_INTERP_RUN_STATE_EMU: { - if (expected_rs != RZ_INTERP_RUN_STATE_EMU) { - break; - } - // From here on, the code plays the role of the IO handler, - // and yield consumer. - // - Handling IO requests. - // - Receiving and adding the found xrefs to RzAnalysis. - // In the final implementation each of those roles would be split into - // two or more separated modules running in parallel. - - // ========== - // IO HANDLER - // ========== - // - // This plays the IO handler for a single(!) interpreter instances. - // In the future we should have only one IO handler for multiple interpreters. - // But this requires multiple IO write caches - // (one for each interpreter instance). - // Because this is not yet implemented, there is only one interpreter thread for now. - RzInterpIOReadRequest io_req = { 0 }; - if (!rz_th_ring_buf_is_empty_unsafe(iset->io_request_rbuf)) { - RzThreadRingBufResult r = rz_th_ring_buf_take(iset->io_request_rbuf, &io_req); - if (r == RZ_THREAD_RING_BUF_CLOSED) { - rz_warn_if_reached(); - goto fatal_error; - } else if (r == RZ_THREAD_RING_BUF_OK) { - RzInterpIOResult io_res = { 0 }; - handle_io_request(iset->il_ctx, &io_req, &io_res); - if (rz_th_ring_buf_put(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { - rz_warn_if_reached(); - goto fatal_error; - } - } - // Else r == RZ_THREAD_RING_BUF_FAIL - // Due to a race condition the ring buffer was actually empty. - } - - // ============== - // YIELD CONSUMER - // ============== - // - // This part plays the role of a yield consumer. - // In our prototype it only receives xrefs and call candidates. - if (!handle_yields(core->inquiry, iset->yield_rbufs)) { - iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; - intpr_terminated++; - // RZ_LOG_DEBUG("Next: TERM\n"); - } - break; - } - case RZ_INTERP_RUN_STATE_CLEAN: { - if (!((expected_rs == RZ_INTERP_RUN_STATE_CLEAN || expected_rs == RZ_INTERP_RUN_STATE_EMU))) { - break; - } - close_reset_ipc_obj(iset); - open_ipc_obj(iset); - rz_th_sem_post(iset->run_state_sync); - iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_INIT; - // RZ_LOG_DEBUG("Next: INIT\n"); - break; - } - case RZ_INTERP_RUN_STATE_TERM: { - if (expected_rs != RZ_INTERP_RUN_STATE_TERM) { - iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; - intpr_terminated++; - } - // RZ_LOG_DEBUG("Next: TERM\n"); - break; - } - } - if (intpr_terminated == n_threads) { - break; - } - } - -fatal_error: - - RZ_LOG_DEBUG("inquiry: Wait for join\n"); - for (size_t i = 0; i < n_threads; i++) { - close_reset_ipc_obj(iset_map[i].iset); - // Open semaphore so the interpreter can transition - // EMU -> CLEAN -> INIT -> TERM - rz_th_sem_post(iset_map[i].iset->run_state_sync); - } - - // Wait for thread to finish before cleaning. - for (size_t i = 0; i < n_threads; i++) { - rz_th_wait(iset_map[i].ithread); - bool interpr_ret = rz_th_get_retv(iset_map[i].ithread); - rz_th_free(iset_map[i].ithread); - if (!interpr_ret || user_sent_signal) { - return_code = false; - if (!user_sent_signal) { - RZ_LOG_ERROR("Interpreter failed with an error. Abort.\n"); - } else { - RZ_LOG_ERROR("User sent signal.\n"); - } - } - } - rz_il_cache_stop_serving(il_cache); - - if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { - eprintf("\n"); - } - - // Apply results - void **it; - rz_pvector_foreach (&inst->results, it) { - RzInterpResult *res = *it; - rz_interp_result_apply_to_analysis(res, core->analysis); - } - - for (size_t i = 0; i < n_threads; i++) { - rz_interp_instance_free(iset_map[i].iset); - } - -#if 0 - RzIterator *iter = rz_il_cache_get_blocks(il_cache); - if (!iter) { - rz_warn_if_reached(); - goto error_free; - } - void **it; - rz_iterator_foreach(iter, it) { - RzILCacheBlock *block = *it; - char *bstr = rz_il_cache_block_str(block); - RZ_LOG_DEBUG("inquiry: Add ILCache block: %s\n", bstr); - free(bstr); - rz_inquiry_bcfg_add_block(core->inquiry->bcfg, block->addr, block->size); - } - rz_iterator_free(iter); - if (!rz_inquiry_bcfg_add_edge_xref(core->inquiry->bcfg, rz_il_cache_get_static_xrefs(il_cache))) { - rz_warn_if_reached(); - } - if (!rz_inquiry_bcfg_add_edge_xref(core->inquiry->bcfg, core->inquiry->dynamic_xrefs)) { - rz_warn_if_reached(); - } - // char *g = rz_inquiry_bcfg_as_dot(core->inquiry->bcfg, "not_reduced"); - // printf("%s\n", g); - // free(g); - if (!rz_inquiry_bcfg_reduce(core->inquiry->bcfg)) { - rz_warn_if_reached(); - } - // g = rz_inquiry_bcfg_as_dot(core->inquiry->bcfg, "reduced"); - // printf("%s\n", g); - // free(g); -#endif - - RZ_LOG_DEBUG("inquiry: inquiry: inquiry: Done\n"); - -error_free: - rz_interp_yield_rbuf_free(yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]); - rz_interp_yield_rbuf_free(yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]); - rz_interp_yield_rbuf_free(yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW]); - free(iset_map); - rz_set_u_free(entry_points); - rz_buf_free(io_buf); - rz_il_cache_stop_serving(il_cache); - if (il_cache_th) { - rz_th_wait(il_cache_th); - rz_th_free(il_cache_th); - } - rz_il_cache_free(il_cache); - rz_cons_pop(); - return return_code && !user_sent_signal; -} - RZ_API bool rz_inquiry_convert_and_add_to_analysis( RzAnalysis *analysis, RzInquiry *inquiry, diff --git a/librz/inquiry/interp/driver.c b/librz/inquiry/interp/driver.c new file mode 100644 index 00000000000..a820c6c2c7a --- /dev/null +++ b/librz/inquiry/interp/driver.c @@ -0,0 +1,316 @@ +// SPDX-FileCopyrightText: 2026 Florian Märkl +// SPDX-FileCopyrightText: 2025-2026 Rot127 +// SPDX-License-Identifier: LGPL-3.0-only + +/** + * \file Interpreter Driver + * + * Main loop spawning and serving interpreter threads, + * as well as integrating results as they are emitted. + */ + +#include +#include + +static void close_reset_ipc_obj(RzInterpInstance *iset) { + // Close and clear all the IPC objects of this interpreter. + // This also clears the buffer and queues + rz_th_ring_buf_close(iset->io_request_rbuf); + rz_th_ring_buf_close(iset->io_result_rbuf); + rz_th_ring_buf_close(iset->entry_points); + rz_th_ring_buf_close(iset->il_cache_client->req_rbuf); + rz_th_queue_close(iset->il_cache_client->il_queue); + rz_list_free(rz_th_queue_pop_all(iset->il_cache_client->il_queue)); +} + +static void open_ipc_obj(RzInterpInstance *iset) { + // Open queue again, so the interpretation can start at another + // jump target again. + rz_th_ring_buf_open(iset->io_request_rbuf); + rz_th_ring_buf_open(iset->io_result_rbuf); + rz_th_ring_buf_open(iset->il_cache_client->req_rbuf); + rz_th_queue_open(iset->il_cache_client->il_queue); + rz_th_ring_buf_open(iset->entry_points); +} + +static void handle_io_request(RzAnalysisILContext *il_ctx, RzInterpIOReadRequest *io_req, RZ_OUT RzInterpIOResult *io_res) { + RZ_LOG_DEBUG("inquiry: Received IO read request: mem:%" PFMTSZd " 0x%" PFMT64x "\n", + io_req->mem_idx, + rz_bv_to_ut64(io_req->addr)); + io_res->req_ok = false; + RzILMemIndex mem_idx = io_req->mem_idx; + if (mem_idx > rz_vector_len(&il_ctx->memory)) { + rz_warn_if_reached(); + return; + } + if (rz_bv_len(io_req->addr) == 64 && rz_bv_msb(io_req->addr)) { + RZ_LOG_ERROR("Due to the Unix seek() implementation, addresses with the " + "63 bit set can't be addresses.\n"); + return; + } + RzAnalysisILMem *mem = rz_vector_index_ptr(&il_ctx->memory, mem_idx); + if (!mem->base_buf) { + io_res->req_ok = false; + } else { + // TODO: here only memory should be read that can be assumed to be constant! + io_res->req_ok = rz_il_loadw_into(mem->base_buf, io_req->ld_data, io_req->addr, io_req->n_bits, io_req->big_endian); + } + RZ_LOG_DEBUG("inquiry: Sent IO read result. Success = %s.\n", + rz_str_bool(io_res->req_ok)); +} + +struct ituple { + RzThread *ithread; + RzInterpInstance *iset; + RzInterpRunStateFlag next_run_state; +}; + +RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { + // All the things we need + bool return_code = true; + RzInterpInstance *inst = NULL; + + RzBuffer *io_buf = rz_buf_new_with_io(rz_analysis_get_io_bind(core->analysis)); + bool user_sent_signal = false; + struct ituple *iset_map = NULL; + RzThread *il_cache_th = NULL; + + rz_cons_push(); + + RZ_LOG_DEBUG("inquiry: Create IL Cache"); + RzILCache *il_cache = rz_il_cache_new(core->analysis, core->io, + rz_bin_object_get_sections(core->bin->cur->o), + RZ_IL_CACHE_CONFIG_NOP_UNLIFTED | RZ_IL_CACHE_CONFIG_NO_SLEEP); + if (!il_cache) { + return_code = false; + goto error_free; + } + + // collect_entry_points(core, entry_points, symbol_targets); + + if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { + eprintf("Total branch targets in binary: %" PFMT32d "\n", rz_set_u_size(entry_points)); + } + + // Initialize the abstract state with the architecture's registers. + if (!rz_analysis_plugin_current(core->analysis)->il_config) { + RZ_LOG_ERROR("The RzArch plugin doesn't have il_config() implemented.\n"); + return_code = false; + goto error_free; + } + + // Bundle all the queues into one object to pass it to the thread. + // Later we would pass a unique iset to each interpreter with + // the required queues only. + // But for the prototype we have only one iset with all queues. + size_t n_threads = 1; + iset_map = RZ_NEWS0(struct ituple, n_threads); + + RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM] = { 0 }; + + // + // Initialize and spawn the interpreters. + // + for (size_t i = 0; i < n_threads; ++i) { + RzILCacheClient *cache_client = rz_il_cache_new_client(il_cache, true); + if (!cache_client) { + return_code = false; + rz_warn_if_reached(); + goto error_free; + } + inst = rz_interp_instance_new( + core->analysis, + &rz_interp_value_domain_const, + cache_client, + yield_rbufs); + if (!inst) { + return_code = false; + rz_warn_if_reached(); + goto error_free; + } + + // Dispatch prototype interpreter into a thread. + RZ_LOG_DEBUG("inquiry: Start main interpretation thread.\n"); + RzThread *interpr_th = rz_th_new((RzThreadFunction)rz_interp_instance_th, inst); + iset_map[i].ithread = interpr_th; + iset_map[i].iset = inst; + iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_INIT; + } + + // + // Spawn the IL cache. + // + RZ_LOG_DEBUG("inquiry: Spawn IL Cache"); + il_cache_th = rz_th_new((RzThreadFunction)rz_il_cache_serve, il_cache); + + ut64 intpr_terminated = 0; + ut64 check_signal = 0; + + // + // Enter the loop to serve the interpreters. + // + for (ut64 i = 0;; check_signal++, i = (i + 1) % n_threads) { + if (check_signal % RZ_INQUIRY_CHECK_USER_SIGNAL_ITC == 0 && rz_cons_is_breaked()) { + user_sent_signal = true; + break; + } + RzInterpInstance *iset = iset_map[i].iset; + RzInterpRunStateFlag expected_rs = iset_map[i].next_run_state; + + switch (rz_interp_run_state_get_unsafe(iset->run_state)) { + case RZ_INTERP_RUN_STATE_OUT_OF_LOOP: + break; + case RZ_INTERP_RUN_STATE_INIT: { + if (expected_rs != RZ_INTERP_RUN_STATE_INIT) { + break; + } + if (!rz_set_u_size(entry_points)) { + rz_th_queue_close(iset->il_cache_client->il_queue); + iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; + intpr_terminated++; + continue; + } + ut64 next_entry_point = rz_set_u_take(entry_points); + switch (rz_th_ring_buf_put(iset->entry_points, &next_entry_point)) { + case RZ_THREAD_RING_BUF_OK: + // Successfully lifted and pushed the entry point's basic block into the queue. + // Expect the interpreter to emulate now. + iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_EMU; + // RZ_LOG_DEBUG("Next: EMU\n"); + break; + case RZ_THREAD_RING_BUF_FAIL: + // The entry point buffer is full. + // Insert the entry point to the todo set again. + rz_set_u_add(entry_points, next_entry_point); + break; + case RZ_THREAD_RING_BUF_CLOSED: + rz_warn_if_reached(); + // Something went pretty wrong. + iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; + intpr_terminated++; + // RZ_LOG_DEBUG("Next: TERM\n"); + continue; + } + break; + } + case RZ_INTERP_RUN_STATE_EMU: { + if (expected_rs != RZ_INTERP_RUN_STATE_EMU) { + break; + } + // From here on, the code plays the role of the IO handler, + // and yield consumer. + // - Handling IO requests. + // - Receiving and adding the found xrefs to RzAnalysis. + // In the final implementation each of those roles would be split into + // two or more separated modules running in parallel. + + // ========== + // IO HANDLER + // ========== + // + // This plays the IO handler for a single(!) interpreter instances. + // In the future we should have only one IO handler for multiple interpreters. + // But this requires multiple IO write caches + // (one for each interpreter instance). + // Because this is not yet implemented, there is only one interpreter thread for now. + RzInterpIOReadRequest io_req = { 0 }; + if (!rz_th_ring_buf_is_empty_unsafe(iset->io_request_rbuf)) { + RzThreadRingBufResult r = rz_th_ring_buf_take(iset->io_request_rbuf, &io_req); + if (r == RZ_THREAD_RING_BUF_CLOSED) { + rz_warn_if_reached(); + goto fatal_error; + } else if (r == RZ_THREAD_RING_BUF_OK) { + RzInterpIOResult io_res = { 0 }; + handle_io_request(iset->il_ctx, &io_req, &io_res); + if (rz_th_ring_buf_put(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { + rz_warn_if_reached(); + goto fatal_error; + } + } + // Else r == RZ_THREAD_RING_BUF_FAIL + // Due to a race condition the ring buffer was actually empty. + } + break; + } + case RZ_INTERP_RUN_STATE_CLEAN: { + if (!((expected_rs == RZ_INTERP_RUN_STATE_CLEAN || expected_rs == RZ_INTERP_RUN_STATE_EMU))) { + break; + } + close_reset_ipc_obj(iset); + open_ipc_obj(iset); + rz_th_sem_post(iset->run_state_sync); + iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_INIT; + // RZ_LOG_DEBUG("Next: INIT\n"); + break; + } + case RZ_INTERP_RUN_STATE_TERM: { + if (expected_rs != RZ_INTERP_RUN_STATE_TERM) { + iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; + intpr_terminated++; + } + // RZ_LOG_DEBUG("Next: TERM\n"); + break; + } + } + if (intpr_terminated == n_threads) { + break; + } + } + +fatal_error: + + RZ_LOG_DEBUG("inquiry: Wait for join\n"); + for (size_t i = 0; i < n_threads; i++) { + close_reset_ipc_obj(iset_map[i].iset); + // Open semaphore so the interpreter can transition + // EMU -> CLEAN -> INIT -> TERM + rz_th_sem_post(iset_map[i].iset->run_state_sync); + } + + // Wait for thread to finish before cleaning. + for (size_t i = 0; i < n_threads; i++) { + rz_th_wait(iset_map[i].ithread); + bool interpr_ret = rz_th_get_retv(iset_map[i].ithread); + rz_th_free(iset_map[i].ithread); + if (!interpr_ret || user_sent_signal) { + return_code = false; + if (!user_sent_signal) { + RZ_LOG_ERROR("Interpreter failed with an error. Abort.\n"); + } else { + RZ_LOG_ERROR("User sent signal.\n"); + } + } + } + rz_il_cache_stop_serving(il_cache); + + if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { + eprintf("\n"); + } + + // Apply results + void **it; + rz_pvector_foreach (&inst->results, it) { + RzInterpResult *res = *it; + rz_interp_result_apply_to_analysis(res, core->analysis); + } + + for (size_t i = 0; i < n_threads; i++) { + rz_interp_instance_free(iset_map[i].iset); + } + + RZ_LOG_DEBUG("inquiry: inquiry: inquiry: Done\n"); + +error_free: + free(iset_map); + rz_set_u_free(entry_points); + rz_buf_free(io_buf); + rz_il_cache_stop_serving(il_cache); + if (il_cache_th) { + rz_th_wait(il_cache_th); + rz_th_free(il_cache_th); + } + rz_il_cache_free(il_cache); + rz_cons_pop(); + return return_code && !user_sent_signal; + +} diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 8fee9f2d813..96f0ee31e75 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -1,4 +1,5 @@ -// SPDX-FileCopyrightText: 2025 RizinOrg +// SPDX-FileCopyrightText: 2026 Florian Märkl +// SPDX-FileCopyrightText: 2025-2026 Rot127 // SPDX-License-Identifier: LGPL-3.0-only /** diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index fb3e44384c0..4120e69c8e0 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -9,6 +9,7 @@ rz_inquiry_sources = [ 'interp/interpreter.c', 'interp/state.c', 'interp/val_abs_constant.c', + 'interp/driver.c' ] rz_inquiry_inc = [platform_inc] From ef75e3c1d80b5ca63894e4a0016a8501b78a943d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Fri, 17 Jul 2026 12:31:32 +0200 Subject: [PATCH 326/334] Remove now-unused code from interpreter --- librz/include/rz_inquiry/rz_interpreter.h | 104 +--------------------- librz/inquiry/interp/driver.c | 87 ++++++++---------- librz/inquiry/interp/interpreter.c | 90 +------------------ test/unit/test_inquiry_interp.c | 2 +- 4 files changed, 45 insertions(+), 238 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index ac3b1059827..e496725b315 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -9,7 +9,6 @@ #ifndef RZ_INTERPRETER #define RZ_INTERPRETER -#include #include #include #include @@ -21,7 +20,6 @@ * \brief Only one IO request at a time is possible (currently). */ #define RZ_INTERP_IO_RBUF_SIZE 128 -#define RZ_INTERP_YIELD_RBUF_SIZE 128 #define RZ_INTERP_ENTRY_POINTS_RBUF_SIZE 4 typedef struct rz_interp_run_state RzInterpRunState; @@ -47,7 +45,7 @@ typedef enum rz_interp_state_flag { * `struct rz_interp_opaque_abstr_val_t` is defined nowhere. Is is used to ensure * type-checking of passing this opaque pointer. */ -typedef struct rz_interp_opaque_abstr_val_t RzInterpAbstrVal; +typedef struct rz_interp_abstr_val_t RzInterpAbstrVal; /** * \brief Helper to explicitly cast a plugin-defined abstract value to an opaque RzInterpAbstrVal @@ -63,40 +61,6 @@ static inline void *rz_interp_abstr_val_unpack(const RzInterpAbstrVal *val) { return (void *)val; } -typedef struct { - RzInquiryBCFGEdgeType cf_type; ///< Control flow type. - /** - * \brief The address of the block which changes the control flow. - * This might be UT64_MAX, if there was no jump that flow originated from. - * If the interpreter was just initialized, for example. - */ - ut64 src_block_addr; - /** - * \brief The original target address a block branches to. - */ - ut64 target_addr; - /** - * \brief Set after a attempted jump to an ignored code region. - * This is almost always a call to an unloaded imported symbol. - * 0 is an invalid address here. - */ - ut64 alt_target; - /** - * \brief Equal to alt_target if alt_target != 0. - * Otherwise equal to target_addr - */ - ut64 actual_target; - /** - * \brief Number of bytes of the destination code block. - * Only set after the IL block was provided by the cache. - */ - size_t target_block_size; - /** - * \brief Control flow type. - */ - RzInquiryBCFGEdgeType type; -} RzInterpCtrlFlow; - typedef enum { RZ_INTERP_PC_CONST, ///< Single known value RZ_INTERP_PC_UNREACHABLE, ///< Bottom/unreachable state, if this is set, pc field is unused and undefined @@ -137,50 +101,6 @@ typedef struct { RzVector /**/ jump_targets; ///< Explicit jump targets, does not contain fallthrough address } RzInterpBlock; -typedef enum { - /** - * \brief The yield is an cross reference. - */ - RZ_INTERP_YIELD_KIND_XREF = 0, - - /** - * \brief This yield is a simple flag, signaling if the current basic block - * storing the next PC (address _after_ the basic block) to memory or an register. - * - * If the last branch instruction does not jump to the neighboring basic block - * it is a strong indicator that the jump is a call and the next address a return point. - */ - RZ_INTERP_YIELD_KIND_CALL_CANDIDATE, - - /** - * \brief Yield is a RzInterpCtrlFlow. - * Reported by every interpreter. - */ - RZ_INTERP_YIELD_KIND_CONTROL_FLOW, - RZ_INTERP_YIELD_KIND_NUM, -} RzInterpYieldKind; - -/** - * \brief A filter for abstract values to decide if they should be pushed into - * the yield ring buffer or not. - */ -typedef bool (*RzInterpYieldFilter)(const void *element, const void *filter_data); - -typedef struct { - RzPVector /**/ *io_boundaries; -} RzInterpYieldFilterData; - -/** - * \brief A ring buffer to push interpretation yields into. - * TODO: remove - */ -typedef struct { - RzInterpYieldKind kind; - RzInterpYieldFilter filter; - RzInterpYieldFilterData *filter_data; - RzThreadRingBuf *rbuf; -} RzInterpYieldRBuf; - typedef struct rz_interp_instance_t RzInterpInstance; typedef struct { @@ -263,8 +183,6 @@ typedef struct { */ RZ_LIFETIME(RzInquiry) struct rz_interp_instance_t { - RzAnalysis *a; ///< TODO: remove - RzInterpRunState *run_state; ///< The state the interpreter is currently in. /** * \brief The semaphore to sync RzInquiry and the interpreter between the Clean and Init run state. @@ -286,19 +204,8 @@ struct rz_interp_instance_t { RzThreadRingBuf /**/ *io_request_rbuf; ///< The ring buffer for read/write requests to the IO layer. RzThreadRingBuf /**/ *io_result_rbuf; ///< The ring buffer for the read/write requests' answers. - /** - * \brief The ring buffers to push the yield of interpretation into. - * These ring buffers are shared with other interpreter sets. - */ - // TODO: remove - RZ_BORROW RZ_NULLABLE RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM]; - RzPVector /**/ results; ///< TODO: replace this by a queue/rbuf/... to handle interp and results concurrently - /** - * \brief Ignored address ranges. - */ - const RzVector /**/ *ignored_code; /** * \brief The interpreter plugin. */ @@ -313,17 +220,10 @@ RZ_API const char *rz_interp_run_state_flag_str(RzInterpRunStateFlag flag); RZ_IPI void rz_interp_run_state_set(RZ_BORROW RZ_NONNULL RzInterpRunState *state, RzInterpRunStateFlag flag); -RZ_API void rz_interp_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yield_rbuf); - -RZ_API RZ_OWN RzInterpYieldRBuf *rz_interp_yield_rbuf_new(RzInterpYieldKind kind, - RzInterpYieldFilter filter, - RZ_OWN RZ_NULLABLE void *filter_data); - RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( RzAnalysis *analysis, RZ_NONNULL RZ_OWN RzInterpValueAbstraction *plugin, - RZ_NONNULL RZ_BORROW RzILCacheClient *il_cache_client, - RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM]); + RZ_NONNULL RZ_BORROW RzILCacheClient *il_cache_client); RZ_API void rz_interp_instance_free(RZ_OWN RZ_NULLABLE RzInterpInstance *iset); /** diff --git a/librz/inquiry/interp/driver.c b/librz/inquiry/interp/driver.c index a820c6c2c7a..b15e68d1a77 100644 --- a/librz/inquiry/interp/driver.c +++ b/librz/inquiry/interp/driver.c @@ -12,25 +12,25 @@ #include #include -static void close_reset_ipc_obj(RzInterpInstance *iset) { +static void close_reset_ipc_obj(RzInterpInstance *inst) { // Close and clear all the IPC objects of this interpreter. // This also clears the buffer and queues - rz_th_ring_buf_close(iset->io_request_rbuf); - rz_th_ring_buf_close(iset->io_result_rbuf); - rz_th_ring_buf_close(iset->entry_points); - rz_th_ring_buf_close(iset->il_cache_client->req_rbuf); - rz_th_queue_close(iset->il_cache_client->il_queue); - rz_list_free(rz_th_queue_pop_all(iset->il_cache_client->il_queue)); + rz_th_ring_buf_close(inst->io_request_rbuf); + rz_th_ring_buf_close(inst->io_result_rbuf); + rz_th_ring_buf_close(inst->entry_points); + rz_th_ring_buf_close(inst->il_cache_client->req_rbuf); + rz_th_queue_close(inst->il_cache_client->il_queue); + rz_list_free(rz_th_queue_pop_all(inst->il_cache_client->il_queue)); } -static void open_ipc_obj(RzInterpInstance *iset) { +static void open_ipc_obj(RzInterpInstance *inst) { // Open queue again, so the interpretation can start at another // jump target again. - rz_th_ring_buf_open(iset->io_request_rbuf); - rz_th_ring_buf_open(iset->io_result_rbuf); - rz_th_ring_buf_open(iset->il_cache_client->req_rbuf); - rz_th_queue_open(iset->il_cache_client->il_queue); - rz_th_ring_buf_open(iset->entry_points); + rz_th_ring_buf_open(inst->io_request_rbuf); + rz_th_ring_buf_open(inst->io_result_rbuf); + rz_th_ring_buf_open(inst->il_cache_client->req_rbuf); + rz_th_queue_open(inst->il_cache_client->il_queue); + rz_th_ring_buf_open(inst->entry_points); } static void handle_io_request(RzAnalysisILContext *il_ctx, RzInterpIOReadRequest *io_req, RZ_OUT RzInterpIOResult *io_res) { @@ -66,13 +66,11 @@ struct ituple { }; RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { - // All the things we need bool return_code = true; RzInterpInstance *inst = NULL; - RzBuffer *io_buf = rz_buf_new_with_io(rz_analysis_get_io_bind(core->analysis)); bool user_sent_signal = false; - struct ituple *iset_map = NULL; + struct ituple *interp_map = NULL; RzThread *il_cache_th = NULL; rz_cons_push(); @@ -86,8 +84,6 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { goto error_free; } - // collect_entry_points(core, entry_points, symbol_targets); - if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { eprintf("Total branch targets in binary: %" PFMT32d "\n", rz_set_u_size(entry_points)); } @@ -104,9 +100,7 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { // the required queues only. // But for the prototype we have only one iset with all queues. size_t n_threads = 1; - iset_map = RZ_NEWS0(struct ituple, n_threads); - - RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM] = { 0 }; + interp_map = RZ_NEWS0(struct ituple, n_threads); // // Initialize and spawn the interpreters. @@ -118,11 +112,7 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { rz_warn_if_reached(); goto error_free; } - inst = rz_interp_instance_new( - core->analysis, - &rz_interp_value_domain_const, - cache_client, - yield_rbufs); + inst = rz_interp_instance_new(core->analysis, &rz_interp_value_domain_const, cache_client); if (!inst) { return_code = false; rz_warn_if_reached(); @@ -132,9 +122,9 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { // Dispatch prototype interpreter into a thread. RZ_LOG_DEBUG("inquiry: Start main interpretation thread.\n"); RzThread *interpr_th = rz_th_new((RzThreadFunction)rz_interp_instance_th, inst); - iset_map[i].ithread = interpr_th; - iset_map[i].iset = inst; - iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_INIT; + interp_map[i].ithread = interpr_th; + interp_map[i].iset = inst; + interp_map[i].next_run_state = RZ_INTERP_RUN_STATE_INIT; } // @@ -143,7 +133,7 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { RZ_LOG_DEBUG("inquiry: Spawn IL Cache"); il_cache_th = rz_th_new((RzThreadFunction)rz_il_cache_serve, il_cache); - ut64 intpr_terminated = 0; + ut64 interp_terminated = 0; ut64 check_signal = 0; // @@ -154,8 +144,8 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { user_sent_signal = true; break; } - RzInterpInstance *iset = iset_map[i].iset; - RzInterpRunStateFlag expected_rs = iset_map[i].next_run_state; + RzInterpInstance *iset = interp_map[i].iset; + RzInterpRunStateFlag expected_rs = interp_map[i].next_run_state; switch (rz_interp_run_state_get_unsafe(iset->run_state)) { case RZ_INTERP_RUN_STATE_OUT_OF_LOOP: @@ -166,8 +156,8 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { } if (!rz_set_u_size(entry_points)) { rz_th_queue_close(iset->il_cache_client->il_queue); - iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; - intpr_terminated++; + interp_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; + interp_terminated++; continue; } ut64 next_entry_point = rz_set_u_take(entry_points); @@ -175,7 +165,7 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { case RZ_THREAD_RING_BUF_OK: // Successfully lifted and pushed the entry point's basic block into the queue. // Expect the interpreter to emulate now. - iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_EMU; + interp_map[i].next_run_state = RZ_INTERP_RUN_STATE_EMU; // RZ_LOG_DEBUG("Next: EMU\n"); break; case RZ_THREAD_RING_BUF_FAIL: @@ -186,8 +176,8 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { case RZ_THREAD_RING_BUF_CLOSED: rz_warn_if_reached(); // Something went pretty wrong. - iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; - intpr_terminated++; + interp_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; + interp_terminated++; // RZ_LOG_DEBUG("Next: TERM\n"); continue; } @@ -239,20 +229,20 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { close_reset_ipc_obj(iset); open_ipc_obj(iset); rz_th_sem_post(iset->run_state_sync); - iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_INIT; + interp_map[i].next_run_state = RZ_INTERP_RUN_STATE_INIT; // RZ_LOG_DEBUG("Next: INIT\n"); break; } case RZ_INTERP_RUN_STATE_TERM: { if (expected_rs != RZ_INTERP_RUN_STATE_TERM) { - iset_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; - intpr_terminated++; + interp_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; + interp_terminated++; } // RZ_LOG_DEBUG("Next: TERM\n"); break; } } - if (intpr_terminated == n_threads) { + if (interp_terminated == n_threads) { break; } } @@ -261,17 +251,17 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { RZ_LOG_DEBUG("inquiry: Wait for join\n"); for (size_t i = 0; i < n_threads; i++) { - close_reset_ipc_obj(iset_map[i].iset); + close_reset_ipc_obj(interp_map[i].iset); // Open semaphore so the interpreter can transition // EMU -> CLEAN -> INIT -> TERM - rz_th_sem_post(iset_map[i].iset->run_state_sync); + rz_th_sem_post(interp_map[i].iset->run_state_sync); } // Wait for thread to finish before cleaning. for (size_t i = 0; i < n_threads; i++) { - rz_th_wait(iset_map[i].ithread); - bool interpr_ret = rz_th_get_retv(iset_map[i].ithread); - rz_th_free(iset_map[i].ithread); + rz_th_wait(interp_map[i].ithread); + bool interpr_ret = rz_th_get_retv(interp_map[i].ithread); + rz_th_free(interp_map[i].ithread); if (!interpr_ret || user_sent_signal) { return_code = false; if (!user_sent_signal) { @@ -295,15 +285,14 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { } for (size_t i = 0; i < n_threads; i++) { - rz_interp_instance_free(iset_map[i].iset); + rz_interp_instance_free(interp_map[i].iset); } RZ_LOG_DEBUG("inquiry: inquiry: inquiry: Done\n"); error_free: - free(iset_map); + free(interp_map); rz_set_u_free(entry_points); - rz_buf_free(io_buf); rz_il_cache_stop_serving(il_cache); if (il_cache_th) { rz_th_wait(il_cache_th); diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 96f0ee31e75..e940c8cd888 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -258,8 +258,6 @@ static void interp_add_comment(RzInterpRunContext *ctx, ut64 addr, const char *c * @{ */ -#define UNWRAP_BLOCK(rbnode) ((rbnode) ? container_of(rbnode, RzInterpBlock, _rb) : NULL) - static RzInterpBlock *interp_block_new(RzInterpInstance *inst, RZ_BORROW RZ_NONNULL RzInterpAbstrState *entry_state) { RzInterpBlock *block = RZ_NEW0(RzInterpBlock); if (!block) { @@ -524,60 +522,6 @@ static RzInterpBlock *rz_interp_run_pop(RZ_BORROW RZ_NONNULL RzInterpRunContext ///////////////////////////////////////////////////////// -RZ_API void rz_interp_yield_rbuf_free(RZ_OWN RZ_NULLABLE RzInterpYieldRBuf *yield_rbufs) { - if (!yield_rbufs) { - return; - } - if (yield_rbufs->rbuf) { - rz_th_ring_buf_free(yield_rbufs->rbuf); - } - if (yield_rbufs->filter_data && yield_rbufs->filter_data->io_boundaries) { - rz_pvector_free(yield_rbufs->filter_data->io_boundaries); - } - free(yield_rbufs->filter_data); - free(yield_rbufs); -} - -RZ_API RZ_OWN RzInterpYieldRBuf *rz_interp_yield_rbuf_new(RzInterpYieldKind kind, - RzInterpYieldFilter filter, - RZ_OWN RZ_NULLABLE void *filter_data) { - RzInterpYieldRBuf *yield_rbufs = RZ_NEW0(RzInterpYieldRBuf); - if (!yield_rbufs) { - return NULL; - } - RzThreadRingBuf *rbuf = NULL; - switch (kind) { - default: - rz_warn_if_reached(); - return NULL; - case RZ_INTERP_YIELD_KIND_CALL_CANDIDATE: - rbuf = rz_th_ring_buf_new(RZ_INTERP_YIELD_RBUF_SIZE, sizeof(RzAnalysisCallCandidate)); - break; - case RZ_INTERP_YIELD_KIND_CONTROL_FLOW: - rbuf = rz_th_ring_buf_new(RZ_INTERP_YIELD_RBUF_SIZE, sizeof(RzInterpCtrlFlow)); - break; - case RZ_INTERP_YIELD_KIND_XREF: - if (filter_data) { - yield_rbufs->filter_data = RZ_NEW0(RzInterpYieldFilterData); - yield_rbufs->filter_data->io_boundaries = filter_data; - } - rbuf = rz_th_ring_buf_new(RZ_INTERP_YIELD_RBUF_SIZE, sizeof(RzAnalysisXRef)); - if (!rbuf) { - rz_pvector_free(filter_data); - return NULL; - } - break; - } - if (!rbuf) { - free(yield_rbufs); - return NULL; - } - yield_rbufs->kind = kind; - yield_rbufs->rbuf = rbuf; - yield_rbufs->filter = filter; - return yield_rbufs; -} - static bool setup_ipc_objects( RZ_OUT RzThreadRingBuf **io_request_rbuf, RZ_OUT RzThreadRingBuf **io_result_rbuf, @@ -613,8 +557,7 @@ static bool setup_ipc_objects( RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( RzAnalysis *analysis, RZ_NONNULL RZ_OWN RzInterpValueAbstraction *plugin, - RZ_NONNULL RZ_BORROW RzILCacheClient *il_cache_client, - RzInterpYieldRBuf *yield_rbufs[RZ_INTERP_YIELD_KIND_NUM]) { + RZ_NONNULL RZ_BORROW RzILCacheClient *il_cache_client) { rz_return_val_if_fail(plugin && analysis && il_cache_client, NULL); RzInterpInstance *inst = RZ_NEW0(RzInterpInstance); @@ -641,7 +584,6 @@ RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( goto err_il_ctx; } - inst->a = analysis; inst->plugin = plugin; inst->run_state = rz_interp_run_state_new(); inst->il_ctx = il_ctx; @@ -659,11 +601,6 @@ RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( inst->il_cache_client = il_cache_client; inst->entry_points = entry_points; - if (yield_rbufs) { - inst->yield_rbufs[RZ_INTERP_YIELD_KIND_XREF] = yield_rbufs[RZ_INTERP_YIELD_KIND_XREF]; - inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE] = yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; - inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW] = yield_rbufs[RZ_INTERP_YIELD_KIND_CONTROL_FLOW]; - } rz_pvector_init(&inst->results, NULL); inst->io_request_rbuf = io_request_rbuf; inst->io_result_rbuf = io_result_rbuf; @@ -750,17 +687,16 @@ static void report_yield_xref( */ static bool report_yield_call_candiate( RzInterpRunContext *ctx) { - RzInterpYieldRBuf *cc_rbuf = ctx->inst->yield_rbufs[RZ_INTERP_YIELD_KIND_CALL_CANDIDATE]; - if (!cc_rbuf) { - return true; - } + // TODO +#if 0 RzAnalysisCallCandidate cc = { 0 }; // TODO? put the bb addr into the call candidate? Currently we do not know it. memcpy(&cc, &ctx->call_cand, sizeof(ctx->call_cand)); if (rz_th_ring_buf_put(cc_rbuf->rbuf, &cc) != RZ_THREAD_RING_BUF_OK) { return false; } +#endif return true; } @@ -1271,26 +1207,8 @@ static bool eval_effect(RzInterpRunContext *ctx, ctx->call_cand.target = target; report_yield_call_candiate(ctx); -#if 0 - // For a call, we need to push a new frame. - RzBitVector ret_addr = { 0 }; - rz_bv_init(&ret_addr, rz_bv_len(eval_out.bv)); - rz_bv_set_from_ut64(&ret_addr->call_cand.npc); - - bool found = false; - ut64 ic = ht_uu_find(plugin_data->bb_invocation_count->call_cand.target, &found); - stack_frame_push(plugin_data, eval_out.bv, &ret_addr, !found ? 0 : ic); - rz_bv_fini(&ret_addr); -#endif - xref_type = RZ_ANALYSIS_XREF_TYPE_CALL; } -#if 0 - if (xref_type == RZ_ANALYSIS_XREF_TYPE_CODE && stack_frame_top_ret_addr_cmp(plugin_data, eval_out.bv)) { - stack_frame_pop(plugin_data, NULL); - xref_type = RZ_ANALYSIS_XREF_TYPE_RETURN; - } -#endif report_yield_xref(ctx, insn_pkt_size, ctx->insn_addr, eval_out, xref_type); diff --git a/test/unit/test_inquiry_interp.c b/test/unit/test_inquiry_interp.c index 56508f339a1..008b78da546 100644 --- a/test/unit/test_inquiry_interp.c +++ b/test/unit/test_inquiry_interp.c @@ -44,7 +44,7 @@ static TestInterp *interp_new(const char *arch, int bits, ut64 baddr, const char interp->io->va = 1; interp->il_cache = rz_il_cache_new(interp->analysis, interp->io, NULL, RZ_IL_CACHE_CONFIG_NOP_UNLIFTED | RZ_IL_CACHE_CONFIG_NO_SLEEP); interp->il_cache_client = rz_il_cache_new_client(interp->il_cache, false); - interp->inst = rz_interp_instance_new(interp->analysis, &rz_interp_value_domain_const, interp->il_cache_client, NULL); + interp->inst = rz_interp_instance_new(interp->analysis, &rz_interp_value_domain_const, interp->il_cache_client); RzIODesc *desc = rz_io_open_at(interp->io, url, RZ_PERM_RX, 0644, baddr, NULL); if (!desc) { mu_perror("load code"); From 1596cf832ed0a17338cef7908ee8b2a25e85038f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Fri, 17 Jul 2026 16:10:09 +0200 Subject: [PATCH 327/334] Move ipc to driver --- librz/include/rz_inquiry/rz_interpreter.h | 63 +--- librz/inquiry/README.md | 1 - librz/inquiry/interp/driver.c | 339 +++++++++++++++++++--- librz/inquiry/interp/interpreter.c | 260 +++++------------ librz/inquiry/interp/state.c | 71 ----- librz/inquiry/meson.build | 1 - test/unit/test_inquiry_interp.c | 32 +- 7 files changed, 386 insertions(+), 381 deletions(-) delete mode 100644 librz/inquiry/interp/state.c diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index e496725b315..01806f9ba11 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -22,22 +22,9 @@ #define RZ_INTERP_IO_RBUF_SIZE 128 #define RZ_INTERP_ENTRY_POINTS_RBUF_SIZE 4 -typedef struct rz_interp_run_state RzInterpRunState; typedef struct rz_interp_run_context_t RzInterpRunContext; typedef struct rz_interp_result_t RzInterpResult; -typedef enum rz_interp_state_flag { - /** - * \brief Interpreter is still outside of its defined loop. - * E.g. shortly after its thread was spawned. - */ - RZ_INTERP_RUN_STATE_OUT_OF_LOOP, - RZ_INTERP_RUN_STATE_INIT, ///< Initialization state. - RZ_INTERP_RUN_STATE_EMU, ///< Emulation state. - RZ_INTERP_RUN_STATE_CLEAN, ///< Cleaning state. - RZ_INTERP_RUN_STATE_TERM, ///< Termination state. -} RzInterpRunStateFlag; - /** * \brief An abstract value representing a set of RzILVal * @@ -165,7 +152,7 @@ typedef struct { } RzInterpValueAbstraction; -typedef struct { +typedef struct ry_interp_io_read_request_t { size_t mem_idx; ///< The memory space to read/write. bool big_endian; ///< Set if the data is big endian ordered. const RzBitVector *addr; ///< The address to read/write. @@ -174,56 +161,31 @@ typedef struct { size_t n_bits; ///< The number of bits to read/write. } RzInterpIOReadRequest; -typedef struct { - bool req_ok; ///< Set to true if IO request succeeded. -} RzInterpIOResult; +typedef struct rz_interp_config_t { + RzInterpValueAbstraction *val_domain; + + RZ_LIFETIME(RzILCache) + RZ_BORROW RzILCacheClient *il_cache_client; + + void *cb_user; + bool (*io_read)(RZ_NONNULL RZ_OWN RzInterpIOReadRequest *req, void *user); +} RzInterpConfig; /** * \brief Root local data of a single interpreter thread */ RZ_LIFETIME(RzInquiry) struct rz_interp_instance_t { - RzInterpRunState *run_state; ///< The state the interpreter is currently in. - /** - * \brief The semaphore to sync RzInquiry and the interpreter between the Clean and Init run state. - */ - RzThreadSemaphore *run_state_sync; - - /** - * \brief Entry points the interpreter starts interpreting from. - */ - RzThreadRingBuf *entry_points; + RzInterpConfig config; RzAnalysisILContext *il_ctx; ///< Context about available global vars and memory HtUP *var_name_hashes; ///< Map of DJB2 hashes to variable names. RZ_DEPRECATE const char *arch_name; ///< Name of architecture. Used only by work-arounds until we have RzArch. - RZ_LIFETIME(RzILCache) - RZ_BORROW RzILCacheClient *il_cache_client; - - RzThreadRingBuf /**/ *io_request_rbuf; ///< The ring buffer for read/write requests to the IO layer. - RzThreadRingBuf /**/ *io_result_rbuf; ///< The ring buffer for the read/write requests' answers. - RzPVector /**/ results; ///< TODO: replace this by a queue/rbuf/... to handle interp and results concurrently - - /** - * \brief The interpreter plugin. - */ - RzInterpValueAbstraction *plugin; }; -RZ_API RZ_OWN RzInterpRunState *rz_interp_run_state_new(); -RZ_API void rz_interp_run_state_free(RZ_OWN RZ_NULLABLE RzInterpRunState *state); -RZ_API RzInterpRunStateFlag rz_interp_run_state_get(RZ_BORROW RZ_NONNULL RzInterpRunState *state); -RZ_API RzInterpRunStateFlag rz_interp_run_state_get_unsafe(const RZ_NONNULL RzInterpRunState *state); -RZ_API const char *rz_interp_run_state_flag_str(RzInterpRunStateFlag flag); - -RZ_IPI void rz_interp_run_state_set(RZ_BORROW RZ_NONNULL RzInterpRunState *state, RzInterpRunStateFlag flag); - -RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( - RzAnalysis *analysis, - RZ_NONNULL RZ_OWN RzInterpValueAbstraction *plugin, - RZ_NONNULL RZ_BORROW RzILCacheClient *il_cache_client); +RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new(RzAnalysis *analysis, RZ_NONNULL RZ_BORROW const RzInterpConfig *config); RZ_API void rz_interp_instance_free(RZ_OWN RZ_NULLABLE RzInterpInstance *iset); /** @@ -271,7 +233,6 @@ struct rz_interp_result_t { } /*RzInterpResult*/; RZ_API RzInterpResult *rz_interp_run(RzInterpInstance *inst, ut64 entry_point, RzInterpResultDimen dimen); -RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *iset); RZ_API void rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, RZ_NONNULL RzAnalysis *analysis); extern RZ_API RzInterpValueAbstraction rz_interp_value_domain_const; diff --git a/librz/inquiry/README.md b/librz/inquiry/README.md index 92ad293a1e4..10b33411453 100644 --- a/librz/inquiry/README.md +++ b/librz/inquiry/README.md @@ -7,7 +7,6 @@ Module implementing basic and advanced binary analysis. ## TODO -* remove yield bufs * rename interp to absint * rename valeabstraction to valuedomain diff --git a/librz/inquiry/interp/driver.c b/librz/inquiry/interp/driver.c index b15e68d1a77..932a06d50a3 100644 --- a/librz/inquiry/interp/driver.c +++ b/librz/inquiry/interp/driver.c @@ -12,25 +12,261 @@ #include #include -static void close_reset_ipc_obj(RzInterpInstance *inst) { +typedef struct rz_interp_io_result_t { + bool req_ok; ///< Set to true if IO request succeeded. +} RzInterpIOResult; + +typedef struct rz_interp_run_state RzInterpRunState; + +typedef enum rz_interp_state_flag { + /** + * \brief Interpreter is still outside of its defined loop. + * E.g. shortly after its thread was spawned. + */ + RZ_INTERP_RUN_STATE_OUT_OF_LOOP, + RZ_INTERP_RUN_STATE_INIT, ///< Initialization state. + RZ_INTERP_RUN_STATE_EMU, ///< Emulation state. + RZ_INTERP_RUN_STATE_CLEAN, ///< Cleaning state. + RZ_INTERP_RUN_STATE_TERM, ///< Termination state. +} RzInterpRunStateFlag; + +RZ_API RZ_OWN RzInterpRunState *rz_interp_run_state_new(); +RZ_API void rz_interp_run_state_free(RZ_OWN RZ_NULLABLE RzInterpRunState *state); +RZ_API RzInterpRunStateFlag rz_interp_run_state_get(RZ_BORROW RZ_NONNULL RzInterpRunState *state); +RZ_API RzInterpRunStateFlag rz_interp_run_state_get_unsafe(const RZ_NONNULL RzInterpRunState *state); +RZ_API const char *rz_interp_run_state_flag_str(RzInterpRunStateFlag flag); + +RZ_IPI void rz_interp_run_state_set(RZ_BORROW RZ_NONNULL RzInterpRunState *state, RzInterpRunStateFlag flag); + +struct rz_interp_run_state { + RzThreadLock *lock; ///< The mutex around the state flag. + RzInterpRunStateFlag flag; ///< The current set state. +}; + +RZ_API const char *rz_interp_run_state_flag_str(RzInterpRunStateFlag flag) { + switch (flag) { + case RZ_INTERP_RUN_STATE_OUT_OF_LOOP: + return "-"; + case RZ_INTERP_RUN_STATE_INIT: + return "I"; + case RZ_INTERP_RUN_STATE_EMU: + return "O"; + case RZ_INTERP_RUN_STATE_CLEAN: + return "C"; + case RZ_INTERP_RUN_STATE_TERM: + return "T"; + } + rz_warn_if_reached(); + return "-"; +} + +RZ_API RZ_OWN RzInterpRunState *rz_interp_run_state_new() { + RzInterpRunState *state = RZ_NEW0(RzInterpRunState); + if (!state) { + return NULL; + } + state->flag = RZ_INTERP_RUN_STATE_OUT_OF_LOOP; + state->lock = rz_th_lock_new(false); + if (!state->lock) { + free(state); + return NULL; + } + return state; +} + +RZ_API void rz_interp_run_state_free(RZ_OWN RZ_NULLABLE RzInterpRunState *state) { + if (!state) { + return; + } + rz_th_lock_free(state->lock); + free(state); +} + +RZ_API RzInterpRunStateFlag rz_interp_run_state_get(RZ_BORROW RZ_NONNULL RzInterpRunState *state) { + rz_return_val_if_fail(state, RZ_INTERP_RUN_STATE_TERM); + rz_th_lock_enter(state->lock); + RzInterpRunStateFlag flag = state->flag; + rz_th_lock_leave(state->lock); + return flag; +} + +RZ_API RzInterpRunStateFlag rz_interp_run_state_get_unsafe(const RZ_NONNULL RzInterpRunState *state) { + rz_return_val_if_fail(state, RZ_INTERP_RUN_STATE_TERM); + return state->flag; +} + +/** + * \brief Sets the run state. + * This function is declared IPI, so it is not used outside of the interpreter module! + */ +RZ_IPI void rz_interp_run_state_set(RZ_BORROW RZ_NONNULL RzInterpRunState *state, RzInterpRunStateFlag flag) { + rz_th_lock_enter(state->lock); + state->flag = flag; + rz_th_lock_leave(state->lock); +} + +typedef struct interp_thread_context { + RzThread *th; + RzInterpInstance *inst; + RzInterpRunState *run_state; ///< The state the interpreter is currently in. + /** + * \brief The semaphore to sync RzInquiry and the interpreter between the Clean and Init run state. + */ + RzThreadSemaphore *run_state_sync; + + RzThreadRingBuf /**/ *io_request_rbuf; ///< The ring buffer for read/write requests to the IO layer. + RzThreadRingBuf /**/ *io_result_rbuf; ///< The ring buffer for the read/write requests' answers. + + /** + * \brief Entry points the interpreter starts interpreting from. + */ + RzThreadRingBuf *entry_points; +} InterpThread; + +static void *interp_th(void *user) { + InterpThread *ctx = user; + RzInterpInstance *inst = ctx->inst; + + bool success = true; + + RZ_LOG_DEBUG("interpreter: Main: Hello.\n"); + + while (true) { + // INIT + RZ_LOG_DEBUG("interpreter: Enter INIT\n"); + rz_interp_run_state_set(ctx->run_state, RZ_INTERP_RUN_STATE_INIT); + + ut64 entry_point; + if (rz_th_ring_buf_take_blocking(ctx->entry_points, &entry_point) != RZ_THREAD_RING_BUF_OK) { + // No more entry points to interpret => Terminate. + // OR. + success = true; + break; + } + + // EMU + RZ_LOG_DEBUG("interpreter: Enter EMU\n"); + rz_interp_run_state_set(ctx->run_state, RZ_INTERP_RUN_STATE_EMU); + + RzInterpResult *res = rz_interp_run(inst, entry_point, RZ_INTERP_RESULT_DIMEN_XREFS | RZ_INTERP_RESULT_DIMEN_COMMENTS); // TODO: make dimensions configurable + if (res) { + // TODO: use some sort of channel for delivering results + rz_pvector_push(&inst->results, res); + } else { + RZ_LOG_ERROR("Interpreter run failed for entry point 0x%" PFMT64x "\n", entry_point); + } + + // CLEAN + RZ_LOG_DEBUG("interpreter: Enter CLEAN\n"); + rz_interp_run_state_set(ctx->run_state, RZ_INTERP_RUN_STATE_CLEAN); + + // Wait until RzInquiry asks to start again. + rz_th_sem_wait(ctx->run_state_sync); + } + + RZ_LOG_DEBUG("interpreter: Enter TERM\n"); + rz_interp_run_state_set(ctx->run_state, RZ_INTERP_RUN_STATE_TERM); + return (void *)success; +} + +static bool setup_ipc_objects( + RZ_OUT RzThreadRingBuf **io_request_rbuf, + RZ_OUT RzThreadRingBuf **io_result_rbuf, + RZ_OUT RzThreadRingBuf **entry_points) { + *io_request_rbuf = NULL; + *io_result_rbuf = NULL; + *entry_points = NULL; + + // Setup the IO queues. Each interpreter instance needs it's own queue at + // for writing IO. Because the writing is done on the IO cache, and each + // instance needs its own cache. + *io_request_rbuf = rz_th_ring_buf_new(RZ_INTERP_IO_RBUF_SIZE, sizeof(RzInterpIOReadRequest)); + *io_result_rbuf = rz_th_ring_buf_new(RZ_INTERP_IO_RBUF_SIZE, sizeof(RzInterpIOResult)); + *entry_points = rz_th_ring_buf_new(RZ_INTERP_ENTRY_POINTS_RBUF_SIZE, sizeof(ut64)); + if (!*io_request_rbuf || !*io_result_rbuf || !*entry_points) { + rz_warn_if_reached(); + goto error_free; + } + + return true; + +error_free: + rz_th_ring_buf_free(*io_request_rbuf); + rz_th_ring_buf_free(*io_result_rbuf); + rz_th_ring_buf_free(*entry_points); + return false; +} + +static InterpThread *interp_thread_new(RZ_OWN RzInterpInstance *inst) { + InterpThread *ctx = RZ_NEW(InterpThread); + if (!ctx) { + return NULL; + } + ctx->inst = inst; + ctx->run_state = rz_interp_run_state_new(); + if (!ctx->run_state) { + goto err_ctx; + } + + RzThreadRingBuf *io_request_rbuf = NULL; + RzThreadRingBuf *io_result_rbuf = NULL; + RzThreadRingBuf *entry_points = NULL; + if (!setup_ipc_objects(&io_request_rbuf, &io_result_rbuf, &entry_points)) { + goto err_run_state; + } + ctx->io_request_rbuf = io_request_rbuf; + ctx->io_result_rbuf = io_result_rbuf; + ctx->entry_points = entry_points; + + inst->config.cb_user = ctx; // TODO: remove this hack + + ctx->run_state_sync = rz_th_sem_new(0); + ctx->th = rz_th_new(interp_th, ctx); + if (!ctx->th) { + goto err_run_state; + } + + return ctx; +err_run_state: + rz_interp_run_state_free(ctx->run_state); +err_ctx: + free(ctx); + return NULL; +} + +static void close_reset_ipc_obj(InterpThread *th) { // Close and clear all the IPC objects of this interpreter. // This also clears the buffer and queues - rz_th_ring_buf_close(inst->io_request_rbuf); - rz_th_ring_buf_close(inst->io_result_rbuf); - rz_th_ring_buf_close(inst->entry_points); - rz_th_ring_buf_close(inst->il_cache_client->req_rbuf); - rz_th_queue_close(inst->il_cache_client->il_queue); - rz_list_free(rz_th_queue_pop_all(inst->il_cache_client->il_queue)); + RzInterpInstance *inst = th->inst; + rz_th_ring_buf_close(th->io_request_rbuf); + rz_th_ring_buf_close(th->io_result_rbuf); + rz_th_ring_buf_close(th->entry_points); + rz_th_ring_buf_close(inst->config.il_cache_client->req_rbuf); + rz_th_queue_close(inst->config.il_cache_client->il_queue); + rz_list_free(rz_th_queue_pop_all(inst->config.il_cache_client->il_queue)); } -static void open_ipc_obj(RzInterpInstance *inst) { +static void open_ipc_obj(InterpThread *th) { // Open queue again, so the interpretation can start at another // jump target again. - rz_th_ring_buf_open(inst->io_request_rbuf); - rz_th_ring_buf_open(inst->io_result_rbuf); - rz_th_ring_buf_open(inst->il_cache_client->req_rbuf); - rz_th_queue_open(inst->il_cache_client->il_queue); - rz_th_ring_buf_open(inst->entry_points); + RzInterpInstance *inst = th->inst; + rz_th_ring_buf_open(th->io_request_rbuf); + rz_th_ring_buf_open(th->io_result_rbuf); + rz_th_ring_buf_open(inst->config.il_cache_client->req_rbuf); + rz_th_queue_open(inst->config.il_cache_client->il_queue); + rz_th_ring_buf_open(th->entry_points); +} + +static bool send_io_read(RZ_NONNULL RzInterpIOReadRequest *req, void *user) { + InterpThread *th = user; + if (rz_th_ring_buf_put(th->io_request_rbuf, req) != RZ_THREAD_RING_BUF_OK) { + return false; + } + RzInterpIOResult io_res = { 0 }; + if (rz_th_ring_buf_take_blocking(th->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { + return false; + } + return io_res.req_ok; } static void handle_io_request(RzAnalysisILContext *il_ctx, RzInterpIOReadRequest *io_req, RZ_OUT RzInterpIOResult *io_res) { @@ -60,14 +296,12 @@ static void handle_io_request(RzAnalysisILContext *il_ctx, RzInterpIOReadRequest } struct ituple { - RzThread *ithread; - RzInterpInstance *iset; + InterpThread *th; RzInterpRunStateFlag next_run_state; }; RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { bool return_code = true; - RzInterpInstance *inst = NULL; bool user_sent_signal = false; struct ituple *interp_map = NULL; @@ -112,18 +346,25 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { rz_warn_if_reached(); goto error_free; } - inst = rz_interp_instance_new(core->analysis, &rz_interp_value_domain_const, cache_client); + RzInterpConfig config = { + .val_domain = &rz_interp_value_domain_const, + .il_cache_client = cache_client, + .cb_user = NULL, // TODO, injected by hack + .io_read = send_io_read + }; + RzInterpInstance *inst = rz_interp_instance_new(core->analysis, &config); if (!inst) { return_code = false; rz_warn_if_reached(); goto error_free; } - - // Dispatch prototype interpreter into a thread. RZ_LOG_DEBUG("inquiry: Start main interpretation thread.\n"); - RzThread *interpr_th = rz_th_new((RzThreadFunction)rz_interp_instance_th, inst); - interp_map[i].ithread = interpr_th; - interp_map[i].iset = inst; + interp_map[i].th = interp_thread_new(inst); + if (!interp_map[i].th) { + rz_interp_instance_free(inst); + return_code = false; + goto error_free; + } interp_map[i].next_run_state = RZ_INTERP_RUN_STATE_INIT; } @@ -144,10 +385,10 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { user_sent_signal = true; break; } - RzInterpInstance *iset = interp_map[i].iset; + RzInterpInstance *inst = interp_map[i].th->inst; RzInterpRunStateFlag expected_rs = interp_map[i].next_run_state; - switch (rz_interp_run_state_get_unsafe(iset->run_state)) { + switch (rz_interp_run_state_get_unsafe(interp_map[i].th->run_state)) { case RZ_INTERP_RUN_STATE_OUT_OF_LOOP: break; case RZ_INTERP_RUN_STATE_INIT: { @@ -155,13 +396,13 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { break; } if (!rz_set_u_size(entry_points)) { - rz_th_queue_close(iset->il_cache_client->il_queue); + rz_th_queue_close(inst->config.il_cache_client->il_queue); interp_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; interp_terminated++; continue; } ut64 next_entry_point = rz_set_u_take(entry_points); - switch (rz_th_ring_buf_put(iset->entry_points, &next_entry_point)) { + switch (rz_th_ring_buf_put(interp_map[i].th->entry_points, &next_entry_point)) { case RZ_THREAD_RING_BUF_OK: // Successfully lifted and pushed the entry point's basic block into the queue. // Expect the interpreter to emulate now. @@ -204,15 +445,15 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { // (one for each interpreter instance). // Because this is not yet implemented, there is only one interpreter thread for now. RzInterpIOReadRequest io_req = { 0 }; - if (!rz_th_ring_buf_is_empty_unsafe(iset->io_request_rbuf)) { - RzThreadRingBufResult r = rz_th_ring_buf_take(iset->io_request_rbuf, &io_req); + if (!rz_th_ring_buf_is_empty_unsafe(interp_map[i].th->io_request_rbuf)) { + RzThreadRingBufResult r = rz_th_ring_buf_take(interp_map[i].th->io_request_rbuf, &io_req); if (r == RZ_THREAD_RING_BUF_CLOSED) { rz_warn_if_reached(); goto fatal_error; } else if (r == RZ_THREAD_RING_BUF_OK) { RzInterpIOResult io_res = { 0 }; - handle_io_request(iset->il_ctx, &io_req, &io_res); - if (rz_th_ring_buf_put(iset->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { + handle_io_request(inst->il_ctx, &io_req, &io_res); + if (rz_th_ring_buf_put(interp_map[i].th->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { rz_warn_if_reached(); goto fatal_error; } @@ -226,9 +467,9 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { if (!((expected_rs == RZ_INTERP_RUN_STATE_CLEAN || expected_rs == RZ_INTERP_RUN_STATE_EMU))) { break; } - close_reset_ipc_obj(iset); - open_ipc_obj(iset); - rz_th_sem_post(iset->run_state_sync); + close_reset_ipc_obj(interp_map[0].th); // TODO + open_ipc_obj(interp_map[0].th); // TODO + rz_th_sem_post(interp_map[0].th->run_state_sync); interp_map[i].next_run_state = RZ_INTERP_RUN_STATE_INIT; // RZ_LOG_DEBUG("Next: INIT\n"); break; @@ -251,17 +492,22 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { RZ_LOG_DEBUG("inquiry: Wait for join\n"); for (size_t i = 0; i < n_threads; i++) { - close_reset_ipc_obj(interp_map[i].iset); + close_reset_ipc_obj(interp_map[i].th); // Open semaphore so the interpreter can transition // EMU -> CLEAN -> INIT -> TERM - rz_th_sem_post(interp_map[i].iset->run_state_sync); + rz_th_sem_post(interp_map[i].th->run_state_sync); } // Wait for thread to finish before cleaning. for (size_t i = 0; i < n_threads; i++) { - rz_th_wait(interp_map[i].ithread); - bool interpr_ret = rz_th_get_retv(interp_map[i].ithread); - rz_th_free(interp_map[i].ithread); + rz_th_wait(interp_map[i].th->th); + bool interpr_ret = rz_th_get_retv(interp_map[i].th->th); + rz_th_free(interp_map[i].th->th); + rz_th_ring_buf_free(interp_map[i].th->io_request_rbuf); + rz_th_ring_buf_free(interp_map[i].th->io_result_rbuf); + rz_th_ring_buf_free(interp_map[i].th->entry_points); + rz_th_sem_free(interp_map[i].th->run_state_sync); + rz_interp_run_state_free(interp_map[i].th->run_state); if (!interpr_ret || user_sent_signal) { return_code = false; if (!user_sent_signal) { @@ -277,15 +523,15 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { eprintf("\n"); } - // Apply results - void **it; - rz_pvector_foreach (&inst->results, it) { - RzInterpResult *res = *it; - rz_interp_result_apply_to_analysis(res, core->analysis); - } - for (size_t i = 0; i < n_threads; i++) { - rz_interp_instance_free(interp_map[i].iset); + // Apply results + void **it; + rz_pvector_foreach (&interp_map[i].th->inst->results, it) { + RzInterpResult *res = *it; + rz_interp_result_apply_to_analysis(res, core->analysis); + } + + rz_interp_instance_free(interp_map[i].th->inst); } RZ_LOG_DEBUG("inquiry: inquiry: inquiry: Done\n"); @@ -301,5 +547,4 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { rz_il_cache_free(il_cache); rz_cons_pop(); return return_code && !user_sent_signal; - } diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index e940c8cd888..4e51e8b0cdb 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -16,6 +16,10 @@ #include #include +static RzInterpValueAbstraction *val_domain(const RzInterpInstance *inst) { + return inst->config.val_domain; +} + ///////////////////////////////////////////////////////// /** * \name RzInterpAbstrState @@ -38,7 +42,7 @@ RZ_API RZ_OWN RzInterpAbstrState *rz_interp_abstr_state_new( state->globals = ht_up_new(NULL, NULL); for (size_t i = 0; i < inst->il_ctx->reg_binding->regs_count; i++) { const char *rname = inst->il_ctx->reg_binding->regs[i].name; - RzInterpAbstrVal *aval = inst->plugin->val_new_top(); + RzInterpAbstrVal *aval = val_domain(inst)->val_new_top(); if (!aval) { rz_warn_if_reached(); ht_up_free(state->globals); @@ -64,7 +68,7 @@ static void var_set_free(RzInterpInstance *inst, HtUP *vars) { RzIterator *it = ht_up_as_iter(vars); RzInterpAbstrVal **v; rz_iterator_foreach(it, v) { - inst->plugin->val_free(*v); + val_domain(inst)->val_free(*v); } rz_iterator_free(it); ht_up_free(vars); @@ -96,7 +100,7 @@ static bool reset_state(RzInterpInstance *inst, RZ_BORROW RzInterpAbstrState *st ut64 djb2_reg_name = *k; RzInterpAbstrVal *av = ht_up_find(state->globals, djb2_reg_name, NULL); if (av) { - inst->plugin->set_top(av); + val_domain(inst)->set_top(av); } } rz_iterator_free(it); @@ -124,7 +128,7 @@ RZ_API bool rz_interp_abstr_state_as_str(RZ_NONNULL RzInterpInstance *inst, RZ_N const char *gname = ht_up_find(inst->var_name_hashes, *k, NULL); rz_strbuf_appendf(sb, "\t%s = ", gname); RzInterpAbstrVal *av = ht_up_find(state->globals, *k, NULL); - inst->plugin->val_as_str(av, sb); + val_domain(inst)->val_as_str(av, sb); rz_strbuf_append(sb, "\n"); } rz_iterator_free(it); @@ -139,7 +143,7 @@ RZ_API void rz_interp_abstr_state_as_str_short(RZ_NONNULL RzInterpInstance *inst rz_iterator_foreach(it, k) { ut64 djb2_reg_name = *k; RzInterpAbstrVal *av = ht_up_find(astate->globals, djb2_reg_name, NULL); - if (!av || inst->plugin->is_top(av)) { + if (!av || val_domain(inst)->is_top(av)) { continue; } all_top = false; @@ -149,14 +153,14 @@ RZ_API void rz_interp_abstr_state_as_str_short(RZ_NONNULL RzInterpInstance *inst first = false; const char *varname = ht_up_find(inst->var_name_hashes, djb2_reg_name, NULL); rz_strbuf_appendf(sb, "%s = ", varname); - inst->plugin->val_as_str(av, sb); + val_domain(inst)->val_as_str(av, sb); } if (all_top) { rz_strbuf_append(sb, STR_TOP); } } -static HtUP *var_set_clone(const RzInterpInstance *iset, HtUP *vars) { +static HtUP *var_set_clone(const RzInterpInstance *inst, HtUP *vars) { HtUP *r = ht_up_new(NULL, NULL); if (!r) { return NULL; @@ -164,11 +168,11 @@ static HtUP *var_set_clone(const RzInterpInstance *iset, HtUP *vars) { RzIterator *it = ht_up_as_iter_keys(vars); ut64 *key; rz_iterator_foreach(it, key) { - RzInterpAbstrVal *val = iset->plugin->val_new_top(); + RzInterpAbstrVal *val = val_domain(inst)->val_new_top(); if (!val) { break; } - iset->plugin->copy(val, ht_up_find(vars, *key, NULL)); + val_domain(inst)->copy(val, ht_up_find(vars, *key, NULL)); ht_up_insert(r, *key, val); } return r; @@ -201,7 +205,7 @@ static bool join_vars(RzInterpInstance *inst, RZ_BORROW RZ_INOUT HtUP *a, RZ_BOR if (!av || !bv) { continue; } - if (inst->plugin->join(av, bv)) { + if (val_domain(inst)->join(av, bv)) { changed = true; } } @@ -522,48 +526,14 @@ static RzInterpBlock *rz_interp_run_pop(RZ_BORROW RZ_NONNULL RzInterpRunContext ///////////////////////////////////////////////////////// -static bool setup_ipc_objects( - RZ_OUT RzThreadRingBuf **io_request_rbuf, - RZ_OUT RzThreadRingBuf **io_result_rbuf, - RZ_OUT RzThreadRingBuf **entry_points) { - *io_request_rbuf = NULL; - *io_result_rbuf = NULL; - *entry_points = NULL; - - // Setup the IO queues. Each interpreter instance needs it's own queue at - // for writing IO. Because the writing is done on the IO cache, and each - // instance needs its own cache. - *io_request_rbuf = rz_th_ring_buf_new(RZ_INTERP_IO_RBUF_SIZE, sizeof(RzInterpIOReadRequest)); - *io_result_rbuf = rz_th_ring_buf_new(RZ_INTERP_IO_RBUF_SIZE, sizeof(RzInterpIOResult)); - *entry_points = rz_th_ring_buf_new(RZ_INTERP_ENTRY_POINTS_RBUF_SIZE, sizeof(ut64)); - if (!*io_request_rbuf || !*io_result_rbuf || !*entry_points) { - rz_warn_if_reached(); - goto error_free; - } - - return true; - -error_free: - rz_th_ring_buf_free(*io_request_rbuf); - rz_th_ring_buf_free(*io_result_rbuf); - rz_th_ring_buf_free(*entry_points); - return false; -} - -/** - * \brief Initializes a new RzInterpSet and returns it. - * If it fails, all arguments are freed. - */ -RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( - RzAnalysis *analysis, - RZ_NONNULL RZ_OWN RzInterpValueAbstraction *plugin, - RZ_NONNULL RZ_BORROW RzILCacheClient *il_cache_client) { - rz_return_val_if_fail(plugin && analysis && il_cache_client, NULL); +RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new(RzAnalysis *analysis, RZ_NONNULL RZ_BORROW const RzInterpConfig *config) { + rz_return_val_if_fail(analysis && config && config->val_domain && config->il_cache_client && config->io_read, NULL); RzInterpInstance *inst = RZ_NEW0(RzInterpInstance); if (!inst) { return NULL; } + inst->config = *config; const RzAnalysisPlugin *cur = rz_analysis_plugin_current(analysis); if (!cur || !cur->arch) { @@ -577,18 +547,12 @@ RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( goto err_inst; } - RzThreadRingBuf *io_request_rbuf = NULL; - RzThreadRingBuf *io_result_rbuf = NULL; - RzThreadRingBuf *entry_points = NULL; - if (!setup_ipc_objects(&io_request_rbuf, &io_result_rbuf, &entry_points)) { - goto err_il_ctx; - } - - inst->plugin = plugin; - inst->run_state = rz_interp_run_state_new(); inst->il_ctx = il_ctx; inst->var_name_hashes = ht_up_new(NULL, free); + if (!inst->var_name_hashes) { + goto err_il_ctx; + } for (size_t i = 0; i < il_ctx->reg_binding->regs_count; i++) { const char *rname = il_ctx->reg_binding->regs[i].name; ut64 djb2_reg_hash = rz_str_djb2_hash(rname); @@ -599,12 +563,7 @@ RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( } } - inst->il_cache_client = il_cache_client; - inst->entry_points = entry_points; rz_pvector_init(&inst->results, NULL); - inst->io_request_rbuf = io_request_rbuf; - inst->io_result_rbuf = io_result_rbuf; - inst->run_state_sync = rz_th_sem_new(0); return inst; err_var_name_hashes: @@ -616,28 +575,13 @@ RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new( return NULL; } -RZ_API void rz_interp_instance_free(RZ_OWN RZ_NULLABLE RzInterpInstance *iset) { - if (!iset) { +RZ_API void rz_interp_instance_free(RZ_OWN RZ_NULLABLE RzInterpInstance *inst) { + if (!inst) { return; } - ht_up_free(iset->var_name_hashes); - if (iset->io_request_rbuf) { - rz_th_ring_buf_free(iset->io_request_rbuf); - } - if (iset->io_result_rbuf) { - rz_th_ring_buf_free(iset->io_result_rbuf); - } - if (iset->entry_points) { - rz_th_ring_buf_free(iset->entry_points); - } - if (iset->run_state_sync) { - rz_th_sem_free(iset->run_state_sync); - } - if (iset->run_state) { - rz_interp_run_state_free(iset->run_state); - } - rz_analysis_il_context_free(iset->il_ctx); - free(iset); + ht_up_free(inst->var_name_hashes); + rz_analysis_il_context_free(inst->il_ctx); + free(inst); } static void report_yield_xref( @@ -651,7 +595,7 @@ static void report_yield_xref( } RzBitVector to_bv; rz_bv_init(&to_bv, 64); - if (!ctx->inst->plugin->to_concrete_const(to, &to_bv) || rz_bv_len(&to_bv) > 64) { + if (!val_domain(ctx->inst)->to_concrete_const(to, &to_bv) || rz_bv_len(&to_bv) > 64) { // Isn't reported // TODO: we might also want to report multiple values here depending on the value domain goto cleanup; @@ -726,14 +670,14 @@ void write_var_to_state(RzInterpInstance *inst, RZ_LOG_WARN("New global variable created: 0x%" PFMT64x "\n", var_id) return; } - av = inst->plugin->val_new_top(); + av = val_domain(inst)->val_new_top(); if (!av) { rz_warn_if_reached(); return; } ht_up_insert(ht_vals, var_id, av); } - inst->plugin->copy(av, data); + val_domain(inst)->copy(av, data); } bool read_var_from_state(RzInterpInstance *inst, @@ -763,7 +707,7 @@ bool read_var_from_state(RzInterpInstance *inst, rz_warn_if_reached(); return false; } - inst->plugin->copy(data, av); + val_domain(inst)->copy(data, av); return true; } @@ -792,18 +736,12 @@ bool load_abstr_data( io_req.mem_idx = mem_idx; io_req.n_bits = n_bits; io_req.big_endian = inst->il_ctx->config->big_endian; - if (rz_th_ring_buf_put(inst->io_request_rbuf, &io_req) != RZ_THREAD_RING_BUF_OK) { - return false; - } - RzInterpIOResult io_res = { 0 }; - if (rz_th_ring_buf_take_blocking(inst->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { - return false; - } - if (!io_res.req_ok) { - inst->plugin->set_top(out); + bool req_ok = inst->config.io_read(&io_req, inst->config.cb_user); + if (!req_ok) { + val_domain(inst)->set_top(out); return true; } - inst->plugin->set_const_bv(out, &out_bv); + val_domain(inst)->set_const_bv(out, &out_bv); char *bytes = rz_bv_as_hex_string(&out_bv, true); RZ_LOG_DEBUG("prototype: READ @ mem:%" PFMT32d " 0x%" PFMT64x " : %s\n", mem_idx, rz_bv_to_ut64(io_req.addr), bytes); @@ -815,7 +753,7 @@ static bool set_abstr_pc(RzInterpInstance *inst, RzInterpAbstrState *state, RzIn rz_return_val_if_fail(state && pc, false); RzBitVector pc_bv; rz_bv_init(&pc_bv, 64); - if (inst->plugin->to_concrete_const(pc, &pc_bv)) { + if (val_domain(inst)->to_concrete_const(pc, &pc_bv)) { state->pc_state = RZ_INTERP_PC_CONST; state->pc = rz_bv_to_ut64(&pc_bv); } else { @@ -842,7 +780,7 @@ static bool value_indicates_ret_addr_write(RzInterpRunContext *ctx, RzInterpAbst // 0x4a ... // but it is questionable whether that should be even detected at all, since there is no way to know // if it is intended as call or jump. - bool ret = ctx->inst->plugin->to_concrete_const(val, &bv) && + bool ret = val_domain(ctx->inst)->to_concrete_const(val, &bv) && (rz_bv_to_ut64(&bv) == ctx->il_block_end || // Sparc stores the call instruction PC into o8. // The return instruction jumps then to o7+8. @@ -887,7 +825,7 @@ static bool eval_pure(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT Rz RzBitVector cond_bv; rz_bv_init(&cond_bv, 64); - if (!ctx->inst->plugin->to_concrete_const(out, &cond_bv)) { + if (!val_domain(ctx->inst)->to_concrete_const(out, &cond_bv)) { // Can't decide which pure to evaluate. rz_bv_fini(&cond_bv); goto map_to_top; @@ -909,17 +847,17 @@ static bool eval_pure(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT Rz break; } case RZ_IL_OP_B0: - ctx->inst->plugin->set_const_bool(out, false); + val_domain(ctx->inst)->set_const_bool(out, false); break; case RZ_IL_OP_B1: - ctx->inst->plugin->set_const_bool(out, false); + val_domain(ctx->inst)->set_const_bool(out, false); break; case RZ_IL_OP_CAST: { if (!eval_pure(ctx, pure->op.cast.val, out)) { RZ_LOG_ERROR("prototype: CAST val failed to evaluate.\n"); return false; } - RzInterpAbstrVal *fill_bit = ctx->inst->plugin->val_new_top(); + RzInterpAbstrVal *fill_bit = val_domain(ctx->inst)->val_new_top(); if (!fill_bit) { return false; } @@ -927,12 +865,12 @@ static bool eval_pure(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT Rz RZ_LOG_ERROR("prototype: CAST fill failed to evaluate.\n"); return false; } - ctx->inst->plugin->eval_cast(pure->op.cast.length, fill_bit, out); - ctx->inst->plugin->val_free(fill_bit); + val_domain(ctx->inst)->eval_cast(pure->op.cast.length, fill_bit, out); + val_domain(ctx->inst)->val_free(fill_bit); break; } case RZ_IL_OP_BITV: - ctx->inst->plugin->set_const_bv(out, pure->op.bitv.value); + val_domain(ctx->inst)->set_const_bv(out, pure->op.bitv.value); break; case RZ_IL_OP_APPEND: case RZ_IL_OP_LOGAND: @@ -967,7 +905,7 @@ static bool eval_pure(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT Rz } // Hint: As an optimization, we could short-circuit if out is top here. // However it entirely depends on the plugin whether this is possible, or we lose a lot of precision by doing so. - RzInterpAbstrVal *y = ctx->inst->plugin->val_new_top(); + RzInterpAbstrVal *y = val_domain(ctx->inst)->val_new_top(); if (!y) { return false; } @@ -975,8 +913,8 @@ static bool eval_pure(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT Rz RZ_LOG_ERROR("prototype: binop y failed to evaluate.\n"); return false; } - ctx->inst->plugin->eval_binop(pure->code, out, y); - ctx->inst->plugin->val_free(y); + val_domain(ctx->inst)->eval_binop(pure->code, out, y); + val_domain(ctx->inst)->val_free(y); break; } case RZ_IL_OP_LOGNOT: @@ -990,7 +928,7 @@ static bool eval_pure(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT Rz RZ_LOG_ERROR("prototype: unop x failed to evaluate.\n"); return false; } - ctx->inst->plugin->eval_unop(pure->code, out); + val_domain(ctx->inst)->eval_unop(pure->code, out); break; } case RZ_IL_OP_SHIFTL: @@ -1004,7 +942,7 @@ static bool eval_pure(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT Rz } // Hint: As an optimization, we could short-circuit if out is top here. // However it entirely depends on the plugin whether this is possible, or we lose a lot of precision by doing so. - RzInterpAbstrVal *y = ctx->inst->plugin->val_new_top(); + RzInterpAbstrVal *y = val_domain(ctx->inst)->val_new_top(); if (!y) { return false; } @@ -1012,19 +950,19 @@ static bool eval_pure(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT Rz RZ_LOG_ERROR("prototype: SHIFT(L/R) y failed to evaluate.\n"); return false; } - RzInterpAbstrVal *fill_bit = ctx->inst->plugin->val_new_top(); + RzInterpAbstrVal *fill_bit = val_domain(ctx->inst)->val_new_top(); if (!fill_bit) { - ctx->inst->plugin->val_free(y); + val_domain(ctx->inst)->val_free(y); return false; } if (!eval_pure(ctx, pfill_bit, fill_bit)) { RZ_LOG_ERROR("prototype: SHIFT(L/R) fill_bit failed to evaluate.\n"); - ctx->inst->plugin->val_free(y); + val_domain(ctx->inst)->val_free(y); return false; } - ctx->inst->plugin->eval_shift(pure->code == RZ_IL_OP_SHIFTR, out, y, fill_bit); - ctx->inst->plugin->val_free(y); - ctx->inst->plugin->val_free(fill_bit); + val_domain(ctx->inst)->eval_shift(pure->code == RZ_IL_OP_SHIFTR, out, y, fill_bit); + val_domain(ctx->inst)->val_free(y); + val_domain(ctx->inst)->val_free(fill_bit); break; } case RZ_IL_OP_LOADW: @@ -1041,7 +979,7 @@ static bool eval_pure(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT Rz // from all possible addresses and join the results. RzBitVector ld_addr; rz_bv_init(&ld_addr, 64); - if (!ctx->inst->plugin->to_concrete_const(out, &ld_addr)) { + if (!val_domain(ctx->inst)->to_concrete_const(out, &ld_addr)) { rz_bv_fini(&ld_addr); goto map_to_top; } @@ -1107,7 +1045,7 @@ static bool eval_pure(RzInterpRunContext *ctx, const RzILOpPure *pure, RZ_OUT Rz return true; map_to_top: - ctx->inst->plugin->set_top(out); + val_domain(ctx->inst)->set_top(out); return true; } @@ -1118,7 +1056,7 @@ static void eval_call(RzInterpRunContext *ctx) { RzIterator *it = ht_up_as_iter(ctx->astate->globals); RzInterpAbstrVal **av; rz_iterator_foreach(it, av) { - ctx->inst->plugin->set_top(*av); + val_domain(ctx->inst)->set_top(*av); } } @@ -1146,7 +1084,7 @@ static bool eval_effect(RzInterpRunContext *ctx, break; } case RZ_IL_OP_SET: { - eval_out = ctx->inst->plugin->val_new_top(); + eval_out = val_domain(ctx->inst)->val_new_top(); ut64 vhash = effect->op.set.hash; if (!eval_out || !eval_pure(ctx, effect->op.set.x, eval_out)) { goto error; @@ -1183,13 +1121,13 @@ static bool eval_effect(RzInterpRunContext *ctx, break; } case RZ_IL_OP_JMP: { - eval_out = ctx->inst->plugin->val_new_top(); + eval_out = val_domain(ctx->inst)->val_new_top(); if (!eval_out || !eval_pure(ctx, effect->op.jmp.dst, eval_out)) { goto error; } RzBitVector eval_out_bv; rz_bv_init(&eval_out_bv, 64); - bool is_const = ctx->inst->plugin->to_concrete_const(eval_out, &eval_out_bv); + bool is_const = val_domain(ctx->inst)->to_concrete_const(eval_out, &eval_out_bv); if (!is_const) { RZ_LOG_DEBUG("PC is going to be set to an abstract value! Current PC = 0x%" PFMT64x "\n", pc); } @@ -1225,12 +1163,12 @@ static bool eval_effect(RzInterpRunContext *ctx, break; } case RZ_IL_OP_BRANCH: { - eval_out = ctx->inst->plugin->val_new_top(); + eval_out = val_domain(ctx->inst)->val_new_top(); if (!eval_out || !eval_pure(ctx, effect->op.branch.condition, eval_out)) { goto error; } - bool may_be_true = ctx->inst->plugin->may_be_bool(eval_out, true); - bool may_be_false = ctx->inst->plugin->may_be_bool(eval_out, false); + bool may_be_true = val_domain(ctx->inst)->may_be_bool(eval_out, true); + bool may_be_false = val_domain(ctx->inst)->may_be_bool(eval_out, false); ut64 fallthrough_pc = ctx->astate->pc; if (may_be_true && may_be_false) { RzInterpAbstrState *true_state = rz_interp_abstr_state_clone(ctx->inst, ctx->astate); @@ -1268,20 +1206,20 @@ static bool eval_effect(RzInterpRunContext *ctx, } case RZ_IL_OP_STORE: case RZ_IL_OP_STOREW: { - RzInterpAbstrVal *st_addr = ctx->inst->plugin->val_new_top(); + RzInterpAbstrVal *st_addr = val_domain(ctx->inst)->val_new_top(); RzILOpPure *key = effect->code == RZ_IL_OP_STORE ? effect->op.store.key : effect->op.storew.key; RzILMemIndex mem_idx = effect->code == RZ_IL_OP_STORE ? 0 : effect->op.storew.mem; if (!eval_pure(ctx, key, st_addr)) { RZ_LOG_ERROR("prototype: STORE/STOREW key failed to evaluate.\n"); - ctx->inst->plugin->val_free(st_addr); + val_domain(ctx->inst)->val_free(st_addr); goto error; } RzBitVector st_addr_bv; rz_bv_init(&st_addr_bv, 64); - bool st_addr_is_const = st_addr && ctx->inst->plugin->to_concrete_const(st_addr, &st_addr_bv); + bool st_addr_is_const = st_addr && val_domain(ctx->inst)->to_concrete_const(st_addr, &st_addr_bv); if (!st_addr_is_const) { rz_bv_fini(&st_addr_bv); - ctx->inst->plugin->val_free(st_addr); + val_domain(ctx->inst)->val_free(st_addr); break; } if (rz_bv_len(&st_addr_bv) == 64) { @@ -1295,11 +1233,11 @@ static bool eval_effect(RzInterpRunContext *ctx, } RzILOpPure *pval = effect->code == RZ_IL_OP_STORE ? effect->op.store.value : effect->op.storew.value; - eval_out = ctx->inst->plugin->val_new_top(); + eval_out = val_domain(ctx->inst)->val_new_top(); if (!eval_out || !eval_pure(ctx, pval, eval_out)) { RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); rz_bv_fini(&st_addr_bv); - ctx->inst->plugin->val_free(st_addr); + val_domain(ctx->inst)->val_free(st_addr); goto error; } if (value_indicates_ret_addr_write(ctx, eval_out)) { @@ -1308,7 +1246,7 @@ static bool eval_effect(RzInterpRunContext *ctx, ctx->call_cand.in_mem = true; } report_yield_xref(ctx, insn_pkt_size, ctx->insn_addr, st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); - ctx->inst->plugin->val_free(st_addr); + val_domain(ctx->inst)->val_free(st_addr); if (!store_abstr_data(ctx->inst, mem_idx, st_addr, eval_out)) { rz_bv_fini(&st_addr_bv); goto error; @@ -1323,10 +1261,10 @@ static bool eval_effect(RzInterpRunContext *ctx, // Ignore for now. break; } - ctx->inst->plugin->val_free(eval_out); + val_domain(ctx->inst)->val_free(eval_out); return true; error: - ctx->inst->plugin->val_free(eval_out); + val_domain(ctx->inst)->val_free(eval_out); return false; } @@ -1447,7 +1385,7 @@ RZ_API RzInterpResult *rz_interp_run(RzInterpInstance *inst, ut64 entry_point, R } ctx.astate = rz_interp_abstr_state_clone(inst, interp_block->entry_state); - const RzILCacheBlock *il_block = rz_il_cache_client_lift_il_block(inst->il_cache_client, ctx.astate->pc); + const RzILCacheBlock *il_block = rz_il_cache_client_lift_il_block(inst->config.il_cache_client, ctx.astate->pc); if (!il_block) { RZ_LOG_ERROR("interpreter: Lifting failed\n"); // TODO: handle this better @@ -1491,7 +1429,7 @@ RZ_API RzInterpResult *rz_interp_run(RzInterpInstance *inst, ut64 entry_point, R RzInterpBlock *interp_block; rz_interval_tree_foreach (&ctx.blocks, it, interp_block) { ctx.astate = rz_interp_abstr_state_clone(inst, interp_block->entry_state); - const RzILCacheBlock *il_block = rz_il_cache_client_lift_il_block(inst->il_cache_client, ctx.astate->pc); + const RzILCacheBlock *il_block = rz_il_cache_client_lift_il_block(inst->config.il_cache_client, ctx.astate->pc); if (!il_block) { RZ_LOG_ERROR("interpreter: Lifting failed\n"); // TODO: handle this better @@ -1513,58 +1451,6 @@ RZ_API RzInterpResult *rz_interp_run(RzInterpInstance *inst, ut64 entry_point, R return res; } -/** - * \brief Interpreter thread - */ -RZ_API bool rz_interp_instance_th(RZ_NONNULL RZ_OWN RzInterpInstance *inst) { - rz_return_val_if_fail(inst && - inst->il_cache_client && - inst->run_state_sync && - inst->plugin, - false); - - bool success = true; - - RZ_LOG_DEBUG("interpreter: Main: Hello.\n"); - - while (true) { - // INIT - RZ_LOG_DEBUG("interpreter: Enter INIT\n"); - rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_INIT); - - ut64 entry_point; - if (rz_th_ring_buf_take_blocking(inst->entry_points, &entry_point) != RZ_THREAD_RING_BUF_OK) { - // No more entry points to interpret => Terminate. - // OR. - success = true; - break; - } - - // EMU - RZ_LOG_DEBUG("interpreter: Enter EMU\n"); - rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_EMU); - - RzInterpResult *res = rz_interp_run(inst, entry_point, RZ_INTERP_RESULT_DIMEN_XREFS | RZ_INTERP_RESULT_DIMEN_COMMENTS); // TODO: make dimensions configurable - if (res) { - // TODO: use some sort of channel for delivering results - rz_pvector_push(&inst->results, res); - } else { - RZ_LOG_ERROR("Interpreter run failed for entry point 0x%" PFMT64x "\n", entry_point); - } - - // CLEAN - RZ_LOG_DEBUG("interpreter: Enter CLEAN\n"); - rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_CLEAN); - - // Wait until RzInquiry asks to start again. - rz_th_sem_wait(inst->run_state_sync); - } - - RZ_LOG_DEBUG("interpreter: Enter TERM\n"); - rz_interp_run_state_set(inst->run_state, RZ_INTERP_RUN_STATE_TERM); - return success; -} - static void bb_add_target(RzAnalysisBlock *abb, ut64 target) { if (abb->jump == UT64_MAX && abb->fail != target) { abb->jump = target; diff --git a/librz/inquiry/interp/state.c b/librz/inquiry/interp/state.c deleted file mode 100644 index e1c4c063606..00000000000 --- a/librz/inquiry/interp/state.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "rz_th.h" -#include "rz_types.h" -#include "rz_util/rz_assert.h" -#include - -struct rz_interp_run_state { - RzThreadLock *lock; ///< The mutex around the state flag. - RzInterpRunStateFlag flag; ///< The current set state. -}; - -RZ_API const char *rz_interp_run_state_flag_str(RzInterpRunStateFlag flag) { - switch (flag) { - case RZ_INTERP_RUN_STATE_OUT_OF_LOOP: - return "-"; - case RZ_INTERP_RUN_STATE_INIT: - return "I"; - case RZ_INTERP_RUN_STATE_EMU: - return "O"; - case RZ_INTERP_RUN_STATE_CLEAN: - return "C"; - case RZ_INTERP_RUN_STATE_TERM: - return "T"; - } - rz_warn_if_reached(); - return "-"; -} - -RZ_API RZ_OWN RzInterpRunState *rz_interp_run_state_new() { - RzInterpRunState *state = RZ_NEW0(RzInterpRunState); - if (!state) { - return NULL; - } - state->flag = RZ_INTERP_RUN_STATE_OUT_OF_LOOP; - state->lock = rz_th_lock_new(false); - if (!state->lock) { - free(state); - return NULL; - } - return state; -} - -RZ_API void rz_interp_run_state_free(RZ_OWN RZ_NULLABLE RzInterpRunState *state) { - if (!state) { - return; - } - rz_th_lock_free(state->lock); - free(state); -} - -RZ_API RzInterpRunStateFlag rz_interp_run_state_get(RZ_BORROW RZ_NONNULL RzInterpRunState *state) { - rz_return_val_if_fail(state, RZ_INTERP_RUN_STATE_TERM); - rz_th_lock_enter(state->lock); - RzInterpRunStateFlag flag = state->flag; - rz_th_lock_leave(state->lock); - return flag; -} - -RZ_API RzInterpRunStateFlag rz_interp_run_state_get_unsafe(const RZ_NONNULL RzInterpRunState *state) { - rz_return_val_if_fail(state, RZ_INTERP_RUN_STATE_TERM); - return state->flag; -} - -/** - * \brief Sets the run state. - * This function is declared IPI, so it is not used outside of the interpreter module! - */ -RZ_IPI void rz_interp_run_state_set(RZ_BORROW RZ_NONNULL RzInterpRunState *state, RzInterpRunStateFlag flag) { - rz_th_lock_enter(state->lock); - state->flag = flag; - rz_th_lock_leave(state->lock); -} diff --git a/librz/inquiry/meson.build b/librz/inquiry/meson.build index 4120e69c8e0..599209872e8 100644 --- a/librz/inquiry/meson.build +++ b/librz/inquiry/meson.build @@ -7,7 +7,6 @@ rz_inquiry_sources = [ 'inquiry.c', 'il_cache.c', 'interp/interpreter.c', - 'interp/state.c', 'interp/val_abs_constant.c', 'interp/driver.c' ] diff --git a/test/unit/test_inquiry_interp.c b/test/unit/test_inquiry_interp.c index 008b78da546..e9cd1c48c29 100644 --- a/test/unit/test_inquiry_interp.c +++ b/test/unit/test_inquiry_interp.c @@ -11,26 +11,10 @@ typedef struct test_interp_t { RzILCache *il_cache; RzILCacheClient *il_cache_client; RzInterpInstance *inst; - RzThread *io_server; } TestInterp; -static void *io_server_th(void *user) { - TestInterp *interp = user; - RzInterpIOReadRequest io_req = { 0 }; - while (true) { - RzThreadRingBufResult r = rz_th_ring_buf_take_blocking(interp->inst->io_request_rbuf, &io_req); - if (r == RZ_THREAD_RING_BUF_CLOSED) { - return NULL; - } else if (r == RZ_THREAD_RING_BUF_OK) { - RzInterpIOResult io_res = { 0 }; - io_res.req_ok = false; // Currently we do not care about contents, just make the interpreter continue - if (rz_th_ring_buf_put(interp->inst->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { - rz_warn_if_reached(); - } - } else { - rz_warn_if_reached(); - } - } +static bool io_read(RzInterpIOReadRequest *req, void *user) { + return false; // Currently we do not care about contents, just make the interpreter continue } static TestInterp *interp_new(const char *arch, int bits, ut64 baddr, const char *url) { @@ -44,20 +28,22 @@ static TestInterp *interp_new(const char *arch, int bits, ut64 baddr, const char interp->io->va = 1; interp->il_cache = rz_il_cache_new(interp->analysis, interp->io, NULL, RZ_IL_CACHE_CONFIG_NOP_UNLIFTED | RZ_IL_CACHE_CONFIG_NO_SLEEP); interp->il_cache_client = rz_il_cache_new_client(interp->il_cache, false); - interp->inst = rz_interp_instance_new(interp->analysis, &rz_interp_value_domain_const, interp->il_cache_client); + RzInterpConfig config = { + .val_domain = &rz_interp_value_domain_const, + .il_cache_client = interp->il_cache_client, + .cb_user = NULL, + .io_read = io_read + }; + interp->inst = rz_interp_instance_new(interp->analysis, &config); RzIODesc *desc = rz_io_open_at(interp->io, url, RZ_PERM_RX, 0644, baddr, NULL); if (!desc) { mu_perror("load code"); return NULL; } - interp->io_server = rz_th_new(io_server_th, interp); return interp; } static void interp_free(TestInterp *interp) { - rz_th_ring_buf_close(interp->inst->io_request_rbuf); - rz_th_wait(interp->io_server); - rz_th_free(interp->io_server); rz_interp_instance_free(interp->inst); rz_il_cache_free(interp->il_cache); rz_io_free(interp->io); From 36bc61f31abfc67b8430298a330da2bafac6c848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sun, 19 Jul 2026 15:21:21 +0200 Subject: [PATCH 328/334] Allow 1-sized ring buffer This is legal too and sometimes useful. --- librz/util/thread_ring_buf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/librz/util/thread_ring_buf.c b/librz/util/thread_ring_buf.c index 4d9cd014840..b6dbcf8b17d 100644 --- a/librz/util/thread_ring_buf.c +++ b/librz/util/thread_ring_buf.c @@ -72,10 +72,10 @@ struct rz_th_ring_buf_t { * \param n The number of elements the buffer can hold. * \param elem_size Number of bytes each element has. * - * \return The new rung buffer or NULL in case of failure. + * \return The new ring buffer or NULL in case of failure. */ RZ_API RZ_OWN RzThreadRingBuf *rz_th_ring_buf_new(size_t n, size_t elem_size) { - rz_return_val_if_fail(n > 1 && elem_size > 0, NULL); + rz_return_val_if_fail(n > 0 && elem_size > 0, NULL); RzThreadRingBuf *rbuf = RZ_NEW0(RzThreadRingBuf); if (!rbuf) { rz_warn_if_reached(); From 141a4beaa251fbbaa81a3931b14bba643c02e6e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sun, 19 Jul 2026 15:23:13 +0200 Subject: [PATCH 329/334] Reimplement interp synchronization --- librz/include/rz_inquiry/rz_interpreter.h | 20 +- librz/inquiry/interp/driver.c | 596 +++++++--------------- librz/inquiry/interp/interpreter.c | 9 +- test/unit/test_inquiry_interp.c | 4 +- 4 files changed, 198 insertions(+), 431 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 01806f9ba11..cf10ce23869 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -12,16 +12,9 @@ #include #include #include -#include #include #include -/** - * \brief Only one IO request at a time is possible (currently). - */ -#define RZ_INTERP_IO_RBUF_SIZE 128 -#define RZ_INTERP_ENTRY_POINTS_RBUF_SIZE 4 - typedef struct rz_interp_run_context_t RzInterpRunContext; typedef struct rz_interp_result_t RzInterpResult; @@ -152,15 +145,20 @@ typedef struct { } RzInterpValueAbstraction; -typedef struct ry_interp_io_read_request_t { +typedef struct rz_interp_io_read_request_t { size_t mem_idx; ///< The memory space to read/write. bool big_endian; ///< Set if the data is big endian ordered. const RzBitVector *addr; ///< The address to read/write. - const RzBitVector *st_data; ///< The data to store. RzBitVector *ld_data; ///< The bit vector to load into. It is BORROWED. size_t n_bits; ///< The number of bits to read/write. } RzInterpIOReadRequest; +typedef enum rz_interp_io_read_result_t { + RZ_INTERP_IO_READ_RESULT_OK, ///< ld_data fully written + RZ_INTERP_IO_READ_RESULT_TOP, ///< result should be considered top (memory contents unknown) + RZ_INTERP_IO_READ_RESULT_BREAK ///< interpreter is signaled to stop +} RzInterpIOReadResult; + typedef struct rz_interp_config_t { RzInterpValueAbstraction *val_domain; @@ -168,7 +166,7 @@ typedef struct rz_interp_config_t { RZ_BORROW RzILCacheClient *il_cache_client; void *cb_user; - bool (*io_read)(RZ_NONNULL RZ_OWN RzInterpIOReadRequest *req, void *user); + RzInterpIOReadResult (*io_read)(RZ_NONNULL RZ_OWN RzInterpIOReadRequest *req, void *user); } RzInterpConfig; /** @@ -181,8 +179,6 @@ struct rz_interp_instance_t { RzAnalysisILContext *il_ctx; ///< Context about available global vars and memory HtUP *var_name_hashes; ///< Map of DJB2 hashes to variable names. RZ_DEPRECATE const char *arch_name; ///< Name of architecture. Used only by work-arounds until we have RzArch. - - RzPVector /**/ results; ///< TODO: replace this by a queue/rbuf/... to handle interp and results concurrently }; RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new(RzAnalysis *analysis, RZ_NONNULL RZ_BORROW const RzInterpConfig *config); diff --git a/librz/inquiry/interp/driver.c b/librz/inquiry/interp/driver.c index 932a06d50a3..e9a9a67aea5 100644 --- a/librz/inquiry/interp/driver.c +++ b/librz/inquiry/interp/driver.c @@ -12,339 +12,233 @@ #include #include -typedef struct rz_interp_io_result_t { - bool req_ok; ///< Set to true if IO request succeeded. -} RzInterpIOResult; - -typedef struct rz_interp_run_state RzInterpRunState; - -typedef enum rz_interp_state_flag { - /** - * \brief Interpreter is still outside of its defined loop. - * E.g. shortly after its thread was spawned. - */ - RZ_INTERP_RUN_STATE_OUT_OF_LOOP, - RZ_INTERP_RUN_STATE_INIT, ///< Initialization state. - RZ_INTERP_RUN_STATE_EMU, ///< Emulation state. - RZ_INTERP_RUN_STATE_CLEAN, ///< Cleaning state. - RZ_INTERP_RUN_STATE_TERM, ///< Termination state. -} RzInterpRunStateFlag; - -RZ_API RZ_OWN RzInterpRunState *rz_interp_run_state_new(); -RZ_API void rz_interp_run_state_free(RZ_OWN RZ_NULLABLE RzInterpRunState *state); -RZ_API RzInterpRunStateFlag rz_interp_run_state_get(RZ_BORROW RZ_NONNULL RzInterpRunState *state); -RZ_API RzInterpRunStateFlag rz_interp_run_state_get_unsafe(const RZ_NONNULL RzInterpRunState *state); -RZ_API const char *rz_interp_run_state_flag_str(RzInterpRunStateFlag flag); - -RZ_IPI void rz_interp_run_state_set(RZ_BORROW RZ_NONNULL RzInterpRunState *state, RzInterpRunStateFlag flag); - -struct rz_interp_run_state { - RzThreadLock *lock; ///< The mutex around the state flag. - RzInterpRunStateFlag flag; ///< The current set state. -}; - -RZ_API const char *rz_interp_run_state_flag_str(RzInterpRunStateFlag flag) { - switch (flag) { - case RZ_INTERP_RUN_STATE_OUT_OF_LOOP: - return "-"; - case RZ_INTERP_RUN_STATE_INIT: - return "I"; - case RZ_INTERP_RUN_STATE_EMU: - return "O"; - case RZ_INTERP_RUN_STATE_CLEAN: - return "C"; - case RZ_INTERP_RUN_STATE_TERM: - return "T"; - } - rz_warn_if_reached(); - return "-"; -} - -RZ_API RZ_OWN RzInterpRunState *rz_interp_run_state_new() { - RzInterpRunState *state = RZ_NEW0(RzInterpRunState); - if (!state) { - return NULL; - } - state->flag = RZ_INTERP_RUN_STATE_OUT_OF_LOOP; - state->lock = rz_th_lock_new(false); - if (!state->lock) { - free(state); - return NULL; - } - return state; -} - -RZ_API void rz_interp_run_state_free(RZ_OWN RZ_NULLABLE RzInterpRunState *state) { - if (!state) { - return; - } - rz_th_lock_free(state->lock); - free(state); -} - -RZ_API RzInterpRunStateFlag rz_interp_run_state_get(RZ_BORROW RZ_NONNULL RzInterpRunState *state) { - rz_return_val_if_fail(state, RZ_INTERP_RUN_STATE_TERM); - rz_th_lock_enter(state->lock); - RzInterpRunStateFlag flag = state->flag; - rz_th_lock_leave(state->lock); - return flag; -} +/** + * + * 1 queue for entry points main -> interps + * 1 request_rbuf for requests from interps -> main + * n_thread response_rbuf for responses main -> interp (rbuf here only works if for every request that causes a response, the requester always also waits for the response) + * + * break must close at least request_rbuf + * + */ -RZ_API RzInterpRunStateFlag rz_interp_run_state_get_unsafe(const RZ_NONNULL RzInterpRunState *state) { - rz_return_val_if_fail(state, RZ_INTERP_RUN_STATE_TERM); - return state->flag; -} +typedef struct interp_thread InterpThread; /** - * \brief Sets the run state. - * This function is declared IPI, so it is not used outside of the interpreter module! + * \brief Message from an interpreter to the main loop */ -RZ_IPI void rz_interp_run_state_set(RZ_BORROW RZ_NONNULL RzInterpRunState *state, RzInterpRunStateFlag flag) { - rz_th_lock_enter(state->lock); - state->flag = flag; - rz_th_lock_leave(state->lock); -} +typedef struct interp_driver_message_t { + enum { + DRIVER_MESSAGE_IO_READ, + DRIVER_MESSAGE_INTERP_RESULT + } type; + union { + struct { + RzInterpIOReadRequest *request; + InterpThread *requester; + } io_read; + RzInterpResult *interp_result; + } payload; +} InterpDriverMessage; + +#define DRIVER_MAIN_CH_SIZE 16 ///< TODO: optimize this to be small but blocking any interpreters under practical circumstances + +typedef struct interp_driver_t { + RzThreadQueue /**/ *entry_points_ch; ///< Main delivers entry points to multiple interpreters with this. TODO: linked list is not optimal, but rbuf may lead to starvation + RzThreadRingBuf *main_ch; ///< Channel to main. Multiple interpreters send info requests and analysis results with this. +} InterpDriver; -typedef struct interp_thread_context { +/** + * \brief Message from the main loop to an interpreter thread + * Currently corresponds to exactly one request sent on InterpDriver.main_ch + */ +typedef struct interp_thread_message_t { + enum { + INTERP_MESSAGE_IO_READ_RESULT + } type; + union { + RzInterpIOReadResult io_read_result; + } payload; +} InterpThreadMessage; + +struct interp_thread { + InterpDriver *driver; RzThread *th; RzInterpInstance *inst; - RzInterpRunState *run_state; ///< The state the interpreter is currently in. - /** - * \brief The semaphore to sync RzInquiry and the interpreter between the Clean and Init run state. - */ - RzThreadSemaphore *run_state_sync; - - RzThreadRingBuf /**/ *io_request_rbuf; ///< The ring buffer for read/write requests to the IO layer. - RzThreadRingBuf /**/ *io_result_rbuf; ///< The ring buffer for the read/write requests' answers. /** - * \brief Entry points the interpreter starts interpreting from. + * \brief Channel to this thread. + * Main delivers responses to requests on InterpDriver.main_ch to this in the exact order they were requested. */ - RzThreadRingBuf *entry_points; -} InterpThread; + RzThreadRingBuf *ch; +} /* InterpThread */; static void *interp_th(void *user) { InterpThread *ctx = user; RzInterpInstance *inst = ctx->inst; - - bool success = true; - - RZ_LOG_DEBUG("interpreter: Main: Hello.\n"); - while (true) { - // INIT - RZ_LOG_DEBUG("interpreter: Enter INIT\n"); - rz_interp_run_state_set(ctx->run_state, RZ_INTERP_RUN_STATE_INIT); - - ut64 entry_point; - if (rz_th_ring_buf_take_blocking(ctx->entry_points, &entry_point) != RZ_THREAD_RING_BUF_OK) { - // No more entry points to interpret => Terminate. - // OR. - success = true; + ut64 *entry_point; + if (!rz_th_queue_pop(ctx->driver->entry_points_ch, false, (void **)&entry_point)) { + // closed, no more entry points to interpret break; } - - // EMU - RZ_LOG_DEBUG("interpreter: Enter EMU\n"); - rz_interp_run_state_set(ctx->run_state, RZ_INTERP_RUN_STATE_EMU); - - RzInterpResult *res = rz_interp_run(inst, entry_point, RZ_INTERP_RESULT_DIMEN_XREFS | RZ_INTERP_RESULT_DIMEN_COMMENTS); // TODO: make dimensions configurable + ut64 entry_point_val = *entry_point; + free(entry_point); + RzInterpResult *res = rz_interp_run(inst, entry_point_val, RZ_INTERP_RESULT_DIMEN_XREFS | RZ_INTERP_RESULT_DIMEN_COMMENTS); // TODO: make dimensions configurable if (res) { - // TODO: use some sort of channel for delivering results - rz_pvector_push(&inst->results, res); + InterpDriverMessage msg = { + .type = DRIVER_MESSAGE_INTERP_RESULT, + .payload = { + .interp_result = res + } + }; + rz_th_ring_buf_put(ctx->driver->main_ch, &msg); } else { - RZ_LOG_ERROR("Interpreter run failed for entry point 0x%" PFMT64x "\n", entry_point); + RZ_LOG_ERROR("Interpreter run failed for entry point 0x%" PFMT64x "\n", entry_point_val); } - - // CLEAN - RZ_LOG_DEBUG("interpreter: Enter CLEAN\n"); - rz_interp_run_state_set(ctx->run_state, RZ_INTERP_RUN_STATE_CLEAN); - - // Wait until RzInquiry asks to start again. - rz_th_sem_wait(ctx->run_state_sync); } - - RZ_LOG_DEBUG("interpreter: Enter TERM\n"); - rz_interp_run_state_set(ctx->run_state, RZ_INTERP_RUN_STATE_TERM); - return (void *)success; -} - -static bool setup_ipc_objects( - RZ_OUT RzThreadRingBuf **io_request_rbuf, - RZ_OUT RzThreadRingBuf **io_result_rbuf, - RZ_OUT RzThreadRingBuf **entry_points) { - *io_request_rbuf = NULL; - *io_result_rbuf = NULL; - *entry_points = NULL; - - // Setup the IO queues. Each interpreter instance needs it's own queue at - // for writing IO. Because the writing is done on the IO cache, and each - // instance needs its own cache. - *io_request_rbuf = rz_th_ring_buf_new(RZ_INTERP_IO_RBUF_SIZE, sizeof(RzInterpIOReadRequest)); - *io_result_rbuf = rz_th_ring_buf_new(RZ_INTERP_IO_RBUF_SIZE, sizeof(RzInterpIOResult)); - *entry_points = rz_th_ring_buf_new(RZ_INTERP_ENTRY_POINTS_RBUF_SIZE, sizeof(ut64)); - if (!*io_request_rbuf || !*io_result_rbuf || !*entry_points) { - rz_warn_if_reached(); - goto error_free; - } - - return true; - -error_free: - rz_th_ring_buf_free(*io_request_rbuf); - rz_th_ring_buf_free(*io_result_rbuf); - rz_th_ring_buf_free(*entry_points); - return false; + return NULL; } -static InterpThread *interp_thread_new(RZ_OWN RzInterpInstance *inst) { +static InterpThread *interp_thread_new(InterpDriver *driver, RZ_OWN RzInterpInstance *inst) { InterpThread *ctx = RZ_NEW(InterpThread); if (!ctx) { return NULL; } + ctx->driver = driver; ctx->inst = inst; - ctx->run_state = rz_interp_run_state_new(); - if (!ctx->run_state) { + ctx->ch = rz_th_ring_buf_new(1, sizeof(InterpThreadMessage)); // At the moment, threads directly wait after a single request, so size 1 is enough + if (!ctx->ch) { goto err_ctx; } - RzThreadRingBuf *io_request_rbuf = NULL; - RzThreadRingBuf *io_result_rbuf = NULL; - RzThreadRingBuf *entry_points = NULL; - if (!setup_ipc_objects(&io_request_rbuf, &io_result_rbuf, &entry_points)) { - goto err_run_state; - } - ctx->io_request_rbuf = io_request_rbuf; - ctx->io_result_rbuf = io_result_rbuf; - ctx->entry_points = entry_points; - inst->config.cb_user = ctx; // TODO: remove this hack - ctx->run_state_sync = rz_th_sem_new(0); ctx->th = rz_th_new(interp_th, ctx); if (!ctx->th) { - goto err_run_state; + goto err_ch; } return ctx; -err_run_state: - rz_interp_run_state_free(ctx->run_state); +err_ch: + rz_th_ring_buf_free(ctx->ch); err_ctx: free(ctx); return NULL; } -static void close_reset_ipc_obj(InterpThread *th) { - // Close and clear all the IPC objects of this interpreter. - // This also clears the buffer and queues - RzInterpInstance *inst = th->inst; - rz_th_ring_buf_close(th->io_request_rbuf); - rz_th_ring_buf_close(th->io_result_rbuf); - rz_th_ring_buf_close(th->entry_points); - rz_th_ring_buf_close(inst->config.il_cache_client->req_rbuf); - rz_th_queue_close(inst->config.il_cache_client->il_queue); - rz_list_free(rz_th_queue_pop_all(inst->config.il_cache_client->il_queue)); -} - -static void open_ipc_obj(InterpThread *th) { - // Open queue again, so the interpretation can start at another - // jump target again. - RzInterpInstance *inst = th->inst; - rz_th_ring_buf_open(th->io_request_rbuf); - rz_th_ring_buf_open(th->io_result_rbuf); - rz_th_ring_buf_open(inst->config.il_cache_client->req_rbuf); - rz_th_queue_open(inst->config.il_cache_client->il_queue); - rz_th_ring_buf_open(th->entry_points); +static void interp_thread_free(InterpThread *th) { + if (!th) { + return; + } + rz_th_wait(th->th); + rz_th_free(th->th); + rz_th_ring_buf_free(th->ch); + rz_interp_instance_free(th->inst); + free(th); } -static bool send_io_read(RZ_NONNULL RzInterpIOReadRequest *req, void *user) { +static RzInterpIOReadResult send_io_read(RZ_NONNULL RzInterpIOReadRequest *req, void *user) { InterpThread *th = user; - if (rz_th_ring_buf_put(th->io_request_rbuf, req) != RZ_THREAD_RING_BUF_OK) { - return false; + InterpDriverMessage msg = { + .type = DRIVER_MESSAGE_IO_READ, + .payload = { + .io_read = { + .request = req, + .requester = th + } + } + }; + if (rz_th_ring_buf_put(th->driver->main_ch, &msg) != RZ_THREAD_RING_BUF_OK) { + return RZ_INTERP_IO_READ_RESULT_BREAK; } - RzInterpIOResult io_res = { 0 }; - if (rz_th_ring_buf_take_blocking(th->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { - return false; + InterpThreadMessage ret; + if (rz_th_ring_buf_take_blocking(th->ch, &ret) != RZ_THREAD_RING_BUF_OK) { + return RZ_INTERP_IO_READ_RESULT_BREAK; } - return io_res.req_ok; + rz_return_val_if_fail(ret.type != INTERP_MESSAGE_IO_READ_RESULT, RZ_INTERP_IO_READ_RESULT_BREAK); + return ret.payload.io_read_result; } -static void handle_io_request(RzAnalysisILContext *il_ctx, RzInterpIOReadRequest *io_req, RZ_OUT RzInterpIOResult *io_res) { +static RzInterpIOReadResult handle_io_request(const RzAnalysisILContext *il_ctx, RzInterpIOReadRequest *io_req) { RZ_LOG_DEBUG("inquiry: Received IO read request: mem:%" PFMTSZd " 0x%" PFMT64x "\n", io_req->mem_idx, rz_bv_to_ut64(io_req->addr)); - io_res->req_ok = false; RzILMemIndex mem_idx = io_req->mem_idx; if (mem_idx > rz_vector_len(&il_ctx->memory)) { rz_warn_if_reached(); - return; + return RZ_INTERP_IO_READ_RESULT_TOP; } if (rz_bv_len(io_req->addr) == 64 && rz_bv_msb(io_req->addr)) { + // TODO: remove this RZ_LOG_ERROR("Due to the Unix seek() implementation, addresses with the " "63 bit set can't be addresses.\n"); - return; + return RZ_INTERP_IO_READ_RESULT_TOP; } RzAnalysisILMem *mem = rz_vector_index_ptr(&il_ctx->memory, mem_idx); if (!mem->base_buf) { - io_res->req_ok = false; - } else { - // TODO: here only memory should be read that can be assumed to be constant! - io_res->req_ok = rz_il_loadw_into(mem->base_buf, io_req->ld_data, io_req->addr, io_req->n_bits, io_req->big_endian); + return RZ_INTERP_IO_READ_RESULT_TOP; } - RZ_LOG_DEBUG("inquiry: Sent IO read result. Success = %s.\n", - rz_str_bool(io_res->req_ok)); + // TODO: here only memory should be read that can be assumed to be constant! + bool ok = rz_il_loadw_into(mem->base_buf, io_req->ld_data, io_req->addr, io_req->n_bits, io_req->big_endian); + RZ_LOG_DEBUG("inquiry: Sent IO read result. Success = %s.\n", rz_str_bool(ok)); + return ok ? RZ_INTERP_IO_READ_RESULT_TOP : RZ_INTERP_IO_READ_RESULT_TOP; } -struct ituple { - InterpThread *th; - RzInterpRunStateFlag next_run_state; -}; - RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { - bool return_code = true; + bool return_code = false; bool user_sent_signal = false; - struct ituple *interp_map = NULL; - RzThread *il_cache_th = NULL; - rz_cons_push(); - - RZ_LOG_DEBUG("inquiry: Create IL Cache"); - RzILCache *il_cache = rz_il_cache_new(core->analysis, core->io, - rz_bin_object_get_sections(core->bin->cur->o), + RzILCache *il_cache = rz_il_cache_new(core->analysis, core->io, rz_bin_object_get_sections(core->bin->cur->o), RZ_IL_CACHE_CONFIG_NOP_UNLIFTED | RZ_IL_CACHE_CONFIG_NO_SLEEP); if (!il_cache) { - return_code = false; - goto error_free; + goto err_none; + } + RzThread *il_cache_th = rz_th_new((RzThreadFunction)rz_il_cache_serve, il_cache); + if (!il_cache_th) { + goto err_il_cache; } - if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { - eprintf("Total branch targets in binary: %" PFMT32d "\n", rz_set_u_size(entry_points)); + InterpDriver driver; + driver.entry_points_ch = rz_th_queue_new(RZ_THREAD_QUEUE_UNLIMITED, free); + if (!driver.entry_points_ch) { + goto err_il_cache_th; + } + driver.main_ch = rz_th_ring_buf_new(DRIVER_MAIN_CH_SIZE, sizeof(InterpDriverMessage)); + if (!driver.main_ch) { + goto err_entry_points_ch; } - // Initialize the abstract state with the architecture's registers. - if (!rz_analysis_plugin_current(core->analysis)->il_config) { - RZ_LOG_ERROR("The RzArch plugin doesn't have il_config() implemented.\n"); - return_code = false; - goto error_free; + // Push all root entries + size_t entries_pushed = 0; + RzIterator *it = rz_set_u_as_iter(entry_points); + ut64 *entry; + rz_iterator_foreach (it, entry) { + ut64 *tmp = RZ_NEW(ut64); + if (!tmp) { + goto err_main_ch; + } + *tmp = *entry; + if (!rz_th_queue_push(driver.entry_points_ch, tmp, true)) { + goto err_main_ch; + } + entries_pushed++; } - // Bundle all the queues into one object to pass it to the thread. - // Later we would pass a unique iset to each interpreter with - // the required queues only. - // But for the prototype we have only one iset with all queues. size_t n_threads = 1; - interp_map = RZ_NEWS0(struct ituple, n_threads); + InterpThread **threads = RZ_NEWS0(InterpThread *, n_threads); + if (!threads) { + goto err_main_ch; + } - // // Initialize and spawn the interpreters. - // for (size_t i = 0; i < n_threads; ++i) { RzILCacheClient *cache_client = rz_il_cache_new_client(il_cache, true); if (!cache_client) { return_code = false; rz_warn_if_reached(); - goto error_free; + goto err_threads; } RzInterpConfig config = { .val_domain = &rz_interp_value_domain_const, @@ -356,195 +250,71 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { if (!inst) { return_code = false; rz_warn_if_reached(); - goto error_free; + goto err_threads; } RZ_LOG_DEBUG("inquiry: Start main interpretation thread.\n"); - interp_map[i].th = interp_thread_new(inst); - if (!interp_map[i].th) { + threads[i] = interp_thread_new(&driver, inst); + if (!threads[i]) { rz_interp_instance_free(inst); - return_code = false; - goto error_free; + goto err_threads; } - interp_map[i].next_run_state = RZ_INTERP_RUN_STATE_INIT; } - // - // Spawn the IL cache. - // - RZ_LOG_DEBUG("inquiry: Spawn IL Cache"); - il_cache_th = rz_th_new((RzThreadFunction)rz_il_cache_serve, il_cache); + // TODO: rz_cons_break_push - ut64 interp_terminated = 0; - ut64 check_signal = 0; - - // - // Enter the loop to serve the interpreters. - // - for (ut64 i = 0;; check_signal++, i = (i + 1) % n_threads) { - if (check_signal % RZ_INQUIRY_CHECK_USER_SIGNAL_ITC == 0 && rz_cons_is_breaked()) { - user_sent_signal = true; + // Serve the interpreters + size_t entries_finished = 0; + while (entries_finished < entries_pushed) { + InterpDriverMessage msg; + if (rz_th_ring_buf_take_blocking(driver.main_ch, &msg) != RZ_THREAD_RING_BUF_OK) { + // closed break; } - RzInterpInstance *inst = interp_map[i].th->inst; - RzInterpRunStateFlag expected_rs = interp_map[i].next_run_state; - - switch (rz_interp_run_state_get_unsafe(interp_map[i].th->run_state)) { - case RZ_INTERP_RUN_STATE_OUT_OF_LOOP: - break; - case RZ_INTERP_RUN_STATE_INIT: { - if (expected_rs != RZ_INTERP_RUN_STATE_INIT) { - break; - } - if (!rz_set_u_size(entry_points)) { - rz_th_queue_close(inst->config.il_cache_client->il_queue); - interp_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; - interp_terminated++; - continue; - } - ut64 next_entry_point = rz_set_u_take(entry_points); - switch (rz_th_ring_buf_put(interp_map[i].th->entry_points, &next_entry_point)) { - case RZ_THREAD_RING_BUF_OK: - // Successfully lifted and pushed the entry point's basic block into the queue. - // Expect the interpreter to emulate now. - interp_map[i].next_run_state = RZ_INTERP_RUN_STATE_EMU; - // RZ_LOG_DEBUG("Next: EMU\n"); - break; - case RZ_THREAD_RING_BUF_FAIL: - // The entry point buffer is full. - // Insert the entry point to the todo set again. - rz_set_u_add(entry_points, next_entry_point); - break; - case RZ_THREAD_RING_BUF_CLOSED: - rz_warn_if_reached(); - // Something went pretty wrong. - interp_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; - interp_terminated++; - // RZ_LOG_DEBUG("Next: TERM\n"); - continue; - } - break; - } - case RZ_INTERP_RUN_STATE_EMU: { - if (expected_rs != RZ_INTERP_RUN_STATE_EMU) { - break; - } - // From here on, the code plays the role of the IO handler, - // and yield consumer. - // - Handling IO requests. - // - Receiving and adding the found xrefs to RzAnalysis. - // In the final implementation each of those roles would be split into - // two or more separated modules running in parallel. - - // ========== - // IO HANDLER - // ========== - // - // This plays the IO handler for a single(!) interpreter instances. - // In the future we should have only one IO handler for multiple interpreters. - // But this requires multiple IO write caches - // (one for each interpreter instance). - // Because this is not yet implemented, there is only one interpreter thread for now. - RzInterpIOReadRequest io_req = { 0 }; - if (!rz_th_ring_buf_is_empty_unsafe(interp_map[i].th->io_request_rbuf)) { - RzThreadRingBufResult r = rz_th_ring_buf_take(interp_map[i].th->io_request_rbuf, &io_req); - if (r == RZ_THREAD_RING_BUF_CLOSED) { - rz_warn_if_reached(); - goto fatal_error; - } else if (r == RZ_THREAD_RING_BUF_OK) { - RzInterpIOResult io_res = { 0 }; - handle_io_request(inst->il_ctx, &io_req, &io_res); - if (rz_th_ring_buf_put(interp_map[i].th->io_result_rbuf, &io_res) != RZ_THREAD_RING_BUF_OK) { - rz_warn_if_reached(); - goto fatal_error; - } + switch (msg.type) { + case DRIVER_MESSAGE_IO_READ: { + RzInterpIOReadResult res = handle_io_request(msg.payload.io_read.requester->inst->il_ctx, msg.payload.io_read.request); + InterpThreadMessage res_msg = { + .type = INTERP_MESSAGE_IO_READ_RESULT, + .payload = { + .io_read_result = res } - // Else r == RZ_THREAD_RING_BUF_FAIL - // Due to a race condition the ring buffer was actually empty. - } - break; - } - case RZ_INTERP_RUN_STATE_CLEAN: { - if (!((expected_rs == RZ_INTERP_RUN_STATE_CLEAN || expected_rs == RZ_INTERP_RUN_STATE_EMU))) { - break; + }; + if (rz_th_ring_buf_put(msg.payload.io_read.requester->ch, &res_msg) != RZ_THREAD_RING_BUF_OK) { + // should not be closed + rz_warn_if_reached(); } - close_reset_ipc_obj(interp_map[0].th); // TODO - open_ipc_obj(interp_map[0].th); // TODO - rz_th_sem_post(interp_map[0].th->run_state_sync); - interp_map[i].next_run_state = RZ_INTERP_RUN_STATE_INIT; - // RZ_LOG_DEBUG("Next: INIT\n"); break; } - case RZ_INTERP_RUN_STATE_TERM: { - if (expected_rs != RZ_INTERP_RUN_STATE_TERM) { - interp_map[i].next_run_state = RZ_INTERP_RUN_STATE_TERM; - interp_terminated++; - } - // RZ_LOG_DEBUG("Next: TERM\n"); + case DRIVER_MESSAGE_INTERP_RESULT: { + RzInterpResult *res = msg.payload.interp_result; + rz_interp_result_apply_to_analysis(res, core->analysis); + entries_finished++; break; } - } - if (interp_terminated == n_threads) { + default: + rz_warn_if_reached(); break; } } -fatal_error: - - RZ_LOG_DEBUG("inquiry: Wait for join\n"); - for (size_t i = 0; i < n_threads; i++) { - close_reset_ipc_obj(interp_map[i].th); - // Open semaphore so the interpreter can transition - // EMU -> CLEAN -> INIT -> TERM - rz_th_sem_post(interp_map[i].th->run_state_sync); - } - - // Wait for thread to finish before cleaning. - for (size_t i = 0; i < n_threads; i++) { - rz_th_wait(interp_map[i].th->th); - bool interpr_ret = rz_th_get_retv(interp_map[i].th->th); - rz_th_free(interp_map[i].th->th); - rz_th_ring_buf_free(interp_map[i].th->io_request_rbuf); - rz_th_ring_buf_free(interp_map[i].th->io_result_rbuf); - rz_th_ring_buf_free(interp_map[i].th->entry_points); - rz_th_sem_free(interp_map[i].th->run_state_sync); - rz_interp_run_state_free(interp_map[i].th->run_state); - if (!interpr_ret || user_sent_signal) { - return_code = false; - if (!user_sent_signal) { - RZ_LOG_ERROR("Interpreter failed with an error. Abort.\n"); - } else { - RZ_LOG_ERROR("User sent signal.\n"); - } - } - } - rz_il_cache_stop_serving(il_cache); - - if (rz_log_get_level() > RZ_LOGLVL_INFO && rz_cons_is_interactive()) { - eprintf("\n"); - } + return_code = true; +err_threads: + // Close channel to make interp threads stop. + rz_th_queue_close(driver.entry_points_ch); for (size_t i = 0; i < n_threads; i++) { - // Apply results - void **it; - rz_pvector_foreach (&interp_map[i].th->inst->results, it) { - RzInterpResult *res = *it; - rz_interp_result_apply_to_analysis(res, core->analysis); - } - - rz_interp_instance_free(interp_map[i].th->inst); + interp_thread_free(threads[i]); } - - RZ_LOG_DEBUG("inquiry: inquiry: inquiry: Done\n"); - -error_free: - free(interp_map); - rz_set_u_free(entry_points); + free(threads); +err_main_ch: + rz_th_ring_buf_free(driver.main_ch); +err_entry_points_ch: + rz_th_queue_free(driver.entry_points_ch); +err_il_cache_th: rz_il_cache_stop_serving(il_cache); - if (il_cache_th) { - rz_th_wait(il_cache_th); - rz_th_free(il_cache_th); - } + rz_th_wait(il_cache_th); +err_il_cache: rz_il_cache_free(il_cache); - rz_cons_pop(); +err_none: return return_code && !user_sent_signal; } diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 4e51e8b0cdb..3dfcbc4fecd 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -563,8 +563,6 @@ RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new(RzAnalysis *analysis, RZ_ } } - rz_pvector_init(&inst->results, NULL); - return inst; err_var_name_hashes: ht_up_free(inst->var_name_hashes); @@ -736,8 +734,11 @@ bool load_abstr_data( io_req.mem_idx = mem_idx; io_req.n_bits = n_bits; io_req.big_endian = inst->il_ctx->config->big_endian; - bool req_ok = inst->config.io_read(&io_req, inst->config.cb_user); - if (!req_ok) { + RzInterpIOReadResult read_res = inst->config.io_read(&io_req, inst->config.cb_user); + if (read_res == RZ_INTERP_IO_READ_RESULT_BREAK) { + // TODO: break + } + if (read_res != RZ_INTERP_IO_READ_RESULT_OK) { val_domain(inst)->set_top(out); return true; } diff --git a/test/unit/test_inquiry_interp.c b/test/unit/test_inquiry_interp.c index e9cd1c48c29..d9e844cd052 100644 --- a/test/unit/test_inquiry_interp.c +++ b/test/unit/test_inquiry_interp.c @@ -13,8 +13,8 @@ typedef struct test_interp_t { RzInterpInstance *inst; } TestInterp; -static bool io_read(RzInterpIOReadRequest *req, void *user) { - return false; // Currently we do not care about contents, just make the interpreter continue +static RzInterpIOReadResult io_read(RzInterpIOReadRequest *req, void *user) { + return RZ_INTERP_IO_READ_RESULT_TOP; // Currently we do not care about contents, just make the interpreter continue } static TestInterp *interp_new(const char *arch, int bits, ut64 baddr, const char *url) { From d77d961601e6bc082f724ad708ffb5d41cf9ad42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sun, 19 Jul 2026 16:07:51 +0200 Subject: [PATCH 330/334] Refactor il client access out of interp --- librz/include/rz_inquiry/rz_interpreter.h | 4 +- librz/inquiry/interp/driver.c | 88 +++++++++++------------ librz/inquiry/interp/interpreter.c | 10 ++- test/unit/test_inquiry_interp.c | 11 ++- 4 files changed, 60 insertions(+), 53 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index cf10ce23869..282c7a56eb6 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -162,11 +162,9 @@ typedef enum rz_interp_io_read_result_t { typedef struct rz_interp_config_t { RzInterpValueAbstraction *val_domain; - RZ_LIFETIME(RzILCache) - RZ_BORROW RzILCacheClient *il_cache_client; - void *cb_user; RzInterpIOReadResult (*io_read)(RZ_NONNULL RZ_OWN RzInterpIOReadRequest *req, void *user); + const RzILCacheBlock *(*lift_block)(ut64 addr, void *user); } RzInterpConfig; /** diff --git a/librz/inquiry/interp/driver.c b/librz/inquiry/interp/driver.c index e9a9a67aea5..b4effb6e249 100644 --- a/librz/inquiry/interp/driver.c +++ b/librz/inquiry/interp/driver.c @@ -63,6 +63,7 @@ typedef struct interp_thread_message_t { struct interp_thread { InterpDriver *driver; + RzILCacheClient *il_cache_client; RzThread *th; RzInterpInstance *inst; @@ -100,26 +101,61 @@ static void *interp_th(void *user) { return NULL; } -static InterpThread *interp_thread_new(InterpDriver *driver, RZ_OWN RzInterpInstance *inst) { +static RzInterpIOReadResult send_io_read(RZ_NONNULL RzInterpIOReadRequest *req, void *user) { + InterpThread *th = user; + InterpDriverMessage msg = { + .type = DRIVER_MESSAGE_IO_READ, + .payload = { + .io_read = { + .request = req, + .requester = th + } + } + }; + if (rz_th_ring_buf_put(th->driver->main_ch, &msg) != RZ_THREAD_RING_BUF_OK) { + return RZ_INTERP_IO_READ_RESULT_BREAK; + } + InterpThreadMessage ret; + if (rz_th_ring_buf_take_blocking(th->ch, &ret) != RZ_THREAD_RING_BUF_OK) { + return RZ_INTERP_IO_READ_RESULT_BREAK; + } + rz_return_val_if_fail(ret.type != INTERP_MESSAGE_IO_READ_RESULT, RZ_INTERP_IO_READ_RESULT_BREAK); + return ret.payload.io_read_result; +} + +static const RzILCacheBlock *send_lift_il_block(ut64 addr, void *user) { + InterpThread *th = user; + return rz_il_cache_client_lift_il_block(th->il_cache_client, addr); +} + +static InterpThread *interp_thread_new(RzAnalysis *analysis, InterpDriver *driver, RZ_OWN RzILCacheClient *il_cache_client) { InterpThread *ctx = RZ_NEW(InterpThread); if (!ctx) { return NULL; } ctx->driver = driver; - ctx->inst = inst; + ctx->il_cache_client = il_cache_client; ctx->ch = rz_th_ring_buf_new(1, sizeof(InterpThreadMessage)); // At the moment, threads directly wait after a single request, so size 1 is enough if (!ctx->ch) { goto err_ctx; } - - inst->config.cb_user = ctx; // TODO: remove this hack - + RzInterpConfig interp_config = { + .val_domain = &rz_interp_value_domain_const, + .cb_user = ctx, + .io_read = send_io_read, + .lift_block = send_lift_il_block + }; + ctx->inst = rz_interp_instance_new(analysis, &interp_config); + if (!ctx->inst) { + goto err_ch; + } ctx->th = rz_th_new(interp_th, ctx); if (!ctx->th) { - goto err_ch; + goto err_inst; } - return ctx; +err_inst: + rz_interp_instance_free(ctx->inst); err_ch: rz_th_ring_buf_free(ctx->ch); err_ctx: @@ -138,28 +174,6 @@ static void interp_thread_free(InterpThread *th) { free(th); } -static RzInterpIOReadResult send_io_read(RZ_NONNULL RzInterpIOReadRequest *req, void *user) { - InterpThread *th = user; - InterpDriverMessage msg = { - .type = DRIVER_MESSAGE_IO_READ, - .payload = { - .io_read = { - .request = req, - .requester = th - } - } - }; - if (rz_th_ring_buf_put(th->driver->main_ch, &msg) != RZ_THREAD_RING_BUF_OK) { - return RZ_INTERP_IO_READ_RESULT_BREAK; - } - InterpThreadMessage ret; - if (rz_th_ring_buf_take_blocking(th->ch, &ret) != RZ_THREAD_RING_BUF_OK) { - return RZ_INTERP_IO_READ_RESULT_BREAK; - } - rz_return_val_if_fail(ret.type != INTERP_MESSAGE_IO_READ_RESULT, RZ_INTERP_IO_READ_RESULT_BREAK); - return ret.payload.io_read_result; -} - static RzInterpIOReadResult handle_io_request(const RzAnalysisILContext *il_ctx, RzInterpIOReadRequest *io_req) { RZ_LOG_DEBUG("inquiry: Received IO read request: mem:%" PFMTSZd " 0x%" PFMT64x "\n", io_req->mem_idx, @@ -240,22 +254,8 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { rz_warn_if_reached(); goto err_threads; } - RzInterpConfig config = { - .val_domain = &rz_interp_value_domain_const, - .il_cache_client = cache_client, - .cb_user = NULL, // TODO, injected by hack - .io_read = send_io_read - }; - RzInterpInstance *inst = rz_interp_instance_new(core->analysis, &config); - if (!inst) { - return_code = false; - rz_warn_if_reached(); - goto err_threads; - } - RZ_LOG_DEBUG("inquiry: Start main interpretation thread.\n"); - threads[i] = interp_thread_new(&driver, inst); + threads[i] = interp_thread_new(core->analysis, &driver, cache_client); if (!threads[i]) { - rz_interp_instance_free(inst); goto err_threads; } } diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 3dfcbc4fecd..a316c431394 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -527,7 +527,7 @@ static RzInterpBlock *rz_interp_run_pop(RZ_BORROW RZ_NONNULL RzInterpRunContext ///////////////////////////////////////////////////////// RZ_API RZ_OWN RzInterpInstance *rz_interp_instance_new(RzAnalysis *analysis, RZ_NONNULL RZ_BORROW const RzInterpConfig *config) { - rz_return_val_if_fail(analysis && config && config->val_domain && config->il_cache_client && config->io_read, NULL); + rz_return_val_if_fail(analysis && config && config->val_domain && config->io_read && config->lift_block, NULL); RzInterpInstance *inst = RZ_NEW0(RzInterpInstance); if (!inst) { @@ -1352,6 +1352,10 @@ RZ_API void rz_interp_run_context_fini(RzInterpRunContext *ctx) { interp_blocks_free(ctx); } +static const RzILCacheBlock *interp_lift_block(RzInterpInstance *inst, ut64 addr) { + return inst->config.lift_block(addr, inst->config.cb_user); +} + /** * \brief Run the interpreter from a single entrypoint until a fixpoint is reached */ @@ -1386,7 +1390,7 @@ RZ_API RzInterpResult *rz_interp_run(RzInterpInstance *inst, ut64 entry_point, R } ctx.astate = rz_interp_abstr_state_clone(inst, interp_block->entry_state); - const RzILCacheBlock *il_block = rz_il_cache_client_lift_il_block(inst->config.il_cache_client, ctx.astate->pc); + const RzILCacheBlock *il_block = interp_lift_block(inst, ctx.astate->pc); if (!il_block) { RZ_LOG_ERROR("interpreter: Lifting failed\n"); // TODO: handle this better @@ -1430,7 +1434,7 @@ RZ_API RzInterpResult *rz_interp_run(RzInterpInstance *inst, ut64 entry_point, R RzInterpBlock *interp_block; rz_interval_tree_foreach (&ctx.blocks, it, interp_block) { ctx.astate = rz_interp_abstr_state_clone(inst, interp_block->entry_state); - const RzILCacheBlock *il_block = rz_il_cache_client_lift_il_block(inst->config.il_cache_client, ctx.astate->pc); + const RzILCacheBlock *il_block = interp_lift_block(inst, ctx.astate->pc); if (!il_block) { RZ_LOG_ERROR("interpreter: Lifting failed\n"); // TODO: handle this better diff --git a/test/unit/test_inquiry_interp.c b/test/unit/test_inquiry_interp.c index d9e844cd052..2d4ad5d64d5 100644 --- a/test/unit/test_inquiry_interp.c +++ b/test/unit/test_inquiry_interp.c @@ -17,6 +17,11 @@ static RzInterpIOReadResult io_read(RzInterpIOReadRequest *req, void *user) { return RZ_INTERP_IO_READ_RESULT_TOP; // Currently we do not care about contents, just make the interpreter continue } +static const RzILCacheBlock *lift_block(ut64 addr, void *user) { + TestInterp *interp = user; + return rz_il_cache_client_lift_il_block(interp->il_cache_client, addr); +} + static TestInterp *interp_new(const char *arch, int bits, ut64 baddr, const char *url) { // for debugging, uncomment: // eprintf("rz -a %s -b %d -m 0x%" PFMT64x " %s\n", arch, bits, baddr, url); @@ -30,9 +35,9 @@ static TestInterp *interp_new(const char *arch, int bits, ut64 baddr, const char interp->il_cache_client = rz_il_cache_new_client(interp->il_cache, false); RzInterpConfig config = { .val_domain = &rz_interp_value_domain_const, - .il_cache_client = interp->il_cache_client, - .cb_user = NULL, - .io_read = io_read + .cb_user = interp, + .io_read = io_read, + .lift_block = lift_block }; interp->inst = rz_interp_instance_new(interp->analysis, &config); RzIODesc *desc = rz_io_open_at(interp->io, url, RZ_PERM_RX, 0644, baddr, NULL); From 1810b658db538ffd80415ddbb295fbc8d9c6e218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Mon, 20 Jul 2026 08:19:02 +0200 Subject: [PATCH 331/334] Move lifting to main loop and strip il cache from everything unused then --- librz/include/rz_inquiry/rz_il_cache.h | 52 +--- librz/include/rz_inquiry/rz_interpreter.h | 8 +- librz/inquiry/il_cache.c | 324 +--------------------- librz/inquiry/interp/driver.c | 72 +++-- librz/inquiry/interp/interpreter.c | 5 +- test/unit/test_inquiry_interp.c | 19 +- 6 files changed, 79 insertions(+), 401 deletions(-) diff --git a/librz/include/rz_inquiry/rz_il_cache.h b/librz/include/rz_inquiry/rz_il_cache.h index bcc67b13f16..85430e8f335 100644 --- a/librz/include/rz_inquiry/rz_il_cache.h +++ b/librz/include/rz_inquiry/rz_il_cache.h @@ -4,17 +4,10 @@ #ifndef RZ_INQUIRY_IL_CACHE_H #define RZ_INQUIRY_IL_CACHE_H -#include "rz_th.h" #include "rz_types.h" #include #include -/** - * \brief This value is pushed over the IL ops queue if the requested - * IL op failed to be lifted. - */ -#define RZ_IL_CACHE_FAILED_LIFTING_PTR ((void *)(utptr)UT64_MAX) - typedef struct rz_il_cache_t RzILCache; typedef struct { @@ -38,30 +31,7 @@ typedef enum { * \brief The IL cache will replace un-lifted instructions * with a NOP. */ - RZ_IL_CACHE_CONFIG_NOP_UNLIFTED = 1 << 0, - /** - * \brief The IL cache will lift instructions only on request. - * If unset, it will lift all instructions in all executable maps - * on initialization. - */ - RZ_IL_CACHE_CONFIG_LIFT_ON_REQUEST = 1 << 1, - - // TODO: Sleep times are not really a useful setting. - // They are just here for experiments. - - /** - * \brief If set, the cache doesn't sleep in its serve loop. - * It continuously checks for requests. - */ - RZ_IL_CACHE_CONFIG_NO_SLEEP = 1 << 2, - /** - * \brief If set, the cache sleeps a short time between checks for requests. - */ - RZ_IL_CACHE_CONFIG_SLEEP_SHORT = 1 << 3, - /** - * \brief If set, the cache sleeps a relatively long time between checks for requests. - */ - RZ_IL_CACHE_CONFIG_SLEEP_LONG = RZ_IL_CACHE_CONFIG_NO_SLEEP | RZ_IL_CACHE_CONFIG_SLEEP_SHORT, + RZ_IL_CACHE_CONFIG_NOP_UNLIFTED = 1 << 0 } RzILCacheConfig; RZ_API RZ_OWN char *rz_il_cache_block_str(RZ_NONNULL const RzILCacheBlock *block); @@ -69,28 +39,10 @@ RZ_API RZ_OWN char *rz_il_cache_block_str(RZ_NONNULL const RzILCacheBlock *block RZ_API RZ_OWN RzILCache *rz_il_cache_new( RZ_BORROW RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, - RZ_OWN RzPVector /**/ *bin_sections, RzILCacheConfig config); RZ_API void rz_il_cache_free(RZ_OWN RZ_NULLABLE RzILCache *cache); -RZ_API void rz_il_cache_close(RZ_BORROW RZ_NONNULL RzILCache *cache); -RZ_API void rz_il_cache_stop_serving(RZ_BORROW RZ_NONNULL RzILCache *cache); -RZ_API const RzVector /**/ *rz_il_cache_get_static_xrefs(const RzILCache *cache); -RZ_API RzIterator /**/ *rz_il_cache_get_blocks(const RzILCache *cache); - -typedef struct rz_il_cache_client_t { - RzILCache *cache; - RzThreadRingBuf *req_rbuf; ///< used only if async - RzThreadQueue *il_queue; ///< used only if async - bool async; -} RzILCacheClient; - -RZ_API RZ_BORROW RzILCacheClient *rz_il_cache_new_client(RZ_NONNULL RZ_BORROW RzILCache *cache, bool async); -RZ_API RZ_NULLABLE RZ_BORROW const RzILCacheBlock *rz_il_cache_client_lift_il_block(RZ_NONNULL RZ_BORROW RzILCacheClient *client, ut64 addr); -RZ_API bool rz_il_cache_was_requested( - RZ_BORROW RzILCache *cache, - ut64 addr); RZ_API bool rz_il_cache_serve(RZ_NONNULL RzILCache *cache); -RZ_API RZ_OWN RzILCacheBlock *rz_il_cache_lift_il_block(const RzILCache *cache, ut64 addr); +RZ_API RZ_OWN const RzILCacheBlock *rz_il_cache_lift_il_block(RzILCache *cache, ut64 addr); #endif // RZ_INQUIRY_IL_CACHE_H diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 282c7a56eb6..ad9107705cb 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -159,12 +159,18 @@ typedef enum rz_interp_io_read_result_t { RZ_INTERP_IO_READ_RESULT_BREAK ///< interpreter is signaled to stop } RzInterpIOReadResult; +typedef enum rz_interp_lift_block_result_t { + RZ_INTERP_LIFT_BLOCK_RESULT_OK, ///< block_out is filled + RZ_INTERP_LIFT_BLOCK_RESULT_FAILED, + RZ_INTERP_LIFT_BLOCK_RESULT_BREAK ///< interpreter is signaled to stop +} RzInterpLiftBlockResult; + typedef struct rz_interp_config_t { RzInterpValueAbstraction *val_domain; void *cb_user; RzInterpIOReadResult (*io_read)(RZ_NONNULL RZ_OWN RzInterpIOReadRequest *req, void *user); - const RzILCacheBlock *(*lift_block)(ut64 addr, void *user); + RzInterpLiftBlockResult (*lift_block)(ut64 addr, RZ_OUT const RzILCacheBlock **block_out, void *user); } RzInterpConfig; /** diff --git a/librz/inquiry/il_cache.c b/librz/inquiry/il_cache.c index cef55ed70e0..978810920a5 100644 --- a/librz/inquiry/il_cache.c +++ b/librz/inquiry/il_cache.c @@ -3,24 +3,14 @@ #include "rz_analysis.h" #include "rz_il/rz_il_opcodes.h" -#include "rz_skyline.h" -#include "rz_th.h" #include "rz_types.h" #include "rz_util/ht_up.h" #include "rz_util/rz_assert.h" #include "rz_util/rz_log.h" #include "rz_util/rz_str.h" -#include "rz_util/rz_sys.h" #include "rz_vector.h" #include -#define RZ_IL_OPS_CACHE_IL_QUEUE_SIZE 128 -#define RZ_IL_OPS_CACHE_ADDR_RBUF_SIZE 1024 - -#define SLEEP_NONE_US 0 -#define SLEEP_SHORT_US (5 * 1000) -#define SLEEP_LONG_US (100 * 1000) - RZ_API RZ_OWN char *rz_il_cache_block_str(RZ_NONNULL const RzILCacheBlock *block) { rz_return_val_if_fail(block, NULL); return rz_str_newf("[0x%" PFMT64x ", 0x%" PFMT64x ")", block->addr, block->addr + block->size); @@ -29,34 +19,8 @@ RZ_API RZ_OWN char *rz_il_cache_block_str(RZ_NONNULL const RzILCacheBlock *block struct rz_il_cache_t { RZ_BORROW RzAnalysis *analysis; RZ_BORROW RzIO *io; - - RZ_NULLABLE RzPVector /**/ *bin_sections; - - size_t n_serving; ///< Number of caches serving currently. - RzThreadLock *n_serving_lock; - - RzAtomicBool *is_serving; ///< Flag to signal cache to serve or not to serve IL ops. - - /** - * \brief Statically known control flow edges. - * Collected for each lifted IL block with a static control flow target. - */ - RzVector /**/ *static_xrefs; - - HtUP /**/ *cache; - - RzPVector /**/ clients; - - RzThreadLock *skyline_lock; - /** - * \brief A skyline (functionally the same as an r-tree) to log the code regions - * the cache served as IL blocks. - */ - RzSkyline *served_regions; - RzILCacheConfig config; ///< The cache configuration. - - size_t us_sleep; + HtUP /**/ *cache; }; static void rz_il_cache_insn_pkt_free(RZ_NULLABLE RZ_OWN RzILCacheInsnPkt *pkt) { @@ -77,46 +41,7 @@ static void rz_il_cache_block_free(RZ_NULLABLE RZ_OWN RzILCacheBlock *il_bb) { free(il_bb); } -static RzAnalysisXRefType op_type_to_xref_type(const RzAnalysisOp *op) { - switch (op->type & RZ_ANALYSIS_OP_TYPE_MASK) { - case RZ_ANALYSIS_OP_TYPE_JMP: - case RZ_ANALYSIS_OP_TYPE_UJMP: - case RZ_ANALYSIS_OP_TYPE_RJMP: - case RZ_ANALYSIS_OP_TYPE_IJMP: - case RZ_ANALYSIS_OP_TYPE_IRJMP: - case RZ_ANALYSIS_OP_TYPE_CJMP: - case RZ_ANALYSIS_OP_TYPE_RCJMP: - case RZ_ANALYSIS_OP_TYPE_MJMP: - case RZ_ANALYSIS_OP_TYPE_MCJMP: - case RZ_ANALYSIS_OP_TYPE_UCJMP: - return RZ_ANALYSIS_XREF_TYPE_CODE; - case RZ_ANALYSIS_OP_TYPE_CALL: - case RZ_ANALYSIS_OP_TYPE_UCALL: - case RZ_ANALYSIS_OP_TYPE_RCALL: - case RZ_ANALYSIS_OP_TYPE_ICALL: - case RZ_ANALYSIS_OP_TYPE_IRCALL: - case RZ_ANALYSIS_OP_TYPE_CCALL: - case RZ_ANALYSIS_OP_TYPE_UCCALL: - return RZ_ANALYSIS_XREF_TYPE_CALL; - case RZ_ANALYSIS_OP_TYPE_RET: - case RZ_ANALYSIS_OP_TYPE_CRET: - return RZ_ANALYSIS_XREF_TYPE_RETURN; - case RZ_ANALYSIS_OP_TYPE_ILL: - case RZ_ANALYSIS_OP_TYPE_UNK: - case RZ_ANALYSIS_OP_TYPE_TRAP: - case RZ_ANALYSIS_OP_TYPE_SWI: - case RZ_ANALYSIS_OP_TYPE_CSWI: - case RZ_ANALYSIS_OP_TYPE_LEAVE: - case RZ_ANALYSIS_OP_TYPE_SWITCH: - default: - break; - } - rz_warn_if_reached(); - return RZ_ANALYSIS_XREF_TYPE_NULL; -} - -RZ_API RZ_OWN RzILCacheBlock *rz_il_cache_lift_il_block(const RzILCache *cache, ut64 addr) { - rz_return_val_if_fail(cache && rz_analysis_plugin_current(cache->analysis) && cache->io, NULL); +RZ_OWN RzILCacheBlock *lift_il_block(const RzILCache *cache, ut64 addr) { RzILCacheBlock *il_block = NULL; RzAnalysisOp op = { 0 }; rz_analysis_op_init(&op); @@ -174,27 +99,6 @@ RZ_API RZ_OWN RzILCacheBlock *rz_il_cache_lift_il_block(const RzILCache *cache, changes_cf = false; } - if (changes_cf && op.jump != UT64_MAX && cache->static_xrefs) { - // The op.jump/op.fail says nothing about the - // type of the control flow. - // This one has to be deduced by the op type. - // Really annoying and imprecise, but so be it. - // The op.fail is to my knowledge always a "normal" control flow - // as in "go to next instruction if branch condition fails". - RzAnalysisXRef xref = { - .bb_addr = il_block->addr, - .from = op.addr, - .to = op.jump, - .type = op_type_to_xref_type(&op) - }; - rz_vector_push(cache->static_xrefs, &xref); - if (op.fail != UT64_MAX) { - xref.to = op.fail; - xref.type = RZ_ANALYSIS_XREF_TYPE_CODE; - rz_vector_push(cache->static_xrefs, &xref); - } - } - addr += op.size; rz_analysis_op_fini(&op); rz_mem_memzero(buf, max_read_size); @@ -220,14 +124,15 @@ RZ_API RZ_OWN RzILCacheBlock *rz_il_cache_lift_il_block(const RzILCache *cache, return NULL; } -static const RzILCacheBlock *lift_il_block(RzILCache *cache, ut64 addr) { +RZ_API const RzILCacheBlock *rz_il_cache_lift_il_block(RzILCache *cache, ut64 addr) { + rz_return_val_if_fail(cache && rz_analysis_plugin_current(cache->analysis) && cache->io, NULL); RzILCacheBlock *block = ht_up_find(cache->cache, addr, NULL); if (block) { char *bstr = rz_il_cache_block_str(block); free(bstr); return block; } - block = rz_il_cache_lift_il_block(cache, addr); + block = lift_il_block(cache, addr); if (!block) { RZ_LOG_DEBUG("ILCache: Failed to lift block at 0x%" PFMT64x "\n", addr); return NULL; @@ -236,148 +141,17 @@ static const RzILCacheBlock *lift_il_block(RzILCache *cache, ut64 addr) { return block; } -static bool lift_executable_maps(RzILCache *cache) { - void **it; - rz_pvector_foreach (cache->bin_sections, it) { - RzBinSection *sec = *it; - if (!(sec->perm & RZ_PERM_X)) { - continue; - } - ut64 addr = sec->vaddr; - while (addr < sec->vaddr + sec->vsize) { - const RzILCacheBlock *block = lift_il_block(cache, addr); - if (!block) { - RZ_LOG_INFO("Failed to lift IL block at 0x%" PFMT64x - " - Stop lifting section '%s'\n", - addr, sec->name); - break; - } - addr += block->size; - } - } - return true; -} - -RZ_API bool rz_il_cache_serve(RZ_NONNULL RzILCache *cache) { - rz_return_val_if_fail(cache, false); - rz_th_lock_enter(cache->n_serving_lock); - cache->n_serving++; - rz_th_lock_leave(cache->n_serving_lock); - - bool success = true; - - rz_atomic_bool_set(cache->is_serving, true); - size_t clients = rz_pvector_len(&cache->clients); - while (rz_atomic_bool_get(cache->is_serving)) { - for (size_t i = 0; i < clients; ++i) { - RzILCacheClient *client = rz_pvector_at(&cache->clients, i); - - // TODO: This unsafe check permits race conditions. - // Not sure if it improve performance here. - // Needs more benchmarks. - if (rz_th_ring_buf_is_empty_unsafe(client->req_rbuf)) { - continue; - } - - ut64 req_addr = 0; - RzThreadRingBufResult r = rz_th_ring_buf_take(client->req_rbuf, &req_addr); - if (r == RZ_THREAD_RING_BUF_CLOSED) { - goto stop_serving; - } else if (r == RZ_THREAD_RING_BUF_OK) { - const RzILCacheBlock *block = lift_il_block(cache, req_addr); - if (block) { - rz_th_queue_push(client->il_queue, (void *)block, true); - } else { - rz_th_queue_push(client->il_queue, RZ_IL_CACHE_FAILED_LIFTING_PTR, true); - RZ_LOG_DEBUG("Failed to lift IL block at 0x%" PFMT64x "\n", req_addr); - } - } - } - rz_sys_usleep(cache->us_sleep); - } - -stop_serving: - rz_th_lock_enter(cache->n_serving_lock); - cache->n_serving--; - rz_th_lock_leave(cache->n_serving_lock); - return success; -} - -/** - * \brief Close all IL Cache owned ring buffers. - */ -RZ_API void rz_il_cache_close(RZ_BORROW RZ_NONNULL RzILCache *cache) { - rz_return_if_fail(cache); - - rz_atomic_bool_set(cache->is_serving, false); - - for (size_t i = 0; i < rz_pvector_len(&cache->clients); ++i) { - RzILCacheClient *client = rz_pvector_at(&cache->clients, i); - if (client->async) { - rz_th_ring_buf_close(client->req_rbuf); - rz_th_queue_close(client->il_queue); - } - } -} - -RZ_API void rz_il_cache_stop_serving(RZ_BORROW RZ_NONNULL RzILCache *cache) { - rz_return_if_fail(cache); - rz_atomic_bool_set(cache->is_serving, false); -} - -/** - * \brief Returns the static xrefs pointing from IL block to IL block. - */ -RZ_API const RzVector /**/ *rz_il_cache_get_static_xrefs(const RzILCache *cache) { - rz_return_val_if_fail(cache, NULL); - return cache->static_xrefs; -} - -RZ_API RZ_OWN RzIterator /**/ *rz_il_cache_get_blocks(const RzILCache *cache) { - rz_return_val_if_fail(cache, NULL); - return ht_up_as_iter(cache->cache); -} - RZ_API void rz_il_cache_free(RZ_OWN RZ_NULLABLE RzILCache *cache) { if (!cache) { return; } - - rz_il_cache_close(cache); - // Wait until all are left. - bool all_stopped = false; - do { - rz_th_lock_enter(cache->n_serving_lock); - all_stopped = cache->n_serving == 0; - rz_th_lock_leave(cache->n_serving_lock); - } while (!all_stopped); - - rz_pvector_fini(&cache->clients); - ht_up_free(cache->cache); - rz_vector_free(cache->static_xrefs); - rz_th_lock_free(cache->n_serving_lock); - rz_atomic_bool_free(cache->is_serving); - rz_pvector_free(cache->bin_sections); - rz_th_lock_free(cache->skyline_lock); - if (cache->served_regions) { - rz_skyline_fini(cache->served_regions); - free(cache->served_regions); - } free(cache); } -static void rz_il_cache_client_free(void *ptr) { - RzILCacheClient *client = ptr; - rz_th_ring_buf_free(client->req_rbuf); - rz_th_queue_free(client->il_queue); - free(client); -} - RZ_API RZ_OWN RzILCache *rz_il_cache_new( RZ_BORROW RZ_NONNULL RzAnalysis *analysis, RZ_BORROW RZ_NONNULL RzIO *io, - RZ_OWN RZ_NULLABLE RzPVector /**/ *bin_sections, RzILCacheConfig config) { rz_return_val_if_fail(analysis && io, NULL); RzILCache *cache = RZ_NEW0(RzILCache); @@ -387,39 +161,13 @@ RZ_API RZ_OWN RzILCache *rz_il_cache_new( } cache->analysis = analysis; cache->io = io; - cache->bin_sections = bin_sections; cache->cache = ht_up_new(NULL, (HtUPFreeValue)rz_il_cache_block_free); - cache->is_serving = rz_atomic_bool_new(false); - cache->n_serving_lock = rz_th_lock_new(false); - cache->static_xrefs = rz_vector_new(sizeof(RzAnalysisXRef), NULL, NULL); - if (!cache->cache || !cache->is_serving || !cache->static_xrefs) { - goto err; - } - - cache->skyline_lock = rz_th_lock_new(false); - cache->served_regions = RZ_NEW0(RzSkyline); - if (!cache->skyline_lock || !cache->served_regions) { + if (!cache->cache) { goto err; } - rz_skyline_init(cache->served_regions); - - rz_pvector_init(&cache->clients, rz_il_cache_client_free); cache->config = config; - cache->us_sleep = SLEEP_NONE_US; - - if ((cache->config & RZ_IL_CACHE_CONFIG_SLEEP_LONG) == RZ_IL_CACHE_CONFIG_SLEEP_LONG) { - cache->us_sleep = SLEEP_LONG_US; - } else if ((cache->config & RZ_IL_CACHE_CONFIG_SLEEP_LONG) == RZ_IL_CACHE_CONFIG_SLEEP_SHORT) { - cache->us_sleep = SLEEP_SHORT_US; - } - - if (!(cache->config & RZ_IL_CACHE_CONFIG_LIFT_ON_REQUEST) && - !lift_executable_maps(cache)) { - RZ_LOG_WARN("An error occurred when lifting the executable sections.\n"); - } - return cache; err: @@ -427,63 +175,3 @@ RZ_API RZ_OWN RzILCache *rz_il_cache_new( rz_il_cache_free(cache); return NULL; } - -/** - * \brief Check if the given address has been requested from the cache. - */ -RZ_API bool rz_il_cache_was_requested( - RZ_BORROW RzILCache *cache, - ut64 addr) { - rz_th_lock_enter(cache->skyline_lock); - bool r = rz_skyline_contains(cache->served_regions, addr); - rz_th_lock_leave(cache->skyline_lock); - return r; -} - -/** - * \brief Create a new client to access the IL cache - * \param async If false, the client will directly call into the IL cache, for single-threaded applications only. - * If true, the client requires another thread to run rz_il_cache_serve() when it requests data, for concurrent applications. - */ -RZ_API RZ_BORROW RzILCacheClient *rz_il_cache_new_client(RZ_NONNULL RZ_BORROW RzILCache *cache, bool async) { - rz_return_val_if_fail(cache, false); - RzILCacheClient *client = RZ_NEW0(RzILCacheClient); - if (!client) { - return NULL; - } - client->cache = cache; - if (async) { - client->async = true; - // The queue to pass the Effects to the interpreter. - client->il_queue = rz_th_queue_new(RZ_IL_OPS_CACHE_IL_QUEUE_SIZE, NULL); - // The ring buffer the interpreter can request new Effects over. - client->req_rbuf = rz_th_ring_buf_new(RZ_IL_OPS_CACHE_ADDR_RBUF_SIZE, sizeof(ut64)); - if (!client->il_queue || !client->req_rbuf) { - goto error_free; - } - } - rz_pvector_push(&cache->clients, client); - return client; -error_free: - rz_th_queue_free(client->il_queue); - rz_th_ring_buf_free(client->req_rbuf); - free(client); - return NULL; -} - -RZ_API RZ_NULLABLE RZ_BORROW const RzILCacheBlock *rz_il_cache_client_lift_il_block(RZ_NONNULL RZ_BORROW RzILCacheClient *client, ut64 addr) { - rz_return_val_if_fail(client, NULL); - if (!client->async) { - return lift_il_block(client->cache, addr); - } - if (rz_th_ring_buf_put(client->req_rbuf, &addr) != RZ_THREAD_RING_BUF_OK) { - // Can't request IL block => Cache closed => Terminate - return NULL; - } - const RzILCacheBlock *il_bb = NULL; - if (!rz_th_queue_pop(client->il_queue, false, (void **)&il_bb) || - il_bb == RZ_IL_CACHE_FAILED_LIFTING_PTR || !il_bb) { - return NULL; - } - return il_bb; -} diff --git a/librz/inquiry/interp/driver.c b/librz/inquiry/interp/driver.c index b4effb6e249..5cb80dc82e0 100644 --- a/librz/inquiry/interp/driver.c +++ b/librz/inquiry/interp/driver.c @@ -30,6 +30,7 @@ typedef struct interp_thread InterpThread; typedef struct interp_driver_message_t { enum { DRIVER_MESSAGE_IO_READ, + DRIVER_MESSAGE_LIFT_BLOCK, DRIVER_MESSAGE_INTERP_RESULT } type; union { @@ -37,6 +38,10 @@ typedef struct interp_driver_message_t { RzInterpIOReadRequest *request; InterpThread *requester; } io_read; + struct { + ut64 addr; + InterpThread *requester; + } lift_block; RzInterpResult *interp_result; } payload; } InterpDriverMessage; @@ -54,16 +59,17 @@ typedef struct interp_driver_t { */ typedef struct interp_thread_message_t { enum { - INTERP_MESSAGE_IO_READ_RESULT + INTERP_MESSAGE_IO_READ_RESULT, + INTERP_MESSAGE_LIFT_BLOCK_RESULT } type; union { RzInterpIOReadResult io_read_result; + const RzILCacheBlock *lift_block_result; } payload; } InterpThreadMessage; struct interp_thread { InterpDriver *driver; - RzILCacheClient *il_cache_client; RzThread *th; RzInterpInstance *inst; @@ -123,18 +129,38 @@ static RzInterpIOReadResult send_io_read(RZ_NONNULL RzInterpIOReadRequest *req, return ret.payload.io_read_result; } -static const RzILCacheBlock *send_lift_il_block(ut64 addr, void *user) { +static RzInterpLiftBlockResult send_lift_il_block(ut64 addr, const RzILCacheBlock **block_out, void *user) { InterpThread *th = user; - return rz_il_cache_client_lift_il_block(th->il_cache_client, addr); + InterpDriverMessage msg = { + .type = DRIVER_MESSAGE_LIFT_BLOCK, + .payload = { + .lift_block = { + .addr = addr, + .requester = th + } + } + }; + if (rz_th_ring_buf_put(th->driver->main_ch, &msg) != RZ_THREAD_RING_BUF_OK) { + return RZ_INTERP_LIFT_BLOCK_RESULT_BREAK; + } + InterpThreadMessage ret; + if (rz_th_ring_buf_take_blocking(th->ch, &ret) != RZ_THREAD_RING_BUF_OK) { + return RZ_INTERP_LIFT_BLOCK_RESULT_BREAK; + } + rz_return_val_if_fail(ret.type == INTERP_MESSAGE_LIFT_BLOCK_RESULT, RZ_INTERP_LIFT_BLOCK_RESULT_BREAK); + if (ret.payload.lift_block_result) { + *block_out = ret.payload.lift_block_result; + return RZ_INTERP_LIFT_BLOCK_RESULT_OK; + } + return RZ_INTERP_LIFT_BLOCK_RESULT_FAILED; } -static InterpThread *interp_thread_new(RzAnalysis *analysis, InterpDriver *driver, RZ_OWN RzILCacheClient *il_cache_client) { +static InterpThread *interp_thread_new(RzAnalysis *analysis, InterpDriver *driver) { InterpThread *ctx = RZ_NEW(InterpThread); if (!ctx) { return NULL; } ctx->driver = driver; - ctx->il_cache_client = il_cache_client; ctx->ch = rz_th_ring_buf_new(1, sizeof(InterpThreadMessage)); // At the moment, threads directly wait after a single request, so size 1 is enough if (!ctx->ch) { goto err_ctx; @@ -204,20 +230,15 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { bool user_sent_signal = false; - RzILCache *il_cache = rz_il_cache_new(core->analysis, core->io, rz_bin_object_get_sections(core->bin->cur->o), - RZ_IL_CACHE_CONFIG_NOP_UNLIFTED | RZ_IL_CACHE_CONFIG_NO_SLEEP); + RzILCache *il_cache = rz_il_cache_new(core->analysis, core->io, RZ_IL_CACHE_CONFIG_NOP_UNLIFTED); if (!il_cache) { goto err_none; } - RzThread *il_cache_th = rz_th_new((RzThreadFunction)rz_il_cache_serve, il_cache); - if (!il_cache_th) { - goto err_il_cache; - } InterpDriver driver; driver.entry_points_ch = rz_th_queue_new(RZ_THREAD_QUEUE_UNLIMITED, free); if (!driver.entry_points_ch) { - goto err_il_cache_th; + goto err_il_cache; } driver.main_ch = rz_th_ring_buf_new(DRIVER_MAIN_CH_SIZE, sizeof(InterpDriverMessage)); if (!driver.main_ch) { @@ -248,13 +269,7 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { // Initialize and spawn the interpreters. for (size_t i = 0; i < n_threads; ++i) { - RzILCacheClient *cache_client = rz_il_cache_new_client(il_cache, true); - if (!cache_client) { - return_code = false; - rz_warn_if_reached(); - goto err_threads; - } - threads[i] = interp_thread_new(core->analysis, &driver, cache_client); + threads[i] = interp_thread_new(core->analysis, &driver); if (!threads[i]) { goto err_threads; } @@ -285,6 +300,20 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { } break; } + case DRIVER_MESSAGE_LIFT_BLOCK: { + const RzILCacheBlock *block = rz_il_cache_lift_il_block(il_cache, msg.payload.lift_block.addr); + InterpThreadMessage res_msg = { + .type = INTERP_MESSAGE_LIFT_BLOCK_RESULT, + .payload = { + .lift_block_result = block + } + }; + if (rz_th_ring_buf_put(msg.payload.lift_block.requester->ch, &res_msg) != RZ_THREAD_RING_BUF_OK) { + // should not be closed + rz_warn_if_reached(); + } + break; + } case DRIVER_MESSAGE_INTERP_RESULT: { RzInterpResult *res = msg.payload.interp_result; rz_interp_result_apply_to_analysis(res, core->analysis); @@ -310,9 +339,6 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { rz_th_ring_buf_free(driver.main_ch); err_entry_points_ch: rz_th_queue_free(driver.entry_points_ch); -err_il_cache_th: - rz_il_cache_stop_serving(il_cache); - rz_th_wait(il_cache_th); err_il_cache: rz_il_cache_free(il_cache); err_none: diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index a316c431394..3931c85463b 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -1353,7 +1353,10 @@ RZ_API void rz_interp_run_context_fini(RzInterpRunContext *ctx) { } static const RzILCacheBlock *interp_lift_block(RzInterpInstance *inst, ut64 addr) { - return inst->config.lift_block(addr, inst->config.cb_user); + const RzILCacheBlock *block; + RzInterpLiftBlockResult res = inst->config.lift_block(addr, &block, inst->config.cb_user); + // TODO: handle break + return res == RZ_INTERP_LIFT_BLOCK_RESULT_OK ? block : NULL; } /** diff --git a/test/unit/test_inquiry_interp.c b/test/unit/test_inquiry_interp.c index 2d4ad5d64d5..80054e65767 100644 --- a/test/unit/test_inquiry_interp.c +++ b/test/unit/test_inquiry_interp.c @@ -9,7 +9,6 @@ typedef struct test_interp_t { RzAnalysis *analysis; RzIO *io; RzILCache *il_cache; - RzILCacheClient *il_cache_client; RzInterpInstance *inst; } TestInterp; @@ -17,9 +16,14 @@ static RzInterpIOReadResult io_read(RzInterpIOReadRequest *req, void *user) { return RZ_INTERP_IO_READ_RESULT_TOP; // Currently we do not care about contents, just make the interpreter continue } -static const RzILCacheBlock *lift_block(ut64 addr, void *user) { +static RzInterpLiftBlockResult lift_block(ut64 addr, const RzILCacheBlock **block_out, void *user) { TestInterp *interp = user; - return rz_il_cache_client_lift_il_block(interp->il_cache_client, addr); + const RzILCacheBlock *block = rz_il_cache_lift_il_block(interp->il_cache, addr); + if (block) { + *block_out = block; + return RZ_INTERP_LIFT_BLOCK_RESULT_OK; + } + return RZ_INTERP_LIFT_BLOCK_RESULT_FAILED; } static TestInterp *interp_new(const char *arch, int bits, ut64 baddr, const char *url) { @@ -31,8 +35,7 @@ static TestInterp *interp_new(const char *arch, int bits, ut64 baddr, const char rz_analysis_set_bits(interp->analysis, bits); interp->io = rz_io_new(); interp->io->va = 1; - interp->il_cache = rz_il_cache_new(interp->analysis, interp->io, NULL, RZ_IL_CACHE_CONFIG_NOP_UNLIFTED | RZ_IL_CACHE_CONFIG_NO_SLEEP); - interp->il_cache_client = rz_il_cache_new_client(interp->il_cache, false); + interp->il_cache = rz_il_cache_new(interp->analysis, interp->io, RZ_IL_CACHE_CONFIG_NOP_UNLIFTED); RzInterpConfig config = { .val_domain = &rz_interp_value_domain_const, .cb_user = interp, @@ -147,7 +150,7 @@ bool test_interp_block_resolve_bounds_single(void) { RzInterpBlock *block = rz_interp_block_at(&ctx, 0x10008); mu_assert_notnull(block, "block"); - RzILCacheBlock *il_block = rz_il_cache_lift_il_block(interp->il_cache, 0x10008); + const RzILCacheBlock *il_block = rz_il_cache_lift_il_block(interp->il_cache, 0x10008); rz_interp_block_resolve_bounds(&ctx, block, il_block); mu_assert_true(block->bounds_resolved, "bounds resolved"); mu_assert_eq(rz_interp_block_get_start(block), 0x10008, "block start"); @@ -206,7 +209,7 @@ bool test_interp_block_resolve_bounds_prepend(bool single_op_existing_block, boo RzInterpBlock *block = rz_interp_block_at(&ctx, prepended_block_start); mu_assert_notnull(block, "block"); - RzILCacheBlock *il_block = rz_il_cache_lift_il_block(interp->il_cache, prepended_block_start); + const RzILCacheBlock *il_block = rz_il_cache_lift_il_block(interp->il_cache, prepended_block_start); rz_interp_block_resolve_bounds(&ctx, block, il_block); mu_assert_true(block->bounds_resolved, "bounds resolved"); @@ -256,7 +259,7 @@ bool test_interp_block_resolve_bounds_split(bool single_op_existing_block, bool RzInterpBlock *existing_block = rz_interp_block_at(&ctx, existing_block_start); mu_assert_notnull(existing_block, "block"); - RzILCacheBlock *il_block = rz_il_cache_lift_il_block(interp->il_cache, existing_block_start); + const RzILCacheBlock *il_block = rz_il_cache_lift_il_block(interp->il_cache, existing_block_start); rz_interp_block_resolve_bounds(&ctx, existing_block, il_block); mu_assert_true(existing_block->bounds_resolved, "bounds resolved"); mu_assert_eq(rz_interp_block_get_start(existing_block), existing_block_start, "block start"); From d9bfa68df0210929fca1acd7b7a6dc510c858159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Mon, 20 Jul 2026 09:13:02 +0200 Subject: [PATCH 332/334] Fix crash in failed apply --- librz/include/rz_inquiry/rz_interpreter.h | 2 +- librz/inquiry/interp/driver.c | 4 +++- librz/inquiry/interp/interpreter.c | 10 ++++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index ad9107705cb..1a25a6cc7b6 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -233,7 +233,7 @@ struct rz_interp_result_t { } /*RzInterpResult*/; RZ_API RzInterpResult *rz_interp_run(RzInterpInstance *inst, ut64 entry_point, RzInterpResultDimen dimen); -RZ_API void rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, RZ_NONNULL RzAnalysis *analysis); +RZ_API bool rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, RZ_NONNULL RzAnalysis *analysis); extern RZ_API RzInterpValueAbstraction rz_interp_value_domain_const; diff --git a/librz/inquiry/interp/driver.c b/librz/inquiry/interp/driver.c index 5cb80dc82e0..2de944a03b2 100644 --- a/librz/inquiry/interp/driver.c +++ b/librz/inquiry/interp/driver.c @@ -316,7 +316,9 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { } case DRIVER_MESSAGE_INTERP_RESULT: { RzInterpResult *res = msg.payload.interp_result; - rz_interp_result_apply_to_analysis(res, core->analysis); + if (!rz_interp_result_apply_to_analysis(res, core->analysis)) { + RZ_LOG_WARN("Failed to apply to analysis\n"); + } entries_finished++; break; } diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 3931c85463b..9417a199697 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -1472,11 +1472,15 @@ static void bb_add_target(RzAnalysisBlock *abb, ut64 target) { } } -RZ_API void rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, RZ_NONNULL RzAnalysis *analysis) { - rz_return_if_fail(res && analysis); +RZ_API bool rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, RZ_NONNULL RzAnalysis *analysis) { + rz_return_val_if_fail(res && analysis, false); // partially copied from rz_inquiry_convert_and_add_to_analysis() char name[128]; RzAnalysisFunction *func = rz_analysis_create_function(analysis, rz_strf(name, "inquiry.0x%" PFMT64x, res->entry), res->entry, RZ_ANALYSIS_FCN_TYPE_FCN); + if (!func) { + // TODO: handle better than skipping everything + return false; + } RzIntervalTreeIter it; RzInterpBlock *block; rz_interval_tree_foreach (&res->blocks, it, block) { @@ -1553,4 +1557,6 @@ RZ_API void rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, R } rz_iterator_free(it); } + + return true; } From cc5a2a8e22ba3bc98dd8406c9e0bfa04ab2216c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Mon, 20 Jul 2026 17:10:52 +0200 Subject: [PATCH 333/334] Fix call with ret addr in memory --- librz/inquiry/interp/interpreter.c | 33 +++++++++++++++--------------- test/unit/test_inquiry_interp.c | 29 ++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/librz/inquiry/interp/interpreter.c b/librz/inquiry/interp/interpreter.c index 9417a199697..761b83774f9 100644 --- a/librz/inquiry/interp/interpreter.c +++ b/librz/inquiry/interp/interpreter.c @@ -1208,6 +1208,9 @@ static bool eval_effect(RzInterpRunContext *ctx, case RZ_IL_OP_STORE: case RZ_IL_OP_STOREW: { RzInterpAbstrVal *st_addr = val_domain(ctx->inst)->val_new_top(); + if (!st_addr) { + goto error; + } RzILOpPure *key = effect->code == RZ_IL_OP_STORE ? effect->op.store.key : effect->op.storew.key; RzILMemIndex mem_idx = effect->code == RZ_IL_OP_STORE ? 0 : effect->op.storew.mem; if (!eval_pure(ctx, key, st_addr)) { @@ -1217,26 +1220,23 @@ static bool eval_effect(RzInterpRunContext *ctx, } RzBitVector st_addr_bv; rz_bv_init(&st_addr_bv, 64); - bool st_addr_is_const = st_addr && val_domain(ctx->inst)->to_concrete_const(st_addr, &st_addr_bv); - if (!st_addr_is_const) { - rz_bv_fini(&st_addr_bv); - val_domain(ctx->inst)->val_free(st_addr); - break; - } - if (rz_bv_len(&st_addr_bv) == 64) { - // TODO: Remove normalization. - // Unset bit 63 is required, because the RzBuffer API only supports - // st64 addresses. - RzBitVector mask = { 0 }; - rz_bv_init(&mask, 64); - rz_bv_set_from_ut64(&mask, 0x7fffffffffffffff); - rz_bv_and_inplace(&st_addr_bv, &mask); + if (val_domain(ctx->inst)->to_concrete_const(st_addr, &st_addr_bv)) { + if (rz_bv_len(&st_addr_bv) == 64) { + // TODO: Remove normalization. + // Unset bit 63 is required, because the RzBuffer API only supports + // st64 addresses. + RzBitVector mask = { 0 }; + rz_bv_init(&mask, 64); + rz_bv_set_from_ut64(&mask, 0x7fffffffffffffff); + rz_bv_and_inplace(&st_addr_bv, &mask); + } + report_yield_xref(ctx, insn_pkt_size, ctx->insn_addr, st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); } RzILOpPure *pval = effect->code == RZ_IL_OP_STORE ? effect->op.store.value : effect->op.storew.value; eval_out = val_domain(ctx->inst)->val_new_top(); if (!eval_out || !eval_pure(ctx, pval, eval_out)) { - RZ_LOG_ERROR("prototype: SUB x failed to evaluate.\n"); + RZ_LOG_ERROR("prototype: STORE/STOREW x failed to evaluate.\n"); rz_bv_fini(&st_addr_bv); val_domain(ctx->inst)->val_free(st_addr); goto error; @@ -1246,12 +1246,11 @@ static bool eval_effect(RzInterpRunContext *ctx, ctx->call_cand.npc = ctx->il_block_end; ctx->call_cand.in_mem = true; } - report_yield_xref(ctx, insn_pkt_size, ctx->insn_addr, st_addr, RZ_ANALYSIS_XREF_TYPE_MEM_WRITE); - val_domain(ctx->inst)->val_free(st_addr); if (!store_abstr_data(ctx->inst, mem_idx, st_addr, eval_out)) { rz_bv_fini(&st_addr_bv); goto error; } + val_domain(ctx->inst)->val_free(st_addr); rz_bv_fini(&st_addr_bv); break; } diff --git a/test/unit/test_inquiry_interp.c b/test/unit/test_inquiry_interp.c index 80054e65767..2705276fe91 100644 --- a/test/unit/test_inquiry_interp.c +++ b/test/unit/test_inquiry_interp.c @@ -558,7 +558,7 @@ bool test_interp_cfg_multi_entry_fallthrough_jmp_inside_other() { mu_end; } -bool test_interp_cfg_call(void) { +bool test_interp_cfg_call_link_register(void) { TestInterp *interp = interp_new("arm", 64, 0x10000, "hex://" "600880d2" // 0x00 mov x0, 0x43 "ff430094" // 0x04 bl 0x11000 @@ -608,6 +608,30 @@ bool test_interp_cfg_call_multi_insn(void) { mu_end; } +bool test_interp_cfg_call_ret_in_memory(void) { + TestInterp *interp = interp_new("x86", 64, 0x10000, "hex://" + "89c1" // 0x00 mov ecx, eax + "ff14cde0000008" // 0x02 call qword [rcx*8+0x80000e0] + "c3" // 0x09 ret + ); + mu_assert_notnull(interp, "init"); + RzInterpResult *res = rz_interp_run(interp->inst, 0x10000, RZ_INTERP_RESULT_DIMEN_BASE); + + EXTRACT_RESULT(res, 2); + mu_assert_eq(res->entry, 0x10000, "result entry"); + ASSERT_BLOCK(0, 0x10000, 0x10009, true, UT64_MAX); + ASSERT_BLOCK(1, 0x10009, 0x1000a, false, UT64_MAX); + + rz_interp_result_apply_to_analysis(res, interp->analysis); + RzAnalysisFunction *fcn = rz_analysis_get_function_at(interp->analysis, 0x10000); + mu_assert_notnull(fcn, "analysis function"); + mu_assert_eq(rz_pvector_len(fcn->bbs), 1, "analysis block count"); + ASSERT_ANALYSIS_BLOCK(rz_pvector_at(fcn->bbs, 0), 0x10000, 0x1000a, UT64_MAX, UT64_MAX); + + interp_free(interp); + mu_end; +} + static int xref_cmp(const void *a, const void *b, void *user) { const RzAnalysisXRef *ax = a; const RzAnalysisXRef *bx = b; @@ -718,8 +742,9 @@ bool all_tests() { mu_run_test(test_interp_cfg_multi_entry_fallthrough_jmp_before); mu_run_test(test_interp_cfg_multi_entry_fallthrough_jmp_inside_self); mu_run_test(test_interp_cfg_multi_entry_fallthrough_jmp_inside_other); - mu_run_test(test_interp_cfg_call); + mu_run_test(test_interp_cfg_call_link_register); mu_run_test(test_interp_cfg_call_multi_insn); + mu_run_test(test_interp_cfg_call_ret_in_memory); mu_run_test(test_interp_xrefs); mu_run_test(test_interp_comments); return tests_passed != tests_run; From bd62179560486c9f1ca14bd5c3153aedd64b8d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Mon, 20 Jul 2026 18:48:56 +0200 Subject: [PATCH 334/334] Add inquiry.comment config var --- librz/core/cconfig.c | 3 +++ librz/core/cmd/cmd_inquiry.c | 19 +++++++++++++------ librz/core/serialize_core.c | 2 ++ librz/include/rz_inquiry/rz_interpreter.h | 2 +- librz/inquiry/README.md | 5 ++++- librz/inquiry/interp/driver.c | 9 ++++++--- 6 files changed, 29 insertions(+), 11 deletions(-) diff --git a/librz/core/cconfig.c b/librz/core/cconfig.c index 881203f122b..21a103f5d4d 100644 --- a/librz/core/cconfig.c +++ b/librz/core/cconfig.c @@ -3859,6 +3859,9 @@ RZ_API int rz_core_config_init(RzCore *core) { SETB("flirt.sigdb.load.extra", true, "Load signatures from the extra path"); SETB("flirt.sigdb.load.home", true, "Load signatures from the home path"); + /* Inquiry. All of these are experimental and not stored in projects. */ + rz_config_add_bool(cfg, "inquiry.comment", "Experimental: Write comments at each address describing the state during abstract interpretation for debugging", false); + return true; } diff --git a/librz/core/cmd/cmd_inquiry.c b/librz/core/cmd/cmd_inquiry.c index 03c3dc65ac8..0a7130e3a69 100644 --- a/librz/core/cmd/cmd_inquiry.c +++ b/librz/core/cmd/cmd_inquiry.c @@ -11,19 +11,26 @@ #include #include -RZ_IPI RzCmdStatus rz_inquiry_analyze_function_handler(RzCore *core, int argc, const char **argv) { - rz_return_val_if_fail(core->analysis && core->io && core->bin->cur && core->bin->cur->o, RZ_CMD_STATUS_ERROR); +static bool core_interp_run(RzCore *core) { + RzInterpResultDimen dimens = RZ_INTERP_RESULT_DIMEN_XREFS; + if (rz_config_get_bool(core->config, "inquiry.comment")) { + dimens |= RZ_INTERP_RESULT_DIMEN_COMMENTS; + } RzSetU *entry_points = rz_set_u_new(); if (!entry_points) { return RZ_CMD_STATUS_ERROR; } rz_set_u_add(entry_points, core->offset); - bool success = rz_interp_driver_run(core, entry_points); + bool success = rz_interp_driver_run(core, entry_points, dimens); if (!success) { RZ_LOG_ERROR("Analysis failed.\n"); - return RZ_CMD_STATUS_ERROR; } - return RZ_CMD_STATUS_OK; + return success; +} + +RZ_IPI RzCmdStatus rz_inquiry_analyze_function_handler(RzCore *core, int argc, const char **argv) { + rz_return_val_if_fail(core->analysis && core->io && core->bin->cur && core->bin->cur->o, RZ_CMD_STATUS_ERROR); + return core_interp_run(core) ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR; } static RzVector /**/ *get_ignored_code_regions( @@ -79,7 +86,7 @@ RZ_IPI RzCmdStatus rz_inquiry_interpreter_prototype_handler(RzCore *core, int ar rz_set_u_add(entry_points, entry_point); } } - bool success = rz_interp_driver_run(core, entry_points); + bool success = rz_interp_driver_run(core, entry_points, RZ_INTERP_RESULT_DIMEN_XREFS); eprintf("Finished reference recovery: %s\n", success ? "OK" : "FAIL"); if (!success) { return RZ_CMD_STATUS_ERROR; diff --git a/librz/core/serialize_core.c b/librz/core/serialize_core.c index af2c51dc0c7..86dd086b52a 100644 --- a/librz/core/serialize_core.c +++ b/librz/core/serialize_core.c @@ -32,6 +32,7 @@ static bool file_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, RZ_NULLABLE c */ static const char *config_exclude_save[] = { + "inquiry.comment", // experimental NULL }; @@ -59,6 +60,7 @@ static const char *config_exclude_load[] = { "scr.utf8", "scr.utf8.curvy", "ghidra.sleighhome", // also important for Cutter + "inquiry.comment", // experimental NULL }; diff --git a/librz/include/rz_inquiry/rz_interpreter.h b/librz/include/rz_inquiry/rz_interpreter.h index 1a25a6cc7b6..77d62ea04f8 100644 --- a/librz/include/rz_inquiry/rz_interpreter.h +++ b/librz/include/rz_inquiry/rz_interpreter.h @@ -237,6 +237,6 @@ RZ_API bool rz_interp_result_apply_to_analysis(RZ_NONNULL RzInterpResult *res, R extern RZ_API RzInterpValueAbstraction rz_interp_value_domain_const; -RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points); +RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points, RzInterpResultDimen dimens); #endif // RZ_INTERPRETER diff --git a/librz/inquiry/README.md b/librz/inquiry/README.md index 10b33411453..d2092153e9f 100644 --- a/librz/inquiry/README.md +++ b/librz/inquiry/README.md @@ -8,7 +8,10 @@ Module implementing basic and advanced binary analysis. ## TODO * rename interp to absint -* rename valeabstraction to valuedomain +* rename valueabstraction to valuedomain +* update tests +* branch and remove everything that should not be merged at first +* rebase/squash abstr-intr-prototype on top of new branch ## Interpreter Notes diff --git a/librz/inquiry/interp/driver.c b/librz/inquiry/interp/driver.c index 2de944a03b2..3dbfd3879c3 100644 --- a/librz/inquiry/interp/driver.c +++ b/librz/inquiry/interp/driver.c @@ -51,6 +51,7 @@ typedef struct interp_driver_message_t { typedef struct interp_driver_t { RzThreadQueue /**/ *entry_points_ch; ///< Main delivers entry points to multiple interpreters with this. TODO: linked list is not optimal, but rbuf may lead to starvation RzThreadRingBuf *main_ch; ///< Channel to main. Multiple interpreters send info requests and analysis results with this. + RzInterpResultDimen dimens; } InterpDriver; /** @@ -91,7 +92,7 @@ static void *interp_th(void *user) { } ut64 entry_point_val = *entry_point; free(entry_point); - RzInterpResult *res = rz_interp_run(inst, entry_point_val, RZ_INTERP_RESULT_DIMEN_XREFS | RZ_INTERP_RESULT_DIMEN_COMMENTS); // TODO: make dimensions configurable + RzInterpResult *res = rz_interp_run(inst, entry_point_val, ctx->driver->dimens); if (res) { InterpDriverMessage msg = { .type = DRIVER_MESSAGE_INTERP_RESULT, @@ -225,7 +226,7 @@ static RzInterpIOReadResult handle_io_request(const RzAnalysisILContext *il_ctx, return ok ? RZ_INTERP_IO_READ_RESULT_TOP : RZ_INTERP_IO_READ_RESULT_TOP; } -RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { +RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points, RzInterpResultDimen dimens) { bool return_code = false; bool user_sent_signal = false; @@ -235,7 +236,9 @@ RZ_API bool rz_interp_driver_run(RzCore *core, RZ_OWN RzSetU *entry_points) { goto err_none; } - InterpDriver driver; + InterpDriver driver = { + .dimens = dimens + }; driver.entry_points_ch = rz_th_queue_new(RZ_THREAD_QUEUE_UNLIMITED, free); if (!driver.entry_points_ch) { goto err_il_cache;