about summary refs log tree commit diff homepage
diff options
context:
space:
mode:
-rw-r--r--include/klee/Module/KCallable.h71
-rw-r--r--include/klee/Module/KModule.h16
-rw-r--r--lib/Core/Executor.cpp47
-rw-r--r--lib/Core/Executor.h3
-rw-r--r--lib/Core/ExternalDispatcher.cpp35
-rw-r--r--lib/Core/ExternalDispatcher.h4
-rw-r--r--lib/Module/KModule.cpp4
-rw-r--r--test/Feature/InlineAsm.c25
-rw-r--r--test/regression/2012-05-13-asm-causes-aborts.c9
-rw-r--r--test/regression/2022-06-28-asm-causes-error.c9
-rw-r--r--tools/klee/main.cpp13
11 files changed, 176 insertions, 60 deletions
diff --git a/include/klee/Module/KCallable.h b/include/klee/Module/KCallable.h
new file mode 100644
index 00000000..bf8b17ea
--- /dev/null
+++ b/include/klee/Module/KCallable.h
@@ -0,0 +1,71 @@
+//===-- KCallable.h ---------------------------------------------*- C++ -*-===//
+//
+//                     The KLEE Symbolic Virtual Machine
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef KLEE_KCALLABLE_H
+#define KLEE_KCALLABLE_H
+
+#include <string>
+
+#include "llvm/ADT/Twine.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/InlineAsm.h"
+#include "llvm/IR/LLVMContext.h"
+
+namespace klee {
+/// Wrapper for callable objects passed in callExternalFunction
+class KCallable {
+public:
+  enum CallableKind { CK_Function, CK_InlineAsm };
+
+private:
+  const CallableKind Kind;
+
+public:
+  KCallable(CallableKind Kind) : Kind(Kind) {}
+
+  CallableKind getKind() const { return Kind; }
+
+  virtual llvm::StringRef getName() const = 0;
+  virtual llvm::PointerType *getType() const = 0;
+  virtual llvm::Value *getValue() = 0;
+
+  virtual ~KCallable() = default;
+};
+
+class KInlineAsm : public KCallable {
+private:
+  static unsigned getFreshAsmId() {
+    static unsigned globalId = 0;
+    return globalId++;
+  }
+
+  llvm::InlineAsm *value;
+  std::string name;
+
+public:
+  KInlineAsm(llvm::InlineAsm *value)
+      : KCallable(CK_InlineAsm), value(value),
+        name("__asm__" + llvm::Twine(getFreshAsmId()).str()) {}
+
+  llvm::StringRef getName() const override { return name; }
+
+  llvm::PointerType *getType() const override { return value->getType(); }
+
+  llvm::Value *getValue() override { return value; }
+
+  static bool classof(const KCallable *callable) {
+    return callable->getKind() == CK_InlineAsm;
+  }
+
+  llvm::InlineAsm *getInlineAsm() { return value; }
+};
+
+} // namespace klee
+
+#endif /* KLEE_KCALLABLE_H */
diff --git a/include/klee/Module/KModule.h b/include/klee/Module/KModule.h
index 9c24cb31..71fe8a0a 100644
--- a/include/klee/Module/KModule.h
+++ b/include/klee/Module/KModule.h
@@ -12,6 +12,7 @@
 
 #include "klee/Config/Version.h"
 #include "klee/Core/Interpreter.h"
+#include "klee/Module/KCallable.h"
 
 #include "llvm/ADT/ArrayRef.h"
 
@@ -39,7 +40,7 @@ namespace klee {
   class KModule;
   template<class T> class ref;
 
-  struct KFunction {
+  struct KFunction : public KCallable {
     llvm::Function *function;
 
     unsigned numArgs, numRegisters;
@@ -53,14 +54,23 @@ namespace klee {
     /// "coverable" for statistics and search heuristics.
     bool trackCoverage;
 
-  public:
-    explicit KFunction(llvm::Function*, KModule *);
+    explicit KFunction(llvm::Function*, KModule*);
     KFunction(const KFunction &) = delete;
     KFunction &operator=(const KFunction &) = delete;
 
     ~KFunction();
 
     unsigned getArgRegister(unsigned index) { return index; }
+
+    llvm::StringRef getName() const override { return function->getName(); }
+
+    llvm::PointerType *getType() const override { return function->getType(); }
+
+    llvm::Value *getValue() override { return function; }
+
+    static bool classof(const KCallable *callable) {
+      return callable->getKind() == CK_Function;
+    }
   };
 
 
diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp
index 11ad902e..42405982 100644
--- a/lib/Core/Executor.cpp
+++ b/lib/Core/Executor.cpp
@@ -37,6 +37,7 @@
 #include "klee/Expr/ExprUtil.h"
 #include "klee/Module/Cell.h"
 #include "klee/Module/InstructionInfoTable.h"
+#include "klee/Module/KCallable.h"
 #include "klee/Module/KInstruction.h"
 #include "klee/Module/KModule.h"
 #include "klee/Solver/Common.h"
@@ -59,6 +60,7 @@
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/Function.h"
+#include "llvm/IR/InlineAsm.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/LLVMContext.h"
@@ -1658,10 +1660,11 @@ void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f,
     return;
   if (f && f->isDeclaration()) {
     switch (f->getIntrinsicID()) {
-    case Intrinsic::not_intrinsic:
+    case Intrinsic::not_intrinsic: {
       // state may be destroyed by this call, cannot touch
-      callExternalFunction(state, ki, f, arguments);
+      callExternalFunction(state, ki, kmodule->functionMap[f], arguments);
       break;
+    }
     case Intrinsic::fabs: {
       ref<ConstantExpr> arg =
           toConstant(state, arguments[0], "floating point");
@@ -2399,10 +2402,6 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
     unsigned numArgs = cb.arg_size();
     Function *f = getTargetFunction(fp, state);
 
-    if (isa<InlineAsm>(fp)) {
-      terminateStateOnExecError(state, "inline assembly is unsupported");
-      break;
-    }
     // evaluate arguments
     std::vector< ref<Expr> > arguments;
     arguments.reserve(numArgs);
@@ -2410,6 +2409,16 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
     for (unsigned j=0; j<numArgs; ++j)
       arguments.push_back(eval(ki, j+1, state).value);
 
+    if (auto* asmValue = dyn_cast<InlineAsm>(fp)) { //TODO: move to `executeCall`
+      if (ExternalCalls != ExternalCallPolicy::None) {
+        KInlineAsm callable(asmValue);
+        callExternalFunction(state, ki, &callable, arguments);
+      } else {
+        terminateStateOnExecError(state, "external calls disallowed (in particular inline asm)");
+      }
+      break;
+    }
+
     if (f) {
       const FunctionType *fType = 
         dyn_cast<FunctionType>(cast<PointerType>(f->getType())->getElementType());
@@ -3784,16 +3793,18 @@ static std::set<std::string> okExternals(okExternalsList,
 
 void Executor::callExternalFunction(ExecutionState &state,
                                     KInstruction *target,
-                                    Function *function,
+                                    KCallable *callable,
                                     std::vector< ref<Expr> > &arguments) {
   // check if specialFunctionHandler wants it
-  if (specialFunctionHandler->handle(state, function, target, arguments))
-    return;
+  if (const auto *func = dyn_cast<KFunction>(callable)) {
+    if (specialFunctionHandler->handle(state, func->function, target, arguments))
+      return;
+  }
 
   if (ExternalCalls == ExternalCallPolicy::None &&
-      !okExternals.count(function->getName().str())) {
+      !okExternals.count(callable->getName().str())) {
     klee_warning("Disallowed call to external function: %s\n",
-               function->getName().str().c_str());
+               callable->getName().str().c_str());
     terminateStateOnUserError(state, "external calls disallowed");
     return;
   }
@@ -3835,7 +3846,7 @@ void Executor::callExternalFunction(ExecutionState &state,
       } else {
         terminateStateOnExecError(state,
                                   "external call with symbolic argument: " +
-                                  function->getName());
+                                  callable->getName());
         return;
       }
     }
@@ -3856,7 +3867,7 @@ void Executor::callExternalFunction(ExecutionState &state,
   if (!errnoValue) {
     terminateStateOnExecError(state,
                               "external call with errno value symbolic: " +
-                                  function->getName());
+                                  callable->getName());
     return;
   }
 
@@ -3868,7 +3879,7 @@ void Executor::callExternalFunction(ExecutionState &state,
 
     std::string TmpStr;
     llvm::raw_string_ostream os(TmpStr);
-    os << "calling external: " << function->getName().str() << "(";
+    os << "calling external: " << callable->getName().str() << "(";
     for (unsigned i=0; i<arguments.size(); i++) {
       os << arguments[i];
       if (i != arguments.size()-1)
@@ -3879,12 +3890,12 @@ void Executor::callExternalFunction(ExecutionState &state,
     if (AllExternalWarnings)
       klee_warning("%s", os.str().c_str());
     else
-      klee_warning_once(function, "%s", os.str().c_str());
+      klee_warning_once(callable->getValue(), "%s", os.str().c_str());
   }
 
-  bool success = externalDispatcher->executeCall(function, target->inst, args);
+  bool success = externalDispatcher->executeCall(callable, target->inst, args);
   if (!success) {
-    terminateStateOnError(state, "failed external call: " + function->getName(),
+    terminateStateOnError(state, "failed external call: " + callable->getName(),
                           StateTerminationType::External);
     return;
   }
@@ -3903,7 +3914,7 @@ void Executor::callExternalFunction(ExecutionState &state,
 #endif
 
   Type *resultType = target->inst->getType();
-  if (resultType != Type::getVoidTy(function->getContext())) {
+  if (resultType != Type::getVoidTy(kmodule->module->getContext())) {
     ref<Expr> e = ConstantExpr::fromMemory((void*) args, 
                                            getWidthForLLVMType(resultType));
     bindLocal(target, state, e);
diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h
index 7da4f63c..279d8bee 100644
--- a/lib/Core/Executor.h
+++ b/lib/Core/Executor.h
@@ -64,6 +64,7 @@ namespace klee {
   class ExternalDispatcher;
   class Expr;
   class InstructionInfoTable;
+  class KCallable;
   struct KFunction;
   struct KInstruction;
   class KInstIterator;
@@ -240,7 +241,7 @@ private:
 
   void callExternalFunction(ExecutionState &state,
                             KInstruction *target,
-                            llvm::Function *function,
+                            KCallable *callable,
                             std::vector< ref<Expr> > &arguments);
 
   ObjectState *bindObjectInState(ExecutionState &state, const MemoryObject *mo,
diff --git a/lib/Core/ExternalDispatcher.cpp b/lib/Core/ExternalDispatcher.cpp
index 13b17337..7a0d8e14 100644
--- a/lib/Core/ExternalDispatcher.cpp
+++ b/lib/Core/ExternalDispatcher.cpp
@@ -9,10 +9,13 @@
 
 #include "ExternalDispatcher.h"
 #include "klee/Config/Version.h"
+#include "klee/Module/KCallable.h"
+#include "klee/Module/KModule.h"
 
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InlineAsm.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Module.h"
@@ -45,7 +48,7 @@ class ExternalDispatcherImpl {
 private:
   typedef std::map<const llvm::Instruction *, llvm::Function *> dispatchers_ty;
   dispatchers_ty dispatchers;
-  llvm::Function *createDispatcher(llvm::Function *f, llvm::Instruction *i,
+  llvm::Function *createDispatcher(KCallable *target, llvm::Instruction *i,
                                    llvm::Module *module);
   llvm::ExecutionEngine *executionEngine;
   LLVMContext &ctx;
@@ -59,7 +62,7 @@ private:
 public:
   ExternalDispatcherImpl(llvm::LLVMContext &ctx);
   ~ExternalDispatcherImpl();
-  bool executeCall(llvm::Function *function, llvm::Instruction *i,
+  bool executeCall(KCallable *callable, llvm::Instruction *i,
                    uint64_t *args);
   void *resolveSymbol(const std::string &name);
   int getLastErrno();
@@ -153,7 +156,7 @@ ExternalDispatcherImpl::~ExternalDispatcherImpl() {
   // we don't need to delete any of them.
 }
 
-bool ExternalDispatcherImpl::executeCall(Function *f, Instruction *i,
+bool ExternalDispatcherImpl::executeCall(KCallable *callable, Instruction *i,
                                          uint64_t *args) {
   dispatchers_ty::iterator it = dispatchers.find(i);
   if (it != dispatchers.end()) {
@@ -180,7 +183,7 @@ bool ExternalDispatcherImpl::executeCall(Function *f, Instruction *i,
   // The MCJIT generates whole modules at a time so for every call that we
   // haven't made before we need to create a new Module.
   dispatchModule = new Module(getFreshModuleID(), ctx);
-  dispatcher = createDispatcher(f, i, dispatchModule);
+  dispatcher = createDispatcher(callable, i, dispatchModule);
   dispatchers.insert(std::make_pair(i, dispatcher));
 
   // Force the JIT execution engine to go ahead and build the function. This
@@ -249,10 +252,10 @@ bool ExternalDispatcherImpl::runProtectedCall(Function *f, uint64_t *args) {
 // the special cases that the JIT knows how to directly call. If this is not
 // done, then the jit will end up generating a nullary stub just to call our
 // stub, for every single function call.
-Function *ExternalDispatcherImpl::createDispatcher(Function *target,
+Function *ExternalDispatcherImpl::createDispatcher(KCallable *target,
                                                    Instruction *inst,
                                                    Module *module) {
-  if (!resolveSymbol(target->getName().str()))
+  if (isa<KFunction>(target) && !resolveSymbol(target->getName().str()))
     return 0;
 
   const CallBase &cb = cast<CallBase>(*inst);
@@ -309,10 +312,18 @@ Function *ExternalDispatcherImpl::createDispatcher(Function *target,
     idx += ((!!argSize ? argSize : 64) + 63) / 64;
   }
 
-  auto dispatchTarget = module->getOrInsertFunction(target->getName(), FTy,
-                                                    target->getAttributes());
-  auto result = Builder.CreateCall(dispatchTarget,
-                                   llvm::ArrayRef<Value *>(args, args + i));
+  llvm::CallInst *result;
+  if (auto* func = dyn_cast<KFunction>(target)) {
+    auto dispatchTarget = module->getOrInsertFunction(target->getName(), FTy,
+                                                      func->function->getAttributes());
+    result = Builder.CreateCall(dispatchTarget,
+                                llvm::ArrayRef<Value *>(args, args + i));
+  } else if (auto* asmValue = dyn_cast<KInlineAsm>(target)) {
+    result = Builder.CreateCall(asmValue->getInlineAsm(),
+                                llvm::ArrayRef<Value *>(args, args + i));
+  } else {
+    assert(0 && "Unhandled KCallable derived class");
+  }
   if (result->getType() != Type::getVoidTy(ctx)) {
     auto resp = Builder.CreateBitCast(
         argI64s, PointerType::getUnqual(result->getType()));
@@ -336,9 +347,9 @@ ExternalDispatcher::ExternalDispatcher(llvm::LLVMContext &ctx)
 
 ExternalDispatcher::~ExternalDispatcher() { delete impl; }
 
-bool ExternalDispatcher::executeCall(llvm::Function *function,
+bool ExternalDispatcher::executeCall(KCallable *callable,
                                      llvm::Instruction *i, uint64_t *args) {
-  return impl->executeCall(function, i, args);
+  return impl->executeCall(callable, i, args);
 }
 
 void *ExternalDispatcher::resolveSymbol(const std::string &name) {
diff --git a/lib/Core/ExternalDispatcher.h b/lib/Core/ExternalDispatcher.h
index 7730ac4e..72e6faaa 100644
--- a/lib/Core/ExternalDispatcher.h
+++ b/lib/Core/ExternalDispatcher.h
@@ -20,11 +20,11 @@
 namespace llvm {
 class Instruction;
 class LLVMContext;
-class Function;
 }
 
 namespace klee {
 class ExternalDispatcherImpl;
+class KCallable;
 class ExternalDispatcher {
 private:
   ExternalDispatcherImpl *impl;
@@ -37,7 +37,7 @@ public:
    * ci with arguments in args[1], args[2], ... and writing the result
    * into args[0].
    */
-  bool executeCall(llvm::Function *function, llvm::Instruction *i,
+  bool executeCall(KCallable *callable, llvm::Instruction *i,
                    uint64_t *args);
   void *resolveSymbol(const std::string &name);
 
diff --git a/lib/Module/KModule.cpp b/lib/Module/KModule.cpp
index 294968a3..2e96c68a 100644
--- a/lib/Module/KModule.cpp
+++ b/lib/Module/KModule.cpp
@@ -303,7 +303,6 @@ void KModule::manifest(InterpreterHandler *ih, bool forceSourceOutput) {
   for (auto &Function : *module) {
     if (Function.isDeclaration()) {
       declarations.push_back(&Function);
-      continue;
     }
 
     auto kf = std::unique_ptr<KFunction>(new KFunction(&Function, this));
@@ -406,7 +405,8 @@ static int getOperandNum(Value *v,
 
 KFunction::KFunction(llvm::Function *_function,
                      KModule *km) 
-  : function(_function),
+  : KCallable(CK_Function),
+    function(_function),
     numArgs(function->arg_size()),
     numInstructions(0),
     trackCoverage(true) {
diff --git a/test/Feature/InlineAsm.c b/test/Feature/InlineAsm.c
new file mode 100644
index 00000000..25e0e72e
--- /dev/null
+++ b/test/Feature/InlineAsm.c
@@ -0,0 +1,25 @@
+// RUN: %clang %s -emit-llvm %O0opt -c -g -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --external-calls=all --exit-on-error --output-dir=%t.klee-out %t.bc > %t.output.log 2>&1
+
+#include <assert.h>
+
+int main() {
+
+  int x;
+  klee_make_symbolic(&x, sizeof(x), "x");
+  if (x == 239) {
+    __asm__("neg %0"
+            : "+r"(x));
+    assert(x == -239);
+  }
+
+  int y = x;
+  __asm__("add $5, %0"
+          : "+r"(x));
+  __asm__("add $0, %0"
+          : "+r"(y));
+  assert(x == y + 5);
+
+  return 0;
+}
diff --git a/test/regression/2012-05-13-asm-causes-aborts.c b/test/regression/2012-05-13-asm-causes-aborts.c
deleted file mode 100644
index 53fd0da9..00000000
--- a/test/regression/2012-05-13-asm-causes-aborts.c
+++ /dev/null
@@ -1,9 +0,0 @@
-// RUN: %clang %s -emit-llvm %O0opt -c -o %t1.bc
-// RUN: rm -rf %t.klee-out
-// RUN: %klee --output-dir=%t.klee-out %t1.bc
-
-int main(int argc, char *argv[]){
-	__asm__ __volatile__ ("movl %eax, %eax");
-	return 0;
-}
-
diff --git a/test/regression/2022-06-28-asm-causes-error.c b/test/regression/2022-06-28-asm-causes-error.c
new file mode 100644
index 00000000..7b60a865
--- /dev/null
+++ b/test/regression/2022-06-28-asm-causes-error.c
@@ -0,0 +1,9 @@
+// RUN: %clang %s -g -emit-llvm %O0opt -c -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --external-calls=none --output-dir=%t.klee-out %t1.bc 2>&1 | FileCheck %s
+
+int main(int argc, char *argv[]) {
+  // CHECK: 2022-06-28-asm-causes-error.c:[[@LINE+1]]: external calls disallowed (in particular inline asm)
+  __asm__ __volatile__("movl %eax, %eax");
+  return 0;
+}
diff --git a/tools/klee/main.cpp b/tools/klee/main.cpp
index 8dc2fd27..d94cdc76 100644
--- a/tools/klee/main.cpp
+++ b/tools/klee/main.cpp
@@ -909,19 +909,6 @@ void externalsAndGlobalsCheck(const llvm::Module *m) {
        fnIt != fn_ie; ++fnIt) {
     if (fnIt->isDeclaration() && !fnIt->use_empty())
       externals.insert(std::make_pair(fnIt->getName(), false));
-    for (Function::const_iterator bbIt = fnIt->begin(), bb_ie = fnIt->end();
-         bbIt != bb_ie; ++bbIt) {
-      for (BasicBlock::const_iterator it = bbIt->begin(), ie = bbIt->end();
-           it != ie; ++it) {
-        if (const CallInst *ci = dyn_cast<CallInst>(it)) {
-          if (isa<InlineAsm>(ci->getCalledOperand())) {
-            klee_warning_once(&*fnIt,
-                              "function \"%s\" has inline asm",
-                              fnIt->getName().data());
-          }
-        }
-      }
-    }
   }
 
   for (Module::const_global_iterator