From 85d7abf67ff922a7c7c4b7c7c21cecd968ba1423 Mon Sep 17 00:00:00 2001 From: Sergey Morozov Date: Mon, 1 Jul 2024 15:44:53 +0200 Subject: [PATCH] [fix] Model objects for ctype_* functions for 32-bit executables. Handle ConstantPointer instead of ConstantExpr where it is required. Removed propagating of operations in SelectExpr (caused division on 0 for *Div operations). [fix] Model return value of ctype_* functions as pointers. [fix] Allow to resolve SelectExpr of ConsantExprs on all memory objects in address space. [fix] DebugInfo may not point to any address. [fix] Do not optimize SelectExpr for *Div operations, terminate state on call to raise function. [refactor] Logic for writing in CType objects put in lambdas. [test] Added tests for propagating select through division, calling to raise and for resolve of symbolic pointer represeneted as a select by a symbolic condition. [refactor] addCType* functions moved to private methods for better readability, optimization on sifting up selects during construction of expressions moved to seperate method also for better readability. --- lib/Core/AddressSpace.cpp | 19 ++ lib/Core/Executor.cpp | 240 +++++++++++++----- lib/Core/Executor.h | 38 ++- lib/Core/SpecialFunctionHandler.cpp | 36 +++ lib/Core/SpecialFunctionHandler.h | 5 + lib/Expr/Expr.cpp | 103 ++++---- lib/Module/LocalVarDeclarationFinderPass.cpp | 2 +- test/regression/2024-07-08-call-to-raise.c | 17 ++ .../2024-07-08-division-in-select.c | 15 ++ .../regression/2024-07-08-resolve-of-select.c | 27 ++ 10 files changed, 372 insertions(+), 130 deletions(-) create mode 100644 test/regression/2024-07-08-call-to-raise.c create mode 100644 test/regression/2024-07-08-division-in-select.c create mode 100644 test/regression/2024-07-08-resolve-of-select.c diff --git a/lib/Core/AddressSpace.cpp b/lib/Core/AddressSpace.cpp index d92c5bc094..d95b8f9e0a 100644 --- a/lib/Core/AddressSpace.cpp +++ b/lib/Core/AddressSpace.cpp @@ -13,6 +13,7 @@ #include "Memory.h" #include "TimingSolver.h" +#include "klee/Expr/ArrayExprVisitor.h" #include "klee/Expr/Expr.h" #include "klee/Module/KType.h" #include "klee/Statistics/TimerStatIncrementer.h" @@ -199,6 +200,24 @@ class ResolvePredicate { timestamp = moBasePair.first->timestamp; } } + // This is hack to deal with the poineters, which are represented as + // select expressions from constant pointers with symbolic condition. + // in such case we allow to resolve in all objects in the address space, + // but should forbid lazy initilization. + if (isa(base)) { + std::vector> alternatives; + ArrayExprHelper::collectAlternatives(cast(*base), + alternatives); + if (std::find_if_not(alternatives.begin(), alternatives.end(), + [](ref expr) { + return isa(expr); + }) == alternatives.end()) { + skipNotSymbolicObjects = false; + skipNotLazyInitialized = false; + skipLocal = false; + skipGlobal = false; + } + } } bool operator()(const MemoryObject *mo, const ObjectState *os) const { diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 20652188c2..b37ac868c8 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -125,9 +125,14 @@ DISABLE_WARNING_POP #include #include #include +#include #include #include +#ifdef HAVE_CTYPE_EXTERNALS +#include +#endif + using namespace llvm; using namespace klee; @@ -813,9 +818,22 @@ void Executor::initializeGlobalObject(ExecutionState &state, ObjectState *os, } } -MemoryObject *Executor::addExternalObject(ExecutionState &state, void *addr, - KType *type, unsigned size, - bool isReadOnly) { +ObjectPair Executor::addExternalObjectAsNonStatic(ExecutionState &state, + KType *type, unsigned size, + bool isReadOnly) { + auto mo = + allocate(state, Expr::createPointer(size), false, true, nullptr, 8, type); + mo->isFixed = true; + + auto os = bindObjectInState(state, mo, type, false); + os->setReadOnly(isReadOnly); + + return ObjectPair{mo, os}; +} + +ObjectPair Executor::addExternalObject(ExecutionState &state, const void *addr, + KType *type, unsigned size, + bool isReadOnly) { auto mo = memory->allocateFixed(reinterpret_cast(addr), size, nullptr, type); ObjectState *os = bindObjectInState(state, mo, type, false); @@ -827,7 +845,7 @@ MemoryObject *Executor::addExternalObject(ExecutionState &state, void *addr, } if (isReadOnly) os->setReadOnly(true); - return mo; + return {mo, os}; } extern void *__dso_handle __attribute__((__weak__)); @@ -846,9 +864,92 @@ void Executor::initializeGlobals(ExecutionState &state) { initializeGlobalObjects(state); } +#ifdef HAVE_CTYPE_EXTERNALS +static const std::size_t kCTypeMemSize = 384; +static const std::size_t kCTypeMemOffset = 128; + +template +decltype(auto) Executor::addCTypeFixedObject(ExecutionState &state, + int addressSpaceNum, Module &m, + F objectProvider) { + using underlying_t = decltype(**objectProvider()); + + static_assert(std::is_integral_v>, + "ctype structure expected to contain integral variables"); + + // Obtain types for required object + llvm::Type *type = + llvm::IntegerType::get(m.getContext(), sizeof(underlying_t) * CHAR_BIT); + llvm::Type *pointerType = llvm::PointerType::get(type, addressSpaceNum); + + auto ctypeObj = + addExternalObject(state, + const_cast(reinterpret_cast( + *objectProvider() - kCTypeMemOffset)), + typeSystemManager->getWrappedType(type), + kCTypeMemSize * sizeof(underlying_t), true); + + auto ctypePointerObj = addExternalObject( + state, objectProvider(), typeSystemManager->getWrappedType(pointerType), + Context::get().getPointerWidthInBytes(), true); + state.addressSpace + .getWriteable(ctypePointerObj.first, ctypePointerObj.second) + ->write(0, + ConstantPointerExpr::create( + ctypeObj.first->getBaseExpr(), + AddExpr::create(ctypeObj.first->getBaseExpr(), + Expr::createPointer(kCTypeMemOffset * + sizeof(underlying_t))))); + return objectProvider(); +} + +template +decltype(auto) Executor::addCTypeModelledObject(ExecutionState &state, + int addressSpaceNum, Module &m, + F objectProvider) { + using underlying_t = decltype(**objectProvider()); + + static_assert(std::is_integral_v>, + "ctype structure expected to contain integral variables"); + // Obtain types for required object + llvm::Type *type = + llvm::IntegerType::get(m.getContext(), sizeof(underlying_t) * CHAR_BIT); + llvm::Type *pointerType = llvm::PointerType::get(type, addressSpaceNum); + + // Allocate memory + auto ctypeObj = addExternalObjectAsNonStatic( + state, typeSystemManager->getWrappedType(type), + kCTypeMemSize * sizeof(underlying_t), true); + auto ctypePointerObj = addExternalObjectAsNonStatic( + state, typeSystemManager->getWrappedType(pointerType), + Context::get().getPointerWidthInBytes(), true); + + // Write address of memory into pointer + state.addressSpace + .getWriteable(ctypePointerObj.first, ctypePointerObj.second) + ->write(0, AddExpr::create(ctypeObj.first->getBaseExpr(), + Expr::createPointer(kCTypeMemOffset * + sizeof(underlying_t)))); + + // Write content from actiual ctype into modelled memory + ref seg = cast(ctypeObj.first->getBaseExpr()); + auto addr = + reinterpret_cast(*objectProvider() - kCTypeMemOffset); + for (unsigned i = 0; i < kCTypeMemSize * sizeof(underlying_t); i++) { + ref byte = ConstantExpr::create(addr[i], Expr::Int8); + state.addressSpace.getWriteable(ctypeObj.first, ctypeObj.second) + ->write(i, PointerExpr::create(seg, byte)); + } + + // Return address to pointer + return reinterpret_cast( + cast(ctypePointerObj.first->getBaseExpr())->getZExtValue()); +}; +#endif + void Executor::allocateGlobalObjects(ExecutionState &state) { Module *m = kmodule->module.get(); - unsigned int adressSpaceNum = kmodule->targetData->getAllocaAddrSpace(); + unsigned int addressSpaceNum = kmodule->targetData->getAllocaAddrSpace(); if (m->getModuleInlineAsm() != "") klee_warning("executable has module level assembly (ignoring)"); @@ -889,18 +990,14 @@ void Executor::allocateGlobalObjects(ExecutionState &state) { llvm::Type *pointerErrnoAddr = llvm::PointerType::get( llvm::IntegerType::get(m->getContext(), sizeof(*errno_addr) * CHAR_BIT), - adressSpaceNum); - MemoryObject *errnoObj = nullptr; + addressSpaceNum); + const MemoryObject *errnoObj = nullptr; if (Context::get().getPointerWidth() == 32) { - errnoObj = allocate(state, Expr::createPointer(sizeof(*errno_addr)), false, - true, nullptr, 8, - typeSystemManager->getWrappedType(pointerErrnoAddr)); - errnoObj->isFixed = true; - - bindObjectInState(state, errnoObj, - typeSystemManager->getWrappedType(pointerErrnoAddr), - false); + errnoObj = addExternalObjectAsNonStatic( + state, typeSystemManager->getWrappedType(pointerErrnoAddr), + sizeof(*errno_addr), false) + .first; errno_addr = reinterpret_cast( cast(errnoObj->getBaseExpr())->getZExtValue()); } else { @@ -908,53 +1005,44 @@ void Executor::allocateGlobalObjects(ExecutionState &state) { errnoObj = addExternalObject(state, (void *)errno_addr, typeSystemManager->getWrappedType(pointerErrnoAddr), - sizeof *errno_addr, false); + sizeof *errno_addr, false) + .first; } // Copy values from and to program space explicitly - errnoObj->isUserSpecified = true; + const_cast(errnoObj)->isUserSpecified = true; #endif // Disabled, we don't want to promote use of live externals. #ifdef HAVE_CTYPE_EXTERNALS #ifndef WINDOWS #ifndef DARWIN + /* from /usr/include/ctype.h: - These point into arrays of 384, so they can be indexed by any `unsigned - char' value [0,255]; by EOF (-1); or by any `signed char' value - [-128,-1). ISO C requires that the ctype functions work for `unsigned */ - const uint16_t **addr = __ctype_b_loc(); - - llvm::Type *pointerAddr = llvm::PointerType::get( - llvm::IntegerType::get(m->getContext(), sizeof(**addr) * CHAR_BIT), - adressSpaceNum); - addExternalObject(state, const_cast(*addr - 128), - typeSystemManager->getWrappedType(pointerAddr), - 384 * sizeof **addr, true); - addExternalObject(state, addr, typeSystemManager->getWrappedType(pointerAddr), - sizeof(*addr), true); - - const int32_t **lowerAddr = __ctype_tolower_loc(); - llvm::Type *pointerLowerAddr = llvm::PointerType::get( - llvm::IntegerType::get(m->getContext(), sizeof(**lowerAddr) * CHAR_BIT), - adressSpaceNum); - addExternalObject(state, const_cast(*lowerAddr - 128), - typeSystemManager->getWrappedType(pointerLowerAddr), - 384 * sizeof **lowerAddr, true); - addExternalObject(state, lowerAddr, - typeSystemManager->getWrappedType(pointerLowerAddr), - sizeof(*lowerAddr), true); - - const int32_t **upper_addr = __ctype_toupper_loc(); - llvm::Type *pointerUpperAddr = llvm::PointerType::get( - llvm::IntegerType::get(m->getContext(), sizeof(**upper_addr) * CHAR_BIT), - 0); - addExternalObject(state, const_cast(*upper_addr - 128), - typeSystemManager->getWrappedType(pointerUpperAddr), - 384 * sizeof **upper_addr, true); - addExternalObject(state, upper_addr, - typeSystemManager->getWrappedType(pointerUpperAddr), - sizeof(*upper_addr), true); + These point into arrays of 384, so they can be indexed by any + `unsigned char' value [0,255]; by EOF (-1); or by any `signed char' value + [-128,-1). ISO C requires that the ctype functions work for + `unsigned */ + + // We assume that KLEE is running on 64-bit platform. + // Therefore, for 32-bit binaries we can not just use addresses + // of actual ctype* objects in memory. + // Hence, we need to model then in address space. + if (Context::get().getPointerWidth() == 32) { + c_type_b_loc_addr = + addCTypeModelledObject(state, addressSpaceNum, *m, __ctype_b_loc); + c_type_tolower_addr = + addCTypeModelledObject(state, addressSpaceNum, *m, __ctype_tolower_loc); + c_type_toupper_addr = + addCTypeModelledObject(state, addressSpaceNum, *m, __ctype_toupper_loc); + } else { + c_type_b_loc_addr = + addCTypeFixedObject(state, addressSpaceNum, *m, __ctype_b_loc); + c_type_tolower_addr = + addCTypeFixedObject(state, addressSpaceNum, *m, __ctype_tolower_loc); + c_type_toupper_addr = + addCTypeFixedObject(state, addressSpaceNum, *m, __ctype_toupper_loc); + } #endif #endif #endif @@ -2113,7 +2201,6 @@ ref Executor::getEhTypeidFor(ref type_info) { void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f, std::vector> &arguments) { - Instruction *i = ki->inst(); if (isa_and_nonnull(i)) return; @@ -4307,9 +4394,20 @@ void Executor::computeOffsetsSeqTy(KGEPInstruction *kgepi, kmodule->targetData->getTypeStoreSize(it->getContainedType(0)); const Value *operand = it.getOperand(); if (const Constant *c = dyn_cast(operand)) { - ref index = - cast(evalConstant(c, llvm::APFloat::rmNearestTiesToEven)) - ->SExt(Context::get().getPointerWidth()); + auto expr = evalConstant(c, llvm::APFloat::rmNearestTiesToEven); + ref index = nullptr; + if (expr->getKind() == Expr::Constant) { + index = cast( + evalConstant(c, llvm::APFloat::rmNearestTiesToEven)) + ->SExt(Context::get().getPointerWidth()); + } else { + assert(expr->getKind() == Expr::ConstantPointer); + index = cast( + cast( + evalConstant(c, llvm::APFloat::rmNearestTiesToEven)) + ->getValue()) + ->SExt(Context::get().getPointerWidth()); + } ref addend = index->Mul( ConstantExpr::alloc(elementSize, Context::get().getPointerWidth())); constantOffset = constantOffset->Add(addend); @@ -5120,6 +5218,12 @@ void Executor::callExternalFunction(ExecutionState &state, KInstruction *target, std::vector> &arguments) { // check if specialFunctionHandler wants it if (const auto *func = dyn_cast(callable)) { + if (func->getName() == "raise") { + terminateStateOnError(state, "Call to \"raise\" is unmodelled", + StateTerminationType::Model); + return; + } + if (specialFunctionHandler->handle(state, func->function(), target, arguments)) return; @@ -5605,7 +5709,6 @@ void Executor::concretizeSize(ExecutionState &state, ref size, bool zeroMemory, const ObjectState *reallocFrom, size_t allocationAlignment, bool checkOutOfMemory) { - // XXX For now we just pick a size. Ideally we would support // symbolic sizes fully but even if we don't it would be better to // "smartly" pick a value, for example we could fork and pick the @@ -5744,7 +5847,6 @@ MemoryObject *Executor::allocate(ExecutionState &state, ref size, ref conditionExpr, ref lazyInitializationSource, unsigned timestamp, bool isSymbolic) { - size = ZExtExpr::create(size, Context::get().getPointerWidth()); /* Try to find existing solution. */ @@ -5895,17 +5997,23 @@ bool Executor::resolveMemoryObjects( bool checkAddress = readBase && readBase->updates.getSize() == 0 && readBase->updates.root->isSymbolicArray(); if (!checkAddress && isa(base)) { - checkAddress = true; std::vector> alternatives; ArrayExprHelper::collectAlternatives(*cast(base), alternatives); - for (auto alt : alternatives) { - if (isa(alt)) { - continue; + checkAddress = std::find_if(alternatives.begin(), alternatives.end(), + [](ref expr) { + return !isa(expr); + }) != alternatives.end(); + + if (checkAddress) { + for (auto alt : alternatives) { + if (isa(alt)) { + continue; + } + readBase = alt->hasOrderedReads(); + checkAddress &= readBase && readBase->updates.getSize() == 0 && + readBase->updates.root->isSymbolicArray(); } - readBase = alt->hasOrderedReads(); - checkAddress &= readBase && readBase->updates.getSize() == 0 && - readBase->updates.root->isSymbolicArray(); } } @@ -5971,7 +6079,6 @@ bool Executor::checkResolvedMemoryObjects( std::vector> &resolveConditions, std::vector> &unboundConditions, ref &checkOutOfBounds, bool &mayBeOutOfBound) { - ref base = address->getBase(); ref basePointer = PointerExpr::create(base, base); unsigned size = bytes; @@ -7119,7 +7226,6 @@ unsigned Executor::getSymbolicPathStreamID(const ExecutionState &state) { void Executor::getConstraintLog(const ExecutionState &state, std::string &res, Interpreter::LogType logFormat) { - switch (logFormat) { case STP: { auto query = state.toQuery(); diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index eb769d1831..b007f6ff3b 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -55,6 +55,10 @@ DISABLE_WARNING_POP #include #include +#ifdef HAVE_CTYPE_EXTERNALS +#include +#endif + struct KTest; namespace llvm { @@ -129,6 +133,12 @@ class Executor : public Interpreter { private: int *errno_addr; +#ifdef HAVE_CTYPE_EXTERNALS + decltype(__ctype_b_loc()) c_type_b_loc_addr; + decltype(__ctype_tolower_loc()) c_type_tolower_addr; + decltype(__ctype_toupper_loc()) c_type_toupper_addr; +#endif + size_t maxNewWriteableOSSize = 0; size_t maxNewStateStackSize = 0; @@ -267,8 +277,21 @@ class Executor : public Interpreter { // Given a concrete object in our [klee's] address space, add it to // objects checked code can reference. - MemoryObject *addExternalObject(ExecutionState &state, void *addr, KType *, - unsigned size, bool isReadOnly); + ObjectPair addExternalObject(ExecutionState &state, const void *addr, KType *, + unsigned size, bool isReadOnly); + ObjectPair addExternalObjectAsNonStatic(ExecutionState &state, KType *, + unsigned size, bool isReadOnly); + +#ifdef HAVE_CTYPE_EXTERNALS + template + decltype(auto) addCTypeFixedObject(ExecutionState &state, int addressSpaceNum, + llvm::Module &m, F objectProvider); + + template + decltype(auto) addCTypeModelledObject(ExecutionState &state, + int addressSpaceNum, llvm::Module &m, + F objectProvider); +#endif void initializeGlobalAlias(const llvm::Constant *c, ExecutionState &state); void initializeGlobalObject(ExecutionState &state, ObjectState *os, @@ -442,8 +465,8 @@ class Executor : public Interpreter { StatePair forkInternal(ExecutionState ¤t, ref condition, BranchType reason); - // If the MaxStatic*Pct limits have been reached, concretize the condition and - // return it. Otherwise, return the unmodified condition. + // If the MaxStatic*Pct limits have been reached, concretize the condition + // and return it. Otherwise, return the unmodified condition. ref maxStaticPctChecks(ExecutionState ¤t, ref condition); /// Add the given (boolean) condition as a constraint on state. This @@ -587,8 +610,8 @@ class Executor : public Interpreter { unsigned size = 0, const MemoryObject *mo = nullptr) const; - // Determines the \param lastInstruction of the \param state which is not KLEE - // internal and returns its KInstruction + // Determines the \param lastInstruction of the \param state which is not + // KLEE internal and returns its KInstruction const KInstruction * getLastNonKleeInternalInstruction(const ExecutionState &state) const; @@ -697,7 +720,8 @@ class Executor : public Interpreter { void doImpliedValueConcretization(ExecutionState &state, ref e, ref value); - /// check memory usage and terminate states when over threshold of -max-memory + /// check memory usage and terminate states when over threshold of + /// -max-memory /// + 100MB \return true if below threshold, false otherwise (states were /// terminated) bool checkMemoryUsage(); diff --git a/lib/Core/SpecialFunctionHandler.cpp b/lib/Core/SpecialFunctionHandler.cpp index cdbd00c84a..58fe990f2f 100644 --- a/lib/Core/SpecialFunctionHandler.cpp +++ b/lib/Core/SpecialFunctionHandler.cpp @@ -191,6 +191,11 @@ static SpecialFunctionHandler::HandlerInfo handlerInfo[] = { add("klee_rintf", handleRint, true), #if defined(__x86_64__) || defined(__i386__) add("klee_rintl", handleRint, true), +#if defined(HAVE_CTYPE_EXTERNALS) + add("__ctype_b_loc", handleCTypeBLoc, true), + add("__ctype_tolower_loc", handleCTypeToLowerLoc, true), + add("__ctype_toupper_loc", handleCTypeToUpperLoc, true), +#endif #endif #undef addDNR #undef add @@ -1214,3 +1219,34 @@ void SpecialFunctionHandler::handleFAbs(ExecutionState &state, ref result = FAbsExpr::create(arguments[0]); executor.bindLocal(target, state, result); } + +#ifdef HAVE_CTYPE_EXTERNALS + +void SpecialFunctionHandler::handleCTypeBLoc( + ExecutionState &state, KInstruction *target, + std::vector> &arguments) { + ref pointerValue = Expr::createPointer( + reinterpret_cast(executor.c_type_b_loc_addr)); + ref result = ConstantPointerExpr::create(pointerValue, pointerValue); + executor.bindLocal(target, state, result); +} + +void SpecialFunctionHandler::handleCTypeToLowerLoc( + ExecutionState &state, KInstruction *target, + std::vector> &arguments) { + ref pointerValue = Expr::createPointer( + reinterpret_cast(executor.c_type_tolower_addr)); + ref result = ConstantPointerExpr::create(pointerValue, pointerValue); + executor.bindLocal(target, state, result); +} + +void SpecialFunctionHandler::handleCTypeToUpperLoc( + ExecutionState &state, KInstruction *target, + std::vector> &arguments) { + ref pointerValue = Expr::createPointer( + reinterpret_cast(executor.c_type_toupper_addr)); + ref result = ConstantPointerExpr::create(pointerValue, pointerValue); + executor.bindLocal(target, state, result); +} + +#endif diff --git a/lib/Core/SpecialFunctionHandler.h b/lib/Core/SpecialFunctionHandler.h index d6a469eca6..217ed1f06a 100644 --- a/lib/Core/SpecialFunctionHandler.h +++ b/lib/Core/SpecialFunctionHandler.h @@ -181,6 +181,11 @@ class SpecialFunctionHandler { HANDLER(handleNonnullArg); HANDLER(handleNullabilityArg); HANDLER(handlePointerOverflow); +#ifdef HAVE_CTYPE_EXTERNALS + HANDLER(handleCTypeBLoc); + HANDLER(handleCTypeToLowerLoc); + HANDLER(handleCTypeToUpperLoc); +#endif #undef HANDLER }; } // namespace klee diff --git a/lib/Expr/Expr.cpp b/lib/Expr/Expr.cpp index 15b836bd11..e346587843 100644 --- a/lib/Expr/Expr.cpp +++ b/lib/Expr/Expr.cpp @@ -2329,20 +2329,45 @@ static ref AShrExpr_create(const ref &l, const ref &r) { } } +template static bool isDiv() { + return T::kind == Expr::Kind::SDiv || T::kind == Expr::Kind::UDiv || + T::kind == Expr::Kind::FDiv; +} + +/// Heuristic. +/// Attempts to sift up select expression during creation. +template +static ref tryCreateWithSiftUpSelectExpr(const ref &l, + const ref &r, + bool skipInnerSelect = false) { + if (isDiv()) { + return nullptr; + } + + if (ref sel = dyn_cast(l)) { + if (isa(sel->trueExpr) && + (!skipInnerSelect || !isa(r))) { + return SelectExpr::create(sel->cond, T::create(sel->trueExpr, r), + T::create(sel->falseExpr, r)); + } + } + if (ref ser = dyn_cast(r)) { + if (isa(ser->trueExpr) && + (!skipInnerSelect || !isa(l))) { + return SelectExpr::create(ser->cond, T::create(l, ser->trueExpr), + T::create(l, ser->falseExpr)); + } + } + + return nullptr; +} + #define BCREATE_R(_e_op, _op, partialL, partialR, pointerL, pointerR) \ ref _e_op ::create(const ref &l, const ref &r) { \ assert(l->getWidth() == r->getWidth() && "type mismatch"); \ - if (SelectExpr *sel = dyn_cast(l)) { \ - if (isa(sel->trueExpr)) { \ - return SelectExpr::create(sel->cond, _e_op::create(sel->trueExpr, r), \ - _e_op::create(sel->falseExpr, r)); \ - } \ - } \ - if (SelectExpr *ser = dyn_cast(r)) { \ - if (isa(ser->trueExpr)) { \ - return SelectExpr::create(ser->cond, _e_op::create(l, ser->trueExpr), \ - _e_op::create(l, ser->falseExpr)); \ - } \ + if (auto withSiftUpSelectExpr = \ + tryCreateWithSiftUpSelectExpr<_e_op>(l, r)) { \ + return withSiftUpSelectExpr; \ } \ if (PointerExpr *pl = dyn_cast(l)) { \ if (PointerExpr *pr = dyn_cast(r)) \ @@ -2364,17 +2389,9 @@ static ref AShrExpr_create(const ref &l, const ref &r) { #define BCREATE_R_C(_e_op, _op, partialL, partialR, pointerL, pointerR) \ ref _e_op ::create(const ref &l, const ref &r) { \ assert(l->getWidth() == r->getWidth() && "type mismatch"); \ - if (SelectExpr *sel = dyn_cast(l)) { \ - if (isa(sel->trueExpr)) { \ - return SelectExpr::create(sel->cond, _e_op::create(sel->trueExpr, r), \ - _e_op::create(sel->falseExpr, r)); \ - } \ - } \ - if (SelectExpr *ser = dyn_cast(r)) { \ - if (isa(ser->trueExpr)) { \ - return SelectExpr::create(ser->cond, _e_op::create(l, ser->trueExpr), \ - _e_op::create(l, ser->falseExpr)); \ - } \ + if (auto withSiftUpSelectExpr = \ + tryCreateWithSiftUpSelectExpr<_e_op>(l, r)) { \ + return withSiftUpSelectExpr; \ } \ if (PointerExpr *pl = dyn_cast(l)) { \ if (PointerExpr *pr = dyn_cast(r)) \ @@ -2409,17 +2426,9 @@ static ref AShrExpr_create(const ref &l, const ref &r) { #define BCREATE(_e_op, _op) \ ref _e_op ::create(const ref &l, const ref &r) { \ assert(l->getWidth() == r->getWidth() && "type mismatch"); \ - if (SelectExpr *sel = dyn_cast(l)) { \ - if (isa(sel->trueExpr)) { \ - return SelectExpr::create(sel->cond, _e_op::create(sel->trueExpr, r), \ - _e_op::create(sel->falseExpr, r)); \ - } \ - } \ - if (SelectExpr *ser = dyn_cast(r)) { \ - if (isa(ser->trueExpr)) { \ - return SelectExpr::create(ser->cond, _e_op::create(l, ser->trueExpr), \ - _e_op::create(l, ser->falseExpr)); \ - } \ + if (auto withSiftUpSelectExpr = \ + tryCreateWithSiftUpSelectExpr<_e_op>(l, r)) { \ + return withSiftUpSelectExpr; \ } \ if (PointerExpr *pl = dyn_cast(l)) { \ if (PointerExpr *pr = dyn_cast(r)) \ @@ -2457,17 +2466,9 @@ BCREATE(AShrExpr, AShr) #define CMPCREATE(_e_op, _op) \ ref _e_op ::create(const ref &l, const ref &r) { \ assert(l->getWidth() == r->getWidth() && "type mismatch"); \ - if (SelectExpr *sel = dyn_cast(l)) { \ - if (isa(sel->trueExpr) && !isa(r)) { \ - return SelectExpr::create(sel->cond, _e_op::create(sel->trueExpr, r), \ - _e_op::create(sel->falseExpr, r)); \ - } \ - } \ - if (SelectExpr *ser = dyn_cast(r)) { \ - if (isa(ser->trueExpr) && !isa(l)) { \ - return SelectExpr::create(ser->cond, _e_op::create(l, ser->trueExpr), \ - _e_op::create(l, ser->falseExpr)); \ - } \ + if (auto withSiftUpSelectExpr = \ + tryCreateWithSiftUpSelectExpr<_e_op>(l, r, true)) { \ + return withSiftUpSelectExpr; \ } \ if (PointerExpr *pl = dyn_cast(l)) { \ if (PointerExpr *pr = dyn_cast(r)) \ @@ -2485,17 +2486,9 @@ BCREATE(AShrExpr, AShr) #define CMPCREATE_T(_e_op, _op, _reflexive_e_op, partialL, partialR) \ ref _e_op ::create(const ref &l, const ref &r) { \ assert(l->getWidth() == r->getWidth() && "type mismatch"); \ - if (SelectExpr *sel = dyn_cast(l)) { \ - if (isa(sel->trueExpr) && !isa(r)) { \ - return SelectExpr::create(sel->cond, _e_op::create(sel->trueExpr, r), \ - _e_op::create(sel->falseExpr, r)); \ - } \ - } \ - if (SelectExpr *ser = dyn_cast(r)) { \ - if (isa(ser->trueExpr) && !isa(l)) { \ - return SelectExpr::create(ser->cond, _e_op::create(l, ser->trueExpr), \ - _e_op::create(l, ser->falseExpr)); \ - } \ + if (auto withSiftUpSelectExpr = \ + tryCreateWithSiftUpSelectExpr<_e_op>(l, r, true)) { \ + return withSiftUpSelectExpr; \ } \ if (PointerExpr *pl = dyn_cast(l)) { \ if (PointerExpr *pr = dyn_cast(r)) \ diff --git a/lib/Module/LocalVarDeclarationFinderPass.cpp b/lib/Module/LocalVarDeclarationFinderPass.cpp index 8c84f7537b..ec8ec45e53 100644 --- a/lib/Module/LocalVarDeclarationFinderPass.cpp +++ b/lib/Module/LocalVarDeclarationFinderPass.cpp @@ -35,7 +35,7 @@ bool LocalVarDeclarationFinderPass::runOnFunction(llvm::Function &function) { llvm::dyn_cast(&instruction)) { llvm::Value *source = debugDeclareInstruction->getAddress(); if (llvm::Instruction *sourceInstruction = - llvm::dyn_cast(source)) { + llvm::dyn_cast_or_null(source)) { sourceInstruction->setDebugLoc( debugDeclareInstruction->getDebugLoc()); anyChanged = true; diff --git a/test/regression/2024-07-08-call-to-raise.c b/test/regression/2024-07-08-call-to-raise.c new file mode 100644 index 0000000000..5e0539e1dc --- /dev/null +++ b/test/regression/2024-07-08-call-to-raise.c @@ -0,0 +1,17 @@ +// RUN: %clang %s -emit-llvm %O0opt -c -g -o %t.bc +// RUN: rm -rf %t.klee-out +// RUN: %klee --output-dir=%t.klee-out --mock-policy=all --external-calls=all %t.bc 2>&1 | FileCheck %s + +#include "klee/klee.h" +#include + +int main() { + int n; + klee_make_symbolic(&n, sizeof(n), "n"); + + if (n) { + // CHECK: Call to "raise" is unmodelled + raise(5); + } + // CHECK: KLEE: done: completed paths = 1 +} diff --git a/test/regression/2024-07-08-division-in-select.c b/test/regression/2024-07-08-division-in-select.c new file mode 100644 index 0000000000..570a2fb4f0 --- /dev/null +++ b/test/regression/2024-07-08-division-in-select.c @@ -0,0 +1,15 @@ +// RUN: %clang %s -emit-llvm %O0opt -c -g -o %t.bc +// RUN: rm -rf %t.klee-out +// RUN: %klee --output-dir=%t.klee-out %t.bc 2>&1 | FileCheck %s + +#include "klee/klee.h" + +int main() { + int n, m; + klee_make_symbolic(&n, sizeof(n), "n"); + klee_make_symbolic(&m, sizeof(m), "m"); + + int z = (n ? 1 : 0) / (m ? 1 : 0); + (void)z; +} +// CHECK: KLEE: done diff --git a/test/regression/2024-07-08-resolve-of-select.c b/test/regression/2024-07-08-resolve-of-select.c new file mode 100644 index 0000000000..4491df841b --- /dev/null +++ b/test/regression/2024-07-08-resolve-of-select.c @@ -0,0 +1,27 @@ +// RUN: %clang %s -emit-llvm %O0opt -c -g -o %t.bc +// RUN: rm -rf %t.klee-out +// RUN: %klee --output-dir=%t.klee-out --skip-not-symbolic-objects --skip-not-lazy-initialized --skip-local --skip-global %t.bc 2>&1 | FileCheck %s + +#include "klee/klee.h" +#include + +int main() { + int x = 0; + int y = 1; + + int cond; + klee_make_symbolic(&cond, sizeof(cond), "cond"); + + int *z = cond ? &x : &y; + // CHECK-NOT: memory error: out of bound pointer + *z = 10; + + // CHECK-NOT: ASSERTION FAIL + if (x == 10) { + assert(y == 1); + } else { + assert(x == 0); + assert(y == 10); + } + // CHECK: KLEE: done: completed paths = 2 +}