-
Notifications
You must be signed in to change notification settings - Fork 13.8k
RawVecInner: add missing unsafe
to unsafe fns
#145067
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
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not an std reviewer, but I wanted to suggest that you add safety comments to the functions that you marked as unsafe.
You could even go one step further and try adding contracts to them, but be aware that it is still highly experimental, and it may not work.
@celinval I considered that, but I noticed that most of the safety preconditions of the functions that were already marked |
Thom is off rotation so r? libs
This is almost certainly oversight rather than an intentional decision about noise. I would be pretty to have explicit |
For the above, I think the changes here look pretty good but will double check once the docs are there |
Reminder, once the PR becomes ready for a review, use |
@rustbot ready |
☔ The latest upstream changes (presumably #146185) made this pull request unmergeable. Please resolve the merge conflicts. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a big improvement over what we have, just a few followup requests here. Could you also make sure to rewrap comments at 100 chars? Seems like the doc comments are closer to 120.
Please also squash, should be good to go with the few things here
/// # Safety | ||
/// - If `current_memory` matches `Some((ptr, old_layout))`, it must accurately describe an allocation: | ||
/// - `ptr` must denote a block of memory *currently allocated* via `alloc` | ||
/// - `old_layout` must *fit* that block of memory | ||
/// - If `current_memory` matches `Some((ptr, old_layout))`, then `new_layout.size()` must be greater than or equal to `old_layout.size()` | ||
// not marked inline(never) since we want optimizers to be able to observe the specifics of this | ||
// function, see tests/codegen-llvm/vec-reserve-extend.rs. | ||
#[cold] | ||
fn finish_grow<A>( | ||
unsafe fn finish_grow<A>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
old_layout
and new_layout
also need the same alignment. The safety preconditions also only are required for when current_memory
is Some
, so I think that could be explicit:
/// # Safety | |
/// - If `current_memory` matches `Some((ptr, old_layout))`, it must accurately describe an allocation: | |
/// - `ptr` must denote a block of memory *currently allocated* via `alloc` | |
/// - `old_layout` must *fit* that block of memory | |
/// - If `current_memory` matches `Some((ptr, old_layout))`, then `new_layout.size()` must be greater than or equal to `old_layout.size()` | |
// not marked inline(never) since we want optimizers to be able to observe the specifics of this | |
// function, see tests/codegen-llvm/vec-reserve-extend.rs. | |
#[cold] | |
fn finish_grow<A>( | |
unsafe fn finish_grow<A>( | |
/// # Safety | |
/// - If `current_memory` matches `Some((ptr, old_layout))`: | |
/// - `ptr` must denote a block of memory *currently allocated* via `alloc` | |
/// - `old_layout` must *fit* that block of memory | |
/// - `new_layout` must have the same alignment as `old_layout` | |
/// - `new_layout.size()` must be greater than or equal to `old_layout.size()` | |
/// If `current_memory` is `None`, this function is safe. | |
// not marked inline(never) since we want optimizers to be able to observe the specifics of this | |
// function, see tests/codegen-llvm/vec-reserve-extend.rs. | |
#[cold] | |
unsafe fn finish_grow<A>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
library/alloc/src/raw_vec/mod.rs
Outdated
// SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items | ||
|
||
unsafe { self.set_ptr_and_cap(ptr, cap) }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Preexisting but may as well fix
// SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items | |
unsafe { self.set_ptr_and_cap(ptr, cap) }; | |
// SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items | |
unsafe { self.set_ptr_and_cap(ptr, cap) }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
pub(crate) fn reserve(&mut self, len: usize, additional: usize) { | ||
self.inner.reserve(len, additional, T::LAYOUT) | ||
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout | ||
unsafe { self.inner.reserve(len, additional, T::LAYOUT) } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The other precondition is
elem_layout
's size must be a multiple of its alignment
This should always be true for T::LAYOUT
, but it would be a good idea to add an assert to RawVec::new
asserting this:
// Layout types should always be a multiple of alignment; ensure this for alloc preconditions
const { assert!(T::LAYOUT.size() % T::LAYOUT.align() == 0) };
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does the multiple of alignment
precondition actually come from? It's mentioned for grow_amortized
but I don't see it for either of the unsafe
calls it makes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It justifies that the old_layout
passed to grow
fits the existing allocation. New layouts are computed using Layout::repeat
, which adds padding if the element size is not a multiple of the element alignment, but old layouts for reallocation and deallocation are computed (in current_memory
) using plain multiplication.
library/alloc/src/raw_vec/mod.rs
Outdated
// SAFETY: Precondition passed to caller | ||
let ptr = | ||
unsafe { finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)? }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
current_memory
is also needed to meet the preconditions for finish_grow
, just worth a note.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added that "current_memory
does the right thing". I have a machine-checked formal proof (with caveats) but replicating the reasoning in English would be tricky (and tedious).
This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
This comment has been minimized.
This comment has been minimized.
- RawVecInner::grow_exact causes UB if called with len and additional arguments such that len + additional is less than the current capacity. Indeed, in that case it calls Allocator::grow with a new_layout that is smaller than old_layout, which violates a safety precondition. - All RawVecInner methods for resizing the buffer cause UB if called with an elem_layout different from the one used to initially allocate the buffer, because in that case Allocator::grow/shrink is called with an old_layout that does not fit the allocated block, which violates a safety precondition. - RawVecInner::current_memory might cause UB if called with an elem_layout different from the one used to initially allocate the buffer, because the unchecked_mul might overflow. - Furthermore, these methods cause UB if called with an elem_layout where the size is not a multiple of the alignment. This is because Layout::repeat is used (in layout_array) to compute the allocation's layout when allocating, which includes padding to ensure alignment of array elements, but simple multiplication is used (in current_memory) to compute the old allocation's layout when resizing or deallocating, which would cause the layout used to resize or deallocate to not fit the allocated block, which violates a safety precondition.
@rustbot ready |
Sorry for the delay here. Thank you for the updates, this is a great improvement! I'm going to run one of the faster try jobs just to make sure this still builds (think there has been some work on rawvec), r=me after that. If you're around, feel free to just rebase. @bors2 try jobs=aarch64-gnu-llvm-20-2 |
RawVecInner: add missing `unsafe` to unsafe fns try-job: aarch64-gnu-llvm-20-2
This comment has been minimized.
This comment has been minimized.
A job failed! Check out the build log: (web) (plain enhanced) (plain) Click to see the possible cause of the failure (guessed by this bot)
|
💔 Test for eea246e failed: CI. Failed jobs:
|
Spurious failures going on |
RawVecInner: add missing `unsafe` to unsafe fns try-job: aarch64-gnu-llvm-20-2
This comment has been minimized.
This comment has been minimized.
💔 Test for 998fdc0 failed: CI. Failed jobs:
|
A job failed! Check out the build log: (web) (plain enhanced) (plain) Click to see the possible cause of the failure (guessed by this bot)
|
Still spurious. Tested locally that std build when rebased. @bors r+ rollup |
RawVecInner: add missing `unsafe` to unsafe fns Some (module-private) functions in `library/alloc/src/raw_vec/mod.rs` are unsafe (i.e. may cause UB when called from safe code) but are not marked `unsafe`. Specifically: - `RawVecInner::grow_exact` causes UB if called with `len` and `additional` arguments such that `len + additional` is less than the current capacity. Indeed, in that case it calls [Allocator::grow](https://doc.rust-lang.org/std/alloc/trait.Allocator.html#method.grow) with a `new_layout` that is smaller than `old_layout`, which violates a safety precondition. - The RawVecInner methods for resizing the buffer cause UB if called with an `elem_layout` different from the one used to initially allocate the buffer, because in that case `Allocator::grow` or `Allocator::shrink` are called with an `old_layout` that does not *fit* the allocated block, which violates a safety precondition. - `RawVecInner::current_memory` might cause UB if called with an `elem_layout` different from the one used to initially allocate the buffer, because the `unchecked_mul` might overflow. - Furthermore, these methods cause UB if called with an `elem_layout` where the size is not a multiple of the alignment. This is because `Layout::repeat` is used (in `layout_array`) to compute the allocation's layout when allocating, which includes padding to ensure alignment of array elements, but simple multiplication is used (in `current_memory`) to compute the old allocation's layout when resizing or deallocating, which would cause the layout used to resize or deallocate to not *fit* the allocated block, which violates a safety precondition. I discovered these issues while performing formal verification of `library/alloc/src/raw_vec/mod.rs` per [Challenge 19](https://model-checking.github.io/verify-rust-std/challenges/0019-rawvec.html) of the [AWS Rust Standard Library Verification Contest](https://aws.amazon.com/blogs/opensource/verify-the-safety-of-the-rust-standard-library/).
Rollup of 14 pull requests Successful merges: - #145067 (RawVecInner: add missing `unsafe` to unsafe fns) - #145277 (Do not materialise X in [X; 0] when X is unsizing a const) - #145973 (Add `std` support for `armv7a-vex-v5`) - #146667 (Add an attribute to check the number of lanes in a SIMD vector after monomorphization) - #146735 (unstably constify float mul_add methods) - #146737 (f16_f128: enable some more tests in Miri) - #146766 (Add attributes for #[global_allocator] functions) - #146905 (llvm: update remarks support on LLVM 22) - #146982 (Remove erroneous normalization step in `tests/run-make/linker-warning`) - #147005 (Small string formatting cleanup) - #147007 (Explicitly note `&[SocketAddr]` impl of `ToSocketAddrs`) - #147008 (bootstrap.py: Respect build.jobs while building bootstrap tool) - #147013 (rustdoc: Fix documentation for `--doctest-build-arg`) - #147015 (Use `LLVMDisposeTargetMachine`) r? `@ghost` `@rustbot` modify labels: rollup
Rollup merge of #145067 - btj:patch-3, r=tgross35 RawVecInner: add missing `unsafe` to unsafe fns Some (module-private) functions in `library/alloc/src/raw_vec/mod.rs` are unsafe (i.e. may cause UB when called from safe code) but are not marked `unsafe`. Specifically: - `RawVecInner::grow_exact` causes UB if called with `len` and `additional` arguments such that `len + additional` is less than the current capacity. Indeed, in that case it calls [Allocator::grow](https://doc.rust-lang.org/std/alloc/trait.Allocator.html#method.grow) with a `new_layout` that is smaller than `old_layout`, which violates a safety precondition. - The RawVecInner methods for resizing the buffer cause UB if called with an `elem_layout` different from the one used to initially allocate the buffer, because in that case `Allocator::grow` or `Allocator::shrink` are called with an `old_layout` that does not *fit* the allocated block, which violates a safety precondition. - `RawVecInner::current_memory` might cause UB if called with an `elem_layout` different from the one used to initially allocate the buffer, because the `unchecked_mul` might overflow. - Furthermore, these methods cause UB if called with an `elem_layout` where the size is not a multiple of the alignment. This is because `Layout::repeat` is used (in `layout_array`) to compute the allocation's layout when allocating, which includes padding to ensure alignment of array elements, but simple multiplication is used (in `current_memory`) to compute the old allocation's layout when resizing or deallocating, which would cause the layout used to resize or deallocate to not *fit* the allocated block, which violates a safety precondition. I discovered these issues while performing formal verification of `library/alloc/src/raw_vec/mod.rs` per [Challenge 19](https://model-checking.github.io/verify-rust-std/challenges/0019-rawvec.html) of the [AWS Rust Standard Library Verification Contest](https://aws.amazon.com/blogs/opensource/verify-the-safety-of-the-rust-standard-library/).
Rollup of 14 pull requests Successful merges: - rust-lang/rust#145067 (RawVecInner: add missing `unsafe` to unsafe fns) - rust-lang/rust#145277 (Do not materialise X in [X; 0] when X is unsizing a const) - rust-lang/rust#145973 (Add `std` support for `armv7a-vex-v5`) - rust-lang/rust#146667 (Add an attribute to check the number of lanes in a SIMD vector after monomorphization) - rust-lang/rust#146735 (unstably constify float mul_add methods) - rust-lang/rust#146737 (f16_f128: enable some more tests in Miri) - rust-lang/rust#146766 (Add attributes for #[global_allocator] functions) - rust-lang/rust#146905 (llvm: update remarks support on LLVM 22) - rust-lang/rust#146982 (Remove erroneous normalization step in `tests/run-make/linker-warning`) - rust-lang/rust#147005 (Small string formatting cleanup) - rust-lang/rust#147007 (Explicitly note `&[SocketAddr]` impl of `ToSocketAddrs`) - rust-lang/rust#147008 (bootstrap.py: Respect build.jobs while building bootstrap tool) - rust-lang/rust#147013 (rustdoc: Fix documentation for `--doctest-build-arg`) - rust-lang/rust#147015 (Use `LLVMDisposeTargetMachine`) r? `@ghost` `@rustbot` modify labels: rollup
Some (module-private) functions in
library/alloc/src/raw_vec/mod.rs
are unsafe (i.e. may cause UB when called from safe code) but are not markedunsafe
. Specifically:RawVecInner::grow_exact
causes UB if called withlen
andadditional
arguments such thatlen + additional
is less than the current capacity. Indeed, in that case it calls Allocator::grow with anew_layout
that is smaller thanold_layout
, which violates a safety precondition.elem_layout
different from the one used to initially allocate the buffer, because in that caseAllocator::grow
orAllocator::shrink
are called with anold_layout
that does not fit the allocated block, which violates a safety precondition.RawVecInner::current_memory
might cause UB if called with anelem_layout
different from the one used to initially allocate the buffer, because theunchecked_mul
might overflow.elem_layout
where the size is not a multiple of the alignment. This is becauseLayout::repeat
is used (inlayout_array
) to compute the allocation's layout when allocating, which includes padding to ensure alignment of array elements, but simple multiplication is used (incurrent_memory
) to compute the old allocation's layout when resizing or deallocating, which would cause the layout used to resize or deallocate to not fit the allocated block, which violates a safety precondition.I discovered these issues while performing formal verification of
library/alloc/src/raw_vec/mod.rs
per Challenge 19 of the AWS Rust Standard Library Verification Contest.