Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions compiler/rustc_mir_build/src/check_unsafety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) =
Expand Down
2 changes: 1 addition & 1 deletion src/tools/miri/tests/pass/both_borrows/smallvec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl<T, const N: usize> RawSmallVec<T, N> {
}

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
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed because it is not an unsafe operation anymore

const unsafe fn as_mut_ptr_heap(&mut self) -> *mut T {
Expand Down
73 changes: 73 additions & 0 deletions tests/ui/union/union-unsafe.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::cell::RefCell;
use std::mem::ManuallyDrop;
use std::ops::Deref;

union U1 {
a: u8,
Expand All @@ -17,6 +18,10 @@ union U4<T: Copy> {
a: T,
}

union U5 {
a: usize,
}

union URef {
p: &'static mut i32,
}
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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
}
54 changes: 43 additions & 11 deletions tests/ui/union/union-unsafe.stderr
Original file line number Diff line number Diff line change
@@ -1,83 +1,115 @@
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
|
= 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
|
= 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
|
= 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
|
= 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
|
= 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
|
= 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
|
= 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
|
= 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
|
= 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`.
Loading