Skip to content
Closed
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
21 changes: 14 additions & 7 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}

/// Visits the memory covered by `place`, sensitive to freezing: the 2nd parameter
/// of `action` will be true if this is frozen, false if this is in an `UnsafeCell`.
/// of `action` will be true if this is frozen, false if this is in an `UnsafeCell` or `UnsafePinned`.
/// The range is relative to `place`.
fn visit_freeze_sensitive(
&self,
Expand Down Expand Up @@ -477,7 +477,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
action(alloc_range(cur_addr - start_addr, frozen_size), /*frozen*/ true)?;
}
cur_addr += frozen_size;
// This `UnsafeCell` is NOT frozen.
// This `UnsafeCell` or `UnsafePinned` is NOT frozen.
if unsafe_cell_size != Size::ZERO {
action(
alloc_range(cur_addr - start_addr, unsafe_cell_size),
Expand Down Expand Up @@ -537,19 +537,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
self.ecx
}

// Hook to detect `UnsafeCell`.
// Hook to detect `UnsafeCell` and `UnsafePinned`.
fn visit_value(&mut self, v: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty);
let is_unsafe_cell = match v.layout.ty.kind() {
ty::Adt(adt, _) =>
Some(adt.did()) == self.ecx.tcx.lang_items().unsafe_cell_type(),
_ => false,
};
if is_unsafe_cell {
// We do not have to recurse further, this is an `UnsafeCell`.

let is_unsafe_pinned = match v.layout.ty.kind() {
ty::Adt(adt, _) =>
Some(adt.did()) == self.ecx.tcx.lang_items().unsafe_pinned_type(),
_ => false,
};
if is_unsafe_cell || is_unsafe_pinned {
// We do not have to recurse further, this is an `UnsafeCell`
// or `UnsafePinned` since both opt out of aliasing rules on their bytes.
(self.unsafe_cell_action)(v)
} else if self.ecx.type_is_freeze(v.layout.ty) {
// This is `Freeze`, there cannot be an `UnsafeCell`
// This is `Freeze`, there cannot be an `UnsafeCell` or `UnsafePinned` inside.
interp_ok(())
} else if matches!(v.layout.fields, FieldsShape::Union(..)) {
// A (non-frozen) union. We fall back to whatever the type says.
Expand All @@ -573,7 +580,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
(self.unsafe_cell_action)(v)
}
Variants::Single { .. } | Variants::Empty => {
// Proceed further, try to find where exactly that `UnsafeCell`
// Proceed further, try to find where exactly that `UnsafeCell` or `UnsafePinned`
// is hiding.
self.walk_value(v)
}
Expand Down
16 changes: 16 additions & 0 deletions tests/pass/both_borrows/unsafe_pinned_per_byte.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#![feature(unsafe_pinned)]
use std::pin::UnsafePinned;
#[repr(C)]
struct Mixed {
before: u32,
pinned: UnsafePinned<u32>,
after: u32,
}

fn main() {
let m = Mixed { before: 1, pinned: UnsafePinned::new(2), after: 3 };
// 'before' and 'after' should still be protected by aliasing rules
// 'pinned' should not — Miri must not flag this as UB
let _ref1 = &m.before;
let _ref2 = &m.after;
}