From 99c522b14dbbf6b26be35b6e7bb8da7b29070287 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Wed, 8 Jun 2022 16:36:28 +0300 Subject: [PATCH] Inline asm external call --- include/klee/Module/KCallable.h | 71 +++++++++++++++++++ include/klee/Module/KModule.h | 16 ++++- lib/Core/Executor.cpp | 47 +++++++----- lib/Core/Executor.h | 3 +- lib/Core/ExternalDispatcher.cpp | 35 +++++---- lib/Core/ExternalDispatcher.h | 4 +- lib/Module/KModule.cpp | 4 +- test/Feature/InlineAsm.c | 25 +++++++ .../regression/2012-05-13-asm-causes-aborts.c | 9 --- test/regression/2022-06-28-asm-causes-error.c | 9 +++ tools/klee/main.cpp | 13 ---- 11 files changed, 176 insertions(+), 60 deletions(-) create mode 100644 include/klee/Module/KCallable.h create mode 100644 test/Feature/InlineAsm.c delete mode 100644 test/regression/2012-05-13-asm-causes-aborts.c create mode 100644 test/regression/2022-06-28-asm-causes-error.c diff --git a/include/klee/Module/KCallable.h b/include/klee/Module/KCallable.h new file mode 100644 index 0000000000..bf8b17eae2 --- /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 + +#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 9c24cb31fc..71fe8a0ace 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 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 11ad902e91..4240598205 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 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(fp)) { - terminateStateOnExecError(state, "inline assembly is unsupported"); - break; - } // evaluate arguments std::vector< ref > arguments; arguments.reserve(numArgs); @@ -2410,6 +2409,16 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { for (unsigned j=0; j(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(cast(f->getType())->getElementType()); @@ -3784,16 +3793,18 @@ static std::set okExternals(okExternalsList, void Executor::callExternalFunction(ExecutionState &state, KInstruction *target, - Function *function, + KCallable *callable, std::vector< ref > &arguments) { // check if specialFunctionHandler wants it - if (specialFunctionHandler->handle(state, function, target, arguments)) - return; + if (const auto *func = dyn_cast(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; igetValue(), "%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 e = ConstantExpr::fromMemory((void*) args, getWidthForLLVMType(resultType)); bindLocal(target, state, e); diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index 7da4f63cba..279d8bee00 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 @@ class Executor : public Interpreter { void callExternalFunction(ExecutionState &state, KInstruction *target, - llvm::Function *function, + KCallable *callable, std::vector< ref > &arguments); ObjectState *bindObjectInState(ExecutionState &state, const MemoryObject *mo, diff --git a/lib/Core/ExternalDispatcher.cpp b/lib/Core/ExternalDispatcher.cpp index 13b17337ff..7a0d8e148b 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 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 @@ class ExternalDispatcherImpl { 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(target) && !resolveSymbol(target->getName().str())) return 0; const CallBase &cb = cast(*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(args, args + i)); + llvm::CallInst *result; + if (auto* func = dyn_cast(target)) { + auto dispatchTarget = module->getOrInsertFunction(target->getName(), FTy, + func->function->getAttributes()); + result = Builder.CreateCall(dispatchTarget, + llvm::ArrayRef(args, args + i)); + } else if (auto* asmValue = dyn_cast(target)) { + result = Builder.CreateCall(asmValue->getInlineAsm(), + llvm::ArrayRef(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 7730ac4e0f..72e6faaa4b 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 @@ class ExternalDispatcher { * 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 294968a329..2e96c68a35 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(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 0000000000..25e0e72e7e --- /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 + +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 53fd0da902..0000000000 --- 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 0000000000..7b60a86592 --- /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 8dc2fd278f..d94cdc7695 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(it)) { - if (isa(ci->getCalledOperand())) { - klee_warning_once(&*fnIt, - "function \"%s\" has inline asm", - fnIt->getName().data()); - } - } - } - } } for (Module::const_global_iterator