Skip to content

Commit cca80b9

Browse files
committed
Auto merge of #103957 - JakobDegen:drop-retag, r=RalfJung
Retag as FnEntry on `drop_in_place` This commit changes the mir drop shim to always retag its argument as if it were a `&mut`. cc rust-lang/unsafe-code-guidelines#373
2 parents e5e4eef + 0229281 commit cca80b9

File tree

10 files changed

+199
-15
lines changed

10 files changed

+199
-15
lines changed

compiler/rustc_const_eval/src/interpret/terminator.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
119119
}
120120

121121
Drop { place, target, unwind } => {
122+
let frame = self.frame();
123+
let ty = place.ty(&frame.body.local_decls, *self.tcx).ty;
124+
let ty = self.subst_from_frame_and_normalize_erasing_regions(frame, ty)?;
125+
let instance = Instance::resolve_drop_in_place(*self.tcx, ty);
126+
if let ty::InstanceDef::DropGlue(_, None) = instance.def {
127+
// This is the branch we enter if and only if the dropped type has no drop glue
128+
// whatsoever. This can happen as a result of monomorphizing a drop of a
129+
// generic. In order to make sure that generic and non-generic code behaves
130+
// roughly the same (and in keeping with Mir semantics) we do nothing here.
131+
self.go_to_block(target);
132+
return Ok(());
133+
}
122134
let place = self.eval_place(place)?;
123-
let ty = place.layout.ty;
124135
trace!("TerminatorKind::drop: {:?}, type {}", place, ty);
125-
126-
let instance = Instance::resolve_drop_in_place(*self.tcx, ty);
127136
self.drop_in_place(&place, instance, target, unwind)?;
128137
}
129138

compiler/rustc_middle/src/mir/syntax.rs

+7-8
Original file line numberDiff line numberDiff line change
@@ -564,14 +564,13 @@ pub enum TerminatorKind<'tcx> {
564564
Unreachable,
565565

566566
/// The behavior of this statement differs significantly before and after drop elaboration.
567-
/// After drop elaboration, `Drop` executes the drop glue for the specified place, after which
568-
/// it continues execution/unwinds at the given basic blocks. It is possible that executing drop
569-
/// glue is special - this would be part of Rust's memory model. (**FIXME**: due we have an
570-
/// issue tracking if drop glue has any interesting semantics in addition to those of a function
571-
/// call?)
572-
///
573-
/// `Drop` before drop elaboration is a *conditional* execution of the drop glue. Specifically, the
574-
/// `Drop` will be executed if...
567+
///
568+
/// After drop elaboration: `Drop` terminators are a complete nop for types that have no drop
569+
/// glue. For other types, `Drop` terminators behave exactly like a call to
570+
/// `core::mem::drop_in_place` with a pointer to the given place.
571+
///
572+
/// `Drop` before drop elaboration is a *conditional* execution of the drop glue. Specifically,
573+
/// the `Drop` will be executed if...
575574
///
576575
/// **Needs clarification**: End of that sentence. This in effect should document the exact
577576
/// behavior of drop elaboration. The following sounds vaguely right, but I'm not quite sure:

compiler/rustc_mir_transform/src/shim.rs

+29-2
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,36 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>)
174174
let mut body =
175175
new_body(source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span);
176176

177+
// The first argument (index 0), but add 1 for the return value.
178+
let mut dropee_ptr = Place::from(Local::new(1 + 0));
179+
if tcx.sess.opts.unstable_opts.mir_emit_retag {
180+
// We want to treat the function argument as if it was passed by `&mut`. As such, we
181+
// generate
182+
// ```
183+
// temp = &mut *arg;
184+
// Retag(temp, FnEntry)
185+
// ```
186+
// It's important that we do this first, before anything that depends on `dropee_ptr`
187+
// has been put into the body.
188+
let reborrow = Rvalue::Ref(
189+
tcx.lifetimes.re_erased,
190+
BorrowKind::Mut { allow_two_phase_borrow: false },
191+
tcx.mk_place_deref(dropee_ptr),
192+
);
193+
let ref_ty = reborrow.ty(body.local_decls(), tcx);
194+
dropee_ptr = body.local_decls.push(LocalDecl::new(ref_ty, span)).into();
195+
let new_statements = [
196+
StatementKind::Assign(Box::new((dropee_ptr, reborrow))),
197+
StatementKind::Retag(RetagKind::FnEntry, Box::new(dropee_ptr)),
198+
];
199+
for s in new_statements {
200+
body.basic_blocks_mut()[START_BLOCK]
201+
.statements
202+
.push(Statement { source_info, kind: s });
203+
}
204+
}
205+
177206
if ty.is_some() {
178-
// The first argument (index 0), but add 1 for the return value.
179-
let dropee_ptr = Place::from(Local::new(1 + 0));
180207
let patch = {
181208
let param_env = tcx.param_env_reveal_all_normalized(def_id);
182209
let mut elaborator =

src/test/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.mir

+5-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33
fn std::ptr::drop_in_place(_1: *mut Test) -> () {
44
let mut _0: (); // return place in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
55
let mut _2: &mut Test; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
6-
let mut _3: (); // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
6+
let mut _3: &mut Test; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
7+
let mut _4: (); // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
78

89
bb0: {
910
_2 = &mut (*_1); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
10-
_3 = <Test as Drop>::drop(move _2) -> bb1; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
11+
Retag([fn entry] _2); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
12+
_3 = &mut (*_2); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
13+
_4 = <Test as Drop>::drop(move _3) -> bb1; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
1114
// mir::Constant
1215
// + span: $SRC_DIR/core/src/ptr/mod.rs:LL:COL
1316
// + literal: Const { ty: for<'a> fn(&'a mut Test) {<Test as Drop>::drop}, val: Value(<ZST>) }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//! Test that drop_in_place retags the entire place,
2+
//! invalidating all aliases to it.
3+
4+
// A zero-sized drop type -- the retagging of `fn drop` itself won't
5+
// do anything (since it is zero-sized); we are entirely relying on the retagging
6+
// in `drop_in_place` here.
7+
#[repr(transparent)]
8+
struct HasDrop;
9+
impl Drop for HasDrop {
10+
fn drop(&mut self) {
11+
unsafe {
12+
let _val = *P;
13+
//~^ ERROR: /not granting access .* because that would remove .* which is strongly protected/
14+
}
15+
}
16+
}
17+
18+
static mut P: *mut u8 = core::ptr::null_mut();
19+
20+
fn main() {
21+
unsafe {
22+
let mut x = (HasDrop, 0u8);
23+
let x = core::ptr::addr_of_mut!(x);
24+
P = x.cast();
25+
core::ptr::drop_in_place(x);
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected because it is an argument of call ID
2+
--> $DIR/drop_in_place_protector.rs:LL:CC
3+
|
4+
LL | let _val = *P;
5+
| ^^ not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected because it is an argument of call ID
6+
|
7+
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
8+
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
9+
help: <TAG> was created by a SharedReadWrite retag at offsets [0x0..0x1]
10+
--> $DIR/drop_in_place_protector.rs:LL:CC
11+
|
12+
LL | let x = core::ptr::addr_of_mut!(x);
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
help: <TAG> is this argument
15+
--> $DIR/drop_in_place_protector.rs:LL:CC
16+
|
17+
LL | core::ptr::drop_in_place(x);
18+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
19+
= note: BACKTRACE:
20+
= note: inside `<HasDrop as std::ops::Drop>::drop` at $DIR/drop_in_place_protector.rs:LL:CC
21+
= note: inside `std::ptr::drop_in_place::<HasDrop> - shim(Some(HasDrop))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
22+
= note: inside `std::ptr::drop_in_place::<(HasDrop, u8)> - shim(Some((HasDrop, u8)))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
23+
note: inside `main`
24+
--> $DIR/drop_in_place_protector.rs:LL:CC
25+
|
26+
LL | core::ptr::drop_in_place(x);
27+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
28+
= note: this error originates in the macro `core::ptr::addr_of_mut` (in Nightly builds, run with -Z macro-backtrace for more info)
29+
30+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
31+
32+
error: aborting due to previous error
33+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//! Test that drop_in_place mutably retags the entire place, even for a type that does not need
2+
//! dropping, ensuring among other things that it is writeable
3+
4+
//@error-pattern: /retag .* for Unique permission .* only grants SharedReadOnly permission/
5+
6+
fn main() {
7+
unsafe {
8+
let x = 0u8;
9+
let x = core::ptr::addr_of!(x);
10+
core::ptr::drop_in_place(x.cast_mut());
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
error: Undefined Behavior: trying to retag from <TAG> for Unique permission at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location
2+
--> RUSTLIB/core/src/ptr/mod.rs:LL:CC
3+
|
4+
LL | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
| |
7+
| trying to retag from <TAG> for Unique permission at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location
8+
| this error occurs as part of retag at ALLOC[0x0..0x1]
9+
|
10+
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
11+
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
12+
help: <TAG> was created by a SharedReadOnly retag at offsets [0x0..0x1]
13+
--> $DIR/drop_in_place_retag.rs:LL:CC
14+
|
15+
LL | let x = core::ptr::addr_of!(x);
16+
| ^^^^^^^^^^^^^^^^^^^^^^
17+
= note: BACKTRACE:
18+
= note: inside `std::ptr::drop_in_place::<u8> - shim(None)` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
19+
note: inside `main`
20+
--> $DIR/drop_in_place_retag.rs:LL:CC
21+
|
22+
LL | core::ptr::drop_in_place(x.cast_mut());
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
24+
= note: this error originates in the macro `core::ptr::addr_of` (in Nightly builds, run with -Z macro-backtrace for more info)
25+
26+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
27+
28+
error: aborting due to previous error
29+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#[repr(transparent)]
2+
struct HasDrop(u8);
3+
4+
impl Drop for HasDrop {
5+
fn drop(&mut self) {}
6+
}
7+
8+
#[repr(C, align(2))]
9+
struct PartialDrop {
10+
a: HasDrop,
11+
b: u8,
12+
}
13+
14+
//@error-pattern: /alignment 2 is required/
15+
fn main() {
16+
unsafe {
17+
// Create an unaligned pointer
18+
let mut x = [0_u16; 2];
19+
let p = core::ptr::addr_of_mut!(x).cast::<u8>();
20+
let p = p.add(1);
21+
let p = p.cast::<PartialDrop>();
22+
23+
core::ptr::drop_in_place(p);
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required
2+
--> RUSTLIB/core/src/ptr/mod.rs:LL:CC
3+
|
4+
LL | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ accessing memory with alignment ALIGN, but alignment ALIGN is required
6+
|
7+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
8+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
= note: BACKTRACE:
10+
= note: inside `std::ptr::drop_in_place::<PartialDrop> - shim(Some(PartialDrop))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
11+
note: inside `main`
12+
--> $DIR/drop_in_place.rs:LL:CC
13+
|
14+
LL | core::ptr::drop_in_place(p);
15+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
16+
17+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
18+
19+
error: aborting due to previous error
20+

0 commit comments

Comments
 (0)