]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/sys_coalition.c
xnu-3247.1.106.tar.gz
[apple/xnu.git] / bsd / kern / sys_coalition.c
CommitLineData
fe8ab488
A
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;
3e170ce0
A
33 int type = COALITION_CREATE_FLAGS_GET_TYPE(flags);
34 boolean_t privileged = !!(flags & COALITION_CREATE_FLAGS_PRIVILEGED);
fe8ab488 35
3e170ce0
A
36 if ((flags & (~COALITION_CREATE_FLAGS_MASK)) != 0)
37 return EINVAL;
38 if (type < 0 || type > COALITION_TYPE_MAX)
fe8ab488 39 return EINVAL;
fe8ab488 40
3e170ce0 41 kr = coalition_create_internal(type, privileged, &coal);
fe8ab488
A
42 if (kr != KERN_SUCCESS) {
43 /* for now, the only kr is KERN_RESOURCE_SHORTAGE */
44 error = ENOMEM;
45 goto out;
46 }
47
48 cid = coalition_id(coal);
49
3e170ce0 50 coal_dbg("(addr, %u) -> %llu", flags, cid);
fe8ab488
A
51 error = copyout(&cid, cidp, sizeof(cid));
52out:
53 return error;
54}
55
56/*
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
61 * count reaches zero.
62 *
63 * Returns:
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.
68 */
69static
70int
71coalition_request_terminate_syscall(user_addr_t cidp, uint32_t flags)
72{
73 kern_return_t kr;
74 int error = 0;
75 uint64_t cid;
76 coalition_t coal;
77
78 if (flags != 0) {
79 return EINVAL;
80 }
81
82 error = copyin(cidp, &cid, sizeof(cid));
83 if (error) {
84 return error;
85 }
86
87 coal = coalition_find_by_id(cid);
88 if (coal == COALITION_NULL) {
89 return ESRCH;
90 }
91
92 kr = coalition_request_terminate_internal(coal);
93 coalition_release(coal);
94
95 switch (kr) {
96 case KERN_SUCCESS:
97 break;
98 case KERN_DEFAULT_SET:
99 error = EPERM;
3e170ce0 100 break;
fe8ab488
A
101 case KERN_TERMINATED:
102 error = EALREADY;
3e170ce0 103 break;
fe8ab488
A
104 case KERN_INVALID_NAME:
105 error = ESRCH;
3e170ce0 106 break;
fe8ab488
A
107 default:
108 error = EIO;
3e170ce0 109 break;
fe8ab488
A
110 }
111
3e170ce0 112 coal_dbg("(%llu, %u) -> %d", cid, flags, error);
fe8ab488
A
113
114 return error;
115}
116
117/*
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.
123 *
124 * Returns:
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
132 */
133static
134int
135coalition_reap_syscall(user_addr_t cidp, uint32_t flags)
136{
137 kern_return_t kr;
138 int error = 0;
139 uint64_t cid;
140 coalition_t coal;
141
142 if (flags != 0) {
143 return EINVAL;
144 }
145
146 error = copyin(cidp, &cid, sizeof(cid));
147 if (error) {
148 return error;
149 }
150
151 coal = coalition_find_by_id(cid);
152 if (coal == COALITION_NULL) {
153 return ESRCH;
154 }
155
156 kr = coalition_reap_internal(coal);
157 coalition_release(coal);
158
159 switch (kr) {
160 case KERN_SUCCESS:
161 break;
162 case KERN_DEFAULT_SET:
163 error = EPERM;
3e170ce0 164 break;
fe8ab488
A
165 case KERN_TERMINATED:
166 error = ESRCH;
3e170ce0 167 break;
fe8ab488
A
168 case KERN_FAILURE:
169 error = EBUSY;
3e170ce0 170 break;
fe8ab488
A
171 default:
172 error = EIO;
3e170ce0 173 break;
fe8ab488
A
174 }
175
3e170ce0 176 coal_dbg("(%llu, %u) -> %d", cid, flags, error);
fe8ab488
A
177
178 return error;
179}
180
181/* Syscall demux.
182 * Returns EPERM if the calling process is not privileged to make this call.
183 */
184int coalition(proc_t p, struct coalition_args *cap, __unused int32_t *retval)
185{
186 uint32_t operation = cap->operation;
187 user_addr_t cidp = cap->cid;
188 uint32_t flags = cap->flags;
189 int error = 0;
3e170ce0 190 int type = COALITION_CREATE_FLAGS_GET_TYPE(flags);
fe8ab488 191
3e170ce0 192 if (!task_is_in_privileged_coalition(p->task, type)) {
fe8ab488
A
193 return EPERM;
194 }
195
196 switch (operation) {
197 case COALITION_OP_CREATE:
198 error = coalition_create_syscall(cidp, flags);
199 break;
200 case COALITION_OP_REAP:
201 error = coalition_reap_syscall(cidp, flags);
202 break;
203 case COALITION_OP_TERMINATE:
204 error = coalition_request_terminate_syscall(cidp, flags);
205 break;
206 default:
207 error = ENOSYS;
208 }
209 return error;
210}
211
212/* This is a temporary interface, likely to be changed by 15385642. */
213static int __attribute__ ((noinline))
214coalition_info_resource_usage(coalition_t coal, user_addr_t buffer, user_size_t bufsize)
215{
216 kern_return_t kr;
217 struct coalition_resource_usage cru;
218
219 if (bufsize != sizeof(cru)) {
220 return EINVAL;
221 }
222
223 kr = coalition_resource_usage_internal(coal, &cru);
224
225 switch (kr) {
226 case KERN_INVALID_ARGUMENT:
227 return EINVAL;
228 case KERN_RESOURCE_SHORTAGE:
229 return ENOMEM;
230 case KERN_SUCCESS:
231 break;
232 default:
233 return EIO; /* shrug */
234 }
235
236 return copyout(&cru, buffer, bufsize);
237}
238
239int coalition_info(proc_t p, struct coalition_info_args *uap, __unused int32_t *retval)
240{
241 user_addr_t cidp = uap->cid;
242 user_addr_t buffer = uap->buffer;
243 user_addr_t bufsizep = uap->bufsize;
244 user_size_t bufsize;
245 uint32_t flavor = uap->flavor;
246 int error;
247 uint64_t cid;
248 coalition_t coal;
249
250 error = copyin(cidp, &cid, sizeof(cid));
251 if (error) {
252 return error;
253 }
254
255 coal = coalition_find_by_id(cid);
256 if (coal == COALITION_NULL) {
257 return ESRCH;
258 }
259 /* TODO: priv check? EPERM or ESRCH? */
260
261 if (IS_64BIT_PROCESS(p)) {
262 user64_size_t size64;
263 error = copyin(bufsizep, &size64, sizeof(size64));
264 bufsize = (user_size_t)size64;
265 } else {
266 user32_size_t size32;
267 error = copyin(bufsizep, &size32, sizeof(size32));
268 bufsize = (user_size_t)size32;
269 }
270 if (error) {
271 goto bad;
272 }
273
274 switch (flavor) {
275 case COALITION_INFO_RESOURCE_USAGE:
276 error = coalition_info_resource_usage(coal, buffer, bufsize);
277 break;
278 default:
279 error = EINVAL;
280 }
281
282bad:
283 coalition_release(coal);
284 return error;
285}
3e170ce0
A
286
287#if defined(DEVELOPMENT) || defined(DEBUG)
288static int sysctl_coalition_get_ids SYSCTL_HANDLER_ARGS
289{
290#pragma unused(oidp, arg1, arg2)
291 int error, pid;
292 proc_t tproc;
293 uint64_t value;
294 uint64_t ids[COALITION_NUM_TYPES];
295
296
297 error = SYSCTL_IN(req, &value, sizeof(value));
298 if (error)
299 return error;
300 if (!req->newptr)
301 pid = req->p->p_pid;
302 else
303 pid = (int)value;
304
305 coal_dbg("looking up coalitions for pid:%d", pid);
306 tproc = proc_find(pid);
307 if (tproc == NULL) {
308 coal_dbg("ERROR: Couldn't find pid:%d", pid);
309 return ESRCH;
310 }
311
312 task_coalition_ids(tproc->task, ids);
313 proc_rele(tproc);
314
315 return SYSCTL_OUT(req, ids, sizeof(ids));
316}
317
318SYSCTL_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");
320
321
322static int sysctl_coalition_get_roles SYSCTL_HANDLER_ARGS
323{
324#pragma unused(oidp, arg1, arg2)
325 int error, pid;
326 proc_t tproc;
327 int value;
328 int roles[COALITION_NUM_TYPES];
329
330
331 error = SYSCTL_IN(req, &value, sizeof(value));
332 if (error)
333 return error;
334 if (!req->newptr)
335 pid = req->p->p_pid;
336 else
337 pid = (int)value;
338
339 coal_dbg("looking up coalitions for pid:%d", pid);
340 tproc = proc_find(pid);
341 if (tproc == NULL) {
342 coal_dbg("ERROR: Couldn't find pid:%d", pid);
343 return ESRCH;
344 }
345
346 task_coalition_roles(tproc->task, roles);
347 proc_rele(tproc);
348
349 return SYSCTL_OUT(req, roles, sizeof(roles));
350}
351
352SYSCTL_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");
354
355
356static int sysctl_coalition_get_page_count SYSCTL_HANDLER_ARGS
357{
358#pragma unused(oidp, arg1, arg2)
359 int error, pid;
360 proc_t tproc;
361 coalition_t coal;
362 uint64_t value;
363 uint64_t pgcount[COALITION_NUM_TYPES];
364
365
366 error = SYSCTL_IN(req, &value, sizeof(value));
367 if (error)
368 return error;
369 if (!req->newptr)
370 pid = req->p->p_pid;
371 else
372 pid = (int)value;
373
374 coal_dbg("looking up coalitions for pid:%d", pid);
375 tproc = proc_find(pid);
376 if (tproc == NULL) {
377 coal_dbg("ERROR: Couldn't find pid:%d", pid);
378 return ESRCH;
379 }
380
381 memset(pgcount, 0, sizeof(pgcount));
382
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) {
387 int ntasks = 0;
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]);
391 }
392 }
393
394 proc_rele(tproc);
395
396 return SYSCTL_OUT(req, pgcount, sizeof(pgcount));
397}
398
399SYSCTL_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");
401
402
403static int sysctl_coalition_get_pid_list SYSCTL_HANDLER_ARGS
404{
405#pragma unused(oidp, arg1, arg2)
406 int error, type, sort_order, pid;
407 int value[3];
408 int has_pid = 1;
409
410 coalition_t coal = COALITION_NULL;
411 proc_t tproc = PROC_NULL;
412 int npids = 0;
413 int pidlist[100] = { 0, };
414
415
416 error = SYSCTL_IN(req, &value, sizeof(value));
417 if (error) {
418 has_pid = 0;
419 error = SYSCTL_IN(req, &value, sizeof(value) - sizeof(value[0]));
420 }
421 if (error)
422 return error;
423 if (!req->newptr) {
424 type = COALITION_TYPE_RESOURCE;
425 sort_order = COALITION_SORT_DEFAULT;
426 pid = req->p->p_pid;
427 } else {
428 type = value[0];
429 sort_order = value[1];
430 if (has_pid)
431 pid = value[2];
432 else
433 pid = req->p->p_pid;
434 }
435
436 if (type < 0 || type >= COALITION_NUM_TYPES)
437 return EINVAL;
438
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);
442 if (tproc == NULL) {
443 coal_dbg("ERROR: Couldn't find pid:%d", pid);
444 return ESRCH;
445 }
446
447 (void)coalition_is_leader(tproc->task, type, &coal);
448 if (coal == COALITION_NULL) {
449 goto out;
450 }
451
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]);
458 }
459
460out:
461 proc_rele(tproc);
462
463 if (npids == 0)
464 return ENOENT;
465
466 return SYSCTL_OUT(req, pidlist, sizeof(pidlist[0]) * npids);
467}
468
469SYSCTL_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");
471
472#if DEVELOPMENT
473static int sysctl_coalition_notify SYSCTL_HANDLER_ARGS
474{
475#pragma unused(oidp, arg1, arg2)
476 int error, should_set;
477 coalition_t coal;
478 uint64_t value[2];
479
480 should_set = 1;
481 error = SYSCTL_IN(req, value, sizeof(value));
482 if (error) {
483 error = SYSCTL_IN(req, value, sizeof(value) - sizeof(value[0]));
484 if (error)
485 return error;
486 should_set = 0;
487 }
488 if (!req->newptr)
489 return error;
490
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]);
494 return ESRCH;
495 }
496
497 if (should_set)
498 coalition_set_notify(coal, (int)value[1]);
499
500 value[0] = (uint64_t)coalition_should_notify(coal);
501
502 coalition_release(coal);
503
504 return SYSCTL_OUT(req, value, sizeof(value[0]));
505}
506
507SYSCTL_PROC(_kern, OID_AUTO, coalition_notify, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
508 0, 0, sysctl_coalition_notify, "Q", "get/set coalition notification flag");
509
510extern int unrestrict_coalition_syscalls;
511SYSCTL_INT(_kern, OID_AUTO, unrestrict_coalitions,
512 CTLFLAG_RW, &unrestrict_coalition_syscalls, 0,
513 "unrestrict the coalition interface");
514
515#endif /* DEVELOPMENT */
516
517#endif /* DEVELOPMENT || DEBUG */