From 080d3e435519e2665502c8bae6c3168ae4232e33 Mon Sep 17 00:00:00 2001
From: Oliver Schneider <git-spam-no-reply9815368754983@oli-obk.de>
Date: Wed, 8 Feb 2017 16:27:28 +0100
Subject: [PATCH] properly prevent recursive statics from marking each other

---
 src/eval_context.rs | 16 +++++++++-------
 src/memory.rs       | 24 ++++++++++++++++++++----
 src/vtable.rs       |  2 +-
 3 files changed, 30 insertions(+), 12 deletions(-)

diff --git a/src/eval_context.rs b/src/eval_context.rs
index 7ea4003b0f7..dedb8a53b71 100644
--- a/src/eval_context.rs
+++ b/src/eval_context.rs
@@ -172,7 +172,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         // FIXME: cache these allocs
         let ptr = self.memory.allocate(s.len() as u64, 1)?;
         self.memory.write_bytes(ptr, s.as_bytes())?;
-        self.memory.mark_static(ptr.alloc_id, false)?;
+        self.memory.mark_static_initalized(ptr.alloc_id, false)?;
         Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::from_u128(s.len() as u128)))
     }
 
@@ -194,9 +194,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
             Str(ref s) => return self.str_to_value(s),
 
             ByteStr(ref bs) => {
+                // FIXME: cache these allocs
                 let ptr = self.memory.allocate(bs.len() as u64, 1)?;
                 self.memory.write_bytes(ptr, bs)?;
-                self.memory.mark_static(ptr.alloc_id, false)?;
+                self.memory.mark_static_initalized(ptr.alloc_id, false)?;
                 PrimVal::Ptr(ptr)
             }
 
@@ -316,16 +317,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                 let global_value = self.globals.get_mut(&id)
                     .expect("global should have been cached (static)");
                 match global_value.value {
-                    Value::ByRef(ptr) => self.memory.mark_static(ptr.alloc_id, mutable)?,
+                    Value::ByRef(ptr) => self.memory.mark_static_initalized(ptr.alloc_id, mutable)?,
                     Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val {
-                        self.memory.mark_static(ptr.alloc_id, mutable)?;
+                        self.memory.mark_static_initalized(ptr.alloc_id, mutable)?;
                     },
                     Value::ByValPair(val1, val2) => {
                         if let PrimVal::Ptr(ptr) = val1 {
-                            self.memory.mark_static(ptr.alloc_id, mutable)?;
+                            self.memory.mark_static_initalized(ptr.alloc_id, mutable)?;
                         }
                         if let PrimVal::Ptr(ptr) = val2 {
-                            self.memory.mark_static(ptr.alloc_id, mutable)?;
+                            self.memory.mark_static_initalized(ptr.alloc_id, mutable)?;
                         }
                     },
                 }
@@ -870,10 +871,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                     Value::ByRef(ptr) => Lvalue::from_ptr(ptr),
                     _ => {
                         let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.substs)?;
+                        self.memory.mark_static(ptr.alloc_id);
                         self.write_value_to_ptr(global_val.value, ptr, global_val.ty)?;
                         // see comment on `initialized` field
                         if global_val.initialized {
-                            self.memory.mark_static(ptr.alloc_id, global_val.mutable)?;
+                            self.memory.mark_static_initalized(ptr.alloc_id, global_val.mutable)?;
                         }
                         let lval = self.globals.get_mut(&cid).expect("already checked");
                         *lval = Global {
diff --git a/src/memory.rs b/src/memory.rs
index 8f0c3a5622c..6dee7ce49a1 100644
--- a/src/memory.rs
+++ b/src/memory.rs
@@ -38,7 +38,7 @@ pub struct Allocation {
     /// The alignment of the allocation to detect unaligned reads.
     pub align: u64,
     /// Whether the allocation may be modified.
-    /// Use the `mark_static` method of `Memory` to ensure that an error occurs, if the memory of this
+    /// Use the `mark_static_initalized` method of `Memory` to ensure that an error occurs, if the memory of this
     /// allocation is modified or deallocated in the future.
     pub static_kind: StaticKind,
 }
@@ -152,6 +152,11 @@ impl<'tcx> Function<'tcx> {
 pub struct Memory<'a, 'tcx> {
     /// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations)
     alloc_map: HashMap<AllocId, Allocation>,
+    /// Set of statics, constants, promoteds, vtables, ... to prevent `mark_static_initalized` from stepping
+    /// out of its own allocations.
+    /// This set only contains statics backed by an allocation. If they are ByVal or ByValPair they
+    /// are not here, but will be inserted once they become ByRef.
+    static_alloc: HashSet<AllocId>,
     /// Number of virtual bytes allocated
     memory_usage: u64,
     /// Maximum number of virtual bytes that may be allocated
@@ -189,6 +194,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
             memory_size: max_memory,
             memory_usage: 0,
             packed: BTreeSet::new(),
+            static_alloc: HashSet::new(),
         }
     }
 
@@ -624,8 +630,15 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
 
 /// Reading and writing
 impl<'a, 'tcx> Memory<'a, 'tcx> {
-    /// mark an allocation as static, either mutable or not
-    pub fn mark_static(&mut self, alloc_id: AllocId, mutable: bool) -> EvalResult<'tcx> {
+    /// mark an allocation as being the entry point to a static (see `static_alloc` field)
+    pub fn mark_static(&mut self, alloc_id: AllocId) {
+        if !self.static_alloc.insert(alloc_id) {
+            bug!("tried to mark an allocation ({:?}) as static twice", alloc_id);
+        }
+    }
+
+    /// mark an allocation as static and initialized, either mutable or not
+    pub fn mark_static_initalized(&mut self, alloc_id: AllocId, mutable: bool) -> EvalResult<'tcx> {
         // do not use `self.get_mut(alloc_id)` here, because we might have already marked a
         // sub-element or have circular pointers (e.g. `Rc`-cycles)
         let relocations = match self.alloc_map.get_mut(&alloc_id) {
@@ -645,7 +658,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
         };
         // recurse into inner allocations
         for &alloc in relocations.values() {
-            self.mark_static(alloc, mutable)?;
+            // relocations into other statics are not "inner allocations"
+            if !self.static_alloc.contains(&alloc) {
+                self.mark_static_initalized(alloc, mutable)?;
+            }
         }
         // put back the relocations
         self.alloc_map.get_mut(&alloc_id).expect("checked above").relocations = relocations;
diff --git a/src/vtable.rs b/src/vtable.rs
index 0b43b349443..bfae608ecbc 100644
--- a/src/vtable.rs
+++ b/src/vtable.rs
@@ -112,7 +112,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
             }
         }
 
-        self.memory.mark_static(vtable.alloc_id, false)?;
+        self.memory.mark_static_initalized(vtable.alloc_id, false)?;
 
         Ok(vtable)
     }