Skip to content

Commit 8b292d5

Browse files
authored
Fix UB in IntoOnes and setup testing with MIRI (#112)
* Fix UB in IntoOnes * Make tests faster on MIRI * Test with MIRI in CI * Clean up invalid action inputs
1 parent 46e7ba3 commit 8b292d5

2 files changed

Lines changed: 45 additions & 47 deletions

File tree

.github/workflows/rust.yml

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@ jobs:
2525
- uses: dtolnay/rust-toolchain@stable
2626
with:
2727
target: x86_64-unknown-linux-gnu
28-
profile: minimal
2928
toolchain: ${{ matrix.rust }}
30-
override: true
3129
- name: Tests (x86_64)
3230
run: |
3331
cargo test -v --no-default-features --tests --lib &&
@@ -45,9 +43,7 @@ jobs:
4543
- uses: dtolnay/rust-toolchain@stable
4644
with:
4745
target: aarch64-unknown-linux-gnu
48-
profile: minimal
4946
toolchain: ${{ matrix.rust }}
50-
override: true
5147
- name: Tests (aarch64)
5248
run: cargo check --target aarch64-unknown-linux-gnu
5349

@@ -63,10 +59,8 @@ jobs:
6359
- uses: actions/checkout@v4
6460
- uses: dtolnay/rust-toolchain@stable
6561
with:
66-
profile: minimal
6762
toolchain: ${{ matrix.rust }}
6863
components: clippy
69-
override: true
7064
- name: Run Clippy
7165
run: |
7266
cargo clippy
@@ -82,10 +76,8 @@ jobs:
8276
- uses: actions/checkout@v4
8377
- uses: dtolnay/rust-toolchain@stable
8478
with:
85-
profile: minimal
8679
toolchain: ${{ matrix.rust }}
8780
components: rustfmt
88-
override: true
8981
- name: Run Clippy
9082
run: |
9183
cargo fmt --all --check
@@ -101,10 +93,8 @@ jobs:
10193
- uses: actions/checkout@v4
10294
- uses: dtolnay/rust-toolchain@stable
10395
with:
104-
profile: minimal
10596
toolchain: ${{ matrix.rust }}
10697
components: clippy
107-
override: true
10898
- name: Run Clippy
10999
run: |
110100
cd benches
@@ -119,4 +109,19 @@ jobs:
119109
with:
120110
target: wasm32-unknown-unknown
121111
- name: Check wasm
122-
run: cargo check --target wasm32-unknown-unknown
112+
run: cargo check --target wasm32-unknown-unknown
113+
114+
miri:
115+
runs-on: ubuntu-latest
116+
strategy:
117+
matrix:
118+
# Check builds only on nightly
119+
rust: [nightly]
120+
steps:
121+
- uses: actions/checkout@v4
122+
- uses: dtolnay/rust-toolchain@stable
123+
with:
124+
toolchain: ${{ matrix.rust }}
125+
components: miri
126+
- name: Run miri
127+
run: cargo miri test

src/lib.rs

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@
1717
#![deny(clippy::undocumented_unsafe_blocks)]
1818

1919
extern crate alloc;
20-
use alloc::{
21-
vec,
22-
vec::{IntoIter, Vec},
23-
};
20+
use alloc::{vec, vec::Vec};
2421

2522
mod block;
2623
mod range;
@@ -30,8 +27,8 @@ extern crate serde;
3027
#[cfg(feature = "serde")]
3128
mod serde_impl;
3229

30+
use core::fmt::Write;
3331
use core::fmt::{Binary, Display, Error, Formatter};
34-
use core::{fmt::Write, mem::ManuallyDrop};
3532

3633
use core::cmp::{Ord, Ordering};
3734
use core::iter::{Chain, FusedIterator};
@@ -545,33 +542,24 @@ impl FixedBitSet {
545542
/// Iterator element is the index of the `1` bit, type `usize`.
546543
/// Unlike `ones`, this function consumes the `FixedBitset`.
547544
pub fn into_ones(self) -> IntoOnes {
548-
// SAFETY: This is using the exact same allocation pattern, size, and capacity
549-
// making this reconstruction of the Vec safe.
550-
let mut data = unsafe {
551-
let mut data = ManuallyDrop::new(self.data);
552-
let ptr = data.as_mut_ptr().cast();
553-
let len = data.len() * SimdBlock::USIZE_COUNT;
554-
let capacity = data.capacity() * SimdBlock::USIZE_COUNT;
555-
Vec::from_raw_parts(ptr, len, capacity)
556-
};
557-
if data.is_empty() {
558-
IntoOnes {
559-
bitset_front: 0,
560-
bitset_back: 0,
561-
block_idx_front: 0,
562-
block_idx_back: 0,
563-
remaining_blocks: data.into_iter(),
564-
}
565-
} else {
566-
let first_block = data.remove(0);
567-
let last_block = data.pop().unwrap_or(0);
568-
IntoOnes {
569-
bitset_front: first_block,
570-
bitset_back: last_block,
571-
block_idx_front: 0,
572-
block_idx_back: (1 + data.len()) * BITS,
573-
remaining_blocks: data.into_iter(),
574-
}
545+
let ptr = self.data.as_ptr().cast();
546+
let len = self.data.len() * SimdBlock::USIZE_COUNT;
547+
// SAFETY:
548+
// - ptr comes from self.data, so it is valid;
549+
// - self.data is valid for self.data.len() SimdBlocks,
550+
// which is exactly self.data.len() * SimdBlock::USIZE_COUNT usizes;
551+
// - we will keep this slice around only as long as self.data is,
552+
// so it won't become dangling.
553+
let slice = unsafe { core::slice::from_raw_parts(ptr, len) };
554+
let mut iter = slice.iter().copied();
555+
556+
IntoOnes {
557+
bitset_front: iter.next().unwrap_or(0),
558+
bitset_back: iter.next_back().unwrap_or(0),
559+
block_idx_front: 0,
560+
block_idx_back: len.saturating_sub(1) * BITS,
561+
remaining_blocks: iter,
562+
_buf: self.data,
575563
}
576564
}
577565

@@ -1151,7 +1139,9 @@ pub struct IntoOnes {
11511139
bitset_back: Block,
11521140
block_idx_front: usize,
11531141
block_idx_back: usize,
1154-
remaining_blocks: IntoIter<usize>,
1142+
remaining_blocks: core::iter::Copied<core::slice::Iter<'static, usize>>,
1143+
// Keep buf along so that `remaining_blocks` remains valid.
1144+
_buf: Vec<SimdBlock>,
11551145
}
11561146

11571147
impl IntoOnes {
@@ -1605,7 +1595,8 @@ mod tests {
16051595

16061596
#[test]
16071597
fn size_hint() {
1608-
for s in 0..1000 {
1598+
let iters = if cfg!(miri) { 250 } else { 1000 };
1599+
for s in 0..iters {
16091600
let mut bitset = FixedBitSet::with_capacity(s);
16101601
bitset.insert_range(..);
16111602
let mut t = s;
@@ -1627,7 +1618,8 @@ mod tests {
16271618

16281619
#[test]
16291620
fn size_hint_alternate() {
1630-
for s in 0..1000 {
1621+
let iters = if cfg!(miri) { 250 } else { 1000 };
1622+
for s in 0..iters {
16311623
let mut bitset = FixedBitSet::with_capacity(s);
16321624
bitset.insert_range(..);
16331625
let mut t = s;
@@ -1688,7 +1680,8 @@ mod tests {
16881680

16891681
#[test]
16901682
fn count_ones_panic() {
1691-
for i in 1..128 {
1683+
let iters = if cfg!(miri) { 48 } else { 128 };
1684+
for i in 1..iters {
16921685
let fb = FixedBitSet::with_capacity(i);
16931686
for j in 0..fb.len() + 1 {
16941687
for k in j..fb.len() + 1 {

0 commit comments

Comments
 (0)