]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/sys_coalition.c
xnu-6153.41.3.tar.gz
[apple/xnu.git] / bsd / kern / sys_coalition.c
CommitLineData
fe8ab488 1#include <kern/kern_types.h>
5ba3f43e 2#include <kern/thread_group.h>
fe8ab488
A
3#include <mach/mach_types.h>
4#include <mach/boolean.h>
5
6#include <kern/coalition.h>
7
8#include <sys/coalition.h>
9#include <sys/errno.h>
cb323159 10#include <sys/kauth.h>
fe8ab488
A
11#include <sys/kernel.h>
12#include <sys/sysproto.h>
13#include <sys/systm.h>
14
15/* Coalitions syscalls */
16
17/*
18 * Create a new, empty coalition and return its ID.
19 *
20 * Returns:
21 * EINVAL Flags parameter was invalid
22 * ENOMEM Unable to allocate kernel resources for a new coalition
23 * EFAULT cidp parameter pointed to invalid memory.
24 *
25 * Returns with reference held for userspace caller.
26 */
27static
28int
29coalition_create_syscall(user_addr_t cidp, uint32_t flags)
30{
31 int error = 0;
32 kern_return_t kr;
33 uint64_t cid;
34 coalition_t coal;
3e170ce0 35 int type = COALITION_CREATE_FLAGS_GET_TYPE(flags);
5ba3f43e 36 int role = COALITION_CREATE_FLAGS_GET_ROLE(flags);
3e170ce0 37 boolean_t privileged = !!(flags & COALITION_CREATE_FLAGS_PRIVILEGED);
fe8ab488 38
0a7de745 39 if ((flags & (~COALITION_CREATE_FLAGS_MASK)) != 0) {
3e170ce0 40 return EINVAL;
0a7de745
A
41 }
42 if (type < 0 || type > COALITION_TYPE_MAX) {
fe8ab488 43 return EINVAL;
0a7de745 44 }
fe8ab488 45
5ba3f43e 46 kr = coalition_create_internal(type, role, privileged, &coal);
fe8ab488
A
47 if (kr != KERN_SUCCESS) {
48 /* for now, the only kr is KERN_RESOURCE_SHORTAGE */
49 error = ENOMEM;
50 goto out;
51 }
52
53 cid = coalition_id(coal);
54
3e170ce0 55 coal_dbg("(addr, %u) -> %llu", flags, cid);
fe8ab488
A
56 error = copyout(&cid, cidp, sizeof(cid));
57out:
58 return error;
59}
60
61/*
62 * Request to terminate the coalition identified by ID.
63 * Attempts to spawn into this coalition using the posix_spawnattr will begin
64 * failing. Processes already within the coalition may still fork.
65 * Arms the 'coalition is empty' notification when the coalition's active
66 * count reaches zero.
67 *
68 * Returns:
69 * ESRCH No coalition with that ID could be found.
70 * EALREADY The coalition with that ID has already been terminated.
71 * EFAULT cidp parameter pointed to invalid memory.
72 * EPERM Caller doesn't have permission to terminate that coalition.
73 */
74static
75int
76coalition_request_terminate_syscall(user_addr_t cidp, uint32_t flags)
77{
78 kern_return_t kr;
79 int error = 0;
80 uint64_t cid;
81 coalition_t coal;
82
83 if (flags != 0) {
84 return EINVAL;
85 }
86
87 error = copyin(cidp, &cid, sizeof(cid));
88 if (error) {
89 return error;
90 }
91
92 coal = coalition_find_by_id(cid);
93 if (coal == COALITION_NULL) {
94 return ESRCH;
95 }
96
97 kr = coalition_request_terminate_internal(coal);
98 coalition_release(coal);
99
100 switch (kr) {
101 case KERN_SUCCESS:
102 break;
103 case KERN_DEFAULT_SET:
104 error = EPERM;
3e170ce0 105 break;
fe8ab488
A
106 case KERN_TERMINATED:
107 error = EALREADY;
3e170ce0 108 break;
fe8ab488
A
109 case KERN_INVALID_NAME:
110 error = ESRCH;
3e170ce0 111 break;
fe8ab488
A
112 default:
113 error = EIO;
3e170ce0 114 break;
fe8ab488
A
115 }
116
3e170ce0 117 coal_dbg("(%llu, %u) -> %d", cid, flags, error);
fe8ab488
A
118
119 return error;
120}
121
122/*
123 * Request the kernel to deallocate the coalition identified by ID, which
124 * must be both terminated and empty. This balances the reference taken
125 * in coalition_create.
5ba3f43e 126 * The memory containing the coalition object may not be freed just yet, if
fe8ab488
A
127 * other kernel operations still hold references to it.
128 *
129 * Returns:
130 * EINVAL Flags parameter was invalid
131 * ESRCH Coalition ID refers to a coalition that doesn't exist.
132 * EBUSY Coalition has not yet been terminated.
133 * EBUSY Coalition is still active.
134 * EFAULT cidp parameter pointed to invalid memory.
135 * EPERM Caller doesn't have permission to terminate that coalition.
136 * Consumes one reference, "held" by caller since coalition_create
137 */
138static
139int
140coalition_reap_syscall(user_addr_t cidp, uint32_t flags)
141{
142 kern_return_t kr;
143 int error = 0;
144 uint64_t cid;
145 coalition_t coal;
146
147 if (flags != 0) {
148 return EINVAL;
149 }
150
151 error = copyin(cidp, &cid, sizeof(cid));
152 if (error) {
153 return error;
154 }
155
156 coal = coalition_find_by_id(cid);
157 if (coal == COALITION_NULL) {
158 return ESRCH;
159 }
160
161 kr = coalition_reap_internal(coal);
162 coalition_release(coal);
163
164 switch (kr) {
165 case KERN_SUCCESS:
166 break;
167 case KERN_DEFAULT_SET:
168 error = EPERM;
3e170ce0 169 break;
fe8ab488
A
170 case KERN_TERMINATED:
171 error = ESRCH;
3e170ce0 172 break;
fe8ab488
A
173 case KERN_FAILURE:
174 error = EBUSY;
3e170ce0 175 break;
fe8ab488
A
176 default:
177 error = EIO;
3e170ce0 178 break;
fe8ab488
A
179 }
180
3e170ce0 181 coal_dbg("(%llu, %u) -> %d", cid, flags, error);
fe8ab488
A
182
183 return error;
184}
185
186/* Syscall demux.
187 * Returns EPERM if the calling process is not privileged to make this call.
188 */
0a7de745
A
189int
190coalition(proc_t p, struct coalition_args *cap, __unused int32_t *retval)
fe8ab488
A
191{
192 uint32_t operation = cap->operation;
193 user_addr_t cidp = cap->cid;
194 uint32_t flags = cap->flags;
195 int error = 0;
3e170ce0 196 int type = COALITION_CREATE_FLAGS_GET_TYPE(flags);
fe8ab488 197
3e170ce0 198 if (!task_is_in_privileged_coalition(p->task, type)) {
fe8ab488
A
199 return EPERM;
200 }
201
202 switch (operation) {
203 case COALITION_OP_CREATE:
204 error = coalition_create_syscall(cidp, flags);
205 break;
206 case COALITION_OP_REAP:
207 error = coalition_reap_syscall(cidp, flags);
208 break;
209 case COALITION_OP_TERMINATE:
210 error = coalition_request_terminate_syscall(cidp, flags);
211 break;
212 default:
213 error = ENOSYS;
214 }
215 return error;
216}
217
218/* This is a temporary interface, likely to be changed by 15385642. */
219static int __attribute__ ((noinline))
220coalition_info_resource_usage(coalition_t coal, user_addr_t buffer, user_size_t bufsize)
221{
222 kern_return_t kr;
527f9951 223 struct coalition_resource_usage cru = {};
fe8ab488 224
fe8ab488
A
225 kr = coalition_resource_usage_internal(coal, &cru);
226
227 switch (kr) {
228 case KERN_INVALID_ARGUMENT:
229 return EINVAL;
230 case KERN_RESOURCE_SHORTAGE:
231 return ENOMEM;
232 case KERN_SUCCESS:
233 break;
234 default:
235 return EIO; /* shrug */
236 }
237
4bd07ac2 238 return copyout(&cru, buffer, MIN(bufsize, sizeof(cru)));
fe8ab488
A
239}
240
5ba3f43e
A
241#define coalition_info_set_name_internal(...) 0
242
243static int
244coalition_info_efficiency(coalition_t coal, user_addr_t buffer, user_size_t bufsize)
245{
246 int error = 0;
0a7de745 247 if (coalition_type(coal) != COALITION_TYPE_JETSAM) {
5ba3f43e 248 return EINVAL;
0a7de745
A
249 }
250 uint64_t flags = 0;
5ba3f43e 251 error = copyin(buffer, &flags, MIN(bufsize, sizeof(flags)));
0a7de745 252 if (error) {
5ba3f43e 253 return error;
0a7de745
A
254 }
255 if ((flags & COALITION_EFFICIENCY_VALID_FLAGS) == 0) {
5ba3f43e 256 return EINVAL;
0a7de745 257 }
5ba3f43e 258 if (flags & COALITION_FLAGS_EFFICIENT) {
0a7de745 259 coalition_set_efficient(coal);
5ba3f43e
A
260 }
261 return error;
262}
263
cb323159
A
264static int
265coalition_ledger_logical_writes_limit(coalition_t coal, user_addr_t buffer, user_size_t bufsize)
266{
267 int error = 0;
268 int64_t limit = 0;
269
270 if (coalition_type(coal) != COALITION_TYPE_RESOURCE) {
271 error = EINVAL;
272 goto out;
273 }
274 error = copyin(buffer, &limit, MIN(bufsize, sizeof(limit)));
275 if (error) {
276 goto out;
277 }
278
279
280 error = coalition_ledger_set_logical_writes_limit(coal, limit);
281out:
282 return error;
283}
284
0a7de745
A
285int
286coalition_info(proc_t p, struct coalition_info_args *uap, __unused int32_t *retval)
fe8ab488
A
287{
288 user_addr_t cidp = uap->cid;
289 user_addr_t buffer = uap->buffer;
290 user_addr_t bufsizep = uap->bufsize;
291 user_size_t bufsize;
292 uint32_t flavor = uap->flavor;
293 int error;
294 uint64_t cid;
295 coalition_t coal;
296
297 error = copyin(cidp, &cid, sizeof(cid));
298 if (error) {
299 return error;
300 }
301
302 coal = coalition_find_by_id(cid);
303 if (coal == COALITION_NULL) {
304 return ESRCH;
305 }
306 /* TODO: priv check? EPERM or ESRCH? */
307
308 if (IS_64BIT_PROCESS(p)) {
309 user64_size_t size64;
310 error = copyin(bufsizep, &size64, sizeof(size64));
311 bufsize = (user_size_t)size64;
312 } else {
313 user32_size_t size32;
314 error = copyin(bufsizep, &size32, sizeof(size32));
315 bufsize = (user_size_t)size32;
316 }
317 if (error) {
318 goto bad;
319 }
320
321 switch (flavor) {
322 case COALITION_INFO_RESOURCE_USAGE:
323 error = coalition_info_resource_usage(coal, buffer, bufsize);
324 break;
5ba3f43e
A
325 case COALITION_INFO_SET_NAME:
326 error = coalition_info_set_name_internal(coal, buffer, bufsize);
327 break;
328 case COALITION_INFO_SET_EFFICIENCY:
329 error = coalition_info_efficiency(coal, buffer, bufsize);
330 break;
fe8ab488
A
331 default:
332 error = EINVAL;
333 }
334
335bad:
336 coalition_release(coal);
337 return error;
338}
3e170ce0 339
cb323159
A
340int
341coalition_ledger(__unused proc_t p, __unused struct coalition_ledger_args *uap, __unused int32_t *retval)
342{
343 user_addr_t cidp = uap->cid;
344 user_addr_t buffer = uap->buffer;
345 user_addr_t bufsizep = uap->bufsize;
346 user_size_t bufsize;
347 uint32_t operation = uap->operation;
348 int error;
349 uint64_t cid;
350 coalition_t coal = COALITION_NULL;
351
352 if (!kauth_cred_issuser(kauth_cred_get())) {
353 error = EPERM;
354 goto out;
355 }
356
357 error = copyin(cidp, &cid, sizeof(cid));
358 if (error) {
359 goto out;
360 }
361
362 coal = coalition_find_by_id(cid);
363 if (coal == COALITION_NULL) {
364 error = ESRCH;
365 goto out;
366 }
367
368 if (IS_64BIT_PROCESS(p)) {
369 user64_size_t size64;
370 error = copyin(bufsizep, &size64, sizeof(size64));
371 bufsize = (user_size_t)size64;
372 } else {
373 user32_size_t size32;
374 error = copyin(bufsizep, &size32, sizeof(size32));
375 bufsize = (user_size_t)size32;
376 }
377 if (error) {
378 goto out;
379 }
380
381 switch (operation) {
382 case COALITION_LEDGER_SET_LOGICAL_WRITES_LIMIT:
383 error = coalition_ledger_logical_writes_limit(coal, buffer, bufsize);
384 break;
385 default:
386 error = EINVAL;
387 }
388out:
389 if (coal != COALITION_NULL) {
390 coalition_release(coal);
391 }
392 return error;
393}
5ba3f43e 394#if DEVELOPMENT || DEBUG
3e170ce0
A
395static int sysctl_coalition_get_ids SYSCTL_HANDLER_ARGS
396{
397#pragma unused(oidp, arg1, arg2)
398 int error, pid;
399 proc_t tproc;
400 uint64_t value;
d9a64523 401 uint64_t ids[COALITION_NUM_TYPES] = {};
3e170ce0
A
402
403
404 error = SYSCTL_IN(req, &value, sizeof(value));
0a7de745 405 if (error) {
3e170ce0 406 return error;
0a7de745
A
407 }
408 if (!req->newptr) {
3e170ce0 409 pid = req->p->p_pid;
0a7de745 410 } else {
3e170ce0 411 pid = (int)value;
0a7de745 412 }
3e170ce0
A
413
414 coal_dbg("looking up coalitions for pid:%d", pid);
415 tproc = proc_find(pid);
416 if (tproc == NULL) {
417 coal_dbg("ERROR: Couldn't find pid:%d", pid);
418 return ESRCH;
419 }
420
421 task_coalition_ids(tproc->task, ids);
422 proc_rele(tproc);
423
424 return SYSCTL_OUT(req, ids, sizeof(ids));
425}
426
427SYSCTL_PROC(_kern, OID_AUTO, coalitions, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
0a7de745 428 0, 0, sysctl_coalition_get_ids, "Q", "coalition ids of a given process");
3e170ce0
A
429
430
431static int sysctl_coalition_get_roles SYSCTL_HANDLER_ARGS
432{
433#pragma unused(oidp, arg1, arg2)
434 int error, pid;
435 proc_t tproc;
436 int value;
d9a64523 437 int roles[COALITION_NUM_TYPES] = {};
3e170ce0
A
438
439
440 error = SYSCTL_IN(req, &value, sizeof(value));
0a7de745 441 if (error) {
3e170ce0 442 return error;
0a7de745
A
443 }
444 if (!req->newptr) {
3e170ce0 445 pid = req->p->p_pid;
0a7de745 446 } else {
3e170ce0 447 pid = (int)value;
0a7de745 448 }
3e170ce0
A
449
450 coal_dbg("looking up coalitions for pid:%d", pid);
451 tproc = proc_find(pid);
452 if (tproc == NULL) {
453 coal_dbg("ERROR: Couldn't find pid:%d", pid);
454 return ESRCH;
455 }
456
457 task_coalition_roles(tproc->task, roles);
458 proc_rele(tproc);
459
460 return SYSCTL_OUT(req, roles, sizeof(roles));
461}
462
463SYSCTL_PROC(_kern, OID_AUTO, coalition_roles, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
0a7de745 464 0, 0, sysctl_coalition_get_roles, "I", "coalition roles of a given process");
3e170ce0
A
465
466
467static int sysctl_coalition_get_page_count SYSCTL_HANDLER_ARGS
468{
469#pragma unused(oidp, arg1, arg2)
470 int error, pid;
471 proc_t tproc;
472 coalition_t coal;
473 uint64_t value;
474 uint64_t pgcount[COALITION_NUM_TYPES];
475
476
477 error = SYSCTL_IN(req, &value, sizeof(value));
0a7de745 478 if (error) {
3e170ce0 479 return error;
0a7de745
A
480 }
481 if (!req->newptr) {
3e170ce0 482 pid = req->p->p_pid;
0a7de745 483 } else {
3e170ce0 484 pid = (int)value;
0a7de745 485 }
3e170ce0
A
486
487 coal_dbg("looking up coalitions for pid:%d", pid);
488 tproc = proc_find(pid);
489 if (tproc == NULL) {
490 coal_dbg("ERROR: Couldn't find pid:%d", pid);
491 return ESRCH;
492 }
493
494 memset(pgcount, 0, sizeof(pgcount));
495
496 for (int t = 0; t < COALITION_NUM_TYPES; t++) {
cb323159 497 coal = task_get_coalition(tproc->task, t);
3e170ce0
A
498 if (coal != COALITION_NULL) {
499 int ntasks = 0;
500 pgcount[t] = coalition_get_page_count(coal, &ntasks);
501 coal_dbg("PID:%d, Coalition:%lld, type:%d, pgcount:%lld",
0a7de745 502 pid, coalition_id(coal), t, pgcount[t]);
3e170ce0
A
503 }
504 }
505
506 proc_rele(tproc);
507
508 return SYSCTL_OUT(req, pgcount, sizeof(pgcount));
509}
510
511SYSCTL_PROC(_kern, OID_AUTO, coalition_page_count, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
0a7de745 512 0, 0, sysctl_coalition_get_page_count, "Q", "coalition page count of a specified process");
3e170ce0
A
513
514
515static int sysctl_coalition_get_pid_list SYSCTL_HANDLER_ARGS
516{
517#pragma unused(oidp, arg1, arg2)
518 int error, type, sort_order, pid;
519 int value[3];
520 int has_pid = 1;
521
522 coalition_t coal = COALITION_NULL;
523 proc_t tproc = PROC_NULL;
524 int npids = 0;
525 int pidlist[100] = { 0, };
526
527
528 error = SYSCTL_IN(req, &value, sizeof(value));
529 if (error) {
530 has_pid = 0;
531 error = SYSCTL_IN(req, &value, sizeof(value) - sizeof(value[0]));
532 }
0a7de745 533 if (error) {
3e170ce0 534 return error;
0a7de745 535 }
3e170ce0
A
536 if (!req->newptr) {
537 type = COALITION_TYPE_RESOURCE;
538 sort_order = COALITION_SORT_DEFAULT;
539 pid = req->p->p_pid;
540 } else {
541 type = value[0];
542 sort_order = value[1];
0a7de745 543 if (has_pid) {
3e170ce0 544 pid = value[2];
0a7de745 545 } else {
3e170ce0 546 pid = req->p->p_pid;
0a7de745 547 }
3e170ce0
A
548 }
549
0a7de745 550 if (type < 0 || type >= COALITION_NUM_TYPES) {
3e170ce0 551 return EINVAL;
0a7de745 552 }
3e170ce0
A
553
554 coal_dbg("getting constituent PIDS for coalition of type %d "
0a7de745 555 "containing pid:%d (sort:%d)", type, pid, sort_order);
3e170ce0
A
556 tproc = proc_find(pid);
557 if (tproc == NULL) {
558 coal_dbg("ERROR: Couldn't find pid:%d", pid);
559 return ESRCH;
560 }
561
cb323159 562 coal = task_get_coalition(tproc->task, type);
3e170ce0
A
563 if (coal == COALITION_NULL) {
564 goto out;
565 }
566
567 npids = coalition_get_pid_list(coal, COALITION_ROLEMASK_ALLROLES, sort_order,
0a7de745 568 pidlist, sizeof(pidlist) / sizeof(pidlist[0]));
3e170ce0
A
569 if (npids > (int)(sizeof(pidlist) / sizeof(pidlist[0]))) {
570 coal_dbg("Too many members in coalition %llu (from pid:%d): %d!",
0a7de745 571 coalition_id(coal), pid, npids);
3e170ce0
A
572 npids = sizeof(pidlist) / sizeof(pidlist[0]);
573 }
574
575out:
576 proc_rele(tproc);
577
5c9f4661
A
578 if (npids < 0) {
579 /* npids is a negative errno */
580 return -npids;
581 }
582
0a7de745 583 if (npids == 0) {
3e170ce0 584 return ENOENT;
0a7de745 585 }
3e170ce0
A
586
587 return SYSCTL_OUT(req, pidlist, sizeof(pidlist[0]) * npids);
588}
589
590SYSCTL_PROC(_kern, OID_AUTO, coalition_pid_list, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
0a7de745 591 0, 0, sysctl_coalition_get_pid_list, "I", "list of PIDS which are members of the coalition of the current process");
3e170ce0
A
592
593#if DEVELOPMENT
594static int sysctl_coalition_notify SYSCTL_HANDLER_ARGS
595{
596#pragma unused(oidp, arg1, arg2)
597 int error, should_set;
598 coalition_t coal;
599 uint64_t value[2];
600
601 should_set = 1;
602 error = SYSCTL_IN(req, value, sizeof(value));
603 if (error) {
604 error = SYSCTL_IN(req, value, sizeof(value) - sizeof(value[0]));
0a7de745 605 if (error) {
3e170ce0 606 return error;
0a7de745 607 }
3e170ce0
A
608 should_set = 0;
609 }
0a7de745 610 if (!req->newptr) {
3e170ce0 611 return error;
0a7de745 612 }
3e170ce0
A
613
614 coal = coalition_find_by_id(value[0]);
615 if (coal == COALITION_NULL) {
616 coal_dbg("Can't find coalition with ID:%lld", value[0]);
617 return ESRCH;
618 }
619
0a7de745 620 if (should_set) {
3e170ce0 621 coalition_set_notify(coal, (int)value[1]);
0a7de745 622 }
3e170ce0
A
623
624 value[0] = (uint64_t)coalition_should_notify(coal);
625
626 coalition_release(coal);
627
628 return SYSCTL_OUT(req, value, sizeof(value[0]));
629}
630
631SYSCTL_PROC(_kern, OID_AUTO, coalition_notify, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
0a7de745 632 0, 0, sysctl_coalition_notify, "Q", "get/set coalition notification flag");
3e170ce0
A
633
634extern int unrestrict_coalition_syscalls;
635SYSCTL_INT(_kern, OID_AUTO, unrestrict_coalitions,
0a7de745
A
636 CTLFLAG_RW, &unrestrict_coalition_syscalls, 0,
637 "unrestrict the coalition interface");
3e170ce0
A
638
639#endif /* DEVELOPMENT */
640
641#endif /* DEVELOPMENT || DEBUG */