Skip to content

Commit 07244c2

Browse files
committed
Auto merge of #1513 - RalfJung:int-align, r=RalfJung
add option to use force_int for alignment check Fixes #1074. Depends on rust-lang/rust#75592.
2 parents ca9e988 + 33a8a1e commit 07244c2

22 files changed

+159
-112
lines changed

README.md

+14-5
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,8 @@ up the sysroot. If you are using `miri` (the Miri driver) directly, see the
165165

166166
Miri adds its own set of `-Z` flags:
167167

168-
* `-Zmiri-disable-alignment-check` disables checking pointer alignment. This is
169-
useful to avoid [false positives][alignment-false-positives]. However, setting
170-
this flag means Miri could miss bugs in your program.
168+
* `-Zmiri-disable-alignment-check` disables checking pointer alignment, so you
169+
can focus on other failures.
171170
* `-Zmiri-disable-stacked-borrows` disables checking the experimental
172171
[Stacked Borrows] aliasing rules. This can make Miri run faster, but it also
173172
means no aliasing violations will be detected.
@@ -189,6 +188,18 @@ Miri adds its own set of `-Z` flags:
189188
entropy. The default seed is 0. **NOTE**: This entropy is not good enough
190189
for cryptographic use! Do not generate secret keys in Miri or perform other
191190
kinds of cryptographic operations that rely on proper random numbers.
191+
* `-Zmiri-symbolic-alignment-check` makes the alignment check more strict. By
192+
default, alignment is checked by casting the pointer to an integer, and making
193+
sure that is a multiple of the alignment. This can lead to cases where a
194+
program passes the alignment check by pure chance, because things "happened to
195+
be" sufficiently aligned -- there is no UB in this execution but there would
196+
be UB in others. To avoid such cases, the symbolic alignment check only takes
197+
into account the requested alignment of the relevant allocation, and the
198+
offset into that allocation. This avoids missing such bugs, but it also
199+
incurs some false positives when the code does manual integer arithmetic to
200+
ensure alignment. (The standard library `align_to` method works fine in both
201+
modes; under symbolic alignment it only fills the middle slice when the
202+
allocation guarantees sufficient alignment.)
192203
* `-Zmiri-track-alloc-id=<id>` shows a backtrace when the given allocation is
193204
being allocated or freed. This helps in debugging memory leaks and
194205
use after free bugs.
@@ -200,8 +211,6 @@ Miri adds its own set of `-Z` flags:
200211
assigned to a stack frame. This helps in debugging UB related to Stacked
201212
Borrows "protectors".
202213

203-
[alignment-false-positives]: https://github.com/rust-lang/miri/issues/1074
204-
205214
Some native rustc `-Z` flags are also very relevant for Miri:
206215

207216
* `-Zmir-opt-level` controls how many MIR optimizations are performed. Miri

rust-version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
814bc4fe9364865bfaa94d7825b8eabc11245c7c
1+
8cdc94e84040ce797fd33d0a7cfda4ec4f2f2421

src/bin/miri.rs

+17-37
Original file line numberDiff line numberDiff line change
@@ -172,48 +172,41 @@ fn main() {
172172
init_early_loggers();
173173

174174
// Parse our arguments and split them across `rustc` and `miri`.
175-
let mut validate = true;
176-
let mut stacked_borrows = true;
177-
let mut check_alignment = true;
178-
let mut communicate = false;
179-
let mut ignore_leaks = false;
180-
let mut seed: Option<u64> = None;
181-
let mut tracked_pointer_tag: Option<miri::PtrId> = None;
182-
let mut tracked_call_id: Option<miri::CallId> = None;
183-
let mut tracked_alloc_id: Option<miri::AllocId> = None;
175+
let mut miri_config = miri::MiriConfig::default();
184176
let mut rustc_args = vec![];
185-
let mut crate_args = vec![];
186177
let mut after_dashdash = false;
187-
let mut excluded_env_vars = vec![];
188178
for arg in env::args() {
189179
if rustc_args.is_empty() {
190180
// Very first arg: binary name.
191181
rustc_args.push(arg);
192182
} else if after_dashdash {
193183
// Everything that comes after `--` is forwarded to the interpreted crate.
194-
crate_args.push(arg);
184+
miri_config.args.push(arg);
195185
} else {
196186
match arg.as_str() {
197187
"-Zmiri-disable-validation" => {
198-
validate = false;
188+
miri_config.validate = false;
199189
}
200190
"-Zmiri-disable-stacked-borrows" => {
201-
stacked_borrows = false;
191+
miri_config.stacked_borrows = false;
202192
}
203193
"-Zmiri-disable-alignment-check" => {
204-
check_alignment = false;
194+
miri_config.check_alignment = miri::AlignmentCheck::None;
195+
}
196+
"-Zmiri-symbolic-alignment-check" => {
197+
miri_config.check_alignment = miri::AlignmentCheck::Symbolic;
205198
}
206199
"-Zmiri-disable-isolation" => {
207-
communicate = true;
200+
miri_config.communicate = true;
208201
}
209202
"-Zmiri-ignore-leaks" => {
210-
ignore_leaks = true;
203+
miri_config.ignore_leaks = true;
211204
}
212205
"--" => {
213206
after_dashdash = true;
214207
}
215208
arg if arg.starts_with("-Zmiri-seed=") => {
216-
if seed.is_some() {
209+
if miri_config.seed.is_some() {
217210
panic!("Cannot specify -Zmiri-seed multiple times!");
218211
}
219212
let seed_raw = hex::decode(arg.strip_prefix("-Zmiri-seed=").unwrap())
@@ -234,10 +227,10 @@ fn main() {
234227

235228
let mut bytes = [0; 8];
236229
bytes[..seed_raw.len()].copy_from_slice(&seed_raw);
237-
seed = Some(u64::from_be_bytes(bytes));
230+
miri_config.seed = Some(u64::from_be_bytes(bytes));
238231
}
239232
arg if arg.starts_with("-Zmiri-env-exclude=") => {
240-
excluded_env_vars
233+
miri_config.excluded_env_vars
241234
.push(arg.strip_prefix("-Zmiri-env-exclude=").unwrap().to_owned());
242235
}
243236
arg if arg.starts_with("-Zmiri-track-pointer-tag=") => {
@@ -249,7 +242,7 @@ fn main() {
249242
),
250243
};
251244
if let Some(id) = miri::PtrId::new(id) {
252-
tracked_pointer_tag = Some(id);
245+
miri_config.tracked_pointer_tag = Some(id);
253246
} else {
254247
panic!("-Zmiri-track-pointer-tag requires a nonzero argument");
255248
}
@@ -263,7 +256,7 @@ fn main() {
263256
),
264257
};
265258
if let Some(id) = miri::CallId::new(id) {
266-
tracked_call_id = Some(id);
259+
miri_config.tracked_call_id = Some(id);
267260
} else {
268261
panic!("-Zmiri-track-call-id requires a nonzero argument");
269262
}
@@ -276,7 +269,7 @@ fn main() {
276269
err
277270
),
278271
};
279-
tracked_alloc_id = Some(miri::AllocId(id));
272+
miri_config.tracked_alloc_id = Some(miri::AllocId(id));
280273
}
281274
_ => {
282275
// Forward to rustc.
@@ -287,19 +280,6 @@ fn main() {
287280
}
288281

289282
debug!("rustc arguments: {:?}", rustc_args);
290-
debug!("crate arguments: {:?}", crate_args);
291-
let miri_config = miri::MiriConfig {
292-
validate,
293-
stacked_borrows,
294-
check_alignment,
295-
communicate,
296-
ignore_leaks,
297-
excluded_env_vars,
298-
seed,
299-
args: crate_args,
300-
tracked_pointer_tag,
301-
tracked_call_id,
302-
tracked_alloc_id,
303-
};
283+
debug!("crate arguments: {:?}", miri_config.args);
304284
run_compiler(rustc_args, &mut MiriCompilerCalls { miri_config })
305285
}

src/diagnostics.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,12 @@ pub fn report_error<'tcx, 'mir>(
100100
panic!("Error should never be raised by Miri: {:?}", e.kind),
101101
Unsupported(_) =>
102102
vec![format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support")],
103-
UndefinedBehavior(UndefinedBehaviorInfo::AlignmentCheckFailed { .. }) =>
103+
UndefinedBehavior(UndefinedBehaviorInfo::AlignmentCheckFailed { .. })
104+
if ecx.memory.extra.check_alignment == AlignmentCheck::Symbolic
105+
=>
104106
vec![
105107
format!("this usually indicates that your program performed an invalid operation and caused Undefined Behavior"),
106-
format!("but alignment errors can also be false positives, see https://github.com/rust-lang/miri/issues/1074"),
107-
format!("you can disable the alignment check with `-Zmiri-disable-alignment-check`, but that could hide true bugs")
108+
format!("but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives"),
108109
],
109110
UndefinedBehavior(_) =>
110111
vec![

src/eval.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,25 @@ use rustc_target::abi::LayoutOf;
1313

1414
use crate::*;
1515

16+
#[derive(Copy, Clone, Debug, PartialEq)]
17+
pub enum AlignmentCheck {
18+
/// Do not check alignment.
19+
None,
20+
/// Check alignment "symbolically", i.e., using only the requested alignment for an allocation and not its real base address.
21+
Symbolic,
22+
/// Check alignment on the actual physical integer address.
23+
Int,
24+
}
25+
1626
/// Configuration needed to spawn a Miri instance.
1727
#[derive(Clone)]
1828
pub struct MiriConfig {
1929
/// Determine if validity checking is enabled.
2030
pub validate: bool,
2131
/// Determines if Stacked Borrows is enabled.
2232
pub stacked_borrows: bool,
23-
/// Determines if alignment checking is enabled.
24-
pub check_alignment: bool,
33+
/// Controls alignment checking.
34+
pub check_alignment: AlignmentCheck,
2535
/// Determines if communication with the host environment is enabled.
2636
pub communicate: bool,
2737
/// Determines if memory leaks should be ignored.
@@ -45,7 +55,7 @@ impl Default for MiriConfig {
4555
MiriConfig {
4656
validate: true,
4757
stacked_borrows: true,
48-
check_alignment: true,
58+
check_alignment: AlignmentCheck::Int,
4959
communicate: false,
5060
ignore_leaks: false,
5161
excluded_env_vars: vec![],

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ pub use crate::diagnostics::{
5555
register_diagnostic, report_error, EvalContextExt as DiagnosticsEvalContextExt,
5656
TerminationInfo, NonHaltingDiagnostic,
5757
};
58-
pub use crate::eval::{create_ecx, eval_main, MiriConfig};
58+
pub use crate::eval::{create_ecx, eval_main, AlignmentCheck, MiriConfig};
5959
pub use crate::helpers::EvalContextExt as HelpersEvalContextExt;
6060
pub use crate::machine::{
6161
AllocExtra, Evaluator, FrameData, MemoryExtra, MiriEvalContext, MiriEvalContextExt,

src/machine.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ pub struct MemoryExtra {
128128
tracked_alloc_id: Option<AllocId>,
129129

130130
/// Controls whether alignment of memory accesses is being checked.
131-
check_alignment: bool,
131+
pub(crate) check_alignment: AlignmentCheck,
132132
}
133133

134134
impl MemoryExtra {
@@ -138,7 +138,7 @@ impl MemoryExtra {
138138
tracked_pointer_tag: Option<PtrId>,
139139
tracked_call_id: Option<CallId>,
140140
tracked_alloc_id: Option<AllocId>,
141-
check_alignment: bool,
141+
check_alignment: AlignmentCheck,
142142
) -> Self {
143143
let stacked_borrows = if stacked_borrows {
144144
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> {
336336

337337
#[inline(always)]
338338
fn enforce_alignment(memory_extra: &MemoryExtra) -> bool {
339-
memory_extra.check_alignment
339+
memory_extra.check_alignment != AlignmentCheck::None
340+
}
341+
342+
#[inline(always)]
343+
fn force_int_for_alignment_check(memory_extra: &Self::MemoryExtra) -> bool {
344+
memory_extra.check_alignment == AlignmentCheck::Int
340345
}
341346

342347
#[inline(always)]

src/shims/mod.rs

+18-17
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ pub mod tls;
1313

1414
// End module management, begin local code
1515

16-
use std::convert::TryFrom;
17-
1816
use log::trace;
1917

2018
use rustc_middle::{mir, ty};
@@ -37,8 +35,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
3735
// There are some more lang items we want to hook that CTFE does not hook (yet).
3836
if this.tcx.lang_items().align_offset_fn() == Some(instance.def.def_id()) {
3937
let &[ptr, align] = check_arg_count(args)?;
40-
this.align_offset(ptr, align, ret, unwind)?;
41-
return Ok(None);
38+
if this.align_offset(ptr, align, ret, unwind)? {
39+
return Ok(None);
40+
}
4241
}
4342

4443
// Try to see if we can do something about foreign items.
@@ -56,46 +55,48 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
5655
Ok(Some(&*this.load_mir(instance.def, None)?))
5756
}
5857

58+
/// Returns `true` if the computation was performed, and `false` if we should just evaluate
59+
/// the actual MIR of `align_offset`.
5960
fn align_offset(
6061
&mut self,
6162
ptr_op: OpTy<'tcx, Tag>,
6263
align_op: OpTy<'tcx, Tag>,
6364
ret: Option<(PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
6465
unwind: Option<mir::BasicBlock>,
65-
) -> InterpResult<'tcx> {
66+
) -> InterpResult<'tcx, bool> {
6667
let this = self.eval_context_mut();
6768
let (dest, ret) = ret.unwrap();
6869

70+
if this.memory.extra.check_alignment != AlignmentCheck::Symbolic {
71+
// Just use actual implementation.
72+
return Ok(false);
73+
}
74+
6975
let req_align = this
7076
.force_bits(this.read_scalar(align_op)?.check_init()?, this.pointer_size())?;
7177

7278
// Stop if the alignment is not a power of two.
7379
if !req_align.is_power_of_two() {
74-
return this.start_panic("align_offset: align is not a power-of-two", unwind);
80+
this.start_panic("align_offset: align is not a power-of-two", unwind)?;
81+
return Ok(true); // nothing left to do
7582
}
7683

7784
let ptr_scalar = this.read_scalar(ptr_op)?.check_init()?;
7885

79-
// Default: no result.
80-
let mut result = this.machine_usize_max();
8186
if let Ok(ptr) = this.force_ptr(ptr_scalar) {
8287
// Only do anything if we can identify the allocation this goes to.
8388
let cur_align =
8489
this.memory.get_size_and_align(ptr.alloc_id, AllocCheck::MaybeDead)?.1.bytes();
8590
if u128::from(cur_align) >= req_align {
8691
// If the allocation alignment is at least the required alignment we use the
87-
// libcore implementation.
88-
// FIXME: is this correct in case of truncation?
89-
result = u64::try_from(
90-
(this.force_bits(ptr_scalar, this.pointer_size())? as *const i8)
91-
.align_offset(usize::try_from(req_align).unwrap())
92-
).unwrap();
92+
// real implementation.
93+
return Ok(false);
9394
}
9495
}
9596

96-
// Return result, and jump to caller.
97-
this.write_scalar(Scalar::from_machine_usize(result, this), dest)?;
97+
// Return error result (usize::MAX), and jump to caller.
98+
this.write_scalar(Scalar::from_machine_usize(this.machine_usize_max(), this), dest)?;
9899
this.go_to_block(ret);
99-
Ok(())
100+
Ok(true)
100101
}
101102
}

tests/compile-fail/unaligned_pointers/atomic_unaligned.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// compile-flags: -Zmiri-symbolic-alignment-check
12
#![feature(core_intrinsics)]
23

34
fn main() {

tests/compile-fail/unaligned_pointers/dyn_alignment.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// should find the bug even without these, but gets masked by optimizations
1+
// should find the bug even without validation and stacked borrows, but gets masked by optimizations
22
// compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows -Zmir-opt-level=0
33

44
#[repr(align(256))]
@@ -15,5 +15,5 @@ fn main() {
1515
// Overwrite the data part of `ptr` so it points to `buf`.
1616
unsafe { (&mut ptr as *mut _ as *mut *const u8).write(&buf as *const _ as *const u8); }
1717
// Re-borrow that. This should be UB.
18-
let _ptr = &*ptr; //~ ERROR accessing memory with alignment 4, but alignment 256 is required
18+
let _ptr = &*ptr; //~ ERROR alignment 256 is required
1919
}

tests/compile-fail/unaligned_pointers/intptrcast_alignment_check.rs

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

tests/compile-fail/unaligned_pointers/reference_to_packed.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ fn main() {
1515
y: 99,
1616
};
1717
let p = unsafe { &foo.x };
18-
let i = *p; //~ ERROR memory with alignment 1, but alignment 4 is required
18+
let i = *p; //~ ERROR alignment 4 is required
1919
}

tests/compile-fail/unaligned_pointers/unaligned_ptr1.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
// compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows
33

44
fn main() {
5-
let x = [2u16, 3, 4]; // Make it big enough so we don't get an out-of-bounds error.
6-
let x = &x[0] as *const _ as *const u32;
7-
// This must fail because alignment is violated: the allocation's base is not sufficiently aligned.
8-
let _x = unsafe { *x }; //~ ERROR memory with alignment 2, but alignment 4 is required
5+
for _ in 0..10 { // Try many times as this might work by chance.
6+
let x = [2u16, 3, 4]; // Make it big enough so we don't get an out-of-bounds error.
7+
let x = &x[0] as *const _ as *const u32;
8+
// This must fail because alignment is violated: the allocation's base is not sufficiently aligned.
9+
let _x = unsafe { *x }; //~ ERROR memory with alignment 2, but alignment 4 is required
10+
}
911
}

0 commit comments

Comments
 (0)