]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/sys_coalition.c
xnu-2782.40.9.tar.gz
[apple/xnu.git] / bsd / kern / sys_coalition.c
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 */
25 static
26 int
27 coalition_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));
53 out:
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 */
70 static
71 int
72 coalition_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 */
132 static
133 int
134 coalition_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 */
181 int 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. */
209 static int __attribute__ ((noinline))
210 coalition_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
235 int 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
278 bad:
279 coalition_release(coal);
280 return error;
281 }