about summary refs log tree commit diff homepage
path: root/lib/Expr
diff options
context:
space:
mode:
authorDan Liew <daniel.liew@imperial.ac.uk>2015-12-16 18:13:11 +0000
committerDan Liew <daniel.liew@imperial.ac.uk>2015-12-18 11:22:50 +0000
commit53ff7a002a8213a5d5e778bef2a895998d9890e1 (patch)
tree026a4f1b16c1996755954f7824e0d10f8ed0ef8e /lib/Expr
parent7e75fa79b2e76251c2cd417a7eae8a7620b014ae (diff)
downloadklee-53ff7a002a8213a5d5e778bef2a895998d9890e1.tar.gz
Fix memory leaks of ``Array`` objects detected by ASan.
Some of these leaks were introduced by the factory constructor for Array
objects (f049ff3bc04daead8c3bb9f06e89e71e2054c82a) but a few others have
been around for far longer.

This leak was fixed by introducing a ``ArrayCache`` object which has two
purposes

* Retains ownership of all created ``Array`` objects and destroys them when
  the ``ArrayCache`` destructor is called.
* Mimic the caching behaviour for symbolic arrays that was introduced
  by f049ff3bc04daead8c3bb9f06e89e71e2054c82a where arrays with the same
  name and size get "uniqued".

The Executor now maintains a ``arrayCache`` member that it uses and
passes by pointer to objects that need to construct ``Array`` objects (i.e.
``ObjectState``). This way when the Executor is destroyed all the
``Array`` objects get freed which seems like the right time to do this.

For Kleaver the ``ParserImpl`` has a ``TheArrayCache`` member that is
used for building ``Array`` objects. This means that the Parser must
live as long as the built expressions will be used otherwise we will
have a use after free. I'm not sure this is the right design choice.
It might be better to transfer ownership of the ``Array`` objects to
the root ``Decl`` returned by the parser.
Diffstat (limited to 'lib/Expr')
-rw-r--r--lib/Expr/ArrayCache.cpp47
-rw-r--r--lib/Expr/Expr.cpp35
-rw-r--r--lib/Expr/Parser.cpp12
3 files changed, 55 insertions, 39 deletions
diff --git a/lib/Expr/ArrayCache.cpp b/lib/Expr/ArrayCache.cpp
new file mode 100644
index 00000000..b669f237
--- /dev/null
+++ b/lib/Expr/ArrayCache.cpp
@@ -0,0 +1,47 @@
+#include "klee/util/ArrayCache.h"
+
+namespace klee {
+
+ArrayCache::~ArrayCache() {
+  // Free Allocated Array objects
+  for (ArrayHashMap::iterator ai = cachedSymbolicArrays.begin(),
+                              e = cachedSymbolicArrays.end();
+       ai != e; ++ai) {
+    delete *ai;
+  }
+  for (ArrayPtrVec::iterator ai = concreteArrays.begin(),
+                             e = concreteArrays.end();
+       ai != e; ++ai) {
+    delete *ai;
+  }
+}
+
+const Array *
+ArrayCache::CreateArray(const std::string &_name, uint64_t _size,
+                        const ref<ConstantExpr> *constantValuesBegin,
+                        const ref<ConstantExpr> *constantValuesEnd,
+                        Expr::Width _domain, Expr::Width _range) {
+
+  const Array *array = new Array(_name, _size, constantValuesBegin,
+                                 constantValuesEnd, _domain, _range);
+  if (array->isSymbolicArray()) {
+    std::pair<ArrayHashMap::const_iterator, bool> success =
+        cachedSymbolicArrays.insert(array);
+    if (success.second) {
+      // Cache miss
+      return array;
+    }
+    // Cache hit
+    delete array;
+    array = *(success.first);
+    assert(array->isSymbolicArray() &&
+           "Cached symbolic array is no longer symbolic");
+    return array;
+  } else {
+    // Treat every constant array as distinct so we never cache them
+    assert(array->isConstantArray());
+    concreteArrays.push_back(array); // For deletion later
+    return array;
+  }
+}
+}
diff --git a/lib/Expr/Expr.cpp b/lib/Expr/Expr.cpp
index baa85663..2c64aff4 100644
--- a/lib/Expr/Expr.cpp
+++ b/lib/Expr/Expr.cpp
@@ -494,41 +494,6 @@ unsigned Array::computeHash() {
   hashValue = res;
   return hashValue; 
 }
-
-std::map<unsigned, std::vector<const Array *> *> Array::symbolicArraySingletonMap;
-
-const Array * Array::CreateArray(const std::string &_name, uint64_t _size,
-                                 const ref<ConstantExpr> *constantValuesBegin,
-                                 const ref<ConstantExpr> *constantValuesEnd,
-                                 Expr::Width _domain,
-				 Expr::Width _range) {
-
-  const Array * array = new Array(_name, _size, constantValuesBegin, constantValuesEnd, _domain,_range);
-  if (array->constantValues.size() == 0) { // symbolic array
-    unsigned hash = array->hash();
-    std::vector<const Array *> * bucket = Array::symbolicArraySingletonMap[hash];
-    if (bucket){
-      for (std::vector<const Array*>::const_iterator it = bucket->begin();
-	   it != bucket->end(); it ++){
-        const Array* prospect = *it;
-	if (prospect->size == array->size && prospect->name == array->name){
-    	  delete array;
-    	  return prospect;
-    	}
-      }
-      bucket->push_back(array);
-      return array;
-    } else {
-      bucket = new std::vector<const Array *>();
-      bucket->push_back(array);
-      Array::symbolicArraySingletonMap[hash] = bucket;
-      return array;
-    }
-  } else { // concrete array
-    return array;
-  }
-}
-
 /***/
 
 ref<Expr> ReadExpr::create(const UpdateList &ul, ref<Expr> index) {
diff --git a/lib/Expr/Parser.cpp b/lib/Expr/Parser.cpp
index 23a292fa..854f6d52 100644
--- a/lib/Expr/Parser.cpp
+++ b/lib/Expr/Parser.cpp
@@ -16,6 +16,7 @@
 #include "klee/ExprBuilder.h"
 #include "klee/Solver.h"
 #include "klee/util/ExprPPrinter.h"
+#include "klee/util/ArrayCache.h"
 
 #include "llvm/ADT/APInt.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -109,6 +110,7 @@ namespace {
     const std::string Filename;
     const MemoryBuffer *TheMemoryBuffer;
     ExprBuilder *Builder;
+    ArrayCache TheArrayCache;
 
     Lexer TheLexer;
     unsigned MaxErrors;
@@ -521,10 +523,10 @@ DeclResult ParserImpl::ParseArrayDecl() {
   const Identifier *Label = GetOrCreateIdentifier(Name);
   const Array *Root;
   if (!Values.empty())
-    Root = Array::CreateArray(Label->Name, Size.get(),
-			      &Values[0], &Values[0] + Values.size());
+    Root = TheArrayCache.CreateArray(Label->Name, Size.get(), &Values[0],
+                                     &Values[0] + Values.size());
   else
-    Root = Array::CreateArray(Label->Name, Size.get());
+    Root = TheArrayCache.CreateArray(Label->Name, Size.get());
   ArrayDecl *AD = new ArrayDecl(Label, Size.get(), 
                                 DomainType.get(), RangeType.get(), Root);
 
@@ -1306,7 +1308,9 @@ VersionResult ParserImpl::ParseVersionSpecifier() {
   VersionResult Res = ParseVersion();
   // Define update list to avoid use-of-undef errors.
   if (!Res.isValid()) {
-    Res = VersionResult(true, UpdateList(Array::CreateArray("", 0), NULL));
+    // FIXME: I'm not sure if this is right. Do we need a unique array here?
+    Res =
+        VersionResult(true, UpdateList(TheArrayCache.CreateArray("", 0), NULL));
   }
   
   if (Label)