xref: /xnu-11215.41.3/bsd/skywalk/nexus/nexus_syscalls.c (revision 33de042d024d46de5ff4e89f2471de6608e37fa4)
1 /*
2  * Copyright (c) 2015-2022 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/filedesc.h>
32 #include <sys/proc_internal.h>
33 #include <sys/file_internal.h>
34 #include <sys/vnode_internal.h>
35 #include <sys/sysproto.h>
36 #include <security/audit/audit.h>
37 #include <skywalk/os_skywalk_private.h>
38 
39 static int nxop_ioctl(struct fileproc *, u_long, caddr_t, vfs_context_t);
40 static int nxop_close(struct fileglob *, vfs_context_t);
41 
42 static const struct fileops nexus_ctl_ops = {
43 	.fo_type     = DTYPE_NEXUS,
44 	.fo_read     = fo_no_read,
45 	.fo_write    = fo_no_write,
46 	.fo_ioctl    = nxop_ioctl,
47 	.fo_select   = fo_no_select,
48 	.fo_close    = nxop_close,
49 	.fo_drain    = fo_no_drain,
50 	.fo_kqfilter = fo_no_kqfilter,
51 };
52 
53 static int
nxop_ioctl(struct fileproc * fp,u_long cmd,caddr_t data,vfs_context_t ctx)54 nxop_ioctl(struct fileproc *fp, u_long cmd, caddr_t data, vfs_context_t ctx)
55 {
56 	struct nxctl *nxctl;
57 	proc_t procp = vfs_context_proc(ctx);
58 
59 	if ((nxctl = (struct nxctl *)fp_get_data(fp)) == NULL) {
60 		/* This is not a valid open file descriptor */
61 		return EBADF;
62 	}
63 	return nxioctl(nxctl, cmd, data, procp);
64 }
65 
66 static int
nxop_close(struct fileglob * fg,vfs_context_t ctx)67 nxop_close(struct fileglob *fg, vfs_context_t ctx)
68 {
69 #pragma unused(ctx)
70 	struct nxctl *nxctl;
71 	int error = 0;
72 
73 	nxctl = (struct nxctl *)fg_get_data(fg);
74 	fg_set_data(fg, NULL);
75 	if (nxctl != NULL) {
76 		nxctl_dtor(nxctl);
77 	}
78 
79 	return error;
80 }
81 
82 int
__nexus_open(struct proc * p,struct __nexus_open_args * uap,int * retval)83 __nexus_open(struct proc *p, struct __nexus_open_args *uap, int *retval)
84 {
85 	struct nxctl *nxctl = NULL;
86 	struct fileproc *__single fp = NULL;
87 	struct nxctl_init init;
88 	uuid_t nxctl_uuid;
89 	int fd = -1, err = 0;
90 	guardid_t guard;
91 
92 	if (__improbable(uap->init == USER_ADDR_NULL ||
93 	    uap->init_len < sizeof(init))) {
94 		SK_DSC(p, "EINVAL: init %p, init_len %u", uap->init,
95 		    uap->init_len);
96 		err = EINVAL;
97 		goto done;
98 	}
99 
100 	err = copyin(uap->init, (caddr_t)&init, sizeof(init));
101 	if (__improbable(err != 0)) {
102 		SK_DSC(p, "copyin err %d, init 0x%llx", err, SK_KVA(uap->init));
103 		goto done;
104 	}
105 
106 	if (__improbable(init.ni_version != NEXUSCTL_INIT_CURRENT_VERSION)) {
107 		SK_DSC(p, "ENOTSUP: version %u != %u", init.ni_version,
108 		    NEXUSCTL_INIT_CURRENT_VERSION);
109 		err = ENOTSUP;
110 		goto done;
111 	}
112 
113 	/* generate guard ID based on nexus controller UUID */
114 	uuid_generate_random(nxctl_uuid);
115 	sk_gen_guard_id(FALSE, nxctl_uuid, &guard);
116 
117 	err = falloc_guarded(p, &fp, &fd, vfs_context_current(), &guard,
118 	    GUARD_CLOSE | GUARD_DUP | GUARD_SOCKET_IPC | GUARD_FILEPORT | GUARD_WRITE);
119 	if (__improbable(err != 0)) {
120 		SK_DSC(p, "falloc_guarded err %d", err);
121 		goto done;
122 	}
123 
124 	nxctl = nxctl_create(p, fp, nxctl_uuid, &err);
125 	if (__improbable(nxctl == NULL)) {
126 		ASSERT(err != 0);
127 		SK_DSC(p, "nxctl_create err %d", err);
128 		goto done;
129 	}
130 
131 	/* update userland with respect to guard ID, etc. */
132 	init.ni_guard = guard;
133 	err = copyout(&init, uap->init, sizeof(init));
134 	if (__improbable(err != 0)) {
135 		SK_DSC(p, "copyout err %d, init 0x%llx", err,
136 		    SK_KVA(uap->init));
137 		goto done;
138 	}
139 
140 	fp->fp_flags |= FP_CLOEXEC | FP_CLOFORK;
141 	fp->fp_glob->fg_flag |= (FREAD | FWRITE);
142 	fp->fp_glob->fg_ops = &nexus_ctl_ops;
143 	fp_set_data(fp, nxctl);   /* ref from nxctl_create */
144 
145 	proc_fdlock(p);
146 	procfdtbl_releasefd(p, fd, NULL);
147 	fp_drop(p, fd, fp, 1);
148 	proc_fdunlock(p);
149 
150 	*retval = fd;
151 
152 	SK_D("%s(%d) fd %d guard 0x%llx",
153 	    sk_proc_name_address(p), sk_proc_pid(p), fd, guard);
154 
155 done:
156 	if (__improbable(err != 0)) {
157 		if (nxctl != NULL) {
158 			nxctl_dtor(nxctl);
159 			nxctl = NULL;
160 		}
161 		if (fp != NULL) {
162 			fp_free(p, fd, fp);
163 			fp = NULL;
164 		}
165 	}
166 
167 	return err;
168 }
169 
170 int
__nexus_register(struct proc * p,struct __nexus_register_args * uap,int * retval)171 __nexus_register(struct proc *p, struct __nexus_register_args *uap, int *retval)
172 {
173 #pragma unused(retval)
174 	struct fileproc *__single fp;
175 	struct kern_nexus_provider *nxprov = NULL;
176 	struct nxctl *nxctl;
177 	struct nxprov_reg reg;
178 	int err = 0;
179 
180 	AUDIT_ARG(fd, uap->ctl);
181 
182 	if (__improbable(uap->reg == USER_ADDR_NULL ||
183 	    uap->reg_len < sizeof(reg) || uap->prov_uuid == USER_ADDR_NULL ||
184 	    uap->prov_uuid_len < sizeof(uuid_t))) {
185 		SK_DSC(p, "EINVAL: reg 0x%llx, reg_len %u, prov_uuid 0x%llx, "
186 		    "prov_uuid_len %u", SK_KVA(uap->reg), uap->reg_len,
187 		    SK_KVA(uap->prov_uuid), uap->prov_uuid_len);
188 		return EINVAL;
189 	}
190 
191 	err = copyin(uap->reg, (caddr_t)&reg, sizeof(reg));
192 	if (err != 0) {
193 		SK_DSC(p, "copyin err %d, reg 0x%llx", err, SK_KVA(uap->reg));
194 		return err;
195 	}
196 
197 	if (__improbable(reg.nxpreg_version != NXPROV_REG_CURRENT_VERSION)) {
198 		SK_DSC(p, "EINVAL: version %u != %u", reg.nxpreg_version,
199 		    NXPROV_REG_CURRENT_VERSION);
200 		return EINVAL;
201 	}
202 
203 	if (__improbable(reg.nxpreg_params.nxp_namelen == 0 ||
204 	    reg.nxpreg_params.nxp_namelen > sizeof(nexus_name_t))) {
205 		SK_DSC(p, "EINVAL: namelen %u", reg.nxpreg_params.nxp_namelen);
206 		return EINVAL;
207 	}
208 
209 	err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
210 	if (__improbable(err != 0)) {
211 		SK_DSC(p, "fp_get_ftype: %d", err);
212 		return err;
213 	}
214 	nxctl = (struct nxctl *)fp_get_data(fp);
215 
216 	lck_mtx_lock(&nxctl->nxctl_lock);
217 	nxprov = nxprov_create(p, nxctl, &reg, &err);
218 	lck_mtx_unlock(&nxctl->nxctl_lock);
219 	if (__improbable(nxprov == NULL)) {
220 		ASSERT(err != 0);
221 		SK_DSC(p, "nxprov_create: %d", err);
222 		goto done;
223 	}
224 
225 	err = copyout(&nxprov->nxprov_uuid, uap->prov_uuid, sizeof(uuid_t));
226 	if (__improbable(err != 0)) {
227 		SK_DSC(p, "copyout err %d, prov_uuid 0x%llx", err,
228 		    SK_KVA(uap->prov_uuid));
229 		goto done;
230 	}
231 
232 done:
233 	fp_drop(p, uap->ctl, fp, 0);
234 
235 	if (__improbable(err != 0 && nxprov != NULL)) {
236 		err = nxprov_close(nxprov, FALSE);
237 	}
238 
239 	/* release extra ref from nxprov_create */
240 	if (nxprov != NULL) {
241 		nxprov_release(nxprov);
242 	}
243 
244 	return err;
245 }
246 
247 int
__nexus_deregister(struct proc * p,struct __nexus_deregister_args * uap,int * retval)248 __nexus_deregister(struct proc *p, struct __nexus_deregister_args *uap,
249     int *retval)
250 {
251 #pragma unused(retval)
252 	struct fileproc *__single fp;
253 	struct nxctl *nxctl = NULL;
254 	uuid_t nxprov_uuid;
255 	int err = 0;
256 
257 	AUDIT_ARG(fd, uap->ctl);
258 
259 	if (__improbable(uap->prov_uuid_len < sizeof(uuid_t))) {
260 		SK_DSC(p, "EINVAL: prov_len %u < %u", uap->prov_uuid_len,
261 		    sizeof(uuid_t));
262 		return EINVAL;
263 	}
264 
265 	err = copyin(uap->prov_uuid, (caddr_t)&nxprov_uuid, sizeof(uuid_t));
266 	if (__improbable(err != 0)) {
267 		SK_DSC(p, "copyin err %d, prov_uuid 0x%llx", err,
268 		    SK_KVA(uap->prov_uuid));
269 		return err;
270 	}
271 
272 	if (__improbable(uuid_is_null(nxprov_uuid))) {
273 		SK_DSC(p, "EINVAL: uuid_is_null");
274 		return EINVAL;
275 	}
276 
277 	err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
278 	if (__improbable(err != 0)) {
279 		SK_DSC(p, "fp_get_ftype: %d", err);
280 		return err;
281 	}
282 	nxctl = (struct nxctl *)fp_get_data(fp);
283 
284 	lck_mtx_lock(&nxctl->nxctl_lock);
285 	err = nxprov_destroy(nxctl, nxprov_uuid);
286 	lck_mtx_unlock(&nxctl->nxctl_lock);
287 
288 	fp_drop(p, uap->ctl, fp, 0);
289 
290 	return err;
291 }
292 
293 int
__nexus_create(struct proc * p,struct __nexus_create_args * uap,int * retval)294 __nexus_create(struct proc *p, struct __nexus_create_args *uap, int *retval)
295 {
296 #pragma unused(retval)
297 	struct fileproc *__single fp;
298 	struct kern_nexus *nx = NULL;
299 	struct nxctl *nxctl = NULL;
300 	uuid_t nxprov_uuid;
301 	int err = 0;
302 
303 	AUDIT_ARG(fd, uap->ctl);
304 
305 	if (__improbable(uap->prov_uuid_len < sizeof(uuid_t) ||
306 	    uap->nx_uuid_len < sizeof(uuid_t) ||
307 	    uap->nx_uuid == USER_ADDR_NULL)) {
308 		SK_DSC(p, "EINVAL: prov_uuid_len %u, nx_uuid_len %u, "
309 		    "nx_uuid 0x%llx", uap->prov_uuid_len, uap->nx_uuid_len,
310 		    SK_KVA(uap->nx_uuid));
311 		return EINVAL;
312 	}
313 
314 	err = copyin(uap->prov_uuid, (caddr_t)&nxprov_uuid, sizeof(uuid_t));
315 	if (__improbable(err != 0)) {
316 		SK_DSC(p, "copyin err %d, prov_uuid 0x%llx", err,
317 		    SK_KVA(uap->prov_uuid));
318 		return err;
319 	}
320 
321 	if (__improbable(uuid_is_null(nxprov_uuid))) {
322 		SK_DSC(p, "EINVAL: uuid_is_null");
323 		return EINVAL;
324 	}
325 
326 	err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
327 	if (__improbable(err != 0)) {
328 		SK_DSC(p, "fp_get_ftype: %d", err);
329 		return err;
330 	}
331 	nxctl = (struct nxctl *)fp_get_data(fp);
332 
333 	lck_mtx_lock(&nxctl->nxctl_lock);
334 	nx = nx_create(nxctl, nxprov_uuid, NEXUS_TYPE_UNDEFINED, NULL, NULL,
335 	    NULL, NULL, &err);
336 	lck_mtx_unlock(&nxctl->nxctl_lock);
337 	if (__improbable(nx == NULL)) {
338 		ASSERT(err != 0);
339 		SK_DSC(p, "nx_create: %d", err);
340 		goto done;
341 	}
342 	err = copyout(&nx->nx_uuid, uap->nx_uuid, sizeof(uuid_t));
343 	if (__improbable(err != 0)) {
344 		SK_DSC(p, "copyout err %d, nx_uuid 0x%llx", err,
345 		    SK_KVA(uap->nx_uuid));
346 		goto done;
347 	}
348 
349 done:
350 	fp_drop(p, uap->ctl, fp, 0);
351 
352 	/* release extra ref from nx_create */
353 	if (nx != NULL) {
354 		(void) nx_release(nx);
355 	}
356 
357 	return err;
358 }
359 
360 int
__nexus_destroy(struct proc * p,struct __nexus_destroy_args * uap,int * retval)361 __nexus_destroy(struct proc *p, struct __nexus_destroy_args *uap, int *retval)
362 {
363 #pragma unused(retval)
364 	struct fileproc *__single fp;
365 	struct nxctl *nxctl = NULL;
366 	int err = 0;
367 	uuid_t nx_uuid;
368 
369 	AUDIT_ARG(fd, uap->ctl);
370 
371 	if (__improbable(uap->nx_uuid == USER_ADDR_NULL ||
372 	    uap->nx_uuid_len < sizeof(uuid_t))) {
373 		SK_DSC(p, "EINVAL: nx_uuid 0x%llx, nx_uuid_len %u",
374 		    SK_KVA(uap->nx_uuid), uap->nx_uuid_len);
375 		return EINVAL;
376 	}
377 
378 	err = copyin(uap->nx_uuid, (caddr_t)&nx_uuid, sizeof(uuid_t));
379 	if (__improbable(err != 0)) {
380 		SK_DSC(p, "copyin err %d, nx_uuid 0x%llx", err,
381 		    SK_KVA(uap->nx_uuid));
382 		return err;
383 	}
384 
385 	if (__improbable(uuid_is_null(nx_uuid))) {
386 		SK_DSC(p, "EINVAL: uuid_is_null");
387 		return EINVAL;
388 	}
389 
390 	err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
391 	if (__improbable(err != 0)) {
392 		SK_DSC(p, "fp_get_ftype: %d", err);
393 		return err;
394 	}
395 	nxctl = (struct nxctl *)fp_get_data(fp);
396 
397 	lck_mtx_lock(&nxctl->nxctl_lock);
398 	err = nx_destroy(nxctl, nx_uuid);
399 	lck_mtx_unlock(&nxctl->nxctl_lock);
400 
401 	fp_drop(p, uap->ctl, fp, 0);
402 
403 	return err;
404 }
405 
406 int
__nexus_get_opt(struct proc * p,struct __nexus_get_opt_args * uap,int * retval)407 __nexus_get_opt(struct proc *p, struct __nexus_get_opt_args *uap, int *retval)
408 {
409 #pragma unused(retval)
410 	struct fileproc *__single fp;
411 	struct nxctl *nxctl = NULL;
412 	struct sockopt sopt;
413 	uint32_t optlen;
414 	int err = 0;
415 
416 	AUDIT_ARG(fd, uap->ctl);
417 
418 	err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
419 	if (__improbable(err != 0)) {
420 		SK_DSC(p, "fp_get_ftype: %d", err);
421 		return err;
422 	}
423 	nxctl = (struct nxctl *)fp_get_data(fp);
424 
425 	if (__improbable(uap->aoptlen == USER_ADDR_NULL)) {
426 		SK_DSC(p, "EINVAL: aoptlen == USER_ADDR_NULL");
427 		err = EINVAL;
428 		goto done;
429 	}
430 
431 	if (uap->aoptval != USER_ADDR_NULL) {
432 		err = copyin(uap->aoptlen, &optlen, sizeof(optlen));
433 		if (__improbable(err != 0)) {
434 			SK_DSC(p, "copyin err %d, aoptlen 0x%llx", err,
435 			    SK_KVA(uap->aoptlen));
436 			goto done;
437 		}
438 	} else {
439 		optlen = 0;
440 	}
441 
442 	bzero(&sopt, sizeof(sopt));
443 	sopt.sopt_dir = SOPT_GET;
444 	sopt.sopt_name = uap->opt;
445 	sopt.sopt_val = uap->aoptval;
446 	sopt.sopt_valsize = optlen;
447 	sopt.sopt_p = p;
448 
449 	lck_mtx_lock(&nxctl->nxctl_lock);
450 	err = nxctl_get_opt(nxctl, &sopt);
451 	lck_mtx_unlock(&nxctl->nxctl_lock);
452 	if (__probable(err == 0)) {
453 		optlen = (uint32_t)sopt.sopt_valsize;
454 		err = copyout(&optlen, uap->aoptlen, sizeof(optlen));
455 #if SK_LOG
456 		if (__improbable(err != 0)) {
457 			SK_DSC(p, "copyout err %d, aoptlen 0x%llx", err,
458 			    SK_KVA(uap->aoptlen));
459 		}
460 #endif /* SK_LOG */
461 	}
462 
463 done:
464 	fp_drop(p, uap->ctl, fp, 0);
465 
466 	return err;
467 }
468 
469 int
__nexus_set_opt(struct proc * p,struct __nexus_set_opt_args * uap,int * retval)470 __nexus_set_opt(struct proc *p, struct __nexus_set_opt_args *uap, int *retval)
471 {
472 #pragma unused(retval)
473 	struct fileproc *__single fp;
474 	struct nxctl *nxctl = NULL;
475 	struct sockopt sopt;
476 	int err = 0;
477 
478 	bzero(&sopt, sizeof(sopt));
479 	sopt.sopt_dir = SOPT_SET;
480 	sopt.sopt_name = uap->opt;
481 	sopt.sopt_val = uap->aoptval;
482 	sopt.sopt_valsize = uap->optlen;
483 	sopt.sopt_p = p;
484 
485 	if (uap->ctl != __OS_NEXUS_SHARED_USER_CONTROLLER_FD) {
486 		AUDIT_ARG(fd, uap->ctl);
487 
488 		err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
489 		if (__improbable(err != 0)) {
490 			SK_DSC(p, "fp_get_ftype: %d", err);
491 			return err;
492 		}
493 		nxctl = (struct nxctl *)fp_get_data(fp);
494 
495 		lck_mtx_lock(&nxctl->nxctl_lock);
496 		err = nxctl_set_opt(nxctl, &sopt);
497 		lck_mtx_unlock(&nxctl->nxctl_lock);
498 
499 		fp_drop(p, uap->ctl, fp, 0);
500 	} else {
501 		/* opt that don't have nxctl uses shared user nxctl */
502 		nxctl = usernxctl.ncd_nxctl;
503 
504 		lck_mtx_lock(&nxctl->nxctl_lock);
505 		err = nxctl_set_opt(nxctl, &sopt);
506 		lck_mtx_unlock(&nxctl->nxctl_lock);
507 	}
508 	return err;
509 }
510