X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/6d2010ae8f7a6078e10b361c6962983bab233e0f..04b8595b18b1b41ac7a206e4b3d51a635f8413d7:/bsd/dev/dtrace/dtrace_subr.c diff --git a/bsd/dev/dtrace/dtrace_subr.c b/bsd/dev/dtrace/dtrace_subr.c index c3a69c48f..aa8fb6c1a 100644 --- a/bsd/dev/dtrace/dtrace_subr.c +++ b/bsd/dev/dtrace/dtrace_subr.c @@ -34,13 +34,23 @@ #include #include #include +#include #include +#include +#include -#if defined(__APPLE__) -/* Solaris proc_t is the struct. Darwin's proc_t is a pointer to it. */ -#define proc_t struct proc /* Steer clear of the Darwin typedef for proc_t */ +#if CONFIG_CSR +#include +#include #endif +/* + * APPLE NOTE: Solaris proc_t is the struct. + * Darwin's proc_t is a pointer to it. + */ +#define proc_t struct proc /* Steer clear of the Darwin typedef for proc_t */ + + /* Copied from an arch specific dtrace_subr.c. */ int (*dtrace_fasttrap_probe_ptr)(struct regs *); @@ -49,14 +59,9 @@ int (*dtrace_fasttrap_probe_ptr)(struct regs *); * They're assigned in dtrace.c but Darwin never calls them. */ void (*dtrace_cpu_init)(processorid_t); -#if !defined(__APPLE__) -void (*dtrace_modload)(struct modctl *); -void (*dtrace_modunload)(struct modctl *); -#else -int (*dtrace_modload)(struct kmod_info *); +int (*dtrace_modload)(struct kmod_info *, uint32_t); int (*dtrace_modunload)(struct kmod_info *); void (*dtrace_helpers_cleanup)(proc_t *); -#endif /*__APPLE__*/ void (*dtrace_helpers_fork)(proc_t *, proc_t *); void (*dtrace_cpustart_init)(void); void (*dtrace_cpustart_fini)(void); @@ -83,16 +88,96 @@ void (*dtrace_fasttrap_exit_ptr)(proc_t *); void dtrace_fasttrap_fork(proc_t *p, proc_t *cp) { -#if !defined(__APPLE__) - ASSERT(p->p_proc_flag & P_PR_LOCK); - ASSERT(p->p_dtrace_count > 0); -#endif /* __APPLE__ */ - if (dtrace_fasttrap_fork_ptr) { (*dtrace_fasttrap_fork_ptr)(p, cp); } } + +/* + * DTrace wait for process execution + * + * This feature is using a list of entries, each entry containing a pointer + * on a process description. The description is provided by a client, and it + * contains the command we want to wait for along with a reserved space for + * the caught process id. + * + * Once an awaited process has been spawned, it will be suspended before + * notifying the client. Once the client has been back to userland, it's its + * duty to resume the task. + */ + +lck_mtx_t dtrace_procwaitfor_lock; + +typedef struct dtrace_proc_awaited_entry { + struct dtrace_procdesc *pdesc; + LIST_ENTRY(dtrace_proc_awaited_entry) entries; +} dtrace_proc_awaited_entry_t; + +LIST_HEAD(listhead, dtrace_proc_awaited_entry) dtrace_proc_awaited_head + = LIST_HEAD_INITIALIZER(dtrace_proc_awaited_head); + +void (*dtrace_proc_waitfor_exec_ptr)(proc_t*) = NULL; + +static void +dtrace_proc_exec_notification(proc_t *p) { + dtrace_proc_awaited_entry_t *entry, *tmp; + + ASSERT(p); + ASSERT(p->p_pid != -1); + ASSERT(current_task() != p->task); + + lck_mtx_lock(&dtrace_procwaitfor_lock); + + /* + * For each entry, if it has not been matched with a process yet we + * try to match it with the newly created process. If they match, the + * entry is initialized with the process id and the process task is + * suspended. Finally, we wake up the client's waiting thread. + */ + LIST_FOREACH_SAFE(entry, &dtrace_proc_awaited_head, entries, tmp) { + if ((entry->pdesc->p_pid == -1) + && !strncmp(entry->pdesc->p_comm, &p->p_comm[0], sizeof(p->p_comm))) + { + entry->pdesc->p_pid = p->p_pid; + task_pidsuspend(p->task); + wakeup(entry); + } + } + + lck_mtx_unlock(&dtrace_procwaitfor_lock); +} + +int +dtrace_proc_waitfor(dtrace_procdesc_t* pdesc) { + dtrace_proc_awaited_entry_t entry; + int res; + + ASSERT(pdesc); + ASSERT(pdesc->p_comm); + + lck_mtx_lock(&dtrace_procwaitfor_lock); + + /* Initialize and insert the entry, then install the hook. */ + pdesc->p_pid = -1; + entry.pdesc = pdesc; + LIST_INSERT_HEAD(&dtrace_proc_awaited_head, &entry, entries); + dtrace_proc_waitfor_exec_ptr = &dtrace_proc_exec_notification; + + /* Sleep until the process has been executed */ + res = msleep(&entry, &dtrace_procwaitfor_lock, PCATCH, "dtrace_proc_waitfor", NULL); + + /* Remove the entry and the hook if it is not needed anymore. */ + LIST_REMOVE(&entry, entries); + if (LIST_EMPTY(&dtrace_proc_awaited_head)) + dtrace_proc_waitfor_exec_ptr = NULL; + + lck_mtx_unlock(&dtrace_procwaitfor_lock); + + return res; +} + + typedef struct dtrace_invop_hdlr { int (*dtih_func)(uintptr_t, uintptr_t *, uintptr_t); struct dtrace_invop_hdlr *dtih_next; @@ -155,3 +240,34 @@ dtrace_invop_remove(int (*func)(uintptr_t, uintptr_t *, uintptr_t)) kmem_free(hdlr, sizeof (dtrace_invop_hdlr_t)); } +/* + * Check if DTrace has been restricted by the current security policy. + */ +boolean_t +dtrace_is_restricted(void) +{ +#if CONFIG_CSR + if (csr_check(CSR_ALLOW_UNRESTRICTED_DTRACE) != 0) + return TRUE; +#endif + + return FALSE; +} + +/* + * Check if the process can be attached. + */ +boolean_t +dtrace_can_attach_to_proc(proc_t *proc) +{ +#pragma unused(proc) + ASSERT(proc != NULL); + +#if CONFIG_CSR + if ((cs_entitlement_flags(proc) & CS_GET_TASK_ALLOW) == 0) + return FALSE; +#endif + + return TRUE; +} +