From 19a5d5f3b4d4b51ed6b7537ae02e6f5768415728 Mon Sep 17 00:00:00 2001
From: Sebastian Poeplau <sebastian.poeplau@eurecom.fr>
Date: Mon, 25 Feb 2019 11:18:55 +0100
Subject: [PATCH 1/2] Add a rudimentary handler for mmap

We handle only fully concrete cases with certain flags, tailored to the CGC
binaries' use of the function. The memory manager now takes an optional address
for new memory objects; if it's specified, memory is allocated with mmap instead
of malloc, and the objects are marked as fixed-address objects to avoid attempts
to call free on the allocated memory.

This implementation is a bit of a hack and leaks memory in many cases, but it
should work for the CGC binaries.
---
 lib/Core/Executor.cpp               |  5 +-
 lib/Core/Executor.h                 |  3 +-
 lib/Core/MemoryManager.cpp          | 74 ++++++++++++++++++-----------
 lib/Core/MemoryManager.h            |  3 +-
 lib/Core/SpecialFunctionHandler.cpp | 73 ++++++++++++++++++++++++++++
 lib/Core/SpecialFunctionHandler.h   |  1 +
 tools/klee/main.cpp                 |  1 +
 7 files changed, 127 insertions(+), 33 deletions(-)

diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp
index 7a676cc..a36dce8 100644
--- a/lib/Core/Executor.cpp
+++ b/lib/Core/Executor.cpp
@@ -3395,7 +3395,8 @@ void Executor::executeAlloc(ExecutionState &state,
                             KInstruction *target,
                             bool zeroMemory,
                             const ObjectState *reallocFrom,
-                            size_t allocationAlignment) {
+                            size_t allocationAlignment,
+                            uint64_t address) {
   size = toUnique(state, size);
   if (ConstantExpr *CE = dyn_cast<ConstantExpr>(size)) {
     const llvm::Value *allocSite = state.prevPC->inst;
@@ -3404,7 +3405,7 @@ void Executor::executeAlloc(ExecutionState &state,
     }
     MemoryObject *mo =
         memory->allocate(CE->getZExtValue(), isLocal, /*isGlobal=*/false,
-                         allocSite, allocationAlignment);
+                         allocSite, allocationAlignment, address);
     if (!mo) {
       bindLocal(target, state, 
                 ConstantExpr::alloc(0, Context::get().getPointerWidth()));
diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h
index 66a28ba..e94c081 100644
--- a/lib/Core/Executor.h
+++ b/lib/Core/Executor.h
@@ -311,7 +311,8 @@ private:
                     KInstruction *target,
                     bool zeroMemory=false,
                     const ObjectState *reallocFrom=0,
-                    size_t allocationAlignment=0);
+                    size_t allocationAlignment=0,
+                    uint64_t address=0);
 
   /// Free the given address with checking for errors. If target is
   /// given it will be bound to 0 in the resulting states (this is a
diff --git a/lib/Core/MemoryManager.cpp b/lib/Core/MemoryManager.cpp
index dba90f8..5d8eeac 100644
--- a/lib/Core/MemoryManager.cpp
+++ b/lib/Core/MemoryManager.cpp
@@ -98,7 +98,8 @@ MemoryManager::~MemoryManager() {
 MemoryObject *MemoryManager::allocate(uint64_t size, bool isLocal,
                                       bool isGlobal,
                                       const llvm::Value *allocSite,
-                                      size_t alignment) {
+                                      size_t alignment,
+                                      uint64_t address) {
   if (size > 10 * 1024 * 1024)
     klee_warning_once(0, "Large alloc: %" PRIu64
                          " bytes.  KLEE may run out of memory.",
@@ -113,44 +114,59 @@ MemoryObject *MemoryManager::allocate(uint64_t size, bool isLocal,
     return 0;
   }
 
-  uint64_t address = 0;
-  if (DeterministicAllocation) {
+  bool isFixed;
+  if (!address) {
+    isFixed = false;
+
+    if (DeterministicAllocation) {
 #if LLVM_VERSION_CODE >= LLVM_VERSION(3, 9)
-    address = llvm::alignTo((uint64_t)nextFreeSlot + alignment - 1, alignment);
+      address = llvm::alignTo((uint64_t)nextFreeSlot + alignment - 1, alignment);
 #else
-    address = llvm::RoundUpToAlignment((uint64_t)nextFreeSlot + alignment - 1,
-                                       alignment);
+      address = llvm::RoundUpToAlignment((uint64_t)nextFreeSlot + alignment - 1,
+                                         alignment);
 #endif
 
-    // Handle the case of 0-sized allocations as 1-byte allocations.
-    // This way, we make sure we have this allocation between its own red zones
-    size_t alloc_size = std::max(size, (uint64_t)1);
-    if ((char *)address + alloc_size < deterministicSpace + spaceSize) {
-      nextFreeSlot = (char *)address + alloc_size + RedzoneSize;
-    } else {
-      klee_warning_once(0, "Couldn't allocate %" PRIu64
-                           " bytes. Not enough deterministic space left.",
-                        size);
-      address = 0;
-    }
-  } else {
-    // Use malloc for the standard case
-    if (alignment <= 8)
-      address = (uint64_t)malloc(size);
-    else {
-      int res = posix_memalign((void **)&address, alignment, size);
-      if (res < 0) {
-        klee_warning("Allocating aligned memory failed.");
+      // Handle the case of 0-sized allocations as 1-byte allocations.
+      // This way, we make sure we have this allocation between its own red zones
+      size_t alloc_size = std::max(size, (uint64_t)1);
+      if ((char *)address + alloc_size < deterministicSpace + spaceSize) {
+        nextFreeSlot = (char *)address + alloc_size + RedzoneSize;
+      } else {
+        klee_warning_once(0, "Couldn't allocate %" PRIu64
+                          " bytes. Not enough deterministic space left.",
+                          size);
         address = 0;
       }
+    } else {
+      // Use malloc for the standard case
+      if (alignment <= 8)
+        address = (uint64_t)malloc(size);
+      else {
+        int res = posix_memalign((void **)&address, alignment, size);
+        if (res < 0) {
+          klee_warning("Allocating aligned memory failed.");
+          address = 0;
+        }
+      }
     }
-  }
 
-  if (!address)
-    return 0;
+    if (!address)
+      return 0;
+  } else {
+    // We have a predetermined address
+    isFixed = true;
+    auto mmap_res = mmap((void *)address,
+                         size,
+                         PROT_READ | PROT_WRITE,
+                         MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+                         -1,
+                         0);
+    if (mmap_res == (void *)-1)
+      return 0;
+  }
 
   ++stats::allocations;
-  MemoryObject *res = new MemoryObject(address, size, isLocal, isGlobal, false,
+  MemoryObject *res = new MemoryObject(address, size, isLocal, isGlobal, isFixed,
                                        allocSite, this);
   objects.insert(res);
   return res;
diff --git a/lib/Core/MemoryManager.h b/lib/Core/MemoryManager.h
index d7233a8..da9929c 100644
--- a/lib/Core/MemoryManager.h
+++ b/lib/Core/MemoryManager.h
@@ -41,7 +41,8 @@ public:
    * memory.
    */
   MemoryObject *allocate(uint64_t size, bool isLocal, bool isGlobal,
-                         const llvm::Value *allocSite, size_t alignment);
+                         const llvm::Value *allocSite, size_t alignment,
+                         uint64_t address=0);
   MemoryObject *allocateFixed(uint64_t address, uint64_t size,
                               const llvm::Value *allocSite);
   void deallocate(const MemoryObject *mo);
diff --git a/lib/Core/SpecialFunctionHandler.cpp b/lib/Core/SpecialFunctionHandler.cpp
index 384481f..c1722e2 100644
--- a/lib/Core/SpecialFunctionHandler.cpp
+++ b/lib/Core/SpecialFunctionHandler.cpp
@@ -29,6 +29,7 @@
 
 #include <errno.h>
 #include <sstream>
+#include <sys/mman.h>
 
 using namespace llvm;
 using namespace klee;
@@ -110,6 +111,7 @@ static SpecialFunctionHandler::HandlerInfo handlerInfo[] = {
   add("klee_alias_function", handleAliasFunction, false),
   add("malloc", handleMalloc, true),
   add("memalign", handleMemalign, true),
+  add("mmap", handleMmap, true),
   add("realloc", handleRealloc, true),
 
   // operator delete[](void*)
@@ -460,6 +462,77 @@ void SpecialFunctionHandler::handleMemalign(ExecutionState &state,
                         alignment);
 }
 
+void SpecialFunctionHandler::handleMmap(ExecutionState &state,
+                                        KInstruction *target,
+                                        std::vector<ref<Expr>> &arguments) {
+  assert(arguments.size() == 6 && "invalid number of arguments to mmap");
+
+  auto addressExpr = dyn_cast<ConstantExpr>(arguments[0]);
+  if (!addressExpr) {
+    executor.terminateStateEarly(state, "mmap with symbolic address");
+    return;
+  }
+
+  uint64_t address = addressExpr->getZExtValue(64);
+
+  auto lengthExpr = dyn_cast<ConstantExpr>(arguments[1]);
+  if (!lengthExpr) {
+    executor.terminateStateEarly(state, "mmap with symbolic length");
+    return;
+  }
+
+  uint64_t length = lengthExpr->getZExtValue(64);
+
+  auto protExpr = dyn_cast<ConstantExpr>(arguments[2]);
+  if (!protExpr) {
+    executor.terminateStateEarly(state, "mmap with symbolic protection setting");
+    return;
+  }
+
+  int prot = protExpr->getZExtValue();
+  if (prot != (PROT_READ | PROT_WRITE)) {
+    executor.terminateStateEarly(state, "mmap with prot other than PROT_READ | PROT_WRITE");
+    return;
+  }
+
+  auto flagsExpr = dyn_cast<ConstantExpr>(arguments[3]);
+  if (!flagsExpr) {
+    executor.terminateStateEarly(state, "mmap with symbolic flags");
+    return;
+  }
+
+  uint64_t flags = flagsExpr->getZExtValue();
+  if (flags != (MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS)) {
+    executor.terminateStateEarly(state, "mmap with unsupported flag combination");
+    return;
+  }
+
+  auto fdExpr = dyn_cast<ConstantExpr>(arguments[4]);
+  if (!fdExpr) {
+    executor.terminateStateEarly(state, "mmap with symbolic file descriptor");
+    return;
+  }
+
+  uint64_t fd = fdExpr->getZExtValue();
+  if ((int)fd != -1) {
+    executor.terminateStateEarly(state, "mmap with file descriptor");
+    return;
+  }
+
+  auto offsetExpr = dyn_cast<ConstantExpr>(arguments[5]);
+  if (!offsetExpr) {
+    executor.terminateStateEarly(state, "mmap with symbolic offset");
+    return;
+  }
+
+  if (!offsetExpr->isZero()) {
+    executor.terminateStateEarly(state, "mmap with non-zero offset");
+    return;
+  }
+
+  executor.executeAlloc(state, arguments[1], false, target, false, nullptr, 0, address);
+}
+
 void SpecialFunctionHandler::handleAssume(ExecutionState &state,
                             KInstruction *target,
                             std::vector<ref<Expr> > &arguments) {
diff --git a/lib/Core/SpecialFunctionHandler.h b/lib/Core/SpecialFunctionHandler.h
index fdb6b6d..69bfd3c 100644
--- a/lib/Core/SpecialFunctionHandler.h
+++ b/lib/Core/SpecialFunctionHandler.h
@@ -120,6 +120,7 @@ namespace klee {
     HANDLER(handleMakeSymbolic);
     HANDLER(handleMalloc);
     HANDLER(handleMemalign);
+    HANDLER(handleMmap);
     HANDLER(handleMarkGlobal);
     HANDLER(handleOpenMerge);
     HANDLER(handleCloseMerge);
diff --git a/tools/klee/main.cpp b/tools/klee/main.cpp
index ee115de..6b33df0 100644
--- a/tools/klee/main.cpp
+++ b/tools/klee/main.cpp
@@ -803,6 +803,7 @@ static const char *modelledExternals[] = {
   "llvm.va_start",
   "llvm.va_end",
   "malloc",
+  "mmap",
   "realloc",
   "memalign",
   "_ZdaPv",
-- 
2.23.0

