Fix xnu_wait stopping prematurely (#7744) (#7745)

* Fix xnu_wait stopping prematurely (#7744)
* Use task_swap_exception_ports (instead of *get* then *set*)
* Add new debugger stop reason for user-initiated breaks
* Stop the inferior when `dw` is interrupted (#7744)
* Stop the inferior after attaching
This commit is contained in:
Xiao Di Guan 2017-06-25 08:57:11 +10:00 committed by radare
parent f0faf8bbea
commit 6ae9d891db
6 changed files with 90 additions and 32 deletions

View File

@ -555,6 +555,7 @@ R_API const char *r_debug_reason_to_string(int type) {
case R_DEBUG_REASON_INT: return "interrupt";
case R_DEBUG_REASON_FPU: return "fpu";
case R_DEBUG_REASON_STEP: return "step";
case R_DEBUG_REASON_USERSUSP: return "suspended-by-user";
}
return "unhandled";
}

View File

@ -355,8 +355,24 @@ static RDebugReasonType r_debug_native_wait (RDebug *dbg, int pid) {
}
#if __APPLE__
// eprintf ("No waitpid here :D\n");
reason = xnu_wait (dbg, pid);
r_cons_break_push (NULL, NULL);
do {
reason = xnu_wait (dbg, pid);
if (reason == R_DEBUG_REASON_MACH_RCV_INTERRUPTED) {
if (r_cons_is_breaked ()) {
// Perhaps check the inferior is still alive,
// otherwise xnu_stop will fail.
reason = xnu_stop (dbg, pid)
? R_DEBUG_REASON_USERSUSP
: R_DEBUG_REASON_UNKNOWN;
} else {
// Weird; we'll retry the wait.
continue;
}
}
break;
} while (true);
r_cons_break_pop ();
status = reason? 1: 0;
#else
#if __linux__ && !defined (WAIT_ON_ALL_CHILDREN)

View File

@ -173,6 +173,7 @@ int xnu_attach(RDebug *dbg, int pid) {
eprintf ("error setting up exception thread\n");
return -1;
}
xnu_stop (dbg, pid);
return pid;
#endif
}
@ -198,6 +199,60 @@ int xnu_detach(RDebug *dbg, int pid) {
#endif
}
static int task_suspend_count(task_t task) {
kern_return_t kr;
struct task_basic_info info;
mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
kr = task_info (task, TASK_BASIC_INFO, (task_info_t) &info, &count);
if (kr != KERN_SUCCESS) {
eprintf ("failed to get task info\n");
return -1;
}
return info.suspend_count;
}
int xnu_stop(RDebug *dbg, int pid) {
#if XNU_USE_PTRACE
eprintf ("xnu_stop: not implemented\n");
return false;
#else
kern_return_t kr;
task_t task;
int suspend_count;
task = pid_to_task (pid);
if (!task) {
return false;
}
suspend_count = task_suspend_count (task);
if (suspend_count == -1) {
return false;
}
if (suspend_count == 1) {
// Hopefully _we_ suspended it.
return true;
}
if (suspend_count > 1) {
// This is unexpected.
return false;
}
kr = task_suspend (task);
if (kr != KERN_SUCCESS) {
eprintf ("failed to suspend task\n");
return false;
}
suspend_count = task_suspend_count (task);
if (suspend_count != 1) {
// This is unexpected.
return false;
}
return true;
#endif
}
int xnu_continue(RDebug *dbg, int pid, int tid, int sig) {
#if XNU_USE_PTRACE
void *data = (void*)(size_t)((sig != -1) ? sig : dbg->reason.signum);

View File

@ -250,6 +250,7 @@ static int coredump_nflavors = 1;
#define MAX_TSTATE_FLAVORS 10
#define DEFAULT_COREFILE_DEST "core.%u"
#define R_DEBUG_REASON_MACH_RCV_INTERRUPTED -2
typedef struct {
vm_offset_t header;
@ -268,6 +269,7 @@ const char *xnu_reg_profile (RDebug *dbg);
int xnu_attach (RDebug *dbg, int pid);
bool xnu_step (RDebug *dbg);
int xnu_detach (RDebug *dbg, int pid);
int xnu_stop (RDebug *dbg, int pid);
int xnu_continue (RDebug *dbg, int pid, int tid, int sig);
RDebugMap *xnu_map_alloc (RDebug *dbg, ut64 addr, int size);
int xnu_map_dealloc (RDebug *dbg, ut64 addr, int size);

View File

@ -199,20 +199,9 @@ static int modify_trace_bit(RDebug *dbg, xnu_thread *th, int enable) {
#error "unknown architecture"
#endif
// TODO: Tuck this into RDebug; `void *user` seems like a good candidate.
static xnu_exception_info ex = { { 0 } };
static bool xnu_save_exception_ports (int pid) {
kern_return_t kr;
task_t task = pid_to_task (pid);
if (!task)
return false;
ex.count = (sizeof (ex.ports) / sizeof (ex.ports[0]));
kr = task_get_exception_ports (task, EXC_MASK_ALL,
ex.masks, &ex.count, ex.ports,
ex.behaviors, ex.flavors);
return (kr == KERN_SUCCESS);
}
static bool xnu_restore_exception_ports (int pid) {
kern_return_t kr;
int i;
@ -372,12 +361,11 @@ static int __xnu_wait (RDebug *dbg, int pid) {
for (;;) {
kr = mach_msg (
&msg.hdr,
MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0,
sizeof (exc_msg), ex.exception_port, 10, MACH_PORT_NULL);
if (kr == MACH_RCV_INTERRUPTED ) {
eprintf ("message interrupted\n");
break;
} else if (kr == MACH_RCV_TIMED_OUT) {
MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0,
sizeof (exc_msg), ex.exception_port,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (kr == MACH_RCV_INTERRUPTED) {
reason = R_DEBUG_REASON_MACH_RCV_INTERRUPTED;
break;
} else if (kr != MACH_MSG_SUCCESS) {
eprintf ("message didn't succeded\n");
@ -428,7 +416,6 @@ bool xnu_create_exception_thread(RDebug *dbg) {
return false;
#else
kern_return_t kr;
bool ret;
mach_port_t exception_port = MACH_PORT_NULL;
mach_port_t req_port;
// Got the mach port for the current process
@ -452,17 +439,13 @@ bool xnu_create_exception_thread(RDebug *dbg) {
kr = mach_port_insert_right (task_self, exception_port, exception_port,
MACH_MSG_TYPE_MAKE_SEND);
RETURN_ON_MACH_ERROR ("error to allocate insert right\n", false);
// Save the original state of the exception ports for our child process
ret = xnu_save_exception_ports (dbg->pid);
if (!ret) {
eprintf ("error to save exception port info\n");
return false;
}
// Set the ability to get all exceptions on this port
kr = task_set_exception_ports (task, EXC_MASK_ALL, exception_port,
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
THREAD_STATE_NONE);
RETURN_ON_MACH_ERROR ("error to set port to receive exceptions\n", false);
// Atomically swap out (and save) the child process's exception ports
// for the one we just created. We'll want to receive all exceptions.
ex.count = (sizeof (ex.ports) / sizeof (*ex.ports));
kr = task_swap_exception_ports (task, EXC_MASK_ALL, exception_port,
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE,
ex.masks, &ex.count, ex.ports, ex.behaviors, ex.flavors);
RETURN_ON_MACH_ERROR ("failed to swap exception ports\n", false);
//get notification when process die
kr = mach_port_request_notification (task_self, pid_to_task (dbg->pid),
MACH_NOTIFY_DEAD_NAME, 0,

View File

@ -108,6 +108,7 @@ typedef enum {
R_DEBUG_REASON_SWI,
R_DEBUG_REASON_INT,
R_DEBUG_REASON_FPU,
R_DEBUG_REASON_USERSUSP,
} RDebugReasonType;