X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/143464d58d2bd6378e74eec636961ceb0d32fb91..fe8ab488e9161c46dd9885d58fc52996dc0249ff:/bsd/kern/sys_coalition.c diff --git a/bsd/kern/sys_coalition.c b/bsd/kern/sys_coalition.c new file mode 100644 index 000000000..3255fb0d6 --- /dev/null +++ b/bsd/kern/sys_coalition.c @@ -0,0 +1,281 @@ +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +/* Coalitions syscalls */ + +/* + * Create a new, empty coalition and return its ID. + * + * Returns: + * EINVAL Flags parameter was invalid + * ENOMEM Unable to allocate kernel resources for a new coalition + * EFAULT cidp parameter pointed to invalid memory. + * + * Returns with reference held for userspace caller. + */ +static +int +coalition_create_syscall(user_addr_t cidp, uint32_t flags) +{ + int error = 0; + kern_return_t kr; + uint64_t cid; + coalition_t coal; + + if ((flags & (~COALITION_CREATE_FLAG_MASK)) != 0) { + return EINVAL; + } + + boolean_t privileged = flags & COALITION_CREATE_FLAG_PRIVILEGED; + + kr = coalition_create_internal(&coal, privileged); + if (kr != KERN_SUCCESS) { + /* for now, the only kr is KERN_RESOURCE_SHORTAGE */ + error = ENOMEM; + goto out; + } + + cid = coalition_id(coal); + +#if COALITION_DEBUG + printf("%s(addr, %u) -> %llu\n", __func__, flags, cid); +#endif + error = copyout(&cid, cidp, sizeof(cid)); +out: + return error; +} + +/* + * Request to terminate the coalition identified by ID. + * Attempts to spawn into this coalition using the posix_spawnattr will begin + * failing. Processes already within the coalition may still fork. + * Arms the 'coalition is empty' notification when the coalition's active + * count reaches zero. + * + * Returns: + * ESRCH No coalition with that ID could be found. + * EALREADY The coalition with that ID has already been terminated. + * EFAULT cidp parameter pointed to invalid memory. + * EPERM Caller doesn't have permission to terminate that coalition. + */ +static +int +coalition_request_terminate_syscall(user_addr_t cidp, uint32_t flags) +{ + kern_return_t kr; + int error = 0; + uint64_t cid; + coalition_t coal; + + if (flags != 0) { + return EINVAL; + } + + error = copyin(cidp, &cid, sizeof(cid)); + if (error) { + return error; + } + + coal = coalition_find_by_id(cid); + if (coal == COALITION_NULL) { + return ESRCH; + } + + kr = coalition_request_terminate_internal(coal); + coalition_release(coal); + + switch (kr) { + case KERN_SUCCESS: + break; + case KERN_DEFAULT_SET: + error = EPERM; + case KERN_TERMINATED: + error = EALREADY; + case KERN_INVALID_NAME: + error = ESRCH; + default: + error = EIO; + } + +#if COALITION_DEBUG + printf("%s(%llu, %u) -> %d\n", __func__, cid, flags, error); +#endif + + return error; +} + +/* + * Request the kernel to deallocate the coalition identified by ID, which + * must be both terminated and empty. This balances the reference taken + * in coalition_create. + * The memory containig the coalition object may not be freed just yet, if + * other kernel operations still hold references to it. + * + * Returns: + * EINVAL Flags parameter was invalid + * ESRCH Coalition ID refers to a coalition that doesn't exist. + * EBUSY Coalition has not yet been terminated. + * EBUSY Coalition is still active. + * EFAULT cidp parameter pointed to invalid memory. + * EPERM Caller doesn't have permission to terminate that coalition. + * Consumes one reference, "held" by caller since coalition_create + */ +static +int +coalition_reap_syscall(user_addr_t cidp, uint32_t flags) +{ + kern_return_t kr; + int error = 0; + uint64_t cid; + coalition_t coal; + + if (flags != 0) { + return EINVAL; + } + + error = copyin(cidp, &cid, sizeof(cid)); + if (error) { + return error; + } + + coal = coalition_find_by_id(cid); + if (coal == COALITION_NULL) { + return ESRCH; + } + + kr = coalition_reap_internal(coal); + coalition_release(coal); + + switch (kr) { + case KERN_SUCCESS: + break; + case KERN_DEFAULT_SET: + error = EPERM; + case KERN_TERMINATED: + error = ESRCH; + case KERN_FAILURE: + error = EBUSY; + default: + error = EIO; + } + +#if COALITION_DEBUG + printf("%s(%llu, %u) -> %d\n", __func__, cid, flags, error); +#endif + + return error; +} + +/* Syscall demux. + * Returns EPERM if the calling process is not privileged to make this call. + */ +int coalition(proc_t p, struct coalition_args *cap, __unused int32_t *retval) +{ + uint32_t operation = cap->operation; + user_addr_t cidp = cap->cid; + uint32_t flags = cap->flags; + int error = 0; + + if (!task_is_in_privileged_coalition(p->task)) { + return EPERM; + } + + switch (operation) { + case COALITION_OP_CREATE: + error = coalition_create_syscall(cidp, flags); + break; + case COALITION_OP_REAP: + error = coalition_reap_syscall(cidp, flags); + break; + case COALITION_OP_TERMINATE: + error = coalition_request_terminate_syscall(cidp, flags); + break; + default: + error = ENOSYS; + } + return error; +} + +/* This is a temporary interface, likely to be changed by 15385642. */ +static int __attribute__ ((noinline)) +coalition_info_resource_usage(coalition_t coal, user_addr_t buffer, user_size_t bufsize) +{ + kern_return_t kr; + struct coalition_resource_usage cru; + + if (bufsize != sizeof(cru)) { + return EINVAL; + } + + kr = coalition_resource_usage_internal(coal, &cru); + + switch (kr) { + case KERN_INVALID_ARGUMENT: + return EINVAL; + case KERN_RESOURCE_SHORTAGE: + return ENOMEM; + case KERN_SUCCESS: + break; + default: + return EIO; /* shrug */ + } + + return copyout(&cru, buffer, bufsize); +} + +int coalition_info(proc_t p, struct coalition_info_args *uap, __unused int32_t *retval) +{ + user_addr_t cidp = uap->cid; + user_addr_t buffer = uap->buffer; + user_addr_t bufsizep = uap->bufsize; + user_size_t bufsize; + uint32_t flavor = uap->flavor; + int error; + uint64_t cid; + coalition_t coal; + + error = copyin(cidp, &cid, sizeof(cid)); + if (error) { + return error; + } + + coal = coalition_find_by_id(cid); + if (coal == COALITION_NULL) { + return ESRCH; + } + /* TODO: priv check? EPERM or ESRCH? */ + + if (IS_64BIT_PROCESS(p)) { + user64_size_t size64; + error = copyin(bufsizep, &size64, sizeof(size64)); + bufsize = (user_size_t)size64; + } else { + user32_size_t size32; + error = copyin(bufsizep, &size32, sizeof(size32)); + bufsize = (user_size_t)size32; + } + if (error) { + goto bad; + } + + switch (flavor) { + case COALITION_INFO_RESOURCE_USAGE: + error = coalition_info_resource_usage(coal, buffer, bufsize); + break; + default: + error = EINVAL; + } + +bad: + coalition_release(coal); + return error; +}