shadow_rs/host/memory_manager/
memory_copier.rs

1use std::fmt::Debug;
2
3use linux_api::errno::Errno;
4use linux_api::posix_types::Pid;
5use log::*;
6use shadow_pod::Pod;
7
8use crate::core::worker::Worker;
9use crate::host::memory_manager::page_size;
10use crate::host::syscall::types::ForeignArrayPtr;
11
12/// A utility for copying data to and from a process's memory.
13#[derive(Debug, Clone)]
14pub struct MemoryCopier {
15    pid: Pid,
16}
17
18impl MemoryCopier {
19    pub fn new(pid: Pid) -> Self {
20        Self { pid }
21    }
22
23    /// Copy the region.
24    /// SAFETY: A mutable reference to the process memory must not exist.
25    #[allow(clippy::uninit_vec)]
26    pub unsafe fn clone_mem<T: Pod>(&self, ptr: ForeignArrayPtr<T>) -> Result<Vec<T>, Errno> {
27        let mut v = Vec::with_capacity(ptr.len());
28        unsafe { v.set_len(v.capacity()) };
29        unsafe { self.copy_from_ptr(&mut v, ptr)? };
30        Ok(v)
31    }
32
33    /// Copy the readable prefix of the region.
34    /// SAFETY: A mutable reference to the process memory must not exist.
35    #[allow(clippy::uninit_vec)]
36    pub unsafe fn clone_mem_prefix<T: Pod>(
37        &self,
38        ptr: ForeignArrayPtr<T>,
39    ) -> Result<Vec<T>, Errno> {
40        let mut v = Vec::with_capacity(ptr.len());
41        unsafe { v.set_len(v.capacity()) };
42        let copied = unsafe { self.copy_prefix_from_ptr(&mut v, ptr)? };
43        v.truncate(copied);
44        Ok(v)
45    }
46
47    // Read as much of `ptr` as is accessible into `dst`.
48    /// SAFETY: A mutable reference to the process memory must not exist.
49    pub unsafe fn copy_prefix_from_ptr<T>(
50        &self,
51        dst: &mut [T],
52        src: ForeignArrayPtr<T>,
53    ) -> Result<usize, Errno>
54    where
55        T: Pod,
56    {
57        // Convert to u8
58        // SAFETY: We do not write uninitialized data into `buf`.
59        let buf: &mut [std::mem::MaybeUninit<u8>] = unsafe { shadow_pod::to_u8_slice_mut(dst) };
60        // SAFETY: this buffer is write-only.
61        // TODO: Fix or move away from nix's process_vm_readv wrapper so that we
62        // don't need to construct this slice, and can instead only ever operate
63        // on the pointer itself.
64        let mut buf: &mut [u8] =
65            unsafe { std::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, buf.len()) };
66
67        let ptr = src.cast_u8();
68
69        // Split at page boundaries to allow partial reads.
70        let mut slices = Vec::with_capacity(buf.len().div_ceil(page_size()) + 1);
71        let mut total_bytes_toread = std::cmp::min(buf.len(), ptr.len());
72
73        // First chunk to read is from pointer to beginning of next page.
74        let prev_page_boundary = usize::from(ptr.ptr()) / page_size() * page_size();
75        let next_page_boundary = prev_page_boundary + page_size();
76        let mut next_bytes_toread = std::cmp::min(
77            next_page_boundary - usize::from(ptr.ptr()),
78            total_bytes_toread,
79        );
80
81        while next_bytes_toread > 0 {
82            // Add the next chunk to read.
83            let (prefix, suffix) = buf.split_at_mut(next_bytes_toread);
84            buf = suffix;
85            slices.push(prefix);
86            total_bytes_toread -= next_bytes_toread;
87
88            // Reads should now be page-aligned. Read a whole page at a time,
89            // up to however much is left.
90            next_bytes_toread = std::cmp::min(total_bytes_toread, page_size());
91        }
92        let bytes_read = unsafe { self.readv_ptrs(&mut slices, &[ptr])? };
93        Ok(bytes_read / std::mem::size_of::<T>())
94    }
95
96    // Copy `dst` into `src`.
97    /// SAFETY: A mutable reference to the process memory must not exist.
98    pub unsafe fn copy_from_ptr<T: Pod>(
99        &self,
100        dst: &mut [T],
101        src: ForeignArrayPtr<T>,
102    ) -> Result<(), Errno> {
103        assert_eq!(dst.len(), src.len());
104        // SAFETY: We do not write uninitialized data into `buf`.
105        let buf = unsafe { shadow_pod::to_u8_slice_mut(dst) };
106        // SAFETY: this buffer is write-only.
107        // TODO: Fix or move away from nix's process_vm_readv wrapper so that we
108        // don't need to construct this slice, and can instead only ever operate
109        // on the pointer itself.
110        let buf: &mut [u8] =
111            unsafe { std::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, buf.len()) };
112        let ptr = src.cast_u8();
113        let bytes_read = unsafe { self.readv_ptrs(&mut [buf], &[ptr])? };
114        if bytes_read != buf.len() {
115            warn!(
116                "Tried to read {} bytes but only got {}",
117                buf.len(),
118                bytes_read
119            );
120            return Err(Errno::EFAULT);
121        }
122        Ok(())
123    }
124
125    // Low level helper for reading directly from `srcs` to `dsts`.
126    // Returns the number of bytes read. Panics if the
127    // MemoryManager's process isn't currently active.
128    /// SAFETY: A mutable reference to the process memory must not exist.
129    unsafe fn readv_ptrs(
130        &self,
131        dsts: &mut [&mut [u8]],
132        srcs: &[ForeignArrayPtr<u8>],
133    ) -> Result<usize, Errno> {
134        let srcs: Vec<_> = srcs
135            .iter()
136            .map(|src| nix::sys::uio::RemoteIoVec {
137                base: usize::from(src.ptr()),
138                len: src.len(),
139            })
140            .collect();
141        let mut dsts: Vec<_> = dsts
142            .iter_mut()
143            .map(|dst: &mut &mut [u8]| -> std::io::IoSliceMut { std::io::IoSliceMut::new(dst) })
144            .collect();
145
146        unsafe { self.readv_iovecs(&mut dsts, &srcs) }
147    }
148
149    // Low level helper for reading directly from `srcs` to `dsts`.
150    // Returns the number of bytes read. Panics if the
151    // MemoryManager's process isn't currently active.
152    /// SAFETY: A mutable reference to the process memory must not exist.
153    unsafe fn readv_iovecs(
154        &self,
155        dsts: &mut [std::io::IoSliceMut],
156        srcs: &[nix::sys::uio::RemoteIoVec],
157    ) -> Result<usize, Errno> {
158        trace!(
159            "Reading from srcs of len {}",
160            srcs.iter().map(|s| s.len).sum::<usize>()
161        );
162        trace!(
163            "Reading to dsts of len {}",
164            dsts.iter().map(|d| d.len()).sum::<usize>()
165        );
166
167        // While the documentation for process_vm_readv says to use the pid, in
168        // practice it needs to be the tid of a still-running thread. i.e. using the
169        // pid after the thread group leader has exited will fail.
170        let tid = Worker::with_active_host(|host| {
171            Worker::with_active_process(|process| {
172                // Don't access another process's memory.
173                assert_eq!(process.native_pid(), self.pid);
174                let thread = process.first_live_thread_borrow(host.root()).unwrap();
175                let thread = thread.borrow(host.root());
176                thread.native_tid()
177            })
178            .unwrap()
179        })
180        .unwrap();
181
182        let nread = nix::sys::uio::process_vm_readv(
183            nix::unistd::Pid::from_raw(tid.as_raw_nonzero().get()),
184            dsts,
185            srcs,
186        )
187        .map_err(|e| Errno::try_from(e as i32).unwrap())?;
188
189        Ok(nread)
190    }
191
192    // Low level helper for writing directly to `dst`. Panics if the
193    // MemoryManager's process isn't currently active.
194    /// SAFETY: A reference to the process memory must not exist.
195    pub unsafe fn copy_to_ptr<T: Pod>(
196        &self,
197        dst: ForeignArrayPtr<T>,
198        src: &[T],
199    ) -> Result<(), Errno> {
200        let dst = dst.cast_u8();
201        let src: &[std::mem::MaybeUninit<u8>] = shadow_pod::to_u8_slice(src);
202        // SAFETY: We *should* never actually read from this buffer in this process;
203        // ultimately its pointer will be passed to the process_vm_writev syscall,
204        // for which unitialized data is ok.
205        // TODO: Fix or move away from nix's process_vm_writev wrapper so that we
206        // don't need to construct this slice, and can instead only ever operate
207        // on the pointer itself.
208        let src: &[u8] =
209            unsafe { std::slice::from_raw_parts(src.as_ptr() as *const u8, src.len()) };
210        assert_eq!(src.len(), dst.len());
211
212        let towrite = src.len();
213        trace!("write_ptr writing {} bytes", towrite);
214        let local = [std::io::IoSlice::new(src)];
215        let remote = [nix::sys::uio::RemoteIoVec {
216            base: usize::from(dst.ptr()),
217            len: towrite,
218        }];
219
220        // While the documentation for process_vm_writev says to use the pid, in
221        // practice it needs to be the tid of a still-running thread. i.e. using the
222        // pid after the thread group leader has exited will fail.
223        //
224        // TODO: get this explicitly from the caller instead of reaching out to
225        // the global Worker.
226        let tid = Worker::with_active_host(|host| {
227            Worker::with_active_process(|process| {
228                if process.native_pid() != self.pid {
229                    // This currently only happens in the clone syscall handler
230                    // if we need to write to the child's memory, and the child
231                    // is a new process. In this case the thread group leader should
232                    // be alive, so the pid will work.
233                    //
234                    // TODO: as above, this hack can be avoided by getting a live tid
235                    // explicitly from the caller.
236                    self.pid
237                } else {
238                    let thread = process.first_live_thread_borrow(host.root()).unwrap();
239                    let thread = thread.borrow(host.root());
240                    thread.native_tid()
241                }
242            })
243            .unwrap()
244        })
245        .unwrap();
246
247        let nwritten = nix::sys::uio::process_vm_writev(
248            nix::unistd::Pid::from_raw(tid.as_raw_nonzero().get()),
249            &local,
250            &remote,
251        )
252        .map_err(|e| Errno::try_from(e as i32).unwrap())?;
253        // There shouldn't be any partial writes with a single remote iovec.
254        assert_eq!(nwritten, towrite);
255        Ok(())
256    }
257}