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.
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.
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]
23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
29 #include <sys/malloc.h>
31 #include <sys/dtrace.h>
32 #include <sys/dtrace_impl.h>
33 #include <sys/proc_internal.h>
34 #include <sys/vnode.h>
35 #include <kern/debug.h>
36 #include <kern/sched_prim.h>
37 #include <kern/task.h>
40 #include <sys/codesign.h>
45 * APPLE NOTE: Solaris proc_t is the struct.
46 * Darwin's proc_t is a pointer to it.
48 #define proc_t struct proc /* Steer clear of the Darwin typedef for proc_t */
51 /* Copied from an arch specific dtrace_subr.c. */
52 int (*dtrace_fasttrap_probe_ptr
)(struct regs
*);
55 * Following DTrace hooks are taken from Solaris' dtrace_subr.c
56 * They're assigned in dtrace.c but Darwin never calls them.
58 void (*dtrace_cpu_init
)(processorid_t
);
59 int (*dtrace_modload
)(struct kmod_info
*, uint32_t);
60 int (*dtrace_modunload
)(struct kmod_info
*);
61 void (*dtrace_helpers_cleanup
)(proc_t
*);
62 void (*dtrace_helpers_fork
)(proc_t
*, proc_t
*);
63 void (*dtrace_cpustart_init
)(void);
64 void (*dtrace_cpustart_fini
)(void);
66 void (*dtrace_debugger_init
)(void);
67 void (*dtrace_debugger_fini
)(void);
69 dtrace_vtime_state_t dtrace_vtime_active
= 0;
70 dtrace_cacheid_t dtrace_predcache_id
= DTRACE_CACHEIDNONE
+ 1;
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
*);
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.
86 dtrace_fasttrap_fork(proc_t
*p
, proc_t
*cp
)
88 if (dtrace_fasttrap_fork_ptr
) {
89 (*dtrace_fasttrap_fork_ptr
)(p
, cp
);
95 * DTrace wait for process execution
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.
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.
107 lck_mtx_t dtrace_procwaitfor_lock
;
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
;
114 LIST_HEAD(listhead
, dtrace_proc_awaited_entry
) dtrace_proc_awaited_head
115 = LIST_HEAD_INITIALIZER(dtrace_proc_awaited_head
);
117 void (*dtrace_proc_waitfor_exec_ptr
)(proc_t
*) = NULL
;
120 dtrace_proc_get_execpath(proc_t
*p
, char *buffer
, int *maxlen
)
122 int err
= 0, vid
= 0;
123 vnode_t tvp
= NULLVP
, nvp
= NULLVP
;
129 if ((tvp
= p
->p_textvp
) == NULLVP
)
132 vid
= vnode_vid(tvp
);
133 if ((err
= vnode_getwithvid(tvp
, vid
)) != 0)
136 if ((err
= vn_getpath_fsenter(tvp
, buffer
, maxlen
)) != 0)
140 if ((err
= vnode_lookup(buffer
, 0, &nvp
, vfs_context_current())) != 0)
150 dtrace_proc_exec_notification(proc_t
*p
) {
151 dtrace_proc_awaited_entry_t
*entry
, *tmp
;
152 static char execpath
[MAXPATHLEN
];
155 ASSERT(p
->p_pid
!= -1);
156 ASSERT(current_task() != p
->task
);
158 lck_mtx_lock(&dtrace_procwaitfor_lock
);
160 LIST_FOREACH_SAFE(entry
, &dtrace_proc_awaited_head
, entries
, tmp
) {
161 /* By default consider we're using p_comm. */
162 char *pname
= p
->p_comm
;
164 /* Already matched with another process. */
165 if ((entry
->pdesc
->p_pid
!= -1))
168 /* p_comm is too short, use the execpath. */
169 if (entry
->pdesc
->p_name_length
>= MAXCOMLEN
) {
171 * Retrieve the executable path. After the call, length contains
172 * the length of the string + 1.
174 int length
= sizeof(execpath
);
175 if (dtrace_proc_get_execpath(p
, execpath
, &length
) != 0)
177 /* Move the cursor to the position after the last / */
178 pname
= &execpath
[length
- 1];
179 while (pname
!= execpath
&& *pname
!= '/')
181 pname
= (*pname
== '/') ? pname
+ 1 : pname
;
184 if (!strcmp(entry
->pdesc
->p_name
, pname
)) {
185 entry
->pdesc
->p_pid
= p
->p_pid
;
186 task_pidsuspend(p
->task
);
191 lck_mtx_unlock(&dtrace_procwaitfor_lock
);
195 dtrace_proc_waitfor(dtrace_procdesc_t
* pdesc
) {
196 dtrace_proc_awaited_entry_t entry
;
200 ASSERT(pdesc
->p_name
);
203 * Never trust user input, compute the length of the process name and ensure the
204 * string is null terminated.
206 pdesc
->p_name_length
= strnlen(pdesc
->p_name
, sizeof(pdesc
->p_name
));
207 if (pdesc
->p_name_length
>= (int) sizeof(pdesc
->p_name
))
210 lck_mtx_lock(&dtrace_procwaitfor_lock
);
212 /* Initialize and insert the entry, then install the hook. */
215 LIST_INSERT_HEAD(&dtrace_proc_awaited_head
, &entry
, entries
);
216 dtrace_proc_waitfor_exec_ptr
= &dtrace_proc_exec_notification
;
218 /* Sleep until the process has been executed */
219 res
= msleep(&entry
, &dtrace_procwaitfor_lock
, PCATCH
, "dtrace_proc_waitfor", NULL
);
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
;
226 lck_mtx_unlock(&dtrace_procwaitfor_lock
);
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
;
237 dtrace_invop_hdlr_t
*dtrace_invop_hdlr
;
240 dtrace_invop(uintptr_t, uintptr_t *, uintptr_t);
243 dtrace_invop(uintptr_t addr
, uintptr_t *stack
, uintptr_t eax
)
245 dtrace_invop_hdlr_t
*hdlr
;
248 for (hdlr
= dtrace_invop_hdlr
; hdlr
!= NULL
; hdlr
= hdlr
->dtih_next
) {
249 if ((rval
= hdlr
->dtih_func(addr
, stack
, eax
)) != 0)
257 dtrace_invop_add(int (*func
)(uintptr_t, uintptr_t *, uintptr_t))
259 dtrace_invop_hdlr_t
*hdlr
;
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
;
268 dtrace_invop_remove(int (*func
)(uintptr_t, uintptr_t *, uintptr_t))
270 dtrace_invop_hdlr_t
*hdlr
= dtrace_invop_hdlr
, *prev
= NULL
;
274 panic("attempt to remove non-existent invop handler");
276 if (hdlr
->dtih_func
== func
)
280 hdlr
= hdlr
->dtih_next
;
284 ASSERT(dtrace_invop_hdlr
== hdlr
);
285 dtrace_invop_hdlr
= hdlr
->dtih_next
;
287 ASSERT(dtrace_invop_hdlr
!= hdlr
);
288 prev
->dtih_next
= hdlr
->dtih_next
;
291 kmem_free(hdlr
, sizeof (dtrace_invop_hdlr_t
));
295 dtrace_ptrauth_strip(void *ptr
, uint64_t key
)
298 #if __has_feature(ptrauth_calls)
300 * The key argument to ptrauth_strip needs to be a compile-time
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
);
317 #endif // __has_feature(ptrauth_calls)
321 dtrace_is_valid_ptrauth_key(uint64_t 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
);
329 #endif /* __has_feature(ptrauth_calls) */
332 static minor_t next_minor
= 0;
333 static dtrace_state_t
* dtrace_clients
[DTRACE_NCLIENTS
] = {NULL
};
337 dtrace_state_reserve(void)
339 for (int i
= 0; i
< DTRACE_NCLIENTS
; i
++) {
340 minor_t minor
= os_atomic_inc_orig(&next_minor
, relaxed
) % DTRACE_NCLIENTS
;
341 if (dtrace_clients
[minor
] == NULL
)
348 dtrace_state_get(minor_t minor
)
350 ASSERT(minor
< DTRACE_NCLIENTS
);
351 return dtrace_clients
[minor
];
355 dtrace_state_allocate(minor_t minor
)
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
);
367 dtrace_state_free(minor_t minor
)
369 dtrace_state_t
*state
= dtrace_clients
[minor
];
370 dtrace_clients
[minor
] = NULL
;
371 _FREE(state
, M_TEMP
);
377 dtrace_restriction_policy_load(void)
382 * Check if DTrace has been restricted by the current security policy.
385 dtrace_is_restricted(void)
388 if (csr_check(CSR_ALLOW_UNRESTRICTED_DTRACE
) != 0)
396 dtrace_are_restrictions_relaxed(void)
399 if (csr_check(CSR_ALLOW_APPLE_INTERNAL
) == 0)
407 dtrace_fbt_probes_restricted(void)
411 if (dtrace_is_restricted() && !dtrace_are_restrictions_relaxed())
419 dtrace_sdt_probes_restricted(void)
426 * Check if the process can be attached.
429 dtrace_can_attach_to_proc(proc_t
*proc
)
432 ASSERT(proc
!= NULL
);
435 if (cs_restricted(proc
))