diff --git a/tests/compile-fail/stacked_borrows/alias_through_mutation.rs b/tests/compile-fail/stacked_borrows/alias_through_mutation.rs new file mode 100644 index 000000000000..9fa50da45bd0 --- /dev/null +++ b/tests/compile-fail/stacked_borrows/alias_through_mutation.rs @@ -0,0 +1,15 @@ +#![allow(unused_variables)] + +// This makes a ref that was passed to us via &mut alias with things it should not alias with +fn retarget(x: &mut &u32, target: &mut u32) { + unsafe { *x = &mut *(target as *mut _); } +} + +fn main() { + let target = &mut 42; + let mut target_alias = &42; // initial dummy value + retarget(&mut target_alias, target); + // now `target_alias` points to the same thing as `target` + *target = 13; + let _val = *target_alias; //~ ERROR should be frozen +} diff --git a/tests/compile-fail/validation_aliasing_mut1.rs b/tests/compile-fail/stacked_borrows/aliasing_mut1.rs similarity index 100% rename from tests/compile-fail/validation_aliasing_mut1.rs rename to tests/compile-fail/stacked_borrows/aliasing_mut1.rs diff --git a/tests/compile-fail/validation_aliasing_mut2.rs b/tests/compile-fail/stacked_borrows/aliasing_mut2.rs similarity index 100% rename from tests/compile-fail/validation_aliasing_mut2.rs rename to tests/compile-fail/stacked_borrows/aliasing_mut2.rs diff --git a/tests/compile-fail/validation_aliasing_mut3.rs b/tests/compile-fail/stacked_borrows/aliasing_mut3.rs similarity index 100% rename from tests/compile-fail/validation_aliasing_mut3.rs rename to tests/compile-fail/stacked_borrows/aliasing_mut3.rs diff --git a/tests/compile-fail/validation_aliasing_mut4.rs b/tests/compile-fail/stacked_borrows/aliasing_mut4.rs similarity index 100% rename from tests/compile-fail/validation_aliasing_mut4.rs rename to tests/compile-fail/stacked_borrows/aliasing_mut4.rs diff --git a/tests/compile-fail/validation_buggy_as_mut_slice.rs b/tests/compile-fail/stacked_borrows/buggy_as_mut_slice.rs similarity index 52% rename from tests/compile-fail/validation_buggy_as_mut_slice.rs rename to tests/compile-fail/stacked_borrows/buggy_as_mut_slice.rs index 282e536ce9b7..3345668cee00 100644 --- a/tests/compile-fail/validation_buggy_as_mut_slice.rs +++ b/tests/compile-fail/stacked_borrows/buggy_as_mut_slice.rs @@ -1,22 +1,17 @@ -// ignore-test validation_op is disabled - #![allow(unused_variables)] -// For some reason, the error location is different when using fullmir -// error-pattern: in conflict with lock WriteLock - mod safe { use std::slice::from_raw_parts_mut; pub fn as_mut_slice(self_: &Vec) -> &mut [T] { unsafe { - from_raw_parts_mut(self_.as_ptr() as *mut T, self_.len()) + from_raw_parts_mut(self_.as_ptr() as *mut T, self_.len()) //~ ERROR shared borrow for mutation } } } fn main() { let v = vec![0,1,2]; - let v1_ = safe::as_mut_slice(&v); - let v2_ = safe::as_mut_slice(&v); + let v1 = safe::as_mut_slice(&v); + let v2 = safe::as_mut_slice(&v); } diff --git a/tests/compile-fail/validation_buggy_split_at_mut.rs b/tests/compile-fail/stacked_borrows/buggy_split_at_mut.rs similarity index 79% rename from tests/compile-fail/validation_buggy_split_at_mut.rs rename to tests/compile-fail/stacked_borrows/buggy_split_at_mut.rs index a750f1466f51..d7f4300f82c0 100644 --- a/tests/compile-fail/validation_buggy_split_at_mut.rs +++ b/tests/compile-fail/stacked_borrows/buggy_split_at_mut.rs @@ -1,5 +1,3 @@ -// ignore-test validation_op is disabled - #![allow(unused_variables)] mod safe { @@ -20,5 +18,7 @@ mod safe { fn main() { let mut array = [1,2,3,4]; - let _x = safe::split_at_mut(&mut array, 0); //~ ERROR: in conflict with lock WriteLock + let (a, b) = safe::split_at_mut(&mut array, 0); + a[1] = 5; //~ ERROR does not exist on the stack + b[1] = 6; } diff --git a/tests/compile-fail/stacked_borrows/illegal_write.rs b/tests/compile-fail/stacked_borrows/illegal_write.rs new file mode 100644 index 000000000000..6a7ccc84012c --- /dev/null +++ b/tests/compile-fail/stacked_borrows/illegal_write.rs @@ -0,0 +1,11 @@ +fn evil(x: &u32) { + let x : &mut u32 = unsafe { &mut *(x as *const _ as *mut _) }; + *x = 42; // mutating shared ref without `UnsafeCell` +} + +fn main() { + let target = 42; + let ref_ = ⌖ + evil(ref_); // invalidates shared ref + let _x = *ref_; //~ ERROR should be frozen +} diff --git a/tests/compile-fail/stacked_borrows/illegal_write2.rs b/tests/compile-fail/stacked_borrows/illegal_write2.rs new file mode 100644 index 000000000000..1d61b1b98896 --- /dev/null +++ b/tests/compile-fail/stacked_borrows/illegal_write2.rs @@ -0,0 +1,10 @@ +#![allow(unused_variables)] + +fn main() { + let target = &mut 42; + let target2 = target as *mut _; + drop(&mut *target); // reborrow + // Now make sure our ref is still the only one + unsafe { *target2 = 13; } // invalidate our ref + let _val = *target; //~ ERROR does not exist on the stack +} diff --git a/tests/compile-fail/stacked_borrows/pointer_smuggling.rs b/tests/compile-fail/stacked_borrows/pointer_smuggling.rs new file mode 100644 index 000000000000..3576aa52b753 --- /dev/null +++ b/tests/compile-fail/stacked_borrows/pointer_smuggling.rs @@ -0,0 +1,22 @@ +#![allow(unused_variables)] + +static mut PTR: *mut u8 = 0 as *mut _; + +fn fun1(x: &mut u8) { + unsafe { + PTR = x; + } +} + +fn fun2() { + // Now we use a pointer we are not allowed to use + let _x = unsafe { *PTR }; +} + +fn main() { + let val = &mut 0; // FIXME: This should also work with a local variable, but currently it does not. + fun1(val); + *val = 2; // this invalidates any raw ptrs `fun1` might have created. + fun2(); // if they now use a raw ptr they break our reference + *val = 3; //~ ERROR does not exist on the stack +} diff --git a/tests/compile-fail/stacked_borrows/shared_confusion.rs b/tests/compile-fail/stacked_borrows/shared_confusion.rs new file mode 100644 index 000000000000..584053f59323 --- /dev/null +++ b/tests/compile-fail/stacked_borrows/shared_confusion.rs @@ -0,0 +1,21 @@ +#![allow(unused_variables)] +use std::cell::RefCell; + +fn test(r: &mut RefCell) { + let x = &*r; // not freezing because interior mutability + let mut x_ref = x.borrow_mut(); + let x_inner : &mut i32 = &mut *x_ref; // Uniq reference + let x_evil = x_inner as *mut _; + { + let x_inner_shr = &*x_inner; // frozen + let y = &*r; // outer ref, not freezing + let x_inner_shr2 = &*x_inner; // freezing again + } + // Our old raw should be dead by now + unsafe { *x_evil = 0; } // this falls back to some Raw higher up the stack + *x_inner = 12; //~ ERROR does not exist on the stack +} + +fn main() { + test(&mut RefCell::new(0)); +} diff --git a/tests/compile-fail/validation_illegal_write.rs b/tests/compile-fail/validation_illegal_write.rs deleted file mode 100644 index cb3e4b3c1a20..000000000000 --- a/tests/compile-fail/validation_illegal_write.rs +++ /dev/null @@ -1,17 +0,0 @@ -// ignore-test validation_op is disabled - -#![allow(unused_variables)] - -mod safe { - pub(crate) fn safe(x: &u32) { - let x : &mut u32 = unsafe { &mut *(x as *const _ as *mut _) }; - *x = 42; //~ ERROR: in conflict with lock ReadLock - } -} - -fn main() { - let target = &mut 42; - let target_ref = ⌖ - // do a reborrow, but we keep the lock - safe::safe(&*target); -} diff --git a/tests/compile-fail/validation_lock_confusion.rs b/tests/compile-fail/validation_lock_confusion.rs deleted file mode 100644 index 2a0857659622..000000000000 --- a/tests/compile-fail/validation_lock_confusion.rs +++ /dev/null @@ -1,26 +0,0 @@ -// ignore-test validation_op is disabled - -// Make sure validation can handle many overlapping shared borrows for different parts of a data structure -#![allow(unused_variables)] -use std::cell::RefCell; - -fn evil(x: *mut i32) { - unsafe { *x = 0; } //~ ERROR: in conflict with lock WriteLock -} - -fn test(r: &mut RefCell) { - let x = &*r; // releasing write lock, first suspension recorded - let mut x_ref = x.borrow_mut(); - let x_inner : &mut i32 = &mut *x_ref; // new inner write lock, with same lifetime as outer lock - { - let x_inner_shr = &*x_inner; // releasing inner write lock, recording suspension - let y = &*r; // second suspension for the outer write lock - let x_inner_shr2 = &*x_inner; // 2nd suspension for inner write lock - } - // If the two locks are mixed up, here we should have a write lock, but we do not. - evil(x_inner as *mut _); -} - -fn main() { - test(&mut RefCell::new(0)); -} diff --git a/tests/compile-fail/validation_pointer_smuggling.rs b/tests/compile-fail/validation_pointer_smuggling.rs deleted file mode 100644 index 14d624286038..000000000000 --- a/tests/compile-fail/validation_pointer_smuggling.rs +++ /dev/null @@ -1,22 +0,0 @@ -// ignore-test validation_op is disabled - -#![allow(unused_variables)] - -static mut PTR: *mut u8 = 0 as *mut _; - -fn fun1(x: &mut u8) { - unsafe { - PTR = x; - } -} - -fn fun2() { - // Now we use a pointer we are not allowed to use - let _x = unsafe { *PTR }; //~ ERROR: in conflict with lock WriteLock -} - -fn main() { - let mut val = 0; - fun1(&mut val); - fun2(); -} diff --git a/tests/compile-fail/validation_recover1.rs b/tests/compile-fail/validation_recover1.rs deleted file mode 100644 index 9061070ef67e..000000000000 --- a/tests/compile-fail/validation_recover1.rs +++ /dev/null @@ -1,18 +0,0 @@ -// ignore-test validation_op is disabled - -#![allow(unused_variables)] - -#[repr(u32)] -enum Bool { True } - -mod safe { - pub(crate) fn safe(x: &mut super::Bool) { - let x = x as *mut _ as *mut u32; - unsafe { *x = 44; } // out-of-bounds enum discriminant - } -} - -fn main() { - let mut x = Bool::True; - safe::safe(&mut x); //~ ERROR: invalid enum discriminant -} diff --git a/tests/compile-fail/validation_recover2.rs b/tests/compile-fail/validation_recover2.rs deleted file mode 100644 index 7a4a417ab1db..000000000000 --- a/tests/compile-fail/validation_recover2.rs +++ /dev/null @@ -1,16 +0,0 @@ -// ignore-test validation_op is disabled - -#![allow(unused_variables)] - -mod safe { - // This makes a ref that was passed to us via &mut alias with things it should not alias with - pub(crate) fn safe(x: &mut &u32, target: &mut u32) { - unsafe { *x = &mut *(target as *mut _); } - } -} - -fn main() { - let target = &mut 42; - let mut target_alias = &42; // initial dummy value - safe::safe(&mut target_alias, target); //~ ERROR: in conflict with lock ReadLock -} diff --git a/tests/compile-fail/validation_recover3.rs b/tests/compile-fail/validation_recover3.rs deleted file mode 100644 index 5cfc8aaa66b5..000000000000 --- a/tests/compile-fail/validation_recover3.rs +++ /dev/null @@ -1,17 +0,0 @@ -// ignore-test validation_op is disabled - -#![allow(unused_variables)] - -mod safe { - pub(crate) fn safe(x: *mut u32) { - unsafe { *x = 42; } //~ ERROR: in conflict with lock WriteLock - } -} - -fn main() { - let target = &mut 42u32; - let target2 = target as *mut _; - drop(&mut *target); // reborrow - // Now make sure we still got the lock - safe::safe(target2); -} diff --git a/tests/compile-fail/validation_undef.rs b/tests/compile-fail/validation_undef.rs deleted file mode 100644 index 939e93a264e8..000000000000 --- a/tests/compile-fail/validation_undef.rs +++ /dev/null @@ -1,16 +0,0 @@ -// ignore-test validation_op is disabled - -#![allow(unused_variables)] -// error-pattern: attempted to read undefined bytes - -mod safe { - use std::mem; - - pub(crate) fn make_float() -> f32 { - unsafe { mem::uninitialized() } - } -} - -fn main() { - let _x = safe::make_float(); -} diff --git a/tests/compile-fail/validation_cast_fn_ptr1.rs b/tests/compile-fail/validity/cast_fn_ptr1.rs similarity index 100% rename from tests/compile-fail/validation_cast_fn_ptr1.rs rename to tests/compile-fail/validity/cast_fn_ptr1.rs diff --git a/tests/compile-fail/validation_cast_fn_ptr2.rs b/tests/compile-fail/validity/cast_fn_ptr2.rs similarity index 100% rename from tests/compile-fail/validation_cast_fn_ptr2.rs rename to tests/compile-fail/validity/cast_fn_ptr2.rs diff --git a/tests/compile-fail/invalid_bool.rs b/tests/compile-fail/validity/invalid_bool.rs similarity index 100% rename from tests/compile-fail/invalid_bool.rs rename to tests/compile-fail/validity/invalid_bool.rs diff --git a/tests/compile-fail/invalid_char.rs b/tests/compile-fail/validity/invalid_char.rs similarity index 100% rename from tests/compile-fail/invalid_char.rs rename to tests/compile-fail/validity/invalid_char.rs diff --git a/tests/compile-fail/invalid_enum_discriminant.rs b/tests/compile-fail/validity/invalid_enum_discriminant.rs similarity index 100% rename from tests/compile-fail/invalid_enum_discriminant.rs rename to tests/compile-fail/validity/invalid_enum_discriminant.rs diff --git a/tests/compile-fail/validity/transmute_through_ptr.rs b/tests/compile-fail/validity/transmute_through_ptr.rs new file mode 100644 index 000000000000..0a4c64bb9bb1 --- /dev/null +++ b/tests/compile-fail/validity/transmute_through_ptr.rs @@ -0,0 +1,16 @@ +#![allow(unused_variables)] + +#[repr(u32)] +enum Bool { True } + +fn evil(x: &mut Bool) { + let x = x as *mut _ as *mut u32; + unsafe { *x = 44; } // out-of-bounds enum discriminant +} + +fn main() { + let mut x = Bool::True; + evil(&mut x); + let _y = x; // reading this ought to be enough to trigger validation + //~^ ERROR invalid enum discriminant 44 +} diff --git a/tests/compile-fail/validity/undef.rs b/tests/compile-fail/validity/undef.rs new file mode 100644 index 000000000000..58d3926dadaf --- /dev/null +++ b/tests/compile-fail/validity/undef.rs @@ -0,0 +1,12 @@ +#![allow(unused_variables)] +// error-pattern: encountered undefined data in pointer + +use std::mem; + +fn make_raw() -> *const f32 { + unsafe { mem::uninitialized() } +} + +fn main() { + let _x = make_raw(); +} diff --git a/tests/compiletest.rs b/tests/compiletest.rs index b240fb31d222..7bec3fa8de2c 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -63,6 +63,7 @@ fn compile_fail(sysroot: &Path, path: &str, target: &str, host: &str, need_fullm flags.push(format!("--sysroot {}", sysroot.display())); flags.push("-Dwarnings -Dunused".to_owned()); // overwrite the -Aunused in compiletest-rs config.src_base = PathBuf::from(path.to_string()); + flags.push("-Zmir-opt-level=0".to_owned()); // optimization circumvents some stacked borrow checks flags.push("-Zmir-emit-validate=1".to_owned()); config.target_rustcflags = Some(flags.join(" ")); config.target = target.to_owned();