1 #include <kern/kern_types.h>
2 #include <kern/thread_group.h>
3 #include <mach/mach_types.h>
4 #include <mach/boolean.h>
6 #include <kern/coalition.h>
8 #include <sys/coalition.h>
10 #include <sys/kauth.h>
11 #include <sys/kernel.h>
12 #include <sys/sysproto.h>
13 #include <sys/systm.h>
15 /* Coalitions syscalls */
18 * Create a new, empty coalition and return its ID.
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.
25 * Returns with reference held for userspace caller.
29 coalition_create_syscall(user_addr_t cidp
, uint32_t flags
)
35 int type
= COALITION_CREATE_FLAGS_GET_TYPE(flags
);
36 int role
= COALITION_CREATE_FLAGS_GET_ROLE(flags
);
37 boolean_t privileged
= !!(flags
& COALITION_CREATE_FLAGS_PRIVILEGED
);
39 if ((flags
& (~COALITION_CREATE_FLAGS_MASK
)) != 0) {
42 if (type
< 0 || type
> COALITION_TYPE_MAX
) {
46 kr
= coalition_create_internal(type
, role
, privileged
, &coal
, &cid
);
47 if (kr
!= KERN_SUCCESS
) {
48 /* for now, the only kr is KERN_RESOURCE_SHORTAGE */
53 coal_dbg("(addr, %u) -> %llu", flags
, cid
);
54 error
= copyout(&cid
, cidp
, sizeof(cid
));
60 * Request to terminate the coalition identified by ID.
61 * Attempts to spawn into this coalition using the posix_spawnattr will begin
62 * failing. Processes already within the coalition may still fork.
63 * Arms the 'coalition is empty' notification when the coalition's active
67 * ESRCH No coalition with that ID could be found.
68 * EALREADY The coalition with that ID has already been terminated.
69 * EFAULT cidp parameter pointed to invalid memory.
70 * EPERM Caller doesn't have permission to terminate that coalition.
74 coalition_request_terminate_syscall(user_addr_t cidp
, uint32_t flags
)
85 error
= copyin(cidp
, &cid
, sizeof(cid
));
90 coal
= coalition_find_by_id(cid
);
91 if (coal
== COALITION_NULL
) {
95 kr
= coalition_request_terminate_internal(coal
);
96 coalition_release(coal
);
101 case KERN_DEFAULT_SET
:
104 case KERN_TERMINATED
:
107 case KERN_INVALID_NAME
:
115 coal_dbg("(%llu, %u) -> %d", cid
, flags
, error
);
121 * Request the kernel to deallocate the coalition identified by ID, which
122 * must be both terminated and empty. This balances the reference taken
123 * in coalition_create.
124 * The memory containing the coalition object may not be freed just yet, if
125 * other kernel operations still hold references to it.
128 * EINVAL Flags parameter was invalid
129 * ESRCH Coalition ID refers to a coalition that doesn't exist.
130 * EBUSY Coalition has not yet been terminated.
131 * EBUSY Coalition is still active.
132 * EFAULT cidp parameter pointed to invalid memory.
133 * EPERM Caller doesn't have permission to terminate that coalition.
134 * Consumes one reference, "held" by caller since coalition_create
138 coalition_reap_syscall(user_addr_t cidp
, uint32_t flags
)
149 error
= copyin(cidp
, &cid
, sizeof(cid
));
154 coal
= coalition_find_by_id(cid
);
155 if (coal
== COALITION_NULL
) {
159 kr
= coalition_reap_internal(coal
);
160 coalition_release(coal
);
165 case KERN_DEFAULT_SET
:
168 case KERN_TERMINATED
:
179 coal_dbg("(%llu, %u) -> %d", cid
, flags
, error
);
185 * Returns EPERM if the calling process is not privileged to make this call.
188 coalition(proc_t p
, struct coalition_args
*cap
, __unused
int32_t *retval
)
190 uint32_t operation
= cap
->operation
;
191 user_addr_t cidp
= cap
->cid
;
192 uint32_t flags
= cap
->flags
;
194 int type
= COALITION_CREATE_FLAGS_GET_TYPE(flags
);
196 if (!task_is_in_privileged_coalition(p
->task
, type
)) {
201 case COALITION_OP_CREATE
:
202 error
= coalition_create_syscall(cidp
, flags
);
204 case COALITION_OP_REAP
:
205 error
= coalition_reap_syscall(cidp
, flags
);
207 case COALITION_OP_TERMINATE
:
208 error
= coalition_request_terminate_syscall(cidp
, flags
);
216 /* This is a temporary interface, likely to be changed by 15385642. */
217 static int __attribute__ ((noinline
))
218 coalition_info_resource_usage(coalition_t coal
, user_addr_t buffer
, user_size_t bufsize
)
221 struct coalition_resource_usage cru
= {};
223 kr
= coalition_resource_usage_internal(coal
, &cru
);
226 case KERN_INVALID_ARGUMENT
:
228 case KERN_RESOURCE_SHORTAGE
:
233 return EIO
; /* shrug */
236 return copyout(&cru
, buffer
, MIN(bufsize
, sizeof(cru
)));
239 #if CONFIG_THREAD_GROUPS
241 coalition_info_set_name_internal(coalition_t coal
, user_addr_t buffer
, user_size_t bufsize
)
244 char name
[THREAD_GROUP_MAXNAME
];
246 if (coalition_type(coal
) != COALITION_TYPE_JETSAM
) {
249 bzero(name
, sizeof(name
));
250 error
= copyin(buffer
, name
, MIN(bufsize
, sizeof(name
) - 1));
254 struct thread_group
*tg
= coalition_get_thread_group(coal
);
255 thread_group_set_name(tg
, name
);
256 thread_group_release(tg
);
260 #else /* CONFIG_THREAD_GROUPS */
261 #define coalition_info_set_name_internal(...) 0
262 #endif /* CONFIG_THREAD_GROUPS */
265 coalition_info_efficiency(coalition_t coal
, user_addr_t buffer
, user_size_t bufsize
)
268 if (coalition_type(coal
) != COALITION_TYPE_JETSAM
) {
272 error
= copyin(buffer
, &flags
, MIN(bufsize
, sizeof(flags
)));
276 if ((flags
& COALITION_EFFICIENCY_VALID_FLAGS
) == 0) {
279 if (flags
& COALITION_FLAGS_EFFICIENT
) {
280 coalition_set_efficient(coal
);
281 #if CONFIG_THREAD_GROUPS
282 struct thread_group
*tg
= coalition_get_thread_group(coal
);
283 thread_group_set_flags(tg
, THREAD_GROUP_FLAGS_EFFICIENT
);
284 thread_group_release(tg
);
285 #endif /* CONFIG_THREAD_GROUPS */
291 coalition_ledger_logical_writes_limit(coalition_t coal
, user_addr_t buffer
, user_size_t bufsize
)
296 if (coalition_type(coal
) != COALITION_TYPE_RESOURCE
) {
300 error
= copyin(buffer
, &limit
, MIN(bufsize
, sizeof(limit
)));
306 error
= coalition_ledger_set_logical_writes_limit(coal
, limit
);
312 coalition_info(proc_t p
, struct coalition_info_args
*uap
, __unused
int32_t *retval
)
314 user_addr_t cidp
= uap
->cid
;
315 user_addr_t buffer
= uap
->buffer
;
316 user_addr_t bufsizep
= uap
->bufsize
;
318 uint32_t flavor
= uap
->flavor
;
323 error
= copyin(cidp
, &cid
, sizeof(cid
));
328 coal
= coalition_find_by_id(cid
);
329 if (coal
== COALITION_NULL
) {
332 /* TODO: priv check? EPERM or ESRCH? */
334 if (IS_64BIT_PROCESS(p
)) {
335 user64_size_t size64
;
336 error
= copyin(bufsizep
, &size64
, sizeof(size64
));
337 bufsize
= (user_size_t
)size64
;
339 user32_size_t size32
;
340 error
= copyin(bufsizep
, &size32
, sizeof(size32
));
341 bufsize
= (user_size_t
)size32
;
348 case COALITION_INFO_RESOURCE_USAGE
:
349 error
= coalition_info_resource_usage(coal
, buffer
, bufsize
);
351 case COALITION_INFO_SET_NAME
:
352 error
= coalition_info_set_name_internal(coal
, buffer
, bufsize
);
354 case COALITION_INFO_SET_EFFICIENCY
:
355 error
= coalition_info_efficiency(coal
, buffer
, bufsize
);
362 coalition_release(coal
);
367 coalition_ledger(__unused proc_t p
, __unused
struct coalition_ledger_args
*uap
, __unused
int32_t *retval
)
369 user_addr_t cidp
= uap
->cid
;
370 user_addr_t buffer
= uap
->buffer
;
371 user_addr_t bufsizep
= uap
->bufsize
;
373 uint32_t operation
= uap
->operation
;
376 coalition_t coal
= COALITION_NULL
;
378 if (!kauth_cred_issuser(kauth_cred_get())) {
383 error
= copyin(cidp
, &cid
, sizeof(cid
));
388 coal
= coalition_find_by_id(cid
);
389 if (coal
== COALITION_NULL
) {
394 if (IS_64BIT_PROCESS(p
)) {
395 user64_size_t size64
;
396 error
= copyin(bufsizep
, &size64
, sizeof(size64
));
397 bufsize
= (user_size_t
)size64
;
399 user32_size_t size32
;
400 error
= copyin(bufsizep
, &size32
, sizeof(size32
));
401 bufsize
= (user_size_t
)size32
;
408 case COALITION_LEDGER_SET_LOGICAL_WRITES_LIMIT
:
409 error
= coalition_ledger_logical_writes_limit(coal
, buffer
, bufsize
);
415 if (coal
!= COALITION_NULL
) {
416 coalition_release(coal
);
420 #if DEVELOPMENT || DEBUG
421 static int sysctl_coalition_get_ids SYSCTL_HANDLER_ARGS
423 #pragma unused(oidp, arg1, arg2)
427 uint64_t ids
[COALITION_NUM_TYPES
] = {};
430 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
440 coal_dbg("looking up coalitions for pid:%d", pid
);
441 tproc
= proc_find(pid
);
443 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
447 task_coalition_ids(tproc
->task
, ids
);
450 return SYSCTL_OUT(req
, ids
, sizeof(ids
));
453 SYSCTL_PROC(_kern
, OID_AUTO
, coalitions
, CTLTYPE_QUAD
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
454 0, 0, sysctl_coalition_get_ids
, "Q", "coalition ids of a given process");
457 static int sysctl_coalition_get_roles SYSCTL_HANDLER_ARGS
459 #pragma unused(oidp, arg1, arg2)
463 int roles
[COALITION_NUM_TYPES
] = {};
466 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
476 coal_dbg("looking up coalitions for pid:%d", pid
);
477 tproc
= proc_find(pid
);
479 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
483 task_coalition_roles(tproc
->task
, roles
);
486 return SYSCTL_OUT(req
, roles
, sizeof(roles
));
489 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_roles
, CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
490 0, 0, sysctl_coalition_get_roles
, "I", "coalition roles of a given process");
493 static int sysctl_coalition_get_page_count SYSCTL_HANDLER_ARGS
495 #pragma unused(oidp, arg1, arg2)
500 uint64_t pgcount
[COALITION_NUM_TYPES
];
503 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
513 coal_dbg("looking up coalitions for pid:%d", pid
);
514 tproc
= proc_find(pid
);
516 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
520 memset(pgcount
, 0, sizeof(pgcount
));
522 for (int t
= 0; t
< COALITION_NUM_TYPES
; t
++) {
523 coal
= task_get_coalition(tproc
->task
, t
);
524 if (coal
!= COALITION_NULL
) {
526 pgcount
[t
] = coalition_get_page_count(coal
, &ntasks
);
527 coal_dbg("PID:%d, Coalition:%lld, type:%d, pgcount:%lld",
528 pid
, coalition_id(coal
), t
, pgcount
[t
]);
534 return SYSCTL_OUT(req
, pgcount
, sizeof(pgcount
));
537 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_page_count
, CTLTYPE_QUAD
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
538 0, 0, sysctl_coalition_get_page_count
, "Q", "coalition page count of a specified process");
541 static int sysctl_coalition_get_pid_list SYSCTL_HANDLER_ARGS
543 #pragma unused(oidp, arg1, arg2)
544 int error
, type
, sort_order
, pid
;
548 coalition_t coal
= COALITION_NULL
;
549 proc_t tproc
= PROC_NULL
;
551 int pidlist
[100] = { 0, };
554 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
557 error
= SYSCTL_IN(req
, &value
, sizeof(value
) - sizeof(value
[0]));
563 type
= COALITION_TYPE_RESOURCE
;
564 sort_order
= COALITION_SORT_DEFAULT
;
568 sort_order
= value
[1];
576 if (type
< 0 || type
>= COALITION_NUM_TYPES
) {
580 coal_dbg("getting constituent PIDS for coalition of type %d "
581 "containing pid:%d (sort:%d)", type
, pid
, sort_order
);
582 tproc
= proc_find(pid
);
584 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
588 coal
= task_get_coalition(tproc
->task
, type
);
589 if (coal
== COALITION_NULL
) {
593 npids
= coalition_get_pid_list(coal
, COALITION_ROLEMASK_ALLROLES
, sort_order
,
594 pidlist
, sizeof(pidlist
) / sizeof(pidlist
[0]));
595 if (npids
> (int)(sizeof(pidlist
) / sizeof(pidlist
[0]))) {
596 coal_dbg("Too many members in coalition %llu (from pid:%d): %d!",
597 coalition_id(coal
), pid
, npids
);
598 npids
= sizeof(pidlist
) / sizeof(pidlist
[0]);
605 /* npids is a negative errno */
613 return SYSCTL_OUT(req
, pidlist
, sizeof(pidlist
[0]) * npids
);
616 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_pid_list
, CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
617 0, 0, sysctl_coalition_get_pid_list
, "I", "list of PIDS which are members of the coalition of the current process");
620 static int sysctl_coalition_notify SYSCTL_HANDLER_ARGS
622 #pragma unused(oidp, arg1, arg2)
623 int error
, should_set
;
628 error
= SYSCTL_IN(req
, value
, sizeof(value
));
630 error
= SYSCTL_IN(req
, value
, sizeof(value
) - sizeof(value
[0]));
640 coal
= coalition_find_by_id(value
[0]);
641 if (coal
== COALITION_NULL
) {
642 coal_dbg("Can't find coalition with ID:%lld", value
[0]);
647 coalition_set_notify(coal
, (int)value
[1]);
650 value
[0] = (uint64_t)coalition_should_notify(coal
);
652 coalition_release(coal
);
654 return SYSCTL_OUT(req
, value
, sizeof(value
[0]));
657 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_notify
, CTLTYPE_QUAD
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
658 0, 0, sysctl_coalition_notify
, "Q", "get/set coalition notification flag");
660 extern int unrestrict_coalition_syscalls
;
661 SYSCTL_INT(_kern
, OID_AUTO
, unrestrict_coalitions
,
662 CTLFLAG_RW
, &unrestrict_coalition_syscalls
, 0,
663 "unrestrict the coalition interface");
665 #endif /* DEVELOPMENT */
667 #endif /* DEVELOPMENT || DEBUG */