Skip to main content

starnix_core/mm/
memory_accessor.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::mm::PAGE_SIZE;
6use crate::vfs::{FsStr, FsString};
7use smallvec::SmallVec;
8use starnix_types::user_buffer::{UserBuffer, UserBuffers, UserBuffers32};
9use starnix_uapi::errors::Errno;
10use starnix_uapi::user_address::{
11    ArchSpecific, MappingMultiArchUserRef, MultiArchUserRef, UserAddress, UserAddress32,
12    UserCString, UserRef,
13};
14use starnix_uapi::user_value::UserValue;
15use starnix_uapi::{PATH_MAX, UIO_MAXIOV, errno, error, uapi};
16use std::ffi::CStr;
17use std::mem::MaybeUninit;
18use usercopy::slice_to_maybe_uninit_mut;
19use zerocopy::{FromBytes, Immutable, IntoBytes};
20
21pub type IOVecPtr = MultiArchUserRef<uapi::iovec, uapi::arch32::iovec>;
22
23pub trait MemoryAccessor {
24    /// Reads exactly `bytes.len()` bytes of memory from `addr` into `bytes`.
25    ///
26    /// In case of success, the number of bytes read will always be `bytes.len()`.
27    ///
28    /// Consider using `MemoryAccessorExt::read_memory_to_*` methods if you do not require control
29    /// over the allocation.
30    fn read_memory<'a>(
31        &self,
32        addr: UserAddress,
33        bytes: &'a mut [MaybeUninit<u8>],
34    ) -> Result<&'a mut [u8], Errno>;
35
36    /// Reads bytes starting at `addr`, continuing until either a null byte is read, `bytes.len()`
37    /// bytes have been read or no more bytes can be read from the target.
38    ///
39    /// This is used, for example, to read null-terminated strings where the exact length is not
40    /// known, only the maximum length is.
41    ///
42    /// Returns the bytes that have been read to on success.
43    fn read_memory_partial_until_null_byte<'a>(
44        &self,
45        addr: UserAddress,
46        bytes: &'a mut [MaybeUninit<u8>],
47    ) -> Result<&'a mut [u8], Errno>;
48
49    /// Reads bytes starting at `addr`, continuing until either `bytes.len()` bytes have been read
50    /// or no more bytes can be read from the target.
51    ///
52    /// This is used, for example, to read null-terminated strings where the exact length is not
53    /// known, only the maximum length is.
54    ///
55    /// Consider using `MemoryAccessorExt::read_memory_partial_to_*` methods if you do not require
56    /// control over the allocation.
57    fn read_memory_partial<'a>(
58        &self,
59        addr: UserAddress,
60        bytes: &'a mut [MaybeUninit<u8>],
61    ) -> Result<&'a mut [u8], Errno>;
62
63    /// Writes the provided bytes to `addr`.
64    ///
65    /// In case of success, the number of bytes written will always be `bytes.len()`.
66    ///
67    /// # Parameters
68    /// - `addr`: The address to write to.
69    /// - `bytes`: The bytes to write from.
70    fn write_memory(&self, addr: UserAddress, bytes: &[u8]) -> Result<usize, Errno>;
71
72    /// Writes bytes starting at `addr`, continuing until either `bytes.len()` bytes have been
73    /// written or no more bytes can be written.
74    ///
75    /// # Parameters
76    /// - `addr`: The address to write to.
77    /// - `bytes`: The bytes to write from.
78    fn write_memory_partial(&self, addr: UserAddress, bytes: &[u8]) -> Result<usize, Errno>;
79
80    /// Writes zeros starting at `addr` and continuing for `length` bytes.
81    ///
82    /// Returns the number of bytes that were zeroed.
83    fn zero(&self, addr: UserAddress, length: usize) -> Result<usize, Errno>;
84}
85
86pub trait TaskMemoryAccessor: MemoryAccessor {
87    /// Returns the maximum valid address for this memory accessor.
88    fn maximum_valid_address(&self) -> Option<UserAddress>;
89}
90
91// TODO(https://fxbug.dev/42079727): replace this with MaybeUninit::as_bytes_mut.
92#[inline]
93fn object_as_mut_bytes<T: FromBytes + Sized>(
94    object: &mut MaybeUninit<T>,
95) -> &mut [MaybeUninit<u8>] {
96    // SAFETY: T is FromBytes, which means that any bit pattern is valid. Interpreting
97    // MaybeUninit<T> as [MaybeUninit<u8>] is safe because T's alignment requirements
98    // are larger than u8.
99    unsafe {
100        std::slice::from_raw_parts_mut(
101            object.as_mut_ptr() as *mut MaybeUninit<u8>,
102            std::mem::size_of::<T>(),
103        )
104    }
105}
106
107// TODO(https://fxbug.dev/42079727): replace this with MaybeUninit::slice_as_bytes_mut.
108#[inline]
109fn slice_as_mut_bytes<T: FromBytes + Sized>(
110    slice: &mut [MaybeUninit<T>],
111) -> &mut [MaybeUninit<u8>] {
112    // SAFETY: T is FromBytes, which means that any bit pattern is valid. Interpreting T as u8
113    // is safe because T's alignment requirements are larger than u8.
114    unsafe {
115        std::slice::from_raw_parts_mut(
116            slice.as_mut_ptr() as *mut MaybeUninit<u8>,
117            slice.len() * std::mem::size_of::<T>(),
118        )
119    }
120}
121
122/// Holds the number of _elements_ read by the callback to [`read_to_vec`].
123///
124/// Used to make it clear to callers that the callback should return the number
125/// of elements read and not the number of bytes read.
126pub struct NumberOfElementsRead(pub usize);
127
128/// Performs a read into a `Vec` using the provided read function.
129///
130/// The read function returns the number of elements of type `T` read.
131///
132/// # Safety
133///
134/// The read function must only return `Ok(n)` if at least one element was read and `n` holds
135/// the number of elements of type `T` read starting from the beginning of the slice.
136#[inline]
137pub unsafe fn read_to_vec<T: FromBytes, E>(
138    max_len: usize,
139    read_fn: impl FnOnce(&mut [MaybeUninit<T>]) -> Result<NumberOfElementsRead, E>,
140) -> Result<Vec<T>, E> {
141    let mut buffer = Vec::with_capacity(max_len);
142    // We can't just pass `spare_capacity_mut` because `Vec::with_capacity`
143    // returns a `Vec` with _at least_ the requested capacity.
144    let NumberOfElementsRead(read_elements) = read_fn(&mut buffer.spare_capacity_mut()[..max_len])?;
145    debug_assert!(read_elements <= max_len, "read_elements={read_elements}, max_len={max_len}");
146    // SAFETY: The new length is equal to the number of elements successfully
147    // initialized (since `read_fn` returned successfully).
148    unsafe { buffer.set_len(read_elements) }
149    Ok(buffer)
150}
151
152/// Performs a read into an array using the provided read function.
153///
154//
155/// The read function returns `Ok(())` if the buffer was fully read to.
156///
157/// # Safety
158///
159/// The read function must only return `Ok(())` if all the bytes were read to.
160#[inline]
161pub unsafe fn read_to_array<T: FromBytes, E, const N: usize>(
162    read_fn: impl FnOnce(&mut [MaybeUninit<T>]) -> Result<(), E>,
163) -> Result<[T; N], E> {
164    // TODO(https://fxbug.dev/129314): replace with MaybeUninit::uninit_array.
165    let buffer: MaybeUninit<[MaybeUninit<T>; N]> = MaybeUninit::uninit();
166    // SAFETY: We are converting from an uninitialized array to an array
167    // of uninitialized elements which is the same. See
168    // https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#initializing-an-array-element-by-element.
169    let mut buffer = unsafe { buffer.assume_init() };
170    read_fn(&mut buffer)?;
171    // SAFETY: This is safe because we have initialized all the elements in
172    // the array (since `read_fn` returned successfully).
173    //
174    // TODO(https://fxbug.deb/129309): replace with MaybeUninit::array_assume_init.
175    let buffer = buffer.map(|a| unsafe { a.assume_init() });
176    Ok(buffer)
177}
178
179/// Performs a read into an object using the provided read function.
180///
181/// The read function returns `Ok(())` if the buffer was fully read to.
182///
183/// # Safety
184///
185/// The read function must only return `Ok(())` if all the bytes were read to.
186#[inline]
187pub unsafe fn read_to_object_as_bytes<T: FromBytes, E>(
188    read_fn: impl FnOnce(&mut [MaybeUninit<u8>]) -> Result<(), E>,
189) -> Result<T, E> {
190    let mut object = MaybeUninit::uninit();
191    read_fn(object_as_mut_bytes(&mut object))?;
192    // SAFETY: The call to `read_fn` succeeded so we know that `object`
193    // has been initialized.
194    let object = unsafe { object.assume_init() };
195    Ok(object)
196}
197
198pub trait MemoryAccessorExt: MemoryAccessor {
199    /// Reads exactly `bytes.len()` bytes of memory from `addr` into `bytes`.
200    ///
201    /// In case of success, the number of bytes read will always be `bytes.len()`.
202    ///
203    /// Consider using `MemoryAccessorExt::read_memory_to_*` methods if you do not require control
204    /// over the allocation.
205    fn read_memory_to_slice(&self, addr: UserAddress, bytes: &mut [u8]) -> Result<(), Errno> {
206        let bytes_len = bytes.len();
207        self.read_memory(addr, slice_to_maybe_uninit_mut(bytes))
208            .map(|bytes_read| debug_assert_eq!(bytes_read.len(), bytes_len))
209    }
210
211    /// Read exactly `len` bytes of memory, returning them as a a Vec.
212    fn read_memory_to_vec(&self, addr: UserAddress, len: usize) -> Result<Vec<u8>, Errno> {
213        // SAFETY: `self.read_memory` only returns `Ok` if all bytes were read to.
214        unsafe {
215            read_to_vec::<u8, _>(len, |buf| {
216                self.read_memory(addr, buf).map(|bytes_read| {
217                    debug_assert_eq!(bytes_read.len(), len);
218                    NumberOfElementsRead(len)
219                })
220            })
221        }
222    }
223
224    /// Read up to `max_len` bytes from `addr`, returning them as a Vec.
225    fn read_memory_partial_to_vec(
226        &self,
227        addr: UserAddress,
228        max_len: usize,
229    ) -> Result<Vec<u8>, Errno> {
230        // SAFETY: `self.read_memory_partial` returns the bytes read.
231        unsafe {
232            read_to_vec::<u8, _>(max_len, |buf| {
233                self.read_memory_partial(addr, buf)
234                    .map(|bytes_read| NumberOfElementsRead(bytes_read.len()))
235            })
236        }
237    }
238
239    /// Read exactly `N` bytes from `addr`, returning them as an array.
240    fn read_memory_to_array<const N: usize>(&self, addr: UserAddress) -> Result<[u8; N], Errno> {
241        // SAFETY: `self.read_memory` only returns `Ok` if all bytes were read to.
242        unsafe {
243            read_to_array(|buf| {
244                self.read_memory(addr, buf).map(|bytes_read| debug_assert_eq!(bytes_read.len(), N))
245            })
246        }
247    }
248
249    /// Read the contents of `buffer`, returning them as a Vec.
250    fn read_buffer(&self, buffer: &UserBuffer) -> Result<Vec<u8>, Errno> {
251        self.read_memory_to_vec(buffer.address, buffer.length)
252    }
253
254    /// Read an instance of T from `user`.
255    fn read_object<T: FromBytes>(&self, user: UserRef<T>) -> Result<T, Errno> {
256        // SAFETY: `self.read_memory` only returns `Ok` if all bytes were read to.
257        unsafe {
258            read_to_object_as_bytes(|buf| {
259                self.read_memory(user.addr(), buf)
260                    .map(|bytes_read| debug_assert_eq!(bytes_read.len(), std::mem::size_of::<T>()))
261            })
262        }
263    }
264
265    fn read_multi_arch_ptr<T64, T32>(
266        &self,
267        user: MultiArchUserRef<MultiArchUserRef<T64, T32>, MultiArchUserRef<T64, T32>>,
268    ) -> Result<MultiArchUserRef<T64, T32>, Errno> {
269        let address = if user.is_arch32() {
270            self.read_object::<UserAddress32>(user.addr().into())?.into()
271        } else {
272            self.read_object::<UserAddress>(user.addr().into())?
273        };
274        Ok(MultiArchUserRef::<T64, T32>::new(&user, address))
275    }
276
277    /// Read an instance of T64 from `user` where the object has a different representation in 32
278    /// and 64 bits.
279    fn read_multi_arch_object<T, T64: FromBytes + TryInto<T>, T32: FromBytes + TryInto<T>>(
280        &self,
281        user: MappingMultiArchUserRef<T, T64, T32>,
282    ) -> Result<T, Errno> {
283        match user {
284            MappingMultiArchUserRef::<T, T64, T32>::Arch64(user, _) => {
285                self.read_object(user)?.try_into().map_err(|_| errno!(EINVAL))
286            }
287            MappingMultiArchUserRef::<T, T64, T32>::Arch32(user) => {
288                self.read_object(user)?.try_into().map_err(|_| errno!(EINVAL))
289            }
290        }
291    }
292
293    /// Read exactly `len` objects from `user`, returning them as a Vec.
294    fn read_multi_arch_objects_to_vec<
295        T,
296        T64: FromBytes + TryInto<T>,
297        T32: FromBytes + TryInto<T>,
298    >(
299        &self,
300        user: MappingMultiArchUserRef<T, T64, T32>,
301        len: usize,
302    ) -> Result<Vec<T>, Errno> {
303        match user {
304            MappingMultiArchUserRef::<T, T64, T32>::Arch64(user, _) => self
305                .read_objects_to_vec(user, len)?
306                .into_iter()
307                .map(TryInto::<T>::try_into)
308                .collect::<Result<Vec<T>, _>>()
309                .map_err(|_| errno!(EINVAL)),
310            MappingMultiArchUserRef::<T, T64, T32>::Arch32(user) => self
311                .read_objects_to_vec(user, len)?
312                .into_iter()
313                .map(TryInto::<T>::try_into)
314                .collect::<Result<Vec<T>, _>>()
315                .map_err(|_| errno!(EINVAL)),
316        }
317    }
318
319    /// Reads the first `partial` bytes of an object, leaving any remainder 0-filled.
320    ///
321    /// This is used for reading size-versioned structures where the user can specify an older
322    /// version of the structure with a smaller size.
323    ///
324    /// Returns EINVAL if the input size is larger than the object (assuming the input size is from
325    /// the user who has specified something we don't support).
326    fn read_object_partial<T: FromBytes>(
327        &self,
328        user: UserRef<T>,
329        partial_size: usize,
330    ) -> Result<T, Errno> {
331        let full_size = std::mem::size_of::<T>();
332        if partial_size > full_size {
333            return error!(EINVAL);
334        }
335
336        // This implementation involves an extra memcpy compared to read_object but avoids unsafe
337        // code. This isn't currently called very often.
338        let mut object = MaybeUninit::uninit();
339        let (to_read, to_zero) = object_as_mut_bytes(&mut object).split_at_mut(partial_size);
340        self.read_memory(user.addr(), to_read)?;
341
342        // Zero pad out to the correct size.
343        to_zero.fill(MaybeUninit::new(0));
344
345        // SAFETY: `T` implements `FromBytes` so any bit pattern is valid and all
346        // bytes of `object` have been initialized.
347        Ok(unsafe { object.assume_init() })
348    }
349
350    /// Read exactly `objects.len()` objects into `objects` from `user`.
351    fn read_objects<'a, T: FromBytes>(
352        &self,
353        user: UserRef<T>,
354        objects: &'a mut [MaybeUninit<T>],
355    ) -> Result<&'a mut [T], Errno> {
356        let objects_len = objects.len();
357        self.read_memory(user.addr(), slice_as_mut_bytes(objects)).map(|bytes_read| {
358            debug_assert_eq!(bytes_read.len(), objects_len * std::mem::size_of::<T>());
359            // SAFETY: `T` implements `FromBytes` and all bytes have been initialized.
360            unsafe {
361                std::slice::from_raw_parts_mut(bytes_read.as_mut_ptr() as *mut T, objects_len)
362            }
363        })
364    }
365
366    /// Read exactly `objects.len()` objects into `objects` from `user`.
367    fn read_objects_to_slice<T: FromBytes>(
368        &self,
369        user: UserRef<T>,
370        objects: &mut [T],
371    ) -> Result<(), Errno> {
372        let objects_len = objects.len();
373        self.read_objects(user, slice_to_maybe_uninit_mut(objects))
374            .map(|objects_read| debug_assert_eq!(objects_read.len(), objects_len))
375    }
376
377    /// Read exactly `len` objects from `user`, returning them as a Vec.
378    fn read_objects_to_vec<T: FromBytes>(
379        &self,
380        user: UserRef<T>,
381        len: usize,
382    ) -> Result<Vec<T>, Errno> {
383        // SAFETY: `self.read_objects` only returns `Ok` if all bytes were read to.
384        unsafe {
385            read_to_vec::<T, _>(len, |buf| {
386                self.read_objects(user, buf).map(|objects_read| {
387                    debug_assert_eq!(objects_read.len(), len);
388                    NumberOfElementsRead(len)
389                })
390            })
391        }
392    }
393
394    /// Read exactly `len` objects from `user`, returning them as a SmallVec.
395    fn read_objects_to_smallvec<T: Clone + FromBytes, const N: usize>(
396        &self,
397        user: UserRef<T>,
398        len: usize,
399    ) -> Result<SmallVec<[T; N]>, Errno> {
400        if len > N {
401            Ok(SmallVec::<[T; N]>::from_vec(self.read_objects_to_vec(user, len)?))
402        } else {
403            let mut buffer: [MaybeUninit<T>; N] = [const { MaybeUninit::uninit() }; N];
404
405            self.read_objects(user, &mut buffer[..len])?;
406
407            // TODO(https://github.com/rust-lang/rust/issues/96097) use MaybeUninit::transpose
408            // SAFETY: MaybeUninit<[T; N]> and [MaybeUninit<T>; N] have the same layout.
409            let buffer: MaybeUninit<[T; N]> = unsafe { std::mem::transmute_copy(&buffer) };
410
411            // SAFETY: `read_objects` guarantees that the first `len` entries are initialized.
412            Ok(unsafe { SmallVec::from_buf_and_len_unchecked(buffer, len) })
413        }
414    }
415
416    /// Read exactly `N` objects from `user`, returning them as an array.
417    fn read_objects_to_array<T: Copy + FromBytes, const N: usize>(
418        &self,
419        user: UserRef<T>,
420    ) -> Result<[T; N], Errno> {
421        // SAFETY: `self.read_objects` only returns `Ok` if all bytes were read to.
422        unsafe {
423            read_to_array(|buf| {
424                self.read_objects(user, buf).map(|objects_read| {
425                    debug_assert_eq!(objects_read.len(), N);
426                })
427            })
428        }
429    }
430
431    /// Read exactly `iovec_count` `UserBuffer`s from `iovec_addr`.
432    ///
433    /// Fails if `iovec_count` is greater than `UIO_MAXIOV`.
434    fn read_iovec<T: Copy + Eq + IntoBytes + FromBytes + Immutable + TryInto<usize>>(
435        &self,
436        iovec_addr: IOVecPtr,
437        iovec_count: UserValue<T>,
438    ) -> Result<UserBuffers, Errno> {
439        let iovec_count = iovec_count.raw().try_into().map_err(|_| errno!(EINVAL))?;
440        if iovec_count > UIO_MAXIOV as usize {
441            return error!(EINVAL);
442        }
443
444        if iovec_addr.is_arch32() {
445            let ub32s: UserBuffers32 =
446                self.read_objects_to_smallvec(iovec_addr.addr().into(), iovec_count)?;
447            Ok(ub32s.iter().map(|&ub32| ub32.into()).collect())
448        } else {
449            self.read_objects_to_smallvec(iovec_addr.addr().into(), iovec_count)
450        }
451    }
452
453    /// Read up to `max_size` bytes from `string`, stopping at the first discovered null byte and
454    /// returning the results as a Vec.
455    fn read_c_string_to_vec(
456        &self,
457        string: UserCString,
458        max_size: usize,
459    ) -> Result<FsString, Errno> {
460        let chunk_size = std::cmp::min(*PAGE_SIZE as usize, max_size);
461
462        let mut buf = Vec::with_capacity(chunk_size);
463        let mut index = 0;
464        loop {
465            // This operation should never overflow: we should fail to read before that.
466            let addr = string.addr().checked_add(index).ok_or_else(|| errno!(EFAULT))?;
467            let read = self.read_memory_partial_until_null_byte(
468                addr,
469                &mut buf.spare_capacity_mut()[index..][..chunk_size],
470            )?;
471            let read_len = read.len();
472
473            // Check if the last byte read is the null byte.
474            if read.last() == Some(&0) {
475                let null_index = index + read_len - 1;
476                // SAFETY: Bytes until `null_index` have been initialized.
477                unsafe { buf.set_len(null_index) }
478                if buf.len() > max_size {
479                    return error!(ENAMETOOLONG);
480                }
481
482                return Ok(buf.into());
483            }
484            index += read_len;
485
486            if read_len < chunk_size || index >= max_size {
487                // There's no more for us to read.
488                return error!(ENAMETOOLONG);
489            }
490
491            // Trigger a capacity increase.
492            buf.reserve(index + chunk_size);
493        }
494    }
495
496    /// Read a path from `path`, returning it as a `FsString`.
497    ///
498    /// A convenience function that enforces the path length limit.
499    fn read_path(&self, path: UserCString) -> Result<FsString, Errno> {
500        self.read_c_string_to_vec(path, PATH_MAX as usize)
501    }
502
503    /// Read a path from `path`, returning it as a `FsString`, if the path is non-null.
504    ///
505    /// A convenience function that enforces the path length limit.
506    fn read_path_if_non_null(&self, path: UserCString) -> Result<FsString, Errno> {
507        if path.is_null() {
508            Ok(Default::default())
509        } else {
510            self.read_c_string_to_vec(path, PATH_MAX as usize)
511        }
512    }
513
514    /// Read `len` bytes from `start` and parse the region as null-delimited CStrings, for example
515    /// how `argv` is stored.
516    ///
517    /// There can be an arbitrary number of null bytes in between `start` and `end`.
518    fn read_nul_delimited_c_string_list(
519        &self,
520        start: UserAddress,
521        len: usize,
522    ) -> Result<Vec<FsString>, Errno> {
523        let buf = self.read_memory_to_vec(start, len)?;
524        let mut buf = &buf[..];
525
526        let mut list = vec![];
527        while !buf.is_empty() {
528            let len_consumed = match CStr::from_bytes_until_nul(buf) {
529                Ok(segment) => {
530                    // Return the string without the null to match our other APIs, but advance the
531                    // "cursor" of the buf variable past the null byte.
532                    list.push(segment.to_bytes().into());
533                    segment.to_bytes_with_nul().len()
534                }
535                Err(_) => {
536                    // If we didn't find a null byte, then the whole rest of the buffer is the
537                    // last string.
538                    list.push(buf.into());
539                    buf.len()
540                }
541            };
542            buf = &buf[len_consumed..];
543        }
544
545        Ok(list)
546    }
547
548    /// Read up to `buffer.len()` bytes from `string`, stopping at the first discovered null byte
549    /// and returning the result as a slice that ends before that null.
550    ///
551    /// Consider using `read_c_string_to_vec` if you do not require control over the allocation.
552    fn read_c_string<'a>(
553        &self,
554        string: UserCString,
555        buffer: &'a mut [MaybeUninit<u8>],
556    ) -> Result<&'a FsStr, Errno> {
557        let buffer = self.read_memory_partial_until_null_byte(string.addr(), buffer)?;
558        // Make sure the last element holds the null byte.
559        if let Some((null_byte, buffer)) = buffer.split_last() {
560            if null_byte == &0 {
561                return Ok(buffer.into());
562            }
563        }
564
565        error!(ENAMETOOLONG)
566    }
567
568    /// Returns a default initialized string if `addr` is null, otherwise
569    /// behaves as `read_c_string`.
570    fn read_c_string_if_non_null<'a>(
571        &self,
572        addr: UserCString,
573        buffer: &'a mut [MaybeUninit<u8>],
574    ) -> Result<&'a FsStr, Errno> {
575        if addr.is_null() { Ok(Default::default()) } else { self.read_c_string(addr, buffer) }
576    }
577
578    fn write_object<T: IntoBytes + Immutable>(
579        &self,
580        user: UserRef<T>,
581        object: &T,
582    ) -> Result<usize, Errno> {
583        self.write_memory(user.addr(), object.as_bytes())
584    }
585
586    fn write_objects<T: IntoBytes + Immutable>(
587        &self,
588        user: UserRef<T>,
589        objects: &[T],
590    ) -> Result<usize, Errno> {
591        self.write_memory(user.addr(), objects.as_bytes())
592    }
593
594    fn write_multi_arch_ptr<Addr, T64, T32>(
595        &self,
596        user: Addr,
597        object: MultiArchUserRef<T64, T32>,
598    ) -> Result<usize, Errno>
599    where
600        Addr: Into<UserAddress>,
601    {
602        if object.is_arch32() {
603            let value = u32::try_from(object.ptr()).map_err(|_| errno!(EINVAL))?;
604            self.write_memory(user.into(), value.as_bytes())
605        } else {
606            self.write_memory(user.into(), object.ptr().as_bytes())
607        }
608    }
609
610    fn write_multi_arch_object<
611        T,
612        T64: IntoBytes + Immutable + TryFrom<T>,
613        T32: IntoBytes + Immutable + TryFrom<T>,
614    >(
615        &self,
616        user: MappingMultiArchUserRef<T, T64, T32>,
617        object: T,
618    ) -> Result<usize, Errno> {
619        match user {
620            MappingMultiArchUserRef::<T, T64, T32>::Arch64(user, _) => {
621                self.write_object(user, &T64::try_from(object).map_err(|_| errno!(EINVAL))?)
622            }
623            MappingMultiArchUserRef::<T, T64, T32>::Arch32(user) => {
624                self.write_object(user, &T32::try_from(object).map_err(|_| errno!(EINVAL))?)
625            }
626        }
627    }
628}
629
630impl MemoryAccessorExt for dyn MemoryAccessor + '_ {}
631impl<T: MemoryAccessor> MemoryAccessorExt for T {}