]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
f427ee49 | 2 | * Copyright (c) 2000-2020 Apple Inc. All rights reserved. |
5d5c5d0d | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
0a7de745 | 5 | * |
2d21ac55 A |
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. | |
0a7de745 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
0a7de745 | 17 | * |
2d21ac55 A |
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 | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
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. | |
0a7de745 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
1c79356b A |
27 | */ |
28 | /* Copyright (c) 1995, 1997 Apple Computer, Inc. All Rights Reserved */ | |
29 | /*- | |
30 | * Copyright (c) 1982, 1986, 1991, 1993 | |
31 | * The Regents of the University of California. All rights reserved. | |
32 | * (c) UNIX System Laboratories, Inc. | |
33 | * All or some portions of this file are derived from material licensed | |
34 | * to the University of California by American Telephone and Telegraph | |
35 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with | |
36 | * the permission of UNIX System Laboratories, Inc. | |
37 | * | |
38 | * Redistribution and use in source and binary forms, with or without | |
39 | * modification, are permitted provided that the following conditions | |
40 | * are met: | |
41 | * 1. Redistributions of source code must retain the above copyright | |
42 | * notice, this list of conditions and the following disclaimer. | |
43 | * 2. Redistributions in binary form must reproduce the above copyright | |
44 | * notice, this list of conditions and the following disclaimer in the | |
45 | * documentation and/or other materials provided with the distribution. | |
46 | * 3. All advertising materials mentioning features or use of this software | |
47 | * must display the following acknowledgement: | |
48 | * This product includes software developed by the University of | |
49 | * California, Berkeley and its contributors. | |
50 | * 4. Neither the name of the University nor the names of its contributors | |
51 | * may be used to endorse or promote products derived from this software | |
52 | * without specific prior written permission. | |
53 | * | |
54 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
55 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
56 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
57 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
58 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
59 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
60 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
61 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
62 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
63 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
64 | * SUCH DAMAGE. | |
65 | * | |
66 | * @(#)kern_resource.c 8.5 (Berkeley) 1/21/94 | |
67 | */ | |
2d21ac55 A |
68 | /* |
69 | * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce | |
70 | * support for mandatory and extensible security protections. This notice | |
71 | * is included in support of clause 2.2 (b) of the Apple Public License, | |
72 | * Version 2.0. | |
73 | */ | |
1c79356b A |
74 | |
75 | #include <sys/param.h> | |
76 | #include <sys/systm.h> | |
55e303ae | 77 | #include <sys/sysctl.h> |
1c79356b | 78 | #include <sys/kernel.h> |
91447636 | 79 | #include <sys/file_internal.h> |
1c79356b A |
80 | #include <sys/resourcevar.h> |
81 | #include <sys/malloc.h> | |
91447636 A |
82 | #include <sys/proc_internal.h> |
83 | #include <sys/kauth.h> | |
91447636 A |
84 | #include <sys/mount_internal.h> |
85 | #include <sys/sysproto.h> | |
1c79356b | 86 | |
b0d623f7 | 87 | #include <security/audit/audit.h> |
e5568f75 | 88 | |
1c79356b A |
89 | #include <machine/vmparam.h> |
90 | ||
91 | #include <mach/mach_types.h> | |
92 | #include <mach/time_value.h> | |
2d21ac55 | 93 | #include <mach/task.h> |
1c79356b | 94 | #include <mach/task_info.h> |
91447636 | 95 | #include <mach/vm_map.h> |
2d21ac55 A |
96 | #include <mach/mach_vm.h> |
97 | #include <mach/thread_act.h> /* for thread_policy_set( ) */ | |
2d21ac55 | 98 | #include <kern/thread.h> |
39037602 | 99 | #include <kern/policy_internal.h> |
2d21ac55 A |
100 | |
101 | #include <kern/task.h> | |
0a7de745 A |
102 | #include <kern/clock.h> /* for absolutetime_to_microtime() */ |
103 | #include <netinet/in.h> /* for TRAFFIC_MGT_SO_* */ | |
104 | #include <sys/socketvar.h> /* for struct socket */ | |
5ba3f43e A |
105 | #if NECP |
106 | #include <net/necp.h> | |
107 | #endif /* NECP */ | |
1c79356b A |
108 | |
109 | #include <vm/vm_map.h> | |
110 | ||
39236c6e A |
111 | #include <kern/assert.h> |
112 | #include <sys/resource.h> | |
39037602 | 113 | #include <sys/priv.h> |
3e170ce0 | 114 | #include <IOKit/IOBSD.h> |
39236c6e | 115 | |
5ba3f43e A |
116 | #if CONFIG_MACF |
117 | #include <security/mac_framework.h> | |
118 | #endif | |
119 | ||
0a7de745 A |
120 | int donice(struct proc *curp, struct proc *chgp, int n); |
121 | int dosetrlimit(struct proc *p, u_int which, struct rlimit *limp); | |
122 | int uthread_get_background_state(uthread_t); | |
fe8ab488 | 123 | static void do_background_socket(struct proc *p, thread_t thread); |
39037602 | 124 | static int do_background_thread(thread_t thread, int priority); |
6d2010ae | 125 | static int do_background_proc(struct proc *curp, struct proc *targetp, int priority); |
fe8ab488 A |
126 | static int set_gpudeny_proc(struct proc *curp, struct proc *targetp, int priority); |
127 | static int proc_set_darwin_role(proc_t curp, proc_t targetp, int priority); | |
128 | static int proc_get_darwin_role(proc_t curp, proc_t targetp, int *priority); | |
39236c6e | 129 | static int get_background_proc(struct proc *curp, struct proc *targetp, int *priority); |
39236c6e | 130 | int proc_pid_rusage(int pid, int flavor, user_addr_t buf, int32_t *retval); |
fe8ab488 A |
131 | void gather_rusage_info(proc_t p, rusage_info_current *ru, int flavor); |
132 | int fill_task_rusage(task_t task, rusage_info_current *ri); | |
133 | void fill_task_billed_usage(task_t task, rusage_info_current *ri); | |
134 | int fill_task_io_rusage(task_t task, rusage_info_current *ri); | |
135 | int fill_task_qos_rusage(task_t task, rusage_info_current *ri); | |
cb323159 | 136 | uint64_t get_task_logical_writes(task_t task, boolean_t external); |
5ba3f43e | 137 | void fill_task_monotonic_rusage(task_t task, rusage_info_current *ri); |
39236c6e A |
138 | |
139 | int proc_get_rusage(proc_t p, int flavor, user_addr_t buffer, __unused int is_zombie); | |
1c79356b | 140 | |
0a7de745 A |
141 | rlim_t maxdmap = MAXDSIZ; /* XXX */ |
142 | rlim_t maxsmap = MAXSSIZ - PAGE_MAX_SIZE; /* XXX */ | |
1c79356b | 143 | |
f427ee49 A |
144 | /* For plimit reference count */ |
145 | os_refgrp_decl(, rlimit_refgrp, "plimit_refcnt", NULL); | |
146 | ||
147 | ZONE_DECLARE(plimit_zone, "plimit", sizeof(struct plimit), ZC_NOENCRYPT); | |
148 | ||
55e303ae A |
149 | /* |
150 | * Limits on the number of open files per process, and the number | |
151 | * of child processes per process. | |
152 | * | |
153 | * Note: would be in kern/subr_param.c in FreeBSD. | |
154 | */ | |
0a7de745 | 155 | __private_extern__ int maxfilesperproc = OPEN_MAX; /* per-proc open files limit */ |
55e303ae | 156 | |
6d2010ae | 157 | SYSCTL_INT(_kern, KERN_MAXPROCPERUID, maxprocperuid, CTLFLAG_RW | CTLFLAG_LOCKED, |
0a7de745 | 158 | &maxprocperuid, 0, "Maximum processes allowed per userid" ); |
55e303ae | 159 | |
6d2010ae | 160 | SYSCTL_INT(_kern, KERN_MAXFILESPERPROC, maxfilesperproc, CTLFLAG_RW | CTLFLAG_LOCKED, |
0a7de745 | 161 | &maxfilesperproc, 0, "Maximum files allowed open per process" ); |
55e303ae | 162 | |
2d21ac55 A |
163 | /* Args and fn for proc_iteration callback used in setpriority */ |
164 | struct puser_nice_args { | |
165 | proc_t curp; | |
0a7de745 A |
166 | int prio; |
167 | id_t who; | |
168 | int * foundp; | |
169 | int * errorp; | |
2d21ac55 A |
170 | }; |
171 | static int puser_donice_callback(proc_t p, void * arg); | |
172 | ||
173 | ||
174 | /* Args and fn for proc_iteration callback used in setpriority */ | |
175 | struct ppgrp_nice_args { | |
176 | proc_t curp; | |
0a7de745 A |
177 | int prio; |
178 | int * foundp; | |
179 | int * errorp; | |
2d21ac55 A |
180 | }; |
181 | static int ppgrp_donice_callback(proc_t p, void * arg); | |
55e303ae | 182 | |
1c79356b A |
183 | /* |
184 | * Resource controls and accounting. | |
185 | */ | |
1c79356b | 186 | int |
b0d623f7 | 187 | getpriority(struct proc *curp, struct getpriority_args *uap, int32_t *retval) |
1c79356b | 188 | { |
2d21ac55 A |
189 | struct proc *p; |
190 | int low = PRIO_MAX + 1; | |
191 | kauth_cred_t my_cred; | |
39236c6e A |
192 | int refheld = 0; |
193 | int error = 0; | |
1c79356b | 194 | |
2d21ac55 | 195 | /* would also test (uap->who < 0), but id_t is unsigned */ |
0a7de745 A |
196 | if (uap->who > 0x7fffffff) { |
197 | return EINVAL; | |
198 | } | |
91447636 | 199 | |
1c79356b | 200 | switch (uap->which) { |
1c79356b | 201 | case PRIO_PROCESS: |
2d21ac55 | 202 | if (uap->who == 0) { |
1c79356b | 203 | p = curp; |
2d21ac55 A |
204 | low = p->p_nice; |
205 | } else { | |
206 | p = proc_find(uap->who); | |
0a7de745 | 207 | if (p == 0) { |
2d21ac55 | 208 | break; |
0a7de745 | 209 | } |
2d21ac55 A |
210 | low = p->p_nice; |
211 | proc_rele(p); | |
2d21ac55 | 212 | } |
1c79356b A |
213 | break; |
214 | ||
215 | case PRIO_PGRP: { | |
2d21ac55 | 216 | struct pgrp *pg = PGRP_NULL; |
1c79356b | 217 | |
2d21ac55 A |
218 | if (uap->who == 0) { |
219 | /* returns the pgrp to ref */ | |
220 | pg = proc_pgrp(curp); | |
0a7de745 | 221 | } else if ((pg = pgfind(uap->who)) == PGRP_NULL) { |
1c79356b | 222 | break; |
2d21ac55 A |
223 | } |
224 | /* No need for iteration as it is a simple scan */ | |
225 | pgrp_lock(pg); | |
39037602 | 226 | PGMEMBERS_FOREACH(pg, p) { |
0a7de745 | 227 | if (p->p_nice < low) { |
1c79356b | 228 | low = p->p_nice; |
0a7de745 | 229 | } |
1c79356b | 230 | } |
2d21ac55 A |
231 | pgrp_unlock(pg); |
232 | pg_rele(pg); | |
1c79356b A |
233 | break; |
234 | } | |
235 | ||
236 | case PRIO_USER: | |
0a7de745 | 237 | if (uap->who == 0) { |
91447636 | 238 | uap->who = kauth_cred_getuid(kauth_cred_get()); |
0a7de745 | 239 | } |
2d21ac55 A |
240 | |
241 | proc_list_lock(); | |
242 | ||
243 | for (p = allproc.lh_first; p != 0; p = p->p_list.le_next) { | |
244 | my_cred = kauth_cred_proc_ref(p); | |
245 | if (kauth_cred_getuid(my_cred) == uap->who && | |
0a7de745 | 246 | p->p_nice < low) { |
1c79356b | 247 | low = p->p_nice; |
0a7de745 | 248 | } |
2d21ac55 A |
249 | kauth_cred_unref(&my_cred); |
250 | } | |
251 | ||
252 | proc_list_unlock(); | |
253 | ||
254 | break; | |
255 | ||
39236c6e | 256 | case PRIO_DARWIN_THREAD: |
2d21ac55 | 257 | /* we currently only support the current thread */ |
0a7de745 A |
258 | if (uap->who != 0) { |
259 | return EINVAL; | |
260 | } | |
2d21ac55 | 261 | |
39037602 | 262 | low = proc_get_thread_policy(current_thread(), TASK_POLICY_INTERNAL, TASK_POLICY_DARWIN_BG); |
39236c6e A |
263 | |
264 | break; | |
265 | ||
266 | case PRIO_DARWIN_PROCESS: | |
267 | if (uap->who == 0) { | |
268 | p = curp; | |
269 | } else { | |
270 | p = proc_find(uap->who); | |
0a7de745 | 271 | if (p == PROC_NULL) { |
39236c6e | 272 | break; |
0a7de745 | 273 | } |
39236c6e | 274 | refheld = 1; |
2d21ac55 | 275 | } |
39236c6e A |
276 | |
277 | error = get_background_proc(curp, p, &low); | |
278 | ||
0a7de745 | 279 | if (refheld) { |
39236c6e | 280 | proc_rele(p); |
0a7de745 A |
281 | } |
282 | if (error) { | |
283 | return error; | |
284 | } | |
1c79356b A |
285 | break; |
286 | ||
fe8ab488 A |
287 | case PRIO_DARWIN_ROLE: |
288 | if (uap->who == 0) { | |
289 | p = curp; | |
290 | } else { | |
291 | p = proc_find(uap->who); | |
0a7de745 | 292 | if (p == PROC_NULL) { |
fe8ab488 | 293 | break; |
0a7de745 | 294 | } |
fe8ab488 A |
295 | refheld = 1; |
296 | } | |
297 | ||
298 | error = proc_get_darwin_role(curp, p, &low); | |
299 | ||
0a7de745 | 300 | if (refheld) { |
fe8ab488 | 301 | proc_rele(p); |
0a7de745 A |
302 | } |
303 | if (error) { | |
304 | return error; | |
305 | } | |
fe8ab488 A |
306 | break; |
307 | ||
1c79356b | 308 | default: |
0a7de745 A |
309 | return EINVAL; |
310 | } | |
311 | if (low == PRIO_MAX + 1) { | |
312 | return ESRCH; | |
1c79356b | 313 | } |
1c79356b | 314 | *retval = low; |
0a7de745 | 315 | return 0; |
1c79356b A |
316 | } |
317 | ||
2d21ac55 A |
318 | /* call back function used for proc iteration in PRIO_USER */ |
319 | static int | |
320 | puser_donice_callback(proc_t p, void * arg) | |
321 | { | |
322 | int error, n; | |
323 | struct puser_nice_args * pun = (struct puser_nice_args *)arg; | |
324 | kauth_cred_t my_cred; | |
325 | ||
326 | my_cred = kauth_cred_proc_ref(p); | |
327 | if (kauth_cred_getuid(my_cred) == pun->who) { | |
328 | error = donice(pun->curp, p, pun->prio); | |
0a7de745 | 329 | if (pun->errorp != NULL) { |
2d21ac55 | 330 | *pun->errorp = error; |
0a7de745 | 331 | } |
2d21ac55 A |
332 | if (pun->foundp != NULL) { |
333 | n = *pun->foundp; | |
0a7de745 | 334 | *pun->foundp = n + 1; |
2d21ac55 A |
335 | } |
336 | } | |
337 | kauth_cred_unref(&my_cred); | |
338 | ||
0a7de745 | 339 | return PROC_RETURNED; |
2d21ac55 A |
340 | } |
341 | ||
342 | /* call back function used for proc iteration in PRIO_PGRP */ | |
343 | static int | |
344 | ppgrp_donice_callback(proc_t p, void * arg) | |
345 | { | |
346 | int error; | |
347 | struct ppgrp_nice_args * pun = (struct ppgrp_nice_args *)arg; | |
348 | int n; | |
349 | ||
350 | error = donice(pun->curp, p, pun->prio); | |
0a7de745 | 351 | if (pun->errorp != NULL) { |
2d21ac55 | 352 | *pun->errorp = error; |
0a7de745 A |
353 | } |
354 | if (pun->foundp != NULL) { | |
2d21ac55 | 355 | n = *pun->foundp; |
0a7de745 | 356 | *pun->foundp = n + 1; |
2d21ac55 A |
357 | } |
358 | ||
0a7de745 | 359 | return PROC_RETURNED; |
2d21ac55 A |
360 | } |
361 | ||
362 | /* | |
363 | * Returns: 0 Success | |
364 | * EINVAL | |
365 | * ESRCH | |
366 | * donice:EPERM | |
367 | * donice:EACCES | |
368 | */ | |
1c79356b A |
369 | /* ARGSUSED */ |
370 | int | |
fe8ab488 | 371 | setpriority(struct proc *curp, struct setpriority_args *uap, int32_t *retval) |
1c79356b | 372 | { |
2d21ac55 | 373 | struct proc *p; |
1c79356b | 374 | int found = 0, error = 0; |
2d21ac55 | 375 | int refheld = 0; |
1c79356b | 376 | |
e5568f75 A |
377 | AUDIT_ARG(cmd, uap->which); |
378 | AUDIT_ARG(owner, uap->who, 0); | |
b0d623f7 | 379 | AUDIT_ARG(value32, uap->prio); |
e5568f75 | 380 | |
2d21ac55 | 381 | /* would also test (uap->who < 0), but id_t is unsigned */ |
0a7de745 A |
382 | if (uap->who > 0x7fffffff) { |
383 | return EINVAL; | |
384 | } | |
91447636 | 385 | |
1c79356b | 386 | switch (uap->which) { |
1c79356b | 387 | case PRIO_PROCESS: |
0a7de745 | 388 | if (uap->who == 0) { |
1c79356b | 389 | p = curp; |
0a7de745 | 390 | } else { |
2d21ac55 | 391 | p = proc_find(uap->who); |
0a7de745 | 392 | if (p == 0) { |
2d21ac55 | 393 | break; |
0a7de745 | 394 | } |
2d21ac55 A |
395 | refheld = 1; |
396 | } | |
1c79356b A |
397 | error = donice(curp, p, uap->prio); |
398 | found++; | |
0a7de745 | 399 | if (refheld != 0) { |
2d21ac55 | 400 | proc_rele(p); |
0a7de745 | 401 | } |
1c79356b A |
402 | break; |
403 | ||
404 | case PRIO_PGRP: { | |
2d21ac55 A |
405 | struct pgrp *pg = PGRP_NULL; |
406 | struct ppgrp_nice_args ppgrp; | |
0a7de745 | 407 | |
2d21ac55 A |
408 | if (uap->who == 0) { |
409 | pg = proc_pgrp(curp); | |
0a7de745 | 410 | } else if ((pg = pgfind(uap->who)) == PGRP_NULL) { |
1c79356b | 411 | break; |
0a7de745 | 412 | } |
2d21ac55 A |
413 | |
414 | ppgrp.curp = curp; | |
415 | ppgrp.prio = uap->prio; | |
416 | ppgrp.foundp = &found; | |
417 | ppgrp.errorp = &error; | |
0a7de745 | 418 | |
2d21ac55 A |
419 | /* PGRP_DROPREF drops the reference on process group */ |
420 | pgrp_iterate(pg, PGRP_DROPREF, ppgrp_donice_callback, (void *)&ppgrp, NULL, NULL); | |
421 | ||
1c79356b A |
422 | break; |
423 | } | |
424 | ||
2d21ac55 A |
425 | case PRIO_USER: { |
426 | struct puser_nice_args punice; | |
427 | ||
0a7de745 | 428 | if (uap->who == 0) { |
91447636 | 429 | uap->who = kauth_cred_getuid(kauth_cred_get()); |
0a7de745 | 430 | } |
2d21ac55 A |
431 | |
432 | punice.curp = curp; | |
433 | punice.prio = uap->prio; | |
434 | punice.who = uap->who; | |
435 | punice.foundp = &found; | |
436 | error = 0; | |
437 | punice.errorp = &error; | |
438 | proc_iterate(PROC_ALLPROCLIST, puser_donice_callback, (void *)&punice, NULL, NULL); | |
439 | ||
1c79356b | 440 | break; |
2d21ac55 A |
441 | } |
442 | ||
443 | case PRIO_DARWIN_THREAD: { | |
444 | /* we currently only support the current thread */ | |
0a7de745 A |
445 | if (uap->who != 0) { |
446 | return EINVAL; | |
447 | } | |
39236c6e | 448 | |
39037602 | 449 | error = do_background_thread(current_thread(), uap->prio); |
2d21ac55 A |
450 | found++; |
451 | break; | |
452 | } | |
1c79356b | 453 | |
b7266188 | 454 | case PRIO_DARWIN_PROCESS: { |
0a7de745 | 455 | if (uap->who == 0) { |
b7266188 | 456 | p = curp; |
0a7de745 | 457 | } else { |
b7266188 | 458 | p = proc_find(uap->who); |
0a7de745 | 459 | if (p == 0) { |
b7266188 | 460 | break; |
0a7de745 | 461 | } |
b7266188 A |
462 | refheld = 1; |
463 | } | |
464 | ||
39236c6e A |
465 | error = do_background_proc(curp, p, uap->prio); |
466 | ||
b7266188 | 467 | found++; |
0a7de745 | 468 | if (refheld != 0) { |
b7266188 | 469 | proc_rele(p); |
0a7de745 | 470 | } |
b7266188 A |
471 | break; |
472 | } | |
473 | ||
fe8ab488 | 474 | case PRIO_DARWIN_GPU: { |
0a7de745 A |
475 | if (uap->who == 0) { |
476 | return EINVAL; | |
477 | } | |
fe8ab488 A |
478 | |
479 | p = proc_find(uap->who); | |
0a7de745 | 480 | if (p == PROC_NULL) { |
fe8ab488 | 481 | break; |
0a7de745 | 482 | } |
fe8ab488 A |
483 | |
484 | error = set_gpudeny_proc(curp, p, uap->prio); | |
485 | ||
486 | found++; | |
487 | proc_rele(p); | |
488 | break; | |
489 | } | |
490 | ||
491 | case PRIO_DARWIN_ROLE: { | |
492 | if (uap->who == 0) { | |
493 | p = curp; | |
494 | } else { | |
495 | p = proc_find(uap->who); | |
0a7de745 | 496 | if (p == PROC_NULL) { |
fe8ab488 | 497 | break; |
0a7de745 | 498 | } |
fe8ab488 A |
499 | refheld = 1; |
500 | } | |
501 | ||
502 | error = proc_set_darwin_role(curp, p, uap->prio); | |
503 | ||
504 | found++; | |
0a7de745 | 505 | if (refheld != 0) { |
fe8ab488 | 506 | proc_rele(p); |
0a7de745 | 507 | } |
fe8ab488 A |
508 | break; |
509 | } | |
510 | ||
1c79356b | 511 | default: |
0a7de745 A |
512 | return EINVAL; |
513 | } | |
514 | if (found == 0) { | |
515 | return ESRCH; | |
1c79356b | 516 | } |
fe8ab488 A |
517 | if (error == EIDRM) { |
518 | *retval = -2; | |
519 | error = 0; | |
520 | } | |
0a7de745 | 521 | return error; |
1c79356b A |
522 | } |
523 | ||
2d21ac55 A |
524 | |
525 | /* | |
526 | * Returns: 0 Success | |
527 | * EPERM | |
528 | * EACCES | |
529 | * mac_check_proc_sched:??? | |
530 | */ | |
1c79356b | 531 | int |
2d21ac55 | 532 | donice(struct proc *curp, struct proc *chgp, int n) |
1c79356b | 533 | { |
2d21ac55 A |
534 | int error = 0; |
535 | kauth_cred_t ucred; | |
536 | kauth_cred_t my_cred; | |
537 | ||
538 | ucred = kauth_cred_proc_ref(curp); | |
539 | my_cred = kauth_cred_proc_ref(chgp); | |
1c79356b | 540 | |
6d2010ae | 541 | if (suser(ucred, NULL) && kauth_cred_getruid(ucred) && |
2d21ac55 | 542 | kauth_cred_getuid(ucred) != kauth_cred_getuid(my_cred) && |
6d2010ae | 543 | kauth_cred_getruid(ucred) != kauth_cred_getuid(my_cred)) { |
2d21ac55 A |
544 | error = EPERM; |
545 | goto out; | |
546 | } | |
0a7de745 | 547 | if (n > PRIO_MAX) { |
1c79356b | 548 | n = PRIO_MAX; |
0a7de745 A |
549 | } |
550 | if (n < PRIO_MIN) { | |
1c79356b | 551 | n = PRIO_MIN; |
0a7de745 | 552 | } |
2d21ac55 A |
553 | if (n < chgp->p_nice && suser(ucred, &curp->p_acflag)) { |
554 | error = EACCES; | |
555 | goto out; | |
556 | } | |
557 | #if CONFIG_MACF | |
558 | error = mac_proc_check_sched(curp, chgp); | |
0a7de745 | 559 | if (error) { |
2d21ac55 | 560 | goto out; |
0a7de745 | 561 | } |
2d21ac55 A |
562 | #endif |
563 | proc_lock(chgp); | |
f427ee49 | 564 | chgp->p_nice = (char)n; |
2d21ac55 | 565 | proc_unlock(chgp); |
1c79356b | 566 | (void)resetpriority(chgp); |
2d21ac55 A |
567 | out: |
568 | kauth_cred_unref(&ucred); | |
569 | kauth_cred_unref(&my_cred); | |
0a7de745 | 570 | return error; |
2d21ac55 A |
571 | } |
572 | ||
fe8ab488 A |
573 | static int |
574 | set_gpudeny_proc(struct proc *curp, struct proc *targetp, int priority) | |
575 | { | |
576 | int error = 0; | |
577 | kauth_cred_t ucred; | |
578 | kauth_cred_t target_cred; | |
579 | ||
580 | ucred = kauth_cred_get(); | |
581 | target_cred = kauth_cred_proc_ref(targetp); | |
582 | ||
583 | /* TODO: Entitlement instead of uid check */ | |
584 | ||
585 | if (!kauth_cred_issuser(ucred) && kauth_cred_getruid(ucred) && | |
0a7de745 A |
586 | kauth_cred_getuid(ucred) != kauth_cred_getuid(target_cred) && |
587 | kauth_cred_getruid(ucred) != kauth_cred_getuid(target_cred)) { | |
fe8ab488 A |
588 | error = EPERM; |
589 | goto out; | |
590 | } | |
591 | ||
592 | if (curp == targetp) { | |
593 | error = EPERM; | |
594 | goto out; | |
595 | } | |
596 | ||
597 | #if CONFIG_MACF | |
598 | error = mac_proc_check_sched(curp, targetp); | |
0a7de745 | 599 | if (error) { |
fe8ab488 | 600 | goto out; |
0a7de745 | 601 | } |
fe8ab488 A |
602 | #endif |
603 | ||
604 | switch (priority) { | |
0a7de745 A |
605 | case PRIO_DARWIN_GPU_DENY: |
606 | task_set_gpu_denied(proc_task(targetp), TRUE); | |
607 | break; | |
608 | case PRIO_DARWIN_GPU_ALLOW: | |
609 | task_set_gpu_denied(proc_task(targetp), FALSE); | |
610 | break; | |
611 | default: | |
612 | error = EINVAL; | |
613 | goto out; | |
fe8ab488 A |
614 | } |
615 | ||
616 | out: | |
617 | kauth_cred_unref(&target_cred); | |
0a7de745 | 618 | return error; |
fe8ab488 A |
619 | } |
620 | ||
621 | static int | |
622 | proc_set_darwin_role(proc_t curp, proc_t targetp, int priority) | |
623 | { | |
624 | int error = 0; | |
5ba3f43e | 625 | uint32_t flagsp = 0; |
fe8ab488 A |
626 | |
627 | kauth_cred_t ucred, target_cred; | |
628 | ||
629 | ucred = kauth_cred_get(); | |
630 | target_cred = kauth_cred_proc_ref(targetp); | |
631 | ||
632 | if (!kauth_cred_issuser(ucred) && kauth_cred_getruid(ucred) && | |
0a7de745 | 633 | kauth_cred_getuid(ucred) != kauth_cred_getuid(target_cred) && |
fe8ab488 | 634 | kauth_cred_getruid(ucred) != kauth_cred_getuid(target_cred)) { |
39037602 A |
635 | if (priv_check_cred(ucred, PRIV_SETPRIORITY_DARWIN_ROLE, 0) != 0) { |
636 | error = EPERM; | |
637 | goto out; | |
638 | } | |
fe8ab488 A |
639 | } |
640 | ||
641 | if (curp != targetp) { | |
642 | #if CONFIG_MACF | |
0a7de745 | 643 | if ((error = mac_proc_check_sched(curp, targetp))) { |
fe8ab488 | 644 | goto out; |
0a7de745 | 645 | } |
fe8ab488 A |
646 | #endif |
647 | } | |
648 | ||
649 | proc_get_darwinbgstate(proc_task(targetp), &flagsp); | |
650 | if ((flagsp & PROC_FLAG_APPLICATION) != PROC_FLAG_APPLICATION) { | |
651 | error = ENOTSUP; | |
652 | goto out; | |
653 | } | |
654 | ||
f427ee49 | 655 | task_role_t role = TASK_UNSPECIFIED; |
fe8ab488 | 656 | |
0a7de745 | 657 | if ((error = proc_darwin_role_to_task_role(priority, &role))) { |
3e170ce0 | 658 | goto out; |
0a7de745 | 659 | } |
fe8ab488 | 660 | |
39037602 | 661 | proc_set_task_policy(proc_task(targetp), TASK_POLICY_ATTRIBUTE, |
0a7de745 | 662 | TASK_POLICY_ROLE, role); |
fe8ab488 A |
663 | |
664 | out: | |
665 | kauth_cred_unref(&target_cred); | |
0a7de745 | 666 | return error; |
fe8ab488 A |
667 | } |
668 | ||
669 | static int | |
670 | proc_get_darwin_role(proc_t curp, proc_t targetp, int *priority) | |
671 | { | |
672 | int error = 0; | |
673 | int role = 0; | |
674 | ||
675 | kauth_cred_t ucred, target_cred; | |
676 | ||
677 | ucred = kauth_cred_get(); | |
678 | target_cred = kauth_cred_proc_ref(targetp); | |
679 | ||
680 | if (!kauth_cred_issuser(ucred) && kauth_cred_getruid(ucred) && | |
0a7de745 | 681 | kauth_cred_getuid(ucred) != kauth_cred_getuid(target_cred) && |
fe8ab488 A |
682 | kauth_cred_getruid(ucred) != kauth_cred_getuid(target_cred)) { |
683 | error = EPERM; | |
684 | goto out; | |
685 | } | |
686 | ||
687 | if (curp != targetp) { | |
688 | #if CONFIG_MACF | |
0a7de745 | 689 | if ((error = mac_proc_check_sched(curp, targetp))) { |
fe8ab488 | 690 | goto out; |
0a7de745 | 691 | } |
fe8ab488 A |
692 | #endif |
693 | } | |
694 | ||
39037602 | 695 | role = proc_get_task_policy(proc_task(targetp), TASK_POLICY_ATTRIBUTE, TASK_POLICY_ROLE); |
fe8ab488 | 696 | |
3e170ce0 | 697 | *priority = proc_task_role_to_darwin_role(role); |
fe8ab488 A |
698 | |
699 | out: | |
700 | kauth_cred_unref(&target_cred); | |
0a7de745 | 701 | return error; |
fe8ab488 A |
702 | } |
703 | ||
704 | ||
39236c6e A |
705 | static int |
706 | get_background_proc(struct proc *curp, struct proc *targetp, int *priority) | |
707 | { | |
708 | int external = 0; | |
709 | int error = 0; | |
710 | kauth_cred_t ucred, target_cred; | |
711 | ||
712 | ucred = kauth_cred_get(); | |
713 | target_cred = kauth_cred_proc_ref(targetp); | |
714 | ||
715 | if (!kauth_cred_issuser(ucred) && kauth_cred_getruid(ucred) && | |
716 | kauth_cred_getuid(ucred) != kauth_cred_getuid(target_cred) && | |
717 | kauth_cred_getruid(ucred) != kauth_cred_getuid(target_cred)) { | |
718 | error = EPERM; | |
719 | goto out; | |
720 | } | |
721 | ||
722 | external = (curp == targetp) ? TASK_POLICY_INTERNAL : TASK_POLICY_EXTERNAL; | |
723 | ||
39037602 | 724 | *priority = proc_get_task_policy(current_task(), external, TASK_POLICY_DARWIN_BG); |
39236c6e A |
725 | |
726 | out: | |
727 | kauth_cred_unref(&target_cred); | |
0a7de745 | 728 | return error; |
39236c6e A |
729 | } |
730 | ||
b7266188 | 731 | static int |
6d2010ae | 732 | do_background_proc(struct proc *curp, struct proc *targetp, int priority) |
b7266188 | 733 | { |
39236c6e A |
734 | #if !CONFIG_MACF |
735 | #pragma unused(curp) | |
736 | #endif | |
b7266188 | 737 | int error = 0; |
6d2010ae A |
738 | kauth_cred_t ucred; |
739 | kauth_cred_t target_cred; | |
39236c6e | 740 | int external; |
39236c6e | 741 | int enable; |
6d2010ae A |
742 | |
743 | ucred = kauth_cred_get(); | |
744 | target_cred = kauth_cred_proc_ref(targetp); | |
745 | ||
746 | if (!kauth_cred_issuser(ucred) && kauth_cred_getruid(ucred) && | |
0a7de745 A |
747 | kauth_cred_getuid(ucred) != kauth_cred_getuid(target_cred) && |
748 | kauth_cred_getruid(ucred) != kauth_cred_getuid(target_cred)) { | |
6d2010ae A |
749 | error = EPERM; |
750 | goto out; | |
751 | } | |
752 | ||
753 | #if CONFIG_MACF | |
754 | error = mac_proc_check_sched(curp, targetp); | |
0a7de745 | 755 | if (error) { |
6d2010ae | 756 | goto out; |
0a7de745 | 757 | } |
6d2010ae A |
758 | #endif |
759 | ||
39236c6e A |
760 | external = (curp == targetp) ? TASK_POLICY_INTERNAL : TASK_POLICY_EXTERNAL; |
761 | ||
762 | switch (priority) { | |
0a7de745 A |
763 | case PRIO_DARWIN_BG: |
764 | enable = TASK_POLICY_ENABLE; | |
765 | break; | |
766 | case PRIO_DARWIN_NONUI: | |
767 | /* ignored for compatibility */ | |
768 | goto out; | |
769 | default: | |
770 | /* TODO: EINVAL if priority != 0 */ | |
771 | enable = TASK_POLICY_DISABLE; | |
772 | break; | |
39236c6e A |
773 | } |
774 | ||
39037602 | 775 | proc_set_task_policy(proc_task(targetp), external, TASK_POLICY_DARWIN_BG, enable); |
d1ecb069 A |
776 | |
777 | out: | |
6d2010ae | 778 | kauth_cred_unref(&target_cred); |
0a7de745 | 779 | return error; |
b7266188 A |
780 | } |
781 | ||
0a7de745 | 782 | static void |
fe8ab488 | 783 | do_background_socket(struct proc *p, thread_t thread) |
b7266188 | 784 | { |
39236c6e | 785 | #if SOCKETS |
f427ee49 A |
786 | struct fileproc *fp; |
787 | int background = false; | |
cb323159 | 788 | #if NECP |
f427ee49 | 789 | int update_necp = false; |
cb323159 | 790 | #endif /* NECP */ |
b7266188 | 791 | |
fe8ab488 A |
792 | proc_fdlock(p); |
793 | ||
0a7de745 | 794 | if (thread != THREAD_NULL) { |
fe8ab488 | 795 | background = proc_get_effective_thread_policy(thread, TASK_POLICY_ALL_SOCKETS_BG); |
0a7de745 | 796 | } else { |
fe8ab488 | 797 | background = proc_get_effective_task_policy(proc_task(p), TASK_POLICY_ALL_SOCKETS_BG); |
0a7de745 | 798 | } |
fe8ab488 A |
799 | |
800 | if (background) { | |
d1ecb069 A |
801 | /* |
802 | * For PRIO_DARWIN_PROCESS (thread is NULL), simply mark | |
803 | * the sockets with the background flag. There's nothing | |
804 | * to do here for the PRIO_DARWIN_THREAD case. | |
805 | */ | |
39236c6e | 806 | if (thread == THREAD_NULL) { |
f427ee49 A |
807 | fdt_foreach(fp, p) { |
808 | if (FILEGLOB_DTYPE(fp->fp_glob) == DTYPE_SOCKET) { | |
809 | struct socket *sockp = (struct socket *)fp->fp_glob->fg_data; | |
5ba3f43e A |
810 | socket_set_traffic_mgt_flags(sockp, TRAFFIC_MGT_SO_BACKGROUND); |
811 | sockp->so_background_thread = NULL; | |
812 | } | |
813 | #if NECP | |
f427ee49 | 814 | else if (FILEGLOB_DTYPE(fp->fp_glob) == DTYPE_NETPOLICY) { |
cb323159 A |
815 | if (necp_set_client_as_background(p, fp, background)) { |
816 | update_necp = true; | |
817 | } | |
5ba3f43e A |
818 | } |
819 | #endif /* NECP */ | |
b7266188 | 820 | } |
b7266188 | 821 | } |
b7266188 A |
822 | } else { |
823 | /* disable networking IO throttle. | |
0a7de745 A |
824 | * NOTE - It is a known limitation of the current design that we |
825 | * could potentially clear TRAFFIC_MGT_SO_BACKGROUND bit for | |
826 | * sockets created by other threads within this process. | |
b7266188 | 827 | */ |
f427ee49 A |
828 | fdt_foreach(fp, p) { |
829 | struct socket *sockp; | |
b7266188 | 830 | |
f427ee49 A |
831 | if (FILEGLOB_DTYPE(fp->fp_glob) == DTYPE_SOCKET) { |
832 | sockp = (struct socket *)fp->fp_glob->fg_data; | |
5ba3f43e A |
833 | /* skip if only clearing this thread's sockets */ |
834 | if ((thread) && (sockp->so_background_thread != thread)) { | |
835 | continue; | |
836 | } | |
837 | socket_clear_traffic_mgt_flags(sockp, TRAFFIC_MGT_SO_BACKGROUND); | |
838 | sockp->so_background_thread = NULL; | |
b7266188 | 839 | } |
5ba3f43e | 840 | #if NECP |
f427ee49 | 841 | else if (FILEGLOB_DTYPE(fp->fp_glob) == DTYPE_NETPOLICY) { |
cb323159 A |
842 | if (necp_set_client_as_background(p, fp, background)) { |
843 | update_necp = true; | |
844 | } | |
5ba3f43e A |
845 | } |
846 | #endif /* NECP */ | |
b7266188 | 847 | } |
b7266188 | 848 | } |
fe8ab488 A |
849 | |
850 | proc_fdunlock(p); | |
cb323159 A |
851 | |
852 | #if NECP | |
853 | if (update_necp) { | |
854 | necp_update_all_clients(); | |
855 | } | |
856 | #endif /* NECP */ | |
39236c6e | 857 | #else |
fe8ab488 | 858 | #pragma unused(p, thread) |
39236c6e | 859 | #endif |
b7266188 A |
860 | } |
861 | ||
862 | ||
2d21ac55 A |
863 | /* |
864 | * do_background_thread | |
39037602 A |
865 | * |
866 | * Requires: thread reference | |
867 | * | |
39236c6e A |
868 | * Returns: 0 Success |
869 | * EPERM Tried to background while in vfork | |
2d21ac55 A |
870 | * XXX - todo - does this need a MACF hook? |
871 | */ | |
872 | static int | |
39037602 | 873 | do_background_thread(thread_t thread, int priority) |
2d21ac55 | 874 | { |
39236c6e A |
875 | struct uthread *ut; |
876 | int enable, external; | |
fe8ab488 | 877 | int rv = 0; |
39236c6e | 878 | |
2d21ac55 A |
879 | ut = get_bsdthread_info(thread); |
880 | ||
6d2010ae | 881 | /* Backgrounding is unsupported for threads in vfork */ |
0a7de745 A |
882 | if ((ut->uu_flag & UT_VFORK) != 0) { |
883 | return EPERM; | |
884 | } | |
6d2010ae | 885 | |
39037602 | 886 | /* Backgrounding is unsupported for workq threads */ |
fe8ab488 | 887 | if (thread_is_static_param(thread)) { |
0a7de745 | 888 | return EPERM; |
fe8ab488 A |
889 | } |
890 | ||
891 | /* Not allowed to combine QoS and DARWIN_BG, doing so strips the QoS */ | |
892 | if (thread_has_qos_policy(thread)) { | |
893 | thread_remove_qos_policy(thread); | |
894 | rv = EIDRM; | |
895 | } | |
896 | ||
39236c6e A |
897 | /* TODO: Fail if someone passes something besides 0 or PRIO_DARWIN_BG */ |
898 | enable = (priority == PRIO_DARWIN_BG) ? TASK_POLICY_ENABLE : TASK_POLICY_DISABLE; | |
899 | external = (current_thread() == thread) ? TASK_POLICY_INTERNAL : TASK_POLICY_EXTERNAL; | |
1c79356b | 900 | |
39037602 | 901 | proc_set_thread_policy(thread, external, TASK_POLICY_DARWIN_BG, enable); |
6d2010ae | 902 | |
fe8ab488 | 903 | return rv; |
6d2010ae | 904 | } |
39236c6e | 905 | |
6d2010ae | 906 | |
2d21ac55 A |
907 | /* |
908 | * Returns: 0 Success | |
909 | * copyin:EFAULT | |
910 | * dosetrlimit: | |
911 | */ | |
1c79356b A |
912 | /* ARGSUSED */ |
913 | int | |
b0d623f7 | 914 | setrlimit(struct proc *p, struct setrlimit_args *uap, __unused int32_t *retval) |
1c79356b A |
915 | { |
916 | struct rlimit alim; | |
917 | int error; | |
918 | ||
91447636 | 919 | if ((error = copyin(uap->rlp, (caddr_t)&alim, |
0a7de745 A |
920 | sizeof(struct rlimit)))) { |
921 | return error; | |
922 | } | |
2d21ac55 | 923 | |
0a7de745 | 924 | return dosetrlimit(p, uap->which, &alim); |
1c79356b A |
925 | } |
926 | ||
2d21ac55 A |
927 | /* |
928 | * Returns: 0 Success | |
929 | * EINVAL | |
2d21ac55 A |
930 | * suser:EPERM |
931 | * | |
932 | * Notes: EINVAL is returned both for invalid arguments, and in the | |
933 | * case that the current usage (e.g. RLIMIT_STACK) is already | |
934 | * in excess of the requested limit. | |
935 | */ | |
1c79356b | 936 | int |
f427ee49 | 937 | dosetrlimit(struct proc *p, u_int which, struct rlimit *newrlim) |
1c79356b | 938 | { |
f427ee49 A |
939 | struct rlimit rlim; |
940 | int error; | |
941 | kern_return_t kr; | |
942 | int posix = (which & _RLIMIT_POSIX_FLAG) ? 1 : 0; | |
2d21ac55 A |
943 | |
944 | /* Mask out POSIX flag, saved above */ | |
945 | which &= ~_RLIMIT_POSIX_FLAG; | |
1c79356b | 946 | |
f427ee49 | 947 | /* Unknown resource */ |
0a7de745 A |
948 | if (which >= RLIM_NLIMITS) { |
949 | return EINVAL; | |
950 | } | |
2d21ac55 | 951 | |
f427ee49 A |
952 | /* |
953 | * Take a snapshot of the current rlimit values and read this throughout | |
954 | * this routine. This minimizes the critical sections and allow other | |
955 | * processes in the system to access the plimit while we are in the | |
956 | * middle of this setrlimit call. | |
957 | */ | |
958 | proc_lock(p); | |
959 | rlim = p->p_limit->pl_rlimit[which]; | |
960 | proc_unlock(p); | |
2d21ac55 | 961 | |
f427ee49 A |
962 | error = 0; |
963 | /* Sanity check: new soft limit cannot exceed new hard limit */ | |
964 | if (newrlim->rlim_cur > newrlim->rlim_max) { | |
965 | error = EINVAL; | |
966 | } | |
967 | /* | |
968 | * Sanity check: only super-user may raise the hard limit. | |
969 | * newrlim->rlim_cur > rlim.rlim_max implies that the call is increasing the hard limit as well. | |
970 | */ | |
971 | else if (newrlim->rlim_cur > rlim.rlim_max || newrlim->rlim_max > rlim.rlim_max) { | |
972 | /* suser() returns 0 if the calling thread is super user. */ | |
973 | error = suser(kauth_cred_get(), &p->p_acflag); | |
1c79356b A |
974 | } |
975 | ||
f427ee49 A |
976 | if (error) { |
977 | /* Invalid setrlimit request: EINVAL or EPERM */ | |
0a7de745 | 978 | return error; |
2d21ac55 A |
979 | } |
980 | ||
f427ee49 A |
981 | /* Only one thread is able to change the current process's rlimit values */ |
982 | proc_lock(p); | |
983 | proc_limitblock(p); | |
984 | proc_unlock(p); | |
1c79356b | 985 | |
f427ee49 | 986 | /* We have the reader lock of the process's plimit so it's safe to read the rlimit values */ |
0a7de745 | 987 | switch (which) { |
2d21ac55 | 988 | case RLIMIT_CPU: |
f427ee49 | 989 | if (newrlim->rlim_cur == RLIM_INFINITY) { |
2d21ac55 A |
990 | task_vtimer_clear(p->task, TASK_VTIMER_RLIM); |
991 | timerclear(&p->p_rlim_cpu); | |
0a7de745 A |
992 | } else { |
993 | task_absolutetime_info_data_t tinfo; | |
f427ee49 A |
994 | mach_msg_type_number_t count; |
995 | struct timeval ttv, tv; | |
996 | clock_sec_t tv_sec; | |
997 | clock_usec_t tv_usec; | |
2d21ac55 A |
998 | |
999 | count = TASK_ABSOLUTETIME_INFO_COUNT; | |
f427ee49 A |
1000 | task_info(p->task, TASK_ABSOLUTETIME_INFO, (task_info_t)&tinfo, &count); |
1001 | absolutetime_to_microtime(tinfo.total_user + tinfo.total_system, &tv_sec, &tv_usec); | |
b0d623f7 A |
1002 | ttv.tv_sec = tv_sec; |
1003 | ttv.tv_usec = tv_usec; | |
2d21ac55 | 1004 | |
f427ee49 | 1005 | tv.tv_sec = (newrlim->rlim_cur > __INT_MAX__ ? __INT_MAX__ : (__darwin_time_t)newrlim->rlim_cur); |
2d21ac55 A |
1006 | tv.tv_usec = 0; |
1007 | timersub(&tv, &ttv, &p->p_rlim_cpu); | |
1008 | ||
1009 | timerclear(&tv); | |
0a7de745 | 1010 | if (timercmp(&p->p_rlim_cpu, &tv, >)) { |
2d21ac55 | 1011 | task_vtimer_set(p->task, TASK_VTIMER_RLIM); |
0a7de745 | 1012 | } else { |
2d21ac55 A |
1013 | task_vtimer_clear(p->task, TASK_VTIMER_RLIM); |
1014 | ||
1015 | timerclear(&p->p_rlim_cpu); | |
1016 | ||
1017 | psignal(p, SIGXCPU); | |
1018 | } | |
1019 | } | |
1020 | break; | |
1021 | ||
1c79356b | 1022 | case RLIMIT_DATA: |
f427ee49 A |
1023 | if (newrlim->rlim_cur > maxdmap) { |
1024 | newrlim->rlim_cur = maxdmap; | |
0a7de745 | 1025 | } |
f427ee49 A |
1026 | if (newrlim->rlim_max > maxdmap) { |
1027 | newrlim->rlim_max = maxdmap; | |
0a7de745 | 1028 | } |
1c79356b A |
1029 | break; |
1030 | ||
1031 | case RLIMIT_STACK: | |
0a7de745 A |
1032 | if (p->p_lflag & P_LCUSTOM_STACK) { |
1033 | /* Process has a custom stack set - rlimit cannot be used to change it */ | |
1034 | error = EINVAL; | |
1035 | goto out; | |
1036 | } | |
1037 | ||
2d21ac55 | 1038 | /* Disallow illegal stack size instead of clipping */ |
f427ee49 A |
1039 | if (newrlim->rlim_cur > maxsmap || |
1040 | newrlim->rlim_max > maxsmap) { | |
2d21ac55 A |
1041 | if (posix) { |
1042 | error = EINVAL; | |
1043 | goto out; | |
0a7de745 A |
1044 | } else { |
1045 | /* | |
1046 | * 4797860 - workaround poorly written installers by | |
1047 | * doing previous implementation (< 10.5) when caller | |
2d21ac55 A |
1048 | * is non-POSIX conforming. |
1049 | */ | |
f427ee49 A |
1050 | if (newrlim->rlim_cur > maxsmap) { |
1051 | newrlim->rlim_cur = maxsmap; | |
0a7de745 | 1052 | } |
f427ee49 A |
1053 | if (newrlim->rlim_max > maxsmap) { |
1054 | newrlim->rlim_max = maxsmap; | |
0a7de745 | 1055 | } |
2d21ac55 A |
1056 | } |
1057 | } | |
1058 | ||
1c79356b A |
1059 | /* |
1060 | * Stack is allocated to the max at exec time with only | |
1061 | * "rlim_cur" bytes accessible. If stack limit is going | |
1062 | * up make more accessible, if going down make inaccessible. | |
1063 | */ | |
f427ee49 A |
1064 | if (newrlim->rlim_cur > rlim.rlim_cur) { |
1065 | mach_vm_offset_t addr; | |
1066 | mach_vm_size_t size; | |
0a7de745 A |
1067 | |
1068 | /* grow stack */ | |
f427ee49 A |
1069 | size = round_page_64(newrlim->rlim_cur); |
1070 | size -= round_page_64(rlim.rlim_cur); | |
1c79356b | 1071 | |
f427ee49 A |
1072 | addr = (mach_vm_offset_t)(p->user_stack - round_page_64(newrlim->rlim_cur)); |
1073 | kr = mach_vm_protect(current_map(), addr, size, FALSE, VM_PROT_DEFAULT); | |
2d21ac55 A |
1074 | if (kr != KERN_SUCCESS) { |
1075 | error = EINVAL; | |
1076 | goto out; | |
1077 | } | |
f427ee49 A |
1078 | } else if (newrlim->rlim_cur < rlim.rlim_cur) { |
1079 | mach_vm_offset_t addr; | |
1080 | mach_vm_size_t size; | |
1081 | uint64_t cur_sp; | |
2d21ac55 | 1082 | |
0a7de745 | 1083 | /* shrink stack */ |
2d21ac55 A |
1084 | |
1085 | /* | |
1086 | * First check if new stack limit would agree | |
1087 | * with current stack usage. | |
1088 | * Get the current thread's stack pointer... | |
1089 | */ | |
f427ee49 | 1090 | cur_sp = thread_adjuserstack(current_thread(), 0); |
2d21ac55 | 1091 | if (cur_sp <= p->user_stack && |
f427ee49 | 1092 | cur_sp > (p->user_stack - round_page_64(rlim.rlim_cur))) { |
2d21ac55 | 1093 | /* stack pointer is in main stack */ |
f427ee49 | 1094 | if (cur_sp <= (p->user_stack - round_page_64(newrlim->rlim_cur))) { |
2d21ac55 | 1095 | /* |
f427ee49 | 1096 | * New limit would cause current usage to be invalid: |
2d21ac55 A |
1097 | * reject new limit. |
1098 | */ | |
1099 | error = EINVAL; | |
1100 | goto out; | |
1101 | } | |
1102 | } else { | |
1103 | /* not on the main stack: reject */ | |
1104 | error = EINVAL; | |
1105 | goto out; | |
1106 | } | |
0a7de745 | 1107 | |
f427ee49 A |
1108 | size = round_page_64(rlim.rlim_cur); |
1109 | size -= round_page_64(rlim.rlim_cur); | |
2d21ac55 | 1110 | |
f427ee49 | 1111 | addr = (mach_vm_offset_t)(p->user_stack - round_page_64(rlim.rlim_cur)); |
2d21ac55 | 1112 | |
f427ee49 | 1113 | kr = mach_vm_protect(current_map(), addr, size, FALSE, VM_PROT_NONE); |
2d21ac55 A |
1114 | if (kr != KERN_SUCCESS) { |
1115 | error = EINVAL; | |
1116 | goto out; | |
1117 | } | |
1118 | } else { | |
1119 | /* no change ... */ | |
1c79356b A |
1120 | } |
1121 | break; | |
1122 | ||
1123 | case RLIMIT_NOFILE: | |
0a7de745 | 1124 | /* |
f427ee49 A |
1125 | * Nothing to be done here as we already performed the sanity checks before entering the switch code block. |
1126 | * The real NOFILE limits enforced by the kernel is capped at MIN(RLIMIT_NOFILE, maxfilesperproc) | |
0a7de745 | 1127 | */ |
1c79356b A |
1128 | break; |
1129 | ||
1130 | case RLIMIT_NPROC: | |
0a7de745 | 1131 | /* |
55e303ae A |
1132 | * Only root can set to the maxproc limits, as it is |
1133 | * systemwide resource; all others are limited to | |
1134 | * maxprocperuid (presumably less than maxproc). | |
1135 | */ | |
0a7de745 | 1136 | if (kauth_cred_issuser(kauth_cred_get())) { |
f427ee49 A |
1137 | if (newrlim->rlim_cur > (rlim_t)maxproc) { |
1138 | newrlim->rlim_cur = maxproc; | |
0a7de745 | 1139 | } |
f427ee49 A |
1140 | if (newrlim->rlim_max > (rlim_t)maxproc) { |
1141 | newrlim->rlim_max = maxproc; | |
0a7de745 A |
1142 | } |
1143 | } else { | |
f427ee49 A |
1144 | if (newrlim->rlim_cur > (rlim_t)maxprocperuid) { |
1145 | newrlim->rlim_cur = maxprocperuid; | |
0a7de745 | 1146 | } |
f427ee49 A |
1147 | if (newrlim->rlim_max > (rlim_t)maxprocperuid) { |
1148 | newrlim->rlim_max = maxprocperuid; | |
0a7de745 | 1149 | } |
1c79356b A |
1150 | } |
1151 | break; | |
2d21ac55 A |
1152 | |
1153 | case RLIMIT_MEMLOCK: | |
1154 | /* | |
1155 | * Tell the Mach VM layer about the new limit value. | |
1156 | */ | |
f427ee49 A |
1157 | newrlim->rlim_cur = (vm_size_t)newrlim->rlim_cur; |
1158 | vm_map_set_user_wire_limit(current_map(), (vm_size_t)newrlim->rlim_cur); | |
2d21ac55 | 1159 | break; |
55e303ae | 1160 | } /* switch... */ |
f427ee49 A |
1161 | |
1162 | /* Everything checks out and we are now ready to update the rlimit */ | |
2d21ac55 | 1163 | error = 0; |
f427ee49 | 1164 | |
2d21ac55 | 1165 | out: |
f427ee49 A |
1166 | |
1167 | if (error == 0) { | |
1168 | /* | |
1169 | * COW the current plimit if it's shared, otherwise update it in place. | |
1170 | * Finally unblock other threads wishing to change plimit. | |
1171 | */ | |
1172 | proc_lock(p); | |
1173 | proc_limitupdate(p, newrlim, (uint8_t)which); | |
1174 | proc_limitunblock(p); | |
1175 | proc_unlock(p); | |
1176 | } else { | |
1177 | /* | |
1178 | * This setrlimit has failed, just leave the plimit as is and unblock other | |
1179 | * threads wishing to change plimit. | |
1180 | */ | |
1181 | proc_lock(p); | |
1182 | proc_limitunblock(p); | |
1183 | proc_unlock(p); | |
1184 | } | |
1185 | ||
0a7de745 | 1186 | return error; |
1c79356b A |
1187 | } |
1188 | ||
1c79356b A |
1189 | /* ARGSUSED */ |
1190 | int | |
b0d623f7 | 1191 | getrlimit(struct proc *p, struct getrlimit_args *uap, __unused int32_t *retval) |
1c79356b | 1192 | { |
527f9951 | 1193 | struct rlimit lim = {}; |
2d21ac55 A |
1194 | |
1195 | /* | |
1196 | * Take out flag now in case we need to use it to trigger variant | |
1197 | * behaviour later. | |
1198 | */ | |
1199 | uap->which &= ~_RLIMIT_POSIX_FLAG; | |
1200 | ||
0a7de745 A |
1201 | if (uap->which >= RLIM_NLIMITS) { |
1202 | return EINVAL; | |
1203 | } | |
2d21ac55 | 1204 | proc_limitget(p, uap->which, &lim); |
0a7de745 A |
1205 | return copyout((caddr_t)&lim, |
1206 | uap->rlp, sizeof(struct rlimit)); | |
1c79356b A |
1207 | } |
1208 | ||
1209 | /* | |
1210 | * Transform the running time and tick information in proc p into user, | |
1211 | * system, and interrupt time usage. | |
1212 | */ | |
2d21ac55 | 1213 | /* No lock on proc is held for this.. */ |
1c79356b | 1214 | void |
2d21ac55 | 1215 | calcru(struct proc *p, struct timeval *up, struct timeval *sp, struct timeval *ip) |
1c79356b | 1216 | { |
0a7de745 | 1217 | task_t task; |
1c79356b A |
1218 | |
1219 | timerclear(up); | |
1220 | timerclear(sp); | |
0a7de745 | 1221 | if (ip != NULL) { |
1c79356b | 1222 | timerclear(ip); |
0a7de745 | 1223 | } |
1c79356b A |
1224 | |
1225 | task = p->task; | |
1226 | if (task) { | |
316670eb | 1227 | mach_task_basic_info_data_t tinfo; |
1c79356b | 1228 | task_thread_times_info_data_t ttimesinfo; |
b0d623f7 A |
1229 | task_events_info_data_t teventsinfo; |
1230 | mach_msg_type_number_t task_info_count, task_ttimes_count; | |
1231 | mach_msg_type_number_t task_events_count; | |
0a7de745 | 1232 | struct timeval ut, st; |
1c79356b | 1233 | |
0a7de745 | 1234 | task_info_count = MACH_TASK_BASIC_INFO_COUNT; |
316670eb | 1235 | task_info(task, MACH_TASK_BASIC_INFO, |
0a7de745 | 1236 | (task_info_t)&tinfo, &task_info_count); |
1c79356b A |
1237 | ut.tv_sec = tinfo.user_time.seconds; |
1238 | ut.tv_usec = tinfo.user_time.microseconds; | |
1239 | st.tv_sec = tinfo.system_time.seconds; | |
1240 | st.tv_usec = tinfo.system_time.microseconds; | |
55e303ae A |
1241 | timeradd(&ut, up, up); |
1242 | timeradd(&st, sp, sp); | |
1c79356b | 1243 | |
b0d623f7 | 1244 | task_ttimes_count = TASK_THREAD_TIMES_INFO_COUNT; |
1c79356b | 1245 | task_info(task, TASK_THREAD_TIMES_INFO, |
0a7de745 | 1246 | (task_info_t)&ttimesinfo, &task_ttimes_count); |
1c79356b A |
1247 | |
1248 | ut.tv_sec = ttimesinfo.user_time.seconds; | |
1249 | ut.tv_usec = ttimesinfo.user_time.microseconds; | |
1250 | st.tv_sec = ttimesinfo.system_time.seconds; | |
1251 | st.tv_usec = ttimesinfo.system_time.microseconds; | |
55e303ae A |
1252 | timeradd(&ut, up, up); |
1253 | timeradd(&st, sp, sp); | |
b0d623f7 A |
1254 | |
1255 | task_events_count = TASK_EVENTS_INFO_COUNT; | |
1256 | task_info(task, TASK_EVENTS_INFO, | |
0a7de745 | 1257 | (task_info_t)&teventsinfo, &task_events_count); |
b0d623f7 A |
1258 | |
1259 | /* | |
1260 | * No need to lock "p": this does not need to be | |
1261 | * completely consistent, right ? | |
1262 | */ | |
1263 | p->p_stats->p_ru.ru_minflt = (teventsinfo.faults - | |
0a7de745 | 1264 | teventsinfo.pageins); |
b0d623f7 A |
1265 | p->p_stats->p_ru.ru_majflt = teventsinfo.pageins; |
1266 | p->p_stats->p_ru.ru_nivcsw = (teventsinfo.csw - | |
0a7de745 A |
1267 | p->p_stats->p_ru.ru_nvcsw); |
1268 | if (p->p_stats->p_ru.ru_nivcsw < 0) { | |
b0d623f7 | 1269 | p->p_stats->p_ru.ru_nivcsw = 0; |
0a7de745 | 1270 | } |
b0d623f7 | 1271 | |
f427ee49 | 1272 | p->p_stats->p_ru.ru_maxrss = (long)tinfo.resident_size_max; |
1c79356b A |
1273 | } |
1274 | } | |
1275 | ||
b0d623f7 A |
1276 | __private_extern__ void munge_user64_rusage(struct rusage *a_rusage_p, struct user64_rusage *a_user_rusage_p); |
1277 | __private_extern__ void munge_user32_rusage(struct rusage *a_rusage_p, struct user32_rusage *a_user_rusage_p); | |
91447636 | 1278 | |
1c79356b A |
1279 | /* ARGSUSED */ |
1280 | int | |
b0d623f7 | 1281 | getrusage(struct proc *p, struct getrusage_args *uap, __unused int32_t *retval) |
1c79356b A |
1282 | { |
1283 | struct rusage *rup, rubuf; | |
d9a64523 A |
1284 | struct user64_rusage rubuf64 = {}; |
1285 | struct user32_rusage rubuf32 = {}; | |
0a7de745 A |
1286 | size_t retsize = sizeof(rubuf); /* default: 32 bits */ |
1287 | caddr_t retbuf = (caddr_t)&rubuf; /* default: 32 bits */ | |
2d21ac55 A |
1288 | struct timeval utime; |
1289 | struct timeval stime; | |
1290 | ||
1c79356b A |
1291 | |
1292 | switch (uap->who) { | |
1c79356b | 1293 | case RUSAGE_SELF: |
2d21ac55 | 1294 | calcru(p, &utime, &stime, NULL); |
2d21ac55 A |
1295 | proc_lock(p); |
1296 | rup = &p->p_stats->p_ru; | |
1297 | rup->ru_utime = utime; | |
1298 | rup->ru_stime = stime; | |
1299 | ||
1c79356b | 1300 | rubuf = *rup; |
2d21ac55 A |
1301 | proc_unlock(p); |
1302 | ||
1c79356b A |
1303 | break; |
1304 | ||
1305 | case RUSAGE_CHILDREN: | |
2d21ac55 | 1306 | proc_lock(p); |
1c79356b A |
1307 | rup = &p->p_stats->p_cru; |
1308 | rubuf = *rup; | |
2d21ac55 | 1309 | proc_unlock(p); |
1c79356b A |
1310 | break; |
1311 | ||
1312 | default: | |
0a7de745 | 1313 | return EINVAL; |
1c79356b | 1314 | } |
91447636 A |
1315 | if (IS_64BIT_PROCESS(p)) { |
1316 | retsize = sizeof(rubuf64); | |
1317 | retbuf = (caddr_t)&rubuf64; | |
b0d623f7 A |
1318 | munge_user64_rusage(&rubuf, &rubuf64); |
1319 | } else { | |
1320 | retsize = sizeof(rubuf32); | |
1321 | retbuf = (caddr_t)&rubuf32; | |
1322 | munge_user32_rusage(&rubuf, &rubuf32); | |
91447636 | 1323 | } |
b0d623f7 | 1324 | |
0a7de745 | 1325 | return copyout(retbuf, uap->rusage, retsize); |
1c79356b A |
1326 | } |
1327 | ||
1328 | void | |
2d21ac55 | 1329 | ruadd(struct rusage *ru, struct rusage *ru2) |
1c79356b | 1330 | { |
2d21ac55 | 1331 | long *ip, *ip2; |
b0d623f7 | 1332 | long i; |
1c79356b A |
1333 | |
1334 | timeradd(&ru->ru_utime, &ru2->ru_utime, &ru->ru_utime); | |
1335 | timeradd(&ru->ru_stime, &ru2->ru_stime, &ru->ru_stime); | |
0a7de745 | 1336 | if (ru->ru_maxrss < ru2->ru_maxrss) { |
1c79356b | 1337 | ru->ru_maxrss = ru2->ru_maxrss; |
0a7de745 | 1338 | } |
1c79356b | 1339 | ip = &ru->ru_first; ip2 = &ru2->ru_first; |
0a7de745 | 1340 | for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--) { |
1c79356b | 1341 | *ip++ += *ip2++; |
0a7de745 | 1342 | } |
1c79356b A |
1343 | } |
1344 | ||
39236c6e A |
1345 | /* |
1346 | * Add the rusage stats of child in parent. | |
0a7de745 | 1347 | * |
39236c6e A |
1348 | * It adds rusage statistics of child process and statistics of all its |
1349 | * children to its parent. | |
1350 | * | |
1351 | * Note: proc lock of parent should be held while calling this function. | |
1352 | */ | |
1353 | void | |
fe8ab488 | 1354 | update_rusage_info_child(struct rusage_info_child *ri, rusage_info_current *ri_current) |
39236c6e | 1355 | { |
fe8ab488 | 1356 | ri->ri_child_user_time += (ri_current->ri_user_time + |
0a7de745 | 1357 | ri_current->ri_child_user_time); |
fe8ab488 | 1358 | ri->ri_child_system_time += (ri_current->ri_system_time + |
0a7de745 | 1359 | ri_current->ri_child_system_time); |
fe8ab488 | 1360 | ri->ri_child_pkg_idle_wkups += (ri_current->ri_pkg_idle_wkups + |
0a7de745 | 1361 | ri_current->ri_child_pkg_idle_wkups); |
fe8ab488 | 1362 | ri->ri_child_interrupt_wkups += (ri_current->ri_interrupt_wkups + |
0a7de745 | 1363 | ri_current->ri_child_interrupt_wkups); |
fe8ab488 | 1364 | ri->ri_child_pageins += (ri_current->ri_pageins + |
0a7de745 | 1365 | ri_current->ri_child_pageins); |
fe8ab488 | 1366 | ri->ri_child_elapsed_abstime += ((ri_current->ri_proc_exit_abstime - |
0a7de745 | 1367 | ri_current->ri_proc_start_abstime) + ri_current->ri_child_elapsed_abstime); |
39236c6e A |
1368 | } |
1369 | ||
f427ee49 A |
1370 | /* |
1371 | * Reading soft limit from specified resource. | |
1372 | */ | |
1373 | rlim_t | |
1374 | proc_limitgetcur(proc_t p, int which, boolean_t to_lock_proc) | |
2d21ac55 | 1375 | { |
f427ee49 | 1376 | rlim_t rlim_cur; |
2d21ac55 | 1377 | |
f427ee49 A |
1378 | assert(p); |
1379 | assert(which < RLIM_NLIMITS); | |
2d21ac55 | 1380 | |
f427ee49 A |
1381 | /* |
1382 | * Serialize access to the process's plimit pointer for concurrent threads. | |
1383 | */ | |
1384 | if (to_lock_proc) { | |
1385 | lck_mtx_assert(&p->p_mlock, LCK_MTX_ASSERT_NOTOWNED); | |
1386 | proc_lock(p); | |
1387 | } | |
1388 | ||
1389 | rlim_cur = p->p_limit->pl_rlimit[which].rlim_cur; | |
1390 | ||
1391 | if (to_lock_proc) { | |
1392 | proc_unlock(p); | |
1393 | } | |
1394 | ||
1395 | return rlim_cur; | |
1396 | } | |
1397 | ||
1398 | /* | |
1399 | * Writing soft limit to specified resource. This is an internal function | |
1400 | * used only by proc_exit and vfork_exit_internal to update RLIMIT_FSIZE in | |
1401 | * place without invoking setrlimit. | |
1402 | */ | |
2d21ac55 | 1403 | void |
f427ee49 | 1404 | proc_limitsetcur_internal(proc_t p, int which, rlim_t value) |
1c79356b | 1405 | { |
f427ee49 | 1406 | struct rlimit rlim; |
2d21ac55 | 1407 | |
f427ee49 A |
1408 | assert(p); |
1409 | assertf(which == RLIMIT_FSIZE, "%s only supports RLIMIT_FSIZE\n", __FUNCTION__); | |
2d21ac55 | 1410 | |
2d21ac55 | 1411 | |
f427ee49 A |
1412 | proc_lock(p); |
1413 | ||
1414 | /* Only one thread is able to change rlimit values at a time */ | |
1415 | proc_limitblock(p); | |
1416 | ||
1417 | /* Prepare an rlimit for proc_limitupdate */ | |
1418 | rlim = p->p_limit->pl_rlimit[which]; | |
1419 | rlim.rlim_cur = value; | |
1420 | ||
1421 | /* | |
1422 | * proc_limitupdate will COW the current plimit and update specified the soft limit | |
1423 | * if the plimit is shared, otherwise it will update the soft limit in place. | |
1424 | */ | |
1425 | proc_limitupdate(p, &rlim, (uint8_t)which); | |
1426 | ||
1427 | /* Unblock other threads wishing to change plimit */ | |
1428 | proc_limitunblock(p); | |
1429 | ||
1430 | proc_unlock(p); | |
2d21ac55 A |
1431 | } |
1432 | ||
f427ee49 A |
1433 | void |
1434 | proc_limitget(proc_t p, int which, struct rlimit * limp) | |
1435 | { | |
1436 | assert(p); | |
1437 | assert(limp); | |
1438 | assert(which < RLIM_NLIMITS); | |
1439 | ||
1440 | /* Protect writes to the process's plimit pointer issued by concurrent threads */ | |
1441 | proc_lock(p); | |
1442 | ||
1443 | limp->rlim_cur = p->p_limit->pl_rlimit[which].rlim_cur; | |
1444 | limp->rlim_max = p->p_limit->pl_rlimit[which].rlim_max; | |
1445 | ||
1446 | proc_unlock(p); | |
1447 | } | |
2d21ac55 A |
1448 | |
1449 | void | |
1450 | proc_limitfork(proc_t parent, proc_t child) | |
1451 | { | |
f427ee49 A |
1452 | assert(parent && child); |
1453 | ||
1454 | proc_lock(parent); | |
1455 | ||
1456 | /* Child proc inherits parent's plimit */ | |
2d21ac55 | 1457 | child->p_limit = parent->p_limit; |
f427ee49 A |
1458 | |
1459 | /* Increment refcnt of the shared plimit */ | |
1460 | os_ref_retain(&parent->p_limit->pl_refcnt); | |
1461 | ||
1462 | proc_unlock(parent); | |
2d21ac55 A |
1463 | } |
1464 | ||
1465 | void | |
f427ee49 | 1466 | proc_limitdrop(proc_t p) |
2d21ac55 | 1467 | { |
f427ee49 A |
1468 | struct plimit *free_plim = NULL; |
1469 | os_ref_count_t refcnt; | |
1470 | ||
2d21ac55 | 1471 | proc_lock(p); |
f427ee49 A |
1472 | |
1473 | /* Drop the plimit reference before exiting the system */ | |
1474 | refcnt = os_ref_release(&p->p_limit->pl_refcnt); | |
1475 | if (refcnt == 0) { | |
1476 | free_plim = p->p_limit; | |
1477 | } | |
1478 | ||
1479 | p->p_limit = NULL; | |
1480 | proc_unlock(p); | |
1481 | ||
1482 | /* We are the last user of this plimit, free it now. */ | |
1483 | if (free_plim != NULL) { | |
1484 | zfree(plimit_zone, free_plim); | |
1485 | } | |
1486 | } | |
1487 | ||
1488 | /* | |
1489 | * proc_limitblock/unblock are used to serialize access to plimit | |
1490 | * from concurrent threads within the same process. | |
1491 | * Callers must be holding the proc lock to enter, return with | |
1492 | * the proc lock locked | |
1493 | */ | |
1494 | void | |
1495 | proc_limitblock(proc_t p) | |
1496 | { | |
1497 | lck_mtx_assert(&p->p_mlock, LCK_MTX_ASSERT_OWNED); | |
1498 | ||
2d21ac55 A |
1499 | while (p->p_lflag & P_LLIMCHANGE) { |
1500 | p->p_lflag |= P_LLIMWAIT; | |
f427ee49 | 1501 | msleep(&p->p_limit, &p->p_mlock, 0, "proc_limitblock", NULL); |
2d21ac55 A |
1502 | } |
1503 | p->p_lflag |= P_LLIMCHANGE; | |
2d21ac55 A |
1504 | } |
1505 | ||
f427ee49 A |
1506 | /* |
1507 | * Callers must be holding the proc lock to enter, return with | |
1508 | * the proc lock locked | |
1509 | */ | |
2d21ac55 A |
1510 | void |
1511 | proc_limitunblock(proc_t p) | |
1512 | { | |
f427ee49 A |
1513 | lck_mtx_assert(&p->p_mlock, LCK_MTX_ASSERT_OWNED); |
1514 | ||
2d21ac55 A |
1515 | p->p_lflag &= ~P_LLIMCHANGE; |
1516 | if (p->p_lflag & P_LLIMWAIT) { | |
1517 | p->p_lflag &= ~P_LLIMWAIT; | |
f427ee49 | 1518 | wakeup(&p->p_limit); |
2d21ac55 | 1519 | } |
2d21ac55 A |
1520 | } |
1521 | ||
f427ee49 A |
1522 | /* |
1523 | * Change the rlimit values of process "p" to "rlim" for resource "which". | |
1524 | * | |
1525 | * If the current plimit is shared by multiple processes (refcnt > 1): | |
1526 | * this routine replaces the process's original plimit with a new plimit, | |
1527 | * update the requeted rlimit values, and free the original plimit if this | |
1528 | * process is the last user. | |
1529 | * | |
1530 | * If the current plimit is used only by the calling process (refcnt == 1): | |
1531 | * this routine updates the new rlimit values in place. | |
1532 | * | |
1533 | * Note: caller must be holding the proc lock before entering this routine. | |
1534 | * This routine allocates and frees kernel memory without holding the proc lock | |
1535 | * to minimize contention, and returns with the proc lock held. | |
1536 | */ | |
1537 | void | |
1538 | proc_limitupdate(proc_t p, struct rlimit *rlim, uint8_t which) | |
2d21ac55 | 1539 | { |
f427ee49 A |
1540 | struct plimit *copy_plim; |
1541 | struct plimit *free_plim; | |
1542 | os_ref_count_t refcnt; | |
2d21ac55 | 1543 | |
f427ee49 A |
1544 | assert(p && p->p_limit); |
1545 | assert(rlim); | |
1546 | assert(which < RLIM_NLIMITS); | |
1547 | lck_mtx_assert(&p->p_mlock, LCK_MTX_ASSERT_OWNED); | |
2d21ac55 | 1548 | |
f427ee49 A |
1549 | /* |
1550 | * If we are the only user of this plimit, don't bother allocating a plimit | |
1551 | * before making changes. Just modify the rlimit values in place. | |
1552 | */ | |
1553 | refcnt = os_ref_get_count(&p->p_limit->pl_refcnt); | |
1554 | if (refcnt == 1) { | |
1555 | p->p_limit->pl_rlimit[which] = *rlim; | |
1556 | return; | |
2d21ac55 | 1557 | } |
0a7de745 | 1558 | |
f427ee49 A |
1559 | /* |
1560 | * Allocating a new plimit for this process to apply the requested rlimit values. | |
1561 | * Not holding the lock on the original plimit gives other processes in the system | |
1562 | * a chance to access the plimit while we wait for memory below. | |
1563 | * | |
1564 | * The default zalloc should always succeed when WAIT flag. | |
1565 | */ | |
1566 | proc_unlock(p); | |
1567 | copy_plim = zalloc(plimit_zone); | |
1568 | ||
1569 | /* Copy the current p_limit */ | |
1570 | proc_lock(p); | |
1571 | bcopy(p->p_limit->pl_rlimit, copy_plim->pl_rlimit, sizeof(struct rlimit) * RLIM_NLIMITS); | |
1c79356b | 1572 | |
f427ee49 A |
1573 | /* |
1574 | * Drop our reference to the old plimit. Other processes sharing the old plimit could | |
1575 | * have exited the system when we wait for memory for the new plimit above, thus, we | |
1576 | * need to check the refcnt again and free the old plimit if this process is the last | |
1577 | * user. Also since we are holding the proc lock here, it's impossible for another threads | |
1578 | * to dereference the plimit, so it's safe to free the old plimit memory. | |
1579 | */ | |
1580 | free_plim = NULL; | |
1581 | refcnt = os_ref_release(&p->p_limit->pl_refcnt); | |
1582 | if (refcnt == 0) { | |
1583 | free_plim = p->p_limit; | |
2d21ac55 | 1584 | } |
f427ee49 A |
1585 | /* Initialize the newly allocated plimit */ |
1586 | os_ref_init_count(©_plim->pl_refcnt, &rlimit_refgrp, 1); | |
2d21ac55 | 1587 | |
f427ee49 A |
1588 | /* Apply new rlimit values */ |
1589 | copy_plim->pl_rlimit[which] = *rlim; | |
2d21ac55 | 1590 | |
f427ee49 A |
1591 | /* All set, update the process's plimit pointer to the new plimit. */ |
1592 | p->p_limit = copy_plim; | |
1593 | proc_unlock(p); | |
1594 | ||
1595 | if (free_plim != NULL) { | |
1596 | zfree(plimit_zone, free_plim); | |
1597 | } | |
1598 | ||
1599 | /* Return with proc->p_mlock locked */ | |
1600 | proc_lock(p); | |
2d21ac55 A |
1601 | } |
1602 | ||
d9a64523 A |
1603 | static int |
1604 | iopolicysys_disk(struct proc *p, int cmd, int scope, int policy, struct _iopol_param_t *iop_param); | |
1605 | static int | |
1606 | iopolicysys_vfs_hfs_case_sensitivity(struct proc *p, int cmd, int scope, int policy, struct _iopol_param_t *iop_param); | |
1607 | static int | |
1608 | iopolicysys_vfs_atime_updates(struct proc *p, int cmd, int scope, int policy, struct _iopol_param_t *iop_param); | |
cb323159 A |
1609 | static int |
1610 | iopolicysys_vfs_materialize_dataless_files(struct proc *p, int cmd, int scope, int policy, struct _iopol_param_t *iop_param); | |
1611 | static int | |
1612 | iopolicysys_vfs_statfs_no_data_volume(struct proc *p, int cmd, int scope, int policy, struct _iopol_param_t *iop_param); | |
f427ee49 A |
1613 | static int |
1614 | iopolicysys_vfs_trigger_resolve(struct proc *p, int cmd, int scope, int policy, struct _iopol_param_t *iop_param); | |
1615 | static int | |
1616 | iopolicysys_vfs_ignore_content_protection(struct proc *p, int cmd, int scope, int policy, struct _iopol_param_t *iop_param); | |
c3c9b80d A |
1617 | static int |
1618 | iopolicysys_vfs_ignore_node_permissions(struct proc *p, int cmd, int scope, int policy, struct _iopol_param_t *ipo_param); | |
1619 | static int | |
1620 | iopolicysys_vfs_skip_mtime_update(struct proc *p, int cmd, int scope, int policy, struct _iopol_param_t *iop_param); | |
d9a64523 | 1621 | |
2d21ac55 A |
1622 | /* |
1623 | * iopolicysys | |
1624 | * | |
1625 | * Description: System call MUX for use in manipulating I/O policy attributes of the current process or thread | |
1626 | * | |
1627 | * Parameters: cmd Policy command | |
1628 | * arg Pointer to policy arguments | |
1629 | * | |
1630 | * Returns: 0 Success | |
1631 | * EINVAL Invalid command or invalid policy arguments | |
1632 | * | |
1633 | */ | |
1634 | int | |
fe8ab488 | 1635 | iopolicysys(struct proc *p, struct iopolicysys_args *uap, int32_t *retval) |
2d21ac55 | 1636 | { |
39236c6e | 1637 | int error = 0; |
6d2010ae | 1638 | struct _iopol_param_t iop_param; |
2d21ac55 | 1639 | |
0a7de745 | 1640 | if ((error = copyin(uap->arg, &iop_param, sizeof(iop_param))) != 0) { |
6d2010ae | 1641 | goto out; |
0a7de745 | 1642 | } |
2d21ac55 | 1643 | |
39236c6e | 1644 | switch (iop_param.iop_iotype) { |
0a7de745 A |
1645 | case IOPOL_TYPE_DISK: |
1646 | error = iopolicysys_disk(p, uap->cmd, iop_param.iop_scope, iop_param.iop_policy, &iop_param); | |
1647 | if (error == EIDRM) { | |
1648 | *retval = -2; | |
1649 | error = 0; | |
1650 | } | |
1651 | if (error) { | |
1652 | goto out; | |
1653 | } | |
1654 | break; | |
1655 | case IOPOL_TYPE_VFS_HFS_CASE_SENSITIVITY: | |
1656 | error = iopolicysys_vfs_hfs_case_sensitivity(p, uap->cmd, iop_param.iop_scope, iop_param.iop_policy, &iop_param); | |
1657 | if (error) { | |
1658 | goto out; | |
1659 | } | |
1660 | break; | |
1661 | case IOPOL_TYPE_VFS_ATIME_UPDATES: | |
1662 | error = iopolicysys_vfs_atime_updates(p, uap->cmd, iop_param.iop_scope, iop_param.iop_policy, &iop_param); | |
1663 | if (error) { | |
39236c6e | 1664 | goto out; |
0a7de745 A |
1665 | } |
1666 | break; | |
cb323159 A |
1667 | case IOPOL_TYPE_VFS_MATERIALIZE_DATALESS_FILES: |
1668 | error = iopolicysys_vfs_materialize_dataless_files(p, uap->cmd, iop_param.iop_scope, iop_param.iop_policy, &iop_param); | |
1669 | if (error) { | |
1670 | goto out; | |
1671 | } | |
1672 | break; | |
1673 | case IOPOL_TYPE_VFS_STATFS_NO_DATA_VOLUME: | |
1674 | error = iopolicysys_vfs_statfs_no_data_volume(p, uap->cmd, iop_param.iop_scope, iop_param.iop_policy, &iop_param); | |
1675 | if (error) { | |
1676 | goto out; | |
1677 | } | |
f427ee49 A |
1678 | break; |
1679 | case IOPOL_TYPE_VFS_TRIGGER_RESOLVE: | |
1680 | error = iopolicysys_vfs_trigger_resolve(p, uap->cmd, iop_param.iop_scope, iop_param.iop_policy, &iop_param); | |
1681 | if (error) { | |
1682 | goto out; | |
1683 | } | |
1684 | break; | |
1685 | case IOPOL_TYPE_VFS_IGNORE_CONTENT_PROTECTION: | |
1686 | error = iopolicysys_vfs_ignore_content_protection(p, uap->cmd, iop_param.iop_scope, iop_param.iop_policy, &iop_param); | |
1687 | if (error) { | |
1688 | goto out; | |
1689 | } | |
1690 | break; | |
c3c9b80d A |
1691 | case IOPOL_TYPE_VFS_IGNORE_PERMISSIONS: |
1692 | error = iopolicysys_vfs_ignore_node_permissions(p, uap->cmd, iop_param.iop_scope, iop_param.iop_policy, &iop_param); | |
1693 | if (error) { | |
1694 | goto out; | |
1695 | } | |
1696 | break; | |
1697 | case IOPOL_TYPE_VFS_SKIP_MTIME_UPDATE: | |
1698 | error = iopolicysys_vfs_skip_mtime_update(p, uap->cmd, iop_param.iop_scope, iop_param.iop_policy, &iop_param); | |
1699 | if (error) { | |
1700 | goto out; | |
1701 | } | |
1702 | break; | |
0a7de745 A |
1703 | default: |
1704 | error = EINVAL; | |
1705 | goto out; | |
6d2010ae A |
1706 | } |
1707 | ||
39236c6e A |
1708 | /* Individual iotype handlers are expected to update iop_param, if requested with a GET command */ |
1709 | if (uap->cmd == IOPOL_CMD_GET) { | |
1710 | error = copyout((caddr_t)&iop_param, uap->arg, sizeof(iop_param)); | |
0a7de745 | 1711 | if (error) { |
39236c6e | 1712 | goto out; |
0a7de745 | 1713 | } |
2d21ac55 | 1714 | } |
39236c6e A |
1715 | |
1716 | out: | |
0a7de745 | 1717 | return error; |
39236c6e A |
1718 | } |
1719 | ||
1720 | static int | |
1721 | iopolicysys_disk(struct proc *p __unused, int cmd, int scope, int policy, struct _iopol_param_t *iop_param) | |
1722 | { | |
0a7de745 A |
1723 | int error = 0; |
1724 | thread_t thread; | |
1725 | int policy_flavor; | |
39236c6e A |
1726 | |
1727 | /* Validate scope */ | |
1728 | switch (scope) { | |
0a7de745 A |
1729 | case IOPOL_SCOPE_PROCESS: |
1730 | thread = THREAD_NULL; | |
1731 | policy_flavor = TASK_POLICY_IOPOL; | |
1732 | break; | |
1733 | ||
1734 | case IOPOL_SCOPE_THREAD: | |
1735 | thread = current_thread(); | |
1736 | policy_flavor = TASK_POLICY_IOPOL; | |
39236c6e | 1737 | |
0a7de745 A |
1738 | /* Not allowed to combine QoS and (non-PASSIVE) IO policy, doing so strips the QoS */ |
1739 | if (cmd == IOPOL_CMD_SET && thread_has_qos_policy(thread)) { | |
1740 | switch (policy) { | |
1741 | case IOPOL_DEFAULT: | |
1742 | case IOPOL_PASSIVE: | |
1743 | break; | |
1744 | case IOPOL_UTILITY: | |
1745 | case IOPOL_THROTTLE: | |
1746 | case IOPOL_IMPORTANT: | |
1747 | case IOPOL_STANDARD: | |
1748 | if (!thread_is_static_param(thread)) { | |
1749 | thread_remove_qos_policy(thread); | |
1750 | /* | |
1751 | * This is not an error case, this is to return a marker to user-space that | |
1752 | * we stripped the thread of its QoS class. | |
1753 | */ | |
1754 | error = EIDRM; | |
1755 | break; | |
fe8ab488 | 1756 | } |
f427ee49 | 1757 | OS_FALLTHROUGH; |
0a7de745 A |
1758 | default: |
1759 | error = EINVAL; | |
1760 | goto out; | |
fe8ab488 | 1761 | } |
0a7de745 A |
1762 | } |
1763 | break; | |
39236c6e | 1764 | |
0a7de745 | 1765 | case IOPOL_SCOPE_DARWIN_BG: |
f427ee49 A |
1766 | #if !defined(XNU_TARGET_OS_OSX) |
1767 | /* We don't want this on platforms outside of macOS as BG is always IOPOL_THROTTLE */ | |
0a7de745 A |
1768 | error = ENOTSUP; |
1769 | goto out; | |
f427ee49 | 1770 | #else /* !defined(XNU_TARGET_OS_OSX) */ |
0a7de745 A |
1771 | thread = THREAD_NULL; |
1772 | policy_flavor = TASK_POLICY_DARWIN_BG_IOPOL; | |
1773 | break; | |
f427ee49 | 1774 | #endif /* !defined(XNU_TARGET_OS_OSX) */ |
39236c6e | 1775 | |
0a7de745 A |
1776 | default: |
1777 | error = EINVAL; | |
1778 | goto out; | |
39236c6e A |
1779 | } |
1780 | ||
1781 | /* Validate policy */ | |
1782 | if (cmd == IOPOL_CMD_SET) { | |
1783 | switch (policy) { | |
0a7de745 A |
1784 | case IOPOL_DEFAULT: |
1785 | if (scope == IOPOL_SCOPE_DARWIN_BG) { | |
1786 | /* the current default BG throttle level is UTILITY */ | |
1787 | policy = IOPOL_UTILITY; | |
1788 | } else { | |
1789 | policy = IOPOL_IMPORTANT; | |
1790 | } | |
1791 | break; | |
1792 | case IOPOL_UTILITY: | |
1793 | /* fall-through */ | |
1794 | case IOPOL_THROTTLE: | |
1795 | /* These levels are OK */ | |
1796 | break; | |
1797 | case IOPOL_IMPORTANT: | |
1798 | /* fall-through */ | |
1799 | case IOPOL_STANDARD: | |
1800 | /* fall-through */ | |
1801 | case IOPOL_PASSIVE: | |
1802 | if (scope == IOPOL_SCOPE_DARWIN_BG) { | |
1803 | /* These levels are invalid for BG */ | |
39236c6e A |
1804 | error = EINVAL; |
1805 | goto out; | |
0a7de745 A |
1806 | } else { |
1807 | /* OK for other scopes */ | |
1808 | } | |
1809 | break; | |
1810 | default: | |
1811 | error = EINVAL; | |
1812 | goto out; | |
6d2010ae | 1813 | } |
39236c6e | 1814 | } |
2d21ac55 | 1815 | |
39236c6e | 1816 | /* Perform command */ |
0a7de745 A |
1817 | switch (cmd) { |
1818 | case IOPOL_CMD_SET: | |
1819 | if (thread != THREAD_NULL) { | |
1820 | proc_set_thread_policy(thread, TASK_POLICY_INTERNAL, policy_flavor, policy); | |
1821 | } else { | |
1822 | proc_set_task_policy(current_task(), TASK_POLICY_INTERNAL, policy_flavor, policy); | |
1823 | } | |
1824 | break; | |
1825 | case IOPOL_CMD_GET: | |
1826 | if (thread != THREAD_NULL) { | |
1827 | policy = proc_get_thread_policy(thread, TASK_POLICY_INTERNAL, policy_flavor); | |
1828 | } else { | |
1829 | policy = proc_get_task_policy(current_task(), TASK_POLICY_INTERNAL, policy_flavor); | |
1830 | } | |
1831 | iop_param->iop_policy = policy; | |
1832 | break; | |
1833 | default: | |
1834 | error = EINVAL; /* unknown command */ | |
1835 | break; | |
6d2010ae A |
1836 | } |
1837 | ||
6d2010ae | 1838 | out: |
0a7de745 | 1839 | return error; |
1c79356b | 1840 | } |
b0d623f7 | 1841 | |
39236c6e | 1842 | static int |
d9a64523 | 1843 | iopolicysys_vfs_hfs_case_sensitivity(struct proc *p, int cmd, int scope, int policy, struct _iopol_param_t *iop_param) |
39236c6e | 1844 | { |
0a7de745 | 1845 | int error = 0; |
b0d623f7 | 1846 | |
39236c6e A |
1847 | /* Validate scope */ |
1848 | switch (scope) { | |
0a7de745 A |
1849 | case IOPOL_SCOPE_PROCESS: |
1850 | /* Only process OK */ | |
1851 | break; | |
1852 | default: | |
1853 | error = EINVAL; | |
1854 | goto out; | |
39236c6e | 1855 | } |
b0d623f7 | 1856 | |
39236c6e A |
1857 | /* Validate policy */ |
1858 | if (cmd == IOPOL_CMD_SET) { | |
1859 | switch (policy) { | |
0a7de745 A |
1860 | case IOPOL_VFS_HFS_CASE_SENSITIVITY_DEFAULT: |
1861 | /* fall-through */ | |
1862 | case IOPOL_VFS_HFS_CASE_SENSITIVITY_FORCE_CASE_SENSITIVE: | |
1863 | /* These policies are OK */ | |
1864 | break; | |
1865 | default: | |
1866 | error = EINVAL; | |
1867 | goto out; | |
39236c6e A |
1868 | } |
1869 | } | |
1870 | ||
1871 | /* Perform command */ | |
0a7de745 A |
1872 | switch (cmd) { |
1873 | case IOPOL_CMD_SET: | |
1874 | if (0 == kauth_cred_issuser(kauth_cred_get())) { | |
1875 | /* If it's a non-root process, it needs to have the entitlement to set the policy */ | |
1876 | boolean_t entitled = FALSE; | |
1877 | entitled = IOTaskHasEntitlement(current_task(), "com.apple.private.iopol.case_sensitivity"); | |
1878 | if (!entitled) { | |
1879 | error = EPERM; | |
1880 | goto out; | |
39236c6e | 1881 | } |
0a7de745 | 1882 | } |
39236c6e | 1883 | |
0a7de745 A |
1884 | switch (policy) { |
1885 | case IOPOL_VFS_HFS_CASE_SENSITIVITY_DEFAULT: | |
1886 | OSBitAndAtomic16(~((uint32_t)P_VFS_IOPOLICY_FORCE_HFS_CASE_SENSITIVITY), &p->p_vfs_iopolicy); | |
39236c6e | 1887 | break; |
0a7de745 A |
1888 | case IOPOL_VFS_HFS_CASE_SENSITIVITY_FORCE_CASE_SENSITIVE: |
1889 | OSBitOrAtomic16((uint32_t)P_VFS_IOPOLICY_FORCE_HFS_CASE_SENSITIVITY, &p->p_vfs_iopolicy); | |
39236c6e A |
1890 | break; |
1891 | default: | |
0a7de745 A |
1892 | error = EINVAL; |
1893 | goto out; | |
1894 | } | |
1895 | ||
1896 | break; | |
1897 | case IOPOL_CMD_GET: | |
1898 | iop_param->iop_policy = (p->p_vfs_iopolicy & P_VFS_IOPOLICY_FORCE_HFS_CASE_SENSITIVITY) | |
1899 | ? IOPOL_VFS_HFS_CASE_SENSITIVITY_FORCE_CASE_SENSITIVE | |
1900 | : IOPOL_VFS_HFS_CASE_SENSITIVITY_DEFAULT; | |
1901 | break; | |
1902 | default: | |
1903 | error = EINVAL; /* unknown command */ | |
1904 | break; | |
39236c6e A |
1905 | } |
1906 | ||
1907 | out: | |
0a7de745 | 1908 | return error; |
6d2010ae A |
1909 | } |
1910 | ||
d9a64523 A |
1911 | static inline int |
1912 | get_thread_atime_policy(struct uthread *ut) | |
1913 | { | |
0a7de745 | 1914 | return (ut->uu_flag & UT_ATIME_UPDATE) ? IOPOL_ATIME_UPDATES_OFF : IOPOL_ATIME_UPDATES_DEFAULT; |
d9a64523 A |
1915 | } |
1916 | ||
1917 | static inline void | |
1918 | set_thread_atime_policy(struct uthread *ut, int policy) | |
1919 | { | |
1920 | if (policy == IOPOL_ATIME_UPDATES_OFF) { | |
1921 | ut->uu_flag |= UT_ATIME_UPDATE; | |
1922 | } else { | |
1923 | ut->uu_flag &= ~UT_ATIME_UPDATE; | |
1924 | } | |
1925 | } | |
1926 | ||
1927 | static inline void | |
1928 | set_task_atime_policy(struct proc *p, int policy) | |
1929 | { | |
1930 | if (policy == IOPOL_ATIME_UPDATES_OFF) { | |
1931 | OSBitOrAtomic16((uint16_t)P_VFS_IOPOLICY_ATIME_UPDATES, &p->p_vfs_iopolicy); | |
1932 | } else { | |
1933 | OSBitAndAtomic16(~((uint16_t)P_VFS_IOPOLICY_ATIME_UPDATES), &p->p_vfs_iopolicy); | |
1934 | } | |
1935 | } | |
1936 | ||
1937 | static inline int | |
1938 | get_task_atime_policy(struct proc *p) | |
1939 | { | |
0a7de745 | 1940 | return (p->p_vfs_iopolicy & P_VFS_IOPOLICY_ATIME_UPDATES) ? IOPOL_ATIME_UPDATES_OFF : IOPOL_ATIME_UPDATES_DEFAULT; |
d9a64523 A |
1941 | } |
1942 | ||
1943 | static int | |
1944 | iopolicysys_vfs_atime_updates(struct proc *p __unused, int cmd, int scope, int policy, struct _iopol_param_t *iop_param) | |
1945 | { | |
0a7de745 A |
1946 | int error = 0; |
1947 | thread_t thread; | |
d9a64523 A |
1948 | |
1949 | /* Validate scope */ | |
1950 | switch (scope) { | |
0a7de745 A |
1951 | case IOPOL_SCOPE_THREAD: |
1952 | thread = current_thread(); | |
1953 | break; | |
1954 | case IOPOL_SCOPE_PROCESS: | |
1955 | thread = THREAD_NULL; | |
1956 | break; | |
1957 | default: | |
1958 | error = EINVAL; | |
1959 | goto out; | |
d9a64523 A |
1960 | } |
1961 | ||
1962 | /* Validate policy */ | |
1963 | if (cmd == IOPOL_CMD_SET) { | |
1964 | switch (policy) { | |
0a7de745 A |
1965 | case IOPOL_ATIME_UPDATES_DEFAULT: |
1966 | case IOPOL_ATIME_UPDATES_OFF: | |
1967 | break; | |
1968 | default: | |
1969 | error = EINVAL; | |
1970 | goto out; | |
d9a64523 A |
1971 | } |
1972 | } | |
1973 | ||
1974 | /* Perform command */ | |
0a7de745 A |
1975 | switch (cmd) { |
1976 | case IOPOL_CMD_SET: | |
1977 | if (thread != THREAD_NULL) { | |
1978 | set_thread_atime_policy(get_bsdthread_info(thread), policy); | |
1979 | } else { | |
1980 | set_task_atime_policy(p, policy); | |
1981 | } | |
1982 | break; | |
1983 | case IOPOL_CMD_GET: | |
1984 | if (thread != THREAD_NULL) { | |
1985 | policy = get_thread_atime_policy(get_bsdthread_info(thread)); | |
1986 | } else { | |
1987 | policy = get_task_atime_policy(p); | |
1988 | } | |
1989 | iop_param->iop_policy = policy; | |
1990 | break; | |
1991 | default: | |
1992 | error = EINVAL; /* unknown command */ | |
1993 | break; | |
d9a64523 A |
1994 | } |
1995 | ||
1996 | out: | |
0a7de745 | 1997 | return error; |
d9a64523 A |
1998 | } |
1999 | ||
cb323159 A |
2000 | static inline int |
2001 | get_thread_materialize_policy(struct uthread *ut) | |
2002 | { | |
2003 | if (ut->uu_flag & UT_NSPACE_NODATALESSFAULTS) { | |
2004 | return IOPOL_MATERIALIZE_DATALESS_FILES_OFF; | |
2005 | } else if (ut->uu_flag & UT_NSPACE_FORCEDATALESSFAULTS) { | |
2006 | return IOPOL_MATERIALIZE_DATALESS_FILES_ON; | |
2007 | } | |
2008 | /* Default thread behavior is "inherit process behavior". */ | |
2009 | return IOPOL_MATERIALIZE_DATALESS_FILES_DEFAULT; | |
2010 | } | |
2011 | ||
2012 | static inline void | |
2013 | set_thread_materialize_policy(struct uthread *ut, int policy) | |
2014 | { | |
2015 | if (policy == IOPOL_MATERIALIZE_DATALESS_FILES_OFF) { | |
2016 | ut->uu_flag &= ~UT_NSPACE_FORCEDATALESSFAULTS; | |
2017 | ut->uu_flag |= UT_NSPACE_NODATALESSFAULTS; | |
2018 | } else if (policy == IOPOL_MATERIALIZE_DATALESS_FILES_ON) { | |
2019 | ut->uu_flag &= ~UT_NSPACE_NODATALESSFAULTS; | |
2020 | ut->uu_flag |= UT_NSPACE_FORCEDATALESSFAULTS; | |
2021 | } else { | |
2022 | ut->uu_flag &= ~(UT_NSPACE_NODATALESSFAULTS | UT_NSPACE_FORCEDATALESSFAULTS); | |
2023 | } | |
2024 | } | |
2025 | ||
2026 | static inline void | |
2027 | set_proc_materialize_policy(struct proc *p, int policy) | |
2028 | { | |
2029 | if (policy == IOPOL_MATERIALIZE_DATALESS_FILES_DEFAULT) { | |
2030 | /* | |
2031 | * Caller has specified "use the default policy". | |
2032 | * The default policy is to NOT materialize dataless | |
2033 | * files. | |
2034 | */ | |
2035 | policy = IOPOL_MATERIALIZE_DATALESS_FILES_OFF; | |
2036 | } | |
2037 | if (policy == IOPOL_MATERIALIZE_DATALESS_FILES_ON) { | |
2038 | OSBitOrAtomic16((uint16_t)P_VFS_IOPOLICY_MATERIALIZE_DATALESS_FILES, &p->p_vfs_iopolicy); | |
2039 | } else { | |
2040 | OSBitAndAtomic16(~((uint16_t)P_VFS_IOPOLICY_MATERIALIZE_DATALESS_FILES), &p->p_vfs_iopolicy); | |
2041 | } | |
2042 | } | |
2043 | ||
2044 | static int | |
2045 | get_proc_materialize_policy(struct proc *p) | |
2046 | { | |
2047 | return (p->p_vfs_iopolicy & P_VFS_IOPOLICY_MATERIALIZE_DATALESS_FILES) ? IOPOL_MATERIALIZE_DATALESS_FILES_ON : IOPOL_MATERIALIZE_DATALESS_FILES_OFF; | |
2048 | } | |
2049 | ||
2050 | static int | |
2051 | iopolicysys_vfs_materialize_dataless_files(struct proc *p __unused, int cmd, int scope, int policy, struct _iopol_param_t *iop_param) | |
2052 | { | |
2053 | int error = 0; | |
2054 | thread_t thread; | |
2055 | ||
2056 | /* Validate scope */ | |
2057 | switch (scope) { | |
2058 | case IOPOL_SCOPE_THREAD: | |
2059 | thread = current_thread(); | |
2060 | break; | |
2061 | case IOPOL_SCOPE_PROCESS: | |
2062 | thread = THREAD_NULL; | |
2063 | break; | |
2064 | default: | |
2065 | error = EINVAL; | |
2066 | goto out; | |
2067 | } | |
2068 | ||
2069 | /* Validate policy */ | |
2070 | if (cmd == IOPOL_CMD_SET) { | |
2071 | switch (policy) { | |
2072 | case IOPOL_MATERIALIZE_DATALESS_FILES_DEFAULT: | |
2073 | case IOPOL_MATERIALIZE_DATALESS_FILES_OFF: | |
2074 | case IOPOL_MATERIALIZE_DATALESS_FILES_ON: | |
2075 | break; | |
2076 | default: | |
2077 | error = EINVAL; | |
2078 | goto out; | |
2079 | } | |
2080 | } | |
2081 | ||
2082 | /* Perform command */ | |
2083 | switch (cmd) { | |
2084 | case IOPOL_CMD_SET: | |
2085 | if (thread != THREAD_NULL) { | |
2086 | set_thread_materialize_policy(get_bsdthread_info(thread), policy); | |
2087 | } else { | |
2088 | set_proc_materialize_policy(p, policy); | |
2089 | } | |
2090 | break; | |
2091 | case IOPOL_CMD_GET: | |
2092 | if (thread != THREAD_NULL) { | |
2093 | policy = get_thread_materialize_policy(get_bsdthread_info(thread)); | |
2094 | } else { | |
2095 | policy = get_proc_materialize_policy(p); | |
2096 | } | |
2097 | iop_param->iop_policy = policy; | |
2098 | break; | |
2099 | default: | |
2100 | error = EINVAL; /* unknown command */ | |
2101 | break; | |
2102 | } | |
2103 | ||
2104 | out: | |
2105 | return error; | |
2106 | } | |
2107 | ||
2108 | static int | |
2109 | iopolicysys_vfs_statfs_no_data_volume(struct proc *p __unused, int cmd, | |
2110 | int scope, int policy, struct _iopol_param_t *iop_param) | |
2111 | { | |
2112 | int error = 0; | |
2113 | ||
2114 | /* Validate scope */ | |
2115 | switch (scope) { | |
2116 | case IOPOL_SCOPE_PROCESS: | |
2117 | /* Only process OK */ | |
2118 | break; | |
2119 | default: | |
2120 | error = EINVAL; | |
2121 | goto out; | |
2122 | } | |
2123 | ||
2124 | /* Validate policy */ | |
2125 | if (cmd == IOPOL_CMD_SET) { | |
2126 | switch (policy) { | |
2127 | case IOPOL_VFS_STATFS_NO_DATA_VOLUME_DEFAULT: | |
2128 | /* fall-through */ | |
2129 | case IOPOL_VFS_STATFS_FORCE_NO_DATA_VOLUME: | |
2130 | /* These policies are OK */ | |
2131 | break; | |
2132 | default: | |
2133 | error = EINVAL; | |
2134 | goto out; | |
2135 | } | |
2136 | } | |
2137 | ||
2138 | /* Perform command */ | |
2139 | switch (cmd) { | |
2140 | case IOPOL_CMD_SET: | |
2141 | if (0 == kauth_cred_issuser(kauth_cred_get())) { | |
2142 | /* If it's a non-root process, it needs to have the entitlement to set the policy */ | |
2143 | boolean_t entitled = FALSE; | |
2144 | entitled = IOTaskHasEntitlement(current_task(), "com.apple.private.iopol.case_sensitivity"); | |
2145 | if (!entitled) { | |
2146 | error = EPERM; | |
2147 | goto out; | |
2148 | } | |
2149 | } | |
2150 | ||
2151 | switch (policy) { | |
2152 | case IOPOL_VFS_STATFS_NO_DATA_VOLUME_DEFAULT: | |
2153 | OSBitAndAtomic16(~((uint32_t)P_VFS_IOPOLICY_STATFS_NO_DATA_VOLUME), &p->p_vfs_iopolicy); | |
2154 | break; | |
2155 | case IOPOL_VFS_STATFS_FORCE_NO_DATA_VOLUME: | |
2156 | OSBitOrAtomic16((uint32_t)P_VFS_IOPOLICY_STATFS_NO_DATA_VOLUME, &p->p_vfs_iopolicy); | |
2157 | break; | |
2158 | default: | |
2159 | error = EINVAL; | |
2160 | goto out; | |
2161 | } | |
2162 | ||
2163 | break; | |
2164 | case IOPOL_CMD_GET: | |
2165 | iop_param->iop_policy = (p->p_vfs_iopolicy & P_VFS_IOPOLICY_STATFS_NO_DATA_VOLUME) | |
2166 | ? IOPOL_VFS_STATFS_FORCE_NO_DATA_VOLUME | |
2167 | : IOPOL_VFS_STATFS_NO_DATA_VOLUME_DEFAULT; | |
2168 | break; | |
2169 | default: | |
2170 | error = EINVAL; /* unknown command */ | |
2171 | break; | |
2172 | } | |
2173 | ||
2174 | out: | |
2175 | return error; | |
2176 | } | |
2177 | ||
f427ee49 A |
2178 | static int |
2179 | iopolicysys_vfs_trigger_resolve(struct proc *p __unused, int cmd, | |
2180 | int scope, int policy, struct _iopol_param_t *iop_param) | |
2181 | { | |
2182 | int error = 0; | |
2183 | ||
2184 | /* Validate scope */ | |
2185 | switch (scope) { | |
2186 | case IOPOL_SCOPE_PROCESS: | |
2187 | /* Only process OK */ | |
2188 | break; | |
2189 | default: | |
2190 | error = EINVAL; | |
2191 | goto out; | |
2192 | } | |
2193 | ||
2194 | /* Validate policy */ | |
2195 | if (cmd == IOPOL_CMD_SET) { | |
2196 | switch (policy) { | |
2197 | case IOPOL_VFS_TRIGGER_RESOLVE_DEFAULT: | |
2198 | /* fall-through */ | |
2199 | case IOPOL_VFS_TRIGGER_RESOLVE_OFF: | |
2200 | /* These policies are OK */ | |
2201 | break; | |
2202 | default: | |
2203 | error = EINVAL; | |
2204 | goto out; | |
2205 | } | |
2206 | } | |
2207 | ||
2208 | /* Perform command */ | |
2209 | switch (cmd) { | |
2210 | case IOPOL_CMD_SET: | |
2211 | switch (policy) { | |
2212 | case IOPOL_VFS_TRIGGER_RESOLVE_DEFAULT: | |
2213 | OSBitAndAtomic16(~((uint32_t)P_VFS_IOPOLICY_TRIGGER_RESOLVE_DISABLE), &p->p_vfs_iopolicy); | |
2214 | break; | |
2215 | case IOPOL_VFS_TRIGGER_RESOLVE_OFF: | |
2216 | OSBitOrAtomic16((uint32_t)P_VFS_IOPOLICY_TRIGGER_RESOLVE_DISABLE, &p->p_vfs_iopolicy); | |
2217 | break; | |
2218 | default: | |
2219 | error = EINVAL; | |
2220 | goto out; | |
2221 | } | |
2222 | ||
2223 | break; | |
2224 | case IOPOL_CMD_GET: | |
2225 | iop_param->iop_policy = (p->p_vfs_iopolicy & P_VFS_IOPOLICY_TRIGGER_RESOLVE_DISABLE) | |
2226 | ? IOPOL_VFS_TRIGGER_RESOLVE_OFF | |
2227 | : IOPOL_VFS_TRIGGER_RESOLVE_DEFAULT; | |
2228 | break; | |
2229 | default: | |
2230 | error = EINVAL; /* unknown command */ | |
2231 | break; | |
2232 | } | |
2233 | ||
2234 | out: | |
2235 | return error; | |
2236 | } | |
2237 | ||
2238 | static int | |
2239 | iopolicysys_vfs_ignore_content_protection(struct proc *p, int cmd, int scope, | |
2240 | int policy, struct _iopol_param_t *iop_param) | |
2241 | { | |
2242 | int error = 0; | |
2243 | ||
2244 | /* Validate scope */ | |
2245 | switch (scope) { | |
2246 | case IOPOL_SCOPE_PROCESS: | |
2247 | /* Only process OK */ | |
2248 | break; | |
2249 | default: | |
2250 | error = EINVAL; | |
2251 | goto out; | |
2252 | } | |
2253 | ||
2254 | /* Validate policy */ | |
2255 | if (cmd == IOPOL_CMD_SET) { | |
2256 | switch (policy) { | |
2257 | case IOPOL_VFS_CONTENT_PROTECTION_DEFAULT: | |
2258 | OS_FALLTHROUGH; | |
2259 | case IOPOL_VFS_CONTENT_PROTECTION_IGNORE: | |
2260 | /* These policies are OK */ | |
2261 | break; | |
2262 | default: | |
2263 | error = EINVAL; | |
2264 | goto out; | |
2265 | } | |
2266 | } | |
2267 | ||
2268 | /* Perform command */ | |
2269 | switch (cmd) { | |
2270 | case IOPOL_CMD_SET: | |
2271 | if (0 == kauth_cred_issuser(kauth_cred_get())) { | |
2272 | /* If it's a non-root process, it needs to have the entitlement to set the policy */ | |
2273 | boolean_t entitled = FALSE; | |
2274 | entitled = IOTaskHasEntitlement(current_task(), "com.apple.private.iopol.case_sensitivity"); | |
2275 | if (!entitled) { | |
2276 | error = EPERM; | |
2277 | goto out; | |
2278 | } | |
2279 | } | |
2280 | ||
2281 | switch (policy) { | |
2282 | case IOPOL_VFS_CONTENT_PROTECTION_DEFAULT: | |
2283 | os_atomic_andnot(&p->p_vfs_iopolicy, P_VFS_IOPOLICY_IGNORE_CONTENT_PROTECTION, relaxed); | |
2284 | break; | |
2285 | case IOPOL_VFS_CONTENT_PROTECTION_IGNORE: | |
2286 | os_atomic_or(&p->p_vfs_iopolicy, P_VFS_IOPOLICY_IGNORE_CONTENT_PROTECTION, relaxed); | |
2287 | break; | |
2288 | default: | |
2289 | error = EINVAL; | |
2290 | goto out; | |
2291 | } | |
2292 | ||
2293 | break; | |
2294 | case IOPOL_CMD_GET: | |
2295 | iop_param->iop_policy = (os_atomic_load(&p->p_vfs_iopolicy, relaxed) & P_VFS_IOPOLICY_IGNORE_CONTENT_PROTECTION) | |
2296 | ? IOPOL_VFS_CONTENT_PROTECTION_IGNORE | |
2297 | : IOPOL_VFS_CONTENT_PROTECTION_DEFAULT; | |
2298 | break; | |
2299 | default: | |
2300 | error = EINVAL; /* unknown command */ | |
2301 | break; | |
2302 | } | |
2303 | ||
2304 | out: | |
2305 | return error; | |
2306 | } | |
2307 | ||
c3c9b80d A |
2308 | #define AUTHORIZED_ACCESS_ENTITLEMENT \ |
2309 | "com.apple.private.vfs.authorized-access" | |
2310 | int | |
2311 | iopolicysys_vfs_ignore_node_permissions(struct proc *p, int cmd, int scope, | |
2312 | int policy, __unused struct _iopol_param_t *iop_param) | |
2313 | { | |
2314 | int error = EINVAL; | |
2315 | ||
2316 | switch (scope) { | |
2317 | case IOPOL_SCOPE_PROCESS: | |
2318 | break; | |
2319 | default: | |
2320 | goto out; | |
2321 | } | |
2322 | ||
2323 | switch (cmd) { | |
2324 | case IOPOL_CMD_GET: | |
2325 | policy = os_atomic_load(&p->p_vfs_iopolicy, relaxed) & P_VFS_IOPOLICY_IGNORE_NODE_PERMISSIONS ? | |
2326 | IOPOL_VFS_IGNORE_PERMISSIONS_ON : IOPOL_VFS_IGNORE_PERMISSIONS_OFF; | |
2327 | iop_param->iop_policy = policy; | |
2328 | goto out_ok; | |
2329 | case IOPOL_CMD_SET: | |
2330 | /* SET is handled after the switch */ | |
2331 | break; | |
2332 | default: | |
2333 | goto out; | |
2334 | } | |
2335 | ||
2336 | if (!IOTaskHasEntitlement(current_task(), AUTHORIZED_ACCESS_ENTITLEMENT)) { | |
2337 | error = EPERM; | |
2338 | goto out; | |
2339 | } | |
2340 | ||
2341 | switch (policy) { | |
2342 | case IOPOL_VFS_IGNORE_PERMISSIONS_OFF: | |
2343 | os_atomic_andnot(&p->p_vfs_iopolicy, P_VFS_IOPOLICY_IGNORE_NODE_PERMISSIONS, relaxed); | |
2344 | break; | |
2345 | case IOPOL_VFS_IGNORE_PERMISSIONS_ON: | |
2346 | os_atomic_or(&p->p_vfs_iopolicy, P_VFS_IOPOLICY_IGNORE_NODE_PERMISSIONS, relaxed); | |
2347 | break; | |
2348 | default: | |
2349 | break; | |
2350 | } | |
2351 | ||
2352 | out_ok: | |
2353 | error = 0; | |
2354 | out: | |
2355 | return error; | |
2356 | } | |
2357 | ||
2358 | #define SKIP_MTIME_UPDATE_ENTITLEMENT \ | |
2359 | "com.apple.private.vfs.skip-mtime-updates" | |
2360 | int | |
2361 | iopolicysys_vfs_skip_mtime_update(struct proc *p, int cmd, int scope, | |
2362 | int policy, __unused struct _iopol_param_t *iop_param) | |
2363 | { | |
2364 | int error = EINVAL; | |
2365 | ||
2366 | switch (scope) { | |
2367 | case IOPOL_SCOPE_PROCESS: | |
2368 | break; | |
2369 | default: | |
2370 | goto out; | |
2371 | } | |
2372 | ||
2373 | switch (cmd) { | |
2374 | case IOPOL_CMD_GET: | |
2375 | policy = os_atomic_load(&p->p_vfs_iopolicy, relaxed) & P_VFS_IOPOLICY_SKIP_MTIME_UPDATE ? | |
2376 | IOPOL_VFS_SKIP_MTIME_UPDATE_ON : IOPOL_VFS_SKIP_MTIME_UPDATE_OFF; | |
2377 | iop_param->iop_policy = policy; | |
2378 | goto out_ok; | |
2379 | case IOPOL_CMD_SET: | |
2380 | break; | |
2381 | default: | |
2382 | break; | |
2383 | } | |
2384 | ||
2385 | if (!IOTaskHasEntitlement(current_task(), SKIP_MTIME_UPDATE_ENTITLEMENT)) { | |
2386 | error = EPERM; | |
2387 | goto out; | |
2388 | } | |
2389 | ||
2390 | switch (policy) { | |
2391 | case IOPOL_VFS_SKIP_MTIME_UPDATE_OFF: | |
2392 | os_atomic_andnot(&p->p_vfs_iopolicy, P_VFS_IOPOLICY_SKIP_MTIME_UPDATE, relaxed); | |
2393 | break; | |
2394 | case IOPOL_VFS_SKIP_MTIME_UPDATE_ON: | |
2395 | os_atomic_or(&p->p_vfs_iopolicy, P_VFS_IOPOLICY_SKIP_MTIME_UPDATE, relaxed); | |
2396 | break; | |
2397 | default: | |
2398 | break; | |
2399 | } | |
2400 | ||
2401 | out_ok: | |
2402 | error = 0; | |
2403 | out: | |
2404 | return error; | |
2405 | } | |
39037602 | 2406 | /* BSD call back function for task_policy networking changes */ |
6d2010ae | 2407 | void |
fe8ab488 | 2408 | proc_apply_task_networkbg(void * bsd_info, thread_t thread) |
6d2010ae | 2409 | { |
fe8ab488 A |
2410 | assert(bsd_info != PROC_NULL); |
2411 | ||
2412 | pid_t pid = proc_pid((proc_t)bsd_info); | |
2413 | ||
2414 | proc_t p = proc_find(pid); | |
6d2010ae | 2415 | |
6d2010ae | 2416 | if (p != PROC_NULL) { |
fe8ab488 A |
2417 | assert(p == (proc_t)bsd_info); |
2418 | ||
2419 | do_background_socket(p, thread); | |
6d2010ae A |
2420 | proc_rele(p); |
2421 | } | |
2422 | } | |
2423 | ||
2424 | void | |
fe8ab488 | 2425 | gather_rusage_info(proc_t p, rusage_info_current *ru, int flavor) |
6d2010ae | 2426 | { |
39236c6e | 2427 | struct rusage_info_child *ri_child; |
6d2010ae | 2428 | |
39236c6e | 2429 | assert(p->p_stats != NULL); |
5ba3f43e | 2430 | memset(ru, 0, sizeof(*ru)); |
0a7de745 | 2431 | switch (flavor) { |
f427ee49 A |
2432 | case RUSAGE_INFO_V5: |
2433 | #if !XNU_TARGET_OS_OSX && __has_feature(ptrauth_calls) | |
2434 | if (vm_shared_region_is_reslide(p->task)) { | |
2435 | ru->ri_flags |= RU_PROC_RUNS_RESLIDE; | |
2436 | } | |
2437 | #endif /* !XNU_TARGET_OS_OSX && __has_feature(ptrauth_calls) */ | |
2438 | OS_FALLTHROUGH; | |
5ba3f43e | 2439 | case RUSAGE_INFO_V4: |
cb323159 | 2440 | ru->ri_logical_writes = get_task_logical_writes(p->task, FALSE); |
5ba3f43e | 2441 | ru->ri_lifetime_max_phys_footprint = get_task_phys_footprint_lifetime_max(p->task); |
d9a64523 A |
2442 | #if CONFIG_LEDGER_INTERVAL_MAX |
2443 | ru->ri_interval_max_phys_footprint = get_task_phys_footprint_interval_max(p->task, FALSE); | |
2444 | #endif | |
5ba3f43e | 2445 | fill_task_monotonic_rusage(p->task, ru); |
f427ee49 | 2446 | OS_FALLTHROUGH; |
fe8ab488 A |
2447 | |
2448 | case RUSAGE_INFO_V3: | |
2449 | fill_task_qos_rusage(p->task, ru); | |
2450 | fill_task_billed_usage(p->task, ru); | |
f427ee49 | 2451 | OS_FALLTHROUGH; |
fe8ab488 | 2452 | |
39236c6e | 2453 | case RUSAGE_INFO_V2: |
fe8ab488 | 2454 | fill_task_io_rusage(p->task, ru); |
f427ee49 | 2455 | OS_FALLTHROUGH; |
39236c6e A |
2456 | |
2457 | case RUSAGE_INFO_V1: | |
2458 | /* | |
2459 | * p->p_stats->ri_child statistics are protected under proc lock. | |
2460 | */ | |
2461 | proc_lock(p); | |
0a7de745 | 2462 | |
39236c6e A |
2463 | ri_child = &(p->p_stats->ri_child); |
2464 | ru->ri_child_user_time = ri_child->ri_child_user_time; | |
2465 | ru->ri_child_system_time = ri_child->ri_child_system_time; | |
2466 | ru->ri_child_pkg_idle_wkups = ri_child->ri_child_pkg_idle_wkups; | |
2467 | ru->ri_child_interrupt_wkups = ri_child->ri_child_interrupt_wkups; | |
2468 | ru->ri_child_pageins = ri_child->ri_child_pageins; | |
2469 | ru->ri_child_elapsed_abstime = ri_child->ri_child_elapsed_abstime; | |
2470 | ||
2471 | proc_unlock(p); | |
f427ee49 | 2472 | OS_FALLTHROUGH; |
39236c6e A |
2473 | |
2474 | case RUSAGE_INFO_V0: | |
0a7de745 | 2475 | proc_getexecutableuuid(p, (unsigned char *)&ru->ri_uuid, sizeof(ru->ri_uuid)); |
fe8ab488 | 2476 | fill_task_rusage(p->task, ru); |
39236c6e | 2477 | ru->ri_proc_start_abstime = p->p_stats->ps_start; |
6d2010ae | 2478 | } |
39236c6e | 2479 | } |
6d2010ae | 2480 | |
39236c6e A |
2481 | int |
2482 | proc_get_rusage(proc_t p, int flavor, user_addr_t buffer, __unused int is_zombie) | |
6d2010ae | 2483 | { |
d9a64523 | 2484 | rusage_info_current ri_current = {}; |
39236c6e A |
2485 | |
2486 | int error = 0; | |
5ba3f43e | 2487 | size_t size = 0; |
39236c6e A |
2488 | |
2489 | switch (flavor) { | |
2490 | case RUSAGE_INFO_V0: | |
5ba3f43e | 2491 | size = sizeof(struct rusage_info_v0); |
39236c6e A |
2492 | break; |
2493 | ||
2494 | case RUSAGE_INFO_V1: | |
5ba3f43e | 2495 | size = sizeof(struct rusage_info_v1); |
39236c6e A |
2496 | break; |
2497 | ||
2498 | case RUSAGE_INFO_V2: | |
5ba3f43e | 2499 | size = sizeof(struct rusage_info_v2); |
39236c6e A |
2500 | break; |
2501 | ||
fe8ab488 | 2502 | case RUSAGE_INFO_V3: |
5ba3f43e | 2503 | size = sizeof(struct rusage_info_v3); |
fe8ab488 A |
2504 | break; |
2505 | ||
5ba3f43e A |
2506 | case RUSAGE_INFO_V4: |
2507 | size = sizeof(struct rusage_info_v4); | |
39236c6e | 2508 | break; |
5ba3f43e | 2509 | |
f427ee49 A |
2510 | case RUSAGE_INFO_V5: |
2511 | size = sizeof(struct rusage_info_v5); | |
2512 | break; | |
5ba3f43e A |
2513 | default: |
2514 | return EINVAL; | |
2515 | } | |
2516 | ||
0a7de745 | 2517 | if (size == 0) { |
5ba3f43e | 2518 | return EINVAL; |
316670eb | 2519 | } |
39236c6e | 2520 | |
0a7de745 | 2521 | /* |
5ba3f43e A |
2522 | * If task is still alive, collect info from the live task itself. |
2523 | * Otherwise, look to the cached info in the zombie proc. | |
2524 | */ | |
2525 | if (p->p_ru == NULL) { | |
2526 | gather_rusage_info(p, &ri_current, flavor); | |
2527 | ri_current.ri_proc_exit_abstime = 0; | |
2528 | error = copyout(&ri_current, buffer, size); | |
2529 | } else { | |
2530 | ri_current = p->p_ru->ri; | |
2531 | error = copyout(&p->p_ru->ri, buffer, size); | |
2532 | } | |
2533 | ||
0a7de745 | 2534 | return error; |
316670eb | 2535 | } |
39236c6e A |
2536 | |
2537 | static int | |
2538 | mach_to_bsd_rv(int mach_rv) | |
316670eb | 2539 | { |
39236c6e A |
2540 | int bsd_rv = 0; |
2541 | ||
2542 | switch (mach_rv) { | |
2543 | case KERN_SUCCESS: | |
2544 | bsd_rv = 0; | |
2545 | break; | |
2546 | case KERN_INVALID_ARGUMENT: | |
2547 | bsd_rv = EINVAL; | |
2548 | break; | |
2549 | default: | |
2550 | panic("unknown error %#x", mach_rv); | |
6d2010ae | 2551 | } |
39236c6e A |
2552 | |
2553 | return bsd_rv; | |
6d2010ae A |
2554 | } |
2555 | ||
39236c6e A |
2556 | /* |
2557 | * Resource limit controls | |
2558 | * | |
2559 | * uap->flavor available flavors: | |
2560 | * | |
2561 | * RLIMIT_WAKEUPS_MONITOR | |
d9a64523 A |
2562 | * RLIMIT_CPU_USAGE_MONITOR |
2563 | * RLIMIT_THREAD_CPULIMITS | |
2564 | * RLIMIT_FOOTPRINT_INTERVAL | |
39236c6e A |
2565 | */ |
2566 | int | |
fe8ab488 | 2567 | proc_rlimit_control(__unused struct proc *p, struct proc_rlimit_control_args *uap, __unused int32_t *retval) |
39236c6e | 2568 | { |
0a7de745 A |
2569 | proc_t targetp; |
2570 | int error = 0; | |
2571 | struct proc_rlimit_control_wakeupmon wakeupmon_args; | |
39236c6e | 2572 | uint32_t cpumon_flags; |
fe8ab488 | 2573 | uint32_t cpulimits_flags; |
39236c6e | 2574 | kauth_cred_t my_cred, target_cred; |
d9a64523 | 2575 | #if CONFIG_LEDGER_INTERVAL_MAX |
0a7de745 | 2576 | uint32_t footprint_interval_flags; |
d9a64523 A |
2577 | uint64_t interval_max_footprint; |
2578 | #endif /* CONFIG_LEDGER_INTERVAL_MAX */ | |
39236c6e | 2579 | |
fe8ab488 A |
2580 | /* -1 implicitly means our own process (perhaps even the current thread for per-thread attributes) */ |
2581 | if (uap->pid == -1) { | |
2582 | targetp = proc_self(); | |
2583 | } else { | |
2584 | targetp = proc_find(uap->pid); | |
2585 | } | |
39236c6e | 2586 | |
fe8ab488 A |
2587 | /* proc_self() can return NULL for an exiting process */ |
2588 | if (targetp == PROC_NULL) { | |
0a7de745 | 2589 | return ESRCH; |
39236c6e A |
2590 | } |
2591 | ||
2592 | my_cred = kauth_cred_get(); | |
2593 | target_cred = kauth_cred_proc_ref(targetp); | |
2594 | ||
2595 | if (!kauth_cred_issuser(my_cred) && kauth_cred_getruid(my_cred) && | |
2596 | kauth_cred_getuid(my_cred) != kauth_cred_getuid(target_cred) && | |
2597 | kauth_cred_getruid(my_cred) != kauth_cred_getuid(target_cred)) { | |
2598 | proc_rele(targetp); | |
2599 | kauth_cred_unref(&target_cred); | |
0a7de745 | 2600 | return EACCES; |
39236c6e A |
2601 | } |
2602 | ||
2603 | switch (uap->flavor) { | |
2604 | case RLIMIT_WAKEUPS_MONITOR: | |
0a7de745 | 2605 | if ((error = copyin(uap->arg, &wakeupmon_args, sizeof(wakeupmon_args))) != 0) { |
39236c6e A |
2606 | break; |
2607 | } | |
2608 | if ((error = mach_to_bsd_rv(task_wakeups_monitor_ctl(targetp->task, &wakeupmon_args.wm_flags, | |
0a7de745 | 2609 | &wakeupmon_args.wm_rate))) != 0) { |
39236c6e A |
2610 | break; |
2611 | } | |
0a7de745 | 2612 | error = copyout(&wakeupmon_args, uap->arg, sizeof(wakeupmon_args)); |
39236c6e A |
2613 | break; |
2614 | case RLIMIT_CPU_USAGE_MONITOR: | |
f427ee49 | 2615 | cpumon_flags = (uint32_t)uap->arg; // XXX temporarily stashing flags in argp (12592127) |
39236c6e A |
2616 | error = mach_to_bsd_rv(task_cpu_usage_monitor_ctl(targetp->task, &cpumon_flags)); |
2617 | break; | |
fe8ab488 A |
2618 | case RLIMIT_THREAD_CPULIMITS: |
2619 | cpulimits_flags = (uint32_t)uap->arg; // only need a limited set of bits, pass in void * argument | |
2620 | ||
2621 | if (uap->pid != -1) { | |
2622 | error = EINVAL; | |
2623 | break; | |
2624 | } | |
2625 | ||
2626 | uint8_t percent = 0; | |
2627 | uint32_t ms_refill = 0; | |
2628 | uint64_t ns_refill; | |
2629 | ||
0a7de745 A |
2630 | percent = (uint8_t)(cpulimits_flags & 0xffU); /* low 8 bits for percent */ |
2631 | ms_refill = (cpulimits_flags >> 8) & 0xffffff; /* next 24 bits represent ms refill value */ | |
fe8ab488 A |
2632 | if (percent >= 100) { |
2633 | error = EINVAL; | |
2634 | break; | |
2635 | } | |
2636 | ||
2637 | ns_refill = ((uint64_t)ms_refill) * NSEC_PER_MSEC; | |
2638 | ||
2639 | error = mach_to_bsd_rv(thread_set_cpulimit(THREAD_CPULIMIT_BLOCK, percent, ns_refill)); | |
2640 | break; | |
d9a64523 A |
2641 | |
2642 | #if CONFIG_LEDGER_INTERVAL_MAX | |
2643 | case RLIMIT_FOOTPRINT_INTERVAL: | |
f427ee49 | 2644 | footprint_interval_flags = (uint32_t)uap->arg; // XXX temporarily stashing flags in argp (12592127) |
d9a64523 A |
2645 | /* |
2646 | * There is currently only one option for this flavor. | |
2647 | */ | |
2648 | if ((footprint_interval_flags & FOOTPRINT_INTERVAL_RESET) == 0) { | |
2649 | error = EINVAL; | |
2650 | break; | |
2651 | } | |
2652 | interval_max_footprint = get_task_phys_footprint_interval_max(targetp->task, TRUE); | |
2653 | break; | |
2654 | #endif /* CONFIG_LEDGER_INTERVAL_MAX */ | |
39236c6e A |
2655 | default: |
2656 | error = EINVAL; | |
2657 | break; | |
2658 | } | |
2659 | ||
2660 | proc_rele(targetp); | |
2661 | kauth_cred_unref(&target_cred); | |
2662 | ||
39236c6e A |
2663 | /* |
2664 | * Return value from this function becomes errno to userland caller. | |
39236c6e | 2665 | */ |
0a7de745 | 2666 | return error; |
39236c6e | 2667 | } |
fe8ab488 A |
2668 | |
2669 | /* | |
2670 | * Return the current amount of CPU consumed by this thread (in either user or kernel mode) | |
2671 | */ | |
0a7de745 A |
2672 | int |
2673 | thread_selfusage(struct proc *p __unused, struct thread_selfusage_args *uap __unused, uint64_t *retval) | |
fe8ab488 A |
2674 | { |
2675 | uint64_t runtime; | |
2676 | ||
2677 | runtime = thread_get_runtime_self(); | |
2678 | *retval = runtime; | |
2679 | ||
0a7de745 | 2680 | return 0; |
fe8ab488 | 2681 | } |
5ba3f43e A |
2682 | |
2683 | #if !MONOTONIC | |
0a7de745 A |
2684 | int |
2685 | thread_selfcounts(__unused struct proc *p, __unused struct thread_selfcounts_args *uap, __unused int *ret_out) | |
5ba3f43e A |
2686 | { |
2687 | return ENOTSUP; | |
2688 | } | |
2689 | #endif /* !MONOTONIC */ |