Skip to content

Commit 9c4c3ca

Browse files
committed
Auto merge of #2977 - RalfJung:rustup, r=RalfJung
Rustup
2 parents 742db95 + c11d709 commit 9c4c3ca

24 files changed

+450
-107
lines changed

rust-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
743333f3dd90721461c09387ec73d09c080d5f5f
1+
136dab66142115d9de16b4cfe2d8395d71a8ab6d

src/borrow_tracker/mod.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ pub struct FrameState {
7474

7575
impl VisitTags for FrameState {
7676
fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
77-
// `protected_tags` are fine to GC.
77+
// `protected_tags` are already recorded by `GlobalStateInner`.
7878
}
7979
}
8080

@@ -108,9 +108,12 @@ pub struct GlobalStateInner {
108108
}
109109

110110
impl VisitTags for GlobalStateInner {
111-
fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
112-
// The only candidate is base_ptr_tags, and that does not need visiting since we don't ever
113-
// GC the bottommost tag.
111+
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
112+
for &tag in self.protected_tags.keys() {
113+
visit(tag);
114+
}
115+
// The only other candidate is base_ptr_tags, and that does not need visiting since we don't ever
116+
// GC the bottommost/root tag.
114117
}
115118
}
116119

@@ -302,12 +305,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
302305
}
303306
}
304307

305-
fn retag_return_place(&mut self) -> InterpResult<'tcx> {
308+
fn protect_place(&mut self, place: &MPlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
306309
let this = self.eval_context_mut();
307310
let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
308311
match method {
309-
BorrowTrackerMethod::StackedBorrows => this.sb_retag_return_place(),
310-
BorrowTrackerMethod::TreeBorrows => this.tb_retag_return_place(),
312+
BorrowTrackerMethod::StackedBorrows => this.sb_protect_place(place),
313+
BorrowTrackerMethod::TreeBorrows => this.tb_protect_place(place),
311314
}
312315
}
313316

src/borrow_tracker/stacked_borrows/diagnostics.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ struct RetagOp {
189189
#[derive(Debug, Clone, Copy, PartialEq)]
190190
pub enum RetagCause {
191191
Normal,
192-
FnReturnPlace,
192+
InPlaceFnPassing,
193193
FnEntry,
194194
TwoPhase,
195195
}
@@ -501,7 +501,7 @@ impl RetagCause {
501501
match self {
502502
RetagCause::Normal => "retag",
503503
RetagCause::FnEntry => "function-entry retag",
504-
RetagCause::FnReturnPlace => "return-place retag",
504+
RetagCause::InPlaceFnPassing => "in-place function argument/return passing protection",
505505
RetagCause::TwoPhase => "two-phase retag",
506506
}
507507
.to_string()

src/borrow_tracker/stacked_borrows/mod.rs

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -994,35 +994,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
994994
}
995995
}
996996

997-
/// After a stack frame got pushed, retag the return place so that we are sure
998-
/// it does not alias with anything.
997+
/// Protect a place so that it cannot be used any more for the duration of the current function
998+
/// call.
999999
///
1000-
/// This is a HACK because there is nothing in MIR that would make the retag
1001-
/// explicit. Also see <https://github.com/rust-lang/rust/issues/71117>.
1002-
fn sb_retag_return_place(&mut self) -> InterpResult<'tcx> {
1000+
/// This is used to ensure soundness of in-place function argument/return passing.
1001+
fn sb_protect_place(&mut self, place: &MPlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
10031002
let this = self.eval_context_mut();
1004-
let return_place = &this.frame().return_place;
1005-
if return_place.layout.is_zst() {
1006-
// There may not be any memory here, nothing to do.
1007-
return Ok(());
1008-
}
1009-
// We need this to be in-memory to use tagged pointers.
1010-
let return_place = this.force_allocation(&return_place.clone())?;
10111003

1012-
// We have to turn the place into a pointer to use the existing code.
1004+
// We have to turn the place into a pointer to use the usual retagging logic.
10131005
// (The pointer type does not matter, so we use a raw pointer.)
1014-
let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx, return_place.layout.ty))?;
1015-
let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout);
1016-
// Reborrow it. With protection! That is part of the point.
1006+
let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx, place.layout.ty))?;
1007+
let ptr = ImmTy::from_immediate(place.to_ref(this), ptr_layout);
1008+
// Reborrow it. With protection! That is the entire point.
10171009
let new_perm = NewPermission::Uniform {
10181010
perm: Permission::Unique,
10191011
access: Some(AccessKind::Write),
10201012
protector: Some(ProtectorKind::StrongProtector),
10211013
};
1022-
let val = this.sb_retag_reference(&val, new_perm, RetagCause::FnReturnPlace)?;
1023-
// And use reborrowed pointer for return place.
1024-
let return_place = this.ref_to_mplace(&val)?;
1025-
this.frame_mut().return_place = return_place.into();
1014+
let _new_ptr = this.sb_retag_reference(&ptr, new_perm, RetagCause::InPlaceFnPassing)?;
1015+
// We just throw away `new_ptr`, so nobody can access this memory while it is protected.
10261016

10271017
Ok(())
10281018
}

src/borrow_tracker/tree_borrows/mod.rs

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
178178
&mut self,
179179
place: &MPlaceTy<'tcx, Provenance>, // parent tag extracted from here
180180
ptr_size: Size,
181-
new_perm: Option<NewPermission>,
181+
new_perm: NewPermission,
182182
new_tag: BorTag,
183183
) -> InterpResult<'tcx, Option<(AllocId, BorTag)>> {
184184
let this = self.eval_context_mut();
@@ -256,10 +256,6 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
256256
ptr_size.bytes()
257257
);
258258

259-
let Some(new_perm) = new_perm else {
260-
return Ok(Some((alloc_id, orig_tag)));
261-
};
262-
263259
if let Some(protect) = new_perm.protector {
264260
// We register the protection in two different places.
265261
// This makes creating a protector slower, but checking whether a tag
@@ -305,7 +301,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
305301
fn tb_retag_reference(
306302
&mut self,
307303
val: &ImmTy<'tcx, Provenance>,
308-
new_perm: Option<NewPermission>,
304+
new_perm: NewPermission,
309305
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
310306
let this = self.eval_context_mut();
311307
// We want a place for where the ptr *points to*, so we get one.
@@ -317,7 +313,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
317313
// - if the pointer is not reborrowed (raw pointer) or if `zero_size` is set
318314
// then we override the size to do a zero-length reborrow.
319315
let reborrow_size = match new_perm {
320-
Some(NewPermission { zero_size: false, .. }) =>
316+
NewPermission { zero_size: false, .. } =>
321317
this.size_and_align_of_mplace(&place)?
322318
.map(|(size, _)| size)
323319
.unwrap_or(place.layout.size),
@@ -374,7 +370,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
374370
NewPermission::from_ref_ty(pointee, mutability, kind, this),
375371
_ => None,
376372
};
377-
this.tb_retag_reference(val, new_perm)
373+
if let Some(new_perm) = new_perm {
374+
this.tb_retag_reference(val, new_perm)
375+
} else {
376+
Ok(val.clone())
377+
}
378378
}
379379

380380
/// Retag all pointers that are stored in this place.
@@ -405,9 +405,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
405405
place: &PlaceTy<'tcx, Provenance>,
406406
new_perm: Option<NewPermission>,
407407
) -> InterpResult<'tcx> {
408-
let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
409-
let val = self.ecx.tb_retag_reference(&val, new_perm)?;
410-
self.ecx.write_immediate(*val, place)?;
408+
if let Some(new_perm) = new_perm {
409+
let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
410+
let val = self.ecx.tb_retag_reference(&val, new_perm)?;
411+
self.ecx.write_immediate(*val, place)?;
412+
}
411413
Ok(())
412414
}
413415
}
@@ -493,36 +495,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
493495
}
494496
}
495497

496-
/// After a stack frame got pushed, retag the return place so that we are sure
497-
/// it does not alias with anything.
498+
/// Protect a place so that it cannot be used any more for the duration of the current function
499+
/// call.
498500
///
499-
/// This is a HACK because there is nothing in MIR that would make the retag
500-
/// explicit. Also see <https://github.com/rust-lang/rust/issues/71117>.
501-
fn tb_retag_return_place(&mut self) -> InterpResult<'tcx> {
501+
/// This is used to ensure soundness of in-place function argument/return passing.
502+
fn tb_protect_place(&mut self, place: &MPlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
502503
let this = self.eval_context_mut();
503-
//this.debug_hint_location();
504-
let return_place = &this.frame().return_place;
505-
if return_place.layout.is_zst() {
506-
// There may not be any memory here, nothing to do.
507-
return Ok(());
508-
}
509-
// We need this to be in-memory to use tagged pointers.
510-
let return_place = this.force_allocation(&return_place.clone())?;
511504

512-
// We have to turn the place into a pointer to use the existing code.
505+
// We have to turn the place into a pointer to use the usual retagging logic.
513506
// (The pointer type does not matter, so we use a raw pointer.)
514-
let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx, return_place.layout.ty))?;
515-
let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout);
516-
// Reborrow it. With protection! That is part of the point.
517-
let new_perm = Some(NewPermission {
507+
let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx, place.layout.ty))?;
508+
let ptr = ImmTy::from_immediate(place.to_ref(this), ptr_layout);
509+
// Reborrow it. With protection! That is the entire point.
510+
let new_perm = NewPermission {
518511
initial_state: Permission::new_active(),
519512
zero_size: false,
520513
protector: Some(ProtectorKind::StrongProtector),
521-
});
522-
let val = this.tb_retag_reference(&val, new_perm)?;
523-
// And use reborrowed pointer for return place.
524-
let return_place = this.ref_to_mplace(&val)?;
525-
this.frame_mut().return_place = return_place.into();
514+
};
515+
let _new_ptr = this.tb_retag_reference(&ptr, new_perm)?;
516+
// We just throw away `new_ptr`, so nobody can access this memory while it is protected.
526517

527518
Ok(())
528519
}

src/lib.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,17 @@
4343
// Needed for rustdoc from bootstrap (with `-Znormalize-docs`).
4444
#![recursion_limit = "256"]
4545

46+
extern crate either; // the one from rustc
47+
4648
extern crate rustc_apfloat;
4749
extern crate rustc_ast;
48-
extern crate rustc_errors;
49-
#[macro_use]
50-
extern crate rustc_middle;
5150
extern crate rustc_const_eval;
5251
extern crate rustc_data_structures;
52+
extern crate rustc_errors;
5353
extern crate rustc_hir;
5454
extern crate rustc_index;
55+
#[macro_use]
56+
extern crate rustc_middle;
5557
extern crate rustc_session;
5658
extern crate rustc_span;
5759
extern crate rustc_target;

src/machine.rs

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::fmt;
77
use std::path::Path;
88
use std::process;
99

10+
use either::Either;
1011
use rand::rngs::StdRng;
1112
use rand::SeedableRng;
1213

@@ -533,15 +534,16 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
533534
let target = &tcx.sess.target;
534535
match target.arch.as_ref() {
535536
"wasm32" | "wasm64" => 64 * 1024, // https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances
536-
"aarch64" =>
537+
"aarch64" => {
537538
if target.options.vendor.as_ref() == "apple" {
538539
// No "definitive" source, but see:
539540
// https://www.wwdcnotes.com/notes/wwdc20/10214/
540541
// https://github.com/ziglang/zig/issues/11308 etc.
541542
16 * 1024
542543
} else {
543544
4 * 1024
544-
},
545+
}
546+
}
545547
_ => 4 * 1024,
546548
}
547549
};
@@ -892,7 +894,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
892894
ecx: &mut MiriInterpCx<'mir, 'tcx>,
893895
instance: ty::Instance<'tcx>,
894896
abi: Abi,
895-
args: &[OpTy<'tcx, Provenance>],
897+
args: &[FnArg<'tcx, Provenance>],
896898
dest: &PlaceTy<'tcx, Provenance>,
897899
ret: Option<mir::BasicBlock>,
898900
unwind: mir::UnwindAction,
@@ -905,12 +907,13 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
905907
ecx: &mut MiriInterpCx<'mir, 'tcx>,
906908
fn_val: Dlsym,
907909
abi: Abi,
908-
args: &[OpTy<'tcx, Provenance>],
910+
args: &[FnArg<'tcx, Provenance>],
909911
dest: &PlaceTy<'tcx, Provenance>,
910912
ret: Option<mir::BasicBlock>,
911913
_unwind: mir::UnwindAction,
912914
) -> InterpResult<'tcx> {
913-
ecx.call_dlsym(fn_val, abi, args, dest, ret)
915+
let args = ecx.copy_fn_args(args)?; // FIXME: Should `InPlace` arguments be reset to uninit?
916+
ecx.call_dlsym(fn_val, abi, &args, dest, ret)
914917
}
915918

916919
#[inline(always)]
@@ -1206,6 +1209,25 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
12061209
Ok(())
12071210
}
12081211

1212+
fn protect_in_place_function_argument(
1213+
ecx: &mut InterpCx<'mir, 'tcx, Self>,
1214+
place: &PlaceTy<'tcx, Provenance>,
1215+
) -> InterpResult<'tcx> {
1216+
// We do need to write `uninit` so that even after the call ends, the former contents of
1217+
// this place cannot be observed any more.
1218+
ecx.write_uninit(place)?;
1219+
// If we have a borrow tracker, we also have it set up protection so that all reads *and
1220+
// writes* during this call are insta-UB.
1221+
if ecx.machine.borrow_tracker.is_some() {
1222+
if let Either::Left(place) = place.as_mplace_or_local() {
1223+
ecx.protect_place(&place)?;
1224+
} else {
1225+
// Locals that don't have their address taken are as protected as they can ever be.
1226+
}
1227+
}
1228+
Ok(())
1229+
}
1230+
12091231
#[inline(always)]
12101232
fn init_frame_extra(
12111233
ecx: &mut InterpCx<'mir, 'tcx, Self>,
@@ -1288,8 +1310,17 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
12881310
let stack_len = ecx.active_thread_stack().len();
12891311
ecx.active_thread_mut().set_top_user_relevant_frame(stack_len - 1);
12901312
}
1291-
if ecx.machine.borrow_tracker.is_some() {
1292-
ecx.retag_return_place()?;
1313+
Ok(())
1314+
}
1315+
1316+
fn before_stack_pop(
1317+
ecx: &InterpCx<'mir, 'tcx, Self>,
1318+
frame: &Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>,
1319+
) -> InterpResult<'tcx> {
1320+
// We want this *before* the return value copy, because the return place itself is protected
1321+
// until we do `end_call` here.
1322+
if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
1323+
borrow_tracker.borrow_mut().end_call(&frame.extra);
12931324
}
12941325
Ok(())
12951326
}
@@ -1308,9 +1339,6 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
13081339
ecx.active_thread_mut().recompute_top_user_relevant_frame();
13091340
}
13101341
let timing = frame.extra.timing.take();
1311-
if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
1312-
borrow_tracker.borrow_mut().end_call(&frame.extra);
1313-
}
13141342
let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding);
13151343
if let Some(profiler) = ecx.machine.profiler.as_ref() {
13161344
profiler.finish_recording_interval_event(timing.unwrap());

src/shims/mod.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
3131
&mut self,
3232
instance: ty::Instance<'tcx>,
3333
abi: Abi,
34-
args: &[OpTy<'tcx, Provenance>],
34+
args: &[FnArg<'tcx, Provenance>],
3535
dest: &PlaceTy<'tcx, Provenance>,
3636
ret: Option<mir::BasicBlock>,
3737
unwind: mir::UnwindAction,
@@ -41,7 +41,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
4141

4242
// There are some more lang items we want to hook that CTFE does not hook (yet).
4343
if this.tcx.lang_items().align_offset_fn() == Some(instance.def.def_id()) {
44-
let [ptr, align] = check_arg_count(args)?;
44+
let args = this.copy_fn_args(args)?;
45+
let [ptr, align] = check_arg_count(&args)?;
4546
if this.align_offset(ptr, align, dest, ret, unwind)? {
4647
return Ok(None);
4748
}
@@ -55,7 +56,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
5556
// to run extra MIR), and Ok(Some(body)) if we found MIR to run for the
5657
// foreign function
5758
// Any needed call to `goto_block` will be performed by `emulate_foreign_item`.
58-
return this.emulate_foreign_item(instance.def_id(), abi, args, dest, ret, unwind);
59+
let args = this.copy_fn_args(args)?; // FIXME: Should `InPlace` arguments be reset to uninit?
60+
return this.emulate_foreign_item(instance.def_id(), abi, &args, dest, ret, unwind);
5961
}
6062

6163
// Otherwise, load the MIR.

0 commit comments

Comments
 (0)