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::<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(e);
329 return;
330 }
331 };
332
333 let _ = tx.send(zx::Status::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 match rx.recv().unwrap() {
402 zx::Status::OK => {}
403 s => {
404 return Err(s);
405 }
406 };
407
408 Ok(Self { shutdown_event, join_handle: Some(join_handle), restricted_address_range })
409 }
410
411 pub unsafe fn raw_hermetic_copy(
420 &self,
421 dest: *mut u8,
422 source: *const u8,
423 count: usize,
424 ret_dest: bool,
425 ) -> usize {
426 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
427 unsafe {
428 do_hermetic_copy(hermetic_copy, dest as usize, source as usize, count, ret_dest)
429 }
430 }
431
432 pub fn zero(&self, dest_addr: usize, count: usize) -> usize {
436 if dest_addr == 0 || !self.restricted_address_range.contains(&dest_addr) {
440 return 0;
441 }
442
443 #[allow(
444 clippy::undocumented_unsafe_blocks,
445 reason = "Force documented unsafe blocks in Starnix"
446 )]
447 let unset_address = unsafe { hermetic_zero(dest_addr as *mut u8, count) };
448 debug_assert!(
449 unset_address >= dest_addr,
450 "unset_address={:#x}, dest_addr={:#x}",
451 unset_address,
452 dest_addr,
453 );
454 let bytes_set = unset_address - dest_addr;
455 debug_assert!(
456 bytes_set <= count,
457 "bytes_set={}, count={}; unset_address={:#x}, dest_addr={:#x}",
458 bytes_set,
459 count,
460 unset_address,
461 dest_addr,
462 );
463 bytes_set
464 }
465
466 pub fn copyout(&self, source: &[u8], dest_addr: usize) -> usize {
470 if dest_addr == 0 || !self.restricted_address_range.contains(&dest_addr) {
474 return 0;
475 }
476
477 unsafe {
480 do_hermetic_copy(hermetic_copy, dest_addr, source.as_ptr() as usize, source.len(), true)
481 }
482 }
483
484 pub fn copyin<'a>(
492 &self,
493 source_addr: usize,
494 dest: &'a mut [MaybeUninit<u8>],
495 ) -> (&'a mut [u8], &'a mut [MaybeUninit<u8>]) {
496 let read_count =
500 if source_addr == 0 || !self.restricted_address_range.contains(&source_addr) {
501 0
502 } else {
503 unsafe {
506 do_hermetic_copy(
507 hermetic_copy,
508 dest.as_ptr() as usize,
509 source_addr,
510 dest.len(),
511 false,
512 )
513 }
514 };
515
516 unsafe { assume_initialized_until(dest, read_count) }
518 }
519
520 pub fn copyin_until_null_byte<'a>(
530 &self,
531 source_addr: usize,
532 dest: &'a mut [MaybeUninit<u8>],
533 ) -> (&'a mut [u8], &'a mut [MaybeUninit<u8>]) {
534 let read_count =
538 if source_addr == 0 || !self.restricted_address_range.contains(&source_addr) {
539 0
540 } else {
541 unsafe {
544 do_hermetic_copy(
545 hermetic_copy_until_null_byte,
546 dest.as_ptr() as usize,
547 source_addr,
548 dest.len(),
549 false,
550 )
551 }
552 };
553
554 unsafe { assume_initialized_until(dest, read_count) }
556 }
557
558 #[inline]
559 fn atomic_load_u32(
560 &self,
561 load_fn: unsafe extern "C" fn(usize) -> u64,
562 addr: usize,
563 ) -> Result<u32, ()> {
564 #[allow(
565 clippy::undocumented_unsafe_blocks,
566 reason = "Force documented unsafe blocks in Starnix"
567 )]
568 let value_or_error = unsafe { load_fn(addr) };
569 if value_or_error & ATOMIC_ERROR_MASK == 0 { Ok(value_or_error as u32) } else { Err(()) }
570 }
571
572 pub fn atomic_load_u32_relaxed(&self, addr: usize) -> Result<u32, ()> {
575 self.atomic_load_u32(atomic_load_u32_relaxed, addr)
576 }
577
578 pub fn atomic_load_u32_acquire(&self, addr: usize) -> Result<u32, ()> {
581 self.atomic_load_u32(atomic_load_u32_acquire, addr)
582 }
583
584 fn atomic_store_u32(
585 &self,
586 store_fn: unsafe extern "C" fn(usize, u32) -> u64,
587 addr: usize,
588 value: u32,
589 ) -> Result<(), ()> {
590 #[allow(
591 clippy::undocumented_unsafe_blocks,
592 reason = "Force documented unsafe blocks in Starnix"
593 )]
594 match unsafe { store_fn(addr, value) } {
595 0 => Ok(()),
596 _ => Err(()),
597 }
598 }
599
600 pub fn atomic_store_u32_relaxed(&self, addr: usize, value: u32) -> Result<(), ()> {
603 self.atomic_store_u32(atomic_store_u32_relaxed, addr, value)
604 }
605
606 pub fn atomic_store_u32_release(&self, addr: usize, value: u32) -> Result<(), ()> {
609 self.atomic_store_u32(atomic_store_u32_release, addr, value)
610 }
611
612 pub fn atomic_compare_exchange_u32_acq_rel(
615 &self,
616 addr: usize,
617 expected: u32,
618 desired: u32,
619 ) -> Result<Result<u32, u32>, ()> {
620 let mut expected = expected;
621 #[allow(
622 clippy::undocumented_unsafe_blocks,
623 reason = "Force documented unsafe blocks in Starnix"
624 )]
625 let value_or_error = unsafe {
626 atomic_compare_exchange_u32_acq_rel(addr, &mut expected as *mut u32, desired)
627 };
628 Self::parse_compare_exchange_result(expected, value_or_error)
629 }
630
631 pub fn atomic_compare_exchange_weak_u32_acq_rel(
634 &self,
635 addr: usize,
636 expected: u32,
637 desired: u32,
638 ) -> Result<Result<u32, u32>, ()> {
639 let mut expected = expected;
640 #[allow(
641 clippy::undocumented_unsafe_blocks,
642 reason = "Force documented unsafe blocks in Starnix"
643 )]
644 let value_or_error = unsafe {
645 atomic_compare_exchange_weak_u32_acq_rel(addr, &mut expected as *mut u32, desired)
646 };
647 Self::parse_compare_exchange_result(expected, value_or_error)
648 }
649
650 fn parse_compare_exchange_result(
651 expected: u32,
652 value_or_error: u64,
653 ) -> Result<Result<u32, u32>, ()> {
654 match value_or_error {
655 0 => Ok(Err(expected)),
656 1 => Ok(Ok(expected)),
657 _ => Err(()),
658 }
659 }
660}
661
662impl Drop for Usercopy {
663 fn drop(&mut self) {
664 self.shutdown_event.signal(zx::Signals::empty(), zx::Signals::USER_0).unwrap();
665 self.join_handle.take().unwrap().join().unwrap();
666 }
667}
668
669#[cfg(test)]
670mod test {
671 #![allow(
672 clippy::undocumented_unsafe_blocks,
673 reason = "Force documented unsafe blocks in Starnix"
674 )]
675 use super::*;
676
677 use test_case::test_case;
678
679 impl Usercopy {
680 fn new_for_test(restricted_address_range: Range<usize>) -> Self {
681 Self::new(restricted_address_range).unwrap()
682 }
683 }
684
685 #[test_case(0, 0)]
686 #[test_case(1, 1)]
687 #[test_case(7, 2)]
688 #[test_case(8, 3)]
689 #[test_case(9, 4)]
690 #[test_case(128, 5)]
691 #[test_case(zx::system_get_page_size() as usize - 1, 6)]
692 #[test_case(zx::system_get_page_size() as usize, 7)]
693 #[::fuchsia::test]
694 fn zero_no_fault(zero_len: usize, ch: u8) {
695 let page_size = zx::system_get_page_size() as usize;
696
697 let dest_vmo = zx::Vmo::create(page_size as u64).unwrap();
698
699 let root_vmar = fuchsia_runtime::vmar_root_self();
700
701 let mapped_addr = root_vmar
702 .map(0, &dest_vmo, 0, page_size, zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE)
703 .unwrap();
704 let mapped_bytes =
705 unsafe { std::slice::from_raw_parts_mut(mapped_addr as *mut u8, page_size) };
706 mapped_bytes.fill(ch);
707
708 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size);
709
710 let result = usercopy.zero(mapped_addr, zero_len);
711 assert_eq!(result, zero_len);
712
713 assert_eq!(&mapped_bytes[..zero_len], &vec![0; zero_len]);
714 assert_eq!(&mapped_bytes[zero_len..], &vec![ch; page_size - zero_len]);
715 }
716
717 #[test_case(1, 2, 0)]
718 #[test_case(1, 4, 1)]
719 #[test_case(1, 8, 2)]
720 #[test_case(1, 16, 3)]
721 #[test_case(1, 32, 4)]
722 #[test_case(1, 64, 5)]
723 #[test_case(1, 128, 6)]
724 #[test_case(1, 256, 7)]
725 #[test_case(1, 512, 8)]
726 #[test_case(1, 1024, 9)]
727 #[test_case(32, 64, 10)]
728 #[test_case(32, 128, 11)]
729 #[test_case(32, 256, 12)]
730 #[test_case(32, 512, 13)]
731 #[test_case(32, 1024, 14)]
732 #[::fuchsia::test]
733 fn zero_fault(offset: usize, zero_len: usize, ch: u8) {
734 let page_size = zx::system_get_page_size() as usize;
735
736 let dest_vmo = zx::Vmo::create(page_size as u64).unwrap();
737
738 let root_vmar = fuchsia_runtime::vmar_root_self();
739
740 let mapped_addr = root_vmar
741 .map(
742 0,
743 &dest_vmo,
744 0,
745 page_size * 2,
746 zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE,
747 )
748 .unwrap();
749 let mapped_bytes =
750 unsafe { std::slice::from_raw_parts_mut(mapped_addr as *mut u8, page_size) };
751 mapped_bytes.fill(ch);
752
753 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size * 2);
754
755 let dest_addr = mapped_addr + page_size - offset;
756
757 let result = usercopy.zero(dest_addr, zero_len);
758 assert_eq!(result, offset);
759
760 assert_eq!(&mapped_bytes[page_size - offset..], &vec![0; offset][..]);
761 assert_eq!(&mapped_bytes[..page_size - offset], &vec![ch; page_size - offset][..]);
762 }
763
764 #[test_case(0)]
765 #[test_case(1)]
766 #[test_case(7)]
767 #[test_case(8)]
768 #[test_case(9)]
769 #[test_case(128)]
770 #[test_case(zx::system_get_page_size() as usize - 1)]
771 #[test_case(zx::system_get_page_size() as usize)]
772 #[::fuchsia::test]
773 fn copyout_no_fault(buf_len: usize) {
774 let page_size = zx::system_get_page_size() as usize;
775
776 let source = vec!['a' as u8; buf_len];
777
778 let dest_vmo = zx::Vmo::create(page_size as u64).unwrap();
779
780 let root_vmar = fuchsia_runtime::vmar_root_self();
781
782 let mapped_addr = root_vmar
783 .map(0, &dest_vmo, 0, page_size, zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE)
784 .unwrap();
785
786 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size);
787
788 let result = usercopy.copyout(&source, mapped_addr);
789 assert_eq!(result, buf_len);
790
791 assert_eq!(
792 unsafe { std::slice::from_raw_parts(mapped_addr as *const u8, buf_len) },
793 &vec!['a' as u8; buf_len]
794 );
795 }
796
797 #[test_case(1, 2)]
798 #[test_case(1, 4)]
799 #[test_case(1, 8)]
800 #[test_case(1, 16)]
801 #[test_case(1, 32)]
802 #[test_case(1, 64)]
803 #[test_case(1, 128)]
804 #[test_case(1, 256)]
805 #[test_case(1, 512)]
806 #[test_case(1, 1024)]
807 #[test_case(32, 64)]
808 #[test_case(32, 128)]
809 #[test_case(32, 256)]
810 #[test_case(32, 512)]
811 #[test_case(32, 1024)]
812 #[::fuchsia::test]
813 fn copyout_fault(offset: usize, buf_len: usize) {
814 let page_size = zx::system_get_page_size() as usize;
815
816 let source = vec!['a' as u8; buf_len];
817
818 let dest_vmo = zx::Vmo::create(page_size as u64).unwrap();
819
820 let root_vmar = fuchsia_runtime::vmar_root_self();
821
822 let mapped_addr = root_vmar
823 .map(
824 0,
825 &dest_vmo,
826 0,
827 page_size * 2,
828 zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE,
829 )
830 .unwrap();
831
832 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size * 2);
833
834 let dest_addr = mapped_addr + page_size - offset;
835
836 let result = usercopy.copyout(&source, dest_addr);
837
838 assert_eq!(result, offset);
839
840 assert_eq!(
841 unsafe { std::slice::from_raw_parts(dest_addr as *const u8, offset) },
842 &vec!['a' as u8; offset][..],
843 );
844 }
845
846 #[test_case(0)]
847 #[test_case(1)]
848 #[test_case(7)]
849 #[test_case(8)]
850 #[test_case(9)]
851 #[test_case(128)]
852 #[test_case(zx::system_get_page_size() as usize - 1)]
853 #[test_case(zx::system_get_page_size() as usize)]
854 #[::fuchsia::test]
855 fn copyin_no_fault(buf_len: usize) {
856 let page_size = zx::system_get_page_size() as usize;
857
858 let mut dest = Vec::with_capacity(buf_len);
859
860 let source_vmo = zx::Vmo::create(page_size as u64).unwrap();
861
862 let root_vmar = fuchsia_runtime::vmar_root_self();
863
864 let mapped_addr = root_vmar
865 .map(0, &source_vmo, 0, page_size, zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE)
866 .unwrap();
867
868 unsafe { std::slice::from_raw_parts_mut(mapped_addr as *mut u8, buf_len) }.fill('a' as u8);
869
870 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size);
871 let dest_as_mut_ptr = dest.as_mut_ptr();
872 let (read_bytes, unread_bytes) = usercopy.copyin(mapped_addr, dest.spare_capacity_mut());
873 let expected = vec!['a' as u8; buf_len];
874 assert_eq!(read_bytes, &expected);
875 assert_eq!(unread_bytes.len(), 0);
876 assert_eq!(read_bytes.as_mut_ptr(), dest_as_mut_ptr);
877
878 unsafe { dest.set_len(buf_len) }
880 assert_eq!(dest, expected);
881 }
882
883 #[test_case(1, 2)]
884 #[test_case(1, 4)]
885 #[test_case(1, 8)]
886 #[test_case(1, 16)]
887 #[test_case(1, 32)]
888 #[test_case(1, 64)]
889 #[test_case(1, 128)]
890 #[test_case(1, 256)]
891 #[test_case(1, 512)]
892 #[test_case(1, 1024)]
893 #[test_case(32, 64)]
894 #[test_case(32, 128)]
895 #[test_case(32, 256)]
896 #[test_case(32, 512)]
897 #[test_case(32, 1024)]
898 #[::fuchsia::test]
899 fn copyin_fault(offset: usize, buf_len: usize) {
900 let page_size = zx::system_get_page_size() as usize;
901
902 let mut dest = vec![0u8; buf_len];
903
904 let source_vmo = zx::Vmo::create(page_size as u64).unwrap();
905
906 let root_vmar = fuchsia_runtime::vmar_root_self();
907
908 let mapped_addr = root_vmar
909 .map(
910 0,
911 &source_vmo,
912 0,
913 page_size * 2,
914 zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE,
915 )
916 .unwrap();
917
918 let source_addr = mapped_addr + page_size - offset;
919
920 unsafe { std::slice::from_raw_parts_mut(source_addr as *mut u8, offset) }.fill('a' as u8);
921
922 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size * 2);
923
924 let (read_bytes, unread_bytes) =
925 usercopy.copyin(source_addr, slice_to_maybe_uninit_mut(&mut dest));
926 let expected_copied = vec!['a' as u8; offset];
927 let expected_uncopied = vec![0 as u8; buf_len - offset];
928 assert_eq!(read_bytes, &expected_copied);
929 assert_eq!(unread_bytes.len(), expected_uncopied.len());
930
931 assert_eq!(&dest[0..offset], &expected_copied);
932 assert_eq!(&dest[offset..], &expected_uncopied);
933 }
934
935 #[test_case(0)]
936 #[test_case(1)]
937 #[test_case(7)]
938 #[test_case(8)]
939 #[test_case(9)]
940 #[test_case(128)]
941 #[test_case(zx::system_get_page_size() as usize - 1)]
942 #[test_case(zx::system_get_page_size() as usize)]
943 #[::fuchsia::test]
944 fn copyin_until_null_byte_no_fault(buf_len: usize) {
945 let page_size = zx::system_get_page_size() as usize;
946
947 let mut dest = Vec::with_capacity(buf_len);
948
949 let source_vmo = zx::Vmo::create(page_size as u64).unwrap();
950
951 let root_vmar = fuchsia_runtime::vmar_root_self();
952
953 let mapped_addr = root_vmar
954 .map(0, &source_vmo, 0, page_size, zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE)
955 .unwrap();
956
957 unsafe { std::slice::from_raw_parts_mut(mapped_addr as *mut u8, buf_len) }.fill('a' as u8);
958
959 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size);
960
961 let dest_as_mut_ptr = dest.as_mut_ptr();
962 let (read_bytes, unread_bytes) =
963 usercopy.copyin_until_null_byte(mapped_addr, dest.spare_capacity_mut());
964 let expected = vec!['a' as u8; buf_len];
965 assert_eq!(read_bytes, &expected);
966 assert_eq!(unread_bytes.len(), 0);
967 assert_eq!(read_bytes.as_mut_ptr(), dest_as_mut_ptr);
968
969 unsafe { dest.set_len(dest.capacity()) }
971 assert_eq!(dest, expected);
972 }
973
974 #[test_case(1, 2)]
975 #[test_case(1, 4)]
976 #[test_case(1, 8)]
977 #[test_case(1, 16)]
978 #[test_case(1, 32)]
979 #[test_case(1, 64)]
980 #[test_case(1, 128)]
981 #[test_case(1, 256)]
982 #[test_case(1, 512)]
983 #[test_case(1, 1024)]
984 #[test_case(32, 64)]
985 #[test_case(32, 128)]
986 #[test_case(32, 256)]
987 #[test_case(32, 512)]
988 #[test_case(32, 1024)]
989 #[::fuchsia::test]
990 fn copyin_until_null_byte_fault(offset: usize, buf_len: usize) {
991 let page_size = zx::system_get_page_size() as usize;
992
993 let mut dest = vec![0u8; buf_len];
994
995 let source_vmo = zx::Vmo::create(page_size as u64).unwrap();
996
997 let root_vmar = fuchsia_runtime::vmar_root_self();
998
999 let mapped_addr = root_vmar
1000 .map(
1001 0,
1002 &source_vmo,
1003 0,
1004 page_size * 2,
1005 zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE,
1006 )
1007 .unwrap();
1008
1009 let source_addr = mapped_addr + page_size - offset;
1010
1011 unsafe { std::slice::from_raw_parts_mut(source_addr as *mut u8, offset) }.fill('a' as u8);
1012
1013 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size * 2);
1014
1015 let (read_bytes, unread_bytes) =
1016 usercopy.copyin_until_null_byte(source_addr, slice_to_maybe_uninit_mut(&mut dest));
1017 let expected_copied = vec!['a' as u8; offset];
1018 let expected_uncopied = vec![0 as u8; buf_len - offset];
1019 assert_eq!(read_bytes, &expected_copied);
1020 assert_eq!(unread_bytes.len(), expected_uncopied.len());
1021
1022 assert_eq!(&dest[0..offset], &expected_copied);
1023 assert_eq!(&dest[offset..], &expected_uncopied);
1024 }
1025
1026 #[test_case(0)]
1027 #[test_case(1)]
1028 #[test_case(2)]
1029 #[test_case(126)]
1030 #[test_case(127)]
1031 #[::fuchsia::test]
1032 fn copyin_until_null_byte_no_fault_with_zero(zero_idx: usize) {
1033 const DEST_LEN: usize = 128;
1034
1035 let page_size = zx::system_get_page_size() as usize;
1036
1037 let mut dest = vec!['b' as u8; DEST_LEN];
1038
1039 let source_vmo = zx::Vmo::create(page_size as u64).unwrap();
1040
1041 let root_vmar = fuchsia_runtime::vmar_root_self();
1042
1043 let mapped_addr = root_vmar
1044 .map(0, &source_vmo, 0, page_size, zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE)
1045 .unwrap();
1046
1047 {
1048 let slice =
1049 unsafe { std::slice::from_raw_parts_mut(mapped_addr as *mut u8, dest.len()) };
1050 slice.fill('a' as u8);
1051 slice[zero_idx] = 0;
1052 };
1053
1054 let usercopy = Usercopy::new_for_test(mapped_addr..mapped_addr + page_size);
1055
1056 let (read_bytes, unread_bytes) =
1057 usercopy.copyin_until_null_byte(mapped_addr, slice_to_maybe_uninit_mut(&mut dest));
1058 let expected_copied_non_zero_bytes = vec!['a' as u8; zero_idx];
1059 let expected_uncopied = vec!['b' as u8; DEST_LEN - zero_idx - 1];
1060 assert_eq!(&read_bytes[..zero_idx], &expected_copied_non_zero_bytes);
1061 assert_eq!(&read_bytes[zero_idx..], &[0]);
1062 assert_eq!(unread_bytes.len(), expected_uncopied.len());
1063
1064 assert_eq!(&dest[..zero_idx], &expected_copied_non_zero_bytes);
1065 assert_eq!(dest[zero_idx], 0);
1066 assert_eq!(&dest[zero_idx + 1..], &expected_uncopied);
1067 }
1068
1069 #[test_case(0..1, 0)]
1070 #[test_case(0..1, 1)]
1071 #[test_case(0..1, 2)]
1072 #[test_case(5..10, 0)]
1073 #[test_case(5..10, 1)]
1074 #[test_case(5..10, 2)]
1075 #[test_case(5..10, 5)]
1076 #[test_case(5..10, 7)]
1077 #[test_case(5..10, 10)]
1078 #[::fuchsia::test]
1079 fn starting_fault_address_copyin_until_null_byte(range: Range<usize>, addr: usize) {
1080 let usercopy = Usercopy::new_for_test(range);
1081
1082 let mut dest = vec![0u8];
1083
1084 let (read_bytes, unread_bytes) =
1085 usercopy.copyin_until_null_byte(addr, slice_to_maybe_uninit_mut(&mut dest));
1086 assert_eq!(read_bytes, &[] as &[u8]);
1087 assert_eq!(unread_bytes.len(), dest.len());
1088 assert_eq!(dest, [0]);
1089 }
1090
1091 #[test_case(0..1, 0)]
1092 #[test_case(0..1, 1)]
1093 #[test_case(0..1, 2)]
1094 #[test_case(5..10, 0)]
1095 #[test_case(5..10, 1)]
1096 #[test_case(5..10, 2)]
1097 #[test_case(5..10, 5)]
1098 #[test_case(5..10, 7)]
1099 #[test_case(5..10, 10)]
1100 #[::fuchsia::test]
1101 fn starting_fault_address_copyin(range: Range<usize>, addr: usize) {
1102 let usercopy = Usercopy::new_for_test(range);
1103
1104 let mut dest = vec![0u8];
1105
1106 let (read_bytes, unread_bytes) =
1107 usercopy.copyin(addr, slice_to_maybe_uninit_mut(&mut dest));
1108 assert_eq!(read_bytes, &[] as &[u8]);
1109 assert_eq!(unread_bytes.len(), dest.len());
1110 assert_eq!(dest, [0]);
1111 }
1112
1113 #[test_case(0..1, 0)]
1114 #[test_case(0..1, 1)]
1115 #[test_case(0..1, 2)]
1116 #[test_case(5..10, 0)]
1117 #[test_case(5..10, 1)]
1118 #[test_case(5..10, 2)]
1119 #[test_case(5..10, 5)]
1120 #[test_case(5..10, 7)]
1121 #[test_case(5..10, 10)]
1122 #[::fuchsia::test]
1123 fn starting_fault_address_copyout(range: Range<usize>, addr: usize) {
1124 let usercopy = Usercopy::new_for_test(range);
1125
1126 let source = vec![0u8];
1127
1128 let result = usercopy.copyout(&source, addr);
1129 assert_eq!(result, 0);
1130 assert_eq!(source, [0]);
1131 }
1132 struct MappedPageUsercopy {
1133 usercopy: Usercopy,
1134 addr: usize,
1135 }
1136
1137 impl MappedPageUsercopy {
1138 fn new(flags: zx::VmarFlags) -> Self {
1139 let page_size = zx::system_get_page_size() as usize;
1140
1141 let vmo = zx::Vmo::create(page_size as u64).unwrap();
1142
1143 let root_vmar = fuchsia_runtime::vmar_root_self();
1144
1145 let addr = root_vmar.map(0, &vmo, 0, page_size, flags).unwrap();
1146
1147 let usercopy = Usercopy::new_for_test(addr..addr + page_size);
1148 Self { usercopy, addr }
1149 }
1150 }
1151
1152 impl std::ops::Drop for MappedPageUsercopy {
1153 fn drop(&mut self) {
1154 let page_size = zx::system_get_page_size() as usize;
1155
1156 unsafe { fuchsia_runtime::vmar_root_self().unmap(self.addr, page_size) }.unwrap();
1157 }
1158 }
1159
1160 #[test_case(|usercopy, mapped_addr| usercopy.atomic_load_u32_relaxed(mapped_addr); "relaxed")]
1161 #[test_case(|usercopy, mapped_addr| usercopy.atomic_load_u32_acquire(mapped_addr); "acquire")]
1162 #[::fuchsia::test]
1163 fn atomic_load_u32_no_fault(load_fn: fn(&Usercopy, usize) -> Result<u32, ()>) {
1164 let m = MappedPageUsercopy::new(zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE);
1165
1166 unsafe { *(m.addr as *mut u32) = 0x12345678 };
1167
1168 let result = load_fn(&m.usercopy, m.addr);
1169
1170 assert_eq!(Ok(0x12345678), result);
1171 }
1172
1173 #[test_case(|usercopy, mapped_addr| usercopy.atomic_load_u32_relaxed(mapped_addr); "relaxed")]
1174 #[test_case(|usercopy, mapped_addr| usercopy.atomic_load_u32_acquire(mapped_addr); "acquire")]
1175 #[::fuchsia::test]
1176 fn atomic_load_u32_fault(load_fn: fn(&Usercopy, usize) -> Result<u32, ()>) {
1177 let m = MappedPageUsercopy::new(zx::VmarFlags::empty());
1178
1179 let result = load_fn(&m.usercopy, m.addr);
1180 assert_eq!(Err(()), result);
1181 }
1182
1183 #[test_case(|usercopy, mapped_addr, val| usercopy.atomic_store_u32_relaxed(mapped_addr, val); "relaxed")]
1184 #[test_case(|usercopy, mapped_addr, val| usercopy.atomic_store_u32_release(mapped_addr, val); "release")]
1185 #[::fuchsia::test]
1186 fn atomic_store_u32_no_fault(store_fn: fn(&Usercopy, usize, u32) -> Result<(), ()>) {
1187 let m = MappedPageUsercopy::new(zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE);
1188
1189 assert_eq!(store_fn(&m.usercopy, m.addr, 0x12345678), Ok(()));
1190
1191 assert_eq!(unsafe { *(m.addr as *mut u32) }, 0x12345678);
1192 }
1193
1194 #[test_case(|usercopy, mapped_addr, val| usercopy.atomic_store_u32_relaxed(mapped_addr, val); "relaxed")]
1195 #[test_case(|usercopy, mapped_addr, val| usercopy.atomic_store_u32_release(mapped_addr, val); "release")]
1196 #[::fuchsia::test]
1197 fn atomic_store_u32_fault(store_fn: fn(&Usercopy, usize, u32) -> Result<(), ()>) {
1198 let m = MappedPageUsercopy::new(zx::VmarFlags::empty());
1199
1200 let result = store_fn(&m.usercopy, m.addr, 0x12345678);
1201 assert_eq!(Err(()), result);
1202
1203 let page_size = zx::system_get_page_size() as usize;
1204 unsafe {
1205 fuchsia_runtime::vmar_root_self().protect(m.addr, page_size, zx::VmarFlags::PERM_READ)
1206 }
1207 .unwrap();
1208
1209 assert_ne!(unsafe { *(m.addr as *mut u32) }, 0x12345678);
1210 }
1211
1212 #[::fuchsia::test]
1213 fn atomic_compare_exchange_u32_acq_rel_no_fault() {
1214 let m = MappedPageUsercopy::new(zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE);
1215
1216 unsafe { *(m.addr as *mut u32) = 0x12345678 };
1217
1218 assert_eq!(
1219 m.usercopy.atomic_compare_exchange_u32_acq_rel(m.addr, 0x12345678, 0xffffffff),
1220 Ok(Ok(0x12345678))
1221 );
1222
1223 assert_eq!(unsafe { *(m.addr as *mut u32) }, 0xffffffff);
1224
1225 assert_eq!(
1226 m.usercopy.atomic_compare_exchange_u32_acq_rel(m.addr, 0x22222222, 0x11111111),
1227 Ok(Err(0xffffffff))
1228 );
1229
1230 assert_eq!(unsafe { *(m.addr as *mut u32) }, 0xffffffff);
1231 }
1232
1233 #[::fuchsia::test]
1234 fn atomic_compare_exchange_u32_acq_rel_fault() {
1235 let m = MappedPageUsercopy::new(zx::VmarFlags::empty());
1236
1237 let result = m.usercopy.atomic_compare_exchange_u32_acq_rel(m.addr, 0x00000000, 0x11111111);
1238 assert_eq!(Err(()), result);
1239
1240 let page_size = zx::system_get_page_size() as usize;
1241 unsafe {
1242 fuchsia_runtime::vmar_root_self().protect(m.addr, page_size, zx::VmarFlags::PERM_READ)
1243 }
1244 .unwrap();
1245
1246 assert_eq!(unsafe { *(m.addr as *mut u32) }, 0x00000000);
1247 }
1248}