1/* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22/* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27/* 28 * #pragma ident "@(#)dtrace_subr.c 1.8 07/06/05 SMI" 29 */ 30 31#include <stdarg.h> 32#include <string.h> 33#include <sys/malloc.h> 34#include <sys/time.h> 35#include <sys/dtrace.h> 36#include <sys/dtrace_impl.h> 37#include <sys/proc_internal.h> 38#include <kern/debug.h> 39#include <kern/sched_prim.h> 40#include <kern/task.h> 41 42#if CONFIG_CSR 43#include <sys/codesign.h> 44#include <sys/csr.h> 45#endif 46 47/* 48 * APPLE NOTE: Solaris proc_t is the struct. 49 * Darwin's proc_t is a pointer to it. 50 */ 51#define proc_t struct proc /* Steer clear of the Darwin typedef for proc_t */ 52 53 54/* Copied from an arch specific dtrace_subr.c. */ 55int (*dtrace_fasttrap_probe_ptr)(struct regs *); 56 57/* 58 * Following DTrace hooks are taken from Solaris' dtrace_subr.c 59 * They're assigned in dtrace.c but Darwin never calls them. 60 */ 61void (*dtrace_cpu_init)(processorid_t); 62int (*dtrace_modload)(struct kmod_info *, uint32_t); 63int (*dtrace_modunload)(struct kmod_info *); 64void (*dtrace_helpers_cleanup)(proc_t *); 65void (*dtrace_helpers_fork)(proc_t *, proc_t *); 66void (*dtrace_cpustart_init)(void); 67void (*dtrace_cpustart_fini)(void); 68 69void (*dtrace_debugger_init)(void); 70void (*dtrace_debugger_fini)(void); 71 72dtrace_vtime_state_t dtrace_vtime_active = 0; 73dtrace_cacheid_t dtrace_predcache_id = DTRACE_CACHEIDNONE + 1; 74 75void (*dtrace_fasttrap_fork_ptr)(proc_t *, proc_t *); 76void (*dtrace_fasttrap_exec_ptr)(proc_t *); 77void (*dtrace_fasttrap_exit_ptr)(proc_t *); 78 79/* 80 * This function is called by cfork() in the event that it appears that 81 * there may be dtrace tracepoints active in the parent process's address 82 * space. This first confirms the existence of dtrace tracepoints in the 83 * parent process and calls into the fasttrap module to remove the 84 * corresponding tracepoints from the child. By knowing that there are 85 * existing tracepoints, and ensuring they can't be removed, we can rely 86 * on the fasttrap module remaining loaded. 87 */ 88void 89dtrace_fasttrap_fork(proc_t *p, proc_t *cp) 90{ 91 if (dtrace_fasttrap_fork_ptr) { 92 (*dtrace_fasttrap_fork_ptr)(p, cp); 93 } 94} 95 96 97/* 98 * DTrace wait for process execution 99 * 100 * This feature is using a list of entries, each entry containing a pointer 101 * on a process description. The description is provided by a client, and it 102 * contains the command we want to wait for along with a reserved space for 103 * the caught process id. 104 * 105 * Once an awaited process has been spawned, it will be suspended before 106 * notifying the client. Once the client has been back to userland, it's its 107 * duty to resume the task. 108 */ 109 110lck_mtx_t dtrace_procwaitfor_lock; 111 112typedef struct dtrace_proc_awaited_entry { 113 struct dtrace_procdesc *pdesc; 114 LIST_ENTRY(dtrace_proc_awaited_entry) entries; 115} dtrace_proc_awaited_entry_t; 116 117LIST_HEAD(listhead, dtrace_proc_awaited_entry) dtrace_proc_awaited_head 118 = LIST_HEAD_INITIALIZER(dtrace_proc_awaited_head); 119 120void (*dtrace_proc_waitfor_exec_ptr)(proc_t*) = NULL; 121 122static void 123dtrace_proc_exec_notification(proc_t *p) { 124 dtrace_proc_awaited_entry_t *entry, *tmp; 125 126 ASSERT(p); 127 ASSERT(p->p_pid != -1); 128 ASSERT(current_task() != p->task); 129 130 lck_mtx_lock(&dtrace_procwaitfor_lock); 131 132 /* 133 * For each entry, if it has not been matched with a process yet we 134 * try to match it with the newly created process. If they match, the 135 * entry is initialized with the process id and the process task is 136 * suspended. Finally, we wake up the client's waiting thread. 137 */ 138 LIST_FOREACH_SAFE(entry, &dtrace_proc_awaited_head, entries, tmp) { 139 if ((entry->pdesc->p_pid == -1) 140 && !strncmp(entry->pdesc->p_comm, &p->p_comm[0], sizeof(p->p_comm))) 141 { 142 entry->pdesc->p_pid = p->p_pid; 143 task_pidsuspend(p->task); 144 wakeup(entry); 145 } 146 } 147 148 lck_mtx_unlock(&dtrace_procwaitfor_lock); 149} 150 151int 152dtrace_proc_waitfor(dtrace_procdesc_t* pdesc) { 153 dtrace_proc_awaited_entry_t entry; 154 int res; 155 156 ASSERT(pdesc); 157 ASSERT(pdesc->p_comm); 158 159 lck_mtx_lock(&dtrace_procwaitfor_lock); 160 161 /* Initialize and insert the entry, then install the hook. */ 162 pdesc->p_pid = -1; 163 entry.pdesc = pdesc; 164 LIST_INSERT_HEAD(&dtrace_proc_awaited_head, &entry, entries); 165 dtrace_proc_waitfor_exec_ptr = &dtrace_proc_exec_notification; 166 167 /* Sleep until the process has been executed */ 168 res = msleep(&entry, &dtrace_procwaitfor_lock, PCATCH, "dtrace_proc_waitfor", NULL); 169 170 /* Remove the entry and the hook if it is not needed anymore. */ 171 LIST_REMOVE(&entry, entries); 172 if (LIST_EMPTY(&dtrace_proc_awaited_head)) 173 dtrace_proc_waitfor_exec_ptr = NULL; 174 175 lck_mtx_unlock(&dtrace_procwaitfor_lock); 176 177 return res; 178} 179 180 181typedef struct dtrace_invop_hdlr { 182 int (*dtih_func)(uintptr_t, uintptr_t *, uintptr_t); 183 struct dtrace_invop_hdlr *dtih_next; 184} dtrace_invop_hdlr_t; 185 186dtrace_invop_hdlr_t *dtrace_invop_hdlr; 187 188int 189dtrace_invop(uintptr_t, uintptr_t *, uintptr_t); 190 191int 192dtrace_invop(uintptr_t addr, uintptr_t *stack, uintptr_t eax) 193{ 194 dtrace_invop_hdlr_t *hdlr; 195 int rval; 196 197 for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next) { 198 if ((rval = hdlr->dtih_func(addr, stack, eax)) != 0) 199 return (rval); 200 } 201 202 return (0); 203} 204 205void 206dtrace_invop_add(int (*func)(uintptr_t, uintptr_t *, uintptr_t)) 207{ 208 dtrace_invop_hdlr_t *hdlr; 209 210 hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP); 211 hdlr->dtih_func = func; 212 hdlr->dtih_next = dtrace_invop_hdlr; 213 dtrace_invop_hdlr = hdlr; 214} 215 216void 217dtrace_invop_remove(int (*func)(uintptr_t, uintptr_t *, uintptr_t)) 218{ 219 dtrace_invop_hdlr_t *hdlr = dtrace_invop_hdlr, *prev = NULL; 220 221 for (;;) { 222 if (hdlr == NULL) 223 panic("attempt to remove non-existent invop handler"); 224 225 if (hdlr->dtih_func == func) 226 break; 227 228 prev = hdlr; 229 hdlr = hdlr->dtih_next; 230 } 231 232 if (prev == NULL) { 233 ASSERT(dtrace_invop_hdlr == hdlr); 234 dtrace_invop_hdlr = hdlr->dtih_next; 235 } else { 236 ASSERT(dtrace_invop_hdlr != hdlr); 237 prev->dtih_next = hdlr->dtih_next; 238 } 239 240 kmem_free(hdlr, sizeof (dtrace_invop_hdlr_t)); 241} 242 243/* 244 * Check if DTrace has been restricted by the current security policy. 245 */ 246boolean_t 247dtrace_is_restricted(void) 248{ 249#if CONFIG_CSR 250 if (csr_check(CSR_ALLOW_UNRESTRICTED_DTRACE) != 0) 251 return TRUE; 252#endif 253 254 return FALSE; 255} 256 257/* 258 * Check if the process can be attached. 259 */ 260boolean_t 261dtrace_can_attach_to_proc(proc_t *proc) 262{ 263#pragma unused(proc) 264 ASSERT(proc != NULL); 265 266#if CONFIG_CSR 267 if ((cs_entitlement_flags(proc) & CS_GET_TASK_ALLOW) == 0) 268 return FALSE; 269#endif 270 271 return TRUE; 272} 273 274