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