]> git.saurik.com Git - apple/xnu.git/blame_incremental - bsd/kern/kern_ktrace.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / bsd / kern / kern_ktrace.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2015-2017 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
73static LCK_GRP_DECLARE(ktrace_grp, "ktrace");
74static LCK_MTX_DECLARE(ktrace_mtx, &ktrace_grp);
75
76/*
77 * The overall state of ktrace, whether it is unconfigured, in foreground mode,
78 * or in background mode. The state determines which processes can configure
79 * ktrace.
80 */
81static enum ktrace_state ktrace_state = KTRACE_STATE_OFF;
82
83/* The true owner of ktrace, checked by ktrace_access_check(). */
84static uint64_t ktrace_owning_unique_id = 0;
85static pid_t ktrace_owning_pid = 0;
86
87/*
88 * The background pid of ktrace, automatically made the owner when
89 * transitioning to background mode.
90 */
91static uint64_t ktrace_bg_unique_id = 0;
92static pid_t ktrace_bg_pid = 0;
93
94/* The name of the last process to configure ktrace. */
95static char ktrace_last_owner_execname[MAXCOMLEN + 1] = { 0 };
96
97/*
98 * Which subsystems of ktrace (currently kdebug and kperf) are active.
99 */
100static uint32_t ktrace_active_mask = 0;
101
102/*
103 * At boot or when a daemon has been newly loaded, it's necessary to bootstrap
104 * user space background tools by sending a background available notification
105 * when the init_background sysctl is made.
106 *
107 * Background tools must be RunAtLoad daemons.
108 */
109static bool should_notify_on_init = true;
110
111/* Set the owning process of ktrace. */
112static void ktrace_set_owning_proc(proc_t p);
113
114/* Reset ktrace ownership back to unowned. */
115static void ktrace_release_ownership(void);
116
117/* Make the background tool the owner of ktrace. */
118static void ktrace_promote_background(void);
119
120/*
121 * If user space sets a pid manually (through kperf "blessing"), ktrace should
122 * not treat resets as releasing ownership. At that point, ownership is only
123 * released when the owner is set to an invalid pid.
124 *
125 * This is managed by the user space-oriented function ktrace_set_owning_pid
126 * and ktrace_unset_owning_pid.
127 */
128bool ktrace_keep_ownership_on_reset = false;
129
130/* Whether the kernel is the owner of ktrace. */
131bool ktrace_owner_kernel = false;
132
133/* Allow user space to unset the owning pid and potentially reset ktrace. */
134static void ktrace_set_invalid_owning_pid(void);
135
136/*
137 * This flag allows any root process to set a new ktrace owner. It is
138 * currently used by Instruments.
139 */
140int ktrace_root_set_owner_allowed = 0;
141
142/*
143 * If ktrace is guaranteed that it's the only thread running on the system
144 * (e.g., during boot or wake) this flag disables locking requirements.
145 */
146static bool ktrace_single_threaded = false;
147
148void
149ktrace_lock(void)
150{
151 if (!ktrace_single_threaded) {
152 lck_mtx_lock(&ktrace_mtx);
153 }
154}
155
156void
157ktrace_unlock(void)
158{
159 if (!ktrace_single_threaded) {
160 lck_mtx_unlock(&ktrace_mtx);
161 }
162}
163
164void
165ktrace_assert_lock_held(void)
166{
167 if (!ktrace_single_threaded) {
168 lck_mtx_assert(&ktrace_mtx, LCK_MTX_ASSERT_OWNED);
169 }
170}
171
172void
173ktrace_start_single_threaded(void)
174{
175 ktrace_single_threaded = true;
176}
177
178void
179ktrace_end_single_threaded(void)
180{
181 ktrace_single_threaded = false;
182}
183
184static void
185ktrace_reset_internal(uint32_t reset_mask)
186{
187 if (!ktrace_keep_ownership_on_reset) {
188 ktrace_active_mask &= ~reset_mask;
189 }
190
191 if (reset_mask & KTRACE_KPERF) {
192 kperf_reset();
193 }
194 if (reset_mask & KTRACE_KDEBUG) {
195 kdebug_reset();
196 }
197
198 if (ktrace_active_mask == 0) {
199 if (ktrace_state == KTRACE_STATE_FG) {
200 /* transition from foreground to background */
201 ktrace_promote_background();
202 } else if (ktrace_state == KTRACE_STATE_BG) {
203 /* background tool is resetting ktrace */
204 should_notify_on_init = true;
205 ktrace_release_ownership();
206 ktrace_state = KTRACE_STATE_OFF;
207 }
208 }
209}
210
211void
212ktrace_reset(uint32_t reset_mask)
213{
214 ktrace_assert_lock_held();
215
216 if (ktrace_active_mask == 0) {
217 if (!ktrace_keep_ownership_on_reset) {
218 assert(ktrace_state == KTRACE_STATE_OFF);
219 }
220 return;
221 }
222
223 ktrace_reset_internal(reset_mask);
224}
225
226static void
227ktrace_promote_background(void)
228{
229 assert(ktrace_state != KTRACE_STATE_BG);
230
231 /*
232 * Remember to send a background available notification on the next init
233 * if the notification failed (meaning no task holds the receive right
234 * for the host special port).
235 */
236 if (ktrace_background_available_notify_user() == KERN_FAILURE) {
237 should_notify_on_init = true;
238 } else {
239 should_notify_on_init = false;
240 }
241
242 ktrace_release_ownership();
243 ktrace_state = KTRACE_STATE_OFF;
244}
245
246bool
247ktrace_background_active(void)
248{
249 return ktrace_state == KTRACE_STATE_BG;
250}
251
252int
253ktrace_read_check(void)
254{
255 ktrace_assert_lock_held();
256
257 if (proc_uniqueid(current_proc()) == ktrace_owning_unique_id) {
258 return 0;
259 }
260
261 return kauth_cred_issuser(kauth_cred_get()) ? 0 : EPERM;
262}
263
264/* If an owning process has exited, reset the ownership. */
265static void
266ktrace_ownership_maintenance(void)
267{
268 ktrace_assert_lock_held();
269
270 /* do nothing if ktrace is not owned */
271 if (ktrace_owning_unique_id == 0) {
272 return;
273 }
274
275 /* reset ownership if process cannot be found */
276
277 proc_t owning_proc = proc_find(ktrace_owning_pid);
278
279 if (owning_proc != NULL) {
280 /* make sure the pid was not recycled */
281 if (proc_uniqueid(owning_proc) != ktrace_owning_unique_id) {
282 ktrace_release_ownership();
283 }
284
285 proc_rele(owning_proc);
286 } else {
287 ktrace_release_ownership();
288 }
289}
290
291int
292ktrace_configure(uint32_t config_mask)
293{
294 ktrace_assert_lock_held();
295 assert(config_mask != 0);
296
297 proc_t p = current_proc();
298
299 /* if process clearly owns ktrace, allow */
300 if (proc_uniqueid(p) == ktrace_owning_unique_id) {
301 ktrace_active_mask |= config_mask;
302 return 0;
303 }
304
305 /* background configure while foreground is active is not allowed */
306 if (proc_uniqueid(p) == ktrace_bg_unique_id &&
307 ktrace_state == KTRACE_STATE_FG) {
308 return EBUSY;
309 }
310
311 ktrace_ownership_maintenance();
312
313 /* allow process to gain control when unowned or background */
314 if (ktrace_owning_unique_id == 0 || ktrace_state == KTRACE_STATE_BG) {
315 if (!kauth_cred_issuser(kauth_cred_get())) {
316 return EPERM;
317 }
318
319 ktrace_owner_kernel = false;
320 ktrace_set_owning_proc(p);
321 ktrace_active_mask |= config_mask;
322 return 0;
323 }
324
325 /* owned by an existing, different process */
326 return EBUSY;
327}
328
329void
330ktrace_disable(enum ktrace_state state_to_match)
331{
332 if (ktrace_state == state_to_match) {
333 kernel_debug_disable();
334 kperf_disable_sampling();
335 }
336}
337
338int
339ktrace_get_owning_pid(void)
340{
341 ktrace_assert_lock_held();
342
343 ktrace_ownership_maintenance();
344 return ktrace_owning_pid;
345}
346
347void
348ktrace_kernel_configure(uint32_t config_mask)
349{
350 assert(ktrace_single_threaded == true);
351
352 if (ktrace_owner_kernel) {
353 ktrace_active_mask |= config_mask;
354 return;
355 }
356
357 if (ktrace_state != KTRACE_STATE_OFF) {
358 if (ktrace_active_mask & config_mask & KTRACE_KPERF) {
359 kperf_reset();
360 }
361 if (ktrace_active_mask & config_mask & KTRACE_KDEBUG) {
362 kdebug_reset();
363 }
364 }
365
366 ktrace_owner_kernel = true;
367 ktrace_active_mask |= config_mask;
368 ktrace_state = KTRACE_STATE_FG;
369
370 ktrace_release_ownership();
371 strlcpy(ktrace_last_owner_execname, "kernel_task",
372 sizeof(ktrace_last_owner_execname));
373}
374
375static errno_t
376ktrace_init_background(void)
377{
378 int err = 0;
379
380 ktrace_assert_lock_held();
381
382 if ((err = priv_check_cred(kauth_cred_get(), PRIV_KTRACE_BACKGROUND, 0))) {
383 return err;
384 }
385
386 /*
387 * When a background tool first checks in, send a notification if ktrace
388 * is available.
389 */
390 if (should_notify_on_init) {
391 if (ktrace_state == KTRACE_STATE_OFF) {
392 /*
393 * This notification can only fail if a process does not
394 * hold the receive right for the host special port.
395 * Return an error and don't make the current process
396 * the background tool.
397 */
398 if (ktrace_background_available_notify_user() == KERN_FAILURE) {
399 return EINVAL;
400 }
401 }
402 should_notify_on_init = false;
403 }
404
405 proc_t p = current_proc();
406
407 ktrace_bg_unique_id = proc_uniqueid(p);
408 ktrace_bg_pid = proc_pid(p);
409
410 if (ktrace_state == KTRACE_STATE_BG) {
411 ktrace_set_owning_proc(p);
412 }
413
414 return 0;
415}
416
417void
418ktrace_set_invalid_owning_pid(void)
419{
420 if (ktrace_keep_ownership_on_reset) {
421 ktrace_keep_ownership_on_reset = false;
422 ktrace_reset_internal(ktrace_active_mask);
423 }
424}
425
426int
427ktrace_set_owning_pid(int pid)
428{
429 ktrace_assert_lock_held();
430
431 /* allow user space to successfully unset owning pid */
432 if (pid == -1) {
433 ktrace_set_invalid_owning_pid();
434 return 0;
435 }
436
437 /* use ktrace_reset or ktrace_release_ownership, not this */
438 if (pid == 0) {
439 ktrace_set_invalid_owning_pid();
440 return EINVAL;
441 }
442
443 proc_t p = proc_find(pid);
444 if (!p) {
445 ktrace_set_invalid_owning_pid();
446 return ESRCH;
447 }
448
449 ktrace_keep_ownership_on_reset = true;
450 ktrace_set_owning_proc(p);
451
452 proc_rele(p);
453 return 0;
454}
455
456static void
457ktrace_set_owning_proc(proc_t p)
458{
459 ktrace_assert_lock_held();
460 assert(p != NULL);
461
462 if (ktrace_state != KTRACE_STATE_FG) {
463 if (proc_uniqueid(p) == ktrace_bg_unique_id) {
464 ktrace_state = KTRACE_STATE_BG;
465 } else {
466 if (ktrace_state == KTRACE_STATE_BG) {
467 if (ktrace_active_mask & KTRACE_KPERF) {
468 kperf_reset();
469 }
470 if (ktrace_active_mask & KTRACE_KDEBUG) {
471 kdebug_reset();
472 }
473
474 ktrace_active_mask = 0;
475 }
476 ktrace_state = KTRACE_STATE_FG;
477 should_notify_on_init = false;
478 }
479 }
480
481 ktrace_owner_kernel = false;
482 ktrace_owning_unique_id = proc_uniqueid(p);
483 ktrace_owning_pid = proc_pid(p);
484 strlcpy(ktrace_last_owner_execname, proc_name_address(p),
485 sizeof(ktrace_last_owner_execname));
486}
487
488static void
489ktrace_release_ownership(void)
490{
491 ktrace_owning_unique_id = 0;
492 ktrace_owning_pid = 0;
493}
494
495#define SYSCTL_INIT_BACKGROUND (1)
496
497static int ktrace_sysctl SYSCTL_HANDLER_ARGS;
498
499SYSCTL_NODE(, OID_AUTO, ktrace, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "ktrace");
500
501SYSCTL_UINT(_ktrace, OID_AUTO, state, CTLFLAG_RD | CTLFLAG_LOCKED,
502 &ktrace_state, 0,
503 "");
504
505SYSCTL_INT(_ktrace, OID_AUTO, owning_pid, CTLFLAG_RD | CTLFLAG_LOCKED,
506 &ktrace_owning_pid, 0,
507 "pid of the process that owns ktrace");
508
509SYSCTL_INT(_ktrace, OID_AUTO, background_pid, CTLFLAG_RD | CTLFLAG_LOCKED,
510 &ktrace_bg_pid, 0,
511 "pid of the background ktrace tool");
512
513SYSCTL_STRING(_ktrace, OID_AUTO, configured_by, CTLFLAG_RD | CTLFLAG_LOCKED,
514 ktrace_last_owner_execname, 0,
515 "execname of process that last configured ktrace");
516
517SYSCTL_PROC(_ktrace, OID_AUTO, init_background, CTLFLAG_RW | CTLFLAG_LOCKED,
518 (void *)SYSCTL_INIT_BACKGROUND, sizeof(int),
519 ktrace_sysctl, "I", "initialize calling process as background");
520
521static int
522ktrace_sysctl SYSCTL_HANDLER_ARGS
523{
524#pragma unused(oidp, arg2)
525 int ret = 0;
526 uintptr_t type = (uintptr_t)arg1;
527
528 ktrace_lock();
529
530 if (!kauth_cred_issuser(kauth_cred_get())) {
531 ret = EPERM;
532 goto out;
533 }
534
535 if (type == SYSCTL_INIT_BACKGROUND) {
536 if (req->newptr != USER_ADDR_NULL) {
537 ret = ktrace_init_background();
538 goto out;
539 } else {
540 ret = EINVAL;
541 goto out;
542 }
543 } else {
544 ret = EINVAL;
545 goto out;
546 }
547
548out:
549 ktrace_unlock();
550 return ret;
551}