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
83static int handle_lowresource(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
84static int handle_cpuuse(int action, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
85static int handle_apptype(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
86static int handle_boost(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
87
88extern kern_return_t task_suspend(task_t);
89extern kern_return_t task_resume(task_t);
90
91#if CONFIG_EMBEDDED
92static 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 */
106int
107process_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 if (target_proc == PROC_NULL)
136 return(ESRCH);
137
138 my_cred = kauth_cred_get();
139
140#if CONFIG_EMBEDDED
141 target_cred = kauth_cred_proc_ref(target_proc);
142
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))
146#else
147 /*
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.
150 */
151 if ((policy != PROC_POLICY_RESOURCE_STARVATION) &&
152 (policy != PROC_POLICY_APPTYPE) &&
153 (!kauth_cred_issuser(my_cred) && curp != p))
154#endif
155 {
156 error = EPERM;
157 goto out;
158 }
159
160#if CONFIG_MACF
161 switch (policy) {
162 case PROC_POLICY_BOOST:
163 case PROC_POLICY_RESOURCE_USAGE:
164#if CONFIG_EMBEDDED
165 case PROC_POLICY_APPTYPE:
166 case PROC_POLICY_APP_LIFECYCLE:
167#endif
168 /* These policies do their own appropriate mac checks */
169 break;
170 default:
171 error = mac_proc_check_sched(curp, target_proc);
172 if (error) goto out;
173 break;
174 }
175#endif /* CONFIG_MACF */
176
177 switch(policy) {
178 case PROC_POLICY_BACKGROUND:
179 error = ENOTSUP;
180 break;
181 case PROC_POLICY_HARDWARE_ACCESS:
182 error = ENOTSUP;
183 break;
184 case PROC_POLICY_RESOURCE_STARVATION:
185 error = handle_lowresource(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
186 break;
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:
195 error = ENOTSUP;
196 goto out;
197 default:
198 error = EINVAL;
199 goto out;
200 case PROC_POLICY_RUSAGE_CPU:
201 break;
202 }
203
204 error = handle_cpuuse(action, attrp, target_proc, target_threadid);
205 break;
206#if CONFIG_EMBEDDED
207 case PROC_POLICY_APP_LIFECYCLE:
208 error = handle_applifecycle(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
209 break;
210#endif /* CONFIG_EMBEDDED */
211 case PROC_POLICY_APPTYPE:
212 error = handle_apptype(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
213 break;
214 case PROC_POLICY_BOOST:
215 error = handle_boost(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
216 break;
217 default:
218 error = EINVAL;
219 break;
220 }
221
222out:
223 proc_rele(target_proc);
224#if CONFIG_EMBEDDED
225 kauth_cred_unref(&target_cred);
226#endif
227 return(error);
228}
229
230static int
231handle_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)
232{
233 int error = 0;
234
235 switch(policy_subtype) {
236 case PROC_POLICY_RS_NONE:
237 case PROC_POLICY_RS_VIRTUALMEM:
238 break;
239 default:
240 return(EINVAL);
241 }
242
243 if (action == PROC_POLICY_ACTION_RESTORE)
244 error = proc_resetpcontrol(proc_pid(proc));
245 else
246 error = EINVAL;
247
248 return(error);
249}
250
251
252static int
253handle_cpuuse(int action, user_addr_t attrp, proc_t proc, __unused uint64_t target_threadid)
254{
255 proc_policy_cpuusage_attr_t cpuattr = { };
256#if CONFIG_MACF || !CONFIG_EMBEDDED
257 proc_t curp = current_proc();
258#endif
259 Boolean privileged = FALSE;
260 Boolean canEnable = FALSE;
261 uint64_t interval = -1ULL;
262 int error = 0;
263 uint8_t percentage;
264
265#if !CONFIG_EMBEDDED
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)
268 && curp != proc) {
269 return (EPERM);
270 }
271 /* No privilege required on macOS. */
272 privileged = TRUE;
273#endif
274
275#if CONFIG_MACF
276 /* Is caller privileged to set less-restrictive scheduling parameters? */
277 if (!privileged) {
278 privileged = (priv_check_cred(kauth_cred_get(), PRIV_PROC_CPUMON_OVERRIDE, 0) == 0);
279 }
280 canEnable = (privileged && action == PROC_POLICY_ACTION_ENABLE);
281
282 if (!canEnable && curp != proc) {
283 /*
284 * Can the current process change scheduling parameters for
285 * the target process?
286 */
287 error = mac_proc_check_sched(curp, proc);
288 if (error) return error;
289 }
290#endif
291
292 switch (action) {
293 case PROC_POLICY_ACTION_GET:
294 error = proc_get_task_ruse_cpu(proc->task, &cpuattr.ppattr_cpu_attr,
295 &percentage,
296 &cpuattr.ppattr_cpu_attr_interval,
297 &cpuattr.ppattr_cpu_attr_deadline);
298 if (error == 0) {
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));
302 }
303 break;
304
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));
308 if (error != 0) {
309 return (error);
310 }
311
312 /*
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.
316 */
317 if (cpuattr.ppattr_cpu_attr_interval != -1ULL) {
318 interval = cpuattr.ppattr_cpu_attr_interval * NSEC_PER_SEC;
319 } else {
320 interval = -1ULL;
321 }
322
323 error = proc_set_task_ruse_cpu(proc->task, cpuattr.ppattr_cpu_attr,
324 cpuattr.ppattr_cpu_percentage,
325 interval,
326 cpuattr.ppattr_cpu_attr_deadline,
327 privileged);
328 break;
329
330 /* restore process to prior state */
331 case PROC_POLICY_ACTION_RESTORE:
332 error = proc_clear_task_ruse_cpu(proc->task, privileged);
333 break;
334
335 /* re-enable suspended monitor */
336 case PROC_POLICY_ACTION_ENABLE:
337 error = task_resume_cpumon(proc->task);
338 break;
339
340 case PROC_POLICY_ACTION_REMOVE:
341
342 default:
343 error = EINVAL;
344 break;
345
346 }
347
348 return(error);
349}
350
351#if CONFIG_EMBEDDED
352static int
353handle_applifecycle(__unused int scope,
354 int action,
355 __unused int policy,
356 int policy_subtype,
357 user_addr_t attrp,
358 proc_t proc,
359 uint64_t target_threadid)
360{
361 int error = 0;
362 int state = 0;
363
364 switch(policy_subtype) {
365 case PROC_POLICY_APPLIFE_NONE:
366 error = 0;
367 break;
368
369 case PROC_POLICY_APPLIFE_STATE:
370 /* appstate is no longer supported */
371 error = ENOTSUP;
372 break;
373
374 case PROC_POLICY_APPLIFE_DEVSTATUS:
375#if CONFIG_MACF
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);
378 if (error) {
379 error = EPERM;
380 goto out;
381 }
382#endif
383#if CONFIG_MEMORYSTATUS
384 if (action == PROC_POLICY_ACTION_APPLY) {
385 /* Used as a freeze hint */
386 memorystatus_on_inactivity(proc);
387
388 /* in future use devicestatus for pid_socketshutdown() */
389 error = 0;
390 } else
391#endif
392 {
393 error = EINVAL;
394 }
395 break;
396
397 case PROC_POLICY_APPLIFE_PIDBIND:
398#if CONFIG_MACF
399 error = mac_proc_check_suspend_resume(current_proc(), MAC_PROC_CHECK_PIDBIND);
400 if (error) {
401 error = EPERM;
402 goto out;
403 }
404#endif
405 error = copyin((user_addr_t)attrp, (int *)&state, sizeof(int));
406 if (error != 0)
407 goto out;
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);
411 } else
412 error = EINVAL;
413 break;
414 default:
415 error = EINVAL;
416 break;
417 }
418
419out:
420 return(error);
421}
422#endif /* CONFIG_EMBEDDED */
423
424static int
425handle_apptype( int scope,
426 int action,
427 __unused int policy,
428 int policy_subtype,
429 __unused user_addr_t attrp,
430 proc_t target_proc,
431 __unused uint64_t target_threadid)
432{
433 int error = 0;
434
435 if (scope != PROC_POLICY_SCOPE_PROCESS)
436 return (EINVAL);
437
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)
442 return (EINVAL);
443 if (target_proc != current_proc())
444 return (EINVAL);
445
446 /* PROCESS ENABLE APPTYPE DONATEIMP */
447 task_importance_mark_donor(target_proc->task, TRUE);
448
449 return(0);
450
451 case PROC_POLICY_IOS_HOLDIMP:
452 if (action != PROC_POLICY_ACTION_ENABLE)
453 return (EINVAL);
454 if (target_proc != current_proc())
455 return (EINVAL);
456
457 /* PROCESS ENABLE APPTYPE HOLDIMP */
458 error = task_importance_hold_legacy_external_assertion(current_task(), 1);
459
460 return(error);
461
462 case PROC_POLICY_IOS_DROPIMP:
463 if (action != PROC_POLICY_ACTION_ENABLE)
464 return (EINVAL);
465 if (target_proc != current_proc())
466 return (EINVAL);
467
468 /* PROCESS ENABLE APPTYPE DROPIMP */
469 error = task_importance_drop_legacy_external_assertion(current_task(), 1);
470
471 return(error);
472
473 default:
474 /* continue to TAL handling */
475 break;
476 }
477
478 if (policy_subtype != PROC_POLICY_OSX_APPTYPE_TAL)
479 return (EINVAL);
480
481 /* need to be super user to do this */
482 if (kauth_cred_issuser(kauth_cred_get()) == 0)
483 return (EPERM);
484
485 if (proc_task_is_tal(target_proc->task) == FALSE)
486 return (EINVAL);
487
488 switch (action) {
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,
493 TASK_POLICY_ENABLE);
494 break;
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);
500 break;
501 default:
502 return (EINVAL);
503 }
504
505 return(0);
506}
507
508static int
509handle_boost(int scope,
510 int action,
511 __unused int policy,
512 int policy_subtype,
513 __unused user_addr_t attrp,
514 proc_t target_proc,
515 __unused uint64_t target_threadid)
516{
517 int error = 0;
518
519 assert(policy == PROC_POLICY_BOOST);
520
521 if (scope != PROC_POLICY_SCOPE_PROCESS)
522 return (EINVAL);
523
524 if (target_proc != current_proc())
525 return (EINVAL);
526
527 switch(policy_subtype) {
528 case PROC_POLICY_IMP_IMPORTANT:
529 if (task_is_importance_receiver_type(target_proc->task) == FALSE)
530 return (EINVAL);
531
532 switch (action) {
533 case PROC_POLICY_ACTION_HOLD:
534 /* PROCESS HOLD BOOST IMPORTANT */
535 error = task_importance_hold_legacy_external_assertion(current_task(), 1);
536 break;
537 case PROC_POLICY_ACTION_DROP:
538 /* PROCESS DROP BOOST IMPORTANT */
539 error = task_importance_drop_legacy_external_assertion(current_task(), 1);
540 break;
541 default:
542 error = (EINVAL);
543 break;
544 }
545 break;
546
547 case PROC_POLICY_IMP_DONATION:
548#if CONFIG_MACF
549 error = mac_proc_check_sched(current_proc(), target_proc);
550 if (error) return error;
551#endif
552 switch (action) {
553 case PROC_POLICY_ACTION_SET:
554 /* PROCESS SET BOOST DONATION */
555 task_importance_mark_donor(target_proc->task, TRUE);
556 break;
557 default:
558 error = (EINVAL);
559 break;
560 }
561 break;
562
563 default:
564 error = (EINVAL);
565 break;
566 }
567
568 return(error);
569}
570
571
572/*
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.
577 */
578int
579proc_pidbackgrounded(pid_t pid, uint32_t* state)
580{
581 proc_t target_proc = PROC_NULL;
582
583 if (state == NULL)
584 return(EINVAL);
585
586 target_proc = proc_find(pid);
587
588 if (target_proc == PROC_NULL)
589 return(ESRCH);
590
591 if ( proc_get_effective_task_policy(target_proc->task, TASK_POLICY_DARWIN_BG) ) {
592 *state = 1;
593 } else {
594 *state = 0;
595 }
596
597 proc_rele(target_proc);
598 return (0);
599}
600
601/*
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.
606 */
607int
608proc_get_originatorbgstate(uint32_t *is_backgrounded)
609{
610 uint32_t bgstate;
611 proc_t p = current_proc();
612 uint32_t flagsp = 0;
613 kern_return_t kr;
614 pid_t pid;
615 int ret;
616 thread_t thread = current_thread();
617
618 bgstate = proc_get_effective_thread_policy(thread, TASK_POLICY_DARWIN_BG);
619
620 /* If current thread or task backgrounded, return background */
621 if (bgstate) {
622 *is_backgrounded = 1;
623 return 0;
624 }
625
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;
630 return 0;
631 }
632
633 /*
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.
636 */
637 kr = thread_get_current_voucher_origin_pid(&pid);
638 if (kr != KERN_SUCCESS) {
639 if (kr == KERN_INVALID_TASK)
640 return ESRCH;
641 else if (kr == KERN_INVALID_VALUE)
642 return ENOATTR;
643 else
644 return EINVAL;
645 }
646
647 ret = proc_pidbackgrounded(pid, is_backgrounded);
648 return ret;
649}
650
651int
652proc_apply_resource_actions(void * bsdinfo, __unused int type, int action)
653{
654 proc_t p = (proc_t)bsdinfo;
655
656 switch(action) {
657 case PROC_POLICY_RSRCACT_THROTTLE:
658 /* no need to do anything */
659 break;
660
661 case PROC_POLICY_RSRCACT_SUSPEND:
662 task_suspend(p->task);
663 break;
664
665 case PROC_POLICY_RSRCACT_TERMINATE:
666 psignal(p, SIGKILL);
667 break;
668
669 case PROC_POLICY_RSRCACT_NOTIFY_KQ:
670 /* not implemented */
671 break;
672
673 case PROC_POLICY_RSRCACT_NOTIFY_EXC:
674 panic("shouldn't be applying exception notification to process!");
675 break;
676 }
677
678 return(0);
679}
680
681int
682proc_restore_resource_actions(void * bsdinfo, __unused int type, int action)
683{
684 proc_t p = (proc_t)bsdinfo;
685
686 switch(action) {
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 */
692 break;
693
694 case PROC_POLICY_RSRCACT_SUSPEND:
695 task_resume(p->task);
696 break;
697
698 }
699
700 return(0);
701}
702
703