shadow_rs/host/syscall/
io.rs

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