Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The lack of safety requirements for some unsafe api documents in std #134242

Open
VaynNecol opened this issue Dec 13, 2024 · 10 comments
Open

The lack of safety requirements for some unsafe api documents in std #134242

VaynNecol opened this issue Dec 13, 2024 · 10 comments
Labels
A-docs Area: documentation for any part of the project, including the compiler, standard library, and tools T-libs Relevant to the library team, which will review and decide on the PR/issue.

Comments

@VaynNecol
Copy link

Location

core::alloc

core/alloc/trait.GlobalAlloc#method.alloc_zeroed

retval.Untyped = ["The allocated block of memory is guaranteed to be initialized but may be untyped."]

core/alloc/trait.GlobalAlloc#method.realloc

layout.Layout = ["`layout` has non-zero size."]
retval.Untyped = ["The allocated block of memory may or may not be initialized."]

core/alloc/trait.Allocator#method.grow

retval.Untyped = ["The allocated block of memory may or may not be initialized."]

core/alloc/trait.Allocator#method.grow_zeroed

ptr.Freed = ["If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been transferred to this allocator. "]
retval.Untyped = ["The allocated block of memory is guaranteed to be initialized but may be untyped."]

core::char

core/char/fn.from_u32_unchecked

i.Initialized = ["Not all valid `u32`s are valid `char`s, it may construct invalid `char` values."]

core::convert

core/convert/trait.FloatToInt

self.Bounded = [
    "The value must not be `NaN`.",
    "The value must not be infinite.",
    "The value must be representable in the return type `Int`, after truncating off its fractional part."
]

core::mem

core/mem/fn.transmute_copy

src.Initialized = [
    "Both the argument and the result must be valid at their given type.",
    "To transmute the inner type of the contents of a container, you must make sure to not violate any of the container's invariants."
]

src.Layout = [
    "Both types must have the same size.",
    "Note that source and destination are passed by-value, which means if `Src` or `Dst` contain padding, that padding is not guaranteed to be preserved by transmute.",
    "When transmuting values that point elsewhere (such as pointers, references, boxes…), the caller has to ensure proper alignment of the pointed-to values."
]

retval.Untyped = ["It is therefore your responsibility to guarantee that every value passed to transmute is valid at both types `Src` and `Dst`. Failing to uphold this condition may lead to unexpected and unstable compilation results."]

core::primitive

core/primitive.pointer#method.as_ref

self.Dereferencable = ["The memory range of the given size starting at the pointer must all be within the bounds of a single allocated object."]
self.Layout = ["The pointer must be properly aligned."]
retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get mutated.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/primitive.pointer#method.as_uninit_ref

self.Dereferencable = ["The memory range of the given size starting at the pointer must all be within the bounds of a single allocated object."]
self.Layout = ["The pointer must be properly aligned."]
retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get mutated.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/primitive.pointer#method.as_ref-1

self.Dereferencable = ["The memory range of the given size starting at the pointer must all be within the bounds of a single allocated object."]
self.Layout = ["The pointer must be properly aligned."]
retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get mutated.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/primitive.pointer#method.as_uninit_ref-1

self.Dereferencable = ["The memory range of the given size starting at the pointer must all be within the bounds of a single allocated object."]
self.Layout = ["The pointer must be properly aligned."]
retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get mutated.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/primitive.pointer#method.as_mut

self.Dereferencable = ["The memory range of the given size starting at the pointer must all be within the bounds of a single allocated object."]
self.Layout = ["The pointer must be properly aligned."]
retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get accessed (read or written) through any other pointer.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/primitive.slice#method.align_to 

self.Initialized = [
    "Both the `T` and the `U` must be valid at their given type.",
    "To transmute the inner type of the contents of a container, you must make sure to not violate any of the container's invariants."
]
self.Layout = [
    "Both types must have the same size.",
    "Note that source and destination are passed by-value, which means if `T` or `U` contain padding, that padding is not guaranteed to be preserved by transmute.",
    "This method has no purpose when either input element `T` or output element `U` are zero-sized and will return the original slice without splitting anything.",
    "When transmuting values that point elsewhere (such as pointers, references, boxes…), the caller has to ensure proper alignment of the pointed-to values."
]
retval.Initialized = ["Both the argument and the result must be valid at their given type."]
retval.Aliased = [
    "It can turn a `*mut T` into an `&mut T`.",
    "It can extend a lifetime, or shorten an invariant lifetime."
]
retval.Untyped = ["It is therefore your responsibility to guarantee that every value passed to transmute is valid at both types `T` and `U`. Failing to uphold this condition may lead to unexpected and unstable compilation results."]

core/primitive.slice#method.align_to_mut 

self.Initialized = [
    "Both the `T` and the `U` must be valid at their given type.",
    "To transmute the inner type of the contents of a container, you must make sure to not violate any of the container's invariants."
]
self.Layout = [
    "Both types must have the same size.",
    "Note that source and destination are passed by-value, which means if `T` or `U` contain padding, that padding is not guaranteed to be preserved by transmute.",
    "This method has no purpose when either input element `T` or output element `U` are zero-sized and will return the original slice without splitting anything.",
    "When transmuting values that point elsewhere (such as pointers, references, boxes…), the caller has to ensure proper alignment of the pointed-to values."
]
retval.Initialized = ["Both the argument and the result must be valid at their given type."]
retval.Aliased = [
    "It can turn a `*mut T` into an `&mut T`.",
    "It can extend a lifetime, or shorten an invariant lifetime."
]
retval.Untyped = ["It is therefore your responsibility to guarantee that every value passed to transmute is valid at both types `T` and `U`. Failing to uphold this condition may lead to unexpected and unstable compilation results."]

core::ptr

core/ptr/fn.swap_nonoverlapping 

x.Untyped = ["The operation is untyped in the sense that data may be uninitialized or otherwise violate the requirements of `T`."]
y.Untyped = ["The operation is untyped in the sense that data may be uninitialized or otherwise violate the requirements of `T`."]

core/ptr/fn.copy 

dst.Leaked = [""]

core/ptr/struct.NonNull#method.as_uninit_ref

self.Allocated = ["Even for operations of size zero, the pointer must not be pointing to deallocated memory."]
self.Dereferencable = ["The memory range of the given size starting at the pointer must all be within the bounds of a single allocated object."]
self.Layout = ["The pointer must be properly aligned."]
retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get mutated.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/ptr/struct.NonNull#method.as_mut

self.Allocated = ["Even for operations of size zero, the pointer must not be pointing to deallocated memory."]
self.Initialized = ["The pointer must point to an initialized instance of `T`."]
self.Dereferencable = ["The memory range of the given size starting at the pointer must all be within the bounds of a single allocated object."]
self.Layout = ["The pointer must be properly aligned."]
retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get accessed (read or written) through any other pointer.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/ptr/struct.NonNull#method.offset

self.Allocated = ["Even for operations of size zero, the pointer must not be pointing to deallocated memory."]

core/ptr/struct.NonNull#method.add

self.Allocated = ["Even for operations of size zero, the pointer must not be pointing to deallocated memory."]

core/ptr/struct.NonNull#method.sub

self.Allocated = ["Even for operations of size zero, the pointer must not be pointing to deallocated memory."]

core/ptr/struct.NonNull#method.offset_from

self.Allocated = ["Even for operations of size zero, the pointer must not be pointing to deallocated memory."]

core/ptr/struct.NonNull#method.sub_ptr

self.Allocated = ["Even for operations of size zero, the pointer must not be pointing to deallocated memory."]

core/ptr/struct.NonNull#method.as_uninit_slice

self.Allocated = ["Even for operations of size zero, the pointer must not be pointing to deallocated memory."]

core/ptr/struct.NonNull#method.as_uninit_slice_mut

self.Allocated = ["Even for operations of size zero, the pointer must not be pointing to deallocated memory."]

core::slice

core/slice/trait.SliceIndex#tymethod.get_unchecked

slice.Allocated = ["A dangling slice pointer is undefined behavior even if the resulting reference is not used."]
slice.Dereferencable = ["When `slice` is not dereferenceable is undefined behavior even if the resulting pointer is not used."]

core/slice/trait.SliceIndex#tymethod.get_unchecked_mut

slice.Allocated = ["A dangling slice pointer is undefined behavior even if the resulting reference is not used."]
slice.Dereferencable = ["When `slice` is not dereferenceable is undefined behavior even if the resulting pointer is not used."]

core::any

[] https://doc.rust-lang.org/core/any/trait.Any.html#method.downcast_ref_unchecked-2

self.Initialized = ["The contained value must be of type `T`."]

[] https://doc.rust-lang.org/core/any/trait.Any.html#method.downcast_mut_unchecked-2

self.Initialized = ["The contained value must be of type `T`."]

core::sync

core/sync/atomic/struct.AtomicBool#method.from_ptr

retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get mutated.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/sync/atomic/struct.AtomicU8#method.from_ptr

retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get mutated.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/sync/atomic/struct.AtomicU16#method.from_ptr

retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get mutated.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/sync/atomic/struct.AtomicU32#method.from_ptr

retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get mutated.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/sync/atomic/struct.AtomicU64#method.from_ptr

retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get mutated.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/sync/atomic/struct.AtomicUsize#method.from_ptr

retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get mutated.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/sync/atomic/struct.AtomicI8#method.from_ptr

retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get mutated.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/sync/atomic/struct.AtomicI16#method.from_ptr

retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get mutated.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/sync/atomic/struct.AtomicI32#method.from_ptr

retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get mutated.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/sync/atomic/struct.AtomicI64#method.from_ptr

retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get mutated.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/sync/atomic/struct.AtomicIsize#method.from_ptr

retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get mutated.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

core/sync/atomic/struct.AtomicPtr#method.from_ptr

retval.Aliased = [
    "You must enforce Rust's aliasing rules. In particular, while this reference exists, the memory the pointer points to must not get mutated.",
    "The returned lifetime `'a` is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data."
]

alloc::ffi

alloc/ffi/struct.CString#method.from_raw 

ptr.Allocated = [
    "The raw pointer must point to a block of memory allocated by the global allocator.",
    "Other usage (trying to take ownership of a string that was allocated by foreign code) is likely to lead to undefined behavior or allocator corruption."
]
retval.DualOwned = ["Retakes ownership of a `CString` that was transferred to C via `CString::into_raw`."]

alloc::boxed

alloc/boxed/struct.Box#method.from_raw

self.Allocated = [
    "For non-zero-sized values, a `Box` will use the Global allocator for its allocation.",
    "For zero-sized values, the `Box` pointer still has to be valid for reads and writes (always be non-null pointers)."
]

alloc/boxed/struct.Box#method.from_raw_in

self.Allocated = [
    "For non-zero-sized values, a `Box` will use the in the given allocator for its allocation.",
    "For zero-sized values, the `Box` pointer still has to be valid for reads and writes (always be non-null pointers)."
]

alloc::sync

alloc/sync/struct.Arc#method.increment_strong_count

ptr.Allocated = ["`ptr` must point to a block of memory allocated by the global allocator."]

alloc/sync/struct.Arc#method.decrement_strong_count

ptr.Allocated = ["`ptr` must point to a block of memory allocated by the global allocator."]

alloc/sync/struct.Weak#method.from_raw

ptr.Allocated = ["`ptr` must point to a block of memory allocated by the global allocator."]

core::intrinsics

core/intrinsics/fn.unaligned_volatile_load

src.Allocated = [
    "A null pointer is never valid, not even for accesses of size zero.",
    "Even for operations of size zero, the pointer must not be pointing to deallocated memory."
]
src.Initialized = ["`src` must point to a properly initialized value of type `T`."]
src.Dereferencable = ["The memory range of the given size starting at the pointer must all be within the bounds of a single allocated object."]
retval.DualOwned = [
    "If `T` is not `Copy`, using both the returned value and the value at `src` can violate memory safety. Note that assigning to `src` counts as a use because it will attempt to drop the value at `src`.",
    "However, storing non-`Copy` types in volatile memory is almost certainly incorrect."
]

core/intrinsics/fn.unaligned_volatile_store

dst.Allocated = [
    "A null pointer is never valid, not even for accesses of size zero.",
    "Even for operations of size zero, the pointer must not be pointing to deallocated memory."
]
dst.Dereferencable = ["The memory range of the given size starting at the pointer must all be within the bounds of a single allocated object."]
dst.Leaked = ["This is safe, but it could leak allocations or resources, so care should be taken not to overwrite an object that should be dropped."]

core/intrinsics/fn.volatile_copy_memory

src.Allocated = [
    "A null pointer is never valid, not even for accesses of size zero.",
    "Even for operations of size zero, the pointer must not be pointing to deallocated memory."
]
src.Dereferencable = ["The memory range of the given size (`count * size_of::<T>()` bytes) starting at the pointer must all be within the bounds of a single allocated object."]

src.Layout = ["`src` must be properly aligned."]
dst.Allocated = [
    "A null pointer is never valid, not even for accesses of size zero.",
    "Even for operations of size zero, the pointer must not be pointing to deallocated memory."
]
dst.Dereferencable = ["The memory range of the given size (`count * size_of::<T>()` bytes) starting at the pointer must all be within the bounds of a single allocated object."]
dst.Layout = ["`dst` must be properly aligned."]
dst.DualOwned = ["If `T` is not `Copy`, using both the values in the region beginning at `self` and the region beginning at `*dst` can violate memory safety. Note that assigning to `*dst` counts as a use because it will attempt to drop the value at `*dst`."]
dst.Untyped = ["The copy is untyped in the sense that data may be uninitialized or otherwise violate the requirements of `T`."]
dst.Leaked = [""]

core/intrinsics/fn.volatile_copy_nonoverlapping_memory

src.Allocated = [
    "A null pointer is never valid, not even for accesses of size zero.",
    "Even for operations of size zero, the pointer must not be pointing to deallocated memory."
]
src.Dereferencable = [
    "The memory range of the given size (`count * size_of::<T>()` bytes) starting at the pointer must all be within the bounds of a single allocated object.",
    "The region of memory beginning at `src` with a size of `count * size_of::<T>()` bytes must not overlap with the region of memory beginning at `dst` with the same size."
]
src.Layout = ["`src` must be properly aligned."]
dst.Allocated = [
    "A null pointer is never valid, not even for accesses of size zero.",
    "Even for operations of size zero, the pointer must not be pointing to deallocated memory."
]
dst.Dereferencable = [
    "The memory range of the given size (`count * size_of::<T>()` bytes) starting at the pointer must all be within the bounds of a single allocated object.",
    "The region of memory beginning at `src` with a size of `count * size_of::<T>()` bytes must not overlap with the region of memory beginning at `dst` with the same size."
]
dst.Layout = ["`dst` must be properly aligned."]
dst.DualOwned = ["If `T` is not `Copy`, using both the values in the region beginning at `self` and the region beginning at `*dst` can violate memory safety. Note that assigning to `*dst` counts as a use because it will attempt to drop the value at `*dst`."]
dst.Untyped = ["The copy is untyped in the sense that data may be uninitialized or otherwise violate the requirements of `T`."]
dst.Leaked = [""]

core/intrinsics/fn.volatile_set_memory

dst.Allocated = [
    "A null pointer is never valid, not even for accesses of size zero.",
    "Even for operations of size zero, the pointer must not be pointing to deallocated memory."
]
dst.Dereferencable = ["The memory range of the given size (`count * size_of::<T>()` bytes) starting at the pointer must all be within the bounds of a single allocated object."]
dst.Layout = ["`dst` must be properly aligned ('min_align_of::<T>()')."]
dst.Untyped = ["Additionally, note that changing `dst` in this way can easily lead to undefined behavior (UB) later if the written bytes are not a valid representation of some `T`."]
dst.Leaked = [""]

core/intrinsics/fn.copy

dst.Leaked = [""]

core/intrinsics/fn.copy_nonoverlapping

dst.Leaked = [""]

core/intrinsics/fn.drop_in_place

to_drop.Allocated = [
    "`to_drop` must be nonnull, even if T has size 0.",
    "Even for operations of size zero, the pointer must not be pointing to deallocated memory."
]
to_drop.Initialized = ["The value `to_drop` points to must be valid for dropping, which may mean it must uphold additional invariants. These invariants depend on the type of the value being dropped."]
to_drop.Dereferencable = ["The memory range of the given size starting at the pointer must all be within the bounds of a single allocated object."]
to_drop.Layout = [
    "`to_drop` must be properly aligned, even if `T` has size 0.",
    "Unaligned values cannot be dropped in place, they must be copied to an aligned location first using `ptr::read_unaligned`."
]
to_drop.Freed = ["Executes the destructor (if any) of the pointed-to value."]

core/intrinsics/fn.write_bytes

dst.Leaked = [""]

Summary

We found that the documentation regarding the safety requirements of unsafe APIs in the Rust std has disadvantages.

In short:

  1. Consistency: Inconsistent text for the same safety requirements.
  2. Completeness: Some APIs have missing safety requirements.
  3. Intuitiveness: Safety requirements for APIs require linking to additional pages for further details.

We published our research on these issues at ICSE 2024 and uploaded an extended version that optimizes the classification of safety requirements on arXiv. https://arxiv.org/abs/2412.06251

Based on the latest version, we reorganize the documents for the standard library's Unsafe APIs, focusing solely on safety requirements, without addressing other functionalities. The document links the original API documents, safety requirements, and corresponding parameters/return value altogether.

For easier viewing, I encoded it into a TOML file, and the link is: Safety Documents

During the process of reorganizing the documents, I found that some APIs had missing safety descriptions. In this issue, we’ve organized a checklist in the format of "namespace—API" for the Rust team. The refined documents we modified is just a preliminary attempt, and we hope that Rust can provide more user-friendly and structured documents for unsafe APIs in the future.

The above outlines the shortcomings I have identified. If there are any errors or if further discussion is needed, I would also greatly appreciate feedback from the Rust team 😊.

@VaynNecol VaynNecol added the A-docs Area: documentation for any part of the project, including the compiler, standard library, and tools label Dec 13, 2024
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Dec 13, 2024
@workingjubilee
Copy link
Member

workingjubilee commented Dec 13, 2024

@VaynNecol You have miscategorized some things, it seems to me. For instance, what you call Untyped is not meaningfully considered a postcondition safety property, as the following produces UB:

let zero_non_zero: NonZeroU32 = unsafe { core::mem::zeroed() };

As it is thus always invalid to call mem::zeroed with a type T that is not valid if given the all-zeroes bitpattern, it seems to be a precondition, to me? Perhaps I don't understand what sort of temporality you mean.

unsafe { core::mem::zeroed::<NonZeroU32>() }

Perhaps it is a postcondition when considering alloc_zeroed? But these are different things. They may look like the same safety problem, but they are definitely not. One has performed a "typed copy" already, and the other has not.

Your paper seems to recommend avoiding linking between pages, but I think it has not come with evaluation of the maintenance cost. As the safety requirements evolve, sometimes duplicate sites are not necessarily always edited in sync, because of oversights. Thus, avoiding linking is precisely what has led to inconsistencies. To avoid linking entirely, as you recommend, would have done much worse at surviving language evolution. So we simply cannot take both recommendations. We can either accept some usage of links or be uselessly inconsistent. If you had prioritized the things you considered desirable, then we could have identified a recommendation already. But perhaps you value one over the other?

Some of your attempts to extract correlations have resulted in what seems like confusing safe behaviors that we document as an API expectation with unsafe requirements. These should not be categorized in similar ways. Nothing that is "if so and so is true, we panic" should be documented as a safety requirement, as a program that panics is not unsound. There is also no requirement that destructors are run in a program, either.

There are other problems, but the ones I have identified are most germane to the presentation of this as an implied suggestion to improve our documentation.

All that said, more consistency is generally desirable, so I am surprised that you have not offered some of these changes as PRs that diff our documentation. Then you may have heard this feedback, more incrementally, during a review of these changes, instead of later. That might have led to a stronger paper, and if those changes were accepted (and some seem likely candidates) the ability to say your study had immediate consequences on the world. I suppose it's a bit late for that, though?

@jieyouxu jieyouxu added T-libs Relevant to the library team, which will review and decide on the PR/issue. and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Dec 13, 2024
@workingjubilee
Copy link
Member

Your suggested documentation improvements are also dated, though. For instance, almost every case of this:

self.Allocated = ["Even for operations of size zero, the pointer must not be pointing to deallocated memory."]

Those have all been eliminated due to the adoption of provenance monotonicity, see #117329

@VaynNecol
Copy link
Author

Thank you for your prompt feedback! The safety properties I provided are based on expert knowledge, and I look forward to receiving more suggestions from experts to continuously improve this classification.

@workingjubilee Regarding the example of zeroed, your explanation gave me valuable insights❤️. The pre-condtion SP is based on the API arguments. Since zeroed does not take any parameters, I placed Untyped in the post-condition.

However, within the function, the validity of the all-zeroed pattern is checked using an assert, so it is a pre-condition that is independent of the parameters.

unsafe {
        intrinsics::assert_zero_valid::<T>();
        MaybeUninit::zeroed().assume_init()
}

Thank you for pointing out this issue. However, Untyped as a post-condition SP is not limited to just one API zeored. For example, https://doc.rust-lang.org/core/primitive.pointer.html#method.swap.


The idea behind using the post-condition is that calling this function does not incur UB immediately, but the parameters and return values involved might lead to UB when used later.

It's similar to creating a raw pointer is safe, but its usage is unsafe.


Regarding link redirections, it's a minor issue for me as a standard library user, where I find reading the documentation cumbersome.

From a maintenance perspective, I fully understand the human effort required for proofreading when synchronizing versions. In my audit of the std documentation, I found it disastrous to face similar APIs with different documents, not to mention maintaining them. So, in this issue, I’m not suggesting removing the links, but rather believe the target of the redirects should have complete documents.

However, for similar APIs like Rc and Arc, I have also identified the inconsistencies. They can be located in Rc, but Arc does not mention them.
alloc/sync/struct.Arc#method.increment_strong_count

ptr.Allocated = ["`ptr` must point to a block of memory allocated by the global allocator."]

alloc/sync/struct.Arc#method.decrement_strong_count

ptr.Allocated = ["`ptr` must point to a block of memory allocated by the global allocator."]

For the paper in Arxiv, I modified the original version after receiving advice from some Rust experts, and it serves as an extended version. For the ICSE version, I simply wanted to build a complete classification without intending to change the existing documentation. But while pairing SP with documentation slices, I discovered some missing issues🤔. Some of these gaps may need to be added to the current documentation.

Furthermore, some descriptions may not be strict safety guarantees, but they should be mentioned in the documentation to help programmers to prevent misuse (e.g., memory leak).

@workingjubilee
Copy link
Member

@workingjubilee Regarding the example of zeroed, your explanation gave me valuable insights❤️. The pre-condtion SP is based on the API arguments. Since zeroed does not take any parameters, I placed Untyped in the post-condition.

The type that is given to instantiate the function must be considered as effectively an input to the function, because it is in every meaningful way an input provided by the programmer, even if it is not an "input" in the sense of being a "value".

@workingjubilee
Copy link
Member

workingjubilee commented Dec 13, 2024

I honestly have no idea why you attached Untyped to mem::swap at all, either, so citing it as an example of "but it appears in other places, too!" underscores my point: Your documentation proposes making things too consistent in some places. It is easy, when trying to make things consistent, to accidentally strip away the important and very specific details relevant to an API.

1.1 is not equal to 1.0, after all.

@workingjubilee

This comment has been minimized.

@workingjubilee
Copy link
Member

workingjubilee commented Dec 13, 2024

So, in this issue, I’m not suggesting removing the links, but rather believe the target of the redirects should have complete documents.

With this, I would be interested if you have any ideas for how to identify all targets-of-redirects and make sure they never acquire any additional links within them? It seems nontrivial to check, because a piece of documentation can have a redirect added after it is already a target of a redirect.

@workingjubilee
Copy link
Member

workingjubilee commented Dec 13, 2024

For both, the actual safety requirement is that the data should be allocated with the allocator A, because Rc and Arc are now Rc<T, A> and Arc<T, A>.

Wait, nevermind. I apparently forgot the increment_strong_count is implicitly A = Global. Ugh. Another reason these things are hard to track, unfortunately.

@VaynNecol
Copy link
Author

I honestly have no idea why you attached Untyped to mem::swap at all, either, so citing it as an example of "but it appears in other places, too!" underscores my point: Your documentation proposes making things too consistent in some places. It is easy, when trying to make things consistent, to accidentally strip away the important and very specific details relevant to an API.

1.1 is not equal to 1.0, after all.

As far as the increment_strong_count inconsistency, that is a case where something should have been removed, but wasn't.

I believe there’s a misunderstanding here😂. I’m not suggesting that the safety requirements for every API should be written in the same text. Different APIs have different functionalities, and excessive consistency can be rigid. The items listed are those I noticed during my review, where similar APIs mentioned these requirements, but those APIs did not (though due to the limitations of my review, there may be mistakes). The supplementary documentation slice is taken from other similar APIs, only as an example for the Rust team’s reference. It doesn't mean the final documentation should be written this way (I think this is a significant misunderstanding).

What I would like to say is whether each safety requirement could have a label, and be paired with specific parameters/return values/types (may be in the future). This would help programmers understand better (even though such work would incur a high maintenance cost).

@workingjubilee
Copy link
Member

Oh, if the most important thing is labels, then sure! That sounds like a decent idea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-docs Area: documentation for any part of the project, including the compiler, standard library, and tools T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

4 participants