Skip to main content

shadow_rs/host/syscall/
io.rs

1use std::ffi::CString;
2use std::mem::MaybeUninit;
3
4use linux_api::errno::Errno;
5use shadow_shim_helper_rs::syscall_types::ForeignPtr;
6
7use crate::host::memory_manager::MemoryManager;
8use crate::host::syscall::types::ForeignArrayPtr;
9use crate::utility::sockaddr::SockaddrStorage;
10
11/// Writes the socket address into a buffer at `plugin_addr` with length `plugin_addr_len`, and
12/// writes the socket address length into `plugin_addr_len`.
13///
14/// The `plugin_addr_len` pointer is a value-result argument, so it should be initialized with the
15/// size of the `plugin_addr` buffer. If the original value of `plugin_addr_len` is smaller than the
16/// socket address' length, then the written socket address will be truncated. In this case the
17/// value written to `plugin_addr_len` will be larger than its original value.
18pub fn write_sockaddr_and_len(
19    mem: &mut MemoryManager,
20    addr: Option<&SockaddrStorage>,
21    plugin_addr: ForeignPtr<u8>,
22    plugin_addr_len_ptr: ForeignPtr<libc::socklen_t>,
23) -> Result<(), Errno> {
24    let addr = match addr {
25        Some(x) => x,
26        None => {
27            mem.write(plugin_addr_len_ptr, &0)?;
28            return Ok(());
29        }
30    };
31
32    let from_addr_slice = addr.as_slice();
33    let from_len: u32 = from_addr_slice.len().try_into().unwrap();
34
35    // get the provided address buffer length, and overwrite it with the real address length
36    let plugin_addr_len = mem.read(plugin_addr_len_ptr)?;
37    mem.write(plugin_addr_len_ptr, &from_len)?;
38
39    // return early if the address length is 0
40    if plugin_addr_len == 0 {
41        return Ok(());
42    }
43
44    // the minimum of the given address buffer length and the real address length
45    let len_to_copy = std::cmp::min(from_len, plugin_addr_len).try_into().unwrap();
46
47    let plugin_addr = ForeignArrayPtr::new(plugin_addr.cast::<MaybeUninit<u8>>(), len_to_copy);
48    mem.copy_to_ptr(plugin_addr, &from_addr_slice[..len_to_copy])?;
49
50    Ok(())
51}
52
53/// Writes the socket address into a buffer at `plugin_addr` with length `plugin_addr_len`.
54///
55/// If the buffer length is smaller than the socket address length, the written address will be
56/// truncated. The length of the socket address is returned.
57pub fn write_sockaddr(
58    mem: &mut MemoryManager,
59    addr: &SockaddrStorage,
60    plugin_addr: ForeignPtr<u8>,
61    plugin_addr_len: libc::socklen_t,
62) -> Result<libc::socklen_t, Errno> {
63    let from_addr_slice = addr.as_slice();
64    let from_len: u32 = from_addr_slice.len().try_into().unwrap();
65
66    // return early if the address length is 0
67    if plugin_addr_len == 0 {
68        return Ok(from_len);
69    }
70
71    // the minimum of the given address buffer length and the real address length
72    let len_to_copy = std::cmp::min(from_len, plugin_addr_len).try_into().unwrap();
73
74    let plugin_addr = ForeignArrayPtr::new(plugin_addr.cast::<MaybeUninit<u8>>(), len_to_copy);
75    mem.copy_to_ptr(plugin_addr, &from_addr_slice[..len_to_copy])?;
76
77    Ok(from_len)
78}
79
80pub fn read_sockaddr(
81    mem: &MemoryManager,
82    addr_ptr: ForeignPtr<u8>,
83    addr_len: libc::socklen_t,
84) -> Result<Option<SockaddrStorage>, Errno> {
85    if addr_ptr.is_null() {
86        return Ok(None);
87    }
88
89    let addr_len_usize: usize = addr_len.try_into().unwrap();
90
91    // this won't have the correct alignment, but that's fine since `SockaddrStorage::from_bytes()`
92    // doesn't require alignment
93    let mut addr_buf = [MaybeUninit::new(0u8); std::mem::size_of::<libc::sockaddr_storage>()];
94
95    // make sure we will not lose data when we copy
96    if addr_len_usize > std::mem::size_of_val(&addr_buf) {
97        log::warn!(
98            "Shadow does not support the address length {}, which is larger than {}",
99            addr_len,
100            std::mem::size_of_val(&addr_buf),
101        );
102        return Err(Errno::EINVAL);
103    }
104
105    let addr_buf = &mut addr_buf[..addr_len_usize];
106
107    mem.copy_from_ptr(
108        addr_buf,
109        ForeignArrayPtr::new(addr_ptr.cast::<MaybeUninit<u8>>(), addr_len_usize),
110    )?;
111
112    let addr = unsafe { SockaddrStorage::from_bytes(addr_buf).ok_or(Errno::EINVAL)? };
113
114    Ok(Some(addr))
115}
116
117/// Writes `val` to `val_ptr`, but will only write a partial value if `val_len_bytes` is smaller
118/// than the size of `val`. Returns the number of bytes written.
119///
120/// ```no_run
121/// # use shadow_rs::host::memory_manager::MemoryManager;
122/// # use shadow_rs::host::syscall::io::write_partial;
123/// # use shadow_shim_helper_rs::syscall_types::ForeignPtr;
124/// # fn foo() -> anyhow::Result<()> {
125/// # let memory_manager: &mut MemoryManager = todo!();
126/// let ptr: ForeignPtr<u32> = todo!();
127/// let val: u32 = 0xAABBCCDD;
128/// // write a single byte of `val` (0xDD on little-endian) to `ptr`
129/// let bytes_written = write_partial(memory_manager, &val, ptr, 1)?;
130/// assert_eq!(bytes_written, 1);
131/// # Ok(())
132/// # }
133/// ```
134pub fn write_partial<T: shadow_pod::Pod>(
135    mem: &mut MemoryManager,
136    val: &T,
137    val_ptr: ForeignPtr<T>,
138    val_len_bytes: usize,
139) -> Result<usize, Errno> {
140    let val_len_bytes = std::cmp::min(val_len_bytes, std::mem::size_of_val(val));
141
142    let val = &shadow_pod::as_u8_slice(val)[..val_len_bytes];
143
144    let val_ptr = val_ptr.cast::<MaybeUninit<u8>>();
145    let val_ptr = ForeignArrayPtr::new(val_ptr, val_len_bytes);
146
147    mem.copy_to_ptr(val_ptr, val)?;
148
149    Ok(val_len_bytes)
150}
151
152/// Analogous to [`libc::msghdr`].
153pub struct MsgHdr {
154    pub name: ForeignPtr<u8>,
155    pub name_len: libc::socklen_t,
156    pub iovs: Vec<IoVec>,
157    pub control: ForeignPtr<u8>,
158    pub control_len: libc::size_t,
159    pub flags: std::ffi::c_int,
160}
161
162/// Analogous to [`libc::iovec`].
163#[derive(Copy, Clone, PartialEq, Eq)]
164pub struct IoVec {
165    pub base: ForeignPtr<u8>,
166    pub len: libc::size_t,
167}
168
169impl From<IoVec> for ForeignArrayPtr<u8> {
170    fn from(iov: IoVec) -> Self {
171        Self::new(iov.base, iov.len)
172    }
173}
174
175impl From<ForeignArrayPtr<u8>> for IoVec {
176    fn from(ptr: ForeignArrayPtr<u8>) -> Self {
177        IoVec {
178            base: ptr.ptr(),
179            len: ptr.len(),
180        }
181    }
182}
183
184/// A reader which reads data from [`IoVec`] buffers of plugin memory.
185///
186/// If an error occurs while reading (for example if an `IoVec` points to an invalid memory
187/// address), the error will be returned only if no bytes have yet been read. If an error occurs
188/// after some bytes have already been read, the [`Read::read`](std::io::Read::read) will return how
189/// many bytes have been read.
190///
191/// In the future we may want to merge this with
192/// [`MemoryReaderCursor`](crate::host::memory_manager::MemoryReaderCursor).
193pub struct IoVecReader<'a, I> {
194    iovs: I,
195    mem: &'a MemoryManager,
196    /// A foreign pointer for the current iov.
197    current_src: Option<ForeignArrayPtr<u8>>,
198}
199
200impl<'a, I> IoVecReader<'a, I> {
201    pub fn new<'b>(
202        iovs: impl IntoIterator<Item = &'b IoVec, IntoIter = I>,
203        mem: &'a MemoryManager,
204    ) -> Self {
205        Self {
206            iovs: iovs.into_iter(),
207            mem,
208            current_src: None,
209        }
210    }
211}
212
213impl<'a, I: Iterator<Item = &'a IoVec>> std::io::Read for IoVecReader<'a, I> {
214    fn read(&mut self, mut buf: &mut [u8]) -> std::io::Result<usize> {
215        let mut bytes_read = 0;
216
217        loop {
218            // we filled the buffer
219            if buf.is_empty() {
220                break;
221            }
222
223            if let Some(ref mut src) = self.current_src {
224                let num_to_read = std::cmp::min(src.len(), buf.len());
225                let result = self
226                    .mem
227                    .copy_from_ptr(&mut buf[..num_to_read], src.slice(..num_to_read));
228
229                match (result, bytes_read) {
230                    // we successfully read the bytes
231                    (Ok(()), _) => {}
232                    // we haven't yet read any bytes, so return the error
233                    (Err(e), 0) => return Err(e.into()),
234                    // return how many bytes we've read
235                    (Err(_), _) => break,
236                }
237
238                bytes_read += num_to_read;
239                buf = &mut buf[num_to_read..];
240                *src = src.slice(num_to_read..);
241
242                if src.is_empty() {
243                    // no bytes remaining in this iov
244                    self.current_src = None;
245                }
246            } else {
247                let Some(next_iov) = self.iovs.next() else {
248                    // no iovs remaining
249                    break;
250                };
251                self.current_src = Some((*next_iov).into());
252            }
253        }
254
255        Ok(bytes_read)
256    }
257}
258
259/// A writer which writes data to [`IoVec`] buffers of plugin memory.
260///
261/// If an error occurs while writing (for example if an `IoVec` points to an invalid memory
262/// address), the error will be returned only if no bytes have yet been written. If an error occurs
263/// after some bytes have already been written, the [`Write::write`](std::io::Write::write) will
264/// return how many bytes have been written.
265///
266/// In the future we may want to merge this with
267/// [`MemoryWriterCursor`](crate::host::memory_manager::MemoryWriterCursor).
268pub struct IoVecWriter<'a, I> {
269    iovs: I,
270    mem: &'a mut MemoryManager,
271    /// A foreign pointer for the current iov.
272    current_dst: Option<ForeignArrayPtr<u8>>,
273}
274
275impl<'a, I> IoVecWriter<'a, I> {
276    pub fn new<'b>(
277        iovs: impl IntoIterator<Item = &'b IoVec, IntoIter = I>,
278        mem: &'a mut MemoryManager,
279    ) -> Self {
280        Self {
281            iovs: iovs.into_iter(),
282            mem,
283            current_dst: None,
284        }
285    }
286}
287
288impl<'a, I: Iterator<Item = &'a IoVec>> std::io::Write for IoVecWriter<'a, I> {
289    fn write(&mut self, mut buf: &[u8]) -> std::io::Result<usize> {
290        let mut bytes_written = 0;
291
292        loop {
293            // no bytes left to write
294            if buf.is_empty() {
295                break;
296            }
297
298            if let Some(ref mut dst) = self.current_dst {
299                let num_to_write = std::cmp::min(dst.len(), buf.len());
300                let result = self
301                    .mem
302                    .copy_to_ptr(dst.slice(..num_to_write), &buf[..num_to_write]);
303
304                match (result, bytes_written) {
305                    // we successfully wrote the bytes
306                    (Ok(()), _) => {}
307                    // we haven't yet written any bytes, so return the error
308                    (Err(e), 0) => return Err(e.into()),
309                    // return how many bytes we've written
310                    (Err(_), _) => break,
311                }
312
313                bytes_written += num_to_write;
314                buf = &buf[num_to_write..];
315                *dst = dst.slice(num_to_write..);
316
317                if dst.is_empty() {
318                    // no space remaining in this iov
319                    self.current_dst = None;
320                }
321            } else {
322                let Some(next_iov) = self.iovs.next() else {
323                    // no iovs remaining
324                    break;
325                };
326                self.current_dst = Some((*next_iov).into());
327            }
328        }
329
330        Ok(bytes_written)
331    }
332
333    fn flush(&mut self) -> std::io::Result<()> {
334        Ok(())
335    }
336}
337
338/// Read a plugin's array of [`libc::iovec`] into a [`Vec<IoVec>`].
339pub fn read_iovecs(
340    mem: &MemoryManager,
341    iov_ptr: ForeignPtr<libc::iovec>,
342    count: usize,
343) -> Result<Vec<IoVec>, Errno> {
344    if count > libc::UIO_MAXIOV.try_into().unwrap() {
345        return Err(Errno::EINVAL);
346    }
347
348    let plugin_iovs = mem.read_vec(ForeignArrayPtr::new(iov_ptr, count))?;
349    let iovs = plugin_iovs
350        .into_iter()
351        .map(|plugin_iov| IoVec {
352            base: ForeignPtr::from_raw_ptr(plugin_iov.iov_base as *mut u8),
353            len: plugin_iov.iov_len,
354        })
355        .collect();
356
357    Ok(iovs)
358}
359
360/// Read a plugin's [`libc::msghdr`] into a [`MsgHdr`].
361pub fn read_msghdr(
362    mem: &MemoryManager,
363    msg_ptr: ForeignPtr<libc::msghdr>,
364) -> Result<MsgHdr, Errno> {
365    let plugin_msg = mem.read(msg_ptr)?;
366    msghdr_to_rust(&plugin_msg, mem)
367}
368
369/// Used to update a `libc::msghdr`. Only writes the [`libc::msghdr`] `msg_namelen`,
370/// `msg_controllen`, and `msg_flags` fields, which are the only fields that can be changed by
371/// `recvmsg()`.
372pub fn update_msghdr(
373    mem: &mut MemoryManager,
374    msg_ptr: ForeignPtr<libc::msghdr>,
375    msg: MsgHdr,
376) -> Result<(), Errno> {
377    let mut plugin_msg = mem.read(msg_ptr)?;
378
379    // write only the msg fields that may have changed
380    plugin_msg.msg_namelen = msg.name_len;
381    plugin_msg.msg_controllen = msg.control_len;
382    plugin_msg.msg_flags = msg.flags;
383
384    mem.write(msg_ptr, &plugin_msg)
385}
386
387/// Helper to read a plugin's [`libc::msghdr`] into a [`MsgHdr`]. While `msg` is a local struct, it
388/// should have been copied from plugin memory, meaning any pointers in the struct are pointers to
389/// plugin memory, not local memory.
390fn msghdr_to_rust(msg: &libc::msghdr, mem: &MemoryManager) -> Result<MsgHdr, Errno> {
391    let iovs = read_iovecs(mem, ForeignPtr::from_raw_ptr(msg.msg_iov), msg.msg_iovlen)?;
392    assert_eq!(iovs.len(), msg.msg_iovlen);
393
394    Ok(MsgHdr {
395        name: ForeignPtr::from_raw_ptr(msg.msg_name as *mut u8),
396        name_len: msg.msg_namelen,
397        iovs,
398        control: ForeignPtr::from_raw_ptr(msg.msg_control as *mut u8),
399        control_len: msg.msg_controllen,
400        flags: msg.msg_flags,
401    })
402}
403
404/// Read an array of strings, each of which with max length
405/// `linux_api::limits::ARG_MAX`.  e.g. suitable for `execve`'s argument and
406/// environment string lists.
407pub fn read_cstring_vec(
408    mem: &MemoryManager,
409    mut ptr_ptr: ForeignPtr<ForeignPtr<i8>>,
410) -> Result<Vec<CString>, Errno> {
411    let mut res = Vec::new();
412
413    // `execve(2)`: Most UNIX implementations impose some limit on the
414    // total size of the command-line  argument  (argv)  and
415    // environment  (envp) strings that may be passed to a new program.
416    // POSIX.1 allows an implementation to advertise this limit using
417    // the ARG_MAX constant
418    let mut arg_buf = [0; linux_api::limits::ARG_MAX];
419
420    loop {
421        let ptr = mem.read(ptr_ptr)?;
422        ptr_ptr = ptr_ptr.add(1);
423        if ptr.is_null() {
424            break;
425        }
426        let cstr = mem.copy_str_from_ptr(
427            &mut arg_buf,
428            ForeignArrayPtr::new(ptr.cast::<u8>(), linux_api::limits::ARG_MAX),
429        )?;
430        res.push(cstr.to_owned());
431    }
432    Ok(res)
433}