From c555aabc5b7df5ccb88c15b7b94f73f5b40d116c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 12 Oct 2020 10:08:25 +0200 Subject: [PATCH 1/3] clarify rules for ZST Boxes --- library/alloc/src/boxed.rs | 9 +++++++++ library/core/src/ptr/mod.rs | 7 +++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 5c8c2c5a5a8..c543ee2d0c5 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -62,6 +62,11 @@ //! T` obtained from [`Box::::into_raw`] may be deallocated using the //! [`Global`] allocator with [`Layout::for_value(&*value)`]. //! +//! For zero-sized values, the `Box` pointer still has to be [valid] for reads and writes and +//! sufficiently aligned. In particular, casting any aligned non-zero integer to a raw pointer +//! produces a valid pointer, but a pointer pointing into previously allocated memory that since got +//! freed is not valid. +//! //! So long as `T: Sized`, a `Box` is guaranteed to be represented //! as a single pointer and is also ABI-compatible with C pointers //! (i.e. the C type `T*`). This means that if you have extern "C" @@ -125,6 +130,7 @@ //! [`Global`]: crate::alloc::Global //! [`Layout`]: crate::alloc::Layout //! [`Layout::for_value(&*value)`]: crate::alloc::Layout::for_value +//! [valid]: ptr#safety #![stable(feature = "rust1", since = "1.0.0")] @@ -385,7 +391,10 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// + /// The safety conditions are described in the [memory layout] section. + /// /// # Examples + /// /// Recreate a `Box` which was previously converted to a raw pointer /// using [`Box::into_raw`]: /// ``` diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 92c4f2ccfe8..453621d9ead 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -16,12 +16,15 @@ //! provided at this point are very minimal: //! //! * A [null] pointer is *never* valid, not even for accesses of [size zero][zst]. -//! * All pointers (except for the null pointer) are valid for all operations of -//! [size zero][zst]. //! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer //! be *dereferenceable*: the memory range of the given size starting at the pointer must all be //! within the bounds of a single allocated object. Note that in Rust, //! every (stack-allocated) variable is considered a separate allocated object. +//! * Even for operations of [size zero][zst], the pointer must not be "dangling" in the sense of +//! pointing to deallocated memory. However, casting any non-zero integer to a pointer is valid +//! for zero-sized accesses. This corresponds to writing your own allocator; allocating zero-sized +//! objects is not very hard. In contrast, when you use the standard allocator, after memory got +//! deallocated, even zero-sized accesses to that memory are invalid. //! * All accesses performed by functions in this module are *non-atomic* in the sense //! of [atomic operations] used to synchronize between threads. This means it is //! undefined behavior to perform two concurrent accesses to the same location from different From 0f572a9810e2c54b7067641583a5527acff6cae5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 13 Oct 2020 09:30:09 +0200 Subject: [PATCH 2/3] explicitly talk about integer literals --- library/alloc/src/boxed.rs | 6 +++--- library/core/src/ptr/mod.rs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index c543ee2d0c5..7fd91aba321 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -63,9 +63,9 @@ //! [`Global`] allocator with [`Layout::for_value(&*value)`]. //! //! For zero-sized values, the `Box` pointer still has to be [valid] for reads and writes and -//! sufficiently aligned. In particular, casting any aligned non-zero integer to a raw pointer -//! produces a valid pointer, but a pointer pointing into previously allocated memory that since got -//! freed is not valid. +//! sufficiently aligned. In particular, casting any aligned non-zero integer literal to a raw +//! pointer produces a valid pointer, but a pointer pointing into previously allocated memory that +//! since got freed is not valid. //! //! So long as `T: Sized`, a `Box` is guaranteed to be represented //! as a single pointer and is also ABI-compatible with C pointers diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 453621d9ead..243346a429a 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -21,10 +21,10 @@ //! within the bounds of a single allocated object. Note that in Rust, //! every (stack-allocated) variable is considered a separate allocated object. //! * Even for operations of [size zero][zst], the pointer must not be "dangling" in the sense of -//! pointing to deallocated memory. However, casting any non-zero integer to a pointer is valid -//! for zero-sized accesses. This corresponds to writing your own allocator; allocating zero-sized -//! objects is not very hard. In contrast, when you use the standard allocator, after memory got -//! deallocated, even zero-sized accesses to that memory are invalid. +//! pointing to deallocated memory. However, casting any non-zero integer literal to a pointer is +//! valid for zero-sized accesses. This corresponds to writing your own allocator; allocating +//! zero-sized objects is not very hard. In contrast, when you use the standard allocator, after +//! memory got deallocated, even zero-sized accesses to that memory are invalid. //! * All accesses performed by functions in this module are *non-atomic* in the sense //! of [atomic operations] used to synchronize between threads. This means it is //! undefined behavior to perform two concurrent accesses to the same location from different From a7677f77146bd69b26b5fb5eaa8f88ac080ff347 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 20 Nov 2020 10:25:59 +0100 Subject: [PATCH 3/3] reference NonNull::dangling --- library/alloc/src/boxed.rs | 10 ++++++---- library/core/src/ptr/mod.rs | 11 ++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 7fd91aba321..9676572e45d 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -62,10 +62,12 @@ //! T` obtained from [`Box::::into_raw`] may be deallocated using the //! [`Global`] allocator with [`Layout::for_value(&*value)`]. //! -//! For zero-sized values, the `Box` pointer still has to be [valid] for reads and writes and -//! sufficiently aligned. In particular, casting any aligned non-zero integer literal to a raw -//! pointer produces a valid pointer, but a pointer pointing into previously allocated memory that -//! since got freed is not valid. +//! For zero-sized values, the `Box` pointer still has to be [valid] for reads +//! and writes and sufficiently aligned. In particular, casting any aligned +//! non-zero integer literal to a raw pointer produces a valid pointer, but a +//! pointer pointing into previously allocated memory that since got freed is +//! not valid. The recommended way to build a Box to a ZST if `Box::new` cannot +//! be used is to use [`ptr::NonNull::dangling`]. //! //! So long as `T: Sized`, a `Box` is guaranteed to be represented //! as a single pointer and is also ABI-compatible with C pointers diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 243346a429a..6544dd4fd7a 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -20,11 +20,12 @@ //! be *dereferenceable*: the memory range of the given size starting at the pointer must all be //! within the bounds of a single allocated object. Note that in Rust, //! every (stack-allocated) variable is considered a separate allocated object. -//! * Even for operations of [size zero][zst], the pointer must not be "dangling" in the sense of -//! pointing to deallocated memory. However, casting any non-zero integer literal to a pointer is -//! valid for zero-sized accesses. This corresponds to writing your own allocator; allocating -//! zero-sized objects is not very hard. In contrast, when you use the standard allocator, after -//! memory got deallocated, even zero-sized accesses to that memory are invalid. +//! * Even for operations of [size zero][zst], the pointer must not be pointing to deallocated +//! memory, i.e., deallocation makes pointers invalid even for zero-sized operations. However, +//! casting any non-zero integer *literal* to a pointer is valid for zero-sized accesses, even if +//! some memory happens to exist at that address and gets deallocated. This corresponds to writing +//! your own allocator: allocating zero-sized objects is not very hard. The canonical way to +//! obtain a pointer that is valid for zero-sized accesses is [`NonNull::dangling`]. //! * All accesses performed by functions in this module are *non-atomic* in the sense //! of [atomic operations] used to synchronize between threads. This means it is //! undefined behavior to perform two concurrent accesses to the same location from different