Skip to content

Commit e1a0f66

Browse files
authored
Tag static/const allocations (#748)
Tag static/const allocations
2 parents 0acfd32 + b231a7e commit e1a0f66

File tree

7 files changed

+87
-69
lines changed

7 files changed

+87
-69
lines changed

rust-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
8b40a188cee5bef97526dfc271afbd2a98008183
1+
627486af15d222bcba336b12ea92a05237cc9ab1

src/intrinsic.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc::ty;
55

66
use crate::{
77
PlaceTy, OpTy, ImmTy, Immediate, Scalar, ScalarMaybeUndef, Tag,
8-
OperatorEvalContextExt
8+
OperatorEvalContextExt, MiriMemoryKind,
99
};
1010

1111
impl<'a, 'mir, 'tcx> EvalContextExt<'a, 'mir, 'tcx> for crate::MiriEvalContext<'a, 'mir, 'tcx> {}
@@ -401,7 +401,8 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a+'mir>: crate::MiriEvalContextExt<'a,
401401
"type_name" => {
402402
let ty = substs.type_at(0);
403403
let ty_name = ty.to_string();
404-
let value = this.str_to_immediate(&ty_name)?;
404+
let ptr = this.memory_mut().allocate_static_bytes(ty_name.as_bytes(), MiriMemoryKind::Static.into());
405+
let value = Immediate::new_slice(Scalar::Ptr(ptr), ty_name.len() as u64, this);
405406
this.write_immediate(value, dest)?;
406407
}
407408

src/lib.rs

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use rand::SeedableRng;
3030

3131
use rustc::ty::{self, TyCtxt, query::TyCtxtAt};
3232
use rustc::ty::layout::{LayoutOf, Size, Align};
33-
use rustc::hir::{self, def_id::DefId};
33+
use rustc::hir::def_id::DefId;
3434
use rustc::mir;
3535
pub use rustc_mir::interpret::*;
3636
// Resolve ambiguity.
@@ -113,7 +113,7 @@ pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
113113

114114
// Return value (in static memory so that it does not count as leak).
115115
let ret = ecx.layout_of(start_mir.return_ty())?;
116-
let ret_ptr = ecx.allocate(ret, MiriMemoryKind::MutStatic.into());
116+
let ret_ptr = ecx.allocate(ret, MiriMemoryKind::Static.into());
117117

118118
// Push our stack frame.
119119
ecx.push_stack_frame(
@@ -128,7 +128,7 @@ pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
128128
let mut args = ecx.frame().mir.args_iter();
129129

130130
// First argument: pointer to `main()`.
131-
let main_ptr = ecx.memory_mut().create_fn_alloc(main_instance).with_default_tag();
131+
let main_ptr = ecx.memory_mut().create_fn_alloc(main_instance);
132132
let dest = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(args.next().unwrap())))?;
133133
ecx.write_scalar(Scalar::Ptr(main_ptr), dest)?;
134134

@@ -162,7 +162,7 @@ pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
162162
// Add `0` terminator.
163163
let mut arg = arg.into_bytes();
164164
arg.push(0);
165-
argvs.push(ecx.memory_mut().allocate_static_bytes(arg.as_slice()).with_default_tag());
165+
argvs.push(ecx.memory_mut().allocate_static_bytes(arg.as_slice(), MiriMemoryKind::Static.into()));
166166
}
167167
// Make an array with all these pointers, in the Miri memory.
168168
let argvs_layout = ecx.layout_of(ecx.tcx.mk_array(ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8), argvs.len() as u64))?;
@@ -299,8 +299,8 @@ pub enum MiriMemoryKind {
299299
C,
300300
/// Part of env var emulation.
301301
Env,
302-
/// Mutable statics.
303-
MutStatic,
302+
/// Statics.
303+
Static,
304304
}
305305

306306
impl Into<MemoryKind<MiriMemoryKind>> for MiriMemoryKind {
@@ -316,7 +316,7 @@ impl MayLeak for MiriMemoryKind {
316316
use self::MiriMemoryKind::*;
317317
match self {
318318
Rust | C => false,
319-
Env | MutStatic => true,
319+
Env | Static => true,
320320
}
321321
}
322322
}
@@ -392,7 +392,7 @@ impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> {
392392

393393
type MemoryMap = MonoHashMap<AllocId, (MemoryKind<MiriMemoryKind>, Allocation<Tag, Self::AllocExtra>)>;
394394

395-
const STATIC_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::MutStatic);
395+
const STATIC_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Static);
396396

397397
#[inline(always)]
398398
fn enforce_validity(ecx: &InterpretCx<'a, 'mir, 'tcx, Self>) -> bool {
@@ -476,8 +476,7 @@ impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> {
476476
fn find_foreign_static(
477477
def_id: DefId,
478478
tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
479-
memory_extra: &Self::MemoryExtra,
480-
) -> EvalResult<'tcx, Cow<'tcx, Allocation<Tag, Self::AllocExtra>>> {
479+
) -> EvalResult<'tcx, Cow<'tcx, Allocation>> {
481480
let attrs = tcx.get_attrs(def_id);
482481
let link_name = match attr::first_attr_value_str_by_name(&attrs, sym::link_name) {
483482
Some(name) => name.as_str(),
@@ -489,8 +488,7 @@ impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> {
489488
// This should be all-zero, pointer-sized.
490489
let size = tcx.data_layout.pointer_size;
491490
let data = vec![0; size.bytes() as usize];
492-
let extra = Stacks::new(size, Tag::default(), Rc::clone(memory_extra));
493-
Allocation::from_bytes(&data, tcx.data_layout.pointer_align.abi, extra)
491+
Allocation::from_bytes(&data, tcx.data_layout.pointer_align.abi)
494492
}
495493
_ => return err!(Unimplemented(
496494
format!("can't access foreign static: {}", link_name),
@@ -506,47 +504,48 @@ impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> {
506504
Ok(())
507505
}
508506

509-
fn adjust_static_allocation<'b>(
510-
alloc: &'b Allocation,
507+
fn tag_allocation<'b>(
508+
id: AllocId,
509+
alloc: Cow<'b, Allocation>,
510+
kind: Option<MemoryKind<Self::MemoryKinds>>,
511511
memory_extra: &Self::MemoryExtra,
512-
) -> Cow<'b, Allocation<Tag, Self::AllocExtra>> {
513-
let extra = Stacks::new(
512+
) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag) {
513+
let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
514+
let alloc = alloc.into_owned();
515+
let (extra, base_tag) = Stacks::new_allocation(
516+
id,
514517
Size::from_bytes(alloc.bytes.len() as u64),
515-
Tag::default(),
516518
Rc::clone(memory_extra),
519+
kind,
517520
);
521+
if kind != MiriMemoryKind::Static.into() {
522+
assert!(alloc.relocations.is_empty(), "Only statics can come initialized with inner pointers");
523+
// Now we can rely on the inner pointers being static, too.
524+
}
525+
let mut memory_extra = memory_extra.borrow_mut();
518526
let alloc: Allocation<Tag, Self::AllocExtra> = Allocation {
519-
bytes: alloc.bytes.clone(),
527+
bytes: alloc.bytes,
520528
relocations: Relocations::from_presorted(
521529
alloc.relocations.iter()
522-
.map(|&(offset, ((), alloc))| (offset, (Tag::default(), alloc)))
530+
// The allocations in the relocations (pointers stored *inside* this allocation)
531+
// all get the base pointer tag.
532+
.map(|&(offset, ((), alloc))| (offset, (memory_extra.static_base_ptr(alloc), alloc)))
523533
.collect()
524534
),
525-
undef_mask: alloc.undef_mask.clone(),
535+
undef_mask: alloc.undef_mask,
526536
align: alloc.align,
527537
mutability: alloc.mutability,
528538
extra,
529539
};
530-
Cow::Owned(alloc)
531-
}
532-
533-
#[inline(always)]
534-
fn new_allocation(
535-
size: Size,
536-
extra: &Self::MemoryExtra,
537-
kind: MemoryKind<MiriMemoryKind>,
538-
) -> (Self::AllocExtra, Self::PointerTag) {
539-
Stacks::new_allocation(size, extra, kind)
540+
(Cow::Owned(alloc), base_tag)
540541
}
541542

542543
#[inline(always)]
543-
fn tag_dereference(
544-
_ecx: &InterpretCx<'a, 'mir, 'tcx, Self>,
545-
place: MPlaceTy<'tcx, Tag>,
546-
_mutability: Option<hir::Mutability>,
547-
) -> EvalResult<'tcx, Scalar<Tag>> {
548-
// Nothing happens.
549-
Ok(place.ptr)
544+
fn tag_static_base_pointer(
545+
id: AllocId,
546+
memory_extra: &Self::MemoryExtra,
547+
) -> Self::PointerTag {
548+
memory_extra.borrow_mut().static_base_ptr(id)
550549
}
551550

552551
#[inline(always)]

src/stacked_borrows.rs

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::cell::RefCell;
2-
use std::collections::HashSet;
2+
use std::collections::{HashMap, HashSet};
33
use std::rc::Rc;
44
use std::fmt;
55
use std::num::NonZeroU64;
@@ -10,7 +10,7 @@ use rustc::mir::RetagKind;
1010

1111
use crate::{
1212
EvalResult, InterpError, MiriEvalContext, HelpersEvalContextExt, Evaluator, MutValueVisitor,
13-
MemoryKind, MiriMemoryKind, RangeMap, Allocation, AllocationExtra, CheckInAllocMsg,
13+
MemoryKind, MiriMemoryKind, RangeMap, Allocation, AllocationExtra, AllocId, CheckInAllocMsg,
1414
Pointer, Immediate, ImmTy, PlaceTy, MPlaceTy,
1515
};
1616

@@ -92,10 +92,18 @@ pub struct Stacks {
9292
/// Extra global state, available to the memory access hooks.
9393
#[derive(Debug)]
9494
pub struct GlobalState {
95+
/// Next unused pointer ID (tag).
9596
next_ptr_id: PtrId,
97+
/// Table storing the "base" tag for each allocation.
98+
/// The base tag is the one used for the initial pointer.
99+
/// We need this in a separate table to handle cyclic statics.
100+
base_ptr_ids: HashMap<AllocId, Tag>,
101+
/// Next unused call ID (for protectors).
96102
next_call_id: CallId,
103+
/// Those call IDs corresponding to functions that are still running.
97104
active_calls: HashSet<CallId>,
98105
}
106+
/// Memory extra state gives us interior mutable access to the global state.
99107
pub type MemoryState = Rc<RefCell<GlobalState>>;
100108

101109
/// Indicates which kind of access is being performed.
@@ -144,14 +152,15 @@ impl Default for GlobalState {
144152
fn default() -> Self {
145153
GlobalState {
146154
next_ptr_id: NonZeroU64::new(1).unwrap(),
155+
base_ptr_ids: HashMap::default(),
147156
next_call_id: NonZeroU64::new(1).unwrap(),
148157
active_calls: HashSet::default(),
149158
}
150159
}
151160
}
152161

153162
impl GlobalState {
154-
pub fn new_ptr(&mut self) -> PtrId {
163+
fn new_ptr(&mut self) -> PtrId {
155164
let id = self.next_ptr_id;
156165
self.next_ptr_id = NonZeroU64::new(id.get() + 1).unwrap();
157166
id
@@ -172,6 +181,15 @@ impl GlobalState {
172181
fn is_active(&self, id: CallId) -> bool {
173182
self.active_calls.contains(&id)
174183
}
184+
185+
pub fn static_base_ptr(&mut self, id: AllocId) -> Tag {
186+
self.base_ptr_ids.get(&id).copied().unwrap_or_else(|| {
187+
let tag = Tag::Tagged(self.new_ptr());
188+
trace!("New allocation {:?} has base tag {:?}", id, tag);
189+
self.base_ptr_ids.insert(id, tag);
190+
tag
191+
})
192+
}
175193
}
176194

177195
// # Stacked Borrows Core Begin
@@ -190,14 +208,6 @@ impl GlobalState {
190208
/// F3: If an access happens with an `&` outside `UnsafeCell`,
191209
/// it requires the `SharedReadOnly` to still be in the stack.
192210
193-
impl Default for Tag {
194-
#[inline(always)]
195-
fn default() -> Tag {
196-
Tag::Untagged
197-
}
198-
}
199-
200-
201211
/// Core relation on `Permission` to define which accesses are allowed
202212
impl Permission {
203213
/// This defines for a given permission, whether it permits the given kind of access.
@@ -431,12 +441,13 @@ impl<'tcx> Stack {
431441
/// Map per-stack operations to higher-level per-location-range operations.
432442
impl<'tcx> Stacks {
433443
/// Creates new stack with initial tag.
434-
pub(crate) fn new(
444+
fn new(
435445
size: Size,
446+
perm: Permission,
436447
tag: Tag,
437448
extra: MemoryState,
438449
) -> Self {
439-
let item = Item { perm: Permission::Unique, tag, protector: None };
450+
let item = Item { perm, tag, protector: None };
440451
let stack = Stack {
441452
borrows: vec![item],
442453
};
@@ -465,27 +476,25 @@ impl<'tcx> Stacks {
465476
/// Glue code to connect with Miri Machine Hooks
466477
impl Stacks {
467478
pub fn new_allocation(
479+
id: AllocId,
468480
size: Size,
469-
extra: &MemoryState,
481+
extra: MemoryState,
470482
kind: MemoryKind<MiriMemoryKind>,
471483
) -> (Self, Tag) {
472-
let tag = match kind {
473-
MemoryKind::Stack => {
474-
// New unique borrow. This `Uniq` is not accessible by the program,
484+
let (tag, perm) = match kind {
485+
MemoryKind::Stack =>
486+
// New unique borrow. This tag is not accessible by the program,
475487
// so it will only ever be used when using the local directly (i.e.,
476-
// not through a pointer). That is, whenever we directly use a local, this will pop
488+
// not through a pointer). That is, whenever we directly write to a local, this will pop
477489
// everything else off the stack, invalidating all previous pointers,
478-
// and in particular, *all* raw pointers. This subsumes the explicit
479-
// `reset` which the blog post [1] says to perform when accessing a local.
480-
//
481-
// [1]: <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>
482-
Tag::Tagged(extra.borrow_mut().new_ptr())
483-
}
484-
_ => {
485-
Tag::Untagged
486-
}
490+
// and in particular, *all* raw pointers.
491+
(Tag::Tagged(extra.borrow_mut().new_ptr()), Permission::Unique),
492+
MemoryKind::Machine(MiriMemoryKind::Static) =>
493+
(extra.borrow_mut().static_base_ptr(id), Permission::SharedReadWrite),
494+
_ =>
495+
(Tag::Untagged, Permission::SharedReadWrite),
487496
};
488-
let stack = Stacks::new(size, tag, Rc::clone(extra));
497+
let stack = Stacks::new(size, perm, tag, extra);
489498
(stack, tag)
490499
}
491500
}

tests/compile-fail/modifying_constants.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// This should fail even without validation
2+
// compile-flags: -Zmiri-disable-validation
13

24
fn main() {
35
let x = &1; // the `&1` is promoted to a constant, but it used to be that only the pointer is marked static, not the pointee
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
static ARRAY: [u8; 2] = [0, 1];
2+
3+
fn main() {
4+
let ptr_to_first = &ARRAY[0] as *const u8;
5+
// Illegally use this to access the 2nd element.
6+
let _val = unsafe { *ptr_to_first.add(1) }; //~ ERROR borrow stack
7+
}

tests/run-pass/thread-local.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ fn main() {
5656
create(None); // check that the no-dtor case works
5757

5858
// Initialize the keys we use to check destructor ordering
59-
for (key, global) in KEYS.iter_mut().zip(GLOBALS.iter()) {
59+
for (key, global) in KEYS.iter_mut().zip(GLOBALS.iter_mut()) {
6060
*key = create(Some(mem::transmute(dtor as unsafe extern fn(*mut u64))));
6161
set(*key, global as *const _ as *mut _);
6262
}

0 commit comments

Comments
 (0)