]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/process_policy.c
xnu-6153.11.26.tar.gz
[apple/xnu.git] / bsd / kern / process_policy.c
1 /*
2 * Copyright (c) 2005-2016 Apple Computer, 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 * process policy syscall implementation
31 */
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/malloc.h>
37 #include <sys/priv.h>
38 #include <sys/proc_internal.h>
39 #include <sys/proc.h>
40 #include <sys/kauth.h>
41 #include <sys/unistd.h>
42 #include <sys/buf.h>
43 #include <sys/ioctl.h>
44 #include <sys/vm.h>
45 #include <sys/user.h>
46
47 #include <mach/machine.h>
48 #include <mach/mach_types.h>
49 #include <mach/vm_param.h>
50 #include <kern/task.h>
51 #include <kern/kalloc.h>
52 #include <kern/assert.h>
53 #include <kern/policy_internal.h>
54
55 #include <vm/vm_kern.h>
56 #include <vm/vm_map.h>
57 #include <mach/host_info.h>
58 #include <mach/task_info.h>
59 #include <mach/thread_info.h>
60 #include <mach/vm_region.h>
61
62 #include <sys/process_policy.h>
63 #include <sys/proc_info.h>
64 #include <sys/bsdtask_info.h>
65 #include <sys/kdebug.h>
66 #include <sys/sysproto.h>
67 #include <sys/msgbuf.h>
68
69 #include <machine/machine_routines.h>
70
71 #include <kern/ipc_misc.h>
72 #include <vm/vm_protos.h>
73
74 #if CONFIG_EMBEDDED
75 #include <sys/kern_memorystatus.h>
76 #endif /* CONFIG_EMBEDDED */
77
78 #if CONFIG_MACF
79 #include <security/mac.h>
80 #include <security/mac_framework.h>
81 #endif /* CONFIG_MACF */
82
83 static int handle_lowresource(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
84 static int handle_cpuuse(int action, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
85 static int handle_apptype(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
86 static int handle_boost(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
87
88 extern kern_return_t task_suspend(task_t);
89 extern kern_return_t task_resume(task_t);
90
91 #if CONFIG_EMBEDDED
92 static int handle_applifecycle(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
93 #endif /* CONFIG_EMBEDDED */
94
95 /***************************** process_policy ********************/
96
97 /*
98 * int process_policy(int scope, int action, int policy, int policy_subtype,
99 * proc_policy_attribute_t * attrp, pid_t target_pid,
100 * uint64_t target_threadid)
101 *{ int process_policy(int scope, int action, int policy, int policy_subtype,
102 * user_addr_t attrp, pid_t target_pid, uint64_t target_threadid); }
103 */
104
105 /* system call implementation */
106 int
107 process_policy(__unused struct proc *p, struct process_policy_args * uap, __unused int32_t *retval)
108 {
109 int error = 0;
110 int scope = uap->scope;
111 int policy = uap->policy;
112 int action = uap->action;
113 int policy_subtype = uap->policy_subtype;
114 user_addr_t attrp = uap->attrp;
115 pid_t target_pid = uap->target_pid;
116 uint64_t target_threadid = uap->target_threadid;
117 proc_t target_proc = PROC_NULL;
118 #if CONFIG_MACF || !CONFIG_EMBEDDED
119 proc_t curp = current_proc();
120 #endif
121 kauth_cred_t my_cred;
122 #if CONFIG_EMBEDDED
123 kauth_cred_t target_cred;
124 #endif
125
126 if ((scope != PROC_POLICY_SCOPE_PROCESS) && (scope != PROC_POLICY_SCOPE_THREAD)) {
127 return EINVAL;
128 }
129
130 if (target_pid == 0 || target_pid == proc_selfpid()) {
131 target_proc = proc_self();
132 } else {
133 target_proc = proc_find(target_pid);
134 }
135
136 if (target_proc == PROC_NULL) {
137 return ESRCH;
138 }
139
140 my_cred = kauth_cred_get();
141
142 #if CONFIG_EMBEDDED
143 target_cred = kauth_cred_proc_ref(target_proc);
144
145 if (!kauth_cred_issuser(my_cred) && kauth_cred_getruid(my_cred) &&
146 kauth_cred_getuid(my_cred) != kauth_cred_getuid(target_cred) &&
147 kauth_cred_getruid(my_cred) != kauth_cred_getuid(target_cred))
148 #else
149 /*
150 * Resoure starvation control can be used by unpriv resource owner but priv at the time of ownership claim. This is
151 * checked in low resource handle routine. So bypass the checks here.
152 */
153 if ((policy != PROC_POLICY_RESOURCE_STARVATION) &&
154 (policy != PROC_POLICY_APPTYPE) &&
155 (!kauth_cred_issuser(my_cred) && curp != target_proc))
156 #endif
157 {
158 error = EPERM;
159 goto out;
160 }
161
162 #if CONFIG_MACF
163 switch (policy) {
164 case PROC_POLICY_BOOST:
165 case PROC_POLICY_RESOURCE_USAGE:
166 #if CONFIG_EMBEDDED
167 case PROC_POLICY_APPTYPE:
168 case PROC_POLICY_APP_LIFECYCLE:
169 #endif
170 /* These policies do their own appropriate mac checks */
171 break;
172 default:
173 error = mac_proc_check_sched(curp, target_proc);
174 if (error) {
175 goto out;
176 }
177 break;
178 }
179 #endif /* CONFIG_MACF */
180
181 switch (policy) {
182 case PROC_POLICY_BACKGROUND:
183 error = ENOTSUP;
184 break;
185 case PROC_POLICY_HARDWARE_ACCESS:
186 error = ENOTSUP;
187 break;
188 case PROC_POLICY_RESOURCE_STARVATION:
189 error = handle_lowresource(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
190 break;
191 case PROC_POLICY_RESOURCE_USAGE:
192 switch (policy_subtype) {
193 case PROC_POLICY_RUSAGE_NONE:
194 case PROC_POLICY_RUSAGE_WIREDMEM:
195 case PROC_POLICY_RUSAGE_VIRTMEM:
196 case PROC_POLICY_RUSAGE_DISK:
197 case PROC_POLICY_RUSAGE_NETWORK:
198 case PROC_POLICY_RUSAGE_POWER:
199 error = ENOTSUP;
200 goto out;
201 default:
202 error = EINVAL;
203 goto out;
204 case PROC_POLICY_RUSAGE_CPU:
205 break;
206 }
207
208 error = handle_cpuuse(action, attrp, target_proc, target_threadid);
209 break;
210 #if CONFIG_EMBEDDED
211 case PROC_POLICY_APP_LIFECYCLE:
212 error = handle_applifecycle(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
213 break;
214 #endif /* CONFIG_EMBEDDED */
215 case PROC_POLICY_APPTYPE:
216 error = handle_apptype(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
217 break;
218 case PROC_POLICY_BOOST:
219 error = handle_boost(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
220 break;
221 default:
222 error = EINVAL;
223 break;
224 }
225
226 out:
227 proc_rele(target_proc);
228 #if CONFIG_EMBEDDED
229 kauth_cred_unref(&target_cred);
230 #endif
231 return error;
232 }
233
234 static int
235 handle_lowresource(__unused int scope, int action, __unused int policy, int policy_subtype, __unused user_addr_t attrp, proc_t proc, __unused uint64_t target_threadid)
236 {
237 int error = 0;
238
239 switch (policy_subtype) {
240 case PROC_POLICY_RS_NONE:
241 case PROC_POLICY_RS_VIRTUALMEM:
242 break;
243 default:
244 return EINVAL;
245 }
246
247 if (action == PROC_POLICY_ACTION_RESTORE) {
248 error = proc_resetpcontrol(proc_pid(proc));
249 } else {
250 error = EINVAL;
251 }
252
253 return error;
254 }
255
256
257 static int
258 handle_cpuuse(int action, user_addr_t attrp, proc_t proc, __unused uint64_t target_threadid)
259 {
260 proc_policy_cpuusage_attr_t cpuattr = { };
261 #if CONFIG_MACF || !CONFIG_EMBEDDED
262 proc_t curp = current_proc();
263 #endif
264 Boolean privileged = FALSE;
265 Boolean canEnable = FALSE;
266 uint64_t interval = -1ULL;
267 int error = 0;
268 uint8_t percentage;
269
270 #if !CONFIG_EMBEDDED
271 /* On macOS, tasks can only set and clear their own CPU limits. */
272 if ((action == PROC_POLICY_ACTION_APPLY || action == PROC_POLICY_ACTION_RESTORE)
273 && curp != proc) {
274 return EPERM;
275 }
276 /* No privilege required on macOS. */
277 privileged = TRUE;
278 #endif
279
280 #if CONFIG_MACF
281 /* Is caller privileged to set less-restrictive scheduling parameters? */
282 if (!privileged) {
283 privileged = (priv_check_cred(kauth_cred_get(), PRIV_PROC_CPUMON_OVERRIDE, 0) == 0);
284 }
285 canEnable = (privileged && action == PROC_POLICY_ACTION_ENABLE);
286
287 if (!canEnable && curp != proc) {
288 /*
289 * Can the current process change scheduling parameters for
290 * the target process?
291 */
292 error = mac_proc_check_sched(curp, proc);
293 if (error) {
294 return error;
295 }
296 }
297 #endif
298
299 switch (action) {
300 case PROC_POLICY_ACTION_GET:
301 error = proc_get_task_ruse_cpu(proc->task, &cpuattr.ppattr_cpu_attr,
302 &percentage,
303 &cpuattr.ppattr_cpu_attr_interval,
304 &cpuattr.ppattr_cpu_attr_deadline);
305 if (error == 0) {
306 cpuattr.ppattr_cpu_percentage = percentage;
307 cpuattr.ppattr_cpu_attr_interval /= NSEC_PER_SEC;
308 error = copyout((proc_policy_cpuusage_attr_t *)&cpuattr, (user_addr_t)attrp, sizeof(proc_policy_cpuusage_attr_t));
309 }
310 break;
311
312 case PROC_POLICY_ACTION_APPLY:
313 case PROC_POLICY_ACTION_SET:
314 error = copyin((user_addr_t)attrp, (proc_policy_cpuusage_attr_t *)&cpuattr, sizeof(proc_policy_cpuusage_attr_t));
315 if (error != 0) {
316 return error;
317 }
318
319 /*
320 * The process_policy API uses seconds as the units for the interval,
321 * but the mach task policy SPI uses nanoseconds. Do the conversion,
322 * but preserve -1 as it has special meaning.
323 */
324 if (cpuattr.ppattr_cpu_attr_interval != -1ULL) {
325 interval = cpuattr.ppattr_cpu_attr_interval * NSEC_PER_SEC;
326 } else {
327 interval = -1ULL;
328 }
329
330 error = proc_set_task_ruse_cpu(proc->task, cpuattr.ppattr_cpu_attr,
331 cpuattr.ppattr_cpu_percentage,
332 interval,
333 cpuattr.ppattr_cpu_attr_deadline,
334 privileged);
335 break;
336
337 /* restore process to prior state */
338 case PROC_POLICY_ACTION_RESTORE:
339 error = proc_clear_task_ruse_cpu(proc->task, privileged);
340 break;
341
342 /* re-enable suspended monitor */
343 case PROC_POLICY_ACTION_ENABLE:
344 error = task_resume_cpumon(proc->task);
345 break;
346
347 case PROC_POLICY_ACTION_REMOVE:
348
349 default:
350 error = EINVAL;
351 break;
352 }
353
354 return error;
355 }
356
357 #if CONFIG_EMBEDDED
358 static int
359 handle_applifecycle(__unused int scope,
360 int action,
361 __unused int policy,
362 int policy_subtype,
363 user_addr_t attrp,
364 proc_t proc,
365 uint64_t target_threadid)
366 {
367 int error = 0;
368 int state = 0;
369
370 switch (policy_subtype) {
371 case PROC_POLICY_APPLIFE_NONE:
372 error = 0;
373 break;
374
375 case PROC_POLICY_APPLIFE_STATE:
376 /* appstate is no longer supported */
377 error = ENOTSUP;
378 break;
379
380 case PROC_POLICY_APPLIFE_DEVSTATUS:
381 #if CONFIG_MACF
382 /* ToDo - this should be a generic check, since we could potentially hang other behaviours here. */
383 error = mac_proc_check_suspend_resume(current_proc(), MAC_PROC_CHECK_HIBERNATE);
384 if (error) {
385 error = EPERM;
386 goto out;
387 }
388 #endif
389 #if CONFIG_MEMORYSTATUS
390 if (action == PROC_POLICY_ACTION_APPLY) {
391 /* Used as a freeze hint */
392 memorystatus_on_inactivity(proc);
393
394 /* in future use devicestatus for pid_socketshutdown() */
395 error = 0;
396 } else
397 #endif
398 {
399 error = EINVAL;
400 }
401 break;
402
403 case PROC_POLICY_APPLIFE_PIDBIND:
404 #if CONFIG_MACF
405 error = mac_proc_check_suspend_resume(current_proc(), MAC_PROC_CHECK_PIDBIND);
406 if (error) {
407 error = EPERM;
408 goto out;
409 }
410 #endif
411 error = copyin((user_addr_t)attrp, (int *)&state, sizeof(int));
412 if (error != 0) {
413 goto out;
414 }
415 if (action == PROC_POLICY_ACTION_APPLY) {
416 /* bind the thread in target_thread in current process to target_proc */
417 error = proc_lf_pidbind(current_task(), target_threadid, proc->task, state);
418 } else {
419 error = EINVAL;
420 }
421 break;
422 default:
423 error = EINVAL;
424 break;
425 }
426
427 out:
428 return error;
429 }
430 #endif /* CONFIG_EMBEDDED */
431
432 static int
433 handle_apptype( int scope,
434 int action,
435 __unused int policy,
436 int policy_subtype,
437 __unused user_addr_t attrp,
438 proc_t target_proc,
439 __unused uint64_t target_threadid)
440 {
441 int error = 0;
442
443 if (scope != PROC_POLICY_SCOPE_PROCESS) {
444 return EINVAL;
445 }
446
447 /* Temporary compatibility with old importance donation interface until libproc is moved to new boost calls */
448 switch (policy_subtype) {
449 case PROC_POLICY_IOS_DONATEIMP:
450 if (action != PROC_POLICY_ACTION_ENABLE) {
451 return EINVAL;
452 }
453 if (target_proc != current_proc()) {
454 return EINVAL;
455 }
456
457 /* PROCESS ENABLE APPTYPE DONATEIMP */
458 task_importance_mark_donor(target_proc->task, TRUE);
459
460 return 0;
461
462 case PROC_POLICY_IOS_HOLDIMP:
463 if (action != PROC_POLICY_ACTION_ENABLE) {
464 return EINVAL;
465 }
466 if (target_proc != current_proc()) {
467 return EINVAL;
468 }
469
470 /* PROCESS ENABLE APPTYPE HOLDIMP */
471 error = task_importance_hold_legacy_external_assertion(current_task(), 1);
472
473 return error;
474
475 case PROC_POLICY_IOS_DROPIMP:
476 if (action != PROC_POLICY_ACTION_ENABLE) {
477 return EINVAL;
478 }
479 if (target_proc != current_proc()) {
480 return EINVAL;
481 }
482
483 /* PROCESS ENABLE APPTYPE DROPIMP */
484 error = task_importance_drop_legacy_external_assertion(current_task(), 1);
485
486 return error;
487
488 default:
489 /* continue to TAL handling */
490 break;
491 }
492
493 if (policy_subtype != PROC_POLICY_OSX_APPTYPE_TAL) {
494 return EINVAL;
495 }
496
497 /* need to be super user to do this */
498 if (kauth_cred_issuser(kauth_cred_get()) == 0) {
499 return EPERM;
500 }
501
502 if (proc_task_is_tal(target_proc->task) == FALSE) {
503 return EINVAL;
504 }
505
506 switch (action) {
507 case PROC_POLICY_ACTION_ENABLE:
508 /* PROCESS ENABLE APPTYPE TAL */
509 proc_set_task_policy(target_proc->task,
510 TASK_POLICY_ATTRIBUTE, TASK_POLICY_TAL,
511 TASK_POLICY_ENABLE);
512 break;
513 case PROC_POLICY_ACTION_DISABLE:
514 /* PROCESS DISABLE APPTYPE TAL */
515 proc_set_task_policy(target_proc->task,
516 TASK_POLICY_ATTRIBUTE, TASK_POLICY_TAL,
517 TASK_POLICY_DISABLE);
518 break;
519 default:
520 return EINVAL;
521 }
522
523 return 0;
524 }
525
526 static int
527 handle_boost(int scope,
528 int action,
529 __unused int policy,
530 int policy_subtype,
531 __unused user_addr_t attrp,
532 proc_t target_proc,
533 __unused uint64_t target_threadid)
534 {
535 int error = 0;
536
537 assert(policy == PROC_POLICY_BOOST);
538
539 if (scope != PROC_POLICY_SCOPE_PROCESS) {
540 return EINVAL;
541 }
542
543 if (target_proc != current_proc()) {
544 return EINVAL;
545 }
546
547 switch (policy_subtype) {
548 case PROC_POLICY_IMP_IMPORTANT:
549 if (task_is_importance_receiver_type(target_proc->task) == FALSE) {
550 return EINVAL;
551 }
552
553 switch (action) {
554 case PROC_POLICY_ACTION_HOLD:
555 /* PROCESS HOLD BOOST IMPORTANT */
556 error = task_importance_hold_legacy_external_assertion(current_task(), 1);
557 break;
558 case PROC_POLICY_ACTION_DROP:
559 /* PROCESS DROP BOOST IMPORTANT */
560 error = task_importance_drop_legacy_external_assertion(current_task(), 1);
561 break;
562 default:
563 error = (EINVAL);
564 break;
565 }
566 break;
567
568 case PROC_POLICY_IMP_DONATION:
569 #if CONFIG_MACF
570 error = mac_proc_check_sched(current_proc(), target_proc);
571 if (error) {
572 return error;
573 }
574 #endif
575 switch (action) {
576 case PROC_POLICY_ACTION_SET:
577 /* PROCESS SET BOOST DONATION */
578 task_importance_mark_donor(target_proc->task, TRUE);
579 break;
580 default:
581 error = (EINVAL);
582 break;
583 }
584 break;
585
586 default:
587 error = (EINVAL);
588 break;
589 }
590
591 return error;
592 }
593
594
595 /*
596 * KPI to determine if a pid is currently backgrounded.
597 * Returns ESRCH if pid cannot be found or has started exiting.
598 * Returns EINVAL if state is NULL.
599 * Sets *state to 1 if pid is backgrounded, and 0 otherwise.
600 */
601 int
602 proc_pidbackgrounded(pid_t pid, uint32_t* state)
603 {
604 proc_t target_proc = PROC_NULL;
605
606 if (state == NULL) {
607 return EINVAL;
608 }
609
610 target_proc = proc_find(pid);
611
612 if (target_proc == PROC_NULL) {
613 return ESRCH;
614 }
615
616 if (proc_get_effective_task_policy(target_proc->task, TASK_POLICY_DARWIN_BG)) {
617 *state = 1;
618 } else {
619 *state = 0;
620 }
621
622 proc_rele(target_proc);
623 return 0;
624 }
625
626 /*
627 * Get the darwin background state of the originator. If the current
628 * process app type is App, then it is the originator, else if it is
629 * a Daemon, then creator of the Resource Accounting attribute of
630 * the current thread voucher is the originator of the work.
631 */
632 int
633 proc_get_originatorbgstate(uint32_t *is_backgrounded)
634 {
635 uint32_t bgstate;
636 proc_t p = current_proc();
637 uint32_t flagsp = 0;
638 kern_return_t kr;
639 pid_t pid;
640 int ret;
641 thread_t thread = current_thread();
642
643 bgstate = proc_get_effective_thread_policy(thread, TASK_POLICY_DARWIN_BG);
644
645 /* If current thread or task backgrounded, return background */
646 if (bgstate) {
647 *is_backgrounded = 1;
648 return 0;
649 }
650
651 /* Check if current process app type is App, then return foreground */
652 proc_get_darwinbgstate(p->task, &flagsp);
653 if ((flagsp & PROC_FLAG_APPLICATION) == PROC_FLAG_APPLICATION) {
654 *is_backgrounded = 0;
655 return 0;
656 }
657
658 /*
659 * Get the current voucher origin pid and it's bgstate.The pid
660 * returned here might not be valid or may have been recycled.
661 */
662 kr = thread_get_current_voucher_origin_pid(&pid);
663 if (kr != KERN_SUCCESS) {
664 if (kr == KERN_INVALID_TASK) {
665 return ESRCH;
666 } else if (kr == KERN_INVALID_VALUE) {
667 return ENOATTR;
668 } else {
669 return EINVAL;
670 }
671 }
672
673 ret = proc_pidbackgrounded(pid, is_backgrounded);
674 return ret;
675 }
676
677 int
678 proc_apply_resource_actions(void * bsdinfo, __unused int type, int action)
679 {
680 proc_t p = (proc_t)bsdinfo;
681
682 switch (action) {
683 case PROC_POLICY_RSRCACT_THROTTLE:
684 /* no need to do anything */
685 break;
686
687 case PROC_POLICY_RSRCACT_SUSPEND:
688 task_suspend(p->task);
689 break;
690
691 case PROC_POLICY_RSRCACT_TERMINATE:
692 psignal(p, SIGKILL);
693 break;
694
695 case PROC_POLICY_RSRCACT_NOTIFY_KQ:
696 /* not implemented */
697 break;
698
699 case PROC_POLICY_RSRCACT_NOTIFY_EXC:
700 panic("shouldn't be applying exception notification to process!");
701 break;
702 }
703
704 return 0;
705 }
706
707 int
708 proc_restore_resource_actions(void * bsdinfo, __unused int type, int action)
709 {
710 proc_t p = (proc_t)bsdinfo;
711
712 switch (action) {
713 case PROC_POLICY_RSRCACT_THROTTLE:
714 case PROC_POLICY_RSRCACT_TERMINATE:
715 case PROC_POLICY_RSRCACT_NOTIFY_KQ:
716 case PROC_POLICY_RSRCACT_NOTIFY_EXC:
717 /* no need to do anything */
718 break;
719
720 case PROC_POLICY_RSRCACT_SUSPEND:
721 task_resume(p->task);
722 break;
723 }
724
725 return 0;
726 }