Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 53 additions & 60 deletions library/core/src/mem/manually_drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,6 @@ use crate::ptr;
/// }
/// ```
///
/// # Interaction with `Box`
///
/// Currently, if you have a `ManuallyDrop<T>`, where the type `T` is a `Box` or
/// contains a `Box` inside, then dropping the `T` followed by moving the
/// `ManuallyDrop<T>` is [considered to be undefined
/// behavior](https://github.com/rust-lang/unsafe-code-guidelines/issues/245).
/// That is, the following code causes undefined behavior:
///
/// ```no_run
/// use std::mem::ManuallyDrop;
///
/// let mut x = ManuallyDrop::new(Box::new(42));
/// unsafe {
/// ManuallyDrop::drop(&mut x);
/// }
/// let y = x; // Undefined behavior!
/// ```
///
/// This is [likely to change in the
/// future](https://rust-lang.github.io/rfcs/3336-maybe-dangling.html). In the
/// meantime, consider using [`MaybeUninit`] instead.
Expand All @@ -75,14 +57,59 @@ use crate::ptr;
/// * There is code that drops the contents of the `ManuallyDrop` field, and
/// this code is outside the struct or enum's `Drop` implementation.
///
/// In particular, the following hazards may occur:
/// In particular, deriving `Debug`, `Clone`, `PartialEq`, `PartialOrd`, `Ord`,
/// or `Hash` on the struct or enum could be unsound, since the derived
/// implementations of these traits would access the `ManuallyDrop` field. For
/// example, the following code causes undefined behavior:
///
/// #### Storing generic types
/// ```no_run
/// use std::mem::ManuallyDrop;
///
/// If the `ManuallyDrop` contains a client-supplied generic type, the client
/// might provide a `Box` as that type. This would cause undefined behavior when
/// the struct or enum is later moved, as mentioned in the previous section. For
/// example, the following code causes undefined behavior:
/// // This derive is unsound in combination with the `ManuallyDrop::drop` call.
/// #[derive(Debug)]
/// pub struct Foo {
/// value: ManuallyDrop<String>,
/// }
/// impl Foo {
/// pub fn new() -> Self {
/// let mut temp = Self {
/// value: ManuallyDrop::new(String::from("Unsafe rust is hard."))
/// };
/// unsafe {
/// // SAFETY: `value` hasn't been dropped yet.
/// ManuallyDrop::drop(&mut temp.value);
/// }
/// temp
/// }
/// }
///
/// // In another crate:
///
/// let foo = Foo::new();
/// println!("{:?}", foo); // Undefined behavior!
/// ```
///
/// # Pre-`1.96` Interaction with `Box`
///
/// Before Rust `1.96.0`, if you had a `ManuallyDrop<T>`, where the type `T`
/// was a `Box` or contained a `Box` inside, then dropping the `T` followed by
/// moving the `ManuallyDrop<T>` was [considered to be undefined
/// behavior](https://github.com/rust-lang/unsafe-code-guidelines/issues/245).
/// That is, the following code caused undefined behavior:
///
/// ```no_run
/// use std::mem::ManuallyDrop;
///
/// let mut x = ManuallyDrop::new(Box::new(42));
/// unsafe {
/// ManuallyDrop::drop(&mut x);
/// }
/// let y = x; // Undefined behavior! (pre 1.96.0)
/// ```
///
/// Note that this could also have happen with a generic type where the user of
/// the library providing it could substitute the generic for a `Box<_>` and
/// then move the library type:
///
/// ```no_run
/// use std::mem::ManuallyDrop;
Expand All @@ -101,7 +128,7 @@ use crate::ptr;
/// self.is_some = false;
/// unsafe {
/// // SAFETY: `value` hasn't been dropped yet, as per the invariant
/// // (This is actually unsound!)
/// // (This is actually unsound pre rust 1.96.0!)
/// ManuallyDrop::drop(&mut self.value);
/// }
/// }
Expand All @@ -112,41 +139,7 @@ use crate::ptr;
///
/// let mut option = BadOption::new(Box::new(42));
/// option.change_to_none();
/// let option2 = option; // Undefined behavior!
/// ```
///
/// #### Deriving traits
///
/// Deriving `Debug`, `Clone`, `PartialEq`, `PartialOrd`, `Ord`, or `Hash` on
/// the struct or enum could be unsound, since the derived implementations of
/// these traits would access the `ManuallyDrop` field. For example, the
/// following code causes undefined behavior:
///
/// ```no_run
/// use std::mem::ManuallyDrop;
///
/// // This derive is unsound in combination with the `ManuallyDrop::drop` call.
/// #[derive(Debug)]
/// pub struct Foo {
/// value: ManuallyDrop<String>,
/// }
/// impl Foo {
/// pub fn new() -> Self {
/// let mut temp = Self {
/// value: ManuallyDrop::new(String::from("Unsafe rust is hard."))
/// };
/// unsafe {
/// // SAFETY: `value` hasn't been dropped yet.
/// ManuallyDrop::drop(&mut temp.value);
/// }
/// temp
/// }
/// }
///
/// // In another crate:
///
/// let foo = Foo::new();
/// println!("{:?}", foo); // Undefined behavior!
/// let option2 = option; // Undefined behavior! (pre 1.96)
/// ```
///
/// [drop order]: https://doc.rust-lang.org/reference/destructors.html
Expand Down
8 changes: 5 additions & 3 deletions library/std/src/sys/process/unix/common/cstring_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ impl CStringArray {
/// Push an additional string to the array.
pub fn push(&mut self, item: CString) {
let argc = self.ptrs.len() - 1;
// Replace the null pointer at the end of the array...
self.ptrs[argc] = item.into_raw();
// ... and recreate it to restore the data structure invariant.
// Amend the array by another null pointer first, to ensure that the
// array is null-terminated even when the `push` panics, in which case
// the array will be left undisturbed (see #155748).
self.ptrs.push(ptr::null());
// Now, replace the previous null pointer.
self.ptrs[argc] = item.into_raw();
}

/// Returns a pointer to the C-string array managed by this type.
Expand Down
Loading