diff --git a/CMakeLists.txt b/CMakeLists.txt index 9743e3ad8..7d792dd2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,10 @@ set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) cmake_policy(SET CMP0077 NEW) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) +# Allow overwriting cache variables of external projects from this CMakeLists file +cmake_policy(SET CMP0126 NEW) +set(CMAKE_POLICY_DEFAULT_CMP0126 NEW) + # Allow portable use of CMAKE_VISIBILITY_INLINES_HIDDEN not only for shared libraries cmake_policy(SET CMP0063 NEW) set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) @@ -303,6 +307,9 @@ endif() option(USE_LLVM_FAT_LIB "Link against libLLVM.so instead of the individual LLVM libraries if possible (default is OFF; always on if BUILD_SHARED_LIBS is ON)" OFF) # LLVM +if (NOT PHASAR_LLVM_VERSION) + set(PHASAR_LLVM_VERSION 14) +endif() include(add_llvm) add_llvm() diff --git a/cmake/add_llvm.cmake b/cmake/add_llvm.cmake index 7cf3ceb94..a2c47b8ee 100644 --- a/cmake/add_llvm.cmake +++ b/cmake/add_llvm.cmake @@ -2,7 +2,7 @@ macro(add_llvm) if (NOT PHASAR_IN_TREE) # Only search for LLVM if we build out of tree - find_package(LLVM 14 REQUIRED CONFIG) + find_package(LLVM ${PHASAR_LLVM_VERSION} REQUIRED CONFIG) find_library(LLVM_LIBRARY NAMES LLVM PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) if(USE_LLVM_FAT_LIB AND ${LLVM_LIBRARY} STREQUAL "LLVM_LIBRARY-NOTFOUND") diff --git a/cmake/phasar_macros.cmake b/cmake/phasar_macros.cmake index 2ce5cd45f..e44a61e00 100644 --- a/cmake/phasar_macros.cmake +++ b/cmake/phasar_macros.cmake @@ -125,10 +125,21 @@ function(generate_ll_file) endif() if(GEN_LL_MEM2REG) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + get_filename_component(COMPILER_PATH_STR ${CMAKE_CXX_COMPILER} DIRECTORY) + find_program(OPT_TOOL opt HINTS ${COMPILER_PATH_STR}) + else() + find_program(OPT_TOOL opt) + endif() + + if(NOT OPT_TOOL) + set(OPT_TOOL opt) + endif() + add_custom_command( OUTPUT ${test_code_ll_file} COMMAND ${GEN_CMD} ${test_code_file_path} -o ${test_code_ll_file} - COMMAND ${CMAKE_CXX_COMPILER_LAUNCHER} opt -mem2reg -S -opaque-pointers=0 ${test_code_ll_file} -o ${test_code_ll_file} + COMMAND ${CMAKE_CXX_COMPILER_LAUNCHER} ${OPT_TOOL} -mem2reg -S -opaque-pointers=0 ${test_code_ll_file} -o ${test_code_ll_file} COMMENT ${GEN_CMD_COMMENT} DEPENDS ${GEN_LL_FILE} VERBATIM diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h index fadaf7625..2a13cc8ea 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h @@ -21,7 +21,6 @@ #include "phasar/DataFlow/IfdsIde/Solver/IDESolver.h" #include "phasar/Domain/BinaryDomain.h" -#include #include #include #include @@ -118,8 +117,7 @@ using IFDSSolver_P = IFDSSolver OwningSolverResults + typename AnalysisDomainTy::d_t, BinaryDomain> solveIFDSProblem(IFDSTabulationProblem &Problem, const typename AnalysisDomainTy::i_t &ICF) { IFDSSolver Solver(Problem, &ICF); diff --git a/include/phasar/DataFlow/Mono/Solver/IntraMonoSolver.h b/include/phasar/DataFlow/Mono/Solver/IntraMonoSolver.h index ac46b9068..739b6fa30 100644 --- a/include/phasar/DataFlow/Mono/Solver/IntraMonoSolver.h +++ b/include/phasar/DataFlow/Mono/Solver/IntraMonoSolver.h @@ -18,7 +18,9 @@ #define PHASAR_DATAFLOW_MONO_SOLVER_INTRAMONOSOLVER_H #include "phasar/DataFlow/Mono/IntraMonoProblem.h" -#include "phasar/Utils/BitVectorSet.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" +#include "phasar/Utils/DefaultValue.h" +#include "phasar/Utils/Utilities.h" #include #include @@ -109,20 +111,47 @@ template class IntraMonoSolver { } } - mono_container_t getResultsAt(n_t Stmt) { return Analysis[Stmt]; } + [[nodiscard]] const mono_container_t &getResultsAt(n_t Stmt) const { + auto It = Analysis.find(Stmt); + if (It != Analysis.end()) { + return It->second; + } + return getDefaultValue(); + } virtual void dumpResults(llvm::raw_ostream &OS = llvm::outs()) { - OS << "Intra-Monotone solver results:\n" - "------------------------------\n"; - for (auto &[Node, FlowFacts] : this->Analysis) { - OS << "Instruction:\n" << NToString(Node); - OS << "\nFacts:\n"; + OS << "\n***************************************************************\n" + << "* Raw IntraMonoSolver results *\n" + << "***************************************************************\n"; + + if (Analysis.empty()) { + OS << "No results computed!" << '\n'; + return; + } + + std::vector> Cells; + Cells.reserve(Analysis.size()); + Cells.insert(Cells.end(), Analysis.begin(), Analysis.end()); + + std::sort(Cells.begin(), Cells.end(), [](const auto &Lhs, const auto &Rhs) { + if constexpr (std::is_same_v) { + return StringIDLess{}(getMetaDataID(Lhs.first), + getMetaDataID(Rhs.first)); + } else { + // If non-LLVM IR is used + return Lhs.first < Rhs.first; + } + }); + + for (const auto &[Node, FlowFacts] : Cells) { + OS << "Instruction: " << NToString(Node); + OS << "\nFacts: "; if (FlowFacts.empty()) { - OS << "\tEMPTY\n"; + OS << "EMPTY\n"; } else { IMProblem.printContainer(OS, FlowFacts); } - OS << "\n\n"; + OS << "\n"; } } diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h index 7d4bf8842..6a841fcb2 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h @@ -31,7 +31,9 @@ class CHAResolver : public Resolver { CHAResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP, const LLVMTypeHierarchy *TH); - ~CHAResolver() override = default; + // Deleting an incomplete type (LLVMTypeHierarchy) is UB, so instantiate the + // dtor in CHAResolver.cpp + ~CHAResolver() override; FunctionSetTy resolveVirtualCall(const llvm::CallBase *CallSite) override; diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h index 156543886..7742ce262 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h @@ -38,14 +38,29 @@ class LLVMVFTableProvider; class LLVMTypeHierarchy; enum class CallGraphAnalysisType; +/// Assuming that `CallSite` is a virtual call through a vtable, retrieves the +/// index in the vtable of the virtual function called. [[nodiscard]] std::optional getVFTIndex(const llvm::CallBase *CallSite); +/// Assuming that `CallSite` is a vall to a non-static member function, +/// retrieves the type of the receiver. Returns nullptr, if the receiver-type +/// could not be extracted [[nodiscard]] const llvm::StructType * getReceiverType(const llvm::CallBase *CallSite); +/// Assuming that `CallSite` is a virtual call, where `Idx` is retrieved through +/// `getVFTIndex()` and `T` through `getReceiverType()` +[[nodiscard]] const llvm::Function * +getNonPureVirtualVFTEntry(const llvm::StructType *T, unsigned Idx, + const llvm::CallBase *CallSite, + const psr::LLVMVFTableProvider &VTP); + [[nodiscard]] std::string getReceiverTypeName(const llvm::CallBase *CallSite); +/// Checks whether the signature of `DestFun` matches the required withature of +/// `CallSite`, such that `DestFun` qualifies as callee-candidate, if `CallSite` +/// is an indirect/virtual call. [[nodiscard]] bool isConsistentCall(const llvm::CallBase *CallSite, const llvm::Function *DestFun); @@ -59,7 +74,12 @@ class Resolver { const llvm::Function * getNonPureVirtualVFTEntry(const llvm::StructType *T, unsigned Idx, - const llvm::CallBase *CallSite); + const llvm::CallBase *CallSite) { + if (!VTP) { + return nullptr; + } + return psr::getNonPureVirtualVFTEntry(T, Idx, CallSite, *VTP); + } public: using FunctionSetTy = llvm::SmallDenseSet; diff --git a/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h b/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h index 47df476ad..677b26639 100644 --- a/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h +++ b/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h @@ -18,7 +18,6 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" -#include "llvm/ADT/iterator_range.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" @@ -44,7 +43,8 @@ class LLVMProjectIRDB : public ProjectIRDBBase { /// Reads and parses the given LLVM IR file and owns the resulting IR Module. /// If an error occurs, an error message is written to stderr and subsequent /// calls to isValid() return false. - explicit LLVMProjectIRDB(const llvm::Twine &IRFileName); + explicit LLVMProjectIRDB(const llvm::Twine &IRFileName, + bool EnableOpaquePointers = LLVM_VERSION_MAJOR > 14); /// Initializes the new ProjectIRDB with the given IR Module _without_ taking /// ownership. The module is optionally being preprocessed. /// @@ -58,7 +58,8 @@ class LLVMProjectIRDB : public ProjectIRDBBase { /// Parses the given LLVM IR file and owns the resulting IR Module. /// If an error occurs, an error message is written to stderr and subsequent /// calls to isValid() return false. - explicit LLVMProjectIRDB(llvm::MemoryBufferRef Buf); + explicit LLVMProjectIRDB(llvm::MemoryBufferRef Buf, + bool EnableOpaquePointers = LLVM_VERSION_MAJOR > 14); LLVMProjectIRDB(const LLVMProjectIRDB &) = delete; LLVMProjectIRDB &operator=(LLVMProjectIRDB &) = delete; diff --git a/include/phasar/PhasarLLVM/TaintConfig/TaintConfigData.h b/include/phasar/PhasarLLVM/TaintConfig/TaintConfigData.h index ba92829e5..6eaa89280 100644 --- a/include/phasar/PhasarLLVM/TaintConfig/TaintConfigData.h +++ b/include/phasar/PhasarLLVM/TaintConfig/TaintConfigData.h @@ -18,7 +18,9 @@ namespace psr { enum class TaintCategory; struct FunctionData { +#if __cplusplus < 202002L FunctionData() noexcept = default; +#endif std::string Name; TaintCategory ReturnCat{}; @@ -29,7 +31,9 @@ struct FunctionData { }; struct VariableData { +#if __cplusplus < 202002L VariableData() noexcept = default; +#endif size_t Line{}; std::string Name; diff --git a/lib/PhasarLLVM/ControlFlow/Resolver/CHAResolver.cpp b/lib/PhasarLLVM/ControlFlow/Resolver/CHAResolver.cpp index 6b2c144a5..fc2e22b5d 100644 --- a/lib/PhasarLLVM/ControlFlow/Resolver/CHAResolver.cpp +++ b/lib/PhasarLLVM/ControlFlow/Resolver/CHAResolver.cpp @@ -40,6 +40,8 @@ CHAResolver::CHAResolver(const LLVMProjectIRDB *IRDB, } } +CHAResolver::~CHAResolver() = default; + auto CHAResolver::resolveVirtualCall(const llvm::CallBase *CallSite) -> FunctionSetTy { PHASAR_LOG_LEVEL(DEBUG, "Call virtual function: "); diff --git a/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp b/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp index e55edefce..06d5f3b6e 100644 --- a/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp +++ b/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp @@ -86,6 +86,22 @@ const llvm::StructType *psr::getReceiverType(const llvm::CallBase *CallSite) { return nullptr; } +const llvm::Function * +psr::getNonPureVirtualVFTEntry(const llvm::StructType *T, unsigned Idx, + const llvm::CallBase *CallSite, + const LLVMVFTableProvider &VTP) { + + if (const auto *VT = VTP.getVFTableOrNull(T)) { + const auto *Target = VT->getFunction(Idx); + if (Target && Target->getName() != LLVMTypeHierarchy::PureVirtualCallName && + isConsistentCall(CallSite, Target)) { + return Target; + } + } + + return nullptr; +} + std::string psr::getReceiverTypeName(const llvm::CallBase *CallSite) { const auto *RT = getReceiverType(CallSite); if (RT) { @@ -134,22 +150,6 @@ Resolver::Resolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP) assert(VTP != nullptr); } -const llvm::Function * -Resolver::getNonPureVirtualVFTEntry(const llvm::StructType *T, unsigned Idx, - const llvm::CallBase *CallSite) { - if (!VTP) { - return nullptr; - } - if (const auto *VT = VTP->getVFTableOrNull(T)) { - const auto *Target = VT->getFunction(Idx); - if (Target && Target->getName() != LLVMTypeHierarchy::PureVirtualCallName && - isConsistentCall(CallSite, Target)) { - return Target; - } - } - return nullptr; -} - void Resolver::preCall(const llvm::Instruction *Inst) {} void Resolver::handlePossibleTargets(const llvm::CallBase *CallSite, diff --git a/lib/PhasarLLVM/DB/LLVMProjectIRDB.cpp b/lib/PhasarLLVM/DB/LLVMProjectIRDB.cpp index d4632d49f..2bb171e1b 100644 --- a/lib/PhasarLLVM/DB/LLVMProjectIRDB.cpp +++ b/lib/PhasarLLVM/DB/LLVMProjectIRDB.cpp @@ -20,6 +20,21 @@ namespace psr { +static void setOpaquePointersForCtx(llvm::LLVMContext &Ctx, bool Enable) { +#if LLVM_VERSION_MAJOR >= 15 && LLVM_VERSION_MAJOR < 17 + if (!Enable) { + Ctx.setOpaquePointers(false); + } +#elif LLVM_VERSION_MAJOR < 15 + if (Enable) { + Ctx.enableOpaquePointers(); + } +#else // LLVM_VERSION_MAJOR >= 17 +#error \ + "Non-opaque pointers are not supported anymore. Refactor PhASAR to remove typed pointer support." +#endif +} + std::unique_ptr LLVMProjectIRDB::getParsedIRModuleOrNull(llvm::MemoryBufferRef IRFileContent, llvm::LLVMContext &Ctx) noexcept { @@ -61,8 +76,9 @@ LLVMProjectIRDB::getParsedIRModuleOrNull(const llvm::Twine &IRFileName, return getParsedIRModuleOrNull(*FileOrErr.get(), Ctx); } -LLVMProjectIRDB::LLVMProjectIRDB(const llvm::Twine &IRFileName) { - +LLVMProjectIRDB::LLVMProjectIRDB(const llvm::Twine &IRFileName, + bool EnableOpaquePointers) { + setOpaquePointersForCtx(Ctx, EnableOpaquePointers); auto M = getParsedIRModuleOrNull(IRFileName, Ctx); if (!M) { @@ -155,8 +171,9 @@ LLVMProjectIRDB::LLVMProjectIRDB(std::unique_ptr Mod, } } -LLVMProjectIRDB::LLVMProjectIRDB(llvm::MemoryBufferRef Buf) { - llvm::SMDiagnostic Diag; +LLVMProjectIRDB::LLVMProjectIRDB(llvm::MemoryBufferRef Buf, + bool EnableOpaquePointers) { + setOpaquePointersForCtx(Ctx, EnableOpaquePointers); auto M = getParsedIRModuleOrNull(Buf, Ctx); if (!M) { return;