]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/process_policy.c
bcf69d7c8f589015827f5b4e30b4f6f5398b7da8
[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/proc_internal.h>
38 #include <sys/proc.h>
39 #include <sys/kauth.h>
40 #include <sys/unistd.h>
41 #include <sys/buf.h>
42 #include <sys/ioctl.h>
43 #include <sys/vm.h>
44 #include <sys/user.h>
45
46 #include <security/audit/audit.h>
47
48 #include <mach/machine.h>
49 #include <mach/mach_types.h>
50 #include <mach/vm_param.h>
51 #include <kern/task.h>
52 #include <kern/kalloc.h>
53 #include <kern/assert.h>
54 #include <kern/policy_internal.h>
55
56 #include <vm/vm_kern.h>
57 #include <vm/vm_map.h>
58 #include <mach/host_info.h>
59 #include <mach/task_info.h>
60 #include <mach/thread_info.h>
61 #include <mach/vm_region.h>
62
63 #include <sys/process_policy.h>
64 #include <sys/proc_info.h>
65 #include <sys/bsdtask_info.h>
66 #include <sys/kdebug.h>
67 #include <sys/sysproto.h>
68 #include <sys/msgbuf.h>
69
70 #include <machine/machine_routines.h>
71
72 #include <kern/ipc_misc.h>
73 #include <vm/vm_protos.h>
74
75 static int handle_lowresource(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
76 static int handle_cpuuse(int action, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
77 static int handle_apptype(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
78 static int handle_boost(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
79
80 extern kern_return_t task_suspend(task_t);
81 extern kern_return_t task_resume(task_t);
82
83
84 /***************************** process_policy ********************/
85
86 /*
87 *int process_policy(int scope, int action, int policy, int policy_subtype,
88 * proc_policy_attribute_t * attrp, pid_t target_pid,
89 * uint64_t target_threadid)
90 *{ int process_policy(int scope, int action, int policy, int policy_subtype,
91 * user_addr_t attrp, pid_t target_pid, uint64_t target_threadid); }
92 */
93
94 /* system call implementation */
95 int
96 process_policy(__unused struct proc *p, struct process_policy_args * uap, __unused int32_t *retval)
97 {
98 int error = 0;
99 int scope = uap->scope;
100 int policy = uap->policy;
101 int action = uap->action;
102 int policy_subtype = uap->policy_subtype;
103 user_addr_t attrp = uap->attrp;
104 pid_t target_pid = uap->target_pid;
105 uint64_t target_threadid = uap->target_threadid;
106 proc_t target_proc = PROC_NULL;
107 proc_t curp = current_proc();
108 kauth_cred_t my_cred;
109
110 if ((scope != PROC_POLICY_SCOPE_PROCESS) && (scope != PROC_POLICY_SCOPE_THREAD)) {
111 return(EINVAL);
112 }
113
114 if (target_pid == 0 || target_pid == proc_selfpid())
115 target_proc = proc_self();
116 else
117 target_proc = proc_find(target_pid);
118
119 if (target_proc == PROC_NULL)
120 return(ESRCH);
121
122 my_cred = kauth_cred_get();
123
124 /*
125 * Resoure starvation control can be used by unpriv resource owner but priv at the time of ownership claim. This is
126 * checked in low resource handle routine. So bypass the checks here.
127 */
128 if ((policy != PROC_POLICY_RESOURCE_STARVATION) &&
129 (policy != PROC_POLICY_APPTYPE) &&
130 (!kauth_cred_issuser(my_cred) && curp != p))
131 {
132 error = EPERM;
133 goto out;
134 }
135
136 #if CONFIG_MACF
137 switch (policy) {
138 case PROC_POLICY_BOOST:
139 case PROC_POLICY_RESOURCE_USAGE:
140 /* These policies do their own appropriate mac checks */
141 break;
142 default:
143 error = mac_proc_check_sched(curp, target_proc);
144 if (error) goto out;
145 break;
146 }
147 #endif /* CONFIG_MACF */
148
149 switch(policy) {
150 case PROC_POLICY_BACKGROUND:
151 error = ENOTSUP;
152 break;
153 case PROC_POLICY_HARDWARE_ACCESS:
154 error = ENOTSUP;
155 break;
156 case PROC_POLICY_RESOURCE_STARVATION:
157 error = handle_lowresource(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
158 break;
159 case PROC_POLICY_RESOURCE_USAGE:
160 switch(policy_subtype) {
161 case PROC_POLICY_RUSAGE_NONE:
162 case PROC_POLICY_RUSAGE_WIREDMEM:
163 case PROC_POLICY_RUSAGE_VIRTMEM:
164 case PROC_POLICY_RUSAGE_DISK:
165 case PROC_POLICY_RUSAGE_NETWORK:
166 case PROC_POLICY_RUSAGE_POWER:
167 error = ENOTSUP;
168 goto out;
169 default:
170 error = EINVAL;
171 goto out;
172 case PROC_POLICY_RUSAGE_CPU:
173 break;
174 }
175
176 error = handle_cpuuse(action, attrp, target_proc, target_threadid);
177 break;
178 case PROC_POLICY_APPTYPE:
179 error = handle_apptype(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
180 break;
181 case PROC_POLICY_BOOST:
182 error = handle_boost(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
183 break;
184 default:
185 error = EINVAL;
186 break;
187 }
188
189 out:
190 proc_rele(target_proc);
191 return(error);
192 }
193
194 static int
195 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)
196 {
197 int error = 0;
198
199 switch(policy_subtype) {
200 case PROC_POLICY_RS_NONE:
201 case PROC_POLICY_RS_VIRTUALMEM:
202 break;
203 default:
204 return(EINVAL);
205 }
206
207 if (action == PROC_POLICY_ACTION_RESTORE)
208 error = proc_resetpcontrol(proc_pid(proc));
209 else
210 error = EINVAL;
211
212 return(error);
213 }
214
215
216 static int
217 handle_cpuuse(int action, user_addr_t attrp, proc_t proc, __unused uint64_t target_threadid)
218 {
219 proc_policy_cpuusage_attr_t cpuattr;
220 #if CONFIG_MACF
221 proc_t curp = current_proc();
222 #endif
223 int entitled = FALSE;
224 Boolean canEnable = FALSE;
225 uint64_t interval = -1ULL;
226 int error = 0;
227 uint8_t percentage;
228
229 #if CONFIG_MACF
230 /*
231 * iOS only allows processes to override their own CPU usage monitor
232 * parameters if they have com.apple.private.kernel.override-cpumon.
233 *
234 * Until rdar://24799462 improves our scheme, we are also using the
235 * same entitlement to indicate which processes can resume monitoring
236 * when they otherwise wouldn't be able to.
237 */
238 entitled = (mac_proc_check_cpumon(curp) == 0) ? TRUE : FALSE;
239 canEnable = (entitled && action == PROC_POLICY_ACTION_ENABLE);
240
241 if (!canEnable && curp != proc) {
242 /* can the current process change scheduling parameters? */
243 error = mac_proc_check_sched(curp, proc);
244 if (error) return error;
245 }
246 #endif
247
248 // on macOS tasks can only set and clear their own CPU limits
249 if ((action == PROC_POLICY_ACTION_APPLY || action == PROC_POLICY_ACTION_RESTORE)
250 && proc != current_proc()) {
251 return (EPERM);
252 }
253
254 switch (action) {
255 case PROC_POLICY_ACTION_GET:
256 error = proc_get_task_ruse_cpu(proc->task, &cpuattr.ppattr_cpu_attr,
257 &percentage,
258 &cpuattr.ppattr_cpu_attr_interval,
259 &cpuattr.ppattr_cpu_attr_deadline);
260 if (error == 0) {
261 cpuattr.ppattr_cpu_percentage = percentage;
262 cpuattr.ppattr_cpu_attr_interval /= NSEC_PER_SEC;
263 error = copyout((proc_policy_cpuusage_attr_t *)&cpuattr, (user_addr_t)attrp, sizeof(proc_policy_cpuusage_attr_t));
264 }
265 break;
266
267 case PROC_POLICY_ACTION_APPLY:
268 case PROC_POLICY_ACTION_SET:
269 error = copyin((user_addr_t)attrp, (proc_policy_cpuusage_attr_t *)&cpuattr, sizeof(proc_policy_cpuusage_attr_t));
270 if (error != 0) {
271 return (error);
272 }
273
274 /*
275 * The process_policy API uses seconds as the units for the interval,
276 * but the mach task policy SPI uses nanoseconds. Do the conversion,
277 * but preserve -1 as it has special meaning.
278 */
279 if (cpuattr.ppattr_cpu_attr_interval != -1ULL) {
280 interval = cpuattr.ppattr_cpu_attr_interval * NSEC_PER_SEC;
281 } else {
282 interval = -1ULL;
283 }
284
285 error = proc_set_task_ruse_cpu(proc->task, cpuattr.ppattr_cpu_attr,
286 cpuattr.ppattr_cpu_percentage,
287 interval,
288 cpuattr.ppattr_cpu_attr_deadline,
289 entitled);
290 break;
291
292 /* restore process to prior state */
293 case PROC_POLICY_ACTION_RESTORE:
294 error = proc_clear_task_ruse_cpu(proc->task, entitled);
295 break;
296
297 /* re-enable suspended monitor */
298 case PROC_POLICY_ACTION_ENABLE:
299 error = task_resume_cpumon(proc->task);
300 break;
301
302 case PROC_POLICY_ACTION_REMOVE:
303
304 default:
305 error = EINVAL;
306 break;
307
308 }
309
310 return(error);
311 }
312
313
314 static int
315 handle_apptype( int scope,
316 int action,
317 __unused int policy,
318 int policy_subtype,
319 __unused user_addr_t attrp,
320 proc_t target_proc,
321 __unused uint64_t target_threadid)
322 {
323 int error = 0;
324
325 if (scope != PROC_POLICY_SCOPE_PROCESS)
326 return (EINVAL);
327
328 /* Temporary compatibility with old importance donation interface until libproc is moved to new boost calls */
329 switch (policy_subtype) {
330 case PROC_POLICY_IOS_DONATEIMP:
331 if (action != PROC_POLICY_ACTION_ENABLE)
332 return (EINVAL);
333 if (target_proc != current_proc())
334 return (EINVAL);
335
336 /* PROCESS ENABLE APPTYPE DONATEIMP */
337 task_importance_mark_donor(target_proc->task, TRUE);
338
339 return(0);
340
341 case PROC_POLICY_IOS_HOLDIMP:
342 if (action != PROC_POLICY_ACTION_ENABLE)
343 return (EINVAL);
344 if (target_proc != current_proc())
345 return (EINVAL);
346
347 /* PROCESS ENABLE APPTYPE HOLDIMP */
348 error = task_importance_hold_legacy_external_assertion(current_task(), 1);
349
350 return(error);
351
352 case PROC_POLICY_IOS_DROPIMP:
353 if (action != PROC_POLICY_ACTION_ENABLE)
354 return (EINVAL);
355 if (target_proc != current_proc())
356 return (EINVAL);
357
358 /* PROCESS ENABLE APPTYPE DROPIMP */
359 error = task_importance_drop_legacy_external_assertion(current_task(), 1);
360
361 return(error);
362
363 default:
364 /* continue to TAL handling */
365 break;
366 }
367
368 if (policy_subtype != PROC_POLICY_OSX_APPTYPE_TAL)
369 return (EINVAL);
370
371 /* need to be super user to do this */
372 if (kauth_cred_issuser(kauth_cred_get()) == 0)
373 return (EPERM);
374
375 if (proc_task_is_tal(target_proc->task) == FALSE)
376 return (EINVAL);
377
378 switch (action) {
379 case PROC_POLICY_ACTION_ENABLE:
380 /* PROCESS ENABLE APPTYPE TAL */
381 proc_set_task_policy(target_proc->task,
382 TASK_POLICY_ATTRIBUTE, TASK_POLICY_TAL,
383 TASK_POLICY_ENABLE);
384 break;
385 case PROC_POLICY_ACTION_DISABLE:
386 /* PROCESS DISABLE APPTYPE TAL */
387 proc_set_task_policy(target_proc->task,
388 TASK_POLICY_ATTRIBUTE, TASK_POLICY_TAL,
389 TASK_POLICY_DISABLE);
390 break;
391 default:
392 return (EINVAL);
393 }
394
395 return(0);
396 }
397
398 static int
399 handle_boost(int scope,
400 int action,
401 __unused int policy,
402 int policy_subtype,
403 __unused user_addr_t attrp,
404 proc_t target_proc,
405 __unused uint64_t target_threadid)
406 {
407 int error = 0;
408
409 assert(policy == PROC_POLICY_BOOST);
410
411 if (scope != PROC_POLICY_SCOPE_PROCESS)
412 return (EINVAL);
413
414 if (target_proc != current_proc())
415 return (EINVAL);
416
417 switch(policy_subtype) {
418 case PROC_POLICY_IMP_IMPORTANT:
419 if (task_is_importance_receiver_type(target_proc->task) == FALSE)
420 return (EINVAL);
421
422 switch (action) {
423 case PROC_POLICY_ACTION_HOLD:
424 /* PROCESS HOLD BOOST IMPORTANT */
425 error = task_importance_hold_legacy_external_assertion(current_task(), 1);
426 break;
427 case PROC_POLICY_ACTION_DROP:
428 /* PROCESS DROP BOOST IMPORTANT */
429 error = task_importance_drop_legacy_external_assertion(current_task(), 1);
430 break;
431 default:
432 error = (EINVAL);
433 break;
434 }
435 break;
436
437 case PROC_POLICY_IMP_DONATION:
438 #if CONFIG_MACF
439 error = mac_proc_check_sched(current_proc(), target_proc);
440 if (error) return error;
441 #endif
442 switch (action) {
443 case PROC_POLICY_ACTION_SET:
444 /* PROCESS SET BOOST DONATION */
445 task_importance_mark_donor(target_proc->task, TRUE);
446 break;
447 default:
448 error = (EINVAL);
449 break;
450 }
451 break;
452
453 default:
454 error = (EINVAL);
455 break;
456 }
457
458 return(error);
459 }
460
461
462 /*
463 * KPI to determine if a pid is currently backgrounded.
464 * Returns ESRCH if pid cannot be found or has started exiting.
465 * Returns EINVAL if state is NULL.
466 * Sets *state to 1 if pid is backgrounded, and 0 otherwise.
467 */
468 int
469 proc_pidbackgrounded(pid_t pid, uint32_t* state)
470 {
471 proc_t target_proc = PROC_NULL;
472
473 if (state == NULL)
474 return(EINVAL);
475
476 target_proc = proc_find(pid);
477
478 if (target_proc == PROC_NULL)
479 return(ESRCH);
480
481 if ( proc_get_effective_task_policy(target_proc->task, TASK_POLICY_DARWIN_BG) ) {
482 *state = 1;
483 } else {
484 *state = 0;
485 }
486
487 proc_rele(target_proc);
488 return (0);
489 }
490
491 /*
492 * Get the darwin background state of the originator. If the current
493 * process app type is App, then it is the originator, else if it is
494 * a Daemon, then creator of the Resource Accounting attribute of
495 * the current thread voucher is the originator of the work.
496 */
497 int
498 proc_get_originatorbgstate(uint32_t *is_backgrounded)
499 {
500 uint32_t bgstate;
501 proc_t p = current_proc();
502 uint32_t flagsp;
503 kern_return_t kr;
504 pid_t pid;
505 int ret;
506 thread_t thread = current_thread();
507
508 bgstate = proc_get_effective_thread_policy(thread, TASK_POLICY_DARWIN_BG);
509
510 /* If current thread or task backgrounded, return background */
511 if (bgstate) {
512 *is_backgrounded = 1;
513 return 0;
514 }
515
516 /* Check if current process app type is App, then return foreground */
517 proc_get_darwinbgstate(p->task, &flagsp);
518 if ((flagsp & PROC_FLAG_APPLICATION) == PROC_FLAG_APPLICATION) {
519 *is_backgrounded = 0;
520 return 0;
521 }
522
523 /*
524 * Get the current voucher origin pid and it's bgstate.The pid
525 * returned here might not be valid or may have been recycled.
526 */
527 kr = thread_get_current_voucher_origin_pid(&pid);
528 if (kr != KERN_SUCCESS) {
529 if (kr == KERN_INVALID_TASK)
530 return ESRCH;
531 else if (kr == KERN_INVALID_VALUE)
532 return ENOATTR;
533 else
534 return EINVAL;
535 }
536
537 ret = proc_pidbackgrounded(pid, is_backgrounded);
538 return ret;
539 }
540
541 int
542 proc_apply_resource_actions(void * bsdinfo, __unused int type, int action)
543 {
544 proc_t p = (proc_t)bsdinfo;
545
546 switch(action) {
547 case PROC_POLICY_RSRCACT_THROTTLE:
548 /* no need to do anything */
549 break;
550
551 case PROC_POLICY_RSRCACT_SUSPEND:
552 task_suspend(p->task);
553 break;
554
555 case PROC_POLICY_RSRCACT_TERMINATE:
556 psignal(p, SIGKILL);
557 break;
558
559 case PROC_POLICY_RSRCACT_NOTIFY_KQ:
560 /* not implemented */
561 break;
562
563 case PROC_POLICY_RSRCACT_NOTIFY_EXC:
564 panic("shouldn't be applying exception notification to process!");
565 break;
566 }
567
568 return(0);
569 }
570
571 int
572 proc_restore_resource_actions(void * bsdinfo, __unused int type, int action)
573 {
574 proc_t p = (proc_t)bsdinfo;
575
576 switch(action) {
577 case PROC_POLICY_RSRCACT_THROTTLE:
578 case PROC_POLICY_RSRCACT_TERMINATE:
579 case PROC_POLICY_RSRCACT_NOTIFY_KQ:
580 case PROC_POLICY_RSRCACT_NOTIFY_EXC:
581 /* no need to do anything */
582 break;
583
584 case PROC_POLICY_RSRCACT_SUSPEND:
585 task_resume(p->task);
586 break;
587
588 }
589
590 return(0);
591 }
592