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>
43 #if defined(KERNEL_INTEGRITY_KTRR) || defined(KERNEL_INTEGRITY_CTRR)
44 extern bool csr_unsafe_kernel_text
;
49 * APPLE NOTE: Solaris proc_t is the struct.
50 * Darwin's proc_t is a pointer to it.
52 #define proc_t struct proc /* Steer clear of the Darwin typedef for proc_t */
55 /* Copied from an arch specific dtrace_subr.c. */
56 int (*dtrace_fasttrap_probe_ptr
)(struct regs
*);
59 * Following DTrace hooks are taken from Solaris' dtrace_subr.c
60 * They're assigned in dtrace.c but Darwin never calls them.
62 void (*dtrace_cpu_init
)(processorid_t
);
63 int (*dtrace_modload
)(struct kmod_info
*, uint32_t);
64 int (*dtrace_modunload
)(struct kmod_info
*);
65 void (*dtrace_helpers_cleanup
)(proc_t
*);
66 void (*dtrace_helpers_fork
)(proc_t
*, proc_t
*);
67 void (*dtrace_cpustart_init
)(void);
68 void (*dtrace_cpustart_fini
)(void);
70 void (*dtrace_debugger_init
)(void);
71 void (*dtrace_debugger_fini
)(void);
73 dtrace_vtime_state_t dtrace_vtime_active
= 0;
74 dtrace_cacheid_t dtrace_predcache_id
= DTRACE_CACHEIDNONE
+ 1;
76 void (*dtrace_fasttrap_fork_ptr
)(proc_t
*, proc_t
*);
77 void (*dtrace_fasttrap_exec_ptr
)(proc_t
*);
78 void (*dtrace_fasttrap_exit_ptr
)(proc_t
*);
81 * This function is called by cfork() in the event that it appears that
82 * there may be dtrace tracepoints active in the parent process's address
83 * space. This first confirms the existence of dtrace tracepoints in the
84 * parent process and calls into the fasttrap module to remove the
85 * corresponding tracepoints from the child. By knowing that there are
86 * existing tracepoints, and ensuring they can't be removed, we can rely
87 * on the fasttrap module remaining loaded.
90 dtrace_fasttrap_fork(proc_t
*p
, proc_t
*cp
)
92 if (dtrace_fasttrap_fork_ptr
) {
93 (*dtrace_fasttrap_fork_ptr
)(p
, cp
);
99 * DTrace wait for process execution
101 * This feature is using a list of entries, each entry containing a pointer
102 * on a process description. The description is provided by a client, and it
103 * contains the command we want to wait for along with a reserved space for
104 * the caught process id.
106 * Once an awaited process has been spawned, it will be suspended before
107 * notifying the client. Once the client has been back to userland, it's its
108 * duty to resume the task.
111 lck_mtx_t dtrace_procwaitfor_lock
;
113 typedef struct dtrace_proc_awaited_entry
{
114 struct dtrace_procdesc
*pdesc
;
115 LIST_ENTRY(dtrace_proc_awaited_entry
) entries
;
116 } dtrace_proc_awaited_entry_t
;
118 LIST_HEAD(listhead
, dtrace_proc_awaited_entry
) dtrace_proc_awaited_head
119 = LIST_HEAD_INITIALIZER(dtrace_proc_awaited_head
);
121 void (*dtrace_proc_waitfor_exec_ptr
)(proc_t
*) = NULL
;
124 dtrace_proc_get_execpath(proc_t
*p
, char *buffer
, int *maxlen
)
126 int err
= 0, vid
= 0;
127 vnode_t tvp
= NULLVP
, nvp
= NULLVP
;
133 if ((tvp
= p
->p_textvp
) == NULLVP
)
136 vid
= vnode_vid(tvp
);
137 if ((err
= vnode_getwithvid(tvp
, vid
)) != 0)
140 if ((err
= vn_getpath_fsenter(tvp
, buffer
, maxlen
)) != 0)
144 if ((err
= vnode_lookup(buffer
, 0, &nvp
, vfs_context_current())) != 0)
154 dtrace_proc_exec_notification(proc_t
*p
) {
155 dtrace_proc_awaited_entry_t
*entry
, *tmp
;
156 static char execpath
[MAXPATHLEN
];
159 ASSERT(p
->p_pid
!= -1);
160 ASSERT(current_task() != p
->task
);
162 lck_mtx_lock(&dtrace_procwaitfor_lock
);
164 LIST_FOREACH_SAFE(entry
, &dtrace_proc_awaited_head
, entries
, tmp
) {
165 /* By default consider we're using p_comm. */
166 char *pname
= p
->p_comm
;
168 /* Already matched with another process. */
169 if ((entry
->pdesc
->p_pid
!= -1))
172 /* p_comm is too short, use the execpath. */
173 if (entry
->pdesc
->p_name_length
>= MAXCOMLEN
) {
175 * Retrieve the executable path. After the call, length contains
176 * the length of the string + 1.
178 int length
= sizeof(execpath
);
179 if (dtrace_proc_get_execpath(p
, execpath
, &length
) != 0)
181 /* Move the cursor to the position after the last / */
182 pname
= &execpath
[length
- 1];
183 while (pname
!= execpath
&& *pname
!= '/')
185 pname
= (*pname
== '/') ? pname
+ 1 : pname
;
188 if (!strcmp(entry
->pdesc
->p_name
, pname
)) {
189 entry
->pdesc
->p_pid
= p
->p_pid
;
190 task_pidsuspend(p
->task
);
195 lck_mtx_unlock(&dtrace_procwaitfor_lock
);
199 dtrace_proc_waitfor(dtrace_procdesc_t
* pdesc
) {
200 dtrace_proc_awaited_entry_t entry
;
204 ASSERT(pdesc
->p_name
);
207 * Never trust user input, compute the length of the process name and ensure the
208 * string is null terminated.
210 pdesc
->p_name_length
= (int) strnlen(pdesc
->p_name
, sizeof(pdesc
->p_name
));
211 if (pdesc
->p_name_length
>= (int) sizeof(pdesc
->p_name
))
214 lck_mtx_lock(&dtrace_procwaitfor_lock
);
216 /* Initialize and insert the entry, then install the hook. */
219 LIST_INSERT_HEAD(&dtrace_proc_awaited_head
, &entry
, entries
);
220 dtrace_proc_waitfor_exec_ptr
= &dtrace_proc_exec_notification
;
222 /* Sleep until the process has been executed */
223 res
= msleep(&entry
, &dtrace_procwaitfor_lock
, PCATCH
, "dtrace_proc_waitfor", NULL
);
225 /* Remove the entry and the hook if it is not needed anymore. */
226 LIST_REMOVE(&entry
, entries
);
227 if (LIST_EMPTY(&dtrace_proc_awaited_head
))
228 dtrace_proc_waitfor_exec_ptr
= NULL
;
230 lck_mtx_unlock(&dtrace_procwaitfor_lock
);
236 typedef struct dtrace_invop_hdlr
{
237 int (*dtih_func
)(uintptr_t, uintptr_t *, uintptr_t);
238 struct dtrace_invop_hdlr
*dtih_next
;
239 } dtrace_invop_hdlr_t
;
241 dtrace_invop_hdlr_t
*dtrace_invop_hdlr
;
244 dtrace_invop(uintptr_t, uintptr_t *, uintptr_t);
247 dtrace_invop(uintptr_t addr
, uintptr_t *stack
, uintptr_t eax
)
249 dtrace_invop_hdlr_t
*hdlr
;
252 for (hdlr
= dtrace_invop_hdlr
; hdlr
!= NULL
; hdlr
= hdlr
->dtih_next
) {
253 if ((rval
= hdlr
->dtih_func(addr
, stack
, eax
)) != 0)
261 dtrace_invop_add(int (*func
)(uintptr_t, uintptr_t *, uintptr_t))
263 dtrace_invop_hdlr_t
*hdlr
;
265 hdlr
= kmem_alloc(sizeof (dtrace_invop_hdlr_t
), KM_SLEEP
);
266 hdlr
->dtih_func
= func
;
267 hdlr
->dtih_next
= dtrace_invop_hdlr
;
268 dtrace_invop_hdlr
= hdlr
;
272 dtrace_invop_remove(int (*func
)(uintptr_t, uintptr_t *, uintptr_t))
274 dtrace_invop_hdlr_t
*hdlr
= dtrace_invop_hdlr
, *prev
= NULL
;
278 panic("attempt to remove non-existent invop handler");
280 if (hdlr
->dtih_func
== func
)
284 hdlr
= hdlr
->dtih_next
;
288 ASSERT(dtrace_invop_hdlr
== hdlr
);
289 dtrace_invop_hdlr
= hdlr
->dtih_next
;
291 ASSERT(dtrace_invop_hdlr
!= hdlr
);
292 prev
->dtih_next
= hdlr
->dtih_next
;
295 kmem_free(hdlr
, sizeof (dtrace_invop_hdlr_t
));
299 dtrace_ptrauth_strip(void *ptr
, uint64_t key
)
302 #if __has_feature(ptrauth_calls)
304 * The key argument to ptrauth_strip needs to be a compile-time
308 case ptrauth_key_asia
:
309 return ptrauth_strip(ptr
, ptrauth_key_asia
);
310 case ptrauth_key_asib
:
311 return ptrauth_strip(ptr
, ptrauth_key_asib
);
312 case ptrauth_key_asda
:
313 return ptrauth_strip(ptr
, ptrauth_key_asda
);
314 case ptrauth_key_asdb
:
315 return ptrauth_strip(ptr
, ptrauth_key_asdb
);
321 #endif // __has_feature(ptrauth_calls)
325 dtrace_is_valid_ptrauth_key(uint64_t key
)
328 #if __has_feature(ptrauth_calls)
329 return (key
== ptrauth_key_asia
) || (key
== ptrauth_key_asib
) ||
330 (key
== ptrauth_key_asda
) || (key
== ptrauth_key_asdb
);
333 #endif /* __has_feature(ptrauth_calls) */
337 dtrace_physmem_read(uint64_t addr
, size_t size
)
341 return (uint64_t)ml_phys_read_byte_64((addr64_t
)addr
);
343 return (uint64_t)ml_phys_read_half_64((addr64_t
)addr
);
345 return (uint64_t)ml_phys_read_64((addr64_t
)addr
);
347 return (uint64_t)ml_phys_read_double_64((addr64_t
)addr
);
349 DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP
);
355 dtrace_physmem_write(uint64_t addr
, uint64_t data
, size_t size
)
359 ml_phys_write_byte_64((addr64_t
)addr
, (unsigned int)data
);
362 ml_phys_write_half_64((addr64_t
)addr
, (unsigned int)data
);
365 ml_phys_write_64((addr64_t
)addr
, (unsigned int)data
);
368 ml_phys_write_double_64((addr64_t
)addr
, (unsigned long long)data
);
371 DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP
);
375 static minor_t next_minor
= 0;
376 static dtrace_state_t
* dtrace_clients
[DTRACE_NCLIENTS
] = {NULL
};
380 dtrace_state_reserve(void)
382 for (int i
= 0; i
< DTRACE_NCLIENTS
; i
++) {
383 minor_t minor
= os_atomic_inc_orig(&next_minor
, relaxed
) % DTRACE_NCLIENTS
;
384 if (dtrace_clients
[minor
] == NULL
)
391 dtrace_state_get(minor_t minor
)
393 ASSERT(minor
< DTRACE_NCLIENTS
);
394 return dtrace_clients
[minor
];
398 dtrace_state_allocate(minor_t minor
)
400 dtrace_state_t
*state
= _MALLOC(sizeof(dtrace_state_t
), M_TEMP
, M_ZERO
| M_WAITOK
);
401 if (dtrace_casptr(&dtrace_clients
[minor
], NULL
, state
) != NULL
) {
402 // We have been raced by another client for this number, abort
403 _FREE(state
, M_TEMP
);
410 dtrace_state_free(minor_t minor
)
412 dtrace_state_t
*state
= dtrace_clients
[minor
];
413 dtrace_clients
[minor
] = NULL
;
414 _FREE(state
, M_TEMP
);
420 dtrace_restriction_policy_load(void)
425 * Check if DTrace has been restricted by the current security policy.
428 dtrace_is_restricted(void)
431 if (csr_check(CSR_ALLOW_UNRESTRICTED_DTRACE
) != 0)
439 dtrace_are_restrictions_relaxed(void)
442 if (csr_check(CSR_ALLOW_APPLE_INTERNAL
) == 0)
450 dtrace_fbt_probes_restricted(void)
454 if (dtrace_is_restricted() && !dtrace_are_restrictions_relaxed())
462 dtrace_sdt_probes_restricted(void)
469 * Check if the process can be attached.
472 dtrace_can_attach_to_proc(proc_t
*proc
)
475 ASSERT(proc
!= NULL
);
478 if (cs_restricted(proc
))