xref: /xnu-8020.121.3/bsd/skywalk/nexus/nexus_syscalls.c (revision fdd8201d7b966f0c3ea610489d29bd841d358941)
1 /*
2  * Copyright (c) 2015-2021 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_close(struct fileglob *, vfs_context_t);
40 
41 static const struct fileops nexus_ctl_ops = {
42 	.fo_type     = DTYPE_NEXUS,
43 	.fo_read     = fo_no_read,
44 	.fo_write    = fo_no_write,
45 	.fo_ioctl    = fo_no_ioctl,
46 	.fo_select   = fo_no_select,
47 	.fo_close    = nxop_close,
48 	.fo_drain    = fo_no_drain,
49 	.fo_kqfilter = fo_no_kqfilter,
50 };
51 
52 static int
nxop_close(struct fileglob * fg,vfs_context_t ctx)53 nxop_close(struct fileglob *fg, vfs_context_t ctx)
54 {
55 #pragma unused(ctx)
56 	struct nxctl *nxctl;
57 	int error = 0;
58 
59 	nxctl = (struct nxctl *)fg_get_data(fg);
60 	fg_set_data(fg, NULL);
61 	if (nxctl != NULL) {
62 		nxctl_dtor(nxctl);
63 	}
64 
65 	return error;
66 }
67 
68 int
__nexus_open(struct proc * p,struct __nexus_open_args * uap,int * retval)69 __nexus_open(struct proc *p, struct __nexus_open_args *uap, int *retval)
70 {
71 	struct nxctl *nxctl = NULL;
72 	struct fileproc *fp = NULL;
73 	struct nxctl_init init;
74 	uuid_t nxctl_uuid;
75 	int fd = -1, err = 0;
76 	guardid_t guard;
77 
78 	if (__improbable(uap->init == USER_ADDR_NULL ||
79 	    uap->init_len < sizeof(init))) {
80 		SK_DSC(p, "EINVAL: init %p, init_len %u", uap->init,
81 		    uap->init_len);
82 		err = EINVAL;
83 		goto done;
84 	}
85 
86 	err = copyin(uap->init, (caddr_t)&init, sizeof(init));
87 	if (__improbable(err != 0)) {
88 		SK_DSC(p, "copyin err %d, init 0x%llx", err, SK_KVA(uap->init));
89 		goto done;
90 	}
91 
92 	if (__improbable(init.ni_version != NEXUSCTL_INIT_CURRENT_VERSION)) {
93 		SK_DSC(p, "ENOTSUP: version %u != %u", init.ni_version,
94 		    NEXUSCTL_INIT_CURRENT_VERSION);
95 		err = ENOTSUP;
96 		goto done;
97 	}
98 
99 	/* generate guard ID based on nexus controller UUID */
100 	uuid_generate_random(nxctl_uuid);
101 	sk_gen_guard_id(FALSE, nxctl_uuid, &guard);
102 
103 	err = falloc_guarded(p, &fp, &fd, vfs_context_current(), &guard,
104 	    GUARD_CLOSE | GUARD_DUP | GUARD_SOCKET_IPC | GUARD_FILEPORT | GUARD_WRITE);
105 	if (__improbable(err != 0)) {
106 		SK_DSC(p, "falloc_guarded err %d", err);
107 		goto done;
108 	}
109 
110 	nxctl = nxctl_create(p, fp, nxctl_uuid, &err);
111 	if (__improbable(nxctl == NULL)) {
112 		ASSERT(err != 0);
113 		SK_DSC(p, "nxctl_create err %d", err);
114 		goto done;
115 	}
116 
117 	/* update userland with respect to guard ID, etc. */
118 	init.ni_guard = guard;
119 	err = copyout(&init, uap->init, sizeof(init));
120 	if (__improbable(err != 0)) {
121 		SK_DSC(p, "copyout err %d, init 0x%llx", err,
122 		    SK_KVA(uap->init));
123 		goto done;
124 	}
125 
126 	fp->fp_flags |= FP_CLOEXEC | FP_CLOFORK;
127 	fp->fp_glob->fg_flag &= ~(FREAD | FWRITE);
128 	fp->fp_glob->fg_ops = &nexus_ctl_ops;
129 	fp_set_data(fp, nxctl);   /* ref from nxctl_create */
130 
131 	proc_fdlock(p);
132 	procfdtbl_releasefd(p, fd, NULL);
133 	fp_drop(p, fd, fp, 1);
134 	proc_fdunlock(p);
135 
136 	*retval = fd;
137 
138 	SK_D("%s(%d) fd %d guard 0x%llx",
139 	    sk_proc_name_address(p), sk_proc_pid(p), fd, guard);
140 
141 done:
142 	if (__improbable(err != 0)) {
143 		if (nxctl != NULL) {
144 			nxctl_dtor(nxctl);
145 			nxctl = NULL;
146 		}
147 		if (fp != NULL) {
148 			fp_free(p, fd, fp);
149 			fp = NULL;
150 		}
151 	}
152 
153 	return err;
154 }
155 
156 int
__nexus_register(struct proc * p,struct __nexus_register_args * uap,int * retval)157 __nexus_register(struct proc *p, struct __nexus_register_args *uap, int *retval)
158 {
159 #pragma unused(retval)
160 	struct fileproc *fp;
161 	struct kern_nexus_provider *nxprov = NULL;
162 	struct nxctl *nxctl;
163 	struct nxprov_reg reg;
164 	int err = 0;
165 
166 	AUDIT_ARG(fd, uap->ctl);
167 
168 	if (__improbable(uap->reg == USER_ADDR_NULL ||
169 	    uap->reg_len < sizeof(reg) || uap->prov_uuid == USER_ADDR_NULL ||
170 	    uap->prov_uuid_len < sizeof(uuid_t))) {
171 		SK_DSC(p, "EINVAL: reg 0x%llx, reg_len %u, prov_uuid 0x%llx, "
172 		    "prov_uuid_len %u", SK_KVA(uap->reg), uap->reg_len,
173 		    SK_KVA(uap->prov_uuid), uap->prov_uuid_len);
174 		return EINVAL;
175 	}
176 
177 	err = copyin(uap->reg, (caddr_t)&reg, sizeof(reg));
178 	if (err != 0) {
179 		SK_DSC(p, "copyin err %d, reg 0x%llx", err, SK_KVA(uap->reg));
180 		return err;
181 	}
182 
183 	if (__improbable(reg.nxpreg_version != NXPROV_REG_CURRENT_VERSION)) {
184 		SK_DSC(p, "EINVAL: version %u != %u", reg.nxpreg_version,
185 		    NXPROV_REG_CURRENT_VERSION);
186 		return EINVAL;
187 	}
188 
189 	if (__improbable(reg.nxpreg_params.nxp_namelen == 0 ||
190 	    reg.nxpreg_params.nxp_namelen > sizeof(nexus_name_t))) {
191 		SK_DSC(p, "EINVAL: namelen %u", reg.nxpreg_params.nxp_namelen);
192 		return EINVAL;
193 	}
194 
195 	err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
196 	if (__improbable(err != 0)) {
197 		SK_DSC(p, "fp_get_ftype: %d", err);
198 		return err;
199 	}
200 	nxctl = (struct nxctl *)fp_get_data(fp);
201 
202 	lck_mtx_lock(&nxctl->nxctl_lock);
203 	nxprov = nxprov_create(p, nxctl, &reg, &err);
204 	lck_mtx_unlock(&nxctl->nxctl_lock);
205 	if (__improbable(nxprov == NULL)) {
206 		ASSERT(err != 0);
207 		SK_DSC(p, "nxprov_create: %d", err);
208 		goto done;
209 	}
210 
211 	err = copyout(&nxprov->nxprov_uuid, uap->prov_uuid, sizeof(uuid_t));
212 	if (__improbable(err != 0)) {
213 		SK_DSC(p, "copyout err %d, prov_uuid 0x%llx", err,
214 		    SK_KVA(uap->prov_uuid));
215 		goto done;
216 	}
217 
218 done:
219 	fp_drop(p, uap->ctl, fp, 0);
220 
221 	if (__improbable(err != 0 && nxprov != NULL)) {
222 		err = nxprov_close(nxprov, FALSE);
223 	}
224 
225 	/* release extra ref from nxprov_create */
226 	if (nxprov != NULL) {
227 		nxprov_release(nxprov);
228 	}
229 
230 	return err;
231 }
232 
233 int
__nexus_deregister(struct proc * p,struct __nexus_deregister_args * uap,int * retval)234 __nexus_deregister(struct proc *p, struct __nexus_deregister_args *uap,
235     int *retval)
236 {
237 #pragma unused(retval)
238 	struct fileproc *fp;
239 	struct nxctl *nxctl = NULL;
240 	uuid_t nxprov_uuid;
241 	int err = 0;
242 
243 	AUDIT_ARG(fd, uap->ctl);
244 
245 	if (__improbable(uap->prov_uuid_len < sizeof(uuid_t))) {
246 		SK_DSC(p, "EINVAL: prov_len %u < %u", uap->prov_uuid_len,
247 		    sizeof(uuid_t));
248 		return EINVAL;
249 	}
250 
251 	err = copyin(uap->prov_uuid, (caddr_t)&nxprov_uuid, sizeof(uuid_t));
252 	if (__improbable(err != 0)) {
253 		SK_DSC(p, "copyin err %d, prov_uuid 0x%llx", err,
254 		    SK_KVA(uap->prov_uuid));
255 		return err;
256 	}
257 
258 	if (__improbable(uuid_is_null(nxprov_uuid))) {
259 		SK_DSC(p, "EINVAL: uuid_is_null");
260 		return EINVAL;
261 	}
262 
263 	err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
264 	if (__improbable(err != 0)) {
265 		SK_DSC(p, "fp_get_ftype: %d", err);
266 		return err;
267 	}
268 	nxctl = (struct nxctl *)fp_get_data(fp);
269 
270 	lck_mtx_lock(&nxctl->nxctl_lock);
271 	err = nxprov_destroy(nxctl, nxprov_uuid);
272 	lck_mtx_unlock(&nxctl->nxctl_lock);
273 
274 	fp_drop(p, uap->ctl, fp, 0);
275 
276 	return err;
277 }
278 
279 int
__nexus_create(struct proc * p,struct __nexus_create_args * uap,int * retval)280 __nexus_create(struct proc *p, struct __nexus_create_args *uap, int *retval)
281 {
282 #pragma unused(retval)
283 	struct fileproc *fp;
284 	struct kern_nexus *nx = NULL;
285 	struct nxctl *nxctl = NULL;
286 	uuid_t nxprov_uuid;
287 	int err = 0;
288 
289 	AUDIT_ARG(fd, uap->ctl);
290 
291 	if (__improbable(uap->prov_uuid_len < sizeof(uuid_t) ||
292 	    uap->nx_uuid_len < sizeof(uuid_t) ||
293 	    uap->nx_uuid == USER_ADDR_NULL)) {
294 		SK_DSC(p, "EINVAL: prov_uuid_len %u, nx_uuid_len %u, "
295 		    "nx_uuid 0x%llx", uap->prov_uuid_len, uap->nx_uuid_len,
296 		    SK_KVA(uap->nx_uuid));
297 		return EINVAL;
298 	}
299 
300 	err = copyin(uap->prov_uuid, (caddr_t)&nxprov_uuid, sizeof(uuid_t));
301 	if (__improbable(err != 0)) {
302 		SK_DSC(p, "copyin err %d, prov_uuid 0x%llx", err,
303 		    SK_KVA(uap->prov_uuid));
304 		return err;
305 	}
306 
307 	if (__improbable(uuid_is_null(nxprov_uuid))) {
308 		SK_DSC(p, "EINVAL: uuid_is_null");
309 		return EINVAL;
310 	}
311 
312 	err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
313 	if (__improbable(err != 0)) {
314 		SK_DSC(p, "fp_get_ftype: %d", err);
315 		return err;
316 	}
317 	nxctl = (struct nxctl *)fp_get_data(fp);
318 
319 	lck_mtx_lock(&nxctl->nxctl_lock);
320 	nx = nx_create(nxctl, nxprov_uuid, NEXUS_TYPE_UNDEFINED, NULL, NULL,
321 	    NULL, NULL, &err);
322 	lck_mtx_unlock(&nxctl->nxctl_lock);
323 	if (__improbable(nx == NULL)) {
324 		ASSERT(err != 0);
325 		SK_DSC(p, "nx_create: %d", err);
326 		goto done;
327 	}
328 	err = copyout(&nx->nx_uuid, uap->nx_uuid, sizeof(uuid_t));
329 	if (__improbable(err != 0)) {
330 		SK_DSC(p, "copyout err %d, nx_uuid 0x%llx", err,
331 		    SK_KVA(uap->nx_uuid));
332 		goto done;
333 	}
334 
335 done:
336 	fp_drop(p, uap->ctl, fp, 0);
337 
338 	/* release extra ref from nx_create */
339 	if (nx != NULL) {
340 		(void) nx_release(nx);
341 	}
342 
343 	return err;
344 }
345 
346 int
__nexus_destroy(struct proc * p,struct __nexus_destroy_args * uap,int * retval)347 __nexus_destroy(struct proc *p, struct __nexus_destroy_args *uap, int *retval)
348 {
349 #pragma unused(retval)
350 	struct fileproc *fp;
351 	struct nxctl *nxctl = NULL;
352 	int err = 0;
353 	uuid_t nx_uuid;
354 
355 	AUDIT_ARG(fd, uap->ctl);
356 
357 	if (__improbable(uap->nx_uuid == USER_ADDR_NULL ||
358 	    uap->nx_uuid_len < sizeof(uuid_t))) {
359 		SK_DSC(p, "EINVAL: nx_uuid 0x%llx, nx_uuid_len %u",
360 		    SK_KVA(uap->nx_uuid), uap->nx_uuid_len);
361 		return EINVAL;
362 	}
363 
364 	err = copyin(uap->nx_uuid, (caddr_t)&nx_uuid, sizeof(uuid_t));
365 	if (__improbable(err != 0)) {
366 		SK_DSC(p, "copyin err %d, nx_uuid 0x%llx", err,
367 		    SK_KVA(uap->nx_uuid));
368 		return err;
369 	}
370 
371 	if (__improbable(uuid_is_null(nx_uuid))) {
372 		SK_DSC(p, "EINVAL: uuid_is_null");
373 		return EINVAL;
374 	}
375 
376 	err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
377 	if (__improbable(err != 0)) {
378 		SK_DSC(p, "fp_get_ftype: %d", err);
379 		return err;
380 	}
381 	nxctl = (struct nxctl *)fp_get_data(fp);
382 
383 	lck_mtx_lock(&nxctl->nxctl_lock);
384 	err = nx_destroy(nxctl, nx_uuid);
385 	lck_mtx_unlock(&nxctl->nxctl_lock);
386 
387 	fp_drop(p, uap->ctl, fp, 0);
388 
389 	return err;
390 }
391 
392 int
__nexus_get_opt(struct proc * p,struct __nexus_get_opt_args * uap,int * retval)393 __nexus_get_opt(struct proc *p, struct __nexus_get_opt_args *uap, int *retval)
394 {
395 #pragma unused(retval)
396 	struct fileproc *fp;
397 	struct nxctl *nxctl = NULL;
398 	struct sockopt sopt;
399 	uint32_t optlen;
400 	int err = 0;
401 
402 	AUDIT_ARG(fd, uap->ctl);
403 
404 	err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
405 	if (__improbable(err != 0)) {
406 		SK_DSC(p, "fp_get_ftype: %d", err);
407 		return err;
408 	}
409 	nxctl = (struct nxctl *)fp_get_data(fp);
410 
411 	if (__improbable(uap->aoptlen == USER_ADDR_NULL)) {
412 		SK_DSC(p, "EINVAL: aoptlen == USER_ADDR_NULL");
413 		err = EINVAL;
414 		goto done;
415 	}
416 
417 	if (uap->aoptval != USER_ADDR_NULL) {
418 		err = copyin(uap->aoptlen, &optlen, sizeof(optlen));
419 		if (__improbable(err != 0)) {
420 			SK_DSC(p, "copyin err %d, aoptlen 0x%llx", err,
421 			    SK_KVA(uap->aoptlen));
422 			goto done;
423 		}
424 	} else {
425 		optlen = 0;
426 	}
427 
428 	bzero(&sopt, sizeof(sopt));
429 	sopt.sopt_dir = SOPT_GET;
430 	sopt.sopt_name = uap->opt;
431 	sopt.sopt_val = uap->aoptval;
432 	sopt.sopt_valsize = optlen;
433 	sopt.sopt_p = p;
434 
435 	lck_mtx_lock(&nxctl->nxctl_lock);
436 	err = nxctl_get_opt(nxctl, &sopt);
437 	lck_mtx_unlock(&nxctl->nxctl_lock);
438 	if (__probable(err == 0)) {
439 		optlen = (uint32_t)sopt.sopt_valsize;
440 		err = copyout(&optlen, uap->aoptlen, sizeof(optlen));
441 #if SK_LOG
442 		if (__improbable(err != 0)) {
443 			SK_DSC(p, "copyout err %d, aoptlen 0x%llx", err,
444 			    SK_KVA(uap->aoptlen));
445 		}
446 #endif /* SK_LOG */
447 	}
448 
449 done:
450 	fp_drop(p, uap->ctl, fp, 0);
451 
452 	return err;
453 }
454 
455 int
__nexus_set_opt(struct proc * p,struct __nexus_set_opt_args * uap,int * retval)456 __nexus_set_opt(struct proc *p, struct __nexus_set_opt_args *uap, int *retval)
457 {
458 #pragma unused(retval)
459 	struct fileproc *fp;
460 	struct nxctl *nxctl = NULL;
461 	struct sockopt sopt;
462 	int err = 0;
463 
464 	AUDIT_ARG(fd, uap->ctl);
465 
466 	err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
467 	if (__improbable(err != 0)) {
468 		SK_DSC(p, "fp_get_ftype: %d", err);
469 		return err;
470 	}
471 	nxctl = (struct nxctl *)fp_get_data(fp);
472 
473 	bzero(&sopt, sizeof(sopt));
474 	sopt.sopt_dir = SOPT_SET;
475 	sopt.sopt_name = uap->opt;
476 	sopt.sopt_val = uap->aoptval;
477 	sopt.sopt_valsize = uap->optlen;
478 	sopt.sopt_p = p;
479 
480 	lck_mtx_lock(&nxctl->nxctl_lock);
481 	err = nxctl_set_opt(nxctl, &sopt);
482 	lck_mtx_unlock(&nxctl->nxctl_lock);
483 
484 	fp_drop(p, uap->ctl, fp, 0);
485 
486 	return err;
487 }
488