about summary refs log tree commit diff homepage
diff options
context:
space:
mode:
authorAndrea Mattavelli <andreamattavelli@users.noreply.github.com>2017-02-21 22:23:52 +0000
committerGitHub <noreply@github.com>2017-02-21 22:23:52 +0000
commit62ee2e574b9f920e6679c91da25f8941552277d9 (patch)
treeb476d2df666340ad26f63d8fd3c4e07b2a248f11
parent27f414cc48bf086552ee4cba7784354c162a8301 (diff)
parent70715151746a24c4c6919292956111b00fcd3a26 (diff)
downloadklee-62ee2e574b9f920e6679c91da25f8941552277d9.tar.gz
Merge pull request #514 from delcypher/fix_klee_get_direct_call_bitcast_weak_alias
Fix assertion failure when klee::getDirectCallTarget() is used on call to bitcasted weak alias
-rw-r--r--include/klee/Internal/Support/ModuleUtil.h6
-rw-r--r--lib/Core/StatsTracker.cpp6
-rw-r--r--lib/Module/ModuleUtil.cpp42
-rw-r--r--test/regression/2016-11-24-bitcast-weak-alias.c43
4 files changed, 78 insertions, 19 deletions
diff --git a/include/klee/Internal/Support/ModuleUtil.h b/include/klee/Internal/Support/ModuleUtil.h
index 29adc94a..c85ba591 100644
--- a/include/klee/Internal/Support/ModuleUtil.h
+++ b/include/klee/Internal/Support/ModuleUtil.h
@@ -29,7 +29,11 @@ namespace klee {
   /// null if it cannot be determined (should be only for indirect
   /// calls, although complicated constant expressions might be
   /// another possibility).
-  llvm::Function *getDirectCallTarget(llvm::CallSite);
+  ///
+  /// If `moduleIsFullyLinked` is set to true it will be assumed that the
+  //  module containing the `llvm::CallSite` is fully linked. This assumption
+  //  allows resolution of functions that are marked as overridable.
+  llvm::Function *getDirectCallTarget(llvm::CallSite, bool moduleIsFullyLinked);
 
   /// Return true iff the given Function value is used in something
   /// other than a direct call (or a constant expression that
diff --git a/lib/Core/StatsTracker.cpp b/lib/Core/StatsTracker.cpp
index 97ed26ea..dbd86524 100644
--- a/lib/Core/StatsTracker.cpp
+++ b/lib/Core/StatsTracker.cpp
@@ -167,7 +167,8 @@ static bool instructionIsCoverable(Instruction *i) {
     } else {
       Instruction *prev = --it;
       if (isa<CallInst>(prev) || isa<InvokeInst>(prev)) {
-        Function *target = getDirectCallTarget(prev);
+        Function *target =
+            getDirectCallTarget(prev, /*moduleIsFullyLinked=*/true);
         if (target && target->doesNotReturn())
           return false;
       }
@@ -690,7 +691,8 @@ void StatsTracker::computeReachableUncovered() {
               // (which should be correct anyhow).
               callTargets.insert(std::make_pair(it,
                                                 std::vector<Function*>()));
-            } else if (Function *target = getDirectCallTarget(cs)) {
+            } else if (Function *target = getDirectCallTarget(
+                           cs, /*moduleIsFullyLinked=*/true)) {
               callTargets[it].push_back(target);
             } else {
               callTargets[it] = 
diff --git a/lib/Module/ModuleUtil.cpp b/lib/Module/ModuleUtil.cpp
index a5394067..2cd41c89 100644
--- a/lib/Module/ModuleUtil.cpp
+++ b/lib/Module/ModuleUtil.cpp
@@ -436,23 +436,33 @@ Module *klee::linkWithLibrary(Module *module,
 #endif
 }
 
-
-
-Function *klee::getDirectCallTarget(CallSite cs) {
+Function *klee::getDirectCallTarget(CallSite cs, bool moduleIsFullyLinked) {
   Value *v = cs.getCalledValue();
-  if (Function *f = dyn_cast<Function>(v)) {
-    return f;
-  } else if (llvm::ConstantExpr *ce = dyn_cast<llvm::ConstantExpr>(v)) {
-    if (ce->getOpcode()==Instruction::BitCast)
-      if (Function *f = dyn_cast<Function>(ce->getOperand(0)->stripPointerCasts()))
-        return f;
-
-    // NOTE: This assert may fire, it isn't necessarily a problem and
-    // can be disabled, I just wanted to know when and if it happened.
-    assert(0 && "FIXME: Unresolved direct target for a constant expression.");
-  }
-  
-  return 0;
+  bool viaConstantExpr = false;
+  // Walk through aliases and bitcasts to try to find
+  // the function being called.
+  do {
+    if (Function *f = dyn_cast<Function>(v)) {
+      return f;
+    } else if (llvm::GlobalAlias *ga = dyn_cast<GlobalAlias>(v)) {
+      if (moduleIsFullyLinked || !(ga->mayBeOverridden())) {
+        v = ga->getAliasee();
+      } else {
+        v = NULL;
+      }
+    } else if (llvm::ConstantExpr *ce = dyn_cast<llvm::ConstantExpr>(v)) {
+      viaConstantExpr = true;
+      v = ce->getOperand(0)->stripPointerCasts();
+    } else {
+      v = NULL;
+    }
+  } while (v != NULL);
+
+  // NOTE: This assert may fire, it isn't necessarily a problem and
+  // can be disabled, I just wanted to know when and if it happened.
+  assert((!viaConstantExpr) &&
+         "FIXME: Unresolved direct target for a constant expression");
+  return NULL;
 }
 
 static bool valueIsOnlyCalled(const Value *v) {
diff --git a/test/regression/2016-11-24-bitcast-weak-alias.c b/test/regression/2016-11-24-bitcast-weak-alias.c
new file mode 100644
index 00000000..f115731b
--- /dev/null
+++ b/test/regression/2016-11-24-bitcast-weak-alias.c
@@ -0,0 +1,43 @@
+// RUN: %llvmgcc %s -Wall -emit-llvm -g -O0 -c -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: klee --output-dir=%t.klee-out -exit-on-error -search=nurs:covnew %t.bc DUMMY_ARG >%t1.log 2>&1
+// RUN: FileCheck -input-file=%t1.log %s
+
+// This test case is designed to cover code in `klee::getDirectCallTarget(CallSite)`.
+// In particular it designed to test the case where a bitcasted function call, calls
+// a weak alias.
+struct v1 {
+  int c;
+  int d;
+};
+
+int __real_function(struct v1 *unused, struct v1 *unused2, int unused3) {
+  return 0;
+}
+
+struct v2 {
+  int e;
+  int f;
+};
+
+int alias_function(struct v1 *, struct v1 *, int)
+    __attribute__((weak, alias("__real_function")));
+
+int main(int argc, char** argv) {
+  struct v2 local = { .e= 0, .f=0 };
+  int choice = (argc == 1);
+  int number = 0;
+
+  // FIXME: Drop the guard when llvm 2.9 is dropped.
+  // Prevent actually making the call at runtime due to llvm-gcc
+  // injecting an abort if the call is made. The call is guarded
+  // in such a way that the compiler cannot remove the call.
+  if (choice) {
+    // Call via a bitcasted alias.
+    number = ((int (*)(struct v2 *, struct v2 *, int))alias_function)(
+        &local, &local, 0);
+  }
+  return number % 255;
+}
+
+// CHECK: KLEE: done: completed paths = 1