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/kernel.h>
11 #include <sys/sysproto.h>
12 #include <sys/systm.h>
14 /* Coalitions syscalls */
17 * Create a new, empty coalition and return its ID.
20 * EINVAL Flags parameter was invalid
21 * ENOMEM Unable to allocate kernel resources for a new coalition
22 * EFAULT cidp parameter pointed to invalid memory.
24 * Returns with reference held for userspace caller.
28 coalition_create_syscall(user_addr_t cidp
, uint32_t flags
)
34 int type
= COALITION_CREATE_FLAGS_GET_TYPE(flags
);
35 int role
= COALITION_CREATE_FLAGS_GET_ROLE(flags
);
36 boolean_t privileged
= !!(flags
& COALITION_CREATE_FLAGS_PRIVILEGED
);
38 if ((flags
& (~COALITION_CREATE_FLAGS_MASK
)) != 0)
40 if (type
< 0 || type
> COALITION_TYPE_MAX
)
43 kr
= coalition_create_internal(type
, role
, privileged
, &coal
);
44 if (kr
!= KERN_SUCCESS
) {
45 /* for now, the only kr is KERN_RESOURCE_SHORTAGE */
50 cid
= coalition_id(coal
);
52 coal_dbg("(addr, %u) -> %llu", flags
, cid
);
53 error
= copyout(&cid
, cidp
, sizeof(cid
));
59 * Request to terminate the coalition identified by ID.
60 * Attempts to spawn into this coalition using the posix_spawnattr will begin
61 * failing. Processes already within the coalition may still fork.
62 * Arms the 'coalition is empty' notification when the coalition's active
66 * ESRCH No coalition with that ID could be found.
67 * EALREADY The coalition with that ID has already been terminated.
68 * EFAULT cidp parameter pointed to invalid memory.
69 * EPERM Caller doesn't have permission to terminate that coalition.
73 coalition_request_terminate_syscall(user_addr_t cidp
, uint32_t flags
)
84 error
= copyin(cidp
, &cid
, sizeof(cid
));
89 coal
= coalition_find_by_id(cid
);
90 if (coal
== COALITION_NULL
) {
94 kr
= coalition_request_terminate_internal(coal
);
95 coalition_release(coal
);
100 case KERN_DEFAULT_SET
:
103 case KERN_TERMINATED
:
106 case KERN_INVALID_NAME
:
114 coal_dbg("(%llu, %u) -> %d", cid
, flags
, error
);
120 * Request the kernel to deallocate the coalition identified by ID, which
121 * must be both terminated and empty. This balances the reference taken
122 * in coalition_create.
123 * The memory containing the coalition object may not be freed just yet, if
124 * other kernel operations still hold references to it.
127 * EINVAL Flags parameter was invalid
128 * ESRCH Coalition ID refers to a coalition that doesn't exist.
129 * EBUSY Coalition has not yet been terminated.
130 * EBUSY Coalition is still active.
131 * EFAULT cidp parameter pointed to invalid memory.
132 * EPERM Caller doesn't have permission to terminate that coalition.
133 * Consumes one reference, "held" by caller since coalition_create
137 coalition_reap_syscall(user_addr_t cidp
, uint32_t flags
)
148 error
= copyin(cidp
, &cid
, sizeof(cid
));
153 coal
= coalition_find_by_id(cid
);
154 if (coal
== COALITION_NULL
) {
158 kr
= coalition_reap_internal(coal
);
159 coalition_release(coal
);
164 case KERN_DEFAULT_SET
:
167 case KERN_TERMINATED
:
178 coal_dbg("(%llu, %u) -> %d", cid
, flags
, error
);
184 * Returns EPERM if the calling process is not privileged to make this call.
186 int coalition(proc_t p
, struct coalition_args
*cap
, __unused
int32_t *retval
)
188 uint32_t operation
= cap
->operation
;
189 user_addr_t cidp
= cap
->cid
;
190 uint32_t flags
= cap
->flags
;
192 int type
= COALITION_CREATE_FLAGS_GET_TYPE(flags
);
194 if (!task_is_in_privileged_coalition(p
->task
, type
)) {
199 case COALITION_OP_CREATE
:
200 error
= coalition_create_syscall(cidp
, flags
);
202 case COALITION_OP_REAP
:
203 error
= coalition_reap_syscall(cidp
, flags
);
205 case COALITION_OP_TERMINATE
:
206 error
= coalition_request_terminate_syscall(cidp
, flags
);
214 /* This is a temporary interface, likely to be changed by 15385642. */
215 static int __attribute__ ((noinline
))
216 coalition_info_resource_usage(coalition_t coal
, user_addr_t buffer
, user_size_t bufsize
)
219 struct coalition_resource_usage cru
= {};
221 kr
= coalition_resource_usage_internal(coal
, &cru
);
224 case KERN_INVALID_ARGUMENT
:
226 case KERN_RESOURCE_SHORTAGE
:
231 return EIO
; /* shrug */
234 return copyout(&cru
, buffer
, MIN(bufsize
, sizeof(cru
)));
237 #define coalition_info_set_name_internal(...) 0
240 coalition_info_efficiency(coalition_t coal
, user_addr_t buffer
, user_size_t bufsize
)
243 if (coalition_type(coal
) != COALITION_TYPE_JETSAM
)
246 error
= copyin(buffer
, &flags
, MIN(bufsize
, sizeof(flags
)));
249 if ((flags
& COALITION_EFFICIENCY_VALID_FLAGS
) == 0)
251 if (flags
& COALITION_FLAGS_EFFICIENT
) {
252 coalition_set_efficient(coal
);
257 int coalition_info(proc_t p
, struct coalition_info_args
*uap
, __unused
int32_t *retval
)
259 user_addr_t cidp
= uap
->cid
;
260 user_addr_t buffer
= uap
->buffer
;
261 user_addr_t bufsizep
= uap
->bufsize
;
263 uint32_t flavor
= uap
->flavor
;
268 error
= copyin(cidp
, &cid
, sizeof(cid
));
273 coal
= coalition_find_by_id(cid
);
274 if (coal
== COALITION_NULL
) {
277 /* TODO: priv check? EPERM or ESRCH? */
279 if (IS_64BIT_PROCESS(p
)) {
280 user64_size_t size64
;
281 error
= copyin(bufsizep
, &size64
, sizeof(size64
));
282 bufsize
= (user_size_t
)size64
;
284 user32_size_t size32
;
285 error
= copyin(bufsizep
, &size32
, sizeof(size32
));
286 bufsize
= (user_size_t
)size32
;
293 case COALITION_INFO_RESOURCE_USAGE
:
294 error
= coalition_info_resource_usage(coal
, buffer
, bufsize
);
296 case COALITION_INFO_SET_NAME
:
297 error
= coalition_info_set_name_internal(coal
, buffer
, bufsize
);
299 case COALITION_INFO_SET_EFFICIENCY
:
300 error
= coalition_info_efficiency(coal
, buffer
, bufsize
);
307 coalition_release(coal
);
311 #if DEVELOPMENT || DEBUG
312 static int sysctl_coalition_get_ids SYSCTL_HANDLER_ARGS
314 #pragma unused(oidp, arg1, arg2)
318 uint64_t ids
[COALITION_NUM_TYPES
] = {};
321 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
329 coal_dbg("looking up coalitions for pid:%d", pid
);
330 tproc
= proc_find(pid
);
332 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
336 task_coalition_ids(tproc
->task
, ids
);
339 return SYSCTL_OUT(req
, ids
, sizeof(ids
));
342 SYSCTL_PROC(_kern
, OID_AUTO
, coalitions
, CTLTYPE_QUAD
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
343 0, 0, sysctl_coalition_get_ids
, "Q", "coalition ids of a given process");
346 static int sysctl_coalition_get_roles SYSCTL_HANDLER_ARGS
348 #pragma unused(oidp, arg1, arg2)
352 int roles
[COALITION_NUM_TYPES
] = {};
355 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
363 coal_dbg("looking up coalitions for pid:%d", pid
);
364 tproc
= proc_find(pid
);
366 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
370 task_coalition_roles(tproc
->task
, roles
);
373 return SYSCTL_OUT(req
, roles
, sizeof(roles
));
376 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_roles
, CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
377 0, 0, sysctl_coalition_get_roles
, "I", "coalition roles of a given process");
380 static int sysctl_coalition_get_page_count SYSCTL_HANDLER_ARGS
382 #pragma unused(oidp, arg1, arg2)
387 uint64_t pgcount
[COALITION_NUM_TYPES
];
390 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
398 coal_dbg("looking up coalitions for pid:%d", pid
);
399 tproc
= proc_find(pid
);
401 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
405 memset(pgcount
, 0, sizeof(pgcount
));
407 for (int t
= 0; t
< COALITION_NUM_TYPES
; t
++) {
408 coal
= COALITION_NULL
;
409 coalition_is_leader(tproc
->task
, t
, &coal
);
410 if (coal
!= COALITION_NULL
) {
412 pgcount
[t
] = coalition_get_page_count(coal
, &ntasks
);
413 coal_dbg("PID:%d, Coalition:%lld, type:%d, pgcount:%lld",
414 pid
, coalition_id(coal
), t
, pgcount
[t
]);
420 return SYSCTL_OUT(req
, pgcount
, sizeof(pgcount
));
423 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_page_count
, CTLTYPE_QUAD
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
424 0, 0, sysctl_coalition_get_page_count
, "Q", "coalition page count of a specified process");
427 static int sysctl_coalition_get_pid_list SYSCTL_HANDLER_ARGS
429 #pragma unused(oidp, arg1, arg2)
430 int error
, type
, sort_order
, pid
;
434 coalition_t coal
= COALITION_NULL
;
435 proc_t tproc
= PROC_NULL
;
437 int pidlist
[100] = { 0, };
440 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
443 error
= SYSCTL_IN(req
, &value
, sizeof(value
) - sizeof(value
[0]));
448 type
= COALITION_TYPE_RESOURCE
;
449 sort_order
= COALITION_SORT_DEFAULT
;
453 sort_order
= value
[1];
460 if (type
< 0 || type
>= COALITION_NUM_TYPES
)
463 coal_dbg("getting constituent PIDS for coalition of type %d "
464 "containing pid:%d (sort:%d)", type
, pid
, sort_order
);
465 tproc
= proc_find(pid
);
467 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
471 (void)coalition_is_leader(tproc
->task
, type
, &coal
);
472 if (coal
== COALITION_NULL
) {
476 npids
= coalition_get_pid_list(coal
, COALITION_ROLEMASK_ALLROLES
, sort_order
,
477 pidlist
, sizeof(pidlist
) / sizeof(pidlist
[0]));
478 if (npids
> (int)(sizeof(pidlist
) / sizeof(pidlist
[0]))) {
479 coal_dbg("Too many members in coalition %llu (from pid:%d): %d!",
480 coalition_id(coal
), pid
, npids
);
481 npids
= sizeof(pidlist
) / sizeof(pidlist
[0]);
488 /* npids is a negative errno */
495 return SYSCTL_OUT(req
, pidlist
, sizeof(pidlist
[0]) * npids
);
498 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_pid_list
, CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
499 0, 0, sysctl_coalition_get_pid_list
, "I", "list of PIDS which are members of the coalition of the current process");
502 static int sysctl_coalition_notify SYSCTL_HANDLER_ARGS
504 #pragma unused(oidp, arg1, arg2)
505 int error
, should_set
;
510 error
= SYSCTL_IN(req
, value
, sizeof(value
));
512 error
= SYSCTL_IN(req
, value
, sizeof(value
) - sizeof(value
[0]));
520 coal
= coalition_find_by_id(value
[0]);
521 if (coal
== COALITION_NULL
) {
522 coal_dbg("Can't find coalition with ID:%lld", value
[0]);
527 coalition_set_notify(coal
, (int)value
[1]);
529 value
[0] = (uint64_t)coalition_should_notify(coal
);
531 coalition_release(coal
);
533 return SYSCTL_OUT(req
, value
, sizeof(value
[0]));
536 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_notify
, CTLTYPE_QUAD
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
537 0, 0, sysctl_coalition_notify
, "Q", "get/set coalition notification flag");
539 extern int unrestrict_coalition_syscalls
;
540 SYSCTL_INT(_kern
, OID_AUTO
, unrestrict_coalitions
,
541 CTLFLAG_RW
, &unrestrict_coalition_syscalls
, 0,
542 "unrestrict the coalition interface");
544 #endif /* DEVELOPMENT */
546 #endif /* DEVELOPMENT || DEBUG */