make alignment check integer-based by default, and add an option to make it symbolic

This commit is contained in:
Ralf Jung 2020-08-16 17:08:34 +02:00
parent ca9e988bdb
commit cb985670c1
10 changed files with 62 additions and 53 deletions

@ -172,48 +172,41 @@ fn main() {
init_early_loggers();
// Parse our arguments and split them across `rustc` and `miri`.
let mut validate = true;
let mut stacked_borrows = true;
let mut check_alignment = true;
let mut communicate = false;
let mut ignore_leaks = false;
let mut seed: Option<u64> = None;
let mut tracked_pointer_tag: Option<miri::PtrId> = None;
let mut tracked_call_id: Option<miri::CallId> = None;
let mut tracked_alloc_id: Option<miri::AllocId> = None;
let mut miri_config = miri::MiriConfig::default();
let mut rustc_args = vec![];
let mut crate_args = vec![];
let mut after_dashdash = false;
let mut excluded_env_vars = vec![];
for arg in env::args() {
if rustc_args.is_empty() {
// Very first arg: binary name.
rustc_args.push(arg);
} else if after_dashdash {
// Everything that comes after `--` is forwarded to the interpreted crate.
crate_args.push(arg);
miri_config.args.push(arg);
} else {
match arg.as_str() {
"-Zmiri-disable-validation" => {
validate = false;
miri_config.validate = false;
}
"-Zmiri-disable-stacked-borrows" => {
stacked_borrows = false;
miri_config.stacked_borrows = false;
}
"-Zmiri-disable-alignment-check" => {
check_alignment = false;
miri_config.check_alignment = miri::AlignmentCheck::None;
}
"-Zmiri-symbolic-alignment-check" => {
miri_config.check_alignment = miri::AlignmentCheck::Symbolic;
}
"-Zmiri-disable-isolation" => {
communicate = true;
miri_config.communicate = true;
}
"-Zmiri-ignore-leaks" => {
ignore_leaks = true;
miri_config.ignore_leaks = true;
}
"--" => {
after_dashdash = true;
}
arg if arg.starts_with("-Zmiri-seed=") => {
if seed.is_some() {
if miri_config.seed.is_some() {
panic!("Cannot specify -Zmiri-seed multiple times!");
}
let seed_raw = hex::decode(arg.strip_prefix("-Zmiri-seed=").unwrap())
@ -234,10 +227,10 @@ fn main() {
let mut bytes = [0; 8];
bytes[..seed_raw.len()].copy_from_slice(&seed_raw);
seed = Some(u64::from_be_bytes(bytes));
miri_config.seed = Some(u64::from_be_bytes(bytes));
}
arg if arg.starts_with("-Zmiri-env-exclude=") => {
excluded_env_vars
miri_config.excluded_env_vars
.push(arg.strip_prefix("-Zmiri-env-exclude=").unwrap().to_owned());
}
arg if arg.starts_with("-Zmiri-track-pointer-tag=") => {
@ -249,7 +242,7 @@ fn main() {
),
};
if let Some(id) = miri::PtrId::new(id) {
tracked_pointer_tag = Some(id);
miri_config.tracked_pointer_tag = Some(id);
} else {
panic!("-Zmiri-track-pointer-tag requires a nonzero argument");
}
@ -263,7 +256,7 @@ fn main() {
),
};
if let Some(id) = miri::CallId::new(id) {
tracked_call_id = Some(id);
miri_config.tracked_call_id = Some(id);
} else {
panic!("-Zmiri-track-call-id requires a nonzero argument");
}
@ -276,7 +269,7 @@ fn main() {
err
),
};
tracked_alloc_id = Some(miri::AllocId(id));
miri_config.tracked_alloc_id = Some(miri::AllocId(id));
}
_ => {
// Forward to rustc.
@ -287,19 +280,6 @@ fn main() {
}
debug!("rustc arguments: {:?}", rustc_args);
debug!("crate arguments: {:?}", crate_args);
let miri_config = miri::MiriConfig {
validate,
stacked_borrows,
check_alignment,
communicate,
ignore_leaks,
excluded_env_vars,
seed,
args: crate_args,
tracked_pointer_tag,
tracked_call_id,
tracked_alloc_id,
};
debug!("crate arguments: {:?}", miri_config.args);
run_compiler(rustc_args, &mut MiriCompilerCalls { miri_config })
}

@ -13,6 +13,16 @@ use rustc_target::abi::LayoutOf;
use crate::*;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum AlignmentCheck {
/// Do not check alignment.
None,
/// Check alignment "symbolically", i.e., using only the requested alignment for an allocation and not its real base address.
Symbolic,
/// Check alignment on the actual physical integer address.
Int,
}
/// Configuration needed to spawn a Miri instance.
#[derive(Clone)]
pub struct MiriConfig {
@ -20,8 +30,8 @@ pub struct MiriConfig {
pub validate: bool,
/// Determines if Stacked Borrows is enabled.
pub stacked_borrows: bool,
/// Determines if alignment checking is enabled.
pub check_alignment: bool,
/// Controls alignment checking.
pub check_alignment: AlignmentCheck,
/// Determines if communication with the host environment is enabled.
pub communicate: bool,
/// Determines if memory leaks should be ignored.
@ -45,7 +55,7 @@ impl Default for MiriConfig {
MiriConfig {
validate: true,
stacked_borrows: true,
check_alignment: true,
check_alignment: AlignmentCheck::Int,
communicate: false,
ignore_leaks: false,
excluded_env_vars: vec![],

@ -55,7 +55,7 @@ pub use crate::diagnostics::{
register_diagnostic, report_error, EvalContextExt as DiagnosticsEvalContextExt,
TerminationInfo, NonHaltingDiagnostic,
};
pub use crate::eval::{create_ecx, eval_main, MiriConfig};
pub use crate::eval::{create_ecx, eval_main, AlignmentCheck, MiriConfig};
pub use crate::helpers::EvalContextExt as HelpersEvalContextExt;
pub use crate::machine::{
AllocExtra, Evaluator, FrameData, MemoryExtra, MiriEvalContext, MiriEvalContextExt,

@ -128,7 +128,7 @@ pub struct MemoryExtra {
tracked_alloc_id: Option<AllocId>,
/// Controls whether alignment of memory accesses is being checked.
check_alignment: bool,
check_alignment: AlignmentCheck,
}
impl MemoryExtra {
@ -138,7 +138,7 @@ impl MemoryExtra {
tracked_pointer_tag: Option<PtrId>,
tracked_call_id: Option<CallId>,
tracked_alloc_id: Option<AllocId>,
check_alignment: bool,
check_alignment: AlignmentCheck,
) -> Self {
let stacked_borrows = if stacked_borrows {
Some(Rc::new(RefCell::new(stacked_borrows::GlobalState::new(tracked_pointer_tag, tracked_call_id))))
@ -336,7 +336,12 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
#[inline(always)]
fn enforce_alignment(memory_extra: &MemoryExtra) -> bool {
memory_extra.check_alignment
memory_extra.check_alignment != AlignmentCheck::None
}
#[inline(always)]
fn force_int_for_alignment_check(memory_extra: &Self::MemoryExtra) -> bool {
memory_extra.check_alignment == AlignmentCheck::Int
}
#[inline(always)]

@ -1,3 +1,4 @@
// compile-flags: -Zmiri-symbolic-alignment-check
#![feature(core_intrinsics)]
fn main() {

@ -1,4 +1,4 @@
// should find the bug even without these, but gets masked by optimizations
// should find the bug even without validation and stacked borrows, but gets masked by optimizations
// compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows -Zmir-opt-level=0
#[repr(align(256))]
@ -15,5 +15,5 @@ fn main() {
// Overwrite the data part of `ptr` so it points to `buf`.
unsafe { (&mut ptr as *mut _ as *mut *const u8).write(&buf as *const _ as *const u8); }
// Re-borrow that. This should be UB.
let _ptr = &*ptr; //~ ERROR accessing memory with alignment 4, but alignment 256 is required
let _ptr = &*ptr; //~ ERROR alignment 256 is required
}

@ -1,7 +1,9 @@
// Even with intptrcast and without validation, we want to be *sure* to catch bugs
// that arise from pointers being insufficiently aligned. The only way to achieve
// that is not not let programs exploit integer information for alignment, so here
// we test that this is indeed the case.
// compile-flags: -Zmiri-symbolic-alignment-check
// With the symbolic alignment check, even with intptrcast and without
// validation, we want to be *sure* to catch bugs that arise from pointers being
// insufficiently aligned. The only way to achieve that is not not let programs
// exploit integer information for alignment, so here we test that this is
// indeed the case.
//
// See https://github.com/rust-lang/miri/issues/1074.
fn main() {

@ -15,5 +15,5 @@ fn main() {
y: 99,
};
let p = unsafe { &foo.x };
let i = *p; //~ ERROR memory with alignment 1, but alignment 4 is required
let i = *p; //~ ERROR alignment 4 is required
}

@ -2,9 +2,9 @@
// compile-flags: -Zmiri-disable-validation
fn main() {
let x = &2u16;
let x = &2u8;
let x = x as *const _ as *const [u32; 0];
// This must fail because alignment is violated. Test specifically for loading ZST.
let _x = unsafe { *x };
//~^ ERROR memory with alignment 2, but alignment 4 is required
//~^ ERROR alignment 4 is required
}

11
tests/run-pass/align.rs Normal file

@ -0,0 +1,11 @@
// This manually makes sure that we have a pointer with the proper alignment.
// Do this a couple times in a loop because it may work "by chance".
fn main() {
for _ in 0..10 {
let x = &mut [0u8; 3];
let base_addr = x as *mut _ as usize;
let base_addr_aligned = if base_addr % 2 == 0 { base_addr } else { base_addr+1 };
let u16_ptr = base_addr_aligned as *mut u16;
unsafe { *u16_ptr = 2; }
}
}