about summary refs log tree commit diff homepage
diff options
context:
space:
mode:
authorNguyễn Gia Phong <cnx@loang.net>2023-10-29 18:23:14 +0900
committerNguyễn Gia Phong <cnx@loang.net>2024-03-05 17:46:16 +0900
commitae1f642b1f04bce817fa66b92515da9459f1a4d0 (patch)
treec2c87047607579dbc0a741db5267327192e4acbd
parentbe10b1d4179db60fc1295be58626c3467c744efd (diff)
downloadklee-ae1f642b1f04bce817fa66b92515da9459f1a4d0.tar.gz
Implement return value detection
Values returned after reaching patch location from functions
in the specified source file are now automatically accounted
for differential test generation.
-rw-r--r--lib/Core/Executor.cpp32
-rw-r--r--lib/Core/Executor.h3
2 files changed, 35 insertions, 0 deletions
diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp
index 73dc21fa..4763fdb5 100644
--- a/lib/Core/Executor.cpp
+++ b/lib/Core/Executor.cpp
@@ -62,6 +62,7 @@
 #include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DebugInfoMetadata.h"
 #include "llvm/IR/Function.h"
 #include "llvm/IR/InlineAsm.h"
 #include "llvm/IR/Instructions.h"
@@ -135,6 +136,10 @@ cl::opt<bool> SingleObjectResolution(
     cl::desc("Try to resolve memory reads/writes to single objects "
              "when offsets are symbolic (default=false)"),
     cl::init(false), cl::cat(MiscCat));
+
+cl::opt<std::string> PatchedFile(
+    "patched-file",
+    cl::desc("Relative path to patched file"));
 } // namespace klee
 
 namespace {
@@ -2120,6 +2125,30 @@ Function *Executor::getTargetFunction(Value *calledVal) {
   }
 }
 
+void Executor::registerOutput(ExecutionState &state, Function *fn,
+    std::string var, Expr::Width bits, ref<Expr> val) {
+  if (state.patchLocs == 0 || fn->getName() == "__choose"
+      || (!PatchedFile.empty()
+          && fn->getSubprogram()->getFilename() != PatchedFile))
+    return;
+  size_t bytes = bits == Expr::Bool ? 1u : bits >> 3;
+  auto const inst = state.prevPC->inst;
+  auto const mo = this->memory->allocate(bytes, true, false,
+                                         &state, inst, bytes);
+  std::string baseName = "out!" + fn->getName().str() + "!" + var + "!";
+  std::string name;
+  unsigned id = 0;
+  do
+    name = baseName + llvm::utostr(id++);
+  while (!state.arrayNames.insert(name).second);
+  this->executeMakeSymbolic(state, mo, name);
+  // Cached symbolic array
+  auto const outArray = this->arrayCache.CreateArray(name, bytes);
+  auto const outVal = Expr::createTempRead(outArray, bits);
+  auto const eq = EqExpr::create(outVal, val);
+  this->addConstraint(state, eq);
+}
+
 void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
   Instruction *i = ki->inst;
   switch (i->getOpcode()) {
@@ -2206,6 +2235,9 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
             }
           }
 
+          auto const fp = cast<CallBase>(*caller).getCalledOperand();
+          if (auto const fn = this->getTargetFunction(fp))
+            registerOutput(state, fn, "ret", to, result);
           bindLocal(kcaller, state, result);
         }
       } else {
diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h
index 8b2a609c..a179c66d 100644
--- a/lib/Core/Executor.h
+++ b/lib/Core/Executor.h
@@ -250,6 +250,9 @@ private:
   ref<ConstantExpr> getEhTypeidFor(ref<Expr> type_info);
 
   llvm::Function* getTargetFunction(llvm::Value *calledVal);
+
+  void registerOutput(ExecutionState &state, llvm::Function *fn,
+                      std::string var, Expr::Width size, ref<Expr> val);
   
   void executeInstruction(ExecutionState &state, KInstruction *ki);