diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index b5e165c75170a..195d45c2c4c49 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -554,6 +554,21 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { visit::walk_expr(self, &self.thir[arg]); return; } + + // Secondly, we allow raw borrows of union field accesses. Peel + // any of those off, and recurse normally on the LHS, which should + // reject any unsafe operations within. + let mut peeled = arg; + while let ExprKind::Scope { value: arg, .. } = self.thir[peeled].kind + && let ExprKind::Field { lhs, name: _, variant_index: _ } = self.thir[arg].kind + && let ty::Adt(def, _) = &self.thir[lhs].ty.kind() + && def.is_union() + { + peeled = lhs; + } + visit::walk_expr(self, &self.thir[peeled]); + // And return so we don't recurse directly onto the union field access(es). + return; } ExprKind::Deref { arg } => { if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) = diff --git a/src/tools/miri/tests/pass/both_borrows/smallvec.rs b/src/tools/miri/tests/pass/both_borrows/smallvec.rs index f48815e37be34..fa5cfb03de2a9 100644 --- a/src/tools/miri/tests/pass/both_borrows/smallvec.rs +++ b/src/tools/miri/tests/pass/both_borrows/smallvec.rs @@ -25,7 +25,7 @@ impl RawSmallVec { } const fn as_mut_ptr_inline(&mut self) -> *mut T { - (unsafe { &raw mut self.inline }) as *mut T + &raw mut self.inline as *mut T } const unsafe fn as_mut_ptr_heap(&mut self) -> *mut T { diff --git a/tests/ui/union/union-unsafe.rs b/tests/ui/union/union-unsafe.rs index bd3946686be36..beb074f4e8ebc 100644 --- a/tests/ui/union/union-unsafe.rs +++ b/tests/ui/union/union-unsafe.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; use std::mem::ManuallyDrop; +use std::ops::Deref; union U1 { a: u8, @@ -17,6 +18,10 @@ union U4 { a: T, } +union U5 { + a: usize, +} + union URef { p: &'static mut i32, } @@ -31,6 +36,20 @@ fn deref_union_field(mut u: URef) { *(u.p) = 13; //~ ERROR access to union field is unsafe } +union A { + a: usize, + b: &'static &'static B, +} + +union B { + c: usize, +} + +fn raw_deref_union_field(mut u: URef) { + // This is unsafe because we first dereference u.p (reading uninitialized memory) + let _p = &raw const *(u.p); //~ ERROR access to union field is unsafe +} + fn assign_noncopy_union_field(mut u: URefCell) { u.a = (ManuallyDrop::new(RefCell::new(0)), 1); // OK (assignment does not drop) u.a.0 = ManuallyDrop::new(RefCell::new(0)); // OK (assignment does not drop) @@ -57,6 +76,20 @@ fn main() { let a = u1.a; //~ ERROR access to union field is unsafe u1.a = 11; // OK + let mut u2 = U1 { a: 10 }; + let a = &raw mut u2.a; // OK + unsafe { *a = 3 }; + + let mut u3 = U1 { a: 10 }; + let a = std::ptr::addr_of_mut!(u3.a); // OK + unsafe { *a = 14 }; + + let u4 = U5 { a: 2 }; + let vec = vec![1, 2, 3]; + // This is unsafe because we read u4.a (potentially uninitialized memory) + // to use as an array index + let _a = &raw const vec[u4.a]; //~ ERROR access to union field is unsafe + let U1 { a } = u1; //~ ERROR access to union field is unsafe if let U1 { a: 12 } = u1 {} //~ ERROR access to union field is unsafe if let Some(U1 { a: 13 }) = Some(u1) {} //~ ERROR access to union field is unsafe @@ -73,4 +106,44 @@ fn main() { let mut u3 = U3 { a: ManuallyDrop::new(String::from("old")) }; // OK u3.a = ManuallyDrop::new(String::from("new")); // OK (assignment does not drop) *u3.a = String::from("new"); //~ ERROR access to union field is unsafe + + let mut unions = [U1 { a: 1 }, U1 { a: 2 }]; + + // Array indexing + union field raw borrow - should be OK + let ptr = &raw mut unions[0].a; // OK + let ptr2 = &raw const unions[1].a; // OK + + let a = A { a: 0 }; + let _p = &raw const (**a.b).c; //~ ERROR access to union field is unsafe + + arbitrary_deref(); +} + +// regression test for https://github.com/rust-lang/rust/pull/141469#discussion_r2312546218 +fn arbitrary_deref() { + use std::ops::Deref; + + union A { + a: usize, + b: B, + } + + #[derive(Copy, Clone)] + struct B(&'static str); + + impl Deref for B { + type Target = C; + + fn deref(&self) -> &C { + println!("{:?}", self.0); + &C { c: 0 } + } + } + + union C { + c: usize, + } + + let a = A { a: 0 }; + let _p = &raw const (*a.b).c; //~ ERROR access to union field is unsafe } diff --git a/tests/ui/union/union-unsafe.stderr b/tests/ui/union/union-unsafe.stderr index 82b3f897167c7..01f4d95eb649d 100644 --- a/tests/ui/union/union-unsafe.stderr +++ b/tests/ui/union/union-unsafe.stderr @@ -1,5 +1,5 @@ error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:31:6 + --> $DIR/union-unsafe.rs:36:6 | LL | *(u.p) = 13; | ^^^^^ access to union field @@ -7,7 +7,15 @@ LL | *(u.p) = 13; = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:43:6 + --> $DIR/union-unsafe.rs:50:26 + | +LL | let _p = &raw const *(u.p); + | ^^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:62:6 | LL | *u3.a = T::default(); | ^^^^ access to union field @@ -15,7 +23,7 @@ LL | *u3.a = T::default(); = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:49:6 + --> $DIR/union-unsafe.rs:68:6 | LL | *u3.a = T::default(); | ^^^^ access to union field @@ -23,7 +31,7 @@ LL | *u3.a = T::default(); = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:57:13 + --> $DIR/union-unsafe.rs:76:13 | LL | let a = u1.a; | ^^^^ access to union field @@ -31,7 +39,15 @@ LL | let a = u1.a; = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:60:14 + --> $DIR/union-unsafe.rs:91:29 + | +LL | let _a = &raw const vec[u4.a]; + | ^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:93:14 | LL | let U1 { a } = u1; | ^ access to union field @@ -39,7 +55,7 @@ LL | let U1 { a } = u1; = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:61:20 + --> $DIR/union-unsafe.rs:94:20 | LL | if let U1 { a: 12 } = u1 {} | ^^ access to union field @@ -47,7 +63,7 @@ LL | if let U1 { a: 12 } = u1 {} = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:62:25 + --> $DIR/union-unsafe.rs:95:25 | LL | if let Some(U1 { a: 13 }) = Some(u1) {} | ^^ access to union field @@ -55,7 +71,7 @@ LL | if let Some(U1 { a: 13 }) = Some(u1) {} = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:67:6 + --> $DIR/union-unsafe.rs:100:6 | LL | *u2.a = String::from("new"); | ^^^^ access to union field @@ -63,7 +79,7 @@ LL | *u2.a = String::from("new"); = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:71:6 + --> $DIR/union-unsafe.rs:104:6 | LL | *u3.a = 1; | ^^^^ access to union field @@ -71,13 +87,29 @@ LL | *u3.a = 1; = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:75:6 + --> $DIR/union-unsafe.rs:108:6 | LL | *u3.a = String::from("new"); | ^^^^ access to union field | = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior -error: aborting due to 10 previous errors +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:117:28 + | +LL | let _p = &raw const (**a.b).c; + | ^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:148:27 + | +LL | let _p = &raw const (*a.b).c; + | ^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error: aborting due to 14 previous errors For more information about this error, try `rustc --explain E0133`.