Rollup merge of #127659 - saethlin:manually-drop-bufwriter, r=joboet

Use ManuallyDrop in BufWriter::into_parts

The fact that `mem::forget` takes by value means that it interacts very poorly with Stacked Borrows; generally users think of calling it as a no-op, but in Stacked Borrows, the field retagging tends to cause surprise tag invalidation.
This commit is contained in:
Jubilee 2024-07-13 20:19:47 -07:00 committed by GitHub
commit 2d8493bb5f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 17 additions and 7 deletions

View File

@ -3,7 +3,7 @@
use crate::io::{ use crate::io::{
self, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE, self, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE,
}; };
use crate::mem; use crate::mem::{self, ManuallyDrop};
use crate::ptr; use crate::ptr;
/// Wraps a writer and buffers its output. /// Wraps a writer and buffers its output.
@ -164,13 +164,13 @@ pub fn into_inner(mut self) -> Result<W, IntoInnerError<BufWriter<W>>> {
/// assert_eq!(&buffered_data.unwrap(), b"ata"); /// assert_eq!(&buffered_data.unwrap(), b"ata");
/// ``` /// ```
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] #[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
pub fn into_parts(mut self) -> (W, Result<Vec<u8>, WriterPanicked>) { pub fn into_parts(self) -> (W, Result<Vec<u8>, WriterPanicked>) {
let buf = mem::take(&mut self.buf); let mut this = ManuallyDrop::new(self);
let buf = if !self.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) }; let buf = mem::take(&mut this.buf);
let buf = if !this.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) };
// SAFETY: forget(self) prevents double dropping inner // SAFETY: double-drops are prevented by putting `this` in a ManuallyDrop that is never dropped
let inner = unsafe { ptr::read(&self.inner) }; let inner = unsafe { ptr::read(&this.inner) };
mem::forget(self);
(inner, buf) (inner, buf)
} }

View File

@ -1067,3 +1067,13 @@ fn read(&mut self, buf: &mut [u8]) -> crate::io::Result<usize> {
// But we initialized the whole buffer! // But we initialized the whole buffer!
assert_eq!(reader.initialized(), reader.capacity()); assert_eq!(reader.initialized(), reader.capacity());
} }
/// This is a regression test for https://github.com/rust-lang/rust/issues/127584.
#[test]
fn bufwriter_aliasing() {
use crate::io::{BufWriter, Cursor};
let mut v = vec![0; 1024];
let c = Cursor::new(&mut v);
let w = BufWriter::new(Box::new(c));
let _ = w.into_parts();
}