1#include <kern/kern_types.h> 2#include <mach/mach_types.h> 3#include <mach/boolean.h> 4 5#include <kern/coalition.h> 6 7#include <sys/coalition.h> 8#include <sys/errno.h> 9#include <sys/kernel.h> 10#include <sys/sysproto.h> 11#include <sys/systm.h> 12 13/* Coalitions syscalls */ 14 15/* 16 * Create a new, empty coalition and return its ID. 17 * 18 * Returns: 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. 22 * 23 * Returns with reference held for userspace caller. 24 */ 25static 26int 27coalition_create_syscall(user_addr_t cidp, uint32_t flags) 28{ 29 int error = 0; 30 kern_return_t kr; 31 uint64_t cid; 32 coalition_t coal; 33 34 if ((flags & (~COALITION_CREATE_FLAG_MASK)) != 0) { 35 return EINVAL; 36 } 37 38 boolean_t privileged = flags & COALITION_CREATE_FLAG_PRIVILEGED; 39 40 kr = coalition_create_internal(&coal, privileged); 41 if (kr != KERN_SUCCESS) { 42 /* for now, the only kr is KERN_RESOURCE_SHORTAGE */ 43 error = ENOMEM; 44 goto out; 45 } 46 47 cid = coalition_id(coal); 48 49#if COALITION_DEBUG 50 printf("%s(addr, %u) -> %llu\n", __func__, flags, cid); 51#endif 52 error = copyout(&cid, cidp, sizeof(cid)); 53out: 54 return error; 55} 56 57/* 58 * Request to terminate the coalition identified by ID. 59 * Attempts to spawn into this coalition using the posix_spawnattr will begin 60 * failing. Processes already within the coalition may still fork. 61 * Arms the 'coalition is empty' notification when the coalition's active 62 * count reaches zero. 63 * 64 * Returns: 65 * ESRCH No coalition with that ID could be found. 66 * EALREADY The coalition with that ID has already been terminated. 67 * EFAULT cidp parameter pointed to invalid memory. 68 * EPERM Caller doesn't have permission to terminate that coalition. 69 */ 70static 71int 72coalition_request_terminate_syscall(user_addr_t cidp, uint32_t flags) 73{ 74 kern_return_t kr; 75 int error = 0; 76 uint64_t cid; 77 coalition_t coal; 78 79 if (flags != 0) { 80 return EINVAL; 81 } 82 83 error = copyin(cidp, &cid, sizeof(cid)); 84 if (error) { 85 return error; 86 } 87 88 coal = coalition_find_by_id(cid); 89 if (coal == COALITION_NULL) { 90 return ESRCH; 91 } 92 93 kr = coalition_request_terminate_internal(coal); 94 coalition_release(coal); 95 96 switch (kr) { 97 case KERN_SUCCESS: 98 break; 99 case KERN_DEFAULT_SET: 100 error = EPERM; 101 case KERN_TERMINATED: 102 error = EALREADY; 103 case KERN_INVALID_NAME: 104 error = ESRCH; 105 default: 106 error = EIO; 107 } 108 109#if COALITION_DEBUG 110 printf("%s(%llu, %u) -> %d\n", __func__, cid, flags, error); 111#endif 112 113 return error; 114} 115 116/* 117 * Request the kernel to deallocate the coalition identified by ID, which 118 * must be both terminated and empty. This balances the reference taken 119 * in coalition_create. 120 * The memory containig the coalition object may not be freed just yet, if 121 * other kernel operations still hold references to it. 122 * 123 * Returns: 124 * EINVAL Flags parameter was invalid 125 * ESRCH Coalition ID refers to a coalition that doesn't exist. 126 * EBUSY Coalition has not yet been terminated. 127 * EBUSY Coalition is still active. 128 * EFAULT cidp parameter pointed to invalid memory. 129 * EPERM Caller doesn't have permission to terminate that coalition. 130 * Consumes one reference, "held" by caller since coalition_create 131 */ 132static 133int 134coalition_reap_syscall(user_addr_t cidp, uint32_t flags) 135{ 136 kern_return_t kr; 137 int error = 0; 138 uint64_t cid; 139 coalition_t coal; 140 141 if (flags != 0) { 142 return EINVAL; 143 } 144 145 error = copyin(cidp, &cid, sizeof(cid)); 146 if (error) { 147 return error; 148 } 149 150 coal = coalition_find_by_id(cid); 151 if (coal == COALITION_NULL) { 152 return ESRCH; 153 } 154 155 kr = coalition_reap_internal(coal); 156 coalition_release(coal); 157 158 switch (kr) { 159 case KERN_SUCCESS: 160 break; 161 case KERN_DEFAULT_SET: 162 error = EPERM; 163 case KERN_TERMINATED: 164 error = ESRCH; 165 case KERN_FAILURE: 166 error = EBUSY; 167 default: 168 error = EIO; 169 } 170 171#if COALITION_DEBUG 172 printf("%s(%llu, %u) -> %d\n", __func__, cid, flags, error); 173#endif 174 175 return error; 176} 177 178/* Syscall demux. 179 * Returns EPERM if the calling process is not privileged to make this call. 180 */ 181int coalition(proc_t p, struct coalition_args *cap, __unused int32_t *retval) 182{ 183 uint32_t operation = cap->operation; 184 user_addr_t cidp = cap->cid; 185 uint32_t flags = cap->flags; 186 int error = 0; 187 188 if (!task_is_in_privileged_coalition(p->task)) { 189 return EPERM; 190 } 191 192 switch (operation) { 193 case COALITION_OP_CREATE: 194 error = coalition_create_syscall(cidp, flags); 195 break; 196 case COALITION_OP_REAP: 197 error = coalition_reap_syscall(cidp, flags); 198 break; 199 case COALITION_OP_TERMINATE: 200 error = coalition_request_terminate_syscall(cidp, flags); 201 break; 202 default: 203 error = ENOSYS; 204 } 205 return error; 206} 207 208/* This is a temporary interface, likely to be changed by 15385642. */ 209static int __attribute__ ((noinline)) 210coalition_info_resource_usage(coalition_t coal, user_addr_t buffer, user_size_t bufsize) 211{ 212 kern_return_t kr; 213 struct coalition_resource_usage cru; 214 215 if (bufsize != sizeof(cru)) { 216 return EINVAL; 217 } 218 219 kr = coalition_resource_usage_internal(coal, &cru); 220 221 switch (kr) { 222 case KERN_INVALID_ARGUMENT: 223 return EINVAL; 224 case KERN_RESOURCE_SHORTAGE: 225 return ENOMEM; 226 case KERN_SUCCESS: 227 break; 228 default: 229 return EIO; /* shrug */ 230 } 231 232 return copyout(&cru, buffer, bufsize); 233} 234 235int coalition_info(proc_t p, struct coalition_info_args *uap, __unused int32_t *retval) 236{ 237 user_addr_t cidp = uap->cid; 238 user_addr_t buffer = uap->buffer; 239 user_addr_t bufsizep = uap->bufsize; 240 user_size_t bufsize; 241 uint32_t flavor = uap->flavor; 242 int error; 243 uint64_t cid; 244 coalition_t coal; 245 246 error = copyin(cidp, &cid, sizeof(cid)); 247 if (error) { 248 return error; 249 } 250 251 coal = coalition_find_by_id(cid); 252 if (coal == COALITION_NULL) { 253 return ESRCH; 254 } 255 /* TODO: priv check? EPERM or ESRCH? */ 256 257 if (IS_64BIT_PROCESS(p)) { 258 user64_size_t size64; 259 error = copyin(bufsizep, &size64, sizeof(size64)); 260 bufsize = (user_size_t)size64; 261 } else { 262 user32_size_t size32; 263 error = copyin(bufsizep, &size32, sizeof(size32)); 264 bufsize = (user_size_t)size32; 265 } 266 if (error) { 267 goto bad; 268 } 269 270 switch (flavor) { 271 case COALITION_INFO_RESOURCE_USAGE: 272 error = coalition_info_resource_usage(coal, buffer, bufsize); 273 break; 274 default: 275 error = EINVAL; 276 } 277 278bad: 279 coalition_release(coal); 280 return error; 281} 282