1 #include <kern/kern_types.h>
2 #include <mach/mach_types.h>
3 #include <mach/boolean.h>
5 #include <kern/coalition.h>
7 #include <sys/coalition.h>
9 #include <sys/kernel.h>
10 #include <sys/sysproto.h>
11 #include <sys/systm.h>
13 /* Coalitions syscalls */
16 * Create a new, empty coalition and return its ID.
19 * EINVAL Flags parameter was invalid
20 * ENOMEM Unable to allocate kernel resources for a new coalition
21 * EFAULT cidp parameter pointed to invalid memory.
23 * Returns with reference held for userspace caller.
27 coalition_create_syscall(user_addr_t cidp
, uint32_t flags
)
33 int type
= COALITION_CREATE_FLAGS_GET_TYPE(flags
);
34 boolean_t privileged
= !!(flags
& COALITION_CREATE_FLAGS_PRIVILEGED
);
36 if ((flags
& (~COALITION_CREATE_FLAGS_MASK
)) != 0)
38 if (type
< 0 || type
> COALITION_TYPE_MAX
)
41 kr
= coalition_create_internal(type
, privileged
, &coal
);
42 if (kr
!= KERN_SUCCESS
) {
43 /* for now, the only kr is KERN_RESOURCE_SHORTAGE */
48 cid
= coalition_id(coal
);
50 coal_dbg("(addr, %u) -> %llu", flags
, cid
);
51 error
= copyout(&cid
, cidp
, sizeof(cid
));
57 * Request to terminate the coalition identified by ID.
58 * Attempts to spawn into this coalition using the posix_spawnattr will begin
59 * failing. Processes already within the coalition may still fork.
60 * Arms the 'coalition is empty' notification when the coalition's active
64 * ESRCH No coalition with that ID could be found.
65 * EALREADY The coalition with that ID has already been terminated.
66 * EFAULT cidp parameter pointed to invalid memory.
67 * EPERM Caller doesn't have permission to terminate that coalition.
71 coalition_request_terminate_syscall(user_addr_t cidp
, uint32_t flags
)
82 error
= copyin(cidp
, &cid
, sizeof(cid
));
87 coal
= coalition_find_by_id(cid
);
88 if (coal
== COALITION_NULL
) {
92 kr
= coalition_request_terminate_internal(coal
);
93 coalition_release(coal
);
98 case KERN_DEFAULT_SET
:
101 case KERN_TERMINATED
:
104 case KERN_INVALID_NAME
:
112 coal_dbg("(%llu, %u) -> %d", cid
, flags
, error
);
118 * Request the kernel to deallocate the coalition identified by ID, which
119 * must be both terminated and empty. This balances the reference taken
120 * in coalition_create.
121 * The memory containig the coalition object may not be freed just yet, if
122 * other kernel operations still hold references to it.
125 * EINVAL Flags parameter was invalid
126 * ESRCH Coalition ID refers to a coalition that doesn't exist.
127 * EBUSY Coalition has not yet been terminated.
128 * EBUSY Coalition is still active.
129 * EFAULT cidp parameter pointed to invalid memory.
130 * EPERM Caller doesn't have permission to terminate that coalition.
131 * Consumes one reference, "held" by caller since coalition_create
135 coalition_reap_syscall(user_addr_t cidp
, uint32_t flags
)
146 error
= copyin(cidp
, &cid
, sizeof(cid
));
151 coal
= coalition_find_by_id(cid
);
152 if (coal
== COALITION_NULL
) {
156 kr
= coalition_reap_internal(coal
);
157 coalition_release(coal
);
162 case KERN_DEFAULT_SET
:
165 case KERN_TERMINATED
:
176 coal_dbg("(%llu, %u) -> %d", cid
, flags
, error
);
182 * Returns EPERM if the calling process is not privileged to make this call.
184 int coalition(proc_t p
, struct coalition_args
*cap
, __unused
int32_t *retval
)
186 uint32_t operation
= cap
->operation
;
187 user_addr_t cidp
= cap
->cid
;
188 uint32_t flags
= cap
->flags
;
190 int type
= COALITION_CREATE_FLAGS_GET_TYPE(flags
);
192 if (!task_is_in_privileged_coalition(p
->task
, type
)) {
197 case COALITION_OP_CREATE
:
198 error
= coalition_create_syscall(cidp
, flags
);
200 case COALITION_OP_REAP
:
201 error
= coalition_reap_syscall(cidp
, flags
);
203 case COALITION_OP_TERMINATE
:
204 error
= coalition_request_terminate_syscall(cidp
, flags
);
212 /* This is a temporary interface, likely to be changed by 15385642. */
213 static int __attribute__ ((noinline
))
214 coalition_info_resource_usage(coalition_t coal
, user_addr_t buffer
, user_size_t bufsize
)
217 struct coalition_resource_usage cru
;
219 if (bufsize
!= sizeof(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
, bufsize
);
239 int coalition_info(proc_t p
, struct coalition_info_args
*uap
, __unused
int32_t *retval
)
241 user_addr_t cidp
= uap
->cid
;
242 user_addr_t buffer
= uap
->buffer
;
243 user_addr_t bufsizep
= uap
->bufsize
;
245 uint32_t flavor
= uap
->flavor
;
250 error
= copyin(cidp
, &cid
, sizeof(cid
));
255 coal
= coalition_find_by_id(cid
);
256 if (coal
== COALITION_NULL
) {
259 /* TODO: priv check? EPERM or ESRCH? */
261 if (IS_64BIT_PROCESS(p
)) {
262 user64_size_t size64
;
263 error
= copyin(bufsizep
, &size64
, sizeof(size64
));
264 bufsize
= (user_size_t
)size64
;
266 user32_size_t size32
;
267 error
= copyin(bufsizep
, &size32
, sizeof(size32
));
268 bufsize
= (user_size_t
)size32
;
275 case COALITION_INFO_RESOURCE_USAGE
:
276 error
= coalition_info_resource_usage(coal
, buffer
, bufsize
);
283 coalition_release(coal
);
287 #if defined(DEVELOPMENT) || defined(DEBUG)
288 static int sysctl_coalition_get_ids SYSCTL_HANDLER_ARGS
290 #pragma unused(oidp, arg1, arg2)
294 uint64_t ids
[COALITION_NUM_TYPES
];
297 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
305 coal_dbg("looking up coalitions for pid:%d", pid
);
306 tproc
= proc_find(pid
);
308 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
312 task_coalition_ids(tproc
->task
, ids
);
315 return SYSCTL_OUT(req
, ids
, sizeof(ids
));
318 SYSCTL_PROC(_kern
, OID_AUTO
, coalitions
, CTLTYPE_QUAD
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
319 0, 0, sysctl_coalition_get_ids
, "Q", "coalition ids of a given process");
322 static int sysctl_coalition_get_roles SYSCTL_HANDLER_ARGS
324 #pragma unused(oidp, arg1, arg2)
328 int roles
[COALITION_NUM_TYPES
];
331 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
339 coal_dbg("looking up coalitions for pid:%d", pid
);
340 tproc
= proc_find(pid
);
342 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
346 task_coalition_roles(tproc
->task
, roles
);
349 return SYSCTL_OUT(req
, roles
, sizeof(roles
));
352 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_roles
, CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
353 0, 0, sysctl_coalition_get_roles
, "I", "coalition roles of a given process");
356 static int sysctl_coalition_get_page_count SYSCTL_HANDLER_ARGS
358 #pragma unused(oidp, arg1, arg2)
363 uint64_t pgcount
[COALITION_NUM_TYPES
];
366 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
374 coal_dbg("looking up coalitions for pid:%d", pid
);
375 tproc
= proc_find(pid
);
377 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
381 memset(pgcount
, 0, sizeof(pgcount
));
383 for (int t
= 0; t
< COALITION_NUM_TYPES
; t
++) {
384 coal
= COALITION_NULL
;
385 coalition_is_leader(tproc
->task
, t
, &coal
);
386 if (coal
!= COALITION_NULL
) {
388 pgcount
[t
] = coalition_get_page_count(coal
, &ntasks
);
389 coal_dbg("PID:%d, Coalition:%lld, type:%d, pgcount:%lld",
390 pid
, coalition_id(coal
), t
, pgcount
[t
]);
396 return SYSCTL_OUT(req
, pgcount
, sizeof(pgcount
));
399 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_page_count
, CTLTYPE_QUAD
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
400 0, 0, sysctl_coalition_get_page_count
, "Q", "coalition page count of a specified process");
403 static int sysctl_coalition_get_pid_list SYSCTL_HANDLER_ARGS
405 #pragma unused(oidp, arg1, arg2)
406 int error
, type
, sort_order
, pid
;
410 coalition_t coal
= COALITION_NULL
;
411 proc_t tproc
= PROC_NULL
;
413 int pidlist
[100] = { 0, };
416 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
419 error
= SYSCTL_IN(req
, &value
, sizeof(value
) - sizeof(value
[0]));
424 type
= COALITION_TYPE_RESOURCE
;
425 sort_order
= COALITION_SORT_DEFAULT
;
429 sort_order
= value
[1];
436 if (type
< 0 || type
>= COALITION_NUM_TYPES
)
439 coal_dbg("getting constituent PIDS for coalition of type %d "
440 "containing pid:%d (sort:%d)", type
, pid
, sort_order
);
441 tproc
= proc_find(pid
);
443 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
447 (void)coalition_is_leader(tproc
->task
, type
, &coal
);
448 if (coal
== COALITION_NULL
) {
452 npids
= coalition_get_pid_list(coal
, COALITION_ROLEMASK_ALLROLES
, sort_order
,
453 pidlist
, sizeof(pidlist
) / sizeof(pidlist
[0]));
454 if (npids
> (int)(sizeof(pidlist
) / sizeof(pidlist
[0]))) {
455 coal_dbg("Too many members in coalition %llu (from pid:%d): %d!",
456 coalition_id(coal
), pid
, npids
);
457 npids
= sizeof(pidlist
) / sizeof(pidlist
[0]);
466 return SYSCTL_OUT(req
, pidlist
, sizeof(pidlist
[0]) * npids
);
469 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_pid_list
, CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
470 0, 0, sysctl_coalition_get_pid_list
, "I", "list of PIDS which are members of the coalition of the current process");
473 static int sysctl_coalition_notify SYSCTL_HANDLER_ARGS
475 #pragma unused(oidp, arg1, arg2)
476 int error
, should_set
;
481 error
= SYSCTL_IN(req
, value
, sizeof(value
));
483 error
= SYSCTL_IN(req
, value
, sizeof(value
) - sizeof(value
[0]));
491 coal
= coalition_find_by_id(value
[0]);
492 if (coal
== COALITION_NULL
) {
493 coal_dbg("Can't find coalition with ID:%lld", value
[0]);
498 coalition_set_notify(coal
, (int)value
[1]);
500 value
[0] = (uint64_t)coalition_should_notify(coal
);
502 coalition_release(coal
);
504 return SYSCTL_OUT(req
, value
, sizeof(value
[0]));
507 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_notify
, CTLTYPE_QUAD
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
508 0, 0, sysctl_coalition_notify
, "Q", "get/set coalition notification flag");
510 extern int unrestrict_coalition_syscalls
;
511 SYSCTL_INT(_kern
, OID_AUTO
, unrestrict_coalitions
,
512 CTLFLAG_RW
, &unrestrict_coalition_syscalls
, 0,
513 "unrestrict the coalition interface");
515 #endif /* DEVELOPMENT */
517 #endif /* DEVELOPMENT || DEBUG */