1 //FIXME deallocate all the port when they are not longer needed
2 
3 #include "xnu_threads.h"
4 
5 #define set_trace_bit(dbg, thread) modify_trace_bit (dbg, thread, 1)
6 #define clear_trace_bit(dbg, thread) modify_trace_bit (dbg, thread, 0)
7 
8 
9 #if defined __i386__ || __x86_64__ // intel processors
10 
11 /* Set/clear bit 8 (Trap Flag) of the EFLAGS processor control
12    register to enable/disable single-step mode.
13    ENABLE is a boolean, indicating whether to set (1) the Trap Flag
14    or clear it (0).  */
15 
modify_trace_bit(RDebug * dbg,xnu_thread_t * th,int enable)16 static bool modify_trace_bit(RDebug *dbg, xnu_thread_t *th, int enable) {
17 	R_REG_T *state;
18 	int ret;
19 	ret = xnu_thread_get_gpr (dbg, th);
20 	if (!ret) {
21 		eprintf ("error to get gpr registers in trace bit intel\n");
22 		return false;
23 	}
24 	state = (R_REG_T *)&th->gpr;
25 	if (state->tsh.flavor == x86_THREAD_STATE32) {
26 		state->uts.ts32.__eflags = (state->uts.ts32.__eflags & \
27 					~0x100UL) | (enable ? 0x100UL : 0);
28 	} else if (state->tsh.flavor == x86_THREAD_STATE64) {
29 		state->uts.ts64.__rflags = (state->uts.ts64.__rflags & \
30 					~0x100UL) | (enable ? 0x100UL : 0);
31 	} else {
32 		eprintf ("Invalid bit size\n");
33 		return false;
34 	}
35 	if (!xnu_thread_set_gpr (dbg, th)) {
36 		eprintf ("error xnu_thread_set_gpr in modify_trace_bit intel\n");
37 		return false;
38 	}
39 	return true;
40 }
41 
42 #elif __POWERPC__ //ppc processor
43 //XXX poor support at this stage i don't care so much. Once intel and arm done it could be done
44 //TODO add better support for ppc
modify_trace_bit(RDebug * dbg,void * th,int enable)45 static bool modify_trace_bit(RDebug *dbg, void *th, int enable) {
46 	return false;
47 }
48 #if 0
49 static bool modify_trace_bit(RDebug *dbg, xnu_thread *th, int enable) {
50 	return false;
51 	R_REG_T state;
52 	unsigned int state_count = R_REG_STATE_SZ;
53 	kern_return_t kr;
54 	kr = thread_get_state (th->tid, R_REG_STATE_T,
55 			(thread_state_t)&state, &state_count);
56 	if (kr != KERN_SUCCESS) {
57 		eprintf ("error modify_trace_bit\n");
58 		return false;
59 	}
60 	state.srr1 = (state.srr1 & ~0x400UL) | (enable ? 0x400UL : 0);
61 	kr = thread_set_state (th->tid, R_REG_STATE_T,
62 			(thread_state_t)&state, state_count);
63 	if (kr != KERN_SUCCESS) {
64 		eprintf ("Error to set thread state modificy_trace_bit ppc\n");
65 		return false;
66 	}
67 	return true;
68 }
69 #endif
70 
71 #elif __arm || __arm64 || __aarch64//arm processor
72 
73 // BCR address match type
74 #define BCR_M_IMVA_MATCH        ((uint32_t)(0u << 21))
75 #define BCR_M_CONTEXT_ID_MATCH  ((uint32_t)(1u << 21))
76 #define BCR_M_IMVA_MISMATCH     ((uint32_t)(2u << 21))
77 #define BCR_M_RESERVED          ((uint32_t)(3u << 21))
78 
79 // Link a BVR/BCR or WVR/WCR pair to another
80 #define E_ENABLE_LINKING	((uint32_t)(1u << 20))
81 
82 // Byte Address Select
83 #define BAS_IMVA_PLUS_0		((uint32_t)(1u << 5))
84 #define BAS_IMVA_PLUS_1		((uint32_t)(1u << 6))
85 #define BAS_IMVA_PLUS_2		((uint32_t)(1u << 7))
86 #define BAS_IMVA_PLUS_3		((uint32_t)(1u << 8))
87 #define BAS_IMVA_0_1		((uint32_t)(3u << 5))
88 #define BAS_IMVA_2_3		((uint32_t)(3u << 7))
89 #define BAS_IMVA_ALL		((uint32_t)(0xfu << 5))
90 
91 // Break only in privileged or user mode
92 #define S_RSVD			((uint32_t)(0u << 1))
93 #define S_PRIV			((uint32_t)(1u << 1))
94 #define S_USER			((uint32_t)(2u << 1))
95 #define S_PRIV_USER		((S_PRIV) | (S_USER))
96 
97 #define BCR_ENABLE		((uint32_t)(1u))
98 #define WCR_ENABLE		((uint32_t)(1u))
99 
100 // Watchpoint load/store
101 #define WCR_LOAD		((uint32_t)(1u << 3))
102 #define WCR_STORE		((uint32_t)(1u << 4))
103 
104 // Single instruction step
105 // (SS bit in the MDSCR_EL1 register)
106 #define SS_ENABLE ((uint32_t)(1u))
107 
108 #if __arm || __arm__ || __armv7 || __armv7__
is_thumb_32(ut16 op)109 static bool is_thumb_32(ut16 op) {
110 	return (((op & 0xE000) == 0xE000) && (op & 0x1800));
111 }
112 #endif
113 
modify_trace_bit(RDebug * dbg,xnu_thread_t * th,int enable)114 static int modify_trace_bit(RDebug *dbg, xnu_thread_t *th, int enable) {
115 	int i = 0;
116 	int ret = xnu_thread_get_drx (dbg, th);
117 	if (!ret) {
118 		eprintf ("error to get drx registers modificy_trace_bit arm\n");
119 		return false;
120 	}
121 #if __arm64 || __arm64__ || __aarch64 || __aarch64__
122 	if (th->flavor == ARM_DEBUG_STATE32) {
123 		arm_debug_state32_t *state = &th->debug.drx32;
124 		if (enable) {
125 			state->__mdscr_el1 = state->__mdscr_el1 | SS_ENABLE;
126 		} else {
127 			state->__mdscr_el1 = state->__mdscr_el1 & ~SS_ENABLE;
128 		}
129 	} else if (th->flavor == ARM_DEBUG_STATE64) {
130 		arm_debug_state64_t *state = &th->debug.drx64;
131 		if (enable) {
132 			state->__mdscr_el1 = state->__mdscr_el1 | SS_ENABLE;
133 		} else {
134 			state->__mdscr_el1 = state->__mdscr_el1 & ~SS_ENABLE;
135 		}
136 	} else
137 #elif __arm || __arm__ || __armv7 || __armv7__
138 	if (th->flavor == ARM_DEBUG_STATE) {
139 		arm_debug_state_t *state = &th->debug.drx;
140 		R_REG_T *regs;
141 		ret = xnu_thread_get_gpr (dbg, th);
142 		if (!ret) {
143 			eprintf ("error to get gpr register modificy_trace_bit arm\n");
144 			return false;
145 		}
146 		regs = (R_REG_T*)&th->gpr;
147 		if (enable) {
148 			static ut64 chained_address = 0;
149 			RIOBind *bio = &dbg->iob;
150 			//set a breakpoint that will stop when the PC doesn't
151 			//match the current one
152 			//set the current PC as the breakpoint address
153 			if (chained_address) {
154 				state->__bvr[i] = chained_address & 0xFFFFFFFCu;
155 				chained_address = 0;
156 			} else {
157 				state->__bvr[i] = regs->ts_32.__pc & 0xFFFFFFFCu;
158 			}
159 			state->__bcr[i] = BCR_M_IMVA_MISMATCH |  // stop on
160 								 // address
161 								 // mismatch
162 					  S_USER |  // stop only in user mode
163 					  BCR_ENABLE;  // enable this breakpoint
164 			if (regs->ts_32.__cpsr & 0x20) {
165 				ut16 op;
166 				// Thumb breakpoint
167 				if (regs->ts_32.__pc & 2)
168 					state->__bcr[i] |= BAS_IMVA_2_3;
169 				else
170 					state->__bcr[i] |= BAS_IMVA_0_1;
171 				if (bio->read_at (bio->io, regs->ts_32.__pc, (void *)&op, 2) < 1) {
172 					eprintf ("Failed to read opcode modify_trace_bit\n");
173 					return false;
174 				}
175 				if (is_thumb_32 (op)) {
176 					chained_address = regs->ts_32.__pc + 2;
177 				} else {
178 					// Extend the number of bits to ignore for the mismatch
179 					state->__bcr[i] |= BAS_IMVA_ALL;
180 				}
181 			} else {
182 				// ARM breakpoint
183 				state->__bcr[i] |= BAS_IMVA_ALL; // Stop when any address bits change
184 			}
185 			//disable bits
186 			for (i = i + 1; i < 16; i++) {
187 				//Disable all others
188 				state->__bcr[i] = 0;
189 				state->__bvr[i] = 0;
190 			}
191 		} else {
192 			if (state->__bcr[i] & BCR_ENABLE) {
193 				state->__bvr[i] = 0;
194 				state->__bcr[i] = 0;
195 			}
196 		}
197 	} else
198 #endif
199 	{
200 		eprintf ("Bad flavor modificy_trace_bit arm\n");
201 		return false;
202 	}
203 	//set state
204 	if (!xnu_thread_set_drx (dbg, th)) {
205 		eprintf ("error to set drx modificy_trace_bit arm\n");
206 		return false;
207 	}
208 	return true;
209 }
210 
211 #elif __POWERPC__
212 	// no need to do this here
modify_trace_bit(RDebug * dbg,xnu_thread * th,int enable)213 static int modify_trace_bit(RDebug *dbg, xnu_thread *th, int enable) {
214 	return true;
215 }
216 #else
217 #error "unknown architecture"
218 #endif
219 
220 // TODO: Tuck this into RDebug; `void *user` seems like a good candidate.
221 static xnu_exception_info ex = { { 0 } };
222 
xnu_restore_exception_ports(int pid)223 static bool xnu_restore_exception_ports (int pid) {
224 	kern_return_t kr;
225 	int i;
226 	task_t task = pid_to_task (pid);
227 	if (!task)
228 		return false;
229 	for (i = 0; i < ex.count; i++) {
230 		kr = task_set_exception_ports (task, ex.masks[i], ex.ports[i],
231 					       ex.behaviors[i], ex.flavors[i]);
232 		if (kr != KERN_SUCCESS) {
233 			eprintf ("fail to restore exception ports\n");
234 			return false;
235 		}
236 	}
237 	kr = mach_port_deallocate (mach_task_self (), ex.exception_port);
238 	if (kr != KERN_SUCCESS) {
239 		eprintf ("failed to deallocate exception port\n");
240 		return false;
241 	}
242 	return true;
243 }
244 
245 
246 //TODO review more closely we are failing here
encode_reply(mig_reply_error_t * reply,mach_msg_header_t * hdr,int code)247 static void encode_reply(mig_reply_error_t *reply, mach_msg_header_t *hdr, int code) {
248 	mach_msg_header_t *rh = &reply->Head;
249 	rh->msgh_bits = MACH_MSGH_BITS (MACH_MSGH_BITS_REMOTE(hdr->msgh_bits), 0);
250 	rh->msgh_remote_port = hdr->msgh_remote_port;
251 	rh->msgh_size = (mach_msg_size_t) sizeof (mig_reply_error_t);
252 	rh->msgh_local_port = MACH_PORT_NULL;
253 	rh->msgh_id = hdr->msgh_id + 100;
254 	reply->NDR = NDR_record;
255 	reply->RetCode = code;
256 }
257 
validate_mach_message(RDebug * dbg,exc_msg * msg)258 static bool validate_mach_message (RDebug *dbg, exc_msg *msg) {
259 	kern_return_t kr;
260 #if __POWERPC__
261 	return false;
262 #else
263 	/*check if the message is for us*/
264 	if (msg->hdr.msgh_local_port != ex.exception_port) {
265 		return false;
266 	}
267 	/*gdb from apple check this so why not us*/
268 	if (!(msg->hdr.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
269 		return false;
270 	}
271 	/*mach exception we are interested*/
272 	//XXX for i386 this id seems to be different
273 	if (msg->hdr.msgh_id > 2405 || msg->hdr.msgh_id < 2401) {
274 		return false;
275 	}
276 	/* check descriptors.  */
277 	if (msg->hdr.msgh_size <
278 	    sizeof (mach_msg_header_t) + sizeof (mach_msg_body_t) +
279 		    2 * sizeof (mach_msg_port_descriptor_t) +
280 		    sizeof (NDR_record_t) + sizeof (exception_type_t) +
281 		    sizeof (mach_msg_type_number_t) +
282 		    sizeof (mach_exception_data_t))
283 		return false;
284 	/* check data representation.  */
285 	if (msg->NDR.mig_vers != NDR_PROTOCOL_2_0 ||
286 	    msg->NDR.if_vers != NDR_PROTOCOL_2_0 ||
287 	    msg->NDR.mig_encoding != NDR_record.mig_encoding ||
288 	    msg->NDR.int_rep != NDR_record.int_rep ||
289 	    msg->NDR.char_rep != NDR_record.char_rep ||
290 	    msg->NDR.float_rep != NDR_record.float_rep) {
291 		return false;
292 	}
293 	if (pid_to_task (dbg->pid) != msg->task.name) {
294 		//we receive a exception from an unknown process this could
295 		//happen if the child fork, as the created process will inherit
296 		//its exception port
297 		/*we got new rights to the task, get rid of it.*/
298 		kr = mach_port_deallocate (mach_task_self (), msg->task.name);
299 		if (kr != KERN_SUCCESS) {
300 			eprintf ("validate_mach_message: failed to deallocate task port\n");
301 		}
302 		kr = mach_port_deallocate (mach_task_self (), msg->thread.name);
303 		if (kr != KERN_SUCCESS) {
304 			eprintf ("validate_mach_message2: failed to deallocated task port\n");
305 		}
306 		return false;
307 	}
308 	return true;
309 #endif
310 }
311 
handle_dead_notify(RDebug * dbg,exc_msg * msg)312 static bool handle_dead_notify (RDebug *dbg, exc_msg *msg) {
313 	if (msg->hdr.msgh_id == 0x48) {
314 		dbg->pid = -1;
315 		return true;
316 	}
317 	return false;
318 }
319 
handle_exception_message(RDebug * dbg,exc_msg * msg,int * ret_code)320 static int handle_exception_message (RDebug *dbg, exc_msg *msg, int *ret_code) {
321 	int ret = R_DEBUG_REASON_UNKNOWN;
322 	kern_return_t kr;
323 	*ret_code = KERN_SUCCESS;
324 	switch (msg->exception) {
325 	case EXC_BAD_ACCESS:
326 		ret = R_DEBUG_REASON_SEGFAULT;
327 		*ret_code = KERN_FAILURE;
328 		kr = task_suspend (msg->task.name);
329 		if (kr != KERN_SUCCESS) {
330 			eprintf ("failed to suspend task bad access\n");
331 		}
332 		eprintf ("EXC_BAD_ACCESS\n");
333 		break;
334 	case EXC_BAD_INSTRUCTION:
335 		ret = R_DEBUG_REASON_ILLEGAL;
336 		*ret_code = KERN_FAILURE;
337 		kr = task_suspend (msg->task.name);
338 		if (kr != KERN_SUCCESS) {
339 			eprintf ("failed to suspend task bad instruction\n");
340 		}
341 		eprintf ("EXC_BAD_INSTRUCTION\n");
342 		break;
343 	case EXC_ARITHMETIC:
344 		eprintf ("EXC_ARITHMETIC\n");
345 		break;
346 	case EXC_EMULATION:
347 		eprintf ("EXC_EMULATION\n");
348 		break;
349 	case EXC_SOFTWARE:
350 		eprintf ("EXC_SOFTWARE\n");
351 		break;
352 	case EXC_BREAKPOINT:
353 		kr = task_suspend (msg->task.name);
354 		if (kr != KERN_SUCCESS) {
355 			eprintf ("failed to suspend task breakpoint\n");
356 		}
357 		ret = R_DEBUG_REASON_BREAKPOINT;
358 		break;
359 	default:
360 		eprintf ("UNKNOWN\n");
361 		break;
362 	}
363 	kr = mach_port_deallocate (mach_task_self (), msg->task.name);
364 	if (kr != KERN_SUCCESS) {
365 		eprintf ("failed to deallocate task port\n");
366 	}
367 	kr = mach_port_deallocate (mach_task_self (), msg->thread.name);
368 	if (kr != KERN_SUCCESS) {
369 		eprintf ("failed to deallocated task port\n");
370 	}
371 	return ret;
372 }
373 
374 //TODO improve this code
__xnu_wait(RDebug * dbg,int pid)375 static int __xnu_wait (RDebug *dbg, int pid) {
376 	// here comes the important thing
377 	kern_return_t kr;
378 	int ret_code, reason = R_DEBUG_REASON_UNKNOWN;
379 	mig_reply_error_t reply;
380 	bool ret;
381 	exc_msg msg;
382 	if (!dbg) {
383 		return reason;
384 	}
385 	msg.hdr.msgh_local_port = ex.exception_port;
386 	msg.hdr.msgh_size = sizeof (exc_msg);
387 	for (;;) {
388 		kr = mach_msg (
389 			&msg.hdr,
390 			MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0,
391 			sizeof (exc_msg), ex.exception_port,
392 			MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
393 		if (kr == MACH_RCV_INTERRUPTED) {
394 			reason = R_DEBUG_REASON_MACH_RCV_INTERRUPTED;
395 			break;
396 		} else if (kr != MACH_MSG_SUCCESS) {
397 			eprintf ("message didn't succeeded\n");
398 			break;
399 		}
400 		ret = validate_mach_message (dbg, &msg);
401 		if (!ret) {
402 			ret = handle_dead_notify (dbg, &msg);
403 			if (ret) {
404 				reason = R_DEBUG_REASON_DEAD;
405 				break;
406 			}
407 		}
408 		if (!ret) {
409 			encode_reply (&reply, &msg.hdr, KERN_FAILURE);
410 			kr = mach_msg (&reply.Head, MACH_SEND_MSG | MACH_SEND_INTERRUPT,
411 					reply.Head.msgh_size, 0,
412 					MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
413 					MACH_PORT_NULL);
414 			if (reply.Head.msgh_remote_port != 0 && kr != MACH_MSG_SUCCESS) {
415 				kr = mach_port_deallocate(mach_task_self (), reply.Head.msgh_remote_port);
416 				if (kr != KERN_SUCCESS) {
417 					eprintf ("failed to deallocate reply port\n");
418 				}
419 			}
420 			continue;
421 		}
422 
423 		reason = handle_exception_message (dbg, &msg, &ret_code);
424 		encode_reply (&reply, &msg.hdr, ret_code);
425 		kr = mach_msg (&reply.Head, MACH_SEND_MSG | MACH_SEND_INTERRUPT,
426 				reply.Head.msgh_size, 0,
427 				MACH_PORT_NULL, 0,
428 				MACH_PORT_NULL);
429 		if (reply.Head.msgh_remote_port != 0 && kr != MACH_MSG_SUCCESS) {
430 			kr = mach_port_deallocate(mach_task_self (), reply.Head.msgh_remote_port);
431 			if (kr != KERN_SUCCESS)
432 				eprintf ("failed to deallocate reply port\n");
433 		}
434 		break; // to avoid infinite loops
435 	}
436 	dbg->stopaddr = r_debug_reg_get (dbg, "PC");
437 	return reason;
438 }
439 
xnu_create_exception_thread(RDebug * dbg)440 bool xnu_create_exception_thread(RDebug *dbg) {
441 #if __POWERPC__
442 	return false;
443 #else
444 	kern_return_t kr;
445 	mach_port_t exception_port = MACH_PORT_NULL;
446 	mach_port_t req_port;
447         // Got the mach port for the current process
448 	mach_port_t task_self = mach_task_self ();
449 	task_t task = pid_to_task (dbg->pid);
450 	if (!task) {
451 		eprintf ("error to get task for the debuggee process"
452 			" xnu_start_exception_thread\n");
453 		return false;
454 	}
455 	r_debug_ptrace (dbg, PT_ATTACHEXC, dbg->pid, 0, 0);
456 	if (!MACH_PORT_VALID (task_self)) {
457 		eprintf ("error to get the task for the current process"
458 			" xnu_start_exception_thread\n");
459 		return false;
460 	}
461         // Allocate an exception port that we will use to track our child process
462         kr = mach_port_allocate (task_self, MACH_PORT_RIGHT_RECEIVE,
463 				&exception_port);
464 	RETURN_ON_MACH_ERROR ("error to allocate mach_port exception\n", false);
465         // Add the ability to send messages on the new exception port
466 	kr = mach_port_insert_right (task_self, exception_port, exception_port,
467 				     MACH_MSG_TYPE_MAKE_SEND);
468 	RETURN_ON_MACH_ERROR ("error to allocate insert right\n", false);
469 	// Atomically swap out (and save) the child process's exception ports
470 	// for the one we just created. We'll want to receive all exceptions.
471 	ex.count = (sizeof (ex.ports) / sizeof (*ex.ports));
472 	kr = task_swap_exception_ports (task, EXC_MASK_ALL, exception_port,
473 		EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE,
474 		ex.masks, &ex.count, ex.ports, ex.behaviors, ex.flavors);
475 	RETURN_ON_MACH_ERROR ("failed to swap exception ports\n", false);
476 	//get notification when process die
477 	kr = mach_port_request_notification (task_self, task, MACH_NOTIFY_DEAD_NAME,
478 		 0, exception_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &req_port);
479 	if (kr != KERN_SUCCESS) {
480 		eprintf ("Termination notification request failed\n");
481 	}
482 	ex.exception_port = exception_port;
483 	return true;
484 #endif
485 }
486