xref: /xnu-10063.121.3/osfmk/kern/exclaves_storage.c (revision 2c2f96dc2b9a4408a43d3150ae9c105355ca3daa)
1 /*
2  * Copyright (c) 2023 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 #if CONFIG_EXCLAVES
30 
31 #if __has_include(<Tightbeam/tightbeam.h>)
32 
33 #include <stdint.h>
34 #include <vm/pmap.h>
35 
36 #include <Tightbeam/tightbeam.h>
37 #include <Tightbeam/tightbeam_private.h>
38 
39 #include <mach/exclaves.h>
40 #include <sys/errno.h>
41 #include <vfs/vfs_exclave_fs.h>
42 #include <kern/kalloc.h>
43 
44 #include "kern/exclaves.tightbeam.h"
45 #include "exclaves_debug.h"
46 #include "exclaves_storage.h"
47 #include "exclaves_boot.h"
48 
49 static const char *STORAGE_EXCLAVE_BUF_ID = "com.apple.named_buffer.6";
50 #define STORAGE_EXCLAVE_BUF_SIZE (4 * 1024 * 1024)
51 
52 static int
verify_string_length(const char * str,size_t size)53 verify_string_length(const char *str, size_t size)
54 {
55 	return (strnlen(str, size) < size) ? 0 : ERANGE;
56 }
57 
58 static int
verify_storage_buf_offset(uint64_t buf,uint64_t length)59 verify_storage_buf_offset(uint64_t buf, uint64_t length)
60 {
61 	uint64_t off;
62 	if (__builtin_add_overflow(buf, length, &off)) {
63 		return ERANGE;
64 	}
65 
66 	if (off > STORAGE_EXCLAVE_BUF_SIZE) {
67 		return ERANGE;
68 	}
69 
70 	return 0;
71 }
72 
73 
74 /* -------------------------------------------------------------------------- */
75 #pragma mark Upcalls
76 
77 tb_error_t
78 exclaves_storage_upcall_root(const uint8_t exclaveid[_Nonnull 32],
79     tb_error_t (^completion)(xnuupcalls_xnuupcalls_root__result_s))
80 {
81 	exclaves_debug_printf(show_storage_upcalls,
82 	    "[storage_upcalls_server] root %s\n", exclaveid);
83 
84 	int error;
85 	uint64_t rootid;
86 	xnuupcalls_xnuupcalls_root__result_s result = {};
87 
88 	if ((error = verify_string_length((const char *)&exclaveid[0], 32))) {
89 		xnuupcalls_xnuupcalls_root__result_init_failure(&result, error);
90 		return completion(result);
91 	}
92 	error = vfs_exclave_fs_root((const char *)&exclaveid[0], &rootid);
93 	if (error) {
94 		exclaves_debug_printf(show_errors,
95 		    "[storage_upcalls_server] vfs_exclave_fs_root failed with %d\n",
96 		    error);
97 		xnuupcalls_xnuupcalls_root__result_init_failure(&result, error);
98 	} else {
99 		exclaves_debug_printf(show_storage_upcalls,
100 		    "[storage_upcalls_server] vfs_exclave_fs_root return "
101 		    "rootId %lld\n", rootid);
102 		xnuupcalls_xnuupcalls_root__result_init_success(&result, rootid);
103 	}
104 
105 	return completion(result);
106 }
107 
108 tb_error_t
109 exclaves_storage_upcall_open(const enum xnuupcalls_fstag_s fstag,
110     const uint64_t rootid, const uint8_t name[_Nonnull 256],
111     tb_error_t (^completion)(xnuupcalls_xnuupcalls_open__result_s))
112 {
113 	exclaves_debug_printf(show_storage_upcalls, "[storage_upcalls_server] "
114 	    "open %d %lld %s\n", fstag, rootid, name);
115 	int error;
116 	uint64_t fileid;
117 	xnuupcalls_xnuupcalls_open__result_s result = {};
118 
119 	if ((error = verify_string_length((const char *)&name[0], 256))) {
120 		xnuupcalls_xnuupcalls_open__result_init_failure(&result, error);
121 		return completion(result);
122 	}
123 	error = vfs_exclave_fs_open((uint32_t)fstag, rootid,
124 	    (const char *)&name[0], &fileid);
125 	if (error) {
126 		exclaves_debug_printf(show_errors,
127 		    "[storage_upcalls_server] vfs_exclave_fs_open failed with %d\n",
128 		    error);
129 		xnuupcalls_xnuupcalls_open__result_init_failure(&result, error);
130 	} else {
131 		exclaves_debug_printf(show_storage_upcalls,
132 		    "[storage_upcalls_server] vfs_exclave_fs_open return "
133 		    "fileId %lld\n", fileid);
134 		xnuupcalls_xnuupcalls_open__result_init_success(&result, fileid);
135 	}
136 
137 	return completion(result);
138 }
139 
140 tb_error_t
141 exclaves_storage_upcall_close(const enum xnuupcalls_fstag_s fstag,
142     const uint64_t fileid, tb_error_t (^completion)(xnuupcalls_xnuupcalls_close__result_s))
143 {
144 	exclaves_debug_printf(show_storage_upcalls, "[storage_upcalls_server] "
145 	    "close %d %lld\n", fstag, fileid);
146 	int error;
147 	xnuupcalls_xnuupcalls_close__result_s result = {};
148 
149 	error = vfs_exclave_fs_close((uint32_t)fstag, fileid);
150 	if (error) {
151 		exclaves_debug_printf(show_errors,
152 		    "[storage_upcalls_server] vfs_exclave_fs_close failed with "
153 		    "%d\n", error);
154 		xnuupcalls_xnuupcalls_close__result_init_failure(&result, error);
155 	} else {
156 		exclaves_debug_printf(show_storage_upcalls,
157 		    "[storage_upcalls_server] vfs_exclave_fs_close succeeded\n");
158 		xnuupcalls_xnuupcalls_close__result_init_success(&result);
159 	}
160 
161 	return completion(result);
162 }
163 
164 tb_error_t
165 exclaves_storage_upcall_create(const enum xnuupcalls_fstag_s fstag,
166     const uint64_t rootid, const uint8_t name[_Nonnull 256],
167     tb_error_t (^completion)(xnuupcalls_xnuupcalls_create__result_s))
168 {
169 	exclaves_debug_printf(show_storage_upcalls, "[storage_upcalls_server]"
170 	    " create %d %lld %s\n", fstag, rootid, name);
171 	int error;
172 	uint64_t fileid;
173 	xnuupcalls_xnuupcalls_create__result_s result = {};
174 
175 	if ((error = verify_string_length((const char *)&name[0], 256))) {
176 		xnuupcalls_xnuupcalls_create__result_init_failure(&result, error);
177 		return completion(result);
178 	}
179 	error = vfs_exclave_fs_create((uint32_t)fstag, rootid,
180 	    (const char *)&name[0], &fileid);
181 	if (error) {
182 		exclaves_debug_printf(show_errors,
183 		    "[storage_upcalls_server] vfs_exclave_fs_create failed with"
184 		    " %d\n", error);
185 		xnuupcalls_xnuupcalls_create__result_init_failure(&result, error);
186 	} else {
187 		exclaves_debug_printf(show_storage_upcalls,
188 		    "[storage_upcalls_server] vfs_exclave_fs_create return "
189 		    "fileId %lld\n", fileid);
190 		xnuupcalls_xnuupcalls_create__result_init_success(&result, fileid);
191 	}
192 
193 	return completion(result);
194 }
195 
196 // Borrowed from bsd_init.c
197 extern bool bsd_rooted_ramdisk(void);
198 
199 static bool
is_restore(void)200 is_restore(void)
201 {
202 	bool is_restore = false;
203 	(void) PE_parse_boot_argn("-restore", &is_restore, sizeof(is_restore));
204 	return is_restore;
205 }
206 
207 static exclaves_resource_t *storage_resource = NULL;
208 static kern_return_t
exclaves_storage_init(void)209 exclaves_storage_init(void)
210 {
211 	kern_return_t kr = exclaves_named_buffer_map(
212 		EXCLAVES_DOMAIN_KERNEL, STORAGE_EXCLAVE_BUF_ID,
213 		STORAGE_EXCLAVE_BUF_SIZE,
214 		EXCLAVES_BUFFER_PERM_READ | EXCLAVES_BUFFER_PERM_WRITE,
215 		&storage_resource);
216 	if (kr != KERN_SUCCESS) {
217 		exclaves_debug_printf(show_errors,
218 		    "[storage_upcalls] exclaves_named_buffer_map failed with %d\n", kr);
219 		if (is_restore() || bsd_rooted_ramdisk()) {
220 			// Don't fail boot here. Fail the upcalls that try to use the sharemem buffer instead.
221 			// This is to prevent panic during boot-time when xnu-proxy was initialized before StorageExclave
222 			// This can be reverted once Storage switched to V2
223 			storage_resource = NULL;
224 		} else {
225 			return kr;
226 		}
227 	}
228 	return KERN_SUCCESS;
229 }
230 EXCLAVES_BOOT_TASK(exclaves_storage_init, EXCLAVES_BOOT_RANK_SECOND);
231 
232 tb_error_t
233 exclaves_storage_upcall_read(const enum xnuupcalls_fstag_s fstag,
234     const uint64_t fileid, const struct xnuupcalls_iodesc_s *descriptor,
235     tb_error_t (^completion)(xnuupcalls_xnuupcalls_read__result_s))
236 {
237 	exclaves_debug_printf(show_storage_upcalls, "[storage_upcalls_server] "
238 	    "read %d %lld %lld %lld %lld\n", fstag, fileid, descriptor->buf,
239 	    descriptor->fileoffset, descriptor->length);
240 	int error;
241 
242 	xnuupcalls_xnuupcalls_read__result_s result = {};
243 
244 	if (!storage_resource) {
245 		exclaves_debug_printf(show_errors,
246 		    "[storage_upcalls] shared memory buffer not initialized\n");
247 		xnuupcalls_xnuupcalls_read__result_init_failure(&result, ENOMEM);
248 		return completion(result);
249 	}
250 
251 	error = verify_storage_buf_offset(descriptor->buf, descriptor->length);
252 	if (error != 0) {
253 		xnuupcalls_xnuupcalls_read__result_init_failure(&result, error);
254 		return completion(result);
255 	}
256 
257 	__block uint64_t off = descriptor->fileoffset;
258 	error = exclaves_named_buffer_io(storage_resource, descriptor->buf,
259 	    descriptor->length, ^(char *buffer, size_t size) {
260 		int ret = vfs_exclave_fs_read((uint32_t)fstag,
261 		fileid, off, size, buffer);
262 		off += size;
263 		return ret;
264 	});
265 
266 	if (error) {
267 		exclaves_debug_printf(show_errors,
268 		    "[storage_upcalls_server] vfs_exclave_fs_read "
269 		    "failed with %d\n", error);
270 		xnuupcalls_xnuupcalls_read__result_init_failure(&result, error);
271 	} else {
272 		exclaves_debug_printf(show_storage_upcalls,
273 		    "[storage_upcalls_server] vfs_exclave_fs_read succeeded\n");
274 		xnuupcalls_xnuupcalls_read__result_init_success(&result);
275 	}
276 
277 	return completion(result);
278 }
279 
280 tb_error_t
281 exclaves_storage_upcall_write(const enum xnuupcalls_fstag_s fstag,
282     const uint64_t fileid, const struct xnuupcalls_iodesc_s *descriptor,
283     tb_error_t (^completion)(xnuupcalls_xnuupcalls_write__result_s))
284 {
285 	exclaves_debug_printf(show_storage_upcalls, "[storage_upcalls_server] "
286 	    "write %d %lld %lld %lld %lld\n", fstag, fileid, descriptor->buf,
287 	    descriptor->fileoffset, descriptor->length);
288 	int error;
289 
290 	xnuupcalls_xnuupcalls_write__result_s result = {};
291 
292 	if (!storage_resource) {
293 		exclaves_debug_printf(show_errors,
294 		    "[storage_upcalls] shared memory buffer not initialized\n");
295 		xnuupcalls_xnuupcalls_write__result_init_failure(&result, ENOMEM);
296 		return completion(result);
297 	}
298 
299 	error = verify_storage_buf_offset(descriptor->buf, descriptor->length);
300 	if (error != 0) {
301 		xnuupcalls_xnuupcalls_write__result_init_failure(&result, error);
302 		return completion(result);
303 	}
304 
305 
306 	__block uint64_t off = descriptor->fileoffset;
307 	error = exclaves_named_buffer_io(storage_resource, descriptor->buf,
308 	    descriptor->length, ^(char *buffer, size_t size) {
309 		int ret = vfs_exclave_fs_write((uint32_t)fstag,
310 		fileid, off, size, buffer);
311 		off += size;
312 		return ret;
313 	});
314 
315 	if (error) {
316 		exclaves_debug_printf(show_errors,
317 		    "[storage_upcalls_server] vfs_exclave_fs_write "
318 		    "failed with %d\n", error);
319 		xnuupcalls_xnuupcalls_write__result_init_failure(&result, error);
320 	} else {
321 		exclaves_debug_printf(show_storage_upcalls,
322 		    "[storage_upcalls_server] vfs_exclave_fs_write succeeded\n");
323 		xnuupcalls_xnuupcalls_write__result_init_success(&result);
324 	}
325 
326 	return completion(result);
327 }
328 
329 tb_error_t
330 exclaves_storage_upcall_remove(const enum xnuupcalls_fstag_s fstag,
331     const uint64_t rootid, const uint8_t name[_Nonnull 256],
332     tb_error_t (^completion)(xnuupcalls_xnuupcalls_remove__result_s))
333 {
334 	exclaves_debug_printf(show_storage_upcalls, "[storage_upcalls_server] "
335 	    "remove %d %lld %s\n", fstag, rootid, name);
336 	int error;
337 	xnuupcalls_xnuupcalls_remove__result_s result = {};
338 
339 	if ((error = verify_string_length((const char *)&name[0], 256))) {
340 		xnuupcalls_xnuupcalls_remove__result_init_failure(&result, error);
341 		return completion(result);
342 	}
343 	error = vfs_exclave_fs_remove((uint32_t)fstag, rootid,
344 	    (const char *)&name[0]);
345 	if (error) {
346 		exclaves_debug_printf(show_errors,
347 		    "[storage_upcalls_server] vfs_exclave_fs_remove failed with "
348 		    "%d\n", error);
349 		xnuupcalls_xnuupcalls_remove__result_init_failure(&result, error);
350 	} else {
351 		exclaves_debug_printf(show_storage_upcalls,
352 		    "[storage_upcalls_server] vfs_exclave_fs_remove succeeded\n");
353 		xnuupcalls_xnuupcalls_remove__result_init_success(&result);
354 	}
355 
356 	return completion(result);
357 }
358 
359 tb_error_t
360 exclaves_storage_upcall_sync(const enum xnuupcalls_fstag_s fstag,
361     const enum xnuupcalls_syncop_s op,
362     const uint64_t fileid, tb_error_t (^completion)(xnuupcalls_xnuupcalls_sync__result_s))
363 {
364 	exclaves_debug_printf(show_storage_upcalls, "[storage_upcalls_server] "
365 	    "sync %d %lld %d\n", fstag, fileid, (int)op);
366 	int error;
367 	xnuupcalls_xnuupcalls_sync__result_s result = {};
368 
369 	error = vfs_exclave_fs_sync((uint32_t)fstag, fileid, op);
370 	if (error) {
371 		exclaves_debug_printf(show_errors,
372 		    "[storage_upcalls_server] vfs_exclave_fs_sync failed with %d\n",
373 		    error);
374 		xnuupcalls_xnuupcalls_sync__result_init_failure(&result, error);
375 	} else {
376 		exclaves_debug_printf(show_storage_upcalls,
377 		    "[storage_upcalls_server] vfs_exclave_fs_sync succeeded\n");
378 		xnuupcalls_xnuupcalls_sync__result_init_success(&result);
379 	}
380 
381 	return completion(result);
382 }
383 
384 tb_error_t
385 exclaves_storage_upcall_readdir(const enum xnuupcalls_fstag_s fstag,
386     const uint64_t fileid, const uint64_t buf,
387     const uint32_t length, tb_error_t (^completion)(xnuupcalls_xnuupcalls_readdir__result_s))
388 {
389 	exclaves_debug_printf(show_storage_upcalls,
390 	    "[storage_upcalls_server] readdir %d %lld %lld %d\n",
391 	    fstag, fileid, buf, length);
392 	int error;
393 	int32_t count;
394 
395 	xnuupcalls_xnuupcalls_readdir__result_s result = {};
396 
397 	if (!storage_resource) {
398 		exclaves_debug_printf(show_errors,
399 		    "[storage_upcalls] shared memory buffer not initialized\n");
400 		xnuupcalls_xnuupcalls_readdir__result_init_failure(&result, ENOMEM);
401 		return completion(result);
402 	}
403 
404 	if ((error = verify_storage_buf_offset(buf, length))) {
405 		xnuupcalls_xnuupcalls_readdir__result_init_failure(&result, error);
406 		return completion(result);
407 	}
408 
409 	/*
410 	 * When we are able to map exclaves shared memory into kernel
411 	 * virtual address space, this temporary buffer will no longer
412 	 * be required.
413 	 */
414 	char *tmpbuf = kalloc_data(length, Z_WAITOK | Z_ZERO | Z_NOFAIL);
415 	error = vfs_exclave_fs_readdir((uint32_t)fstag, fileid,
416 	    tmpbuf, length, &count);
417 	assert3u(error, ==, 0);
418 
419 	__block char *p = tmpbuf;
420 	error = exclaves_named_buffer_io(storage_resource, buf, length,
421 	    ^(char *buffer, size_t size) {
422 		memcpy(buffer, p, size);
423 		p += size;
424 		return 0;
425 	});
426 	assert3u(error, ==, 0);
427 	kfree_data(tmpbuf, length);
428 
429 	if (error) {
430 		exclaves_debug_printf(show_errors,
431 		    "[storage_upcalls_server] vfs_exclave_fs_readdir "
432 		    "failed with %d\n", error);
433 		xnuupcalls_xnuupcalls_readdir__result_init_failure(&result, error);
434 	} else {
435 		exclaves_debug_printf(show_storage_upcalls,
436 		    "[storage_upcalls_server] vfs_exclave_fs_readdir succeeded\n");
437 		xnuupcalls_xnuupcalls_readdir__result_init_success(&result, count);
438 	}
439 
440 	return completion(result);
441 }
442 
443 tb_error_t
444 exclaves_storage_upcall_getsize(const enum xnuupcalls_fstag_s fstag,
445     const uint64_t fileid,
446     tb_error_t (^completion)(xnuupcalls_xnuupcalls_getsize__result_s result))
447 {
448 	exclaves_debug_printf(show_storage_upcalls,
449 	    "[storage_upcalls_server] getsize %d %lld\n",
450 	    fstag, fileid);
451 	int error;
452 	uint64_t size;
453 	xnuupcalls_xnuupcalls_getsize__result_s result = {};
454 
455 	error = vfs_exclave_fs_getsize((uint32_t)fstag, fileid, &size);
456 	if (error) {
457 		exclaves_debug_printf(show_errors,
458 		    "[storage_upcalls_server] vfs_exclave_fs_getsize(%d, %lld) "
459 		    "failed with %d\n", fstag, fileid, error);
460 		xnuupcalls_xnuupcalls_getsize__result_init_failure(&result, error);
461 	} else {
462 		exclaves_debug_printf(show_storage_upcalls,
463 		    "[storage_upcalls_server] vfs_exclave_fs_getsize succeeded\n");
464 		xnuupcalls_xnuupcalls_getsize__result_init_success(&result, size);
465 	}
466 
467 	return completion(result);
468 }
469 
470 tb_error_t
471 exclaves_storage_upcall_sealstate(const enum xnuupcalls_fstag_s fstag,
472     tb_error_t (^completion)(xnuupcalls_xnuupcalls_sealstate__result_s result))
473 {
474 	exclaves_debug_printf(show_storage_upcalls,
475 	    "[storage_upcalls_server] sealstate %d\n",
476 	    fstag);
477 	int error;
478 	bool sealed;
479 	xnuupcalls_xnuupcalls_sealstate__result_s result = {};
480 
481 	error = vfs_exclave_fs_sealstate((uint32_t)fstag, &sealed);
482 	if (error) {
483 		exclaves_debug_printf(show_errors,
484 		    "[storage_upcalls_server] vfs_exclave_fs_sealstate(%d) "
485 		    "failed with %d\n", fstag, error);
486 		xnuupcalls_xnuupcalls_sealstate__result_init_failure(&result, error);
487 	} else {
488 		exclaves_debug_printf(show_storage_upcalls,
489 		    "[storage_upcalls_server] vfs_exclave_fs_sealstate succeeded\n");
490 		xnuupcalls_xnuupcalls_sealstate__result_init_success(&result, sealed);
491 	}
492 
493 	return completion(result);
494 }
495 
496 #endif /* __has_include(<Tightbeam/tightbeam.h>) */
497 
498 #endif /* CONFIG_EXCLAVES */
499