-
Notifications
You must be signed in to change notification settings - Fork 353
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #122240 - RalfJung:miri-addr-reuse, r=oli-obk
miri: add some chance to reuse addresses of previously freed allocations The hope is that this can help us find ABA issues. Unfortunately this needs rustc changes so I can't easily run the regular benchmark suite. I used `src/tools/miri/tests/pass/float_nan.rs` as a substitute: ``` Before: Benchmark 1: ./x.py run miri --stage 0 --args src/tools/miri/tests/pass/float_nan.rs --args --edition=2021 Time (mean ± σ): 9.570 s ± 0.013 s [User: 9.279 s, System: 0.290 s] Range (min … max): 9.561 s … 9.579 s 2 runs After: Benchmark 1: ./x.py run miri --stage 0 --args src/tools/miri/tests/pass/float_nan.rs --args --edition=2021 Time (mean ± σ): 9.698 s ± 0.046 s [User: 9.413 s, System: 0.279 s] Range (min … max): 9.666 s … 9.731 s 2 runs ``` That's a ~1.3% slowdown, which seems fine to me. I have seen a lot of noise in this style of benchmarking so I don't quite trust this anyway; we can make further experiments in the Miri repo after this migrated there. r? `@oli-obk`
- Loading branch information
Showing
12 changed files
with
210 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
//! Manages a pool of addresses that can be reused. | ||
use rand::Rng; | ||
|
||
use rustc_target::abi::{Align, Size}; | ||
|
||
const MAX_POOL_SIZE: usize = 64; | ||
|
||
// Just use fair coins, until we have evidence that other numbers are better. | ||
const ADDR_REMEMBER_CHANCE: f64 = 0.5; | ||
const ADDR_TAKE_CHANCE: f64 = 0.5; | ||
|
||
/// The pool strikes a balance between exploring more possible executions and making it more likely | ||
/// to find bugs. The hypothesis is that bugs are more likely to occur when reuse happens for | ||
/// allocations with the same layout, since that can trigger e.g. ABA issues in a concurrent data | ||
/// structure. Therefore we only reuse allocations when size and alignment match exactly. | ||
#[derive(Debug)] | ||
pub struct ReusePool { | ||
/// The i-th element in `pool` stores allocations of alignment `2^i`. We store these reusable | ||
/// allocations as address-size pairs, the list must be sorted by the size. | ||
/// | ||
/// Each of these maps has at most MAX_POOL_SIZE elements, and since alignment is limited to | ||
/// less than 64 different possible value, that bounds the overall size of the pool. | ||
pool: Vec<Vec<(u64, Size)>>, | ||
} | ||
|
||
impl ReusePool { | ||
pub fn new() -> Self { | ||
ReusePool { pool: vec![] } | ||
} | ||
|
||
fn subpool(&mut self, align: Align) -> &mut Vec<(u64, Size)> { | ||
let pool_idx: usize = align.bytes().trailing_zeros().try_into().unwrap(); | ||
if self.pool.len() <= pool_idx { | ||
self.pool.resize(pool_idx + 1, Vec::new()); | ||
} | ||
&mut self.pool[pool_idx] | ||
} | ||
|
||
pub fn add_addr(&mut self, rng: &mut impl Rng, addr: u64, size: Size, align: Align) { | ||
// Let's see if we even want to remember this address. | ||
if !rng.gen_bool(ADDR_REMEMBER_CHANCE) { | ||
return; | ||
} | ||
// Determine the pool to add this to, and where in the pool to put it. | ||
let subpool = self.subpool(align); | ||
let pos = subpool.partition_point(|(_addr, other_size)| *other_size < size); | ||
// Make sure the pool does not grow too big. | ||
if subpool.len() >= MAX_POOL_SIZE { | ||
// Pool full. Replace existing element, or last one if this would be even bigger. | ||
let clamped_pos = pos.min(subpool.len() - 1); | ||
subpool[clamped_pos] = (addr, size); | ||
return; | ||
} | ||
// Add address to pool, at the right position. | ||
subpool.insert(pos, (addr, size)); | ||
} | ||
|
||
pub fn take_addr(&mut self, rng: &mut impl Rng, size: Size, align: Align) -> Option<u64> { | ||
// Determine whether we'll even attempt a reuse. | ||
if !rng.gen_bool(ADDR_TAKE_CHANCE) { | ||
return None; | ||
} | ||
// Determine the pool to take this from. | ||
let subpool = self.subpool(align); | ||
// Let's see if we can find something of the right size. We want to find the full range of | ||
// such items, beginning with the first, so we can't use `binary_search_by_key`. | ||
let begin = subpool.partition_point(|(_addr, other_size)| *other_size < size); | ||
let mut end = begin; | ||
while let Some((_addr, other_size)) = subpool.get(end) { | ||
if *other_size != size { | ||
break; | ||
} | ||
end += 1; | ||
} | ||
if end == begin { | ||
// Could not find any item of the right size. | ||
return None; | ||
} | ||
// Pick a random element with the desired size. | ||
let idx = rng.gen_range(begin..end); | ||
// Remove it from the pool and return. | ||
let (chosen_addr, chosen_size) = subpool.remove(idx); | ||
debug_assert!(chosen_size >= size && chosen_addr % align.bytes() == 0); | ||
Some(chosen_addr) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.