|  | //==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // This checker flags misuses of KeyChainAPI. In particular, the password data | 
|  | // allocated/returned by SecKeychainItemCopyContent, | 
|  | // SecKeychainFindGenericPassword, SecKeychainFindInternetPassword functions has | 
|  | // to be freed using a call to SecKeychainItemFreeContent. | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" | 
|  | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" | 
|  | #include "clang/StaticAnalyzer/Core/Checker.h" | 
|  | #include "clang/StaticAnalyzer/Core/CheckerManager.h" | 
|  | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" | 
|  | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" | 
|  | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" | 
|  | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" | 
|  | #include "llvm/ADT/SmallString.h" | 
|  | #include "llvm/Support/raw_ostream.h" | 
|  |  | 
|  | using namespace clang; | 
|  | using namespace ento; | 
|  |  | 
|  | namespace { | 
|  | class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, | 
|  | check::PostStmt<CallExpr>, | 
|  | check::DeadSymbols, | 
|  | check::PointerEscape, | 
|  | eval::Assume> { | 
|  | mutable std::unique_ptr<BugType> BT; | 
|  |  | 
|  | public: | 
|  | /// AllocationState is a part of the checker specific state together with the | 
|  | /// MemRegion corresponding to the allocated data. | 
|  | struct AllocationState { | 
|  | /// The index of the allocator function. | 
|  | unsigned int AllocatorIdx; | 
|  | SymbolRef Region; | 
|  |  | 
|  | AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) : | 
|  | AllocatorIdx(Idx), | 
|  | Region(R) {} | 
|  |  | 
|  | bool operator==(const AllocationState &X) const { | 
|  | return (AllocatorIdx == X.AllocatorIdx && | 
|  | Region == X.Region); | 
|  | } | 
|  |  | 
|  | void Profile(llvm::FoldingSetNodeID &ID) const { | 
|  | ID.AddInteger(AllocatorIdx); | 
|  | ID.AddPointer(Region); | 
|  | } | 
|  | }; | 
|  |  | 
|  | void checkPreStmt(const CallExpr *S, CheckerContext &C) const; | 
|  | void checkPostStmt(const CallExpr *S, CheckerContext &C) const; | 
|  | void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; | 
|  | ProgramStateRef checkPointerEscape(ProgramStateRef State, | 
|  | const InvalidatedSymbols &Escaped, | 
|  | const CallEvent *Call, | 
|  | PointerEscapeKind Kind) const; | 
|  | ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond, | 
|  | bool Assumption) const; | 
|  | void printState(raw_ostream &Out, ProgramStateRef State, | 
|  | const char *NL, const char *Sep) const; | 
|  |  | 
|  | private: | 
|  | typedef std::pair<SymbolRef, const AllocationState*> AllocationPair; | 
|  | typedef SmallVector<AllocationPair, 2> AllocationPairVec; | 
|  |  | 
|  | enum APIKind { | 
|  | /// Denotes functions tracked by this checker. | 
|  | ValidAPI = 0, | 
|  | /// The functions commonly/mistakenly used in place of the given API. | 
|  | ErrorAPI = 1, | 
|  | /// The functions which may allocate the data. These are tracked to reduce | 
|  | /// the false alarm rate. | 
|  | PossibleAPI = 2 | 
|  | }; | 
|  | /// Stores the information about the allocator and deallocator functions - | 
|  | /// these are the functions the checker is tracking. | 
|  | struct ADFunctionInfo { | 
|  | const char* Name; | 
|  | unsigned int Param; | 
|  | unsigned int DeallocatorIdx; | 
|  | APIKind Kind; | 
|  | }; | 
|  | static const unsigned InvalidIdx = 100000; | 
|  | static const unsigned FunctionsToTrackSize = 8; | 
|  | static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize]; | 
|  | /// The value, which represents no error return value for allocator functions. | 
|  | static const unsigned NoErr = 0; | 
|  |  | 
|  | /// Given the function name, returns the index of the allocator/deallocator | 
|  | /// function. | 
|  | static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator); | 
|  |  | 
|  | inline void initBugType() const { | 
|  | if (!BT) | 
|  | BT.reset(new BugType(this, "Improper use of SecKeychain API", | 
|  | "API Misuse (Apple)")); | 
|  | } | 
|  |  | 
|  | void generateDeallocatorMismatchReport(const AllocationPair &AP, | 
|  | const Expr *ArgExpr, | 
|  | CheckerContext &C) const; | 
|  |  | 
|  | /// Find the allocation site for Sym on the path leading to the node N. | 
|  | const ExplodedNode *getAllocationNode(const ExplodedNode *N, SymbolRef Sym, | 
|  | CheckerContext &C) const; | 
|  |  | 
|  | std::unique_ptr<BugReport> generateAllocatedDataNotReleasedReport( | 
|  | const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const; | 
|  |  | 
|  | /// Mark an AllocationPair interesting for diagnostic reporting. | 
|  | void markInteresting(PathSensitiveBugReport *R, | 
|  | const AllocationPair &AP) const { | 
|  | R->markInteresting(AP.first); | 
|  | R->markInteresting(AP.second->Region); | 
|  | } | 
|  |  | 
|  | /// The bug visitor which allows us to print extra diagnostics along the | 
|  | /// BugReport path. For example, showing the allocation site of the leaked | 
|  | /// region. | 
|  | class SecKeychainBugVisitor : public BugReporterVisitor { | 
|  | protected: | 
|  | // The allocated region symbol tracked by the main analysis. | 
|  | SymbolRef Sym; | 
|  |  | 
|  | public: | 
|  | SecKeychainBugVisitor(SymbolRef S) : Sym(S) {} | 
|  |  | 
|  | void Profile(llvm::FoldingSetNodeID &ID) const override { | 
|  | static int X = 0; | 
|  | ID.AddPointer(&X); | 
|  | ID.AddPointer(Sym); | 
|  | } | 
|  |  | 
|  | PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, | 
|  | BugReporterContext &BRC, | 
|  | PathSensitiveBugReport &BR) override; | 
|  | }; | 
|  | }; | 
|  | } | 
|  |  | 
|  | /// ProgramState traits to store the currently allocated (and not yet freed) | 
|  | /// symbols. This is a map from the allocated content symbol to the | 
|  | /// corresponding AllocationState. | 
|  | REGISTER_MAP_WITH_PROGRAMSTATE(AllocatedData, | 
|  | SymbolRef, | 
|  | MacOSKeychainAPIChecker::AllocationState) | 
|  |  | 
|  | static bool isEnclosingFunctionParam(const Expr *E) { | 
|  | E = E->IgnoreParenCasts(); | 
|  | if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { | 
|  | const ValueDecl *VD = DRE->getDecl(); | 
|  | if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD)) | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | const MacOSKeychainAPIChecker::ADFunctionInfo | 
|  | MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = { | 
|  | {"SecKeychainItemCopyContent", 4, 3, ValidAPI},                       // 0 | 
|  | {"SecKeychainFindGenericPassword", 6, 3, ValidAPI},                   // 1 | 
|  | {"SecKeychainFindInternetPassword", 13, 3, ValidAPI},                 // 2 | 
|  | {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI},              // 3 | 
|  | {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI},             // 4 | 
|  | {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI},    // 5 | 
|  | {"free", 0, InvalidIdx, ErrorAPI},                                    // 6 | 
|  | {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI},        // 7 | 
|  | }; | 
|  |  | 
|  | unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name, | 
|  | bool IsAllocator) { | 
|  | for (unsigned I = 0; I < FunctionsToTrackSize; ++I) { | 
|  | ADFunctionInfo FI = FunctionsToTrack[I]; | 
|  | if (FI.Name != Name) | 
|  | continue; | 
|  | // Make sure the function is of the right type (allocator vs deallocator). | 
|  | if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx)) | 
|  | return InvalidIdx; | 
|  | if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx)) | 
|  | return InvalidIdx; | 
|  |  | 
|  | return I; | 
|  | } | 
|  | // The function is not tracked. | 
|  | return InvalidIdx; | 
|  | } | 
|  |  | 
|  | static bool isBadDeallocationArgument(const MemRegion *Arg) { | 
|  | if (!Arg) | 
|  | return false; | 
|  | return isa<AllocaRegion>(Arg) || isa<BlockDataRegion>(Arg) || | 
|  | isa<TypedRegion>(Arg); | 
|  | } | 
|  |  | 
|  | /// Given the address expression, retrieve the value it's pointing to. Assume | 
|  | /// that value is itself an address, and return the corresponding symbol. | 
|  | static SymbolRef getAsPointeeSymbol(const Expr *Expr, | 
|  | CheckerContext &C) { | 
|  | ProgramStateRef State = C.getState(); | 
|  | SVal ArgV = C.getSVal(Expr); | 
|  |  | 
|  | if (Optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) { | 
|  | StoreManager& SM = C.getStoreManager(); | 
|  | SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol(); | 
|  | if (sym) | 
|  | return sym; | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | // Report deallocator mismatch. Remove the region from tracking - reporting a | 
|  | // missing free error after this one is redundant. | 
|  | void MacOSKeychainAPIChecker:: | 
|  | generateDeallocatorMismatchReport(const AllocationPair &AP, | 
|  | const Expr *ArgExpr, | 
|  | CheckerContext &C) const { | 
|  | ProgramStateRef State = C.getState(); | 
|  | State = State->remove<AllocatedData>(AP.first); | 
|  | ExplodedNode *N = C.generateNonFatalErrorNode(State); | 
|  |  | 
|  | if (!N) | 
|  | return; | 
|  | initBugType(); | 
|  | SmallString<80> sbuf; | 
|  | llvm::raw_svector_ostream os(sbuf); | 
|  | unsigned int PDeallocIdx = | 
|  | FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx; | 
|  |  | 
|  | os << "Deallocator doesn't match the allocator: '" | 
|  | << FunctionsToTrack[PDeallocIdx].Name << "' should be used."; | 
|  | auto Report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); | 
|  | Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first)); | 
|  | Report->addRange(ArgExpr->getSourceRange()); | 
|  | markInteresting(Report.get(), AP); | 
|  | C.emitReport(std::move(Report)); | 
|  | } | 
|  |  | 
|  | void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, | 
|  | CheckerContext &C) const { | 
|  | unsigned idx = InvalidIdx; | 
|  | ProgramStateRef State = C.getState(); | 
|  |  | 
|  | const FunctionDecl *FD = C.getCalleeDecl(CE); | 
|  | if (!FD || FD->getKind() != Decl::Function) | 
|  | return; | 
|  |  | 
|  | StringRef funName = C.getCalleeName(FD); | 
|  | if (funName.empty()) | 
|  | return; | 
|  |  | 
|  | // If it is a call to an allocator function, it could be a double allocation. | 
|  | idx = getTrackedFunctionIndex(funName, true); | 
|  | if (idx != InvalidIdx) { | 
|  | unsigned paramIdx = FunctionsToTrack[idx].Param; | 
|  | if (CE->getNumArgs() <= paramIdx) | 
|  | return; | 
|  |  | 
|  | const Expr *ArgExpr = CE->getArg(paramIdx); | 
|  | if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) | 
|  | if (const AllocationState *AS = State->get<AllocatedData>(V)) { | 
|  | // Remove the value from the state. The new symbol will be added for | 
|  | // tracking when the second allocator is processed in checkPostStmt(). | 
|  | State = State->remove<AllocatedData>(V); | 
|  | ExplodedNode *N = C.generateNonFatalErrorNode(State); | 
|  | if (!N) | 
|  | return; | 
|  | initBugType(); | 
|  | SmallString<128> sbuf; | 
|  | llvm::raw_svector_ostream os(sbuf); | 
|  | unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; | 
|  | os << "Allocated data should be released before another call to " | 
|  | << "the allocator: missing a call to '" | 
|  | << FunctionsToTrack[DIdx].Name | 
|  | << "'."; | 
|  | auto Report = | 
|  | std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); | 
|  | Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(V)); | 
|  | Report->addRange(ArgExpr->getSourceRange()); | 
|  | Report->markInteresting(AS->Region); | 
|  | C.emitReport(std::move(Report)); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Is it a call to one of deallocator functions? | 
|  | idx = getTrackedFunctionIndex(funName, false); | 
|  | if (idx == InvalidIdx) | 
|  | return; | 
|  |  | 
|  | unsigned paramIdx = FunctionsToTrack[idx].Param; | 
|  | if (CE->getNumArgs() <= paramIdx) | 
|  | return; | 
|  |  | 
|  | // Check the argument to the deallocator. | 
|  | const Expr *ArgExpr = CE->getArg(paramIdx); | 
|  | SVal ArgSVal = C.getSVal(ArgExpr); | 
|  |  | 
|  | // Undef is reported by another checker. | 
|  | if (ArgSVal.isUndef()) | 
|  | return; | 
|  |  | 
|  | SymbolRef ArgSM = ArgSVal.getAsLocSymbol(); | 
|  |  | 
|  | // If the argument is coming from the heap, globals, or unknown, do not | 
|  | // report it. | 
|  | bool RegionArgIsBad = false; | 
|  | if (!ArgSM) { | 
|  | if (!isBadDeallocationArgument(ArgSVal.getAsRegion())) | 
|  | return; | 
|  | RegionArgIsBad = true; | 
|  | } | 
|  |  | 
|  | // Is the argument to the call being tracked? | 
|  | const AllocationState *AS = State->get<AllocatedData>(ArgSM); | 
|  | if (!AS) | 
|  | return; | 
|  |  | 
|  | // TODO: We might want to report double free here. | 
|  | // (that would involve tracking all the freed symbols in the checker state). | 
|  | if (RegionArgIsBad) { | 
|  | // It is possible that this is a false positive - the argument might | 
|  | // have entered as an enclosing function parameter. | 
|  | if (isEnclosingFunctionParam(ArgExpr)) | 
|  | return; | 
|  |  | 
|  | ExplodedNode *N = C.generateNonFatalErrorNode(State); | 
|  | if (!N) | 
|  | return; | 
|  | initBugType(); | 
|  | auto Report = std::make_unique<PathSensitiveBugReport>( | 
|  | *BT, "Trying to free data which has not been allocated.", N); | 
|  | Report->addRange(ArgExpr->getSourceRange()); | 
|  | if (AS) | 
|  | Report->markInteresting(AS->Region); | 
|  | C.emitReport(std::move(Report)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Process functions which might deallocate. | 
|  | if (FunctionsToTrack[idx].Kind == PossibleAPI) { | 
|  |  | 
|  | if (funName == "CFStringCreateWithBytesNoCopy") { | 
|  | const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); | 
|  | // NULL ~ default deallocator, so warn. | 
|  | if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), | 
|  | Expr::NPC_ValueDependentIsNotNull)) { | 
|  | const AllocationPair AP = std::make_pair(ArgSM, AS); | 
|  | generateDeallocatorMismatchReport(AP, ArgExpr, C); | 
|  | return; | 
|  | } | 
|  | // One of the default allocators, so warn. | 
|  | if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { | 
|  | StringRef DeallocatorName = DE->getFoundDecl()->getName(); | 
|  | if (DeallocatorName == "kCFAllocatorDefault" || | 
|  | DeallocatorName == "kCFAllocatorSystemDefault" || | 
|  | DeallocatorName == "kCFAllocatorMalloc") { | 
|  | const AllocationPair AP = std::make_pair(ArgSM, AS); | 
|  | generateDeallocatorMismatchReport(AP, ArgExpr, C); | 
|  | return; | 
|  | } | 
|  | // If kCFAllocatorNull, which does not deallocate, we still have to | 
|  | // find the deallocator. | 
|  | if (DE->getFoundDecl()->getName() == "kCFAllocatorNull") | 
|  | return; | 
|  | } | 
|  | // In all other cases, assume the user supplied a correct deallocator | 
|  | // that will free memory so stop tracking. | 
|  | State = State->remove<AllocatedData>(ArgSM); | 
|  | C.addTransition(State); | 
|  | return; | 
|  | } | 
|  |  | 
|  | llvm_unreachable("We know of no other possible APIs."); | 
|  | } | 
|  |  | 
|  | // The call is deallocating a value we previously allocated, so remove it | 
|  | // from the next state. | 
|  | State = State->remove<AllocatedData>(ArgSM); | 
|  |  | 
|  | // Check if the proper deallocator is used. | 
|  | unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; | 
|  | if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { | 
|  | const AllocationPair AP = std::make_pair(ArgSM, AS); | 
|  | generateDeallocatorMismatchReport(AP, ArgExpr, C); | 
|  | return; | 
|  | } | 
|  |  | 
|  | C.addTransition(State); | 
|  | } | 
|  |  | 
|  | void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, | 
|  | CheckerContext &C) const { | 
|  | ProgramStateRef State = C.getState(); | 
|  | const FunctionDecl *FD = C.getCalleeDecl(CE); | 
|  | if (!FD || FD->getKind() != Decl::Function) | 
|  | return; | 
|  |  | 
|  | StringRef funName = C.getCalleeName(FD); | 
|  |  | 
|  | // If a value has been allocated, add it to the set for tracking. | 
|  | unsigned idx = getTrackedFunctionIndex(funName, true); | 
|  | if (idx == InvalidIdx) | 
|  | return; | 
|  |  | 
|  | const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); | 
|  | // If the argument entered as an enclosing function parameter, skip it to | 
|  | // avoid false positives. | 
|  | if (isEnclosingFunctionParam(ArgExpr) && | 
|  | C.getLocationContext()->getParent() == nullptr) | 
|  | return; | 
|  |  | 
|  | if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) { | 
|  | // If the argument points to something that's not a symbolic region, it | 
|  | // can be: | 
|  | //  - unknown (cannot reason about it) | 
|  | //  - undefined (already reported by other checker) | 
|  | //  - constant (null - should not be tracked, | 
|  | //              other constant will generate a compiler warning) | 
|  | //  - goto (should be reported by other checker) | 
|  |  | 
|  | // The call return value symbol should stay alive for as long as the | 
|  | // allocated value symbol, since our diagnostics depend on the value | 
|  | // returned by the call. Ex: Data should only be freed if noErr was | 
|  | // returned during allocation.) | 
|  | SymbolRef RetStatusSymbol = C.getSVal(CE).getAsSymbol(); | 
|  | C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol); | 
|  |  | 
|  | // Track the allocated value in the checker state. | 
|  | State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx, | 
|  | RetStatusSymbol)); | 
|  | assert(State); | 
|  | C.addTransition(State); | 
|  | } | 
|  | } | 
|  |  | 
|  | // TODO: This logic is the same as in Malloc checker. | 
|  | const ExplodedNode * | 
|  | MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N, | 
|  | SymbolRef Sym, | 
|  | CheckerContext &C) const { | 
|  | const LocationContext *LeakContext = N->getLocationContext(); | 
|  | // Walk the ExplodedGraph backwards and find the first node that referred to | 
|  | // the tracked symbol. | 
|  | const ExplodedNode *AllocNode = N; | 
|  |  | 
|  | while (N) { | 
|  | if (!N->getState()->get<AllocatedData>(Sym)) | 
|  | break; | 
|  | // Allocation node, is the last node in the current or parent context in | 
|  | // which the symbol was tracked. | 
|  | const LocationContext *NContext = N->getLocationContext(); | 
|  | if (NContext == LeakContext || | 
|  | NContext->isParentOf(LeakContext)) | 
|  | AllocNode = N; | 
|  | N = N->pred_empty() ? nullptr : *(N->pred_begin()); | 
|  | } | 
|  |  | 
|  | return AllocNode; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<BugReport> | 
|  | MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport( | 
|  | const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const { | 
|  | const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; | 
|  | initBugType(); | 
|  | SmallString<70> sbuf; | 
|  | llvm::raw_svector_ostream os(sbuf); | 
|  | os << "Allocated data is not released: missing a call to '" | 
|  | << FunctionsToTrack[FI.DeallocatorIdx].Name << "'."; | 
|  |  | 
|  | // Most bug reports are cached at the location where they occurred. | 
|  | // With leaks, we want to unique them by the location where they were | 
|  | // allocated, and only report a single path. | 
|  | PathDiagnosticLocation LocUsedForUniqueing; | 
|  | const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C); | 
|  | const Stmt *AllocStmt = AllocNode->getStmtForDiagnostics(); | 
|  |  | 
|  | if (AllocStmt) | 
|  | LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt, | 
|  | C.getSourceManager(), | 
|  | AllocNode->getLocationContext()); | 
|  |  | 
|  | auto Report = std::make_unique<PathSensitiveBugReport>( | 
|  | *BT, os.str(), N, LocUsedForUniqueing, | 
|  | AllocNode->getLocationContext()->getDecl()); | 
|  |  | 
|  | Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first)); | 
|  | markInteresting(Report.get(), AP); | 
|  | return Report; | 
|  | } | 
|  |  | 
|  | /// If the return symbol is assumed to be error, remove the allocated info | 
|  | /// from consideration. | 
|  | ProgramStateRef MacOSKeychainAPIChecker::evalAssume(ProgramStateRef State, | 
|  | SVal Cond, | 
|  | bool Assumption) const { | 
|  | AllocatedDataTy AMap = State->get<AllocatedData>(); | 
|  | if (AMap.isEmpty()) | 
|  | return State; | 
|  |  | 
|  | auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr()); | 
|  | if (!CondBSE) | 
|  | return State; | 
|  | BinaryOperator::Opcode OpCode = CondBSE->getOpcode(); | 
|  | if (OpCode != BO_EQ && OpCode != BO_NE) | 
|  | return State; | 
|  |  | 
|  | // Match for a restricted set of patterns for cmparison of error codes. | 
|  | // Note, the comparisons of type '0 == st' are transformed into SymIntExpr. | 
|  | SymbolRef ReturnSymbol = nullptr; | 
|  | if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) { | 
|  | const llvm::APInt &RHS = SIE->getRHS(); | 
|  | bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) || | 
|  | (OpCode == BO_NE && RHS == NoErr); | 
|  | if (!Assumption) | 
|  | ErrorIsReturned = !ErrorIsReturned; | 
|  | if (ErrorIsReturned) | 
|  | ReturnSymbol = SIE->getLHS(); | 
|  | } | 
|  |  | 
|  | if (ReturnSymbol) | 
|  | for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { | 
|  | if (ReturnSymbol == I->second.Region) | 
|  | State = State->remove<AllocatedData>(I->first); | 
|  | } | 
|  |  | 
|  | return State; | 
|  | } | 
|  |  | 
|  | void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, | 
|  | CheckerContext &C) const { | 
|  | ProgramStateRef State = C.getState(); | 
|  | AllocatedDataTy AMap = State->get<AllocatedData>(); | 
|  | if (AMap.isEmpty()) | 
|  | return; | 
|  |  | 
|  | bool Changed = false; | 
|  | AllocationPairVec Errors; | 
|  | for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { | 
|  | if (!SR.isDead(I->first)) | 
|  | continue; | 
|  |  | 
|  | Changed = true; | 
|  | State = State->remove<AllocatedData>(I->first); | 
|  | // If the allocated symbol is null do not report. | 
|  | ConstraintManager &CMgr = State->getConstraintManager(); | 
|  | ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey()); | 
|  | if (AllocFailed.isConstrainedTrue()) | 
|  | continue; | 
|  | Errors.push_back(std::make_pair(I->first, &I->second)); | 
|  | } | 
|  | if (!Changed) { | 
|  | // Generate the new, cleaned up state. | 
|  | C.addTransition(State); | 
|  | return; | 
|  | } | 
|  |  | 
|  | static CheckerProgramPointTag Tag(this, "DeadSymbolsLeak"); | 
|  | ExplodedNode *N = C.generateNonFatalErrorNode(C.getState(), &Tag); | 
|  | if (!N) | 
|  | return; | 
|  |  | 
|  | // Generate the error reports. | 
|  | for (const auto &P : Errors) | 
|  | C.emitReport(generateAllocatedDataNotReleasedReport(P, N, C)); | 
|  |  | 
|  | // Generate the new, cleaned up state. | 
|  | C.addTransition(State, N); | 
|  | } | 
|  |  | 
|  | ProgramStateRef MacOSKeychainAPIChecker::checkPointerEscape( | 
|  | ProgramStateRef State, const InvalidatedSymbols &Escaped, | 
|  | const CallEvent *Call, PointerEscapeKind Kind) const { | 
|  | // FIXME: This branch doesn't make any sense at all, but it is an overfitted | 
|  | // replacement for a previous overfitted code that was making even less sense. | 
|  | if (!Call || Call->getDecl()) | 
|  | return State; | 
|  |  | 
|  | for (auto I : State->get<AllocatedData>()) { | 
|  | SymbolRef Sym = I.first; | 
|  | if (Escaped.count(Sym)) | 
|  | State = State->remove<AllocatedData>(Sym); | 
|  |  | 
|  | // This checker is special. Most checkers in fact only track symbols of | 
|  | // SymbolConjured type, eg. symbols returned from functions such as | 
|  | // malloc(). This checker tracks symbols returned as out-parameters. | 
|  | // | 
|  | // When a function is evaluated conservatively, the out-parameter's pointee | 
|  | // base region gets invalidated with a SymbolConjured. If the base region is | 
|  | // larger than the region we're interested in, the value we're interested in | 
|  | // would be SymbolDerived based on that SymbolConjured. However, such | 
|  | // SymbolDerived will never be listed in the Escaped set when the base | 
|  | // region is invalidated because ExprEngine doesn't know which symbols | 
|  | // were derived from a given symbol, while there can be infinitely many | 
|  | // valid symbols derived from any given symbol. | 
|  | // | 
|  | // Hence the extra boilerplate: remove the derived symbol when its parent | 
|  | // symbol escapes. | 
|  | // | 
|  | if (const auto *SD = dyn_cast<SymbolDerived>(Sym)) { | 
|  | SymbolRef ParentSym = SD->getParentSymbol(); | 
|  | if (Escaped.count(ParentSym)) | 
|  | State = State->remove<AllocatedData>(Sym); | 
|  | } | 
|  | } | 
|  | return State; | 
|  | } | 
|  |  | 
|  | PathDiagnosticPieceRef | 
|  | MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( | 
|  | const ExplodedNode *N, BugReporterContext &BRC, | 
|  | PathSensitiveBugReport &BR) { | 
|  | const AllocationState *AS = N->getState()->get<AllocatedData>(Sym); | 
|  | if (!AS) | 
|  | return nullptr; | 
|  | const AllocationState *ASPrev = | 
|  | N->getFirstPred()->getState()->get<AllocatedData>(Sym); | 
|  | if (ASPrev) | 
|  | return nullptr; | 
|  |  | 
|  | // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the | 
|  | // allocation site. | 
|  | const CallExpr *CE = | 
|  | cast<CallExpr>(N->getLocation().castAs<StmtPoint>().getStmt()); | 
|  | const FunctionDecl *funDecl = CE->getDirectCallee(); | 
|  | assert(funDecl && "We do not support indirect function calls as of now."); | 
|  | StringRef funName = funDecl->getName(); | 
|  |  | 
|  | // Get the expression of the corresponding argument. | 
|  | unsigned Idx = getTrackedFunctionIndex(funName, true); | 
|  | assert(Idx != InvalidIdx && "This should be a call to an allocator."); | 
|  | const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param); | 
|  | PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(), | 
|  | N->getLocationContext()); | 
|  | return std::make_shared<PathDiagnosticEventPiece>(Pos, | 
|  | "Data is allocated here."); | 
|  | } | 
|  |  | 
|  | void MacOSKeychainAPIChecker::printState(raw_ostream &Out, | 
|  | ProgramStateRef State, | 
|  | const char *NL, | 
|  | const char *Sep) const { | 
|  |  | 
|  | AllocatedDataTy AMap = State->get<AllocatedData>(); | 
|  |  | 
|  | if (!AMap.isEmpty()) { | 
|  | Out << Sep << "KeychainAPIChecker :" << NL; | 
|  | for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { | 
|  | I.getKey()->dumpToStream(Out); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) { | 
|  | mgr.registerChecker<MacOSKeychainAPIChecker>(); | 
|  | } | 
|  |  | 
|  | bool ento::shouldRegisterMacOSKeychainAPIChecker(const LangOptions &LO) { | 
|  | return true; | 
|  | } |