diff options
| author | Petros Angelatos <petrosagg@gmail.com> | 2025-05-15 15:46:53 +0300 |
|---|---|---|
| committer | Petros Angelatos <petrosagg@gmail.com> | 2025-09-22 10:59:52 +0300 |
| commit | e9b2c4f395673399b0445733c7e8d3955e42ff0c (patch) | |
| tree | 8904930357d9c6d8eed262aa706db7b1b01f1223 /src/tools/miri | |
| parent | b00998aaa50a78b1e45fb107a6c0cfd3f1dc44dd (diff) | |
avoid violating `slice::from_raw_parts` safety contract in `Vec::extract_if`
The implementation of the `Vec::extract_if` iterator violates the safety
contract adverized by `slice::from_raw_parts` by always constructing a
mutable slice for the entire length of the vector even though that span
of memory can contain holes from items already drained. The safety
contract of `slice::from_raw_parts` requires that all elements must be
properly initialized.
As an example we can look at the following code:
```rust
let mut v = vec![Box::new(0u64), Box::new(1u64)];
for item in v.extract_if(.., |x| **x == 0) {
drop(item);
}
```
In the second iteration a `&mut [Box<u64>]` slice of length 2 will be
constructed. The first slot of the slice contains the bitpattern of an
already deallocated box, which is invalid.
This fixes the issue by only creating references to valid items and
using pointer manipulation for the rest. I have also taken the liberty
to remove the big `unsafe` blocks in place of targetted ones with a
SAFETY comment. The approach closely mirrors the implementation of
`Vec::retain_mut`.
Signed-off-by: Petros Angelatos <petrosagg@gmail.com>
Diffstat (limited to 'src/tools/miri')
| -rw-r--r-- | src/tools/miri/tests/pass/vec.rs | 10 |
1 files changed, 10 insertions, 0 deletions
diff --git a/src/tools/miri/tests/pass/vec.rs b/src/tools/miri/tests/pass/vec.rs index 3e526813bb4..8b1b1e143b1 100644 --- a/src/tools/miri/tests/pass/vec.rs +++ b/src/tools/miri/tests/pass/vec.rs @@ -169,6 +169,15 @@ fn miri_issue_2759() { input.replace_range(0..0, "0"); } +/// This was skirting the edge of UB, let's make sure it remains on the sound side. +/// Context: <https://github.com/rust-lang/rust/pull/141032>. +fn extract_if() { + let mut v = vec![Box::new(0u64), Box::new(1u64)]; + for item in v.extract_if(.., |x| **x == 0) { + drop(item); + } +} + fn main() { assert_eq!(vec_reallocate().len(), 5); @@ -199,4 +208,5 @@ fn main() { swap_remove(); reverse(); miri_issue_2759(); + extract_if(); } |
