about summary refs log tree commit diff homepage
diff options
context:
space:
mode:
authorFrank Busse <bb0xfb@gmail.com>2017-10-13 15:26:05 +0100
committerMartinNowack <martin.nowack@gmail.com>2018-05-01 15:34:38 +0200
commit76e4240f6e17ab1f17200c603cf827b2bb28458d (patch)
tree118bc50bf6ecbfe1c275411829095e9faaaf4c1a
parent3d00adf56f866a802c857682b382d11972a727a1 (diff)
downloadklee-76e4240f6e17ab1f17200c603cf827b2bb28458d.tar.gz
add blockaddress and indirectbr instructions
-rw-r--r--lib/Core/Executor.cpp75
-rw-r--r--lib/Core/ExecutorUtil.cpp5
-rw-r--r--test/Feature/AddressOfLabels.c56
-rw-r--r--test/Feature/AddressOfLabelsSymbolic.c38
4 files changed, 146 insertions, 28 deletions
diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp
index 794b5980..81b9ec3c 100644
--- a/lib/Core/Executor.cpp
+++ b/lib/Core/Executor.cpp
@@ -1526,6 +1526,79 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
     }
     break;
   }
+  case Instruction::IndirectBr: {
+    // implements indirect branch to a label within the current function
+    const auto bi = cast<IndirectBrInst>(i);
+    auto address = eval(ki, 0, state).value;
+    address = toUnique(state, address);
+
+    // concrete address
+    if (const auto CE = dyn_cast<ConstantExpr>(address.get())) {
+      const auto bb_address = (BasicBlock *) CE->getZExtValue(Context::get().getPointerWidth());
+      transferToBasicBlock(bb_address, bi->getParent(), state);
+      break;
+    }
+
+    // symbolic address
+    const auto numDestinations = bi->getNumDestinations();
+    std::vector<BasicBlock *> targets;
+    targets.reserve(numDestinations);
+    std::vector<ref<Expr>> expressions;
+    expressions.reserve(numDestinations);
+
+    ref<Expr> errorCase = ConstantExpr::alloc(1, Expr::Bool);
+    SmallPtrSet<BasicBlock *, 5> destinations;
+    // collect and check destinations from label list
+    for (unsigned k = 0; k < numDestinations; ++k) {
+      // filter duplicates
+      const auto d = bi->getDestination(k);
+      if (destinations.count(d)) continue;
+      destinations.insert(d);
+
+      // create address expression
+      const auto PE = Expr::createPointer((uint64_t) (unsigned long) (void *) d);
+      ref<Expr> e = EqExpr::create(address, PE);
+
+      // exclude address from errorCase
+      errorCase = AndExpr::create(errorCase, Expr::createIsZero(e));
+
+      // check feasibility
+      bool result;
+      bool success __attribute__ ((unused)) = solver->mayBeTrue(state, e, result);
+      assert(success && "FIXME: Unhandled solver failure");
+      if (result) {
+        targets.push_back(d);
+        expressions.push_back(e);
+      }
+    }
+    // check errorCase feasibility
+    bool result;
+    bool success __attribute__ ((unused)) = solver->mayBeTrue(state, errorCase, result);
+    assert(success && "FIXME: Unhandled solver failure");
+    if (result) {
+      expressions.push_back(errorCase);
+    }
+
+    // fork states
+    std::vector<ExecutionState *> branches;
+    branch(state, expressions, branches);
+
+    // terminate error state
+    if (result) {
+      terminateStateOnExecError(*branches.back(), "indirectbr: illegal label address");
+      branches.pop_back();
+    }
+
+    // branch states to resp. target blocks
+    assert(targets.size() == branches.size());
+    for (std::vector<ExecutionState *>::size_type k = 0; k < branches.size(); ++k) {
+      if (branches[k]) {
+        transferToBasicBlock(targets[k], bi->getParent(), *branches[k]);
+      }
+    }
+
+    break;
+  }
   case Instruction::Switch: {
     SwitchInst *si = cast<SwitchInst>(i);
     ref<Expr> cond = eval(ki, 0, state).value;
@@ -1641,7 +1714,7 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
       }
     }
     break;
- }
+  }
   case Instruction::Unreachable:
     // Note that this is not necessarily an internal bug, llvm will
     // generate unreachable instructions in cases where it knows the
diff --git a/lib/Core/ExecutorUtil.cpp b/lib/Core/ExecutorUtil.cpp
index 62d65c31..a0f4de3d 100644
--- a/lib/Core/ExecutorUtil.cpp
+++ b/lib/Core/ExecutorUtil.cpp
@@ -119,6 +119,11 @@ namespace klee {
         assert(isa<ConstantExpr>(res) &&
                "result of constant vector built is not a constant");
         return cast<ConstantExpr>(res);
+      } else if (const BlockAddress * ba = dyn_cast<BlockAddress>(c)) {
+        // return the address of the specified basic block in the specified function
+        const auto arg_bb = (BasicBlock *) ba->getOperand(1);
+        const auto res = Expr::createPointer((uint64_t) (unsigned long) (void *) arg_bb);
+        return cast<ConstantExpr>(res);
       } else {
         std::string msg("Cannot handle constant ");
         llvm::raw_string_ostream os(msg);
diff --git a/test/Feature/AddressOfLabels.c b/test/Feature/AddressOfLabels.c
index 84493fd8..6d807dbb 100644
--- a/test/Feature/AddressOfLabels.c
+++ b/test/Feature/AddressOfLabels.c
@@ -1,36 +1,38 @@
 // RUN: %llvmgcc -emit-llvm -g -c %s -o %t.bc
 // RUN: rm -rf %t.klee-out
-// RUN: not %klee --output-dir=%t.klee-out %t.bc 2> %t.log
-// RUN: FileCheck --input-file %t.log %s
+// RUN: %klee --output-dir=%t.klee-out %t.bc > %t.log
+// RUN: FileCheck --input-file=%t.log %s
 
-/* 
-   This currently tests that KLEE clearly reports that it doesn't
-   support taking the address of the labels, in particular
-   blockaddress constants.
+#include <stdio.h>
 
-   The test would need to be changes once support for this feature is
-   added. 
-*/
+int main(int argc, char * argv[]) {
+	// prevent CFGSimplificationPass optimisation
+	void * dummy = &&one;
+	switch(argc) {
+		case 1: break;
+		case 2:
+			dummy = &&three;
+			goto *dummy;
+		default:
+			goto *dummy;
+	}
 
-#include <stdio.h>
+	// the real test
+	void * lptr = &&two;
+	goto *lptr;
 
-int main (int argc, char** argv)
-{
-  int i = 1;
-  // CHECK: Cannot handle constant 'i8* blockaddress 
-  // CHECK: AddressOfLabels.c:[[@LINE+1]]
-  void * lptr = &&one;
+one:
+	puts("Label one");
+// CHECK-NOT: Label one
+	return 1;
 
-  if (argc > 1)
-    lptr = &&two;
-  
-  goto *lptr;
+two:
+	puts("Label two");
+// CHECK: Label two
+	return 0;
 
- one:
-  printf("argc is 1\n");
-  return 0;
-  
- two:
-  printf("argc is >1\n");
-  return 0;
+three:
+	puts("Label three");
+// CHECK-NOT: Label three
+	return 1;
 }
diff --git a/test/Feature/AddressOfLabelsSymbolic.c b/test/Feature/AddressOfLabelsSymbolic.c
new file mode 100644
index 00000000..2c7e39c7
--- /dev/null
+++ b/test/Feature/AddressOfLabelsSymbolic.c
@@ -0,0 +1,38 @@
+// RUN: %llvmgcc -emit-llvm -g -c %s -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out %t.bc > %t.log 2> %t.stderr.log
+// RUN: FileCheck %s -check-prefix=CHECK-MSG --input-file=%t.log
+// RUN: FileCheck %s -check-prefix=CHECK-ERR --input-file=%t.stderr.log
+
+#include <stdio.h>
+
+int main(void) {
+	void * lptr = &&one;
+	lptr = &&two;
+	lptr = &&three;
+
+	klee_make_symbolic(&lptr, sizeof(lptr), "lptr");
+// CHECK-ERR: KLEE: ERROR: {{.*}} indirectbr: illegal label address
+	goto *lptr;
+
+one:
+	puts("Label one");
+// CHECK-MSG-DAG: Label one
+	return 0;
+
+two:
+	puts("Label two");
+// CHECK-MSG-DAG: Label two
+	return 0;
+
+three:
+	puts("Label three");
+// CHECK-MSG-DAG: Label three
+	return 0;
+
+// LLVM should infer only {one, two, three} as valid targets
+four:
+	puts("Label four");
+// CHECK-MSG-NOT: Label four
+	return 0;
+}