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
);
47 if (kr
!= KERN_SUCCESS
) {
48 /* for now, the only kr is KERN_RESOURCE_SHORTAGE */
53 cid
= coalition_id(coal
);
55 coal_dbg("(addr, %u) -> %llu", flags
, cid
);
56 error
= copyout(&cid
, cidp
, sizeof(cid
));
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
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.
76 coalition_request_terminate_syscall(user_addr_t cidp
, uint32_t flags
)
87 error
= copyin(cidp
, &cid
, sizeof(cid
));
92 coal
= coalition_find_by_id(cid
);
93 if (coal
== COALITION_NULL
) {
97 kr
= coalition_request_terminate_internal(coal
);
98 coalition_release(coal
);
103 case KERN_DEFAULT_SET
:
106 case KERN_TERMINATED
:
109 case KERN_INVALID_NAME
:
117 coal_dbg("(%llu, %u) -> %d", cid
, flags
, error
);
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.
126 * The memory containing the coalition object may not be freed just yet, if
127 * other kernel operations still hold references to it.
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
140 coalition_reap_syscall(user_addr_t cidp
, uint32_t flags
)
151 error
= copyin(cidp
, &cid
, sizeof(cid
));
156 coal
= coalition_find_by_id(cid
);
157 if (coal
== COALITION_NULL
) {
161 kr
= coalition_reap_internal(coal
);
162 coalition_release(coal
);
167 case KERN_DEFAULT_SET
:
170 case KERN_TERMINATED
:
181 coal_dbg("(%llu, %u) -> %d", cid
, flags
, error
);
187 * Returns EPERM if the calling process is not privileged to make this call.
190 coalition(proc_t p
, struct coalition_args
*cap
, __unused
int32_t *retval
)
192 uint32_t operation
= cap
->operation
;
193 user_addr_t cidp
= cap
->cid
;
194 uint32_t flags
= cap
->flags
;
196 int type
= COALITION_CREATE_FLAGS_GET_TYPE(flags
);
198 if (!task_is_in_privileged_coalition(p
->task
, type
)) {
203 case COALITION_OP_CREATE
:
204 error
= coalition_create_syscall(cidp
, flags
);
206 case COALITION_OP_REAP
:
207 error
= coalition_reap_syscall(cidp
, flags
);
209 case COALITION_OP_TERMINATE
:
210 error
= coalition_request_terminate_syscall(cidp
, flags
);
218 /* This is a temporary interface, likely to be changed by 15385642. */
219 static int __attribute__ ((noinline
))
220 coalition_info_resource_usage(coalition_t coal
, user_addr_t buffer
, user_size_t bufsize
)
223 struct coalition_resource_usage cru
= {};
225 kr
= coalition_resource_usage_internal(coal
, &cru
);
228 case KERN_INVALID_ARGUMENT
:
230 case KERN_RESOURCE_SHORTAGE
:
235 return EIO
; /* shrug */
238 return copyout(&cru
, buffer
, MIN(bufsize
, sizeof(cru
)));
241 #define coalition_info_set_name_internal(...) 0
244 coalition_info_efficiency(coalition_t coal
, user_addr_t buffer
, user_size_t bufsize
)
247 if (coalition_type(coal
) != COALITION_TYPE_JETSAM
) {
251 error
= copyin(buffer
, &flags
, MIN(bufsize
, sizeof(flags
)));
255 if ((flags
& COALITION_EFFICIENCY_VALID_FLAGS
) == 0) {
258 if (flags
& COALITION_FLAGS_EFFICIENT
) {
259 coalition_set_efficient(coal
);
265 coalition_ledger_logical_writes_limit(coalition_t coal
, user_addr_t buffer
, user_size_t bufsize
)
270 if (coalition_type(coal
) != COALITION_TYPE_RESOURCE
) {
274 error
= copyin(buffer
, &limit
, MIN(bufsize
, sizeof(limit
)));
280 error
= coalition_ledger_set_logical_writes_limit(coal
, limit
);
286 coalition_info(proc_t p
, struct coalition_info_args
*uap
, __unused
int32_t *retval
)
288 user_addr_t cidp
= uap
->cid
;
289 user_addr_t buffer
= uap
->buffer
;
290 user_addr_t bufsizep
= uap
->bufsize
;
292 uint32_t flavor
= uap
->flavor
;
297 error
= copyin(cidp
, &cid
, sizeof(cid
));
302 coal
= coalition_find_by_id(cid
);
303 if (coal
== COALITION_NULL
) {
306 /* TODO: priv check? EPERM or ESRCH? */
308 if (IS_64BIT_PROCESS(p
)) {
309 user64_size_t size64
;
310 error
= copyin(bufsizep
, &size64
, sizeof(size64
));
311 bufsize
= (user_size_t
)size64
;
313 user32_size_t size32
;
314 error
= copyin(bufsizep
, &size32
, sizeof(size32
));
315 bufsize
= (user_size_t
)size32
;
322 case COALITION_INFO_RESOURCE_USAGE
:
323 error
= coalition_info_resource_usage(coal
, buffer
, bufsize
);
325 case COALITION_INFO_SET_NAME
:
326 error
= coalition_info_set_name_internal(coal
, buffer
, bufsize
);
328 case COALITION_INFO_SET_EFFICIENCY
:
329 error
= coalition_info_efficiency(coal
, buffer
, bufsize
);
336 coalition_release(coal
);
341 coalition_ledger(__unused proc_t p
, __unused
struct coalition_ledger_args
*uap
, __unused
int32_t *retval
)
343 user_addr_t cidp
= uap
->cid
;
344 user_addr_t buffer
= uap
->buffer
;
345 user_addr_t bufsizep
= uap
->bufsize
;
347 uint32_t operation
= uap
->operation
;
350 coalition_t coal
= COALITION_NULL
;
352 if (!kauth_cred_issuser(kauth_cred_get())) {
357 error
= copyin(cidp
, &cid
, sizeof(cid
));
362 coal
= coalition_find_by_id(cid
);
363 if (coal
== COALITION_NULL
) {
368 if (IS_64BIT_PROCESS(p
)) {
369 user64_size_t size64
;
370 error
= copyin(bufsizep
, &size64
, sizeof(size64
));
371 bufsize
= (user_size_t
)size64
;
373 user32_size_t size32
;
374 error
= copyin(bufsizep
, &size32
, sizeof(size32
));
375 bufsize
= (user_size_t
)size32
;
382 case COALITION_LEDGER_SET_LOGICAL_WRITES_LIMIT
:
383 error
= coalition_ledger_logical_writes_limit(coal
, buffer
, bufsize
);
389 if (coal
!= COALITION_NULL
) {
390 coalition_release(coal
);
394 #if DEVELOPMENT || DEBUG
395 static int sysctl_coalition_get_ids SYSCTL_HANDLER_ARGS
397 #pragma unused(oidp, arg1, arg2)
401 uint64_t ids
[COALITION_NUM_TYPES
] = {};
404 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
414 coal_dbg("looking up coalitions for pid:%d", pid
);
415 tproc
= proc_find(pid
);
417 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
421 task_coalition_ids(tproc
->task
, ids
);
424 return SYSCTL_OUT(req
, ids
, sizeof(ids
));
427 SYSCTL_PROC(_kern
, OID_AUTO
, coalitions
, CTLTYPE_QUAD
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
428 0, 0, sysctl_coalition_get_ids
, "Q", "coalition ids of a given process");
431 static int sysctl_coalition_get_roles SYSCTL_HANDLER_ARGS
433 #pragma unused(oidp, arg1, arg2)
437 int roles
[COALITION_NUM_TYPES
] = {};
440 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
450 coal_dbg("looking up coalitions for pid:%d", pid
);
451 tproc
= proc_find(pid
);
453 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
457 task_coalition_roles(tproc
->task
, roles
);
460 return SYSCTL_OUT(req
, roles
, sizeof(roles
));
463 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_roles
, CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
464 0, 0, sysctl_coalition_get_roles
, "I", "coalition roles of a given process");
467 static int sysctl_coalition_get_page_count SYSCTL_HANDLER_ARGS
469 #pragma unused(oidp, arg1, arg2)
474 uint64_t pgcount
[COALITION_NUM_TYPES
];
477 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
487 coal_dbg("looking up coalitions for pid:%d", pid
);
488 tproc
= proc_find(pid
);
490 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
494 memset(pgcount
, 0, sizeof(pgcount
));
496 for (int t
= 0; t
< COALITION_NUM_TYPES
; t
++) {
497 coal
= task_get_coalition(tproc
->task
, t
);
498 if (coal
!= COALITION_NULL
) {
500 pgcount
[t
] = coalition_get_page_count(coal
, &ntasks
);
501 coal_dbg("PID:%d, Coalition:%lld, type:%d, pgcount:%lld",
502 pid
, coalition_id(coal
), t
, pgcount
[t
]);
508 return SYSCTL_OUT(req
, pgcount
, sizeof(pgcount
));
511 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_page_count
, CTLTYPE_QUAD
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
512 0, 0, sysctl_coalition_get_page_count
, "Q", "coalition page count of a specified process");
515 static int sysctl_coalition_get_pid_list SYSCTL_HANDLER_ARGS
517 #pragma unused(oidp, arg1, arg2)
518 int error
, type
, sort_order
, pid
;
522 coalition_t coal
= COALITION_NULL
;
523 proc_t tproc
= PROC_NULL
;
525 int pidlist
[100] = { 0, };
528 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
531 error
= SYSCTL_IN(req
, &value
, sizeof(value
) - sizeof(value
[0]));
537 type
= COALITION_TYPE_RESOURCE
;
538 sort_order
= COALITION_SORT_DEFAULT
;
542 sort_order
= value
[1];
550 if (type
< 0 || type
>= COALITION_NUM_TYPES
) {
554 coal_dbg("getting constituent PIDS for coalition of type %d "
555 "containing pid:%d (sort:%d)", type
, pid
, sort_order
);
556 tproc
= proc_find(pid
);
558 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
562 coal
= task_get_coalition(tproc
->task
, type
);
563 if (coal
== COALITION_NULL
) {
567 npids
= coalition_get_pid_list(coal
, COALITION_ROLEMASK_ALLROLES
, sort_order
,
568 pidlist
, sizeof(pidlist
) / sizeof(pidlist
[0]));
569 if (npids
> (int)(sizeof(pidlist
) / sizeof(pidlist
[0]))) {
570 coal_dbg("Too many members in coalition %llu (from pid:%d): %d!",
571 coalition_id(coal
), pid
, npids
);
572 npids
= sizeof(pidlist
) / sizeof(pidlist
[0]);
579 /* npids is a negative errno */
587 return SYSCTL_OUT(req
, pidlist
, sizeof(pidlist
[0]) * npids
);
590 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_pid_list
, CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
591 0, 0, sysctl_coalition_get_pid_list
, "I", "list of PIDS which are members of the coalition of the current process");
594 static int sysctl_coalition_notify SYSCTL_HANDLER_ARGS
596 #pragma unused(oidp, arg1, arg2)
597 int error
, should_set
;
602 error
= SYSCTL_IN(req
, value
, sizeof(value
));
604 error
= SYSCTL_IN(req
, value
, sizeof(value
) - sizeof(value
[0]));
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]);
621 coalition_set_notify(coal
, (int)value
[1]);
624 value
[0] = (uint64_t)coalition_should_notify(coal
);
626 coalition_release(coal
);
628 return SYSCTL_OUT(req
, value
, sizeof(value
[0]));
631 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_notify
, CTLTYPE_QUAD
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
632 0, 0, sysctl_coalition_notify
, "Q", "get/set coalition notification flag");
634 extern int unrestrict_coalition_syscalls
;
635 SYSCTL_INT(_kern
, OID_AUTO
, unrestrict_coalitions
,
636 CTLFLAG_RW
, &unrestrict_coalition_syscalls
, 0,
637 "unrestrict the coalition interface");
639 #endif /* DEVELOPMENT */
641 #endif /* DEVELOPMENT || DEBUG */