vm_memory/
io.rs

1// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3//! Module containing versions of the standard library's [`Read`](std::io::Read) and
4//! [`Write`](std::io::Write) traits compatible with volatile memory accesses.
5
6use crate::bitmap::BitmapSlice;
7use crate::volatile_memory::copy_slice_impl::{copy_from_volatile_slice, copy_to_volatile_slice};
8use crate::{VolatileMemoryError, VolatileSlice};
9use std::io::{Cursor, ErrorKind};
10
11#[cfg(feature = "rawfd")]
12use std::io::Stdout;
13
14#[cfg(feature = "rawfd")]
15use std::os::fd::{AsFd, AsRawFd, BorrowedFd};
16
17macro_rules! retry_eintr {
18    ($io_call: expr) => {
19        loop {
20            let r = $io_call;
21
22            if let Err(crate::VolatileMemoryError::IOError(ref err)) = r {
23                if err.kind() == std::io::ErrorKind::Interrupted {
24                    continue;
25                }
26            }
27
28            break r;
29        }
30    };
31}
32
33pub(crate) use retry_eintr;
34
35/// A version of the standard library's [`Read`](std::io::Read) trait that operates on volatile
36/// memory instead of slices
37///
38/// This trait is needed as rust slices (`&[u8]` and `&mut [u8]`) cannot be used when operating on
39/// guest memory [1].
40///
41/// [1]: https://github.com/rust-vmm/vm-memory/pull/217
42pub trait ReadVolatile {
43    /// Tries to read some bytes into the given [`VolatileSlice`] buffer, returning how many bytes
44    /// were read.
45    ///
46    /// The behavior of implementations should be identical to [`Read::read`](std::io::Read::read)
47    fn read_volatile<B: BitmapSlice>(
48        &mut self,
49        buf: &mut VolatileSlice<B>,
50    ) -> Result<usize, VolatileMemoryError>;
51
52    /// Tries to fill the given [`VolatileSlice`] buffer by reading from `self` returning an error
53    /// if insufficient bytes could be read.
54    ///
55    /// The default implementation is identical to that of [`Read::read_exact`](std::io::Read::read_exact)
56    fn read_exact_volatile<B: BitmapSlice>(
57        &mut self,
58        buf: &mut VolatileSlice<B>,
59    ) -> Result<(), VolatileMemoryError> {
60        // Implementation based on https://github.com/rust-lang/rust/blob/7e7483d26e3cec7a44ef00cf7ae6c9c8c918bec6/library/std/src/io/mod.rs#L465
61
62        let mut partial_buf = buf.offset(0)?;
63
64        while !partial_buf.is_empty() {
65            match retry_eintr!(self.read_volatile(&mut partial_buf)) {
66                Ok(0) => {
67                    return Err(VolatileMemoryError::IOError(std::io::Error::new(
68                        ErrorKind::UnexpectedEof,
69                        "failed to fill whole buffer",
70                    )))
71                }
72                Ok(bytes_read) => partial_buf = partial_buf.offset(bytes_read)?,
73                Err(err) => return Err(err),
74            }
75        }
76
77        Ok(())
78    }
79}
80
81/// A version of the standard library's [`Write`](std::io::Write) trait that operates on volatile
82/// memory instead of slices.
83///
84/// This trait is needed as rust slices (`&[u8]` and `&mut [u8]`) cannot be used when operating on
85/// guest memory [1].
86///
87/// [1]: https://github.com/rust-vmm/vm-memory/pull/217
88pub trait WriteVolatile {
89    /// Tries to write some bytes from the given [`VolatileSlice`] buffer, returning how many bytes
90    /// were written.
91    ///
92    /// The behavior of implementations should be identical to [`Write::write`](std::io::Write::write)
93    fn write_volatile<B: BitmapSlice>(
94        &mut self,
95        buf: &VolatileSlice<B>,
96    ) -> Result<usize, VolatileMemoryError>;
97
98    /// Tries write the entire content of the given [`VolatileSlice`] buffer to `self` returning an
99    /// error if not all bytes could be written.
100    ///
101    /// The default implementation is identical to that of [`Write::write_all`](std::io::Write::write_all)
102    fn write_all_volatile<B: BitmapSlice>(
103        &mut self,
104        buf: &VolatileSlice<B>,
105    ) -> Result<(), VolatileMemoryError> {
106        // Based on https://github.com/rust-lang/rust/blob/7e7483d26e3cec7a44ef00cf7ae6c9c8c918bec6/library/std/src/io/mod.rs#L1570
107
108        let mut partial_buf = buf.offset(0)?;
109
110        while !partial_buf.is_empty() {
111            match retry_eintr!(self.write_volatile(&partial_buf)) {
112                Ok(0) => {
113                    return Err(VolatileMemoryError::IOError(std::io::Error::new(
114                        ErrorKind::WriteZero,
115                        "failed to write whole buffer",
116                    )))
117                }
118                Ok(bytes_written) => partial_buf = partial_buf.offset(bytes_written)?,
119                Err(err) => return Err(err),
120            }
121        }
122
123        Ok(())
124    }
125}
126
127// We explicitly implement our traits for [`std::fs::File`] and [`std::os::unix::net::UnixStream`]
128// instead of providing blanket implementation for [`AsRawFd`] due to trait coherence limitations: A
129// blanket implementation would prevent us from providing implementations for `&mut [u8]` below, as
130// "an upstream crate could implement AsRawFd for &mut [u8]".
131
132macro_rules! impl_read_write_volatile_for_raw_fd {
133    ($raw_fd_ty:ty) => {
134        #[cfg(feature = "rawfd")]
135        impl ReadVolatile for $raw_fd_ty {
136            fn read_volatile<B: BitmapSlice>(
137                &mut self,
138                buf: &mut VolatileSlice<B>,
139            ) -> Result<usize, VolatileMemoryError> {
140                read_volatile_raw_fd(self.as_fd(), buf)
141            }
142        }
143
144        #[cfg(feature = "rawfd")]
145        impl ReadVolatile for &$raw_fd_ty {
146            fn read_volatile<B: BitmapSlice>(
147                &mut self,
148                buf: &mut VolatileSlice<B>,
149            ) -> Result<usize, VolatileMemoryError> {
150                read_volatile_raw_fd(self.as_fd(), buf)
151            }
152        }
153
154        #[cfg(feature = "rawfd")]
155        impl ReadVolatile for &mut $raw_fd_ty {
156            fn read_volatile<B: BitmapSlice>(
157                &mut self,
158                buf: &mut VolatileSlice<B>,
159            ) -> Result<usize, VolatileMemoryError> {
160                read_volatile_raw_fd(self.as_fd(), buf)
161            }
162        }
163
164        #[cfg(feature = "rawfd")]
165        impl WriteVolatile for $raw_fd_ty {
166            fn write_volatile<B: BitmapSlice>(
167                &mut self,
168                buf: &VolatileSlice<B>,
169            ) -> Result<usize, VolatileMemoryError> {
170                write_volatile_raw_fd(self.as_fd(), buf)
171            }
172        }
173
174        #[cfg(feature = "rawfd")]
175        impl WriteVolatile for &$raw_fd_ty {
176            fn write_volatile<B: BitmapSlice>(
177                &mut self,
178                buf: &VolatileSlice<B>,
179            ) -> Result<usize, VolatileMemoryError> {
180                write_volatile_raw_fd(self.as_fd(), buf)
181            }
182        }
183
184        #[cfg(feature = "rawfd")]
185        impl WriteVolatile for &mut $raw_fd_ty {
186            fn write_volatile<B: BitmapSlice>(
187                &mut self,
188                buf: &VolatileSlice<B>,
189            ) -> Result<usize, VolatileMemoryError> {
190                write_volatile_raw_fd(self.as_fd(), buf)
191            }
192        }
193    };
194}
195
196#[cfg(feature = "rawfd")]
197impl WriteVolatile for Stdout {
198    fn write_volatile<B: BitmapSlice>(
199        &mut self,
200        buf: &VolatileSlice<B>,
201    ) -> Result<usize, VolatileMemoryError> {
202        write_volatile_raw_fd(self.as_fd(), buf)
203    }
204}
205
206#[cfg(feature = "rawfd")]
207impl WriteVolatile for &Stdout {
208    fn write_volatile<B: BitmapSlice>(
209        &mut self,
210        buf: &VolatileSlice<B>,
211    ) -> Result<usize, VolatileMemoryError> {
212        write_volatile_raw_fd(self.as_fd(), buf)
213    }
214}
215
216impl_read_write_volatile_for_raw_fd!(std::fs::File);
217impl_read_write_volatile_for_raw_fd!(std::net::TcpStream);
218impl_read_write_volatile_for_raw_fd!(std::os::unix::net::UnixStream);
219impl_read_write_volatile_for_raw_fd!(std::os::fd::OwnedFd);
220impl_read_write_volatile_for_raw_fd!(std::os::fd::BorrowedFd<'_>);
221
222/// Tries to do a single `read` syscall on the provided file descriptor, storing the data raed in
223/// the given [`VolatileSlice`].
224///
225/// Returns the numbers of bytes read.
226#[cfg(feature = "rawfd")]
227fn read_volatile_raw_fd(
228    raw_fd: BorrowedFd<'_>,
229    buf: &mut VolatileSlice<impl BitmapSlice>,
230) -> Result<usize, VolatileMemoryError> {
231    let fd = raw_fd.as_raw_fd();
232    let guard = buf.ptr_guard_mut();
233
234    let dst = guard.as_ptr().cast::<libc::c_void>();
235
236    // SAFETY: Rust's I/O safety invariants ensure that BorrowedFd contains a valid file descriptor`.
237    // The memory pointed to by `dst` is valid for writes of length `buf.len() by the invariants
238    // upheld by the constructor of `VolatileSlice`.
239    let bytes_read = unsafe { libc::read(fd, dst, buf.len()) };
240
241    if bytes_read < 0 {
242        // We don't know if a partial read might have happened, so mark everything as dirty
243        buf.bitmap().mark_dirty(0, buf.len());
244
245        Err(VolatileMemoryError::IOError(std::io::Error::last_os_error()))
246    } else {
247        let bytes_read = bytes_read.try_into().unwrap();
248        buf.bitmap().mark_dirty(0, bytes_read);
249        Ok(bytes_read)
250    }
251}
252
253/// Tries to do a single `write` syscall on the provided file descriptor, attempting to write the
254/// data stored in the given [`VolatileSlice`].
255///
256/// Returns the numbers of bytes written.
257#[cfg(feature = "rawfd")]
258fn write_volatile_raw_fd(
259    raw_fd: BorrowedFd<'_>,
260    buf: &VolatileSlice<impl BitmapSlice>,
261) -> Result<usize, VolatileMemoryError> {
262    let fd = raw_fd.as_raw_fd();
263    let guard = buf.ptr_guard();
264
265    let src = guard.as_ptr().cast::<libc::c_void>();
266
267    // SAFETY: Rust's I/O safety invariants ensure that BorrowedFd contains a valid file descriptor`.
268    // The memory pointed to by `src` is valid for reads of length `buf.len() by the invariants
269    // upheld by the constructor of `VolatileSlice`.
270    let bytes_written = unsafe { libc::write(fd, src, buf.len()) };
271
272    if bytes_written < 0 {
273        Err(VolatileMemoryError::IOError(std::io::Error::last_os_error()))
274    } else {
275        Ok(bytes_written.try_into().unwrap())
276    }
277}
278
279impl WriteVolatile for &mut [u8] {
280    fn write_volatile<B: BitmapSlice>(
281        &mut self,
282        buf: &VolatileSlice<B>,
283    ) -> Result<usize, VolatileMemoryError> {
284        let total = buf.len().min(self.len());
285
286        // SAFETY:
287        // `buf` is contiguously allocated memory of length `total <= buf.len())` by the invariants
288        // of `VolatileSlice`.
289        // Furthermore, both source and destination of the call to copy_from_volatile_slice are valid
290        // for reads and writes respectively of length `total` since total is the minimum of lengths
291        // of the memory areas pointed to. The areas do not overlap, since the source is inside guest
292        // memory, and the destination is a pointer derived from a slice (no slices to guest memory
293        // are possible without violating rust's aliasing rules).
294        let written = unsafe { copy_from_volatile_slice(self.as_mut_ptr(), buf, total) };
295
296        // Advance the slice, just like the stdlib: https://doc.rust-lang.org/src/std/io/impls.rs.html#335
297        *self = std::mem::take(self).split_at_mut(written).1;
298
299        Ok(written)
300    }
301
302    fn write_all_volatile<B: BitmapSlice>(
303        &mut self,
304        buf: &VolatileSlice<B>,
305    ) -> Result<(), VolatileMemoryError> {
306        // Based on https://github.com/rust-lang/rust/blob/f7b831ac8a897273f78b9f47165cf8e54066ce4b/library/std/src/io/impls.rs#L376-L382
307        if self.write_volatile(buf)? == buf.len() {
308            Ok(())
309        } else {
310            Err(VolatileMemoryError::IOError(std::io::Error::new(
311                ErrorKind::WriteZero,
312                "failed to write whole buffer",
313            )))
314        }
315    }
316}
317
318impl ReadVolatile for &[u8] {
319    fn read_volatile<B: BitmapSlice>(
320        &mut self,
321        buf: &mut VolatileSlice<B>,
322    ) -> Result<usize, VolatileMemoryError> {
323        let total = buf.len().min(self.len());
324
325        // SAFETY:
326        // `buf` is contiguously allocated memory of length `total <= buf.len())` by the invariants
327        // of `VolatileSlice`.
328        // Furthermore, both source and destination of the call to copy_to_volatile_slice are valid
329        // for reads and writes respectively of length `total` since total is the minimum of lengths
330        // of the memory areas pointed to. The areas do not overlap, since the destination is inside
331        // guest memory, and the source is a pointer derived from a slice (no slices to guest memory
332        // are possible without violating rust's aliasing rules).
333        let read = unsafe { copy_to_volatile_slice(buf, self.as_ptr(), total) };
334
335        // Advance the slice, just like the stdlib: https://doc.rust-lang.org/src/std/io/impls.rs.html#232-310
336        *self = self.split_at(read).1;
337
338        Ok(read)
339    }
340
341    fn read_exact_volatile<B: BitmapSlice>(
342        &mut self,
343        buf: &mut VolatileSlice<B>,
344    ) -> Result<(), VolatileMemoryError> {
345        // Based on https://github.com/rust-lang/rust/blob/f7b831ac8a897273f78b9f47165cf8e54066ce4b/library/std/src/io/impls.rs#L282-L302
346        if buf.len() > self.len() {
347            return Err(VolatileMemoryError::IOError(std::io::Error::new(
348                ErrorKind::UnexpectedEof,
349                "failed to fill whole buffer",
350            )));
351        }
352
353        self.read_volatile(buf).map(|_| ())
354    }
355}
356
357// WriteVolatile implementation for Vec<u8> is based upon the Write impl for Vec, which
358// defers to Vec::append_elements, after which the below functionality is modelled.
359impl WriteVolatile for Vec<u8> {
360    fn write_volatile<B: BitmapSlice>(
361        &mut self,
362        buf: &VolatileSlice<B>,
363    ) -> Result<usize, VolatileMemoryError> {
364        let count = buf.len();
365        self.reserve(count);
366        let len = self.len();
367
368        // SAFETY: Calling Vec::reserve() above guarantees the the backing storage of the Vec has
369        // length at least `len + count`. This means that self.as_mut_ptr().add(len) remains within
370        // the same allocated object, the offset does not exceed isize (as otherwise reserve would
371        // have panicked), and does not rely on address space wrapping around.
372        // In particular, the entire `count` bytes after `self.as_mut_ptr().add(count)` is
373        // contiguously allocated and valid for writes.
374        // Lastly, `copy_to_volatile_slice` correctly initialized `copied_len` additional bytes
375        // in the Vec's backing storage, and we assert this to be equal to `count`. Additionally,
376        // `len + count` is at most the reserved capacity of the vector. Thus the call to `set_len`
377        // is safe.
378        unsafe {
379            let copied_len = copy_from_volatile_slice(self.as_mut_ptr().add(len), buf, count);
380
381            assert_eq!(copied_len, count);
382            self.set_len(len + count);
383        }
384        Ok(count)
385    }
386}
387
388// ReadVolatile and WriteVolatile implementations for Cursor<T> is modelled after the standard
389// library's implementation (modulo having to inline `Cursor::remaining_slice`, as that's nightly only)
390impl<T> ReadVolatile for Cursor<T>
391where
392    T: AsRef<[u8]>,
393{
394    fn read_volatile<B: BitmapSlice>(
395        &mut self,
396        buf: &mut VolatileSlice<B>,
397    ) -> Result<usize, VolatileMemoryError> {
398        let inner = self.get_ref().as_ref();
399        let len = self.position().min(inner.len() as u64);
400        let n = ReadVolatile::read_volatile(&mut &inner[(len as usize)..], buf)?;
401        self.set_position(self.position() + n as u64);
402        Ok(n)
403    }
404
405    fn read_exact_volatile<B: BitmapSlice>(
406        &mut self,
407        buf: &mut VolatileSlice<B>,
408    ) -> Result<(), VolatileMemoryError> {
409        let inner = self.get_ref().as_ref();
410        let n = buf.len();
411        let len = self.position().min(inner.len() as u64);
412        ReadVolatile::read_exact_volatile(&mut &inner[(len as usize)..], buf)?;
413        self.set_position(self.position() + n as u64);
414        Ok(())
415    }
416}
417
418impl WriteVolatile for Cursor<&mut [u8]> {
419    fn write_volatile<B: BitmapSlice>(
420        &mut self,
421        buf: &VolatileSlice<B>,
422    ) -> Result<usize, VolatileMemoryError> {
423        let pos = self.position().min(self.get_ref().len() as u64);
424        let n = WriteVolatile::write_volatile(&mut &mut self.get_mut()[(pos as usize)..], buf)?;
425        self.set_position(self.position() + n as u64);
426        Ok(n)
427    }
428
429    // no write_all provided in standard library, since our default for write_all is based on the
430    // standard library's write_all, omitting it here as well will correctly mimic stdlib behavior.
431}
432
433#[cfg(test)]
434mod tests {
435    use crate::io::{ReadVolatile, WriteVolatile};
436    use crate::{VolatileMemoryError, VolatileSlice};
437    use std::io::{Cursor, ErrorKind};
438    #[cfg(feature = "rawfd")]
439    use std::io::{Read, Seek, Write};
440    #[cfg(feature = "rawfd")]
441    use vmm_sys_util::tempfile::TempFile;
442
443    // ---- Test ReadVolatile for &[u8] ----
444    fn read_4_bytes_to_5_byte_memory(source: Vec<u8>, expected_output: [u8; 5]) {
445        // Test read_volatile for &[u8] works
446        let mut memory = vec![0u8; 5];
447
448        assert_eq!(
449            (&source[..])
450                .read_volatile(&mut VolatileSlice::from(&mut memory[..4]))
451                .unwrap(),
452            source.len().min(4)
453        );
454        assert_eq!(&memory, &expected_output);
455
456        // Test read_exact_volatile for &[u8] works
457        let mut memory = vec![0u8; 5];
458        let result = (&source[..]).read_exact_volatile(&mut VolatileSlice::from(&mut memory[..4]));
459
460        // read_exact fails if there are not enough bytes in input to completely fill
461        // memory[..4]
462        if source.len() < 4 {
463            match result.unwrap_err() {
464                VolatileMemoryError::IOError(ioe) => {
465                    assert_eq!(ioe.kind(), ErrorKind::UnexpectedEof)
466                }
467                err => panic!("{:?}", err),
468            }
469            assert_eq!(memory, vec![0u8; 5]);
470        } else {
471            result.unwrap();
472            assert_eq!(&memory, &expected_output);
473        }
474    }
475
476    // ---- Test ReadVolatile for File ----
477    #[cfg(all(feature = "rawfd", not(miri)))]
478    fn read_4_bytes_from_file(source: Vec<u8>, expected_output: [u8; 5]) {
479        let mut temp_file = TempFile::new().unwrap().into_file();
480        temp_file.write_all(source.as_ref()).unwrap();
481        temp_file.rewind().unwrap();
482
483        // Test read_volatile for File works
484        let mut memory = vec![0u8; 5];
485
486        assert_eq!(
487            temp_file
488                .read_volatile(&mut VolatileSlice::from(&mut memory[..4]))
489                .unwrap(),
490            source.len().min(4)
491        );
492        assert_eq!(&memory, &expected_output);
493
494        temp_file.rewind().unwrap();
495
496        // Test read_exact_volatile for File works
497        let mut memory = vec![0u8; 5];
498
499        let read_exact_result =
500            temp_file.read_exact_volatile(&mut VolatileSlice::from(&mut memory[..4]));
501
502        if source.len() < 4 {
503            read_exact_result.unwrap_err();
504        } else {
505            read_exact_result.unwrap();
506        }
507        assert_eq!(&memory, &expected_output);
508    }
509
510    #[test]
511    fn test_read_volatile() {
512        let test_cases = [
513            (vec![1u8, 2], [1u8, 2, 0, 0, 0]),
514            (vec![1, 2, 3, 4], [1, 2, 3, 4, 0]),
515            // ensure we don't have a buffer overrun
516            (vec![5, 6, 7, 8, 9], [5, 6, 7, 8, 0]),
517        ];
518
519        for (input, output) in test_cases {
520            read_4_bytes_to_5_byte_memory(input.clone(), output);
521            #[cfg(all(feature = "rawfd", not(miri)))]
522            read_4_bytes_from_file(input, output);
523        }
524    }
525
526    // ---- Test WriteVolatile for &mut [u8] ----
527    fn write_4_bytes_to_5_byte_vec(mut source: Vec<u8>, expected_result: [u8; 5]) {
528        let mut memory = vec![0u8; 5];
529
530        // Test write_volatile for &mut [u8] works
531        assert_eq!(
532            (&mut memory[..4])
533                .write_volatile(&VolatileSlice::from(source.as_mut_slice()))
534                .unwrap(),
535            source.len().min(4)
536        );
537        assert_eq!(&memory, &expected_result);
538
539        // Test write_all_volatile for &mut [u8] works
540        let mut memory = vec![0u8; 5];
541
542        let result =
543            (&mut memory[..4]).write_all_volatile(&VolatileSlice::from(source.as_mut_slice()));
544
545        if source.len() > 4 {
546            match result.unwrap_err() {
547                VolatileMemoryError::IOError(ioe) => {
548                    assert_eq!(ioe.kind(), ErrorKind::WriteZero)
549                }
550                err => panic!("{:?}", err),
551            }
552            // This quirky behavior of writing to the slice even in the case of failure is also
553            // exhibited by the stdlib
554            assert_eq!(&memory, &expected_result);
555        } else {
556            result.unwrap();
557            assert_eq!(&memory, &expected_result);
558        }
559    }
560
561    // ---- Test ẂriteVolatile for File works ----
562    #[cfg(all(feature = "rawfd", not(miri)))]
563    fn write_5_bytes_to_file(mut source: Vec<u8>) {
564        // Test write_volatile for File works
565        let mut temp_file = TempFile::new().unwrap().into_file();
566
567        temp_file
568            .write_volatile(&VolatileSlice::from(source.as_mut_slice()))
569            .unwrap();
570        temp_file.rewind().unwrap();
571
572        let mut written = vec![0u8; source.len()];
573        temp_file.read_exact(written.as_mut_slice()).unwrap();
574
575        assert_eq!(source, written);
576        // check no excess bytes were written to the file
577        assert_eq!(temp_file.read(&mut [0u8]).unwrap(), 0);
578
579        // Test write_all_volatile for File works
580        let mut temp_file = TempFile::new().unwrap().into_file();
581
582        temp_file
583            .write_all_volatile(&VolatileSlice::from(source.as_mut_slice()))
584            .unwrap();
585        temp_file.rewind().unwrap();
586
587        let mut written = vec![0u8; source.len()];
588        temp_file.read_exact(written.as_mut_slice()).unwrap();
589
590        assert_eq!(source, written);
591        // check no excess bytes were written to the file
592        assert_eq!(temp_file.read(&mut [0u8]).unwrap(), 0);
593    }
594
595    #[test]
596    fn test_write_volatile() {
597        let test_cases = [
598            (vec![1u8, 2], [1u8, 2, 0, 0, 0]),
599            (vec![1, 2, 3, 4], [1, 2, 3, 4, 0]),
600            // ensure we don't have a buffer overrun
601            (vec![5, 6, 7, 8, 9], [5, 6, 7, 8, 0]),
602        ];
603
604        for (input, output) in test_cases {
605            write_4_bytes_to_5_byte_vec(input.clone(), output);
606            #[cfg(all(feature = "rawfd", not(miri)))]
607            write_5_bytes_to_file(input);
608        }
609    }
610
611    #[test]
612    fn test_read_volatile_for_cursor() {
613        let read_buffer = [1, 2, 3, 4, 5, 6, 7];
614        let mut output = vec![0u8; 5];
615
616        let mut cursor = Cursor::new(read_buffer);
617
618        // Read 4 bytes from cursor to volatile slice (amount read limited by volatile slice length)
619        assert_eq!(
620            cursor
621                .read_volatile(&mut VolatileSlice::from(&mut output[..4]))
622                .unwrap(),
623            4
624        );
625        assert_eq!(output, vec![1, 2, 3, 4, 0]);
626
627        // Read next 3 bytes from cursor to volatile slice (amount read limited by length of remaining data in cursor)
628        assert_eq!(
629            cursor
630                .read_volatile(&mut VolatileSlice::from(&mut output[..4]))
631                .unwrap(),
632            3
633        );
634        assert_eq!(output, vec![5, 6, 7, 4, 0]);
635
636        cursor.set_position(0);
637        // Same as first test above, but with read_exact
638        cursor
639            .read_exact_volatile(&mut VolatileSlice::from(&mut output[..4]))
640            .unwrap();
641        assert_eq!(output, vec![1, 2, 3, 4, 0]);
642
643        // Same as above, but with read_exact. Should fail now, because we cannot fill a 4 byte buffer
644        // with whats remaining in the cursor (3 bytes). Output should remain unchanged.
645        assert!(cursor
646            .read_exact_volatile(&mut VolatileSlice::from(&mut output[..4]))
647            .is_err());
648        assert_eq!(output, vec![1, 2, 3, 4, 0]);
649    }
650
651    #[test]
652    fn test_write_volatile_for_cursor() {
653        let mut write_buffer = vec![0u8; 7];
654        let mut input = [1, 2, 3, 4];
655
656        let mut cursor = Cursor::new(write_buffer.as_mut_slice());
657
658        // Write 4 bytes from volatile slice to cursor (amount written limited by volatile slice length)
659        assert_eq!(
660            cursor
661                .write_volatile(&VolatileSlice::from(input.as_mut_slice()))
662                .unwrap(),
663            4
664        );
665        assert_eq!(cursor.get_ref(), &[1, 2, 3, 4, 0, 0, 0]);
666
667        // Write 3 bytes from volatile slice to cursor (amount written limited by remaining space in cursor)
668        assert_eq!(
669            cursor
670                .write_volatile(&VolatileSlice::from(input.as_mut_slice()))
671                .unwrap(),
672            3
673        );
674        assert_eq!(cursor.get_ref(), &[1, 2, 3, 4, 1, 2, 3]);
675    }
676
677    #[test]
678    fn test_write_volatile_for_vec() {
679        let mut write_buffer = Vec::new();
680        let mut input = [1, 2, 3, 4];
681
682        assert_eq!(
683            write_buffer
684                .write_volatile(&VolatileSlice::from(input.as_mut_slice()))
685                .unwrap(),
686            4
687        );
688
689        assert_eq!(&write_buffer, &input);
690    }
691}