diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h index f3af3cc810..57bff2b656 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h @@ -179,6 +179,12 @@ mapFactsToCallee(const llvm::CallBase *CallSite, const llvm::Function *DestFun, llvm::Function::const_arg_iterator ParamIt = DestFun->arg_begin(); llvm::Function::const_arg_iterator ParamEnd = DestFun->arg_end(); + if (ParamIt != ParamEnd && (*ParamIt).hasStructRetAttr()) { + // sret parameters are writeonly + ++ParamIt; + ++ArgIt; + } + for (; ParamIt != ParamEnd; ++ParamIt, ++ArgIt) { if (std::invoke(PropArg, ArgIt->get(), Source)) { Res.insert(std::invoke(FactConstructor, &*ParamIt)); diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h index b39e22a42c..9df7cf69b3 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h @@ -98,8 +98,10 @@ class IFDSTaintAnalysis bool isSanitizerCall(const llvm::CallBase *CB, const llvm::Function *Callee) const; - void populateWithMayAliases(std::set &Facts) const; - void populateWithMustAliases(std::set &Facts) const; + void populateWithMayAliases(container_type &Facts, + const llvm::Instruction *Context) const; + void populateWithMustAliases(container_type &Facts, + const llvm::Instruction *Context) const; }; } // namespace psr diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.cpp index 13fa3aa4b2..ddf60d0160 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.cpp @@ -24,8 +24,13 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Demangle/Demangle.h" #include "llvm/IR/AbstractCallSite.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Value.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/raw_ostream.h" #include @@ -105,66 +110,105 @@ bool IFDSTaintAnalysis::isSanitizerCall(const llvm::CallBase * /*CB*/, [this](const auto &Arg) { return Config->isSanitizer(&Arg); }); } -void IFDSTaintAnalysis::populateWithMayAliases(std::set &Facts) const { - std::set Tmp = Facts; +void IFDSTaintAnalysis::populateWithMayAliases( + container_type &Facts, const llvm::Instruction *Context) const { + container_type Tmp = Facts; for (const auto *Fact : Facts) { auto Aliases = PT.getAliasSet(Fact); - Tmp.insert(Aliases->begin(), Aliases->end()); + for (const auto *Alias : *Aliases) { + if (const auto *Inst = llvm::dyn_cast(Alias)) { + /// Mapping instructions between functions is done via the call-FF and + /// ret-FF + if (Inst->getFunction() != Context->getFunction()) { + continue; + } + if (Inst->getParent() == Context->getParent() && + Context->comesBefore(Inst)) { + // We will see that inst later + continue; + } + } else if (const auto *Glob = + llvm::dyn_cast(Alias)) { + if (Glob != Fact && Glob->isConstant()) { + // Data cannot flow into the readonly-data section + continue; + } + } + + Tmp.insert(Alias); + } } Facts = std::move(Tmp); } -void IFDSTaintAnalysis::populateWithMustAliases(std::set &Facts) const { +void IFDSTaintAnalysis::populateWithMustAliases( + container_type &Facts, const llvm::Instruction *Context) const { /// TODO: Find must-aliases; Currently the AliasSet only contains /// may-aliases } -IFDSTaintAnalysis::FlowFunctionPtrType IFDSTaintAnalysis::getNormalFlowFunction( - IFDSTaintAnalysis::n_t Curr, [[maybe_unused]] IFDSTaintAnalysis::n_t Succ) { +auto IFDSTaintAnalysis::getNormalFlowFunction(n_t Curr, + [[maybe_unused]] n_t Succ) + -> FlowFunctionPtrType { // If a tainted value is stored, the store location must be tainted too if (const auto *Store = llvm::dyn_cast(Curr)) { - struct TAFF : FlowFunction { - const llvm::StoreInst *Store; - TAFF(const llvm::StoreInst *S) : Store(S){}; - std::set - computeTargets(IFDSTaintAnalysis::d_t Source) override { - if (Store->getValueOperand() == Source) { - return std::set{Store->getPointerOperand(), - Source}; - } - if (Store->getValueOperand() != Source && - Store->getPointerOperand() == Source) { - return {}; - } - return {Source}; - } - }; - return std::make_shared(Store); + container_type Gen; + Gen.insert(Store->getPointerOperand()); + populateWithMayAliases(Gen, Store); + Gen.insert(Store->getValueOperand()); + + return lambdaFlow( + [Store, Gen{std::move(Gen)}](d_t Source) -> container_type { + if (Store->getValueOperand() == Source) { + return Gen; + } + if (Store->getPointerOperand() == Source) { + return {}; + } + return {Source}; + }); } // If a tainted value is loaded, the loaded value is of course tainted if (const auto *Load = llvm::dyn_cast(Curr)) { - return generateFlow(Load, Load->getPointerOperand()); + return transferFlow(Load, Load->getPointerOperand()); } // Check if an address is computed from a tainted base pointer of an // aggregated object if (const auto *GEP = llvm::dyn_cast(Curr)) { - return generateFlow(GEP, GEP->getPointerOperand()); + return transferFlow(GEP, GEP->getPointerOperand()); } // Check if a tainted value is extracted and taint the targets of // the extract operation accordingly if (const auto *Extract = llvm::dyn_cast(Curr)) { + return transferFlow(Extract, Extract->getAggregateOperand()); + } + + if (const auto *Insert = llvm::dyn_cast(Curr)) { + return lambdaFlow([Insert](d_t Source) -> container_type { + if (Source == Insert->getAggregateOperand() || + Source == Insert->getInsertedValueOperand()) { + return {Source, Insert}; + } + + if (Source == Insert) { + return {}; + } + + return {Source}; + }); + } - return generateFlow(Extract, Extract->getAggregateOperand()); + if (const auto *Cast = llvm::dyn_cast(Curr)) { + return transferFlow(Cast, Cast->getOperand(0)); } // Otherwise we do not care and leave everything as it is - return Identity::getInstance(); + return Identity::getInstance(); } -IFDSTaintAnalysis::FlowFunctionPtrType -IFDSTaintAnalysis::getCallFlowFunction(IFDSTaintAnalysis::n_t CallSite, - IFDSTaintAnalysis::f_t DestFun) { +auto IFDSTaintAnalysis::getCallFlowFunction(n_t CallSite, f_t DestFun) + -> FlowFunctionPtrType { const auto *CS = llvm::cast(CallSite); // Check if a source or sink function is called: // We then can kill all data-flow facts not following the called function. @@ -178,10 +222,10 @@ IFDSTaintAnalysis::getCallFlowFunction(IFDSTaintAnalysis::n_t CallSite, return mapFactsToCallee(CS, DestFun); } -IFDSTaintAnalysis::FlowFunctionPtrType IFDSTaintAnalysis::getRetFlowFunction( - IFDSTaintAnalysis::n_t CallSite, IFDSTaintAnalysis::f_t /*CalleeFun*/, - IFDSTaintAnalysis::n_t ExitStmt, - [[maybe_unused]] IFDSTaintAnalysis::n_t RetSite) { +auto IFDSTaintAnalysis::getRetFlowFunction(n_t CallSite, f_t /*CalleeFun*/, + n_t ExitStmt, + [[maybe_unused]] n_t RetSite) + -> FlowFunctionPtrType { // We must check if the return value and formal parameter are tainted, if so // we must taint all user's of the function call. We are only interested in // formal parameters of pointer/reference type. @@ -194,74 +238,86 @@ IFDSTaintAnalysis::FlowFunctionPtrType IFDSTaintAnalysis::getRetFlowFunction( // All other stuff is killed at this point } -IFDSTaintAnalysis::FlowFunctionPtrType -IFDSTaintAnalysis::getCallToRetFlowFunction( - IFDSTaintAnalysis::n_t CallSite, - [[maybe_unused]] IFDSTaintAnalysis::n_t RetSite, - llvm::ArrayRef Callees) { +auto IFDSTaintAnalysis::getCallToRetFlowFunction(n_t CallSite, + [[maybe_unused]] n_t RetSite, + llvm::ArrayRef Callees) + -> FlowFunctionPtrType { + const auto *CS = llvm::cast(CallSite); - std::set Gen; - std::set Leak; - std::set Kill; - bool HasBody = false; - // Process the effects of source or sink functions that are called - for (const auto *Callee : Callees) { - if (!Callee->isDeclaration()) { - HasBody = true; - } - collectGeneratedFacts(Gen, *Config, CS, Callee); - collectLeakedFacts(Leak, *Config, CS, Callee); - collectSanitizedFacts(Kill, *Config, CS, Callee); - } - if (HasBody && Gen.empty() && Leak.empty() && Kill.empty()) { - // We have a normal function-call and the ret-FF is responsible for handling - // pointer parameters. So we need to kill them here - for (const auto &Arg : CS->args()) { - if (Arg->getType()->isPointerTy()) { - Kill.insert(Arg.get()); - } - } + bool HasDeclOnly = llvm::any_of( + Callees, [](const auto *DestFun) { return DestFun->isDeclaration(); }); + + return mapFactsAlongsideCallSite(CS, [HasDeclOnly](d_t Arg) { + return HasDeclOnly || !Arg->getType()->isPointerTy(); + }); +} + +auto IFDSTaintAnalysis::getSummaryFlowFunction([[maybe_unused]] n_t CallSite, + [[maybe_unused]] f_t DestFun) + -> FlowFunctionPtrType { + // $sSS1poiyS2S_SStFZ is Swift's String append method + // if concat a tainted string with something else the + // result should be tainted + if (DestFun->getName().equals("$sSS1poiyS2S_SStFZ")) { + const auto *CS = llvm::cast(CallSite); + + return generateFlowIf(CallSite, [CS](d_t Source) { + return ((Source == CS->getArgOperand(1)) || + (Source == CS->getArgOperand(3))); + }); } - populateWithMayAliases(Gen); - populateWithMayAliases(Leak); + const auto *CS = llvm::cast(CallSite); + container_type Gen; + container_type Leak; + container_type Kill; - Gen.insert(LLVMZeroValue::getInstance()); + // Process the effects of source or sink functions that are called + collectGeneratedFacts(Gen, *Config, CS, DestFun); + collectLeakedFacts(Leak, *Config, CS, DestFun); + collectSanitizedFacts(Kill, *Config, CS, DestFun); + + populateWithMayAliases(Gen, CallSite); + /// We now generate all aliases within the flow functions as facts, so we can + /// safely just check for the sink values here + // populateWithMayAliases(Leak, CallSite); + populateWithMustAliases(Kill, CallSite); + + if (CS->hasStructRetAttr()) { + const auto *SRet = CS->getArgOperand(0); + if (!Gen.count(SRet)) { + // SRet is guaranteed to be written to by the call. If it does not + // generate it, we can freely kill it + Kill.insert(SRet); + } + } - populateWithMustAliases(Kill); + if (Gen.empty()) { + if (!Leak.empty() || !Kill.empty()) { + return lambdaFlow([Leak{std::move(Leak)}, Kill{std::move(Kill)}, + this, CallSite](d_t Source) -> container_type { + if (Leak.count(Source)) { + Leaks[CallSite].insert(Source); + } - if (Gen.empty() && (!Leak.empty() || !Kill.empty())) { - return lambdaFlow([Leak{std::move(Leak)}, Kill{std::move(Kill)}, this, - CallSite](d_t Source) -> std::set { - if (Leak.count(Source)) { - Leaks[CallSite].insert(Source); - } + if (Kill.count(Source)) { + return {}; + } - if (Kill.count(Source)) { - return {}; - } + return {Source}; + }); + } - return {Source}; - }); + // all empty + return nullptr; } - if (Kill.empty()) { - return lambdaFlow([Gen{std::move(Gen)}, Leak{std::move(Leak)}, this, - CallSite](d_t Source) -> std::set { - if (LLVMZeroValue::isLLVMZeroValue(Source)) { - return Gen; - } - if (Leak.count(Source)) { - Leaks[CallSite].insert(Source); - } + // Gen nonempty - return {Source}; - }); - } - return lambdaFlow([Gen{std::move(Gen)}, Leak{std::move(Leak)}, - Kill{std::move(Kill)}, this, - CallSite](d_t Source) -> std::set { + Gen.insert(LLVMZeroValue::getInstance()); + return lambdaFlow([Gen{std::move(Gen)}, Leak{std::move(Leak)}, this, + CallSite](d_t Source) -> container_type { if (LLVMZeroValue::isLLVMZeroValue(Source)) { return Gen; } @@ -270,38 +326,11 @@ IFDSTaintAnalysis::getCallToRetFlowFunction( Leaks[CallSite].insert(Source); } - if (Kill.count(Source)) { - return {}; - } - return {Source}; }); - - // Otherwise pass everything as it is - return Identity::getInstance(); -} - -IFDSTaintAnalysis::FlowFunctionPtrType -IFDSTaintAnalysis::getSummaryFlowFunction( - [[maybe_unused]] IFDSTaintAnalysis::n_t CallSite, - [[maybe_unused]] IFDSTaintAnalysis::f_t DestFun) { - // $sSS1poiyS2S_SStFZ is Swift's String append method - // if concat a tainted string with something else the - // result should be tainted - if (DestFun->getName().equals("$sSS1poiyS2S_SStFZ")) { - const auto *CS = llvm::cast(CallSite); - - return generateFlowIf(CallSite, [CS](d_t Source) { - return ((Source == CS->getArgOperand(1)) || - (Source == CS->getArgOperand(3))); - }); - } - return nullptr; } -InitialSeeds -IFDSTaintAnalysis::initialSeeds() { +auto IFDSTaintAnalysis::initialSeeds() -> InitialSeeds { PHASAR_LOG_LEVEL(DEBUG, "IFDSTaintAnalysis::initialSeeds()"); // If main function is the entry point, commandline arguments have to be // tainted. Otherwise we just use the zero value to initialize the analysis. @@ -311,7 +340,6 @@ IFDSTaintAnalysis::initialSeeds() { forallStartingPoints(EntryPoints, IRDB, C, [this, &Seeds](n_t SP) { Seeds.addSeed(SP, getZeroValue()); if (SP->getFunction()->getName() == "main") { - std::set CmdArgs; for (const auto &Arg : SP->getFunction()->args()) { Seeds.addSeed(SP, &Arg); } @@ -321,28 +349,26 @@ IFDSTaintAnalysis::initialSeeds() { return Seeds; } -IFDSTaintAnalysis::d_t IFDSTaintAnalysis::createZeroValue() const { +auto IFDSTaintAnalysis::createZeroValue() const -> d_t { PHASAR_LOG_LEVEL(DEBUG, "IFDSTaintAnalysis::createZeroValue()"); // create a special value to represent the zero value! return LLVMZeroValue::getInstance(); } -bool IFDSTaintAnalysis::isZeroValue(IFDSTaintAnalysis::d_t FlowFact) const { +bool IFDSTaintAnalysis::isZeroValue(d_t FlowFact) const { return LLVMZeroValue::isLLVMZeroValue(FlowFact); } -void IFDSTaintAnalysis::printNode(llvm::raw_ostream &Os, - IFDSTaintAnalysis::n_t Inst) const { +void IFDSTaintAnalysis::printNode(llvm::raw_ostream &Os, n_t Inst) const { Os << llvmIRToString(Inst); } -void IFDSTaintAnalysis::printDataFlowFact( - llvm::raw_ostream &Os, IFDSTaintAnalysis::d_t FlowFact) const { +void IFDSTaintAnalysis::printDataFlowFact(llvm::raw_ostream &Os, + d_t FlowFact) const { Os << llvmIRToString(FlowFact); } -void IFDSTaintAnalysis::printFunction(llvm::raw_ostream &Os, - IFDSTaintAnalysis::f_t Fun) const { +void IFDSTaintAnalysis::printFunction(llvm::raw_ostream &Os, f_t Fun) const { Os << Fun->getName(); } @@ -352,21 +378,22 @@ void IFDSTaintAnalysis::emitTextReport( OS << "\n----- Found the following leaks -----\n"; if (Leaks.empty()) { OS << "No leaks found!\n"; - } else { - for (const auto &Leak : Leaks) { - OS << "At instruction\nIR : " << llvmIRToString(Leak.first) << '\n'; - OS << "\n\nLeak(s):\n"; - for (const auto *LeakedValue : Leak.second) { - OS << "IR : "; - // Get the actual leaked alloca instruction if possible - if (const auto *Load = llvm::dyn_cast(LeakedValue)) { - OS << llvmIRToString(Load->getPointerOperand()) << '\n'; - } else { - OS << llvmIRToString(LeakedValue) << '\n'; - } + return; + } + + for (const auto &Leak : Leaks) { + OS << "At instruction\nIR : " << llvmIRToString(Leak.first) << '\n'; + OS << "\nLeak(s):\n"; + for (const auto *LeakedValue : Leak.second) { + OS << "IR : "; + // Get the actual leaked alloca instruction if possible + if (const auto *Load = llvm::dyn_cast(LeakedValue)) { + OS << llvmIRToString(Load->getPointerOperand()) << '\n'; + } else { + OS << llvmIRToString(LeakedValue) << '\n'; } - OS << "-------------------\n"; } + OS << "-------------------\n"; } }