1use std::mem::MaybeUninit;
6use std::ops::Range;
7
8use zerocopy::FromBytes;
9use zx::Task;
10
11unsafe extern "C" {
12 fn hermetic_copy(dest: *mut u8, source: *const u8, len: usize, ret_dest: bool) -> usize;
17 fn hermetic_copy_end();
18
19 fn hermetic_copy_until_null_byte(
24 dest: *mut u8,
25 source: *const u8,
26 len: usize,
27 ret_dest: bool,
28 ) -> usize;
29 fn hermetic_copy_until_null_byte_end();
30
31 fn hermetic_zero(dest: *mut u8, len: usize) -> usize;
35 fn hermetic_zero_end();
36
37 fn hermetic_copy_error();
39
40 fn atomic_error();
42
43 fn atomic_load_u32_relaxed(addr: usize) -> u64;
47
48 fn atomic_load_u32_relaxed_end();
50
51 fn atomic_load_u32_acquire(addr: usize) -> u64;
55
56 fn atomic_load_u32_acquire_end();
58
59 fn atomic_store_u32_relaxed(addr: usize, value: u32) -> u64;
62
63 fn atomic_store_u32_relaxed_end();
65
66 fn atomic_store_u32_release(addr: usize, value: u32) -> u64;
69
70 fn atomic_store_u32_release_end();
72
73 fn atomic_compare_exchange_u32_acq_rel(addr: usize, expected: *mut u32, desired: u32) -> u64;
85
86 fn atomic_compare_exchange_u32_acq_rel_end();
88
89 fn atomic_compare_exchange_weak_u32_acq_rel(
102 addr: usize,
103 expected: *mut u32,
104 desired: u32,
105 ) -> u64;
106
107 fn atomic_compare_exchange_weak_u32_acq_rel_end();
109}
110
111pub fn slice_to_maybe_uninit_mut<T>(slice: &mut [T]) -> &mut [MaybeUninit<T>] {
113 let ptr = slice.as_mut_ptr();
114 let ptr = ptr as *mut MaybeUninit<T>;
115 unsafe { std::slice::from_raw_parts_mut(ptr, slice.len()) }
122}
123
124type HermeticCopyFn =
125 unsafe extern "C" fn(dest: *mut u8, source: *const u8, len: usize, ret_dest: bool) -> usize;
126
127#[derive(Debug)]
128pub struct Usercopy {
129 shutdown_event: zx::Event,
131
132 join_handle: Option<std::thread::JoinHandle<()>>,
134
135 restricted_address_range: Range<usize>,
137}
138
139fn parse_fault_exception(
144 regs: &mut zx::sys::zx_thread_state_general_regs_t,
145 report: zx::ExceptionReport,
146) -> (usize, usize) {
147 #[cfg(target_arch = "x86_64")]
148 {
149 let pc = regs.rip as usize;
150 let fault_address = report.arch.cr2;
151
152 (pc, fault_address as usize)
153 }
154
155 #[cfg(target_arch = "aarch64")]
156 {
157 let pc = regs.pc as usize;
158 let fault_address = report.arch.far;
159
160 (pc, fault_address as usize)
161 }
162
163 #[cfg(target_arch = "riscv64")]
164 {
165 let pc = regs.pc as usize;
166 let fault_address = report.arch.tval;
167
168 (pc, fault_address as usize)
169 }
170}
171
172fn set_registers_for_hermetic_error(
173 regs: &mut zx::sys::zx_thread_state_general_regs_t,
174 fault_address: usize,
175) {
176 #[cfg(target_arch = "x86_64")]
177 {
178 regs.rip = hermetic_copy_error as *const () as u64;
179 regs.rax = fault_address as u64;
180 }
181
182 #[cfg(target_arch = "aarch64")]
183 {
184 regs.pc = hermetic_copy_error as *const () as u64;
185 regs.r[0] = fault_address as u64;
186 }
187
188 #[cfg(target_arch = "riscv64")]
189 {
190 regs.pc = hermetic_copy_error as *const () as u64;
191 regs.a0 = fault_address as u64;
192 }
193}
194
195const ATOMIC_ERROR_MASK: u64 = 0xFFFFFFFF00000000;
196
197fn set_registers_for_atomic_error(regs: &mut zx::sys::zx_thread_state_general_regs_t) {
198 #[cfg(target_arch = "x86_64")]
199 {
200 regs.rax = ATOMIC_ERROR_MASK;
201 regs.rip = atomic_error as *const () as u64;
202 }
203
204 #[cfg(target_arch = "aarch64")]
205 {
206 regs.r[0] = ATOMIC_ERROR_MASK;
207 regs.pc = atomic_error as *const () as u64;
208 }
209
210 #[cfg(target_arch = "riscv64")]
211 {
212 regs.a0 = ATOMIC_ERROR_MASK;
213 regs.pc = atomic_error as *const () as u64;
214 }
215}
216
217unsafe fn assume_initialized_until(
225 buf: &mut [MaybeUninit<u8>],
226 initialized_until: usize,
227) -> (&mut [u8], &mut [MaybeUninit<u8>]) {
228 let (init_bytes, uninit_bytes) = buf.split_at_mut(initialized_until);
229 debug_assert_eq!(init_bytes.len(), initialized_until);
230
231 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
232 let init_bytes = unsafe {
233 std::slice::from_raw_parts_mut(init_bytes.as_mut_ptr() as *mut u8, init_bytes.len())
234 };
235
236 (init_bytes, uninit_bytes)
237}
238
239unsafe fn do_hermetic_copy(
248 f: HermeticCopyFn,
249 dest: usize,
250 source: usize,
251 count: usize,
252 ret_dest: bool,
253) -> usize {
254 #[allow(
255 clippy::undocumented_unsafe_blocks,
256 reason = "Force documented unsafe blocks in Starnix"
257 )]
258 let unread_address = unsafe { f(dest as *mut u8, source as *const u8, count, ret_dest) };
259
260 let ret_base = if ret_dest { dest } else { source };
261
262 debug_assert!(
263 unread_address >= ret_base,
264 "unread_address={:#x}, ret_base={:#x}",
265 unread_address,
266 ret_base,
267 );
268 let copied = unread_address - ret_base;
269 debug_assert!(
270 copied <= count,
271 "copied={}, count={}; unread_address={:#x}, ret_base={:#x}",
272 copied,
273 count,
274 unread_address,
275 ret_base,
276 );
277 copied
278}
279
280impl Usercopy {
281 pub fn new(restricted_address_range: Range<usize>) -> Result<Self, zx::Status> {
284 let hermetic_copy_addr_range =
285 hermetic_copy as *const () as usize..hermetic_copy_end as *const () as usize;
286
287 let hermetic_copy_until_null_byte_addr_range = hermetic_copy_until_null_byte as *const ()
288 as usize
289 ..hermetic_copy_until_null_byte_end as *const () as usize;
290
291 let hermetic_zero_addr_range =
292 hermetic_zero as *const () as usize..hermetic_zero_end as *const () as usize;
293
294 let atomic_load_relaxed_range = atomic_load_u32_relaxed as *const () as usize
295 ..atomic_load_u32_relaxed_end as *const () as usize;
296
297 let atomic_load_acquire_range = atomic_load_u32_acquire as *const () as usize
298 ..atomic_load_u32_acquire_end as *const () as usize;
299
300 let atomic_store_relaxed_range = atomic_store_u32_relaxed as *const () as usize
301 ..atomic_store_u32_relaxed_end as *const () as usize;
302
303 let atomic_store_release_range = atomic_store_u32_release as *const () as usize
304 ..atomic_store_u32_release_end as *const () as usize;
305
306 let atomic_compare_exchange_range = atomic_compare_exchange_u32_acq_rel as *const ()
307 as usize
308 ..atomic_compare_exchange_u32_acq_rel_end as *const () as usize;
309
310 let atomic_compare_exchange_weak_range = atomic_compare_exchange_weak_u32_acq_rel
311 as *const () as usize
312 ..atomic_compare_exchange_weak_u32_acq_rel_end as *const () as usize;
313
314 let (tx, rx) = std::sync::mpsc::channel::<Result<(), zx::Status>>();
315
316 let shutdown_event = zx::Event::create();
317 let shutdown_event_clone =
318 shutdown_event.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
319
320 let faultable_addresses = restricted_address_range.clone();
321 let join_handle = std::thread::spawn(move || {
322 let exception_channel_result =
323 fuchsia_runtime::job_default().create_exception_channel();
324
325 let exception_channel = match exception_channel_result {
326 Ok(c) => c,
327 Err(e) => {
328 let _ = tx.send(Err(e));
329 return;
330 }
331 };
332
333 let _ = tx.send(Ok(()));
335
336 loop {
338 let mut wait_items = [
339 exception_channel.wait_item(zx::Signals::CHANNEL_READABLE),
340 shutdown_event_clone.wait_item(zx::Signals::USER_0),
341 ];
342 let _ = zx::object_wait_many(&mut wait_items, zx::MonotonicInstant::INFINITE);
343 if wait_items[1].pending() == zx::Signals::USER_0 {
344 break;
345 }
346 let mut buf = zx::MessageBuf::new();
347 exception_channel.read(&mut buf).unwrap();
348
349 let excp_info = zx::sys::zx_exception_info_t::read_from_bytes(buf.bytes()).unwrap();
350
351 if excp_info.type_ != zx::sys::ZX_EXCP_FATAL_PAGE_FAULT {
352 continue;
354 }
355
356 let excp = zx::Exception::from(buf.take_handle(0).unwrap());
357 let thread = excp.get_thread().unwrap();
358 let mut regs = thread.read_state_general_regs().unwrap();
359 let report = thread.exception_report().unwrap();
360
361 let (pc, fault_address) = parse_fault_exception(&mut regs, report);
371
372 if !faultable_addresses.contains(&fault_address) {
375 continue;
376 }
377
378 if hermetic_copy_addr_range.contains(&pc)
380 || hermetic_copy_until_null_byte_addr_range.contains(&pc)
381 || hermetic_zero_addr_range.contains(&pc)
382 {
383 set_registers_for_hermetic_error(&mut regs, fault_address);
384 } else if atomic_load_relaxed_range.contains(&pc)
385 || atomic_load_acquire_range.contains(&pc)
386 || atomic_store_relaxed_range.contains(&pc)
387 || atomic_store_release_range.contains(&pc)
388 || atomic_compare_exchange_range.contains(&pc)
389 || atomic_compare_exchange_weak_range.contains(&pc)
390 {
391 set_registers_for_atomic_error(&mut regs);
392 } else {
393 continue;
394 }
395
396 thread.write_state_general_regs(regs).unwrap();
397 excp.set_exception_state(&zx::sys::ZX_EXCEPTION_STATE_HANDLED).unwrap();
398 }
399 });
400
401 rx.recv().unwrap()?;
402
403 Ok(Self { shutdown_event, join_handle: Some(join_handle), restricted_address_range })
404 }
405
406 pub unsafe fn raw_hermetic_copy(
415 &self,
416 dest: *mut u8,
417 source: *const u8,
418 count: usize,
419 ret_dest: bool,
420 ) -> usize {
421 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
422 unsafe {
423 do_hermetic_copy(hermetic_copy, dest as usize, source as usize, count, ret_dest)
424 }
425 }
426
427 pub fn zero(&self, dest_addr: usize, count: usize) -> usize {
431 if dest_addr == 0 || !self.restricted_address_range.contains(&dest_addr) {
435 return 0;
436 }
437
438 #[allow(
439 clippy::undocumented_unsafe_blocks,
440 reason = "Force documented unsafe blocks in Starnix"
441 )]
442 let unset_address = unsafe { hermetic_zero(dest_addr as *mut u8, count) };
443 debug_assert!(
444 unset_address >= dest_addr,
445 "unset_address={:#x}, dest_addr={:#x}",
446 unset_address,
447 dest_addr,
448 );
449 let bytes_set = unset_address - dest_addr;
450 debug_assert!(
451 bytes_set <= count,
452 "bytes_set={}, count={}; unset_address={:#x}, dest_addr={:#x}",
453 bytes_set,
454 count,
455 unset_address,
456 dest_addr,
457 );
458 bytes_set
459 }
460
461 pub fn copyout(&self, source: &[u8], dest_addr: usize) -> usize {
465 if dest_addr == 0 || !self.restricted_address_range.contains(&dest_addr) {
469 return 0;
470 }
471
472 unsafe {
475 do_hermetic_copy(hermetic_copy, dest_addr, source.as_ptr() as usize, source.len(), true)
476 }
477 }
478
479 pub fn copyin<'a>(
487 &self,
488 source_addr: usize,
489 dest: &'a mut [MaybeUninit<u8>],
490 ) -> (&'a mut [u8], &'a mut [MaybeUninit<u8>]) {
491 let read_count =
495 if source_addr == 0 || !self.restricted_address_range.contains(&source_addr) {
496 0
497 } else {
498 unsafe {
501 do_hermetic_copy(
502 hermetic_copy,
503 dest.as_ptr() as usize,
504 source_addr,
505 dest.len(),
506 false,
507 )
508 }
509 };
510
511 unsafe { assume_initialized_until(dest, read_count) }
513 }
514
515 pub fn copyin_until_null_byte<'a>(
525 &self,
526 source_addr: usize,
527 dest: &'a mut [MaybeUninit<u8>],
528 ) -> (&'a mut [u8], &'a mut [MaybeUninit<u8>]) {
529 let read_count =
533 if source_addr == 0 || !self.restricted_address_range.contains(&source_addr) {
534 0
535 } else {
536 unsafe {
539 do_hermetic_copy(
540 hermetic_copy_until_null_byte,
541 dest.as_ptr() as usize,
542 source_addr,
543 dest.len(),
544 false,
545 )
546 }
547 };
548
549 unsafe { assume_initialized_until(dest, read_count) }
551 }
552
553 #[inline]
554 fn atomic_load_u32(
555 &self,
556 load_fn: unsafe extern "C" fn(usize) -> u64,
557 addr: usize,
558 ) -> Result<u32, ()> {
559 #[allow(
560 clippy::undocumented_unsafe_blocks,
561 reason = "Force documented unsafe blocks in Starnix"
562 )]
563 let value_or_error = unsafe { load_fn(addr) };
564 if value_or_error & ATOMIC_ERROR_MASK == 0 { Ok(value_or_error as u32) } else { Err(()) }
565 }
566
567 pub fn atomic_load_u32_relaxed(&self, addr: usize) -> Result<u32, ()> {
570 self.atomic_load_u32(atomic_load_u32_relaxed, addr)
571 }
572
573 pub fn atomic_load_u32_acquire(&self, addr: usize) -> Result<u32, ()> {
576 self.atomic_load_u32(atomic_load_u32_acquire, addr)
577 }
578
579 fn atomic_store_u32(
580 &self,
581 store_fn: unsafe extern "C" fn(usize, u32) -> u64,
582 addr: usize,
583 value: u32,
584 ) -> Result<(), ()> {
585 #[allow(
586 clippy::undocumented_unsafe_blocks,
587 reason = "Force documented unsafe blocks in Starnix"
588 )]
589 match unsafe { store_fn(addr, value) } {
590 0 => Ok(()),
591 _ => Err(()),
592 }
593 }
594
595 pub fn atomic_store_u32_relaxed(&self, addr: usize, value: u32) -> Result<(), ()> {
598 self.atomic_store_u32(atomic_store_u32_relaxed, addr, value)
599 }
600
601 pub fn atomic_store_u32_release(&self, addr: usize, value: u32) -> Result<(), ()> {
604 self.atomic_store_u32(atomic_store_u32_release, addr, value)
605 }
606
607 pub fn atomic_compare_exchange_u32_acq_rel(
610 &self,
611 addr: usize,
612 expected: u32,
613 desired: u32,
614 ) -> Result<Result<u32, u32>, ()> {
615 let mut expected = expected;
616 #[allow(
617 clippy::undocumented_unsafe_blocks,
618 reason = "Force documented unsafe blocks in Starnix"
619 )]
620 let value_or_error = unsafe {
621 atomic_compare_exchange_u32_acq_rel(addr, &mut expected as *mut u32, desired)
622 };
623 Self::parse_compare_exchange_result(expected, value_or_error)
624 }
625
626 pub fn atomic_compare_exchange_weak_u32_acq_rel(
629 &self,
630 addr: usize,
631 expected: u32,
632 desired: u32,
633 ) -> Result<Result<u32, u32>, ()> {
634 let mut expected = expected;
635 #[allow(
636 clippy::undocumented_unsafe_blocks,
637 reason = "Force documented unsafe blocks in Starnix"
638 )]
639 let value_or_error = unsafe {
640 atomic_compare_exchange_weak_u32_acq_rel(addr, &mut expected as *mut u32, desired)
641 };
642 Self::parse_compare_exchange_result(expected, value_or_error)
643 }
644
645 fn parse_compare_exchange_result(
646 expected: u32,
647 value_or_error: u64,
648 ) -> Result<Result<u32, u32>, ()> {
649 match value_or_error {
650 0 => Ok(Err(expected)),
651 1 => Ok(Ok(expected)),
652 _ => Err(()),
653 }
654 }
655}
656
657impl Drop for Usercopy {
658 fn drop(&mut self) {
659 self.shutdown_event.signal(zx::Signals::empty(), zx::Signals::USER_0).unwrap();
660 self.join_handle.take().unwrap().join().unwrap();
661 }
662}
663
664#[cfg(test)]
665mod test {
666 #![allow(
667 clippy::undocumented_unsafe_blocks,
668 reason = "Force documented unsafe blocks in Starnix"
669 )]
670 use super::*;
671
672 use test_case::test_case;
673
674 impl Usercopy {
675 fn new_for_test(restricted_address_range: Range<usize>) -> Self {
676 Self::new(restricted_address_range).unwrap()
677 }
678 }
679
680 #[test_case(0, 0)]
681 #[test_case(1, 1)]
682 #[test_case(7, 2)]
683 #[test_case(8, 3)]
684 #[test_case(9, 4)]
685 #[test_case(128, 5)]
686 #[test_case(zx::system_get_page_size() as usize - 1, 6)]
687 #[test_case(zx::system_get_page_size() as usize, 7)]
688 #[::fuchsia::test]
689 fn zero_no_fault(zero_len: usize, ch: u8) {
690 let page_size = zx::system_get_page_size() as usize;
691
692 let dest_vmo = zx::Vmo::create(page_size as u64).unwrap();
693
694 let root_vmar = fuchsia_runtime::vmar_root_self();
695
696 let mapped_addr = root_vmar
697 .map(0, &dest_vmo, 0, page_size, zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE)
698 .unwrap();
699 let mapped_bytes =
700 unsafe { std::slice::from_raw_parts_mut(mapped_addr as *mut u8, page_size) };
701 mapped_bytes.fill(ch);
702
703 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size);
704
705 let result = usercopy.zero(mapped_addr, zero_len);
706 assert_eq!(result, zero_len);
707
708 assert_eq!(&mapped_bytes[..zero_len], &vec![0; zero_len]);
709 assert_eq!(&mapped_bytes[zero_len..], &vec![ch; page_size - zero_len]);
710 }
711
712 #[test_case(1, 2, 0)]
713 #[test_case(1, 4, 1)]
714 #[test_case(1, 8, 2)]
715 #[test_case(1, 16, 3)]
716 #[test_case(1, 32, 4)]
717 #[test_case(1, 64, 5)]
718 #[test_case(1, 128, 6)]
719 #[test_case(1, 256, 7)]
720 #[test_case(1, 512, 8)]
721 #[test_case(1, 1024, 9)]
722 #[test_case(32, 64, 10)]
723 #[test_case(32, 128, 11)]
724 #[test_case(32, 256, 12)]
725 #[test_case(32, 512, 13)]
726 #[test_case(32, 1024, 14)]
727 #[::fuchsia::test]
728 fn zero_fault(offset: usize, zero_len: usize, ch: u8) {
729 let page_size = zx::system_get_page_size() as usize;
730
731 let dest_vmo = zx::Vmo::create(page_size as u64).unwrap();
732
733 let root_vmar = fuchsia_runtime::vmar_root_self();
734
735 let mapped_addr = root_vmar
736 .map(
737 0,
738 &dest_vmo,
739 0,
740 page_size * 2,
741 zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE,
742 )
743 .unwrap();
744 let mapped_bytes =
745 unsafe { std::slice::from_raw_parts_mut(mapped_addr as *mut u8, page_size) };
746 mapped_bytes.fill(ch);
747
748 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size * 2);
749
750 let dest_addr = mapped_addr + page_size - offset;
751
752 let result = usercopy.zero(dest_addr, zero_len);
753 assert_eq!(result, offset);
754
755 assert_eq!(&mapped_bytes[page_size - offset..], &vec![0; offset][..]);
756 assert_eq!(&mapped_bytes[..page_size - offset], &vec![ch; page_size - offset][..]);
757 }
758
759 #[test_case(0)]
760 #[test_case(1)]
761 #[test_case(7)]
762 #[test_case(8)]
763 #[test_case(9)]
764 #[test_case(128)]
765 #[test_case(zx::system_get_page_size() as usize - 1)]
766 #[test_case(zx::system_get_page_size() as usize)]
767 #[::fuchsia::test]
768 fn copyout_no_fault(buf_len: usize) {
769 let page_size = zx::system_get_page_size() as usize;
770
771 let source = vec!['a' as u8; buf_len];
772
773 let dest_vmo = zx::Vmo::create(page_size as u64).unwrap();
774
775 let root_vmar = fuchsia_runtime::vmar_root_self();
776
777 let mapped_addr = root_vmar
778 .map(0, &dest_vmo, 0, page_size, zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE)
779 .unwrap();
780
781 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size);
782
783 let result = usercopy.copyout(&source, mapped_addr);
784 assert_eq!(result, buf_len);
785
786 assert_eq!(
787 unsafe { std::slice::from_raw_parts(mapped_addr as *const u8, buf_len) },
788 &vec!['a' as u8; buf_len]
789 );
790 }
791
792 #[test_case(1, 2)]
793 #[test_case(1, 4)]
794 #[test_case(1, 8)]
795 #[test_case(1, 16)]
796 #[test_case(1, 32)]
797 #[test_case(1, 64)]
798 #[test_case(1, 128)]
799 #[test_case(1, 256)]
800 #[test_case(1, 512)]
801 #[test_case(1, 1024)]
802 #[test_case(32, 64)]
803 #[test_case(32, 128)]
804 #[test_case(32, 256)]
805 #[test_case(32, 512)]
806 #[test_case(32, 1024)]
807 #[::fuchsia::test]
808 fn copyout_fault(offset: usize, buf_len: usize) {
809 let page_size = zx::system_get_page_size() as usize;
810
811 let source = vec!['a' as u8; buf_len];
812
813 let dest_vmo = zx::Vmo::create(page_size as u64).unwrap();
814
815 let root_vmar = fuchsia_runtime::vmar_root_self();
816
817 let mapped_addr = root_vmar
818 .map(
819 0,
820 &dest_vmo,
821 0,
822 page_size * 2,
823 zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE,
824 )
825 .unwrap();
826
827 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size * 2);
828
829 let dest_addr = mapped_addr + page_size - offset;
830
831 let result = usercopy.copyout(&source, dest_addr);
832
833 assert_eq!(result, offset);
834
835 assert_eq!(
836 unsafe { std::slice::from_raw_parts(dest_addr as *const u8, offset) },
837 &vec!['a' as u8; offset][..],
838 );
839 }
840
841 #[test_case(0)]
842 #[test_case(1)]
843 #[test_case(7)]
844 #[test_case(8)]
845 #[test_case(9)]
846 #[test_case(128)]
847 #[test_case(zx::system_get_page_size() as usize - 1)]
848 #[test_case(zx::system_get_page_size() as usize)]
849 #[::fuchsia::test]
850 fn copyin_no_fault(buf_len: usize) {
851 let page_size = zx::system_get_page_size() as usize;
852
853 let mut dest = Vec::with_capacity(buf_len);
854
855 let source_vmo = zx::Vmo::create(page_size as u64).unwrap();
856
857 let root_vmar = fuchsia_runtime::vmar_root_self();
858
859 let mapped_addr = root_vmar
860 .map(0, &source_vmo, 0, page_size, zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE)
861 .unwrap();
862
863 unsafe { std::slice::from_raw_parts_mut(mapped_addr as *mut u8, buf_len) }.fill('a' as u8);
864
865 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size);
866 let dest_as_mut_ptr = dest.as_mut_ptr();
867 let (read_bytes, unread_bytes) = usercopy.copyin(mapped_addr, dest.spare_capacity_mut());
868 let expected = vec!['a' as u8; buf_len];
869 assert_eq!(read_bytes, &expected);
870 assert_eq!(unread_bytes.len(), 0);
871 assert_eq!(read_bytes.as_mut_ptr(), dest_as_mut_ptr);
872
873 unsafe { dest.set_len(buf_len) }
875 assert_eq!(dest, expected);
876 }
877
878 #[test_case(1, 2)]
879 #[test_case(1, 4)]
880 #[test_case(1, 8)]
881 #[test_case(1, 16)]
882 #[test_case(1, 32)]
883 #[test_case(1, 64)]
884 #[test_case(1, 128)]
885 #[test_case(1, 256)]
886 #[test_case(1, 512)]
887 #[test_case(1, 1024)]
888 #[test_case(32, 64)]
889 #[test_case(32, 128)]
890 #[test_case(32, 256)]
891 #[test_case(32, 512)]
892 #[test_case(32, 1024)]
893 #[::fuchsia::test]
894 fn copyin_fault(offset: usize, buf_len: usize) {
895 let page_size = zx::system_get_page_size() as usize;
896
897 let mut dest = vec![0u8; buf_len];
898
899 let source_vmo = zx::Vmo::create(page_size as u64).unwrap();
900
901 let root_vmar = fuchsia_runtime::vmar_root_self();
902
903 let mapped_addr = root_vmar
904 .map(
905 0,
906 &source_vmo,
907 0,
908 page_size * 2,
909 zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE,
910 )
911 .unwrap();
912
913 let source_addr = mapped_addr + page_size - offset;
914
915 unsafe { std::slice::from_raw_parts_mut(source_addr as *mut u8, offset) }.fill('a' as u8);
916
917 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size * 2);
918
919 let (read_bytes, unread_bytes) =
920 usercopy.copyin(source_addr, slice_to_maybe_uninit_mut(&mut dest));
921 let expected_copied = vec!['a' as u8; offset];
922 let expected_uncopied = vec![0 as u8; buf_len - offset];
923 assert_eq!(read_bytes, &expected_copied);
924 assert_eq!(unread_bytes.len(), expected_uncopied.len());
925
926 assert_eq!(&dest[0..offset], &expected_copied);
927 assert_eq!(&dest[offset..], &expected_uncopied);
928 }
929
930 #[test_case(0)]
931 #[test_case(1)]
932 #[test_case(7)]
933 #[test_case(8)]
934 #[test_case(9)]
935 #[test_case(128)]
936 #[test_case(zx::system_get_page_size() as usize - 1)]
937 #[test_case(zx::system_get_page_size() as usize)]
938 #[::fuchsia::test]
939 fn copyin_until_null_byte_no_fault(buf_len: usize) {
940 let page_size = zx::system_get_page_size() as usize;
941
942 let mut dest = Vec::with_capacity(buf_len);
943
944 let source_vmo = zx::Vmo::create(page_size as u64).unwrap();
945
946 let root_vmar = fuchsia_runtime::vmar_root_self();
947
948 let mapped_addr = root_vmar
949 .map(0, &source_vmo, 0, page_size, zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE)
950 .unwrap();
951
952 unsafe { std::slice::from_raw_parts_mut(mapped_addr as *mut u8, buf_len) }.fill('a' as u8);
953
954 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size);
955
956 let dest_as_mut_ptr = dest.as_mut_ptr();
957 let (read_bytes, unread_bytes) =
958 usercopy.copyin_until_null_byte(mapped_addr, dest.spare_capacity_mut());
959 let expected = vec!['a' as u8; buf_len];
960 assert_eq!(read_bytes, &expected);
961 assert_eq!(unread_bytes.len(), 0);
962 assert_eq!(read_bytes.as_mut_ptr(), dest_as_mut_ptr);
963
964 unsafe { dest.set_len(dest.capacity()) }
966 assert_eq!(dest, expected);
967 }
968
969 #[test_case(1, 2)]
970 #[test_case(1, 4)]
971 #[test_case(1, 8)]
972 #[test_case(1, 16)]
973 #[test_case(1, 32)]
974 #[test_case(1, 64)]
975 #[test_case(1, 128)]
976 #[test_case(1, 256)]
977 #[test_case(1, 512)]
978 #[test_case(1, 1024)]
979 #[test_case(32, 64)]
980 #[test_case(32, 128)]
981 #[test_case(32, 256)]
982 #[test_case(32, 512)]
983 #[test_case(32, 1024)]
984 #[::fuchsia::test]
985 fn copyin_until_null_byte_fault(offset: usize, buf_len: usize) {
986 let page_size = zx::system_get_page_size() as usize;
987
988 let mut dest = vec![0u8; buf_len];
989
990 let source_vmo = zx::Vmo::create(page_size as u64).unwrap();
991
992 let root_vmar = fuchsia_runtime::vmar_root_self();
993
994 let mapped_addr = root_vmar
995 .map(
996 0,
997 &source_vmo,
998 0,
999 page_size * 2,
1000 zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE,
1001 )
1002 .unwrap();
1003
1004 let source_addr = mapped_addr + page_size - offset;
1005
1006 unsafe { std::slice::from_raw_parts_mut(source_addr as *mut u8, offset) }.fill('a' as u8);
1007
1008 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size * 2);
1009
1010 let (read_bytes, unread_bytes) =
1011 usercopy.copyin_until_null_byte(source_addr, slice_to_maybe_uninit_mut(&mut dest));
1012 let expected_copied = vec!['a' as u8; offset];
1013 let expected_uncopied = vec![0 as u8; buf_len - offset];
1014 assert_eq!(read_bytes, &expected_copied);
1015 assert_eq!(unread_bytes.len(), expected_uncopied.len());
1016
1017 assert_eq!(&dest[0..offset], &expected_copied);
1018 assert_eq!(&dest[offset..], &expected_uncopied);
1019 }
1020
1021 #[test_case(0)]
1022 #[test_case(1)]
1023 #[test_case(2)]
1024 #[test_case(126)]
1025 #[test_case(127)]
1026 #[::fuchsia::test]
1027 fn copyin_until_null_byte_no_fault_with_zero(zero_idx: usize) {
1028 const DEST_LEN: usize = 128;
1029
1030 let page_size = zx::system_get_page_size() as usize;
1031
1032 let mut dest = vec!['b' as u8; DEST_LEN];
1033
1034 let source_vmo = zx::Vmo::create(page_size as u64).unwrap();
1035
1036 let root_vmar = fuchsia_runtime::vmar_root_self();
1037
1038 let mapped_addr = root_vmar
1039 .map(0, &source_vmo, 0, page_size, zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE)
1040 .unwrap();
1041
1042 {
1043 let slice =
1044 unsafe { std::slice::from_raw_parts_mut(mapped_addr as *mut u8, dest.len()) };
1045 slice.fill('a' as u8);
1046 slice[zero_idx] = 0;
1047 };
1048
1049 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size);
1050
1051 let (read_bytes, unread_bytes) =
1052 usercopy.copyin_until_null_byte(mapped_addr, slice_to_maybe_uninit_mut(&mut dest));
1053 let expected_copied_non_zero_bytes = vec!['a' as u8; zero_idx];
1054 let expected_uncopied = vec!['b' as u8; DEST_LEN - zero_idx - 1];
1055 assert_eq!(&read_bytes[..zero_idx], &expected_copied_non_zero_bytes);
1056 assert_eq!(&read_bytes[zero_idx..], &[0]);
1057 assert_eq!(unread_bytes.len(), expected_uncopied.len());
1058
1059 assert_eq!(&dest[..zero_idx], &expected_copied_non_zero_bytes);
1060 assert_eq!(dest[zero_idx], 0);
1061 assert_eq!(&dest[zero_idx + 1..], &expected_uncopied);
1062 }
1063
1064 #[test_case(0..1, 0)]
1065 #[test_case(0..1, 1)]
1066 #[test_case(0..1, 2)]
1067 #[test_case(5..10, 0)]
1068 #[test_case(5..10, 1)]
1069 #[test_case(5..10, 2)]
1070 #[test_case(5..10, 5)]
1071 #[test_case(5..10, 7)]
1072 #[test_case(5..10, 10)]
1073 #[::fuchsia::test]
1074 fn starting_fault_address_copyin_until_null_byte(range: Range<usize>, addr: usize) {
1075 let usercopy = Usercopy::new_for_test(range);
1076
1077 let mut dest = vec![0u8];
1078
1079 let (read_bytes, unread_bytes) =
1080 usercopy.copyin_until_null_byte(addr, slice_to_maybe_uninit_mut(&mut dest));
1081 assert_eq!(read_bytes, &[] as &[u8]);
1082 assert_eq!(unread_bytes.len(), dest.len());
1083 assert_eq!(dest, [0]);
1084 }
1085
1086 #[test_case(0..1, 0)]
1087 #[test_case(0..1, 1)]
1088 #[test_case(0..1, 2)]
1089 #[test_case(5..10, 0)]
1090 #[test_case(5..10, 1)]
1091 #[test_case(5..10, 2)]
1092 #[test_case(5..10, 5)]
1093 #[test_case(5..10, 7)]
1094 #[test_case(5..10, 10)]
1095 #[::fuchsia::test]
1096 fn starting_fault_address_copyin(range: Range<usize>, addr: usize) {
1097 let usercopy = Usercopy::new_for_test(range);
1098
1099 let mut dest = vec![0u8];
1100
1101 let (read_bytes, unread_bytes) =
1102 usercopy.copyin(addr, slice_to_maybe_uninit_mut(&mut dest));
1103 assert_eq!(read_bytes, &[] as &[u8]);
1104 assert_eq!(unread_bytes.len(), dest.len());
1105 assert_eq!(dest, [0]);
1106 }
1107
1108 #[test_case(0..1, 0)]
1109 #[test_case(0..1, 1)]
1110 #[test_case(0..1, 2)]
1111 #[test_case(5..10, 0)]
1112 #[test_case(5..10, 1)]
1113 #[test_case(5..10, 2)]
1114 #[test_case(5..10, 5)]
1115 #[test_case(5..10, 7)]
1116 #[test_case(5..10, 10)]
1117 #[::fuchsia::test]
1118 fn starting_fault_address_copyout(range: Range<usize>, addr: usize) {
1119 let usercopy = Usercopy::new_for_test(range);
1120
1121 let source = vec![0u8];
1122
1123 let result = usercopy.copyout(&source, addr);
1124 assert_eq!(result, 0);
1125 assert_eq!(source, [0]);
1126 }
1127 struct MappedPageUsercopy {
1128 usercopy: Usercopy,
1129 addr: usize,
1130 }
1131
1132 impl MappedPageUsercopy {
1133 fn new(flags: zx::VmarFlags) -> Self {
1134 let page_size = zx::system_get_page_size() as usize;
1135
1136 let vmo = zx::Vmo::create(page_size as u64).unwrap();
1137
1138 let root_vmar = fuchsia_runtime::vmar_root_self();
1139
1140 let addr = root_vmar.map(0, &vmo, 0, page_size, flags).unwrap();
1141
1142 let usercopy = Usercopy::new_for_test(addr..addr + page_size);
1143 Self { usercopy, addr }
1144 }
1145 }
1146
1147 impl std::ops::Drop for MappedPageUsercopy {
1148 fn drop(&mut self) {
1149 let page_size = zx::system_get_page_size() as usize;
1150
1151 unsafe { fuchsia_runtime::vmar_root_self().unmap(self.addr, page_size) }.unwrap();
1152 }
1153 }
1154
1155 #[test_case(|usercopy, mapped_addr| usercopy.atomic_load_u32_relaxed(mapped_addr); "relaxed")]
1156 #[test_case(|usercopy, mapped_addr| usercopy.atomic_load_u32_acquire(mapped_addr); "acquire")]
1157 #[::fuchsia::test]
1158 fn atomic_load_u32_no_fault(load_fn: fn(&Usercopy, usize) -> Result<u32, ()>) {
1159 let m = MappedPageUsercopy::new(zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE);
1160
1161 unsafe { *(m.addr as *mut u32) = 0x12345678 };
1162
1163 let result = load_fn(&m.usercopy, m.addr);
1164
1165 assert_eq!(Ok(0x12345678), result);
1166 }
1167
1168 #[test_case(|usercopy, mapped_addr| usercopy.atomic_load_u32_relaxed(mapped_addr); "relaxed")]
1169 #[test_case(|usercopy, mapped_addr| usercopy.atomic_load_u32_acquire(mapped_addr); "acquire")]
1170 #[::fuchsia::test]
1171 fn atomic_load_u32_fault(load_fn: fn(&Usercopy, usize) -> Result<u32, ()>) {
1172 let m = MappedPageUsercopy::new(zx::VmarFlags::empty());
1173
1174 let result = load_fn(&m.usercopy, m.addr);
1175 assert_eq!(Err(()), result);
1176 }
1177
1178 #[test_case(|usercopy, mapped_addr, val| usercopy.atomic_store_u32_relaxed(mapped_addr, val); "relaxed")]
1179 #[test_case(|usercopy, mapped_addr, val| usercopy.atomic_store_u32_release(mapped_addr, val); "release")]
1180 #[::fuchsia::test]
1181 fn atomic_store_u32_no_fault(store_fn: fn(&Usercopy, usize, u32) -> Result<(), ()>) {
1182 let m = MappedPageUsercopy::new(zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE);
1183
1184 assert_eq!(store_fn(&m.usercopy, m.addr, 0x12345678), Ok(()));
1185
1186 assert_eq!(unsafe { *(m.addr as *mut u32) }, 0x12345678);
1187 }
1188
1189 #[test_case(|usercopy, mapped_addr, val| usercopy.atomic_store_u32_relaxed(mapped_addr, val); "relaxed")]
1190 #[test_case(|usercopy, mapped_addr, val| usercopy.atomic_store_u32_release(mapped_addr, val); "release")]
1191 #[::fuchsia::test]
1192 fn atomic_store_u32_fault(store_fn: fn(&Usercopy, usize, u32) -> Result<(), ()>) {
1193 let m = MappedPageUsercopy::new(zx::VmarFlags::empty());
1194
1195 let result = store_fn(&m.usercopy, m.addr, 0x12345678);
1196 assert_eq!(Err(()), result);
1197
1198 let page_size = zx::system_get_page_size() as usize;
1199 unsafe {
1200 fuchsia_runtime::vmar_root_self().protect(m.addr, page_size, zx::VmarFlags::PERM_READ)
1201 }
1202 .unwrap();
1203
1204 assert_ne!(unsafe { *(m.addr as *mut u32) }, 0x12345678);
1205 }
1206
1207 #[::fuchsia::test]
1208 fn atomic_compare_exchange_u32_acq_rel_no_fault() {
1209 let m = MappedPageUsercopy::new(zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE);
1210
1211 unsafe { *(m.addr as *mut u32) = 0x12345678 };
1212
1213 assert_eq!(
1214 m.usercopy.atomic_compare_exchange_u32_acq_rel(m.addr, 0x12345678, 0xffffffff),
1215 Ok(Ok(0x12345678))
1216 );
1217
1218 assert_eq!(unsafe { *(m.addr as *mut u32) }, 0xffffffff);
1219
1220 assert_eq!(
1221 m.usercopy.atomic_compare_exchange_u32_acq_rel(m.addr, 0x22222222, 0x11111111),
1222 Ok(Err(0xffffffff))
1223 );
1224
1225 assert_eq!(unsafe { *(m.addr as *mut u32) }, 0xffffffff);
1226 }
1227
1228 #[::fuchsia::test]
1229 fn atomic_compare_exchange_u32_acq_rel_fault() {
1230 let m = MappedPageUsercopy::new(zx::VmarFlags::empty());
1231
1232 let result = m.usercopy.atomic_compare_exchange_u32_acq_rel(m.addr, 0x00000000, 0x11111111);
1233 assert_eq!(Err(()), result);
1234
1235 let page_size = zx::system_get_page_size() as usize;
1236 unsafe {
1237 fuchsia_runtime::vmar_root_self().protect(m.addr, page_size, zx::VmarFlags::PERM_READ)
1238 }
1239 .unwrap();
1240
1241 assert_eq!(unsafe { *(m.addr as *mut u32) }, 0x00000000);
1242 }
1243}