Skip to content

Conversation

btj
Copy link
Contributor

@btj btj commented Aug 7, 2025

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 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 of the AWS Rust Standard Library Verification Contest.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Aug 7, 2025
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@btj btj marked this pull request as ready for review August 8, 2025 07:54
@rustbot
Copy link
Collaborator

rustbot commented Aug 8, 2025

r? @thomcc

rustbot has assigned @thomcc.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Aug 8, 2025
Copy link
Contributor

@celinval celinval left a 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.

@btj
Copy link
Contributor Author

btj commented Aug 8, 2025

@celinval I considered that, but I noticed that most of the safety preconditions of the functions that were already marked unsafe were not documented either, so concerns of adhering to existing practice and avoiding excessive "noise" kept me from adding them so far. Happy to add them if @thomcc thinks it makes sense. Mostly each resizing or deallocation method should get a safety precondition saying elem_layout must be the same one used in earlier calls and must have a size that is a multiple of the alignment.

@tgross35
Copy link
Contributor

tgross35 commented Sep 3, 2025

Thom is off rotation so

r? libs

@celinval I considered that, but I noticed that most of the safety preconditions of the functions that were already marked unsafe were not documented either, so concerns of adhering to existing practice and avoiding excessive "noise" kept me from adding them so far. Happy to add them if @thomcc thinks it makes sense. Mostly each resizing or deallocation method should get a safety precondition saying elem_layout must be the same one used in earlier calls and must have a size that is a multiple of the alignment.

This is almost certainly oversight rather than an intentional decision about noise. I would be pretty to have explicit # Safety sections, the preconditions are super tricky to follow through functions.

@rustbot rustbot assigned tgross35 and unassigned thomcc Sep 3, 2025
@tgross35
Copy link
Contributor

tgross35 commented Sep 3, 2025

For the above,
@rustbot author

I think the changes here look pretty good but will double check once the docs are there

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 3, 2025
@rustbot
Copy link
Collaborator

rustbot commented Sep 3, 2025

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@btj
Copy link
Contributor Author

btj commented Sep 3, 2025

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Sep 3, 2025
@bors
Copy link
Collaborator

bors commented Sep 4, 2025

☔ The latest upstream changes (presumably #146185) made this pull request unmergeable. Please resolve the merge conflicts.

Copy link
Contributor

@tgross35 tgross35 left a 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

View changes since this review

Comment on lines 823 to 849
/// # 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>(
Copy link
Contributor

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:

Suggested change
/// # 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>(

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 710 to 722
// 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) };
Copy link
Contributor

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

Suggested change
// 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) };

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 330 to 335
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) }
}
Copy link
Contributor

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) };

Copy link
Contributor

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

Copy link
Contributor Author

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.

Comment on lines 735 to 752
// SAFETY: Precondition passed to caller
let ptr =
unsafe { finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)? };
Copy link
Contributor

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.

Copy link
Contributor Author

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).

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 5, 2025
@rustbot
Copy link
Collaborator

rustbot commented Sep 5, 2025

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.

@rust-log-analyzer

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.
@btj
Copy link
Contributor Author

btj commented Sep 5, 2025

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Sep 5, 2025
@tgross35
Copy link
Contributor

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

rust-bors bot added a commit that referenced this pull request Sep 24, 2025
RawVecInner: add missing `unsafe` to unsafe fns

try-job: aarch64-gnu-llvm-20-2
@rust-bors

This comment has been minimized.

@rust-log-analyzer
Copy link
Collaborator

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)

@rust-bors
Copy link

rust-bors bot commented Sep 24, 2025

💔 Test for eea246e failed: CI. Failed jobs:

@tgross35
Copy link
Contributor

Spurious failures going on
@bors2 try jobs=aarch64-gnu-llvm-20-2

rust-bors bot added a commit that referenced this pull request Sep 24, 2025
RawVecInner: add missing `unsafe` to unsafe fns

try-job: aarch64-gnu-llvm-20-2
@rust-bors

This comment has been minimized.

@rust-bors
Copy link

rust-bors bot commented Sep 24, 2025

💔 Test for 998fdc0 failed: CI. Failed jobs:

@rust-log-analyzer
Copy link
Collaborator

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)

@tgross35
Copy link
Contributor

Still spurious. Tested locally that std build when rebased.

@bors r+ rollup

@bors
Copy link
Collaborator

bors commented Sep 24, 2025

📌 Commit 96f385b has been approved by tgross35

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 24, 2025
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Sep 25, 2025
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/).
bors added a commit that referenced this pull request Sep 25, 2025
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
@bors bors merged commit 2acd80c into rust-lang:master Sep 25, 2025
10 of 11 checks passed
@rustbot rustbot added this to the 1.92.0 milestone Sep 25, 2025
rust-timer added a commit that referenced this pull request Sep 25, 2025
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/).
github-actions bot pushed a commit to rust-lang/miri that referenced this pull request Sep 26, 2025
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants