]>
Commit | Line | Data |
---|---|---|
2d21ac55 A |
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 | /* | |
b0d623f7 | 23 | * Copyright 2007 Sun Microsystems, Inc. All rights reserved. |
2d21ac55 A |
24 | * Use is subject to license terms. |
25 | */ | |
26 | ||
2d21ac55 A |
27 | #include <stdarg.h> |
28 | #include <string.h> | |
29 | #include <sys/malloc.h> | |
30 | #include <sys/time.h> | |
31 | #include <sys/dtrace.h> | |
32 | #include <sys/dtrace_impl.h> | |
fe8ab488 | 33 | #include <sys/proc_internal.h> |
3e170ce0 | 34 | #include <sys/vnode.h> |
2d21ac55 | 35 | #include <kern/debug.h> |
fe8ab488 A |
36 | #include <kern/sched_prim.h> |
37 | #include <kern/task.h> | |
2d21ac55 | 38 | |
fe8ab488 A |
39 | #if CONFIG_CSR |
40 | #include <sys/codesign.h> | |
41 | #include <sys/csr.h> | |
2d21ac55 A |
42 | #endif |
43 | ||
fe8ab488 A |
44 | /* |
45 | * APPLE NOTE: Solaris proc_t is the struct. | |
46 | * Darwin's proc_t is a pointer to it. | |
47 | */ | |
48 | #define proc_t struct proc /* Steer clear of the Darwin typedef for proc_t */ | |
49 | ||
50 | ||
2d21ac55 A |
51 | /* Copied from an arch specific dtrace_subr.c. */ |
52 | int (*dtrace_fasttrap_probe_ptr)(struct regs *); | |
53 | ||
54 | /* | |
55 | * Following DTrace hooks are taken from Solaris' dtrace_subr.c | |
56 | * They're assigned in dtrace.c but Darwin never calls them. | |
57 | */ | |
58 | void (*dtrace_cpu_init)(processorid_t); | |
316670eb | 59 | int (*dtrace_modload)(struct kmod_info *, uint32_t); |
6d2010ae | 60 | int (*dtrace_modunload)(struct kmod_info *); |
2d21ac55 | 61 | void (*dtrace_helpers_cleanup)(proc_t *); |
2d21ac55 A |
62 | void (*dtrace_helpers_fork)(proc_t *, proc_t *); |
63 | void (*dtrace_cpustart_init)(void); | |
64 | void (*dtrace_cpustart_fini)(void); | |
65 | ||
2d21ac55 A |
66 | void (*dtrace_debugger_init)(void); |
67 | void (*dtrace_debugger_fini)(void); | |
68 | ||
69 | dtrace_vtime_state_t dtrace_vtime_active = 0; | |
70 | dtrace_cacheid_t dtrace_predcache_id = DTRACE_CACHEIDNONE + 1; | |
71 | ||
72 | void (*dtrace_fasttrap_fork_ptr)(proc_t *, proc_t *); | |
73 | void (*dtrace_fasttrap_exec_ptr)(proc_t *); | |
74 | void (*dtrace_fasttrap_exit_ptr)(proc_t *); | |
75 | ||
76 | /* | |
77 | * This function is called by cfork() in the event that it appears that | |
78 | * there may be dtrace tracepoints active in the parent process's address | |
79 | * space. This first confirms the existence of dtrace tracepoints in the | |
80 | * parent process and calls into the fasttrap module to remove the | |
81 | * corresponding tracepoints from the child. By knowing that there are | |
82 | * existing tracepoints, and ensuring they can't be removed, we can rely | |
83 | * on the fasttrap module remaining loaded. | |
84 | */ | |
85 | void | |
86 | dtrace_fasttrap_fork(proc_t *p, proc_t *cp) | |
87 | { | |
2d21ac55 A |
88 | if (dtrace_fasttrap_fork_ptr) { |
89 | (*dtrace_fasttrap_fork_ptr)(p, cp); | |
90 | } | |
91 | } | |
92 | ||
fe8ab488 A |
93 | |
94 | /* | |
95 | * DTrace wait for process execution | |
96 | * | |
97 | * This feature is using a list of entries, each entry containing a pointer | |
98 | * on a process description. The description is provided by a client, and it | |
99 | * contains the command we want to wait for along with a reserved space for | |
100 | * the caught process id. | |
101 | * | |
102 | * Once an awaited process has been spawned, it will be suspended before | |
103 | * notifying the client. Once the client has been back to userland, it's its | |
104 | * duty to resume the task. | |
105 | */ | |
106 | ||
107 | lck_mtx_t dtrace_procwaitfor_lock; | |
108 | ||
109 | typedef struct dtrace_proc_awaited_entry { | |
110 | struct dtrace_procdesc *pdesc; | |
111 | LIST_ENTRY(dtrace_proc_awaited_entry) entries; | |
112 | } dtrace_proc_awaited_entry_t; | |
113 | ||
114 | LIST_HEAD(listhead, dtrace_proc_awaited_entry) dtrace_proc_awaited_head | |
115 | = LIST_HEAD_INITIALIZER(dtrace_proc_awaited_head); | |
116 | ||
117 | void (*dtrace_proc_waitfor_exec_ptr)(proc_t*) = NULL; | |
118 | ||
3e170ce0 A |
119 | static int |
120 | dtrace_proc_get_execpath(proc_t *p, char *buffer, int *maxlen) | |
121 | { | |
122 | int err = 0, vid = 0; | |
123 | vnode_t tvp = NULLVP, nvp = NULLVP; | |
124 | ||
125 | ASSERT(p); | |
126 | ASSERT(buffer); | |
127 | ASSERT(maxlen); | |
128 | ||
129 | if ((tvp = p->p_textvp) == NULLVP) | |
130 | return ESRCH; | |
131 | ||
132 | vid = vnode_vid(tvp); | |
133 | if ((err = vnode_getwithvid(tvp, vid)) != 0) | |
134 | return err; | |
135 | ||
136 | if ((err = vn_getpath_fsenter(tvp, buffer, maxlen)) != 0) | |
137 | return err; | |
138 | vnode_put(tvp); | |
139 | ||
140 | if ((err = vnode_lookup(buffer, 0, &nvp, vfs_context_current())) != 0) | |
141 | return err; | |
142 | if (nvp != NULLVP) | |
143 | vnode_put(nvp); | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
148 | ||
fe8ab488 A |
149 | static void |
150 | dtrace_proc_exec_notification(proc_t *p) { | |
151 | dtrace_proc_awaited_entry_t *entry, *tmp; | |
3e170ce0 | 152 | static char execpath[MAXPATHLEN]; |
fe8ab488 A |
153 | |
154 | ASSERT(p); | |
155 | ASSERT(p->p_pid != -1); | |
156 | ASSERT(current_task() != p->task); | |
157 | ||
158 | lck_mtx_lock(&dtrace_procwaitfor_lock); | |
159 | ||
fe8ab488 | 160 | LIST_FOREACH_SAFE(entry, &dtrace_proc_awaited_head, entries, tmp) { |
3e170ce0 A |
161 | /* By default consider we're using p_comm. */ |
162 | char *pname = p->p_comm; | |
163 | ||
164 | /* Already matched with another process. */ | |
165 | if ((entry->pdesc->p_pid != -1)) | |
166 | continue; | |
167 | ||
168 | /* p_comm is too short, use the execpath. */ | |
169 | if (entry->pdesc->p_name_length >= MAXCOMLEN) { | |
170 | /* | |
171 | * Retrieve the executable path. After the call, length contains | |
172 | * the length of the string + 1. | |
173 | */ | |
174 | int length = sizeof(execpath); | |
175 | if (dtrace_proc_get_execpath(p, execpath, &length) != 0) | |
176 | continue; | |
177 | /* Move the cursor to the position after the last / */ | |
178 | pname = &execpath[length - 1]; | |
179 | while (pname != execpath && *pname != '/') | |
180 | pname--; | |
181 | pname = (*pname == '/') ? pname + 1 : pname; | |
182 | } | |
183 | ||
184 | if (!strcmp(entry->pdesc->p_name, pname)) { | |
fe8ab488 A |
185 | entry->pdesc->p_pid = p->p_pid; |
186 | task_pidsuspend(p->task); | |
187 | wakeup(entry); | |
188 | } | |
189 | } | |
190 | ||
191 | lck_mtx_unlock(&dtrace_procwaitfor_lock); | |
192 | } | |
193 | ||
194 | int | |
195 | dtrace_proc_waitfor(dtrace_procdesc_t* pdesc) { | |
196 | dtrace_proc_awaited_entry_t entry; | |
197 | int res; | |
198 | ||
199 | ASSERT(pdesc); | |
3e170ce0 A |
200 | ASSERT(pdesc->p_name); |
201 | ||
202 | /* | |
203 | * Never trust user input, compute the length of the process name and ensure the | |
204 | * string is null terminated. | |
205 | */ | |
206 | pdesc->p_name_length = strnlen(pdesc->p_name, sizeof(pdesc->p_name)); | |
207 | if (pdesc->p_name_length >= (int) sizeof(pdesc->p_name)) | |
208 | return -1; | |
fe8ab488 A |
209 | |
210 | lck_mtx_lock(&dtrace_procwaitfor_lock); | |
211 | ||
212 | /* Initialize and insert the entry, then install the hook. */ | |
213 | pdesc->p_pid = -1; | |
214 | entry.pdesc = pdesc; | |
215 | LIST_INSERT_HEAD(&dtrace_proc_awaited_head, &entry, entries); | |
216 | dtrace_proc_waitfor_exec_ptr = &dtrace_proc_exec_notification; | |
217 | ||
218 | /* Sleep until the process has been executed */ | |
219 | res = msleep(&entry, &dtrace_procwaitfor_lock, PCATCH, "dtrace_proc_waitfor", NULL); | |
220 | ||
221 | /* Remove the entry and the hook if it is not needed anymore. */ | |
222 | LIST_REMOVE(&entry, entries); | |
223 | if (LIST_EMPTY(&dtrace_proc_awaited_head)) | |
224 | dtrace_proc_waitfor_exec_ptr = NULL; | |
225 | ||
226 | lck_mtx_unlock(&dtrace_procwaitfor_lock); | |
227 | ||
228 | return res; | |
229 | } | |
230 | ||
231 | ||
2d21ac55 A |
232 | typedef struct dtrace_invop_hdlr { |
233 | int (*dtih_func)(uintptr_t, uintptr_t *, uintptr_t); | |
234 | struct dtrace_invop_hdlr *dtih_next; | |
235 | } dtrace_invop_hdlr_t; | |
236 | ||
237 | dtrace_invop_hdlr_t *dtrace_invop_hdlr; | |
238 | ||
239 | int | |
240 | dtrace_invop(uintptr_t, uintptr_t *, uintptr_t); | |
241 | ||
242 | int | |
243 | dtrace_invop(uintptr_t addr, uintptr_t *stack, uintptr_t eax) | |
244 | { | |
245 | dtrace_invop_hdlr_t *hdlr; | |
246 | int rval; | |
247 | ||
248 | for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next) { | |
249 | if ((rval = hdlr->dtih_func(addr, stack, eax)) != 0) | |
250 | return (rval); | |
251 | } | |
252 | ||
253 | return (0); | |
254 | } | |
255 | ||
256 | void | |
257 | dtrace_invop_add(int (*func)(uintptr_t, uintptr_t *, uintptr_t)) | |
258 | { | |
259 | dtrace_invop_hdlr_t *hdlr; | |
260 | ||
261 | hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP); | |
262 | hdlr->dtih_func = func; | |
263 | hdlr->dtih_next = dtrace_invop_hdlr; | |
264 | dtrace_invop_hdlr = hdlr; | |
265 | } | |
266 | ||
267 | void | |
268 | dtrace_invop_remove(int (*func)(uintptr_t, uintptr_t *, uintptr_t)) | |
269 | { | |
270 | dtrace_invop_hdlr_t *hdlr = dtrace_invop_hdlr, *prev = NULL; | |
271 | ||
272 | for (;;) { | |
273 | if (hdlr == NULL) | |
274 | panic("attempt to remove non-existent invop handler"); | |
275 | ||
276 | if (hdlr->dtih_func == func) | |
277 | break; | |
278 | ||
279 | prev = hdlr; | |
280 | hdlr = hdlr->dtih_next; | |
281 | } | |
282 | ||
283 | if (prev == NULL) { | |
284 | ASSERT(dtrace_invop_hdlr == hdlr); | |
285 | dtrace_invop_hdlr = hdlr->dtih_next; | |
286 | } else { | |
287 | ASSERT(dtrace_invop_hdlr != hdlr); | |
288 | prev->dtih_next = hdlr->dtih_next; | |
289 | } | |
290 | ||
291 | kmem_free(hdlr, sizeof (dtrace_invop_hdlr_t)); | |
292 | } | |
293 | ||
cb323159 A |
294 | void* |
295 | dtrace_ptrauth_strip(void *ptr, uint64_t key) | |
296 | { | |
297 | #pragma unused(key) | |
298 | #if __has_feature(ptrauth_calls) | |
299 | /* | |
300 | * The key argument to ptrauth_strip needs to be a compile-time | |
301 | * constant | |
302 | */ | |
303 | switch (key) { | |
304 | case ptrauth_key_asia: | |
305 | return ptrauth_strip(ptr, ptrauth_key_asia); | |
306 | case ptrauth_key_asib: | |
307 | return ptrauth_strip(ptr, ptrauth_key_asib); | |
308 | case ptrauth_key_asda: | |
309 | return ptrauth_strip(ptr, ptrauth_key_asda); | |
310 | case ptrauth_key_asdb: | |
311 | return ptrauth_strip(ptr, ptrauth_key_asdb); | |
312 | default: | |
313 | return ptr; | |
314 | } | |
315 | #else | |
316 | return ptr; | |
317 | #endif // __has_feature(ptrauth_calls) | |
318 | } | |
319 | ||
320 | int | |
321 | dtrace_is_valid_ptrauth_key(uint64_t key) | |
322 | { | |
323 | #pragma unused(key) | |
324 | #if __has_feature(ptrauth_calls) | |
325 | return (key == ptrauth_key_asia) || (key == ptrauth_key_asib) || | |
326 | (key == ptrauth_key_asda) || (key == ptrauth_key_asdb); | |
327 | #else | |
328 | return (0); | |
329 | #endif /* __has_feature(ptrauth_calls) */ | |
330 | } | |
331 | ||
39037602 A |
332 | static minor_t next_minor = 0; |
333 | static dtrace_state_t* dtrace_clients[DTRACE_NCLIENTS] = {NULL}; | |
334 | ||
335 | ||
336 | minor_t | |
337 | dtrace_state_reserve(void) | |
338 | { | |
339 | for (int i = 0; i < DTRACE_NCLIENTS; i++) { | |
cb323159 | 340 | minor_t minor = os_atomic_inc_orig(&next_minor, relaxed) % DTRACE_NCLIENTS; |
39037602 A |
341 | if (dtrace_clients[minor] == NULL) |
342 | return minor; | |
343 | } | |
344 | return 0; | |
345 | } | |
346 | ||
347 | dtrace_state_t* | |
348 | dtrace_state_get(minor_t minor) | |
349 | { | |
350 | ASSERT(minor < DTRACE_NCLIENTS); | |
351 | return dtrace_clients[minor]; | |
352 | } | |
353 | ||
354 | dtrace_state_t* | |
355 | dtrace_state_allocate(minor_t minor) | |
356 | { | |
357 | dtrace_state_t *state = _MALLOC(sizeof(dtrace_state_t), M_TEMP, M_ZERO | M_WAITOK); | |
358 | if (dtrace_casptr(&dtrace_clients[minor], NULL, state) != NULL) { | |
359 | // We have been raced by another client for this number, abort | |
360 | _FREE(state, M_TEMP); | |
361 | return NULL; | |
362 | } | |
363 | return state; | |
364 | } | |
365 | ||
366 | void | |
367 | dtrace_state_free(minor_t minor) | |
368 | { | |
369 | dtrace_state_t *state = dtrace_clients[minor]; | |
370 | dtrace_clients[minor] = NULL; | |
371 | _FREE(state, M_TEMP); | |
372 | } | |
3e170ce0 A |
373 | |
374 | ||
375 | ||
376 | void | |
377 | dtrace_restriction_policy_load(void) | |
378 | { | |
379 | } | |
380 | ||
fe8ab488 A |
381 | /* |
382 | * Check if DTrace has been restricted by the current security policy. | |
383 | */ | |
384 | boolean_t | |
385 | dtrace_is_restricted(void) | |
386 | { | |
387 | #if CONFIG_CSR | |
388 | if (csr_check(CSR_ALLOW_UNRESTRICTED_DTRACE) != 0) | |
389 | return TRUE; | |
390 | #endif | |
391 | ||
392 | return FALSE; | |
393 | } | |
394 | ||
3e170ce0 | 395 | boolean_t |
39037602 | 396 | dtrace_are_restrictions_relaxed(void) |
3e170ce0 A |
397 | { |
398 | #if CONFIG_CSR | |
399 | if (csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0) | |
400 | return TRUE; | |
401 | #endif | |
402 | ||
403 | return FALSE; | |
404 | } | |
405 | ||
406 | boolean_t | |
407 | dtrace_fbt_probes_restricted(void) | |
408 | { | |
409 | ||
410 | #if CONFIG_CSR | |
39037602 | 411 | if (dtrace_is_restricted() && !dtrace_are_restrictions_relaxed()) |
3e170ce0 A |
412 | return TRUE; |
413 | #endif | |
414 | ||
415 | return FALSE; | |
416 | } | |
417 | ||
39037602 A |
418 | boolean_t |
419 | dtrace_sdt_probes_restricted(void) | |
420 | { | |
421 | ||
422 | return FALSE; | |
423 | } | |
424 | ||
fe8ab488 A |
425 | /* |
426 | * Check if the process can be attached. | |
427 | */ | |
428 | boolean_t | |
429 | dtrace_can_attach_to_proc(proc_t *proc) | |
430 | { | |
431 | #pragma unused(proc) | |
432 | ASSERT(proc != NULL); | |
433 | ||
434 | #if CONFIG_CSR | |
3e170ce0 | 435 | if (cs_restricted(proc)) |
fe8ab488 A |
436 | return FALSE; |
437 | #endif | |
438 | ||
439 | return TRUE; | |
440 | } | |
441 |