2 * Copyright (c) 2005-2016 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 * process policy syscall implementation
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/malloc.h>
38 #include <sys/proc_internal.h>
40 #include <sys/kauth.h>
41 #include <sys/unistd.h>
43 #include <sys/ioctl.h>
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>
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>
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>
69 #include <machine/machine_routines.h>
71 #include <kern/ipc_misc.h>
72 #include <vm/vm_protos.h>
75 #include <sys/kern_memorystatus.h>
76 #endif /* CONFIG_EMBEDDED */
79 #include <security/mac.h>
80 #include <security/mac_framework.h>
81 #endif /* CONFIG_MACF */
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
);
88 extern kern_return_t
task_suspend(task_t
);
89 extern kern_return_t
task_resume(task_t
);
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 */
95 /***************************** process_policy ********************/
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); }
105 /* system call implementation */
107 process_policy(__unused
struct proc
*p
, struct process_policy_args
* uap
, __unused
int32_t *retval
)
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();
121 kauth_cred_t my_cred
;
123 kauth_cred_t target_cred
;
126 if ((scope
!= PROC_POLICY_SCOPE_PROCESS
) && (scope
!= PROC_POLICY_SCOPE_THREAD
)) {
130 if (target_pid
== 0 || target_pid
== proc_selfpid())
131 target_proc
= proc_self();
133 target_proc
= proc_find(target_pid
);
135 if (target_proc
== PROC_NULL
)
138 my_cred
= kauth_cred_get();
141 target_cred
= kauth_cred_proc_ref(target_proc
);
143 if (!kauth_cred_issuser(my_cred
) && kauth_cred_getruid(my_cred
) &&
144 kauth_cred_getuid(my_cred
) != kauth_cred_getuid(target_cred
) &&
145 kauth_cred_getruid(my_cred
) != kauth_cred_getuid(target_cred
))
148 * Resoure starvation control can be used by unpriv resource owner but priv at the time of ownership claim. This is
149 * checked in low resource handle routine. So bypass the checks here.
151 if ((policy
!= PROC_POLICY_RESOURCE_STARVATION
) &&
152 (policy
!= PROC_POLICY_APPTYPE
) &&
153 (!kauth_cred_issuser(my_cred
) && curp
!= p
))
162 case PROC_POLICY_BOOST
:
163 case PROC_POLICY_RESOURCE_USAGE
:
165 case PROC_POLICY_APPTYPE
:
166 case PROC_POLICY_APP_LIFECYCLE
:
168 /* These policies do their own appropriate mac checks */
171 error
= mac_proc_check_sched(curp
, target_proc
);
175 #endif /* CONFIG_MACF */
178 case PROC_POLICY_BACKGROUND
:
181 case PROC_POLICY_HARDWARE_ACCESS
:
184 case PROC_POLICY_RESOURCE_STARVATION
:
185 error
= handle_lowresource(scope
, action
, policy
, policy_subtype
, attrp
, target_proc
, target_threadid
);
187 case PROC_POLICY_RESOURCE_USAGE
:
188 switch(policy_subtype
) {
189 case PROC_POLICY_RUSAGE_NONE
:
190 case PROC_POLICY_RUSAGE_WIREDMEM
:
191 case PROC_POLICY_RUSAGE_VIRTMEM
:
192 case PROC_POLICY_RUSAGE_DISK
:
193 case PROC_POLICY_RUSAGE_NETWORK
:
194 case PROC_POLICY_RUSAGE_POWER
:
200 case PROC_POLICY_RUSAGE_CPU
:
204 error
= handle_cpuuse(action
, attrp
, target_proc
, target_threadid
);
207 case PROC_POLICY_APP_LIFECYCLE
:
208 error
= handle_applifecycle(scope
, action
, policy
, policy_subtype
, attrp
, target_proc
, target_threadid
);
210 #endif /* CONFIG_EMBEDDED */
211 case PROC_POLICY_APPTYPE
:
212 error
= handle_apptype(scope
, action
, policy
, policy_subtype
, attrp
, target_proc
, target_threadid
);
214 case PROC_POLICY_BOOST
:
215 error
= handle_boost(scope
, action
, policy
, policy_subtype
, attrp
, target_proc
, target_threadid
);
223 proc_rele(target_proc
);
225 kauth_cred_unref(&target_cred
);
231 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
)
235 switch(policy_subtype
) {
236 case PROC_POLICY_RS_NONE
:
237 case PROC_POLICY_RS_VIRTUALMEM
:
243 if (action
== PROC_POLICY_ACTION_RESTORE
)
244 error
= proc_resetpcontrol(proc_pid(proc
));
253 handle_cpuuse(int action
, user_addr_t attrp
, proc_t proc
, __unused
uint64_t target_threadid
)
255 proc_policy_cpuusage_attr_t cpuattr
= { };
256 #if CONFIG_MACF || !CONFIG_EMBEDDED
257 proc_t curp
= current_proc();
259 Boolean privileged
= FALSE
;
260 Boolean canEnable
= FALSE
;
261 uint64_t interval
= -1ULL;
266 /* On macOS, tasks can only set and clear their own CPU limits. */
267 if ((action
== PROC_POLICY_ACTION_APPLY
|| action
== PROC_POLICY_ACTION_RESTORE
)
271 /* No privilege required on macOS. */
276 /* Is caller privileged to set less-restrictive scheduling parameters? */
278 privileged
= (priv_check_cred(kauth_cred_get(), PRIV_PROC_CPUMON_OVERRIDE
, 0) == 0);
280 canEnable
= (privileged
&& action
== PROC_POLICY_ACTION_ENABLE
);
282 if (!canEnable
&& curp
!= proc
) {
284 * Can the current process change scheduling parameters for
285 * the target process?
287 error
= mac_proc_check_sched(curp
, proc
);
288 if (error
) return error
;
293 case PROC_POLICY_ACTION_GET
:
294 error
= proc_get_task_ruse_cpu(proc
->task
, &cpuattr
.ppattr_cpu_attr
,
296 &cpuattr
.ppattr_cpu_attr_interval
,
297 &cpuattr
.ppattr_cpu_attr_deadline
);
299 cpuattr
.ppattr_cpu_percentage
= percentage
;
300 cpuattr
.ppattr_cpu_attr_interval
/= NSEC_PER_SEC
;
301 error
= copyout((proc_policy_cpuusage_attr_t
*)&cpuattr
, (user_addr_t
)attrp
, sizeof(proc_policy_cpuusage_attr_t
));
305 case PROC_POLICY_ACTION_APPLY
:
306 case PROC_POLICY_ACTION_SET
:
307 error
= copyin((user_addr_t
)attrp
, (proc_policy_cpuusage_attr_t
*)&cpuattr
, sizeof(proc_policy_cpuusage_attr_t
));
313 * The process_policy API uses seconds as the units for the interval,
314 * but the mach task policy SPI uses nanoseconds. Do the conversion,
315 * but preserve -1 as it has special meaning.
317 if (cpuattr
.ppattr_cpu_attr_interval
!= -1ULL) {
318 interval
= cpuattr
.ppattr_cpu_attr_interval
* NSEC_PER_SEC
;
323 error
= proc_set_task_ruse_cpu(proc
->task
, cpuattr
.ppattr_cpu_attr
,
324 cpuattr
.ppattr_cpu_percentage
,
326 cpuattr
.ppattr_cpu_attr_deadline
,
330 /* restore process to prior state */
331 case PROC_POLICY_ACTION_RESTORE
:
332 error
= proc_clear_task_ruse_cpu(proc
->task
, privileged
);
335 /* re-enable suspended monitor */
336 case PROC_POLICY_ACTION_ENABLE
:
337 error
= task_resume_cpumon(proc
->task
);
340 case PROC_POLICY_ACTION_REMOVE
:
353 handle_applifecycle(__unused
int scope
,
359 uint64_t target_threadid
)
364 switch(policy_subtype
) {
365 case PROC_POLICY_APPLIFE_NONE
:
369 case PROC_POLICY_APPLIFE_STATE
:
370 /* appstate is no longer supported */
374 case PROC_POLICY_APPLIFE_DEVSTATUS
:
376 /* ToDo - this should be a generic check, since we could potentially hang other behaviours here. */
377 error
= mac_proc_check_suspend_resume(current_proc(), MAC_PROC_CHECK_HIBERNATE
);
383 #if CONFIG_MEMORYSTATUS
384 if (action
== PROC_POLICY_ACTION_APPLY
) {
385 /* Used as a freeze hint */
386 memorystatus_on_inactivity(proc
);
388 /* in future use devicestatus for pid_socketshutdown() */
397 case PROC_POLICY_APPLIFE_PIDBIND
:
399 error
= mac_proc_check_suspend_resume(current_proc(), MAC_PROC_CHECK_PIDBIND
);
405 error
= copyin((user_addr_t
)attrp
, (int *)&state
, sizeof(int));
408 if (action
== PROC_POLICY_ACTION_APPLY
) {
409 /* bind the thread in target_thread in current process to target_proc */
410 error
= proc_lf_pidbind(current_task(), target_threadid
, proc
->task
, state
);
422 #endif /* CONFIG_EMBEDDED */
425 handle_apptype( int scope
,
429 __unused user_addr_t attrp
,
431 __unused
uint64_t target_threadid
)
435 if (scope
!= PROC_POLICY_SCOPE_PROCESS
)
438 /* Temporary compatibility with old importance donation interface until libproc is moved to new boost calls */
439 switch (policy_subtype
) {
440 case PROC_POLICY_IOS_DONATEIMP
:
441 if (action
!= PROC_POLICY_ACTION_ENABLE
)
443 if (target_proc
!= current_proc())
446 /* PROCESS ENABLE APPTYPE DONATEIMP */
447 task_importance_mark_donor(target_proc
->task
, TRUE
);
451 case PROC_POLICY_IOS_HOLDIMP
:
452 if (action
!= PROC_POLICY_ACTION_ENABLE
)
454 if (target_proc
!= current_proc())
457 /* PROCESS ENABLE APPTYPE HOLDIMP */
458 error
= task_importance_hold_legacy_external_assertion(current_task(), 1);
462 case PROC_POLICY_IOS_DROPIMP
:
463 if (action
!= PROC_POLICY_ACTION_ENABLE
)
465 if (target_proc
!= current_proc())
468 /* PROCESS ENABLE APPTYPE DROPIMP */
469 error
= task_importance_drop_legacy_external_assertion(current_task(), 1);
474 /* continue to TAL handling */
478 if (policy_subtype
!= PROC_POLICY_OSX_APPTYPE_TAL
)
481 /* need to be super user to do this */
482 if (kauth_cred_issuser(kauth_cred_get()) == 0)
485 if (proc_task_is_tal(target_proc
->task
) == FALSE
)
489 case PROC_POLICY_ACTION_ENABLE
:
490 /* PROCESS ENABLE APPTYPE TAL */
491 proc_set_task_policy(target_proc
->task
,
492 TASK_POLICY_ATTRIBUTE
, TASK_POLICY_TAL
,
495 case PROC_POLICY_ACTION_DISABLE
:
496 /* PROCESS DISABLE APPTYPE TAL */
497 proc_set_task_policy(target_proc
->task
,
498 TASK_POLICY_ATTRIBUTE
, TASK_POLICY_TAL
,
499 TASK_POLICY_DISABLE
);
509 handle_boost(int scope
,
513 __unused user_addr_t attrp
,
515 __unused
uint64_t target_threadid
)
519 assert(policy
== PROC_POLICY_BOOST
);
521 if (scope
!= PROC_POLICY_SCOPE_PROCESS
)
524 if (target_proc
!= current_proc())
527 switch(policy_subtype
) {
528 case PROC_POLICY_IMP_IMPORTANT
:
529 if (task_is_importance_receiver_type(target_proc
->task
) == FALSE
)
533 case PROC_POLICY_ACTION_HOLD
:
534 /* PROCESS HOLD BOOST IMPORTANT */
535 error
= task_importance_hold_legacy_external_assertion(current_task(), 1);
537 case PROC_POLICY_ACTION_DROP
:
538 /* PROCESS DROP BOOST IMPORTANT */
539 error
= task_importance_drop_legacy_external_assertion(current_task(), 1);
547 case PROC_POLICY_IMP_DONATION
:
549 error
= mac_proc_check_sched(current_proc(), target_proc
);
550 if (error
) return error
;
553 case PROC_POLICY_ACTION_SET
:
554 /* PROCESS SET BOOST DONATION */
555 task_importance_mark_donor(target_proc
->task
, TRUE
);
573 * KPI to determine if a pid is currently backgrounded.
574 * Returns ESRCH if pid cannot be found or has started exiting.
575 * Returns EINVAL if state is NULL.
576 * Sets *state to 1 if pid is backgrounded, and 0 otherwise.
579 proc_pidbackgrounded(pid_t pid
, uint32_t* state
)
581 proc_t target_proc
= PROC_NULL
;
586 target_proc
= proc_find(pid
);
588 if (target_proc
== PROC_NULL
)
591 if ( proc_get_effective_task_policy(target_proc
->task
, TASK_POLICY_DARWIN_BG
) ) {
597 proc_rele(target_proc
);
602 * Get the darwin background state of the originator. If the current
603 * process app type is App, then it is the originator, else if it is
604 * a Daemon, then creator of the Resource Accounting attribute of
605 * the current thread voucher is the originator of the work.
608 proc_get_originatorbgstate(uint32_t *is_backgrounded
)
611 proc_t p
= current_proc();
616 thread_t thread
= current_thread();
618 bgstate
= proc_get_effective_thread_policy(thread
, TASK_POLICY_DARWIN_BG
);
620 /* If current thread or task backgrounded, return background */
622 *is_backgrounded
= 1;
626 /* Check if current process app type is App, then return foreground */
627 proc_get_darwinbgstate(p
->task
, &flagsp
);
628 if ((flagsp
& PROC_FLAG_APPLICATION
) == PROC_FLAG_APPLICATION
) {
629 *is_backgrounded
= 0;
634 * Get the current voucher origin pid and it's bgstate.The pid
635 * returned here might not be valid or may have been recycled.
637 kr
= thread_get_current_voucher_origin_pid(&pid
);
638 if (kr
!= KERN_SUCCESS
) {
639 if (kr
== KERN_INVALID_TASK
)
641 else if (kr
== KERN_INVALID_VALUE
)
647 ret
= proc_pidbackgrounded(pid
, is_backgrounded
);
652 proc_apply_resource_actions(void * bsdinfo
, __unused
int type
, int action
)
654 proc_t p
= (proc_t
)bsdinfo
;
657 case PROC_POLICY_RSRCACT_THROTTLE
:
658 /* no need to do anything */
661 case PROC_POLICY_RSRCACT_SUSPEND
:
662 task_suspend(p
->task
);
665 case PROC_POLICY_RSRCACT_TERMINATE
:
669 case PROC_POLICY_RSRCACT_NOTIFY_KQ
:
670 /* not implemented */
673 case PROC_POLICY_RSRCACT_NOTIFY_EXC
:
674 panic("shouldn't be applying exception notification to process!");
682 proc_restore_resource_actions(void * bsdinfo
, __unused
int type
, int action
)
684 proc_t p
= (proc_t
)bsdinfo
;
687 case PROC_POLICY_RSRCACT_THROTTLE
:
688 case PROC_POLICY_RSRCACT_TERMINATE
:
689 case PROC_POLICY_RSRCACT_NOTIFY_KQ
:
690 case PROC_POLICY_RSRCACT_NOTIFY_EXC
:
691 /* no need to do anything */
694 case PROC_POLICY_RSRCACT_SUSPEND
:
695 task_resume(p
->task
);