1use std::fmt::Debug;
23use linux_api::errno::Errno;
4use linux_api::posix_types::Pid;
5use log::*;
6use shadow_pod::Pod;
78use crate::core::worker::Worker;
9use crate::host::memory_manager::page_size;
10use crate::host::syscall::types::ForeignArrayPtr;
1112/// A utility for copying data to and from a process's memory.
13#[derive(Debug, Clone)]
14pub struct MemoryCopier {
15 pid: Pid,
16}
1718impl MemoryCopier {
19pub fn new(pid: Pid) -> Self {
20Self { pid }
21 }
2223/// Copy the region.
24 /// SAFETY: A mutable reference to the process memory must not exist.
25#[allow(clippy::uninit_vec)]
26pub unsafe fn clone_mem<T: Pod>(&self, ptr: ForeignArrayPtr<T>) -> Result<Vec<T>, Errno> {
27let mut v = Vec::with_capacity(ptr.len());
28unsafe { v.set_len(v.capacity()) };
29unsafe { self.copy_from_ptr(&mut v, ptr)? };
30Ok(v)
31 }
3233/// Copy the readable prefix of the region.
34 /// SAFETY: A mutable reference to the process memory must not exist.
35#[allow(clippy::uninit_vec)]
36pub unsafe fn clone_mem_prefix<T: Pod>(
37&self,
38 ptr: ForeignArrayPtr<T>,
39 ) -> Result<Vec<T>, Errno> {
40let mut v = Vec::with_capacity(ptr.len());
41unsafe { v.set_len(v.capacity()) };
42let copied = unsafe { self.copy_prefix_from_ptr(&mut v, ptr)? };
43 v.truncate(copied);
44Ok(v)
45 }
4647// Read as much of `ptr` as is accessible into `dst`.
48/// SAFETY: A mutable reference to the process memory must not exist.
49pub unsafe fn copy_prefix_from_ptr<T>(
50&self,
51 dst: &mut [T],
52 src: ForeignArrayPtr<T>,
53 ) -> Result<usize, Errno>
54where
55T: Pod,
56 {
57// Convert to u8
58 // SAFETY: We do not write uninitialized data into `buf`.
59let 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.
64let mut buf: &mut [u8] =
65unsafe { std::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, buf.len()) };
6667let ptr = src.cast_u8();
6869// Split at page boundaries to allow partial reads.
70let mut slices = Vec::with_capacity(buf.len().div_ceil(page_size()) + 1);
71let mut total_bytes_toread = std::cmp::min(buf.len(), ptr.len());
7273// First chunk to read is from pointer to beginning of next page.
74let prev_page_boundary = usize::from(ptr.ptr()) / page_size() * page_size();
75let next_page_boundary = prev_page_boundary + page_size();
76let mut next_bytes_toread = std::cmp::min(
77 next_page_boundary - usize::from(ptr.ptr()),
78 total_bytes_toread,
79 );
8081while next_bytes_toread > 0 {
82// Add the next chunk to read.
83let (prefix, suffix) = buf.split_at_mut(next_bytes_toread);
84 buf = suffix;
85 slices.push(prefix);
86 total_bytes_toread -= next_bytes_toread;
8788// Reads should now be page-aligned. Read a whole page at a time,
89 // up to however much is left.
90next_bytes_toread = std::cmp::min(total_bytes_toread, page_size());
91 }
92let bytes_read = unsafe { self.readv_ptrs(&mut slices, &[ptr])? };
93Ok(bytes_read / std::mem::size_of::<T>())
94 }
9596// Copy `dst` into `src`.
97/// SAFETY: A mutable reference to the process memory must not exist.
98pub unsafe fn copy_from_ptr<T: Pod>(
99&self,
100 dst: &mut [T],
101 src: ForeignArrayPtr<T>,
102 ) -> Result<(), Errno> {
103assert_eq!(dst.len(), src.len());
104// SAFETY: We do not write uninitialized data into `buf`.
105let 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.
110let buf: &mut [u8] =
111unsafe { std::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, buf.len()) };
112let ptr = src.cast_u8();
113let bytes_read = unsafe { self.readv_ptrs(&mut [buf], &[ptr])? };
114if bytes_read != buf.len() {
115warn!(
116"Tried to read {} bytes but only got {}",
117 buf.len(),
118 bytes_read
119 );
120return Err(Errno::EFAULT);
121 }
122Ok(())
123 }
124125// 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.
129unsafe fn readv_ptrs(
130&self,
131 dsts: &mut [&mut [u8]],
132 srcs: &[ForeignArrayPtr<u8>],
133 ) -> Result<usize, Errno> {
134let 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();
141let mut dsts: Vec<_> = dsts
142 .iter_mut()
143 .map(|dst: &mut &mut [u8]| -> std::io::IoSliceMut { std::io::IoSliceMut::new(dst) })
144 .collect();
145146unsafe { self.readv_iovecs(&mut dsts, &srcs) }
147 }
148149// 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.
153unsafe fn readv_iovecs(
154&self,
155 dsts: &mut [std::io::IoSliceMut],
156 srcs: &[nix::sys::uio::RemoteIoVec],
157 ) -> Result<usize, Errno> {
158trace!(
159"Reading from srcs of len {}",
160 srcs.iter().map(|s| s.len).sum::<usize>()
161 );
162trace!(
163"Reading to dsts of len {}",
164 dsts.iter().map(|d| d.len()).sum::<usize>()
165 );
166167// 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.
170let tid = Worker::with_active_host(|host| {
171 Worker::with_active_process(|process| {
172// Don't access another process's memory.
173assert_eq!(process.native_pid(), self.pid);
174let thread = process.first_live_thread_borrow(host.root()).unwrap();
175let thread = thread.borrow(host.root());
176 thread.native_tid()
177 })
178 .unwrap()
179 })
180 .unwrap();
181182let 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())?;
188189Ok(nread)
190 }
191192// 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.
195pub unsafe fn copy_to_ptr<T: Pod>(
196&self,
197 dst: ForeignArrayPtr<T>,
198 src: &[T],
199 ) -> Result<(), Errno> {
200let dst = dst.cast_u8();
201let 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.
208let src: &[u8] =
209unsafe { std::slice::from_raw_parts(src.as_ptr() as *const u8, src.len()) };
210assert_eq!(src.len(), dst.len());
211212let towrite = src.len();
213trace!("write_ptr writing {} bytes", towrite);
214let local = [std::io::IoSlice::new(src)];
215let remote = [nix::sys::uio::RemoteIoVec {
216 base: usize::from(dst.ptr()),
217 len: towrite,
218 }];
219220// 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.
226let tid = Worker::with_active_host(|host| {
227 Worker::with_active_process(|process| {
228if 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.
236self.pid
237 } else {
238let thread = process.first_live_thread_borrow(host.root()).unwrap();
239let thread = thread.borrow(host.root());
240 thread.native_tid()
241 }
242 })
243 .unwrap()
244 })
245 .unwrap();
246247let 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.
254assert_eq!(nwritten, towrite);
255Ok(())
256 }
257}