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