From d9061f0cba48d4cebf97a1cdc7650cf85d38ceba Mon Sep 17 00:00:00 2001 From: well-mannered-goat Date: Tue, 9 Jun 2026 17:08:47 +0530 Subject: [PATCH 01/10] classify signals recevied by the child in debugger --- librz/debug/debug.c | 55 ++++++++++++++++-------- librz/debug/p/native/linux/linux_debug.c | 18 ++++++++ 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/librz/debug/debug.c b/librz/debug/debug.c index f97004cfdc1..b0e62a5bde2 100644 --- a/librz/debug/debug.c +++ b/librz/debug/debug.c @@ -1150,6 +1150,27 @@ RZ_API int rz_debug_step_cnum(RzDebug *dbg, int steps) { return steps; } +static bool skip_current_instruction(RzDebug *dbg) { + ut8 buf[64]; + RzAnalysisOp op = { 0 }; + ut64 pc = rz_debug_reg_get(dbg, "PC"); + + dbg->iob.read_at(dbg->iob.io, pc, buf, sizeof(buf)); + + rz_analysis_op_init(&op); + rz_analysis_op(dbg->analysis, &op, pc, buf, sizeof(buf), + RZ_ANALYSIS_OP_MASK_BASIC); + + if (op.size <= 0) { + rz_analysis_op_fini(&op); + return false; + } + + rz_debug_reg_set(dbg, "PC", pc + op.size); + rz_analysis_op_fini(&op); + return true; +} + RZ_API int rz_debug_continue_kill(RzDebug *dbg, int sig) { RzDebugReasonType reason = RZ_DEBUG_REASON_NONE; int ret = 0; @@ -1298,34 +1319,30 @@ RZ_API int rz_debug_continue_kill(RzDebug *dbg, int sig) { } sig = 0; // clear continuation after signal if needed - /* handle general signals here based on the return from the wait - * function */ + /* Skip the instruction which causes the kernel to send the signal, to skip the signal handler + Do not skip the instruction if the signal was send using kill etc types of system call*/ if (dbg->reason.signum != -1) { int what = rz_debug_signal_what(dbg, dbg->reason.signum); + if (what & RZ_DBG_SIGNAL_CONT) { sig = dbg->reason.signum; eprintf("Continue into the signal %d handler\n", sig); goto repeat; } else if (what & RZ_DBG_SIGNAL_SKIP) { - // skip signal. requires skipping one instruction - ut8 buf[64]; - RzAnalysisOp op = { 0 }; - ut64 pc = rz_debug_reg_get(dbg, "PC"); - dbg->iob.read_at(dbg->iob.io, pc, buf, sizeof(buf)); - rz_analysis_op_init(&op); - rz_analysis_op(dbg->analysis, &op, pc, buf, sizeof(buf), RZ_ANALYSIS_OP_MASK_BASIC); - if (op.size > 0) { - const char *signame = rz_signal_to_string(dbg->reason.signum); - rz_debug_reg_set(dbg, "PC", pc + op.size); - eprintf("Skip signal %d handler %s\n", - dbg->reason.signum, signame); - rz_analysis_op_fini(&op); + const char *signame = rz_signal_to_string(dbg->reason.signum); + + if (dbg->reason.sig_source == RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL) { + eprintf("Skipped signal handler for %d (%s)\n", dbg->reason.signum, signame); goto repeat; - } else { - ut64 pc = rz_debug_reg_get(dbg, "PC"); - eprintf("Stalled with an exception at 0x%08" PFMT64x "\n", pc); } - rz_analysis_op_fini(&op); + + if (skip_current_instruction(dbg)) { + eprintf("Skipped signal handler for %d (%s)\n", dbg->reason.signum, signame); + goto repeat; + } + + ut64 pc = rz_debug_reg_get(dbg, "PC"); + eprintf("Stalled with an exception at 0x%08" PFMT64x "\n", pc); } } #if __WINDOWS__ diff --git a/librz/debug/p/native/linux/linux_debug.c b/librz/debug/p/native/linux/linux_debug.c index 85eb7dbcb08..d3c0d96f9db 100644 --- a/librz/debug/p/native/linux/linux_debug.c +++ b/librz/debug/p/native/linux/linux_debug.c @@ -65,6 +65,23 @@ static void linux_dbg_wait_break_main(RzDebug *dbg); static void linux_dbg_wait_break(RzDebug *dbg); static RzDebugReasonType linux_handle_new_task(RzDebug *dbg, int tid); +/** + * @brief Determine whether a signal was externally sent or internally generated. + * + * @param si_code Signal code from siginfo_t::si_code. + * @return RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL if the signal was sent via + * kill(), sigqueue(), tkill()/tgkill(), otherwise + * RZ_DEBUG_SIGNAL_SOURCE_INTERNAL. + */ +static RzDebugSignalSource find_signal_source(int si_code){ + if(si_code == SI_USER || + si_code == SI_QUEUE || + si_code == SI_TKILL){ + return RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL; + } + return RZ_DEBUG_SIGNAL_SOURCE_INTERNAL; +} + int linux_handle_signals(RzDebug *dbg, int tid) { siginfo_t siginfo = { 0 }; int ret = rz_debug_ptrace(dbg, PTRACE_GETSIGINFO, tid, 0, (rz_ptrace_data_t)(size_t)&siginfo); @@ -85,6 +102,7 @@ int linux_handle_signals(RzDebug *dbg, int tid) { dbg->reason.signum = siginfo.si_signo; dbg->stopaddr = (ut64)(size_t)siginfo.si_addr; // dbg->errno = siginfo.si_errno; + dbg->reason.sig_source = find_signal_source(siginfo.si_code); // siginfo.si_code -> HWBKPT, USER, KERNEL or WHAT // TODO: DO MORE RDEBUGREASON HERE switch (dbg->reason.signum) { From 3d95063c60b480b26b8d423265a6078b63f6d749 Mon Sep 17 00:00:00 2001 From: well-mannered-goat Date: Thu, 11 Jun 2026 10:58:22 +0530 Subject: [PATCH 02/10] sources of signals for the debugger --- librz/include/rz_debug.h | 7 +++++++ test/db/archos/linux-ppc/dbg_sig | 1 - test/db/archos/linux-x64/dbg | 22 ++++++++++++++++++++-- test/db/archos/linux-x64/dbg_cont_back | 1 - 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/librz/include/rz_debug.h b/librz/include/rz_debug.h index 7ed09e5d84c..05d672f38f7 100644 --- a/librz/include/rz_debug.h +++ b/librz/include/rz_debug.h @@ -123,10 +123,17 @@ typedef struct rz_debug_frame_t { ut64 bp; } RzDebugFrame; +typedef enum{ + RZ_DEBUG_SIGNAL_SOURCE_UNKNOWN, + RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL, + RZ_DEBUG_SIGNAL_SOURCE_INTERNAL, +} RzDebugSignalSource; + typedef struct rz_debug_reason_t { int /*RzDebugReasonType*/ type; int tid; int signum; + RzDebugSignalSource sig_source; ut64 bp_addr; ut64 timestamp; ut64 addr; diff --git a/test/db/archos/linux-ppc/dbg_sig b/test/db/archos/linux-ppc/dbg_sig index fffcb01e0e7..78d3539d67d 100644 --- a/test/db/archos/linux-ppc/dbg_sig +++ b/test/db/archos/linux-ppc/dbg_sig @@ -1,6 +1,5 @@ NAME=Signal handling ppc64le FILE=bins/elf/ppc64le/dbg_signal_test -BROKEN=1 ARGS=-a ppc -d CMDS=< Date: Sun, 14 Jun 2026 09:59:43 +0530 Subject: [PATCH 03/10] classify signals in BSD too --- librz/debug/p/native/bsd/bsd_debug.c | 18 ++++++++++++++++++ librz/debug/p/native/bsd/kfbsd_debug.c | 18 ++++++++++++++++++ librz/debug/p/native/bsd/netbsd_debug.c | 18 ++++++++++++++++++ librz/debug/p/native/linux/linux_debug.c | 18 +++++++++--------- 4 files changed, 63 insertions(+), 9 deletions(-) diff --git a/librz/debug/p/native/bsd/bsd_debug.c b/librz/debug/p/native/bsd/bsd_debug.c index dbbb16b5936..32d4d095c6c 100644 --- a/librz/debug/p/native/bsd/bsd_debug.c +++ b/librz/debug/p/native/bsd/bsd_debug.c @@ -63,6 +63,23 @@ static void addr_to_string(struct sockaddr_storage *ss, char *buffer, int buflen } #endif +/** + * \brief Determine whether a signal was externally sent or internally generated. + * + * \param si_code Signal code from siginfo_t::si_code. + * \return RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL if the signal was sent via + * kill(), sigqueue(), tkill()/tgkill(), otherwise + * RZ_DEBUG_SIGNAL_SOURCE_INTERNAL. + */ +static RzDebugSignalSource find_signal_source(int si_code) { + if (si_code == SI_USER || + si_code == SI_QUEUE || + si_code == SI_LWP) { + return RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL; + } + return RZ_DEBUG_SIGNAL_SOURCE_INTERNAL; +} + int bsd_handle_signals(RzDebug *dbg) { #if __KFBSD__ || __NetBSD__ siginfo_t siginfo; @@ -95,6 +112,7 @@ int bsd_handle_signals(RzDebug *dbg) { #endif dbg->reason.type = RZ_DEBUG_REASON_SIGNAL; dbg->reason.signum = siginfo.si_signo; + dbg->reason.sig_source = find_signal_source(siginfo.si_code); switch (dbg->reason.signum) { case SIGABRT: diff --git a/librz/debug/p/native/bsd/kfbsd_debug.c b/librz/debug/p/native/bsd/kfbsd_debug.c index be43dcc7bfc..faa98e5fa7b 100644 --- a/librz/debug/p/native/bsd/kfbsd_debug.c +++ b/librz/debug/p/native/bsd/kfbsd_debug.c @@ -57,6 +57,23 @@ static void addr_to_string(struct sockaddr_storage *ss, char *buffer, int buflen } } +/** + * \brief Determine whether a signal was externally sent or internally generated. + * + * \param si_code Signal code from siginfo_t::si_code. + * \return RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL if the signal was sent via + * kill(), sigqueue(), tkill()/tgkill(), otherwise + * RZ_DEBUG_SIGNAL_SOURCE_INTERNAL. + */ +static RzDebugSignalSource find_signal_source(int si_code) { + if (si_code == SI_USER || + si_code == SI_QUEUE || + si_code == SI_LWP) { + return RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL; + } + return RZ_DEBUG_SIGNAL_SOURCE_INTERNAL; +} + int bsd_handle_signals(RzDebug *dbg) { siginfo_t siginfo; // Trying to figure out a bit by the signal @@ -74,6 +91,7 @@ int bsd_handle_signals(RzDebug *dbg) { siginfo = linfo.pl_siginfo; dbg->reason.type = RZ_DEBUG_REASON_SIGNAL; dbg->reason.signum = siginfo.si_signo; + dbg->reason.sig_source = find_signal_source(siginfo.si_code); switch (dbg->reason.signum) { case SIGABRT: diff --git a/librz/debug/p/native/bsd/netbsd_debug.c b/librz/debug/p/native/bsd/netbsd_debug.c index 8bb407f50e6..35ee674c5f5 100644 --- a/librz/debug/p/native/bsd/netbsd_debug.c +++ b/librz/debug/p/native/bsd/netbsd_debug.c @@ -23,6 +23,23 @@ #include #include +/** + * \brief Determine whether a signal was externally sent or internally generated. + * + * \param si_code Signal code from siginfo_t::si_code. + * \return RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL if the signal was sent via + * kill(), sigqueue(), tkill()/tgkill(), otherwise + * RZ_DEBUG_SIGNAL_SOURCE_INTERNAL. + */ +static RzDebugSignalSource find_signal_source(int si_code) { + if (si_code == SI_USER || + si_code == SI_QUEUE || + si_code == SI_LWP) { + return RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL; + } + return RZ_DEBUG_SIGNAL_SOURCE_INTERNAL; +} + int bsd_handle_signals(RzDebug *dbg) { siginfo_t siginfo; struct ptrace_siginfo sinfo = { 0 }; @@ -38,6 +55,7 @@ int bsd_handle_signals(RzDebug *dbg) { siginfo = sinfo.psi_siginfo; dbg->reason.type = RZ_DEBUG_REASON_SIGNAL; dbg->reason.signum = siginfo.si_signo; + dbg->reason.sig_source = find_signal_source(siginfo.si_code); switch (dbg->reason.signum) { case SIGABRT: diff --git a/librz/debug/p/native/linux/linux_debug.c b/librz/debug/p/native/linux/linux_debug.c index d3c0d96f9db..7fc58cf2726 100644 --- a/librz/debug/p/native/linux/linux_debug.c +++ b/librz/debug/p/native/linux/linux_debug.c @@ -66,20 +66,20 @@ static void linux_dbg_wait_break(RzDebug *dbg); static RzDebugReasonType linux_handle_new_task(RzDebug *dbg, int tid); /** - * @brief Determine whether a signal was externally sent or internally generated. + * \brief Determine whether a signal was externally sent or internally generated. * - * @param si_code Signal code from siginfo_t::si_code. - * @return RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL if the signal was sent via + * \param si_code Signal code from siginfo_t::si_code. + * \return RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL if the signal was sent via * kill(), sigqueue(), tkill()/tgkill(), otherwise * RZ_DEBUG_SIGNAL_SOURCE_INTERNAL. */ -static RzDebugSignalSource find_signal_source(int si_code){ - if(si_code == SI_USER || +static RzDebugSignalSource find_signal_source(int si_code) { + if (si_code == SI_USER || si_code == SI_QUEUE || - si_code == SI_TKILL){ - return RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL; - } - return RZ_DEBUG_SIGNAL_SOURCE_INTERNAL; + si_code == SI_TKILL) { + return RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL; + } + return RZ_DEBUG_SIGNAL_SOURCE_INTERNAL; } int linux_handle_signals(RzDebug *dbg, int tid) { From 98a39c1b9bec03d3e89fe28759febec57c60c75e Mon Sep 17 00:00:00 2001 From: well-mannered-goat Date: Thu, 18 Jun 2026 18:01:44 +0530 Subject: [PATCH 04/10] fix netbsd build --- librz/debug/p/native/bsd/netbsd_debug.c | 8 ++++---- librz/include/rz_debug.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/librz/debug/p/native/bsd/netbsd_debug.c b/librz/debug/p/native/bsd/netbsd_debug.c index 35ee674c5f5..a4dbe281fa7 100644 --- a/librz/debug/p/native/bsd/netbsd_debug.c +++ b/librz/debug/p/native/bsd/netbsd_debug.c @@ -31,10 +31,10 @@ * kill(), sigqueue(), tkill()/tgkill(), otherwise * RZ_DEBUG_SIGNAL_SOURCE_INTERNAL. */ -static RzDebugSignalSource find_signal_source(int si_code) { - if (si_code == SI_USER || - si_code == SI_QUEUE || - si_code == SI_LWP) { +static RzDebugSignalSource find_signal_source(int signal_code) { + if (signal_code == SI_USER || + signal_code == SI_QUEUE || + signal_code == SI_LWP) { return RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL; } return RZ_DEBUG_SIGNAL_SOURCE_INTERNAL; diff --git a/librz/include/rz_debug.h b/librz/include/rz_debug.h index 05d672f38f7..2b2fc118648 100644 --- a/librz/include/rz_debug.h +++ b/librz/include/rz_debug.h @@ -123,7 +123,7 @@ typedef struct rz_debug_frame_t { ut64 bp; } RzDebugFrame; -typedef enum{ +typedef enum { RZ_DEBUG_SIGNAL_SOURCE_UNKNOWN, RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL, RZ_DEBUG_SIGNAL_SOURCE_INTERNAL, From 6bed7a106c0c440593efbba94f5c106c37bc3939 Mon Sep 17 00:00:00 2001 From: well-mannered-goat Date: Fri, 19 Jun 2026 11:07:56 +0530 Subject: [PATCH 05/10] ppc add regex for signal test --- test/db/archos/linux-ppc/dbg_sig | 1 + 1 file changed, 1 insertion(+) diff --git a/test/db/archos/linux-ppc/dbg_sig b/test/db/archos/linux-ppc/dbg_sig index 78d3539d67d..f422615a21d 100644 --- a/test/db/archos/linux-ppc/dbg_sig +++ b/test/db/archos/linux-ppc/dbg_sig @@ -16,4 +16,5 @@ EXPECT=< Date: Fri, 19 Jun 2026 21:15:00 +0530 Subject: [PATCH 06/10] fix regex in tests --- test/db/archos/linux-ppc/dbg_sig | 3 ++- test/db/archos/linux-x64/dbg | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/test/db/archos/linux-ppc/dbg_sig b/test/db/archos/linux-ppc/dbg_sig index f422615a21d..1e770d57718 100644 --- a/test/db/archos/linux-ppc/dbg_sig +++ b/test/db/archos/linux-ppc/dbg_sig @@ -14,7 +14,8 @@ EXPECT=< Date: Tue, 23 Jun 2026 21:09:34 +0530 Subject: [PATCH 07/10] fix leaks in float.c --- librz/util/float/float.c | 1 + 1 file changed, 1 insertion(+) diff --git a/librz/util/float/float.c b/librz/util/float/float.c index af01b4bc307..6c30bfc04ef 100644 --- a/librz/util/float/float.c +++ b/librz/util/float/float.c @@ -1342,6 +1342,7 @@ RZ_API RZ_OWN RzFloat *rz_float_mod_ieee_bin(RZ_NONNULL RzFloat *left, RZ_NONNUL } rz_float_free(ret); + rz_float_free(right_abs); ret = same_sign; } } From 11d3cf2e8d990f9b4188b59c9a06323b935ca020 Mon Sep 17 00:00:00 2001 From: well-mannered-goat Date: Thu, 25 Jun 2026 23:48:23 +0530 Subject: [PATCH 08/10] use loop instead of goto and labels --- librz/debug/debug.c | 283 +++++++++++++++++++++++--------------------- 1 file changed, 149 insertions(+), 134 deletions(-) diff --git a/librz/debug/debug.c b/librz/debug/debug.c index b0e62a5bde2..b53570fef4f 100644 --- a/librz/debug/debug.c +++ b/librz/debug/debug.c @@ -1171,190 +1171,205 @@ static bool skip_current_instruction(RzDebug *dbg) { return true; } -RZ_API int rz_debug_continue_kill(RzDebug *dbg, int sig) { - RzDebugReasonType reason = RZ_DEBUG_REASON_NONE; - int ret = 0; - RzBreakpointItem *bp = NULL; +static int session_forward_to_breakpoint(RzDebug *dbg) { + bool has_bp = false; + RzRegItem *ripc = rz_reg_get_by_role(dbg->reg, RZ_REG_NAME_PC); + if (!ripc) { + return 0; + } + RzVector *vreg = ht_up_find(dbg->session->registers, ripc->offset | (ripc->arena << 16), NULL); + RzDebugChangeReg *reg; + rz_vector_foreach_prev (vreg, reg) { + if (reg->cnum <= dbg->session->cnum) { + continue; + } + has_bp = rz_bp_get_in(dbg->bp, reg->data, RZ_PERM_X) != NULL; + if (has_bp) { + eprintf("hit breakpoint at: 0x%" PFMT64x " cnum: %d\n", reg->data, reg->cnum); + rz_debug_goto_cnum(dbg, reg->cnum); + return dbg->tid; + } + } + rz_debug_goto_cnum(dbg, dbg->session->maxcnum); + return dbg->tid; +} + +RZ_API int rz_debug_continue_kill(RzDebug *dbg, int sig) { if (!dbg) { return 0; } - // If the debugger is not at the end of the changes - // Go to the end or the next breakpoint in the changes + // 1. Handle Time-Travel / Replay sessions first if (dbg->session && dbg->session->cnum != dbg->session->maxcnum) { - bool has_bp = false; - RzRegItem *ripc = rz_reg_get_by_role(dbg->reg, RZ_REG_NAME_PC); - if (!ripc) { + return session_forward_to_breakpoint(dbg); + } + + RzDebugReasonType reason = RZ_DEBUG_REASON_NONE; + int ret = 0; + RzBreakpointItem *bp = NULL; + + // Main execution loop: Keep running until we hit an event meant for the user + while (true) { + if (rz_debug_is_dead(dbg)) { return 0; } - RzVector *vreg = ht_up_find(dbg->session->registers, ripc->offset | (ripc->arena << 16), NULL); - RzDebugChangeReg *reg; - rz_vector_foreach_prev (vreg, reg) { - if (reg->cnum <= dbg->session->cnum) { - continue; + + /* --- STAGE 1: Resume execution --- */ + if (dbg->session && dbg->trace_continue) { + while (!rz_cons_is_breaked()) { + if (rz_debug_step(dbg, 1) != 1) { + break; + } + if (dbg->session->reasontype != RZ_DEBUG_REASON_STEP) { + break; + } } - has_bp = rz_bp_get_in(dbg->bp, reg->data, RZ_PERM_X) != NULL; - if (has_bp) { - eprintf("hit breakpoint at: 0x%" PFMT64x " cnum: %d\n", reg->data, reg->cnum); - rz_debug_goto_cnum(dbg, reg->cnum); - return dbg->tid; + reason = dbg->session->reasontype; + bp = dbg->session->bp; + } else if (dbg->cur && dbg->cur->cont) { + /* handle the stage-2 of breakpoints */ + if (!rz_debug_recoil(dbg, RZ_DBG_RECOIL_CONTINUE)) { + return 0; } + /* tell the inferior to go! */ + ret = dbg->cur->cont(dbg, dbg->pid, dbg->tid, sig); + reason = rz_debug_wait(dbg, &bp); + } else { + return 0; } - rz_debug_goto_cnum(dbg, dbg->session->maxcnum); - return dbg->tid; - } + /* --- STAGE 2: Filter background OS/Debugger events --- */ -repeat: - if (rz_debug_is_dead(dbg)) { - return 0; - } - if (dbg->session && dbg->trace_continue) { - while (!rz_cons_is_breaked()) { - if (rz_debug_step(dbg, 1) != 1) { - break; - } - if (dbg->session->reasontype != RZ_DEBUG_REASON_STEP) { - break; + // Conditional Breakpoints + if (dbg->corebind.core) { + RzCore *core = (RzCore *)dbg->corebind.core; + RzNum *num = core->num; + if (reason == RZ_DEBUG_REASON_COND) { + if (bp && bp->cond && dbg->corebind.cmd) { + dbg->corebind.cmd(dbg->corebind.core, bp->cond); + } + if (num->value) { + continue; // Condition not met, keep running + } } } - reason = dbg->session->reasontype; - bp = dbg->session->bp; - } else if (dbg->cur && dbg->cur->cont) { - /* handle the stage-2 of breakpoints */ - if (!rz_debug_recoil(dbg, RZ_DBG_RECOIL_CONTINUE)) { - return 0; + + // Disabled Breakpoints + if (reason == RZ_DEBUG_REASON_BREAKPOINT && + ((bp && !bp->enabled) || (!bp && !rz_cons_is_breaked() && dbg->corebind.core && dbg->corebind.cfggeti(dbg->corebind.core, "dbg.bpsysign")))) { + continue; } - /* tell the inferior to go! */ - ret = dbg->cur->cont(dbg, dbg->pid, dbg->tid, sig); - // XXX(jjd): why? //dbg->reason.signum = 0; - reason = rz_debug_wait(dbg, &bp); - } else { - return 0; - } - if (dbg->corebind.core) { - RzCore *core = (RzCore *)dbg->corebind.core; - RzNum *num = core->num; - if (reason == RZ_DEBUG_REASON_COND) { - if (bp && bp->cond && dbg->corebind.cmd) { - dbg->corebind.cmd(dbg->corebind.core, bp->cond); +#if __linux__ + // New Process Forked + if (reason == RZ_DEBUG_REASON_NEW_PID && dbg->follow_child) { +#if DEBUGGER + static bool (*linux_attach_new_process)(RzDebug *dbg, int pid) = NULL; + if (!linux_attach_new_process) { + linux_attach_new_process = rz_sys_dlsym(NULL, "linux_attach_new_process"); } - if (num->value) { - goto repeat; + if (linux_attach_new_process) { + linux_attach_new_process(dbg, dbg->forked_pid); } +#endif + continue; } - } - if (reason == RZ_DEBUG_REASON_BREAKPOINT && - ((bp && !bp->enabled) || (!bp && !rz_cons_is_breaked() && dbg->corebind.core && dbg->corebind.cfggeti(dbg->corebind.core, "dbg.bpsysign")))) { - goto repeat; - } -#if __linux__ - if (reason == RZ_DEBUG_REASON_NEW_PID && dbg->follow_child) { -#if DEBUGGER - /// if the plugin is not compiled link fails, so better do runtime linking - /// until this code gets fixed - static bool (*linux_attach_new_process)(RzDebug *dbg, int pid) = NULL; - if (!linux_attach_new_process) { - linux_attach_new_process = rz_sys_dlsym(NULL, "linux_attach_new_process"); + // New Thread Spawned + if (reason == RZ_DEBUG_REASON_NEW_TID) { + ret = dbg->tid; + if (!dbg->trace_clone) { + continue; + } } - if (linux_attach_new_process) { - linux_attach_new_process(dbg, dbg->forked_pid); + + // Thread Exited + if (reason == RZ_DEBUG_REASON_EXIT_TID) { + continue; } #endif - goto repeat; - } - if (reason == RZ_DEBUG_REASON_NEW_TID) { - ret = dbg->tid; - if (!dbg->trace_clone) { - goto repeat; + if (reason != RZ_DEBUG_REASON_DEAD) { + ret = dbg->tid; } - } - if (reason == RZ_DEBUG_REASON_EXIT_TID) { - goto repeat; - } -#endif - if (reason != RZ_DEBUG_REASON_DEAD) { - ret = dbg->tid; - } #if __WINDOWS__ - if (reason == RZ_DEBUG_REASON_NEW_LIB || - reason == RZ_DEBUG_REASON_EXIT_LIB || - reason == RZ_DEBUG_REASON_NEW_TID || - reason == RZ_DEBUG_REASON_NONE || - reason == RZ_DEBUG_REASON_EXIT_TID) { - goto repeat; - } + if (reason == RZ_DEBUG_REASON_NEW_LIB || + reason == RZ_DEBUG_REASON_EXIT_LIB || + reason == RZ_DEBUG_REASON_NEW_TID || + reason == RZ_DEBUG_REASON_NONE || + reason == RZ_DEBUG_REASON_EXIT_TID) { + continue; + } #endif - if (reason == RZ_DEBUG_REASON_EXIT_PID) { + + if (reason == RZ_DEBUG_REASON_EXIT_PID) { #if __WINDOWS__ - dbg->pid = -1; + dbg->pid = -1; #elif __linux__ - rz_debug_bp_update(dbg); - rz_bp_restore(dbg->bp, false); // (vdf) there has got to be a better way + rz_debug_bp_update(dbg); + rz_bp_restore(dbg->bp, false); #endif - } + } - /* if continuing killed the inferior, we won't be able to get - * the registers.. */ - if (reason == RZ_DEBUG_REASON_DEAD || rz_debug_is_dead(dbg)) { - return 0; - } + if (reason == RZ_DEBUG_REASON_DEAD || rz_debug_is_dead(dbg)) { + return 0; + } - /* if we hit a tracing breakpoint, we need to continue in - * whatever mode the user desired. */ - if (reason == RZ_DEBUG_REASON_TRACEPOINT) { - rz_debug_step(dbg, 1); - goto repeat; - } + // Hit a background Tracepoint + if (reason == RZ_DEBUG_REASON_TRACEPOINT) { + rz_debug_step(dbg, 1); + continue; + } - /* choose the thread that was returned from the continue function */ - // XXX(jjd): there must be a cleaner way to do this... - if (ret != dbg->tid) { - rz_debug_select(dbg, dbg->pid, ret); - } - sig = 0; // clear continuation after signal if needed + // Thread alignment context switch + if (ret != dbg->tid) { + rz_debug_select(dbg, dbg->pid, ret); + } + sig = 0; - /* Skip the instruction which causes the kernel to send the signal, to skip the signal handler - Do not skip the instruction if the signal was send using kill etc types of system call*/ - if (dbg->reason.signum != -1) { - int what = rz_debug_signal_what(dbg, dbg->reason.signum); + /* --- STAGE 3: OS Signal/Exception Handling --- */ + if (dbg->reason.signum != -1) { + int what = rz_debug_signal_what(dbg, dbg->reason.signum); - if (what & RZ_DBG_SIGNAL_CONT) { - sig = dbg->reason.signum; - eprintf("Continue into the signal %d handler\n", sig); - goto repeat; - } else if (what & RZ_DBG_SIGNAL_SKIP) { - const char *signame = rz_signal_to_string(dbg->reason.signum); + if (what & RZ_DBG_SIGNAL_CONT) { + sig = dbg->reason.signum; + eprintf("Continue into the signal %d handler\n", sig); + continue; + } else if (what & RZ_DBG_SIGNAL_SKIP) { + const char *signame = rz_signal_to_string(dbg->reason.signum); - if (dbg->reason.sig_source == RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL) { - eprintf("Skipped signal handler for %d (%s)\n", dbg->reason.signum, signame); - goto repeat; - } + if (dbg->reason.sig_source == RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL) { + eprintf("Skipped signal handler for %d (%s)\n", dbg->reason.signum, signame); + continue; + } - if (skip_current_instruction(dbg)) { - eprintf("Skipped signal handler for %d (%s)\n", dbg->reason.signum, signame); - goto repeat; - } + if (skip_current_instruction(dbg)) { + eprintf("Skipped signal handler for %d (%s)\n", dbg->reason.signum, signame); + continue; + } - ut64 pc = rz_debug_reg_get(dbg, "PC"); - eprintf("Stalled with an exception at 0x%08" PFMT64x "\n", pc); + ut64 pc = rz_debug_reg_get(dbg, "PC"); + eprintf("Stalled with an exception at 0x%08" PFMT64x "\n", pc); + } } + + // If execution reaches here, it means none of the automated filters triggered. + // This is a legitimate breakpoint or event that requires stopping! + break; } + #if __WINDOWS__ rz_cons_break_pop(); #endif - // Unset breakpoints before leaving + // Clean up breakpoints before handing control back to user if (reason != RZ_DEBUG_REASON_BREAKPOINT) { rz_bp_restore(dbg->bp, false); } - // Add a checkpoint at stops + // Add a Time-Travel checkpoint snapshot at this stop if (dbg->session && !dbg->trace_continue) { dbg->session->cnum++; dbg->session->maxcnum++; From 69b3e6dd6a081ded1ee6955fcf0abed28d6a3f45 Mon Sep 17 00:00:00 2001 From: well-mannered-goat Date: Sun, 5 Jul 2026 09:57:23 +0530 Subject: [PATCH 09/10] add readme --- librz/debug/README.md | 41 ++++++++++++++++++++++++++++++++++++++++ librz/debug/debug.c | 1 + librz/include/rz_debug.h | 2 +- 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/librz/debug/README.md b/librz/debug/README.md index e69de29bb2d..16baf14f541 100644 --- a/librz/debug/README.md +++ b/librz/debug/README.md @@ -0,0 +1,41 @@ +#THIS IS A WORK IN PROGRESS + +The `rz_debug_continue_kill` function is the core execution engine of the Rizin debugger. It manages process resumption, handles background events/exceptions, and determines whether to return control to the user or silently keep running. + +--- + +```c +RZ_API int rz_debug_continue_kill(RzDebug *dbg, int sig); + +``` + +* **Returns:** The active Thread ID (**TID**) where the debugger halted, or `0` if the process died. + +--- + +## High-Level Workflow + +The function executes a continuous control loop divided into three main phases: + +### 1. Process Resumption & Recoil + +* **Time-Travel Check:** If inspecting a reverse-debugging session's past history, it skips live execution and forwards through recorded snapshots instead. +* **Recoil (Step-Off):** If the program counter is sitting exactly on a breakpoint, it temporarily lifts the breakpoint, single-steps forward by one instruction, restores the breakpoint, and resumes. This prevents the debugger from getting trapped on the same line. + +### 2. The Silent Event Filter + +When the target process hits a trap, the loop intercepts the event reason (`rz_debug_wait`). If the event is an internal background task, it handles it silently and loops back (`continue`) to keep the program running without bothering the user: + +* **Conditional Breakpoints:** Evaluates the condition; resumes if false. +* **Disabled Breakpoints:** Ignores and skips over them. +* **OS Events (Linux/Windows):** Automatically processes process forks (`fork`), thread creations (`NEW_TID`), thread exits, and library loads. +* **Tracepoints:** Logs the tracing metric, steps over it, and moves on. +* **OS Signals:** Either injects the signal directly into the application's native signal handler or forcibly steps past the faulting instruction to skip the handler entirely. + +### 3. Cleanup & Halt + +When a legitimate user breakpoint or unexpected crash occurs, the loop breaks: + +1. **Thread Alignment:** Synchronizes the debugger's focus to the specific thread that tripped the halt. +2. **Breakpoint Restoration:** Restores software breakpoint patches back into memory. +3. **History Snapshot:** If recording is active, it saves a time-travel checkpoint so the user can safely rewind back to this exact stop later. \ No newline at end of file diff --git a/librz/debug/debug.c b/librz/debug/debug.c index b53570fef4f..987f8e3b4be 100644 --- a/librz/debug/debug.c +++ b/librz/debug/debug.c @@ -1342,6 +1342,7 @@ RZ_API int rz_debug_continue_kill(RzDebug *dbg, int sig) { if (dbg->reason.sig_source == RZ_DEBUG_SIGNAL_SOURCE_EXTERNAL) { eprintf("Skipped signal handler for %d (%s)\n", dbg->reason.signum, signame); + dbg->reason.signum = -1; continue; } diff --git a/librz/include/rz_debug.h b/librz/include/rz_debug.h index 2b2fc118648..9f95ff1cef8 100644 --- a/librz/include/rz_debug.h +++ b/librz/include/rz_debug.h @@ -133,11 +133,11 @@ typedef struct rz_debug_reason_t { int /*RzDebugReasonType*/ type; int tid; int signum; - RzDebugSignalSource sig_source; ut64 bp_addr; ut64 timestamp; ut64 addr; ut64 ptr; + RzDebugSignalSource sig_source; } RzDebugReason; typedef struct rz_debug_map_t { From 50b67e528cdc685c66568d30454d6d696b0084f0 Mon Sep 17 00:00:00 2001 From: well-mannered-goat Date: Fri, 17 Jul 2026 10:19:29 +0530 Subject: [PATCH 10/10] fix leaks --- librz/main/rizin.c | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/librz/main/rizin.c b/librz/main/rizin.c index 380ea640e10..3eb90066fa7 100644 --- a/librz/main/rizin.c +++ b/librz/main/rizin.c @@ -1094,39 +1094,58 @@ RZ_API int rz_main_rizin(int argc, const char **argv) { } } else { const char *f = (haveRarunProfile && pfile) ? pfile : argv[opt.ind]; + char *f_copy = rz_str_dup(f); // Create a safe, independent copy + + if (f_copy && *f_copy) { + char *uri = rz_str_dup(f_copy); + if (uri) { + char *p = strstr(uri, "://"); + if (p) { + *p = 0; + if (!strcmp(uri, "winedbg")) { + debugbackend = rz_str_dup("io"); + } else { + debugbackend = uri; + uri = NULL; + } + debug = 2; + } + free(uri); + } + } is_gdb = (!memcmp(f, "gdb://", RZ_MIN(f ? strlen(f) : 0, 6))); if (!is_gdb) { + RZ_FREE(pfile); pfile = rz_str_dup("dbg://"); } #if __UNIX__ /* implicit ./ to make unix behave like windows */ - if (f) { + if (f_copy) { char *path, *escaped_path; - if (strchr(f, '/')) { - // f is a path - path = rz_str_dup(f); + if (strchr(f_copy, '/')) { + path = rz_str_dup(f_copy); } else { - // f is a filename - if (rz_file_exists(f)) { - path = rz_str_prepend(rz_str_dup(f), "./"); + if (rz_file_exists(f_copy)) { + path = rz_str_prepend(rz_str_dup(f_copy), "./"); } else { - path = rz_file_path(f); + path = rz_file_path(f_copy); } } escaped_path = rz_str_arg_escape(path); pfile = rz_str_append(pfile, escaped_path); - file = pfile; // probably leaks + file = pfile; RZ_FREE(escaped_path); RZ_FREE(path); } #else - if (f) { - char *escaped_path = rz_str_arg_escape(f); + if (f_copy) { + char *escaped_path = rz_str_arg_escape(f_copy); pfile = rz_str_append(pfile, escaped_path); free(escaped_path); file = pfile; // rz_str_append (file, escaped_path); } #endif + RZ_FREE(f_copy); opt.ind++; while (opt.ind < argc) { char *escaped_arg = rz_str_arg_escape(argv[opt.ind]);