Skip to main content

usercopy/
lib.rs

1// Copyright 2023 The Fuchsia Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::mem::MaybeUninit;
6use std::ops::Range;
7
8use zerocopy::FromBytes;
9use zx::Task;
10
11unsafe extern "C" {
12    // This function performs a data copy like `memcpy`.
13    //
14    // Returns the last accessed destination address when `ret_dest` is `true`,
15    // or the last accessed source address when `ret_dest` is `false`.
16    fn hermetic_copy(dest: *mut u8, source: *const u8, len: usize, ret_dest: bool) -> usize;
17    fn hermetic_copy_end();
18
19    // Performs a data copy like `strncpy`.
20    //
21    // Returns the last accessed destination address when `ret_dest` is `true`,
22    // or the last accessed source address when `ret_dest` is `false`.
23    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    // This function performs a `memset` to 0.
32    //
33    // Returns the last accessed destination address.
34    fn hermetic_zero(dest: *mut u8, len: usize) -> usize;
35    fn hermetic_zero_end();
36
37    // This function generates a "return" from the usercopy routine with an error.
38    fn hermetic_copy_error();
39
40    // This generates a return from an error generated by an atomic routine.
41    fn atomic_error();
42
43    // This performs a relaxed atomic load of a 32 bit value at `addr`.
44    // On success the loaded value will be in the lower 32 bits of the returned value and the high
45    // bits will be zero. If a fault occurred, the high bits will be one.
46    fn atomic_load_u32_relaxed(addr: usize) -> u64;
47
48    // Symbol representing the end of the atomic_load_u32_relaxed() function.
49    fn atomic_load_u32_relaxed_end();
50
51    // This performs an atomic load-acquire of a 32 bit value at `addr`.
52    // On success the loaded value will be in the lower 32 bits of the returned value and the high
53    // bits will be zero. If a fault occurred, the high bits will be one.
54    fn atomic_load_u32_acquire(addr: usize) -> u64;
55
56    // Symbol representing the end of the atomic_load_u32_acquire() function.
57    fn atomic_load_u32_acquire_end();
58
59    // This performs a relaxed atomic store of a 32 bit value to `addr`.
60    // On success zero is returned. On fault a nonzero value is returned.
61    fn atomic_store_u32_relaxed(addr: usize, value: u32) -> u64;
62
63    // Symbol representing the end of the atomic_store_u32_relaxed() function.
64    fn atomic_store_u32_relaxed_end();
65
66    // This performs an atomic store-release of a 32 bit value to `addr`.
67    // On success zero is returned. On fault a nonzero value is returned.
68    fn atomic_store_u32_release(addr: usize, value: u32) -> u64;
69
70    // Symbol representing the end of the atomic_store_u32_release() function.
71    fn atomic_store_u32_release_end();
72
73    // This performs an atomic compare-and-exchange operation of the 32 bit value at `addr`.
74    // If the operation succeeded, stores `desired` to `addr` and returns 1.
75    //
76    // If the operation failed because `addr` did not contain the value `*expected`, stores the
77    // observed value to `*expected`.
78    //
79    // Memory ordering:
80    // On success, the read-modify-write has both acquire and release semantics.
81    // On failure, the load from 'addr' has acquire semantics.
82    //
83    // If the operation encountered a fault, the high bits of the returned value will be one.
84    fn atomic_compare_exchange_u32_acq_rel(addr: usize, expected: *mut u32, desired: u32) -> u64;
85
86    // Symbol representing the end of the atomic_compare_exchange_u32_acq_rel() function.
87    fn atomic_compare_exchange_u32_acq_rel_end();
88
89    // This performs an atomic compare-and-exchange operation of the 32 bit value at `addr`.
90    // If the operation succeeded, stores `desired` to `addr` and returns 1.
91    // If the operation failed (perhaps because `addr` did not contain the value `*expected`),
92    // stores the observed value to `*expected` and returns 0.
93    //
94    // This operation can fail spuriously.
95    //
96    // Memory ordering:
97    // On success, the read-modify-write has both acquire and release semantics.
98    // On failure, the load from 'addr' has acquire semantics.
99    //
100    // If the operation encountered a fault, the high bits of the returned value will be one.
101    fn atomic_compare_exchange_weak_u32_acq_rel(
102        addr: usize,
103        expected: *mut u32,
104        desired: u32,
105    ) -> u64;
106
107    // Symbol representing the end of the atomic_compare_exchange_weak_u32_relaxed() function.
108    fn atomic_compare_exchange_weak_u32_acq_rel_end();
109}
110
111/// Converts a slice to an equivalent MaybeUninit slice.
112pub 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    // SAFETY: This is effectively reinterpreting the `slice` reference as a
116    // slice of uninitialized T's. `MaybeUninit<T>` has the same layout[1] as
117    // `T` and we know the original slice is initialized and its okay to from
118    // initialized to maybe initialized.
119    //
120    // [1]: https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#layout-1
121    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    // This is an event used to signal the exception handling thread to shut down.
130    shutdown_event: zx::Event,
131
132    // Handle to the exception handling thread.
133    join_handle: Option<std::thread::JoinHandle<()>>,
134
135    // The range of the restricted address space.
136    restricted_address_range: Range<usize>,
137}
138
139/// Parses a fault exception.
140///
141/// Returns `(pc, fault_address)`, where `pc` is the address of the instruction
142/// that triggered the fault and `fault_address` is the address that faulted.
143fn 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
217/// Assumes the buffer's first `initialized_until` bytes are initialized and
218/// returns the initialized and uninitialized portions.
219///
220/// # Safety
221///
222/// The caller must guarantee that `buf`'s first `initialized_until` bytes are
223/// initialized.
224unsafe 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
239/// Copies bytes from the source address to the destination address using the
240/// provided copy function.
241///
242/// # Safety
243///
244/// Only one of `source`/`dest` may be an address to a buffer owned by user/restricted-mode.
245/// The other must be a valid Starnix/normal-mode buffer that will never cause a fault
246/// when the first `count` bytes are read/written.
247unsafe 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    /// Returns a new instance of `Usercopy` if unified address spaces is
282    /// supported on the target architecture.
283    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            // register exception handler
334            let _ = tx.send(zx::Status::OK);
335
336            // loop on exceptions
337            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                    // Only process page faults.
353                    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                // Get the address of the instruction that triggered the fault and
362                // the address that faulted. Setup the registers such that execution
363                // restarts in the `hermetic_copy_error` method with the faulting
364                // address in the platform-specific register where the first argument
365                // is held.
366                //
367                // Note that even though the registers are modified, the registers
368                // are not written to the thread's CPU until some checks below are
369                // performed.
370                let (pc, fault_address) = parse_fault_exception(&mut regs, report);
371
372                // Only handle faults if the faulting address is within the range
373                // of faultable addresses.
374                if !faultable_addresses.contains(&fault_address) {
375                    continue;
376                }
377
378                // Only handle faults that occur within one of our usercopy routines.
379                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    /// Copies bytes from the source address to the destination address.
412    ///
413    /// # Safety
414    ///
415    /// Only one of `source`/`dest` may be an address to a buffer owned by user/restricted-mode
416    /// (`ret_dest` indicates whether the user-owned buffer is `dest` when `true`).
417    /// The other must be a valid Starnix/normal-mode buffer that will never cause a fault
418    /// when the first `count` bytes are read/written.
419    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    /// Zeros `count` bytes to starting at `dest_addr`.
433    ///
434    /// Returns the number of bytes zeroed.
435    pub fn zero(&self, dest_addr: usize, count: usize) -> usize {
436        // Assumption: The address 0 is invalid and cannot be mapped.  The error encoding scheme has
437        // a collision on the value 0 - it could mean that there was a fault at the address 0 or
438        // that there was no fault. We want to treat an attempt to copy to 0 as a fault always.
439        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    /// Copies data from `source` to the restricted address `dest_addr`.
467    ///
468    /// Returns the number of bytes copied.
469    pub fn copyout(&self, source: &[u8], dest_addr: usize) -> usize {
470        // Assumption: The address 0 is invalid and cannot be mapped.  The error encoding scheme has
471        // a collision on the value 0 - it could mean that there was a fault at the address 0 or
472        // that there was no fault. We want to treat an attempt to copy to 0 as a fault always.
473        if dest_addr == 0 || !self.restricted_address_range.contains(&dest_addr) {
474            return 0;
475        }
476
477        // SAFETY: `source` is a valid Starnix-owned buffer and `dest_addr` is the user-mode
478        // buffer.
479        unsafe {
480            do_hermetic_copy(hermetic_copy, dest_addr, source.as_ptr() as usize, source.len(), true)
481        }
482    }
483
484    /// Copies data from the restricted address `source_addr` to `dest`.
485    ///
486    /// Returns the read and unread bytes.
487    ///
488    /// The returned slices will always reference `dest`. Because of this, it is
489    /// guaranteed that that `dest` and the returned initialized slice will have
490    /// the same address.
491    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        // Assumption: The address 0 is invalid and cannot be mapped.  The error encoding scheme has
497        // a collision on the value 0 - it could mean that there was a fault at the address 0 or
498        // that there was no fault. We want to treat an attempt to copy from 0 as a fault always.
499        let read_count =
500            if source_addr == 0 || !self.restricted_address_range.contains(&source_addr) {
501                0
502            } else {
503                // SAFETY: `dest` is a valid Starnix-owned buffer and `source_addr` is the user-mode
504                // buffer.
505                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        // SAFETY: `dest`'s first `read_count` bytes are initialized.
517        unsafe { assume_initialized_until(dest, read_count) }
518    }
519
520    /// Copies data from the restricted address `source_addr` to `dest` until the
521    /// first null byte.
522    ///
523    /// Returns the read and unread bytes. The read bytes includes the null byte
524    /// if present.
525    ///
526    /// The returned slices will always reference `dest`. Because of this, it is
527    /// guaranteed that that `dest` and the returned initialized slice will have
528    /// the same address.
529    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        // Assumption: The address 0 is invalid and cannot be mapped.  The error encoding scheme has
535        // a collision on the value 0 - it could mean that there was a fault at the address 0 or
536        // that there was no fault. We want to treat an attempt to copy from 0 as a fault always.
537        let read_count =
538            if source_addr == 0 || !self.restricted_address_range.contains(&source_addr) {
539                0
540            } else {
541                // SAFETY: `dest` is a valid Starnix-owned buffer and `source_addr` is the user-mode
542                // buffer.
543                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        // SAFETY: `dest`'s first `read_count` bytes are initialized
555        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    /// Performs an atomic load of a 32 bit value at `addr`.
573    /// `addr` must be aligned to 4 bytes.
574    pub fn atomic_load_u32_relaxed(&self, addr: usize) -> Result<u32, ()> {
575        self.atomic_load_u32(atomic_load_u32_relaxed, addr)
576    }
577
578    /// Performs an atomic load of a 32 bit value at `addr`.
579    /// `addr` must be aligned to 4 bytes.
580    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    /// Performs an atomic store of a 32 bit value to `addr`.
601    /// `addr` must be aligned to 4 bytes.
602    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    /// Performs an atomic store of a 32 bit value to `addr`.
607    /// `addr` must be aligned to 4 bytes.
608    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    /// Performs an atomic compare and exchange of a 32 bit value at addr `addr`.
613    /// `addr` must be aligned to 4 bytes.
614    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    /// Performs a weak atomic compare and exchange of a 32 bit value at addr `addr`.
632    /// `addr` must be aligned to 4 bytes.
633    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        // SAFETY: OK because the copyin was successful.
879        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        // SAFETY: OK because the copyin_until_null_byte was successful.
970        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}