]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/kern_ktrace.c
xnu-3789.1.32.tar.gz
[apple/xnu.git] / bsd / kern / kern_ktrace.c
CommitLineData
39037602
A
1/*
2 * Copyright (c) 2015 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29/*
30 * This file manages the ownership of ktrace and its subsystems, like kdebug
31 * and kperf, as well as the overall state of the system, whether it is in
32 * foreground or background mode.
33 *
34 * When unconfigured or in background mode, any root process can take ownership
35 * of ktrace and configure it, changing the state to foreground and, in the case
36 * of a transition out of background, resetting the background configuration.
37 *
38 * When in foreground mode, if the owning process is still running, only it may
39 * configure ktrace. If it exits, ktrace keeps running but any root process can
40 * change the configuration. When ktrace is reset, the state changes back to
41 * unconfigured and a notification is sent on the ktrace_background host special
42 * port.
43 *
44 * If a process has set itself as the background tool, using the init_background
45 * sysctl, it can configure ktrace only when ktrace is off or already in
46 * background mode. The first attempt to configure ktrace by the background pid
47 * when it is off results in the transition to background mode.
48 */
49
50#include <sys/ktrace.h>
51
52#include <mach/host_priv.h>
53#include <mach/mach_types.h>
54#include <mach/ktrace_background.h>
55
56#include <sys/kauth.h>
57#include <sys/priv.h>
58#include <sys/proc.h>
59char *proc_name_address(void *p);
60#include <sys/sysctl.h>
61#include <sys/vm.h>
62
63#include <kern/locks.h>
64#include <kern/assert.h>
65
66#include <sys/kdebug.h>
67#include <kperf/kperf.h>
68
69#include <kern/host.h>
70
71kern_return_t ktrace_background_available_notify_user(void);
72
73lck_mtx_t *ktrace_lock;
74
75/*
76 * The overall state of ktrace, whether it is unconfigured, in foreground mode,
77 * or in background mode. The state determines which processes can configure
78 * ktrace.
79 */
80static enum ktrace_state ktrace_state = KTRACE_STATE_OFF;
81
82/* The true owner of ktrace, checked by ktrace_access_check(). */
83static uint64_t ktrace_owning_unique_id = 0;
84static pid_t ktrace_owning_pid = 0;
85
86/*
87 * The background pid of ktrace, automatically made the owner when
88 * transitioning to background mode.
89 */
90static uint64_t ktrace_bg_unique_id = 0;
91static pid_t ktrace_bg_pid = 0;
92
93/* The name of the last process to configure ktrace. */
94static char ktrace_last_owner_execname[MAXCOMLEN + 1] = { 0 };
95
96/*
97 * Which subsystems of ktrace (currently kdebug and kperf) are active.
98 */
99static uint32_t ktrace_active_mask = 0;
100
101/*
102 * At boot or when a daemon has been newly loaded, it's necessary to bootstrap
103 * user space background tools by sending a background available notification
104 * when the init_background sysctl is made.
105 *
106 * Background tools must be RunAtLoad daemons.
107 */
108static boolean_t should_notify_on_init = TRUE;
109
110/* Set the owning process of ktrace. */
111static void ktrace_set_owning_proc(proc_t p);
112
113/* Reset ktrace ownership back to unowned. */
114static void ktrace_release_ownership(void);
115
116/* Make the background tool the owner of ktrace. */
117static void ktrace_promote_background(void);
118
119/*
120 * If user space sets a pid manually (through kperf "blessing"), ktrace should
121 * not treat resets as releasing ownership. At that point, ownership is only
122 * released when the owner is set to an invalid pid.
123 *
124 * This is managed by the user space-oriented function ktrace_set_owning_pid
125 * and ktrace_unset_owning_pid.
126 */
127boolean_t ktrace_keep_ownership_on_reset = FALSE;
128
129/* Allow user space to unset the owning pid and potentially reset ktrace. */
130static void ktrace_set_invalid_owning_pid(void);
131
132/*
133 * This flag allows any root process to set a new ktrace owner. It is
134 * currently used by Instruments.
135 */
136int ktrace_root_set_owner_allowed = 0;
137
138void
139ktrace_reset(uint32_t reset_mask)
140{
141 lck_mtx_assert(ktrace_lock, LCK_MTX_ASSERT_OWNED);
142 assert(reset_mask != 0);
143
144 if (ktrace_active_mask == 0) {
145 if (!ktrace_keep_ownership_on_reset) {
146 assert(ktrace_state == KTRACE_STATE_OFF);
147 }
148 return;
149 }
150
151 if (!ktrace_keep_ownership_on_reset) {
152 ktrace_active_mask &= ~reset_mask;
153 }
154
155 if (reset_mask & KTRACE_KPERF) {
156 kperf_reset();
157 }
158 if (reset_mask & KTRACE_KDEBUG) {
159 kdebug_reset();
160 }
161
162 if (ktrace_active_mask == 0) {
163 if (ktrace_state == KTRACE_STATE_FG) {
164 /* transition from foreground to background */
165 ktrace_promote_background();
166 } else if (ktrace_state == KTRACE_STATE_BG) {
167 /* background tool is resetting ktrace */
168 should_notify_on_init = TRUE;
169 ktrace_release_ownership();
170 ktrace_state = KTRACE_STATE_OFF;
171 }
172 }
173}
174
175static void
176ktrace_promote_background(void)
177{
178 lck_mtx_assert(ktrace_lock, LCK_MTX_ASSERT_OWNED);
179 assert(ktrace_state != KTRACE_STATE_BG);
180
181 /*
182 * Remember to send a background available notification on the next init
183 * if the notification failed (meaning no task holds the receive right
184 * for the host special port).
185 */
186 if (ktrace_background_available_notify_user() == KERN_FAILURE) {
187 should_notify_on_init = TRUE;
188 } else {
189 should_notify_on_init = FALSE;
190 }
191
192 ktrace_release_ownership();
193 ktrace_state = KTRACE_STATE_OFF;
194}
195
196bool
197ktrace_background_active(void)
198{
199 lck_mtx_assert(ktrace_lock, LCK_MTX_ASSERT_OWNED);
200 return (ktrace_state == KTRACE_STATE_BG);
201}
202
203int
204ktrace_read_check(void)
205{
206 lck_mtx_assert(ktrace_lock, LCK_MTX_ASSERT_OWNED);
207
208 if (proc_uniqueid(current_proc()) == ktrace_owning_unique_id)
209 {
210 return 0;
211 }
212
213 return kauth_cred_issuser(kauth_cred_get()) ? 0 : EPERM;
214}
215
216/* If an owning process has exited, reset the ownership. */
217static void
218ktrace_ownership_maintenance(void)
219{
220 lck_mtx_assert(ktrace_lock, LCK_MTX_ASSERT_OWNED);
221
222 /* do nothing if ktrace is not owned */
223 if (ktrace_owning_unique_id == 0) {
224 return;
225 }
226
227 /* reset ownership if process cannot be found */
228
229 proc_t owning_proc = proc_find(ktrace_owning_pid);
230
231 if (owning_proc != NULL) {
232 /* make sure the pid was not recycled */
233 if (proc_uniqueid(owning_proc) != ktrace_owning_unique_id) {
234 ktrace_release_ownership();
235 }
236
237 proc_rele(owning_proc);
238 } else {
239 ktrace_release_ownership();
240 }
241}
242
243int
244ktrace_configure(uint32_t config_mask)
245{
246 lck_mtx_assert(ktrace_lock, LCK_MTX_ASSERT_OWNED);
247 assert(config_mask != 0);
248
249 proc_t p = current_proc();
250
251 /* if process clearly owns ktrace, allow */
252 if (proc_uniqueid(p) == ktrace_owning_unique_id) {
253 ktrace_active_mask |= config_mask;
254 return 0;
255 }
256
257 /* background configure while foreground is active is not allowed */
258 if (proc_uniqueid(p) == ktrace_bg_unique_id &&
259 ktrace_state == KTRACE_STATE_FG)
260 {
261 return EBUSY;
262 }
263
264 ktrace_ownership_maintenance();
265
266 /* allow process to gain control when unowned or background */
267 if (ktrace_owning_unique_id == 0 || ktrace_state == KTRACE_STATE_BG) {
268 if (!kauth_cred_issuser(kauth_cred_get())) {
269 return EPERM;
270 }
271
272 ktrace_set_owning_proc(p);
273 ktrace_active_mask |= config_mask;
274 return 0;
275 }
276
277 /* owned by an existing, different process */
278 return EBUSY;
279}
280
281void
282ktrace_disable(enum ktrace_state state_to_match)
283{
284 if (ktrace_state == state_to_match) {
285 kernel_debug_disable();
286 kperf_sampling_disable();
287 }
288}
289
290int
291ktrace_get_owning_pid(void)
292{
293 lck_mtx_assert(ktrace_lock, LCK_MTX_ASSERT_OWNED);
294
295 ktrace_ownership_maintenance();
296 return ktrace_owning_pid;
297}
298
299void
300ktrace_kernel_configure(uint32_t config_mask)
301{
302 lck_mtx_assert(ktrace_lock, LCK_MTX_ASSERT_OWNED);
303
304 if (ktrace_state != KTRACE_STATE_OFF) {
305 if (ktrace_active_mask & KTRACE_KPERF) {
306 kperf_reset();
307 }
308 if (ktrace_active_mask & KTRACE_KDEBUG) {
309 kdebug_reset();
310 }
311 }
312
313 ktrace_active_mask = config_mask;
314 ktrace_state = KTRACE_STATE_FG;
315
316 ktrace_release_ownership();
317 strlcpy(ktrace_last_owner_execname, "kernel_task",
318 sizeof(ktrace_last_owner_execname));
319}
320
321static errno_t
322ktrace_init_background(void)
323{
324 int err = 0;
325
326 lck_mtx_assert(ktrace_lock, LCK_MTX_ASSERT_OWNED);
327
328 if ((err = priv_check_cred(kauth_cred_get(), PRIV_KTRACE_BACKGROUND, 0))) {
329 return err;
330 }
331
332 /*
333 * When a background tool first checks in, send a notification if ktrace
334 * is available.
335 */
336 if (should_notify_on_init) {
337 if (ktrace_state == KTRACE_STATE_OFF) {
338 /*
339 * This notification can only fail if a process does not
340 * hold the receive right for the host special port.
341 * Return an error and don't make the current process
342 * the background tool.
343 */
344 if (ktrace_background_available_notify_user() == KERN_FAILURE) {
345 return EINVAL;
346 }
347 }
348 should_notify_on_init = FALSE;
349 }
350
351 proc_t p = current_proc();
352
353 ktrace_bg_unique_id = proc_uniqueid(p);
354 ktrace_bg_pid = proc_pid(p);
355
356 if (ktrace_state == KTRACE_STATE_BG) {
357 ktrace_set_owning_proc(p);
358 }
359
360 return 0;
361}
362
363void
364ktrace_set_invalid_owning_pid(void)
365{
366 if (ktrace_keep_ownership_on_reset) {
367 ktrace_reset(ktrace_active_mask);
368 ktrace_keep_ownership_on_reset = FALSE;
369 }
370}
371
372int
373ktrace_set_owning_pid(int pid)
374{
375 lck_mtx_assert(ktrace_lock, LCK_MTX_ASSERT_OWNED);
376
377 /* allow user space to successfully unset owning pid */
378 if (pid == -1) {
379 ktrace_set_invalid_owning_pid();
380 return 0;
381 }
382
383 /* use ktrace_reset or ktrace_release_ownership, not this */
384 if (pid == 0) {
385 ktrace_set_invalid_owning_pid();
386 return EINVAL;
387 }
388
389 proc_t p = proc_find(pid);
390 if (!p) {
391 ktrace_set_invalid_owning_pid();
392 return ESRCH;
393 }
394
395 ktrace_keep_ownership_on_reset = TRUE;
396 ktrace_set_owning_proc(p);
397
398 proc_rele(p);
399 return 0;
400}
401
402static void
403ktrace_set_owning_proc(proc_t p)
404{
405 lck_mtx_assert(ktrace_lock, LCK_MTX_ASSERT_OWNED);
406 assert(p);
407
408 if (ktrace_state != KTRACE_STATE_FG) {
409 if (proc_uniqueid(p) == ktrace_bg_unique_id) {
410 ktrace_state = KTRACE_STATE_BG;
411 } else {
412 if (ktrace_state == KTRACE_STATE_BG) {
413 if (ktrace_active_mask & KTRACE_KPERF) {
414 kperf_reset();
415 }
416 if (ktrace_active_mask & KTRACE_KDEBUG) {
417 kdebug_reset();
418 }
419
420 ktrace_active_mask = 0;
421 }
422 ktrace_state = KTRACE_STATE_FG;
423 should_notify_on_init = FALSE;
424 }
425 }
426
427 ktrace_owning_unique_id = proc_uniqueid(p);
428 ktrace_owning_pid = proc_pid(p);
429 strlcpy(ktrace_last_owner_execname, proc_name_address(p),
430 sizeof(ktrace_last_owner_execname));
431}
432
433static void
434ktrace_release_ownership(void)
435{
436 lck_mtx_assert(ktrace_lock, LCK_MTX_ASSERT_OWNED);
437
438 ktrace_owning_unique_id = 0;
439 ktrace_owning_pid = 0;
440}
441
442#define SYSCTL_INIT_BACKGROUND (1)
443
444static int ktrace_sysctl SYSCTL_HANDLER_ARGS;
445
446SYSCTL_NODE(, OID_AUTO, ktrace, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "ktrace");
447
448SYSCTL_UINT(_ktrace, OID_AUTO, state, CTLFLAG_RD | CTLFLAG_LOCKED,
449 &ktrace_state, 0,
450 "");
451
452SYSCTL_INT(_ktrace, OID_AUTO, owning_pid, CTLFLAG_RD | CTLFLAG_LOCKED,
453 &ktrace_owning_pid, 0,
454 "pid of the process that owns ktrace");
455
456SYSCTL_INT(_ktrace, OID_AUTO, background_pid, CTLFLAG_RD | CTLFLAG_LOCKED,
457 &ktrace_bg_pid, 0,
458 "pid of the background ktrace tool");
459
460SYSCTL_STRING(_ktrace, OID_AUTO, configured_by, CTLFLAG_RD | CTLFLAG_LOCKED,
461 ktrace_last_owner_execname, 0,
462 "execname of process that last configured ktrace");
463
464SYSCTL_PROC(_ktrace, OID_AUTO, init_background, CTLFLAG_RW | CTLFLAG_LOCKED,
465 (void *)SYSCTL_INIT_BACKGROUND, sizeof(int),
466 ktrace_sysctl, "I", "initialize calling process as background");
467
468static int
469ktrace_sysctl SYSCTL_HANDLER_ARGS
470{
471#pragma unused(oidp, arg2)
472 int ret = 0;
473 uintptr_t type = (uintptr_t)arg1;
474
475 lck_mtx_lock(ktrace_lock);
476
477 if (!kauth_cred_issuser(kauth_cred_get())) {
478 ret = EPERM;
479 goto out;
480 }
481
482 if (type == SYSCTL_INIT_BACKGROUND) {
483 if (req->newptr != USER_ADDR_NULL) {
484 ret = ktrace_init_background();
485 goto out;
486 } else {
487 ret = EINVAL;
488 goto out;
489 }
490 } else {
491 ret = EINVAL;
492 goto out;
493 }
494
495out:
496 lck_mtx_unlock(ktrace_lock);
497 return ret;
498}
499
500/* This should only be called from the bootstrap thread. */
501void
502ktrace_init(void)
503{
504 static lck_grp_attr_t *lock_grp_attr = NULL;
505 static lck_grp_t *lock_grp = NULL;
506 static boolean_t initialized = FALSE;
507
508 if (initialized) {
509 return;
510 }
511
512 lock_grp_attr = lck_grp_attr_alloc_init();
513 lock_grp = lck_grp_alloc_init("ktrace", lock_grp_attr);
514 lck_grp_attr_free(lock_grp_attr);
515
516 ktrace_lock = lck_mtx_alloc_init(lock_grp, LCK_ATTR_NULL);
517 assert(ktrace_lock);
518 initialized = TRUE;
519}