Skip to content

Commit 4f3fc85

Browse files
authored
Merge pull request #121 from oli-obk/master
fix `static mut` dealloc or freeze
2 parents 63cd994 + fbfd2d4 commit 4f3fc85

File tree

9 files changed

+126
-53
lines changed

9 files changed

+126
-53
lines changed

src/error.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ pub enum EvalError<'tcx> {
4949
AssumptionNotHeld,
5050
InlineAsm,
5151
TypeNotPrimitive(Ty<'tcx>),
52-
ReallocatedFrozenMemory,
53-
DeallocatedFrozenMemory,
52+
ReallocatedStaticMemory,
53+
DeallocatedStaticMemory,
5454
Layout(layout::LayoutError<'tcx>),
5555
Unreachable,
5656
ExpectedConcreteFunction(Function<'tcx>),
@@ -118,10 +118,10 @@ impl<'tcx> Error for EvalError<'tcx> {
118118
"cannot evaluate inline assembly",
119119
EvalError::TypeNotPrimitive(_) =>
120120
"expected primitive type, got nonprimitive",
121-
EvalError::ReallocatedFrozenMemory =>
122-
"tried to reallocate frozen memory",
123-
EvalError::DeallocatedFrozenMemory =>
124-
"tried to deallocate frozen memory",
121+
EvalError::ReallocatedStaticMemory =>
122+
"tried to reallocate static memory",
123+
EvalError::DeallocatedStaticMemory =>
124+
"tried to deallocate static memory",
125125
EvalError::Layout(_) =>
126126
"rustc layout computation failed",
127127
EvalError::UnterminatedCString(_) =>

src/eval_context.rs

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,11 @@ pub struct Frame<'tcx> {
100100
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
101101
pub enum StackPopCleanup {
102102
/// The stackframe existed to compute the initial value of a static/constant, make sure it
103-
/// isn't modifyable afterwards. The allocation of the result is frozen iff it's an
104-
/// actual allocation. `PrimVal`s are unmodifyable anyway.
105-
Freeze,
103+
/// isn't modifyable afterwards in case of constants.
104+
/// In case of `static mut`, mark the memory to ensure it's never marked as immutable through
105+
/// references or deallocated
106+
/// The bool decides whether the value is mutable (true) or not (false)
107+
MarkStatic(bool),
106108
/// A regular stackframe added due to a function call will need to get forwarded to the next
107109
/// block
108110
Goto(mir::BasicBlock),
@@ -170,7 +172,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
170172
// FIXME: cache these allocs
171173
let ptr = self.memory.allocate(s.len() as u64, 1)?;
172174
self.memory.write_bytes(ptr, s.as_bytes())?;
173-
self.memory.freeze(ptr.alloc_id)?;
175+
self.memory.mark_static(ptr.alloc_id, false)?;
174176
Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::from_u128(s.len() as u128)))
175177
}
176178

@@ -194,7 +196,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
194196
ByteStr(ref bs) => {
195197
let ptr = self.memory.allocate(bs.len() as u64, 1)?;
196198
self.memory.write_bytes(ptr, bs)?;
197-
self.memory.freeze(ptr.alloc_id)?;
199+
self.memory.mark_static(ptr.alloc_id, false)?;
198200
PrimVal::Ptr(ptr)
199201
}
200202

@@ -310,27 +312,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
310312
::log_settings::settings().indentation -= 1;
311313
let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none");
312314
match frame.return_to_block {
313-
StackPopCleanup::Freeze => if let Lvalue::Global(id) = frame.return_lvalue {
315+
StackPopCleanup::MarkStatic(mutable) => if let Lvalue::Global(id) = frame.return_lvalue {
314316
let global_value = self.globals.get_mut(&id)
315-
.expect("global should have been cached (freeze)");
317+
.expect("global should have been cached (static)");
316318
match global_value.value {
317-
Value::ByRef(ptr) => self.memory.freeze(ptr.alloc_id)?,
319+
Value::ByRef(ptr) => self.memory.mark_static(ptr.alloc_id, mutable)?,
318320
Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val {
319-
self.memory.freeze(ptr.alloc_id)?;
321+
self.memory.mark_static(ptr.alloc_id, mutable)?;
320322
},
321323
Value::ByValPair(val1, val2) => {
322324
if let PrimVal::Ptr(ptr) = val1 {
323-
self.memory.freeze(ptr.alloc_id)?;
325+
self.memory.mark_static(ptr.alloc_id, mutable)?;
324326
}
325327
if let PrimVal::Ptr(ptr) = val2 {
326-
self.memory.freeze(ptr.alloc_id)?;
328+
self.memory.mark_static(ptr.alloc_id, mutable)?;
327329
}
328330
},
329331
}
330332
assert!(global_value.mutable);
331-
global_value.mutable = false;
333+
global_value.mutable = mutable;
332334
} else {
333-
bug!("StackPopCleanup::Freeze on: {:?}", frame.return_lvalue);
335+
bug!("StackPopCleanup::MarkStatic on: {:?}", frame.return_lvalue);
334336
},
335337
StackPopCleanup::Goto(target) => self.goto_block(target),
336338
StackPopCleanup::None => {},
@@ -341,11 +343,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
341343
trace!("deallocating local");
342344
self.memory.dump_alloc(ptr.alloc_id);
343345
match self.memory.deallocate(ptr) {
344-
// Any frozen memory means that it belongs to a constant or something referenced
345-
// by a constant. We could alternatively check whether the alloc_id is frozen
346-
// before calling deallocate, but this is much simpler and is probably the
347-
// rare case.
348-
Ok(()) | Err(EvalError::DeallocatedFrozenMemory) => {},
346+
// We could alternatively check whether the alloc_id is static before calling
347+
// deallocate, but this is much simpler and is probably the rare case.
348+
Ok(()) | Err(EvalError::DeallocatedStaticMemory) => {},
349349
other => return other,
350350
}
351351
}
@@ -868,9 +868,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
868868
_ => {
869869
let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.substs)?;
870870
self.write_value_to_ptr(global_val.value, ptr, global_val.ty)?;
871-
if !global_val.mutable {
872-
self.memory.freeze(ptr.alloc_id)?;
873-
}
871+
self.memory.mark_static(ptr.alloc_id, global_val.mutable)?;
874872
let lval = self.globals.get_mut(&cid).expect("already checked");
875873
*lval = Global {
876874
value: Value::ByRef(ptr),

src/memory.rs

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,19 @@ pub struct Allocation {
3838
/// The alignment of the allocation to detect unaligned reads.
3939
pub align: u64,
4040
/// Whether the allocation may be modified.
41-
/// Use the `freeze` method of `Memory` to ensure that an error occurs, if the memory of this
42-
/// allocation is modified in the future.
43-
pub immutable: bool,
41+
/// Use the `mark_static` method of `Memory` to ensure that an error occurs, if the memory of this
42+
/// allocation is modified or deallocated in the future.
43+
pub static_kind: StaticKind,
44+
}
45+
46+
#[derive(Debug, PartialEq, Copy, Clone)]
47+
pub enum StaticKind {
48+
/// may be deallocated without breaking miri's invariants
49+
NotStatic,
50+
/// may be modified, but never deallocated
51+
Mutable,
52+
/// may neither be modified nor deallocated
53+
Immutable,
4454
}
4555

4656
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -272,7 +282,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
272282
relocations: BTreeMap::new(),
273283
undef_mask: UndefMask::new(size),
274284
align,
275-
immutable: false,
285+
static_kind: StaticKind::NotStatic,
276286
};
277287
let id = self.next_id;
278288
self.next_id.0 += 1;
@@ -290,8 +300,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
290300
if ptr.points_to_zst() {
291301
return self.allocate(new_size, align);
292302
}
293-
if self.get(ptr.alloc_id).map(|alloc| alloc.immutable).ok() == Some(true) {
294-
return Err(EvalError::ReallocatedFrozenMemory);
303+
if self.get(ptr.alloc_id).ok().map_or(false, |alloc| alloc.static_kind != StaticKind::NotStatic) {
304+
return Err(EvalError::ReallocatedStaticMemory);
295305
}
296306

297307
let size = self.get(ptr.alloc_id)?.bytes.len() as u64;
@@ -325,8 +335,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
325335
// TODO(solson): Report error about non-__rust_allocate'd pointer.
326336
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
327337
}
328-
if self.get(ptr.alloc_id).map(|alloc| alloc.immutable).ok() == Some(true) {
329-
return Err(EvalError::DeallocatedFrozenMemory);
338+
if self.get(ptr.alloc_id).ok().map_or(false, |alloc| alloc.static_kind != StaticKind::NotStatic) {
339+
return Err(EvalError::DeallocatedStaticMemory);
330340
}
331341

332342
if let Some(alloc) = self.alloc_map.remove(&ptr.alloc_id) {
@@ -430,8 +440,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
430440

431441
pub fn get_mut(&mut self, id: AllocId) -> EvalResult<'tcx, &mut Allocation> {
432442
match self.alloc_map.get_mut(&id) {
433-
Some(ref alloc) if alloc.immutable => Err(EvalError::ModifiedConstantMemory),
434-
Some(alloc) => Ok(alloc),
443+
Some(alloc) => match alloc.static_kind {
444+
StaticKind::Mutable |
445+
StaticKind::NotStatic => Ok(alloc),
446+
StaticKind::Immutable => Err(EvalError::ModifiedConstantMemory),
447+
},
435448
None => match self.functions.get(&id) {
436449
Some(_) => Err(EvalError::DerefFunctionPointer),
437450
None if id == NEVER_ALLOC_ID || id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess),
@@ -514,7 +527,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
514527
}
515528
}
516529

517-
let immutable = if alloc.immutable { " (immutable)" } else { "" };
530+
let immutable = match alloc.static_kind {
531+
StaticKind::Mutable => " (static mut)",
532+
StaticKind::Immutable => " (immutable)",
533+
StaticKind::NotStatic => "",
534+
};
518535
trace!("{}({} bytes){}", msg, alloc.bytes.len(), immutable);
519536

520537
if !relocations.is_empty() {
@@ -607,23 +624,28 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
607624

608625
/// Reading and writing
609626
impl<'a, 'tcx> Memory<'a, 'tcx> {
610-
pub fn freeze(&mut self, alloc_id: AllocId) -> EvalResult<'tcx> {
611-
// do not use `self.get_mut(alloc_id)` here, because we might have already frozen a
627+
/// mark an allocation as static, either mutable or not
628+
pub fn mark_static(&mut self, alloc_id: AllocId, mutable: bool) -> EvalResult<'tcx> {
629+
// do not use `self.get_mut(alloc_id)` here, because we might have already marked a
612630
// sub-element or have circular pointers (e.g. `Rc`-cycles)
613631
let relocations = match self.alloc_map.get_mut(&alloc_id) {
614-
Some(ref mut alloc) if !alloc.immutable => {
615-
alloc.immutable = true;
632+
Some(&mut Allocation { ref mut relocations, static_kind: ref mut kind @ StaticKind::NotStatic, .. }) => {
633+
*kind = if mutable {
634+
StaticKind::Mutable
635+
} else {
636+
StaticKind::Immutable
637+
};
616638
// take out the relocations vector to free the borrow on self, so we can call
617-
// freeze recursively
618-
mem::replace(&mut alloc.relocations, Default::default())
639+
// mark recursively
640+
mem::replace(relocations, Default::default())
619641
},
620642
None if alloc_id == NEVER_ALLOC_ID || alloc_id == ZST_ALLOC_ID => return Ok(()),
621643
None if !self.functions.contains_key(&alloc_id) => return Err(EvalError::DanglingPointerDeref),
622644
_ => return Ok(()),
623645
};
624646
// recurse into inner allocations
625647
for &alloc in relocations.values() {
626-
self.freeze(alloc)?;
648+
self.mark_static(alloc, mutable)?;
627649
}
628650
// put back the relocations
629651
self.alloc_map.get_mut(&alloc_id).expect("checked above").relocations = relocations;

src/step.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,7 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
156156
self.try(|this| {
157157
let mir = this.ecx.load_mir(def_id)?;
158158
this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty));
159-
let cleanup = if immutable && !mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe() {
160-
StackPopCleanup::Freeze
161-
} else {
162-
StackPopCleanup::None
163-
};
159+
let cleanup = StackPopCleanup::MarkStatic(!immutable || mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe());
164160
let name = ty::tls::with(|tcx| tcx.item_path_str(def_id));
165161
trace!("pushing stack frame for global: {}", name);
166162
this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Global(cid), cleanup, Vec::new())
@@ -213,7 +209,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
213209
mir,
214210
this.substs,
215211
Lvalue::Global(cid),
216-
StackPopCleanup::Freeze,
212+
StackPopCleanup::MarkStatic(false),
217213
Vec::new())
218214
});
219215
}

src/vtable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
112112
}
113113
}
114114

115-
self.memory.freeze(vtable.alloc_id)?;
115+
self.memory.mark_static(vtable.alloc_id, false)?;
116116

117117
Ok(vtable)
118118
}

tests/compile-fail/modifying_constants.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
fn main() {
2-
let x = &1; // the `&1` is promoted to a constant, but it used to be that only the pointer is frozen, not the pointee
2+
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
33
let y = unsafe { &mut *(x as *const i32 as *mut i32) };
44
*y = 42; //~ ERROR tried to modify constant memory
55
assert_eq!(*x, 42);

tests/run-pass/const-vec-of-fns.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// pretty-expanded FIXME #23616
12+
13+
/*!
14+
* Try to double-check that static fns have the right size (with or
15+
* without dummy env ptr, as appropriate) by iterating a size-2 array.
16+
* If the static size differs from the runtime size, the second element
17+
* should be read as a null or otherwise wrong pointer and crash.
18+
*/
19+
20+
fn f() { }
21+
static mut CLOSURES: &'static mut [fn()] = &mut [f as fn(), f as fn()];
22+
23+
pub fn main() {
24+
unsafe {
25+
for closure in &mut *CLOSURES {
26+
(*closure)()
27+
}
28+
}
29+
}

tests/run-pass/recursive_static.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![feature(static_recursion)]
2+
3+
struct S(&'static S);
4+
static S1: S = S(&S2);
5+
static S2: S = S(&S1);
6+
7+
fn main() {
8+
let p: *const S = S2.0;
9+
let q: *const S = &S1;
10+
assert_eq!(p, q);
11+
}

tests/run-pass/static_mut.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![allow(dead_code)]
2+
3+
static mut FOO: i32 = 42;
4+
static BAR: Foo = Foo(unsafe { &FOO as *const _} );
5+
6+
struct Foo(*const i32);
7+
8+
unsafe impl Sync for Foo {}
9+
10+
fn main() {
11+
unsafe {
12+
assert_eq!(*BAR.0, 42);
13+
FOO = 5;
14+
assert_eq!(FOO, 5);
15+
assert_eq!(*BAR.0, 5);
16+
}
17+
}

0 commit comments

Comments
 (0)