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 kr
= coalition_resource_usage_internal(coal
, &cru
);
222 case KERN_INVALID_ARGUMENT
:
224 case KERN_RESOURCE_SHORTAGE
:
229 return EIO
; /* shrug */
232 return copyout(&cru
, buffer
, MIN(bufsize
, sizeof(cru
)));
235 int coalition_info(proc_t p
, struct coalition_info_args
*uap
, __unused
int32_t *retval
)
237 user_addr_t cidp
= uap
->cid
;
238 user_addr_t buffer
= uap
->buffer
;
239 user_addr_t bufsizep
= uap
->bufsize
;
241 uint32_t flavor
= uap
->flavor
;
246 error
= copyin(cidp
, &cid
, sizeof(cid
));
251 coal
= coalition_find_by_id(cid
);
252 if (coal
== COALITION_NULL
) {
255 /* TODO: priv check? EPERM or ESRCH? */
257 if (IS_64BIT_PROCESS(p
)) {
258 user64_size_t size64
;
259 error
= copyin(bufsizep
, &size64
, sizeof(size64
));
260 bufsize
= (user_size_t
)size64
;
262 user32_size_t size32
;
263 error
= copyin(bufsizep
, &size32
, sizeof(size32
));
264 bufsize
= (user_size_t
)size32
;
271 case COALITION_INFO_RESOURCE_USAGE
:
272 error
= coalition_info_resource_usage(coal
, buffer
, bufsize
);
279 coalition_release(coal
);
283 #if defined(DEVELOPMENT) || defined(DEBUG)
284 static int sysctl_coalition_get_ids SYSCTL_HANDLER_ARGS
286 #pragma unused(oidp, arg1, arg2)
290 uint64_t ids
[COALITION_NUM_TYPES
];
293 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
301 coal_dbg("looking up coalitions for pid:%d", pid
);
302 tproc
= proc_find(pid
);
304 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
308 task_coalition_ids(tproc
->task
, ids
);
311 return SYSCTL_OUT(req
, ids
, sizeof(ids
));
314 SYSCTL_PROC(_kern
, OID_AUTO
, coalitions
, CTLTYPE_QUAD
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
315 0, 0, sysctl_coalition_get_ids
, "Q", "coalition ids of a given process");
318 static int sysctl_coalition_get_roles SYSCTL_HANDLER_ARGS
320 #pragma unused(oidp, arg1, arg2)
324 int roles
[COALITION_NUM_TYPES
];
327 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
335 coal_dbg("looking up coalitions for pid:%d", pid
);
336 tproc
= proc_find(pid
);
338 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
342 task_coalition_roles(tproc
->task
, roles
);
345 return SYSCTL_OUT(req
, roles
, sizeof(roles
));
348 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_roles
, CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
349 0, 0, sysctl_coalition_get_roles
, "I", "coalition roles of a given process");
352 static int sysctl_coalition_get_page_count SYSCTL_HANDLER_ARGS
354 #pragma unused(oidp, arg1, arg2)
359 uint64_t pgcount
[COALITION_NUM_TYPES
];
362 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
370 coal_dbg("looking up coalitions for pid:%d", pid
);
371 tproc
= proc_find(pid
);
373 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
377 memset(pgcount
, 0, sizeof(pgcount
));
379 for (int t
= 0; t
< COALITION_NUM_TYPES
; t
++) {
380 coal
= COALITION_NULL
;
381 coalition_is_leader(tproc
->task
, t
, &coal
);
382 if (coal
!= COALITION_NULL
) {
384 pgcount
[t
] = coalition_get_page_count(coal
, &ntasks
);
385 coal_dbg("PID:%d, Coalition:%lld, type:%d, pgcount:%lld",
386 pid
, coalition_id(coal
), t
, pgcount
[t
]);
392 return SYSCTL_OUT(req
, pgcount
, sizeof(pgcount
));
395 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_page_count
, CTLTYPE_QUAD
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
396 0, 0, sysctl_coalition_get_page_count
, "Q", "coalition page count of a specified process");
399 static int sysctl_coalition_get_pid_list SYSCTL_HANDLER_ARGS
401 #pragma unused(oidp, arg1, arg2)
402 int error
, type
, sort_order
, pid
;
406 coalition_t coal
= COALITION_NULL
;
407 proc_t tproc
= PROC_NULL
;
409 int pidlist
[100] = { 0, };
412 error
= SYSCTL_IN(req
, &value
, sizeof(value
));
415 error
= SYSCTL_IN(req
, &value
, sizeof(value
) - sizeof(value
[0]));
420 type
= COALITION_TYPE_RESOURCE
;
421 sort_order
= COALITION_SORT_DEFAULT
;
425 sort_order
= value
[1];
432 if (type
< 0 || type
>= COALITION_NUM_TYPES
)
435 coal_dbg("getting constituent PIDS for coalition of type %d "
436 "containing pid:%d (sort:%d)", type
, pid
, sort_order
);
437 tproc
= proc_find(pid
);
439 coal_dbg("ERROR: Couldn't find pid:%d", pid
);
443 (void)coalition_is_leader(tproc
->task
, type
, &coal
);
444 if (coal
== COALITION_NULL
) {
448 npids
= coalition_get_pid_list(coal
, COALITION_ROLEMASK_ALLROLES
, sort_order
,
449 pidlist
, sizeof(pidlist
) / sizeof(pidlist
[0]));
450 if (npids
> (int)(sizeof(pidlist
) / sizeof(pidlist
[0]))) {
451 coal_dbg("Too many members in coalition %llu (from pid:%d): %d!",
452 coalition_id(coal
), pid
, npids
);
453 npids
= sizeof(pidlist
) / sizeof(pidlist
[0]);
462 return SYSCTL_OUT(req
, pidlist
, sizeof(pidlist
[0]) * npids
);
465 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_pid_list
, CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
466 0, 0, sysctl_coalition_get_pid_list
, "I", "list of PIDS which are members of the coalition of the current process");
469 static int sysctl_coalition_notify SYSCTL_HANDLER_ARGS
471 #pragma unused(oidp, arg1, arg2)
472 int error
, should_set
;
477 error
= SYSCTL_IN(req
, value
, sizeof(value
));
479 error
= SYSCTL_IN(req
, value
, sizeof(value
) - sizeof(value
[0]));
487 coal
= coalition_find_by_id(value
[0]);
488 if (coal
== COALITION_NULL
) {
489 coal_dbg("Can't find coalition with ID:%lld", value
[0]);
494 coalition_set_notify(coal
, (int)value
[1]);
496 value
[0] = (uint64_t)coalition_should_notify(coal
);
498 coalition_release(coal
);
500 return SYSCTL_OUT(req
, value
, sizeof(value
[0]));
503 SYSCTL_PROC(_kern
, OID_AUTO
, coalition_notify
, CTLTYPE_QUAD
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
504 0, 0, sysctl_coalition_notify
, "Q", "get/set coalition notification flag");
506 extern int unrestrict_coalition_syscalls
;
507 SYSCTL_INT(_kern
, OID_AUTO
, unrestrict_coalitions
,
508 CTLFLAG_RW
, &unrestrict_coalition_syscalls
, 0,
509 "unrestrict the coalition interface");
511 #endif /* DEVELOPMENT */
513 #endif /* DEVELOPMENT || DEBUG */