xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg // This checker flags misuses of KeyChainAPI. In particular, the password data
97330f729Sjoerg // allocated/returned by SecKeychainItemCopyContent,
107330f729Sjoerg // SecKeychainFindGenericPassword, SecKeychainFindInternetPassword functions has
117330f729Sjoerg // to be freed using a call to SecKeychainItemFreeContent.
127330f729Sjoerg //===----------------------------------------------------------------------===//
137330f729Sjoerg 
147330f729Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
157330f729Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
167330f729Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
177330f729Sjoerg #include "clang/StaticAnalyzer/Core/CheckerManager.h"
187330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
197330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
207330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
217330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
227330f729Sjoerg #include "llvm/ADT/SmallString.h"
237330f729Sjoerg #include "llvm/Support/raw_ostream.h"
247330f729Sjoerg 
257330f729Sjoerg using namespace clang;
267330f729Sjoerg using namespace ento;
277330f729Sjoerg 
287330f729Sjoerg namespace {
297330f729Sjoerg class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
307330f729Sjoerg                                                check::PostStmt<CallExpr>,
317330f729Sjoerg                                                check::DeadSymbols,
327330f729Sjoerg                                                check::PointerEscape,
337330f729Sjoerg                                                eval::Assume> {
347330f729Sjoerg   mutable std::unique_ptr<BugType> BT;
357330f729Sjoerg 
367330f729Sjoerg public:
377330f729Sjoerg   /// AllocationState is a part of the checker specific state together with the
387330f729Sjoerg   /// MemRegion corresponding to the allocated data.
397330f729Sjoerg   struct AllocationState {
407330f729Sjoerg     /// The index of the allocator function.
417330f729Sjoerg     unsigned int AllocatorIdx;
427330f729Sjoerg     SymbolRef Region;
437330f729Sjoerg 
AllocationState__anonf1e97ce70111::MacOSKeychainAPIChecker::AllocationState447330f729Sjoerg     AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) :
457330f729Sjoerg       AllocatorIdx(Idx),
467330f729Sjoerg       Region(R) {}
477330f729Sjoerg 
operator ==__anonf1e97ce70111::MacOSKeychainAPIChecker::AllocationState487330f729Sjoerg     bool operator==(const AllocationState &X) const {
497330f729Sjoerg       return (AllocatorIdx == X.AllocatorIdx &&
507330f729Sjoerg               Region == X.Region);
517330f729Sjoerg     }
527330f729Sjoerg 
Profile__anonf1e97ce70111::MacOSKeychainAPIChecker::AllocationState537330f729Sjoerg     void Profile(llvm::FoldingSetNodeID &ID) const {
547330f729Sjoerg       ID.AddInteger(AllocatorIdx);
557330f729Sjoerg       ID.AddPointer(Region);
567330f729Sjoerg     }
577330f729Sjoerg   };
587330f729Sjoerg 
597330f729Sjoerg   void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
607330f729Sjoerg   void checkPostStmt(const CallExpr *S, CheckerContext &C) const;
617330f729Sjoerg   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
627330f729Sjoerg   ProgramStateRef checkPointerEscape(ProgramStateRef State,
637330f729Sjoerg                                      const InvalidatedSymbols &Escaped,
647330f729Sjoerg                                      const CallEvent *Call,
657330f729Sjoerg                                      PointerEscapeKind Kind) const;
667330f729Sjoerg   ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
677330f729Sjoerg                              bool Assumption) const;
687330f729Sjoerg   void printState(raw_ostream &Out, ProgramStateRef State,
69*e038c9c4Sjoerg                   const char *NL, const char *Sep) const override;
707330f729Sjoerg 
717330f729Sjoerg private:
727330f729Sjoerg   typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
737330f729Sjoerg   typedef SmallVector<AllocationPair, 2> AllocationPairVec;
747330f729Sjoerg 
757330f729Sjoerg   enum APIKind {
767330f729Sjoerg     /// Denotes functions tracked by this checker.
777330f729Sjoerg     ValidAPI = 0,
787330f729Sjoerg     /// The functions commonly/mistakenly used in place of the given API.
797330f729Sjoerg     ErrorAPI = 1,
807330f729Sjoerg     /// The functions which may allocate the data. These are tracked to reduce
817330f729Sjoerg     /// the false alarm rate.
827330f729Sjoerg     PossibleAPI = 2
837330f729Sjoerg   };
847330f729Sjoerg   /// Stores the information about the allocator and deallocator functions -
857330f729Sjoerg   /// these are the functions the checker is tracking.
867330f729Sjoerg   struct ADFunctionInfo {
877330f729Sjoerg     const char* Name;
887330f729Sjoerg     unsigned int Param;
897330f729Sjoerg     unsigned int DeallocatorIdx;
907330f729Sjoerg     APIKind Kind;
917330f729Sjoerg   };
927330f729Sjoerg   static const unsigned InvalidIdx = 100000;
937330f729Sjoerg   static const unsigned FunctionsToTrackSize = 8;
947330f729Sjoerg   static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize];
957330f729Sjoerg   /// The value, which represents no error return value for allocator functions.
967330f729Sjoerg   static const unsigned NoErr = 0;
977330f729Sjoerg 
987330f729Sjoerg   /// Given the function name, returns the index of the allocator/deallocator
997330f729Sjoerg   /// function.
1007330f729Sjoerg   static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator);
1017330f729Sjoerg 
initBugType() const1027330f729Sjoerg   inline void initBugType() const {
1037330f729Sjoerg     if (!BT)
1047330f729Sjoerg       BT.reset(new BugType(this, "Improper use of SecKeychain API",
1057330f729Sjoerg                            "API Misuse (Apple)"));
1067330f729Sjoerg   }
1077330f729Sjoerg 
1087330f729Sjoerg   void generateDeallocatorMismatchReport(const AllocationPair &AP,
1097330f729Sjoerg                                          const Expr *ArgExpr,
1107330f729Sjoerg                                          CheckerContext &C) const;
1117330f729Sjoerg 
1127330f729Sjoerg   /// Find the allocation site for Sym on the path leading to the node N.
1137330f729Sjoerg   const ExplodedNode *getAllocationNode(const ExplodedNode *N, SymbolRef Sym,
1147330f729Sjoerg                                         CheckerContext &C) const;
1157330f729Sjoerg 
1167330f729Sjoerg   std::unique_ptr<PathSensitiveBugReport>
1177330f729Sjoerg   generateAllocatedDataNotReleasedReport(const AllocationPair &AP,
1187330f729Sjoerg                                          ExplodedNode *N,
1197330f729Sjoerg                                          CheckerContext &C) const;
1207330f729Sjoerg 
1217330f729Sjoerg   /// Mark an AllocationPair interesting for diagnostic reporting.
markInteresting(PathSensitiveBugReport * R,const AllocationPair & AP) const1227330f729Sjoerg   void markInteresting(PathSensitiveBugReport *R,
1237330f729Sjoerg                        const AllocationPair &AP) const {
1247330f729Sjoerg     R->markInteresting(AP.first);
1257330f729Sjoerg     R->markInteresting(AP.second->Region);
1267330f729Sjoerg   }
1277330f729Sjoerg 
1287330f729Sjoerg   /// The bug visitor which allows us to print extra diagnostics along the
1297330f729Sjoerg   /// BugReport path. For example, showing the allocation site of the leaked
1307330f729Sjoerg   /// region.
1317330f729Sjoerg   class SecKeychainBugVisitor : public BugReporterVisitor {
1327330f729Sjoerg   protected:
1337330f729Sjoerg     // The allocated region symbol tracked by the main analysis.
1347330f729Sjoerg     SymbolRef Sym;
1357330f729Sjoerg 
1367330f729Sjoerg   public:
SecKeychainBugVisitor(SymbolRef S)1377330f729Sjoerg     SecKeychainBugVisitor(SymbolRef S) : Sym(S) {}
1387330f729Sjoerg 
Profile(llvm::FoldingSetNodeID & ID) const1397330f729Sjoerg     void Profile(llvm::FoldingSetNodeID &ID) const override {
1407330f729Sjoerg       static int X = 0;
1417330f729Sjoerg       ID.AddPointer(&X);
1427330f729Sjoerg       ID.AddPointer(Sym);
1437330f729Sjoerg     }
1447330f729Sjoerg 
1457330f729Sjoerg     PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
1467330f729Sjoerg                                      BugReporterContext &BRC,
1477330f729Sjoerg                                      PathSensitiveBugReport &BR) override;
1487330f729Sjoerg   };
1497330f729Sjoerg };
1507330f729Sjoerg }
1517330f729Sjoerg 
1527330f729Sjoerg /// ProgramState traits to store the currently allocated (and not yet freed)
1537330f729Sjoerg /// symbols. This is a map from the allocated content symbol to the
1547330f729Sjoerg /// corresponding AllocationState.
REGISTER_MAP_WITH_PROGRAMSTATE(AllocatedData,SymbolRef,MacOSKeychainAPIChecker::AllocationState)1557330f729Sjoerg REGISTER_MAP_WITH_PROGRAMSTATE(AllocatedData,
1567330f729Sjoerg                                SymbolRef,
1577330f729Sjoerg                                MacOSKeychainAPIChecker::AllocationState)
1587330f729Sjoerg 
1597330f729Sjoerg static bool isEnclosingFunctionParam(const Expr *E) {
1607330f729Sjoerg   E = E->IgnoreParenCasts();
1617330f729Sjoerg   if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
1627330f729Sjoerg     const ValueDecl *VD = DRE->getDecl();
1637330f729Sjoerg     if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
1647330f729Sjoerg       return true;
1657330f729Sjoerg   }
1667330f729Sjoerg   return false;
1677330f729Sjoerg }
1687330f729Sjoerg 
1697330f729Sjoerg const MacOSKeychainAPIChecker::ADFunctionInfo
1707330f729Sjoerg   MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
1717330f729Sjoerg     {"SecKeychainItemCopyContent", 4, 3, ValidAPI},                       // 0
1727330f729Sjoerg     {"SecKeychainFindGenericPassword", 6, 3, ValidAPI},                   // 1
1737330f729Sjoerg     {"SecKeychainFindInternetPassword", 13, 3, ValidAPI},                 // 2
1747330f729Sjoerg     {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI},              // 3
1757330f729Sjoerg     {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI},             // 4
1767330f729Sjoerg     {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI},    // 5
1777330f729Sjoerg     {"free", 0, InvalidIdx, ErrorAPI},                                    // 6
1787330f729Sjoerg     {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI},        // 7
1797330f729Sjoerg };
1807330f729Sjoerg 
getTrackedFunctionIndex(StringRef Name,bool IsAllocator)1817330f729Sjoerg unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
1827330f729Sjoerg                                                           bool IsAllocator) {
1837330f729Sjoerg   for (unsigned I = 0; I < FunctionsToTrackSize; ++I) {
1847330f729Sjoerg     ADFunctionInfo FI = FunctionsToTrack[I];
1857330f729Sjoerg     if (FI.Name != Name)
1867330f729Sjoerg       continue;
1877330f729Sjoerg     // Make sure the function is of the right type (allocator vs deallocator).
1887330f729Sjoerg     if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
1897330f729Sjoerg       return InvalidIdx;
1907330f729Sjoerg     if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
1917330f729Sjoerg       return InvalidIdx;
1927330f729Sjoerg 
1937330f729Sjoerg     return I;
1947330f729Sjoerg   }
1957330f729Sjoerg   // The function is not tracked.
1967330f729Sjoerg   return InvalidIdx;
1977330f729Sjoerg }
1987330f729Sjoerg 
isBadDeallocationArgument(const MemRegion * Arg)1997330f729Sjoerg static bool isBadDeallocationArgument(const MemRegion *Arg) {
2007330f729Sjoerg   if (!Arg)
2017330f729Sjoerg     return false;
2027330f729Sjoerg   return isa<AllocaRegion>(Arg) || isa<BlockDataRegion>(Arg) ||
2037330f729Sjoerg          isa<TypedRegion>(Arg);
2047330f729Sjoerg }
2057330f729Sjoerg 
2067330f729Sjoerg /// Given the address expression, retrieve the value it's pointing to. Assume
2077330f729Sjoerg /// that value is itself an address, and return the corresponding symbol.
getAsPointeeSymbol(const Expr * Expr,CheckerContext & C)2087330f729Sjoerg static SymbolRef getAsPointeeSymbol(const Expr *Expr,
2097330f729Sjoerg                                     CheckerContext &C) {
2107330f729Sjoerg   ProgramStateRef State = C.getState();
2117330f729Sjoerg   SVal ArgV = C.getSVal(Expr);
2127330f729Sjoerg 
2137330f729Sjoerg   if (Optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) {
2147330f729Sjoerg     StoreManager& SM = C.getStoreManager();
2157330f729Sjoerg     SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol();
2167330f729Sjoerg     if (sym)
2177330f729Sjoerg       return sym;
2187330f729Sjoerg   }
2197330f729Sjoerg   return nullptr;
2207330f729Sjoerg }
2217330f729Sjoerg 
2227330f729Sjoerg // Report deallocator mismatch. Remove the region from tracking - reporting a
2237330f729Sjoerg // missing free error after this one is redundant.
2247330f729Sjoerg void MacOSKeychainAPIChecker::
generateDeallocatorMismatchReport(const AllocationPair & AP,const Expr * ArgExpr,CheckerContext & C) const2257330f729Sjoerg   generateDeallocatorMismatchReport(const AllocationPair &AP,
2267330f729Sjoerg                                     const Expr *ArgExpr,
2277330f729Sjoerg                                     CheckerContext &C) const {
2287330f729Sjoerg   ProgramStateRef State = C.getState();
2297330f729Sjoerg   State = State->remove<AllocatedData>(AP.first);
2307330f729Sjoerg   ExplodedNode *N = C.generateNonFatalErrorNode(State);
2317330f729Sjoerg 
2327330f729Sjoerg   if (!N)
2337330f729Sjoerg     return;
2347330f729Sjoerg   initBugType();
2357330f729Sjoerg   SmallString<80> sbuf;
2367330f729Sjoerg   llvm::raw_svector_ostream os(sbuf);
2377330f729Sjoerg   unsigned int PDeallocIdx =
2387330f729Sjoerg                FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
2397330f729Sjoerg 
2407330f729Sjoerg   os << "Deallocator doesn't match the allocator: '"
2417330f729Sjoerg      << FunctionsToTrack[PDeallocIdx].Name << "' should be used.";
2427330f729Sjoerg   auto Report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
2437330f729Sjoerg   Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first));
2447330f729Sjoerg   Report->addRange(ArgExpr->getSourceRange());
2457330f729Sjoerg   markInteresting(Report.get(), AP);
2467330f729Sjoerg   C.emitReport(std::move(Report));
2477330f729Sjoerg }
2487330f729Sjoerg 
checkPreStmt(const CallExpr * CE,CheckerContext & C) const2497330f729Sjoerg void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
2507330f729Sjoerg                                            CheckerContext &C) const {
2517330f729Sjoerg   unsigned idx = InvalidIdx;
2527330f729Sjoerg   ProgramStateRef State = C.getState();
2537330f729Sjoerg 
2547330f729Sjoerg   const FunctionDecl *FD = C.getCalleeDecl(CE);
2557330f729Sjoerg   if (!FD || FD->getKind() != Decl::Function)
2567330f729Sjoerg     return;
2577330f729Sjoerg 
2587330f729Sjoerg   StringRef funName = C.getCalleeName(FD);
2597330f729Sjoerg   if (funName.empty())
2607330f729Sjoerg     return;
2617330f729Sjoerg 
2627330f729Sjoerg   // If it is a call to an allocator function, it could be a double allocation.
2637330f729Sjoerg   idx = getTrackedFunctionIndex(funName, true);
2647330f729Sjoerg   if (idx != InvalidIdx) {
2657330f729Sjoerg     unsigned paramIdx = FunctionsToTrack[idx].Param;
2667330f729Sjoerg     if (CE->getNumArgs() <= paramIdx)
2677330f729Sjoerg       return;
2687330f729Sjoerg 
2697330f729Sjoerg     const Expr *ArgExpr = CE->getArg(paramIdx);
2707330f729Sjoerg     if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C))
2717330f729Sjoerg       if (const AllocationState *AS = State->get<AllocatedData>(V)) {
2727330f729Sjoerg         // Remove the value from the state. The new symbol will be added for
2737330f729Sjoerg         // tracking when the second allocator is processed in checkPostStmt().
2747330f729Sjoerg         State = State->remove<AllocatedData>(V);
2757330f729Sjoerg         ExplodedNode *N = C.generateNonFatalErrorNode(State);
2767330f729Sjoerg         if (!N)
2777330f729Sjoerg           return;
2787330f729Sjoerg         initBugType();
2797330f729Sjoerg         SmallString<128> sbuf;
2807330f729Sjoerg         llvm::raw_svector_ostream os(sbuf);
2817330f729Sjoerg         unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
2827330f729Sjoerg         os << "Allocated data should be released before another call to "
2837330f729Sjoerg             << "the allocator: missing a call to '"
2847330f729Sjoerg             << FunctionsToTrack[DIdx].Name
2857330f729Sjoerg             << "'.";
2867330f729Sjoerg         auto Report =
2877330f729Sjoerg             std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
2887330f729Sjoerg         Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(V));
2897330f729Sjoerg         Report->addRange(ArgExpr->getSourceRange());
2907330f729Sjoerg         Report->markInteresting(AS->Region);
2917330f729Sjoerg         C.emitReport(std::move(Report));
2927330f729Sjoerg       }
2937330f729Sjoerg     return;
2947330f729Sjoerg   }
2957330f729Sjoerg 
2967330f729Sjoerg   // Is it a call to one of deallocator functions?
2977330f729Sjoerg   idx = getTrackedFunctionIndex(funName, false);
2987330f729Sjoerg   if (idx == InvalidIdx)
2997330f729Sjoerg     return;
3007330f729Sjoerg 
3017330f729Sjoerg   unsigned paramIdx = FunctionsToTrack[idx].Param;
3027330f729Sjoerg   if (CE->getNumArgs() <= paramIdx)
3037330f729Sjoerg     return;
3047330f729Sjoerg 
3057330f729Sjoerg   // Check the argument to the deallocator.
3067330f729Sjoerg   const Expr *ArgExpr = CE->getArg(paramIdx);
3077330f729Sjoerg   SVal ArgSVal = C.getSVal(ArgExpr);
3087330f729Sjoerg 
3097330f729Sjoerg   // Undef is reported by another checker.
3107330f729Sjoerg   if (ArgSVal.isUndef())
3117330f729Sjoerg     return;
3127330f729Sjoerg 
3137330f729Sjoerg   SymbolRef ArgSM = ArgSVal.getAsLocSymbol();
3147330f729Sjoerg 
3157330f729Sjoerg   // If the argument is coming from the heap, globals, or unknown, do not
3167330f729Sjoerg   // report it.
3177330f729Sjoerg   bool RegionArgIsBad = false;
3187330f729Sjoerg   if (!ArgSM) {
3197330f729Sjoerg     if (!isBadDeallocationArgument(ArgSVal.getAsRegion()))
3207330f729Sjoerg       return;
3217330f729Sjoerg     RegionArgIsBad = true;
3227330f729Sjoerg   }
3237330f729Sjoerg 
3247330f729Sjoerg   // Is the argument to the call being tracked?
3257330f729Sjoerg   const AllocationState *AS = State->get<AllocatedData>(ArgSM);
3267330f729Sjoerg   if (!AS)
3277330f729Sjoerg     return;
3287330f729Sjoerg 
3297330f729Sjoerg   // TODO: We might want to report double free here.
3307330f729Sjoerg   // (that would involve tracking all the freed symbols in the checker state).
3317330f729Sjoerg   if (RegionArgIsBad) {
3327330f729Sjoerg     // It is possible that this is a false positive - the argument might
3337330f729Sjoerg     // have entered as an enclosing function parameter.
3347330f729Sjoerg     if (isEnclosingFunctionParam(ArgExpr))
3357330f729Sjoerg       return;
3367330f729Sjoerg 
3377330f729Sjoerg     ExplodedNode *N = C.generateNonFatalErrorNode(State);
3387330f729Sjoerg     if (!N)
3397330f729Sjoerg       return;
3407330f729Sjoerg     initBugType();
3417330f729Sjoerg     auto Report = std::make_unique<PathSensitiveBugReport>(
3427330f729Sjoerg         *BT, "Trying to free data which has not been allocated.", N);
3437330f729Sjoerg     Report->addRange(ArgExpr->getSourceRange());
3447330f729Sjoerg     if (AS)
3457330f729Sjoerg       Report->markInteresting(AS->Region);
3467330f729Sjoerg     C.emitReport(std::move(Report));
3477330f729Sjoerg     return;
3487330f729Sjoerg   }
3497330f729Sjoerg 
3507330f729Sjoerg   // Process functions which might deallocate.
3517330f729Sjoerg   if (FunctionsToTrack[idx].Kind == PossibleAPI) {
3527330f729Sjoerg 
3537330f729Sjoerg     if (funName == "CFStringCreateWithBytesNoCopy") {
3547330f729Sjoerg       const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts();
3557330f729Sjoerg       // NULL ~ default deallocator, so warn.
3567330f729Sjoerg       if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(),
3577330f729Sjoerg           Expr::NPC_ValueDependentIsNotNull)) {
3587330f729Sjoerg         const AllocationPair AP = std::make_pair(ArgSM, AS);
3597330f729Sjoerg         generateDeallocatorMismatchReport(AP, ArgExpr, C);
3607330f729Sjoerg         return;
3617330f729Sjoerg       }
3627330f729Sjoerg       // One of the default allocators, so warn.
3637330f729Sjoerg       if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
3647330f729Sjoerg         StringRef DeallocatorName = DE->getFoundDecl()->getName();
3657330f729Sjoerg         if (DeallocatorName == "kCFAllocatorDefault" ||
3667330f729Sjoerg             DeallocatorName == "kCFAllocatorSystemDefault" ||
3677330f729Sjoerg             DeallocatorName == "kCFAllocatorMalloc") {
3687330f729Sjoerg           const AllocationPair AP = std::make_pair(ArgSM, AS);
3697330f729Sjoerg           generateDeallocatorMismatchReport(AP, ArgExpr, C);
3707330f729Sjoerg           return;
3717330f729Sjoerg         }
3727330f729Sjoerg         // If kCFAllocatorNull, which does not deallocate, we still have to
3737330f729Sjoerg         // find the deallocator.
3747330f729Sjoerg         if (DE->getFoundDecl()->getName() == "kCFAllocatorNull")
3757330f729Sjoerg           return;
3767330f729Sjoerg       }
3777330f729Sjoerg       // In all other cases, assume the user supplied a correct deallocator
3787330f729Sjoerg       // that will free memory so stop tracking.
3797330f729Sjoerg       State = State->remove<AllocatedData>(ArgSM);
3807330f729Sjoerg       C.addTransition(State);
3817330f729Sjoerg       return;
3827330f729Sjoerg     }
3837330f729Sjoerg 
3847330f729Sjoerg     llvm_unreachable("We know of no other possible APIs.");
3857330f729Sjoerg   }
3867330f729Sjoerg 
3877330f729Sjoerg   // The call is deallocating a value we previously allocated, so remove it
3887330f729Sjoerg   // from the next state.
3897330f729Sjoerg   State = State->remove<AllocatedData>(ArgSM);
3907330f729Sjoerg 
3917330f729Sjoerg   // Check if the proper deallocator is used.
3927330f729Sjoerg   unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
3937330f729Sjoerg   if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) {
3947330f729Sjoerg     const AllocationPair AP = std::make_pair(ArgSM, AS);
3957330f729Sjoerg     generateDeallocatorMismatchReport(AP, ArgExpr, C);
3967330f729Sjoerg     return;
3977330f729Sjoerg   }
3987330f729Sjoerg 
3997330f729Sjoerg   C.addTransition(State);
4007330f729Sjoerg }
4017330f729Sjoerg 
checkPostStmt(const CallExpr * CE,CheckerContext & C) const4027330f729Sjoerg void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
4037330f729Sjoerg                                             CheckerContext &C) const {
4047330f729Sjoerg   ProgramStateRef State = C.getState();
4057330f729Sjoerg   const FunctionDecl *FD = C.getCalleeDecl(CE);
4067330f729Sjoerg   if (!FD || FD->getKind() != Decl::Function)
4077330f729Sjoerg     return;
4087330f729Sjoerg 
4097330f729Sjoerg   StringRef funName = C.getCalleeName(FD);
4107330f729Sjoerg 
4117330f729Sjoerg   // If a value has been allocated, add it to the set for tracking.
4127330f729Sjoerg   unsigned idx = getTrackedFunctionIndex(funName, true);
4137330f729Sjoerg   if (idx == InvalidIdx)
4147330f729Sjoerg     return;
4157330f729Sjoerg 
4167330f729Sjoerg   const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
4177330f729Sjoerg   // If the argument entered as an enclosing function parameter, skip it to
4187330f729Sjoerg   // avoid false positives.
4197330f729Sjoerg   if (isEnclosingFunctionParam(ArgExpr) &&
4207330f729Sjoerg       C.getLocationContext()->getParent() == nullptr)
4217330f729Sjoerg     return;
4227330f729Sjoerg 
4237330f729Sjoerg   if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) {
4247330f729Sjoerg     // If the argument points to something that's not a symbolic region, it
4257330f729Sjoerg     // can be:
4267330f729Sjoerg     //  - unknown (cannot reason about it)
4277330f729Sjoerg     //  - undefined (already reported by other checker)
4287330f729Sjoerg     //  - constant (null - should not be tracked,
4297330f729Sjoerg     //              other constant will generate a compiler warning)
4307330f729Sjoerg     //  - goto (should be reported by other checker)
4317330f729Sjoerg 
4327330f729Sjoerg     // The call return value symbol should stay alive for as long as the
4337330f729Sjoerg     // allocated value symbol, since our diagnostics depend on the value
4347330f729Sjoerg     // returned by the call. Ex: Data should only be freed if noErr was
4357330f729Sjoerg     // returned during allocation.)
4367330f729Sjoerg     SymbolRef RetStatusSymbol = C.getSVal(CE).getAsSymbol();
4377330f729Sjoerg     C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol);
4387330f729Sjoerg 
4397330f729Sjoerg     // Track the allocated value in the checker state.
4407330f729Sjoerg     State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx,
4417330f729Sjoerg                                                          RetStatusSymbol));
4427330f729Sjoerg     assert(State);
4437330f729Sjoerg     C.addTransition(State);
4447330f729Sjoerg   }
4457330f729Sjoerg }
4467330f729Sjoerg 
4477330f729Sjoerg // TODO: This logic is the same as in Malloc checker.
4487330f729Sjoerg const ExplodedNode *
getAllocationNode(const ExplodedNode * N,SymbolRef Sym,CheckerContext & C) const4497330f729Sjoerg MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N,
4507330f729Sjoerg                                            SymbolRef Sym,
4517330f729Sjoerg                                            CheckerContext &C) const {
4527330f729Sjoerg   const LocationContext *LeakContext = N->getLocationContext();
4537330f729Sjoerg   // Walk the ExplodedGraph backwards and find the first node that referred to
4547330f729Sjoerg   // the tracked symbol.
4557330f729Sjoerg   const ExplodedNode *AllocNode = N;
4567330f729Sjoerg 
4577330f729Sjoerg   while (N) {
4587330f729Sjoerg     if (!N->getState()->get<AllocatedData>(Sym))
4597330f729Sjoerg       break;
4607330f729Sjoerg     // Allocation node, is the last node in the current or parent context in
4617330f729Sjoerg     // which the symbol was tracked.
4627330f729Sjoerg     const LocationContext *NContext = N->getLocationContext();
4637330f729Sjoerg     if (NContext == LeakContext ||
4647330f729Sjoerg         NContext->isParentOf(LeakContext))
4657330f729Sjoerg       AllocNode = N;
4667330f729Sjoerg     N = N->pred_empty() ? nullptr : *(N->pred_begin());
4677330f729Sjoerg   }
4687330f729Sjoerg 
4697330f729Sjoerg   return AllocNode;
4707330f729Sjoerg }
4717330f729Sjoerg 
4727330f729Sjoerg std::unique_ptr<PathSensitiveBugReport>
generateAllocatedDataNotReleasedReport(const AllocationPair & AP,ExplodedNode * N,CheckerContext & C) const4737330f729Sjoerg MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
4747330f729Sjoerg     const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const {
4757330f729Sjoerg   const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
4767330f729Sjoerg   initBugType();
4777330f729Sjoerg   SmallString<70> sbuf;
4787330f729Sjoerg   llvm::raw_svector_ostream os(sbuf);
4797330f729Sjoerg   os << "Allocated data is not released: missing a call to '"
4807330f729Sjoerg       << FunctionsToTrack[FI.DeallocatorIdx].Name << "'.";
4817330f729Sjoerg 
4827330f729Sjoerg   // Most bug reports are cached at the location where they occurred.
4837330f729Sjoerg   // With leaks, we want to unique them by the location where they were
4847330f729Sjoerg   // allocated, and only report a single path.
4857330f729Sjoerg   PathDiagnosticLocation LocUsedForUniqueing;
4867330f729Sjoerg   const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C);
4877330f729Sjoerg   const Stmt *AllocStmt = AllocNode->getStmtForDiagnostics();
4887330f729Sjoerg 
4897330f729Sjoerg   if (AllocStmt)
4907330f729Sjoerg     LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt,
4917330f729Sjoerg                                               C.getSourceManager(),
4927330f729Sjoerg                                               AllocNode->getLocationContext());
4937330f729Sjoerg 
4947330f729Sjoerg   auto Report = std::make_unique<PathSensitiveBugReport>(
4957330f729Sjoerg       *BT, os.str(), N, LocUsedForUniqueing,
4967330f729Sjoerg       AllocNode->getLocationContext()->getDecl());
4977330f729Sjoerg 
4987330f729Sjoerg   Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first));
4997330f729Sjoerg   markInteresting(Report.get(), AP);
5007330f729Sjoerg   return Report;
5017330f729Sjoerg }
5027330f729Sjoerg 
5037330f729Sjoerg /// If the return symbol is assumed to be error, remove the allocated info
5047330f729Sjoerg /// from consideration.
evalAssume(ProgramStateRef State,SVal Cond,bool Assumption) const5057330f729Sjoerg ProgramStateRef MacOSKeychainAPIChecker::evalAssume(ProgramStateRef State,
5067330f729Sjoerg                                                     SVal Cond,
5077330f729Sjoerg                                                     bool Assumption) const {
5087330f729Sjoerg   AllocatedDataTy AMap = State->get<AllocatedData>();
5097330f729Sjoerg   if (AMap.isEmpty())
5107330f729Sjoerg     return State;
5117330f729Sjoerg 
512*e038c9c4Sjoerg   auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymbol());
5137330f729Sjoerg   if (!CondBSE)
5147330f729Sjoerg     return State;
5157330f729Sjoerg   BinaryOperator::Opcode OpCode = CondBSE->getOpcode();
5167330f729Sjoerg   if (OpCode != BO_EQ && OpCode != BO_NE)
5177330f729Sjoerg     return State;
5187330f729Sjoerg 
5197330f729Sjoerg   // Match for a restricted set of patterns for cmparison of error codes.
5207330f729Sjoerg   // Note, the comparisons of type '0 == st' are transformed into SymIntExpr.
5217330f729Sjoerg   SymbolRef ReturnSymbol = nullptr;
5227330f729Sjoerg   if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
5237330f729Sjoerg     const llvm::APInt &RHS = SIE->getRHS();
5247330f729Sjoerg     bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) ||
5257330f729Sjoerg                            (OpCode == BO_NE && RHS == NoErr);
5267330f729Sjoerg     if (!Assumption)
5277330f729Sjoerg       ErrorIsReturned = !ErrorIsReturned;
5287330f729Sjoerg     if (ErrorIsReturned)
5297330f729Sjoerg       ReturnSymbol = SIE->getLHS();
5307330f729Sjoerg   }
5317330f729Sjoerg 
5327330f729Sjoerg   if (ReturnSymbol)
5337330f729Sjoerg     for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
5347330f729Sjoerg       if (ReturnSymbol == I->second.Region)
5357330f729Sjoerg         State = State->remove<AllocatedData>(I->first);
5367330f729Sjoerg     }
5377330f729Sjoerg 
5387330f729Sjoerg   return State;
5397330f729Sjoerg }
5407330f729Sjoerg 
checkDeadSymbols(SymbolReaper & SR,CheckerContext & C) const5417330f729Sjoerg void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
5427330f729Sjoerg                                                CheckerContext &C) const {
5437330f729Sjoerg   ProgramStateRef State = C.getState();
5447330f729Sjoerg   AllocatedDataTy AMap = State->get<AllocatedData>();
5457330f729Sjoerg   if (AMap.isEmpty())
5467330f729Sjoerg     return;
5477330f729Sjoerg 
5487330f729Sjoerg   bool Changed = false;
5497330f729Sjoerg   AllocationPairVec Errors;
5507330f729Sjoerg   for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
5517330f729Sjoerg     if (!SR.isDead(I->first))
5527330f729Sjoerg       continue;
5537330f729Sjoerg 
5547330f729Sjoerg     Changed = true;
5557330f729Sjoerg     State = State->remove<AllocatedData>(I->first);
5567330f729Sjoerg     // If the allocated symbol is null do not report.
5577330f729Sjoerg     ConstraintManager &CMgr = State->getConstraintManager();
5587330f729Sjoerg     ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey());
5597330f729Sjoerg     if (AllocFailed.isConstrainedTrue())
5607330f729Sjoerg       continue;
5617330f729Sjoerg     Errors.push_back(std::make_pair(I->first, &I->second));
5627330f729Sjoerg   }
5637330f729Sjoerg   if (!Changed) {
5647330f729Sjoerg     // Generate the new, cleaned up state.
5657330f729Sjoerg     C.addTransition(State);
5667330f729Sjoerg     return;
5677330f729Sjoerg   }
5687330f729Sjoerg 
5697330f729Sjoerg   static CheckerProgramPointTag Tag(this, "DeadSymbolsLeak");
5707330f729Sjoerg   ExplodedNode *N = C.generateNonFatalErrorNode(C.getState(), &Tag);
5717330f729Sjoerg   if (!N)
5727330f729Sjoerg     return;
5737330f729Sjoerg 
5747330f729Sjoerg   // Generate the error reports.
5757330f729Sjoerg   for (const auto &P : Errors)
5767330f729Sjoerg     C.emitReport(generateAllocatedDataNotReleasedReport(P, N, C));
5777330f729Sjoerg 
5787330f729Sjoerg   // Generate the new, cleaned up state.
5797330f729Sjoerg   C.addTransition(State, N);
5807330f729Sjoerg }
5817330f729Sjoerg 
checkPointerEscape(ProgramStateRef State,const InvalidatedSymbols & Escaped,const CallEvent * Call,PointerEscapeKind Kind) const5827330f729Sjoerg ProgramStateRef MacOSKeychainAPIChecker::checkPointerEscape(
5837330f729Sjoerg     ProgramStateRef State, const InvalidatedSymbols &Escaped,
5847330f729Sjoerg     const CallEvent *Call, PointerEscapeKind Kind) const {
5857330f729Sjoerg   // FIXME: This branch doesn't make any sense at all, but it is an overfitted
5867330f729Sjoerg   // replacement for a previous overfitted code that was making even less sense.
5877330f729Sjoerg   if (!Call || Call->getDecl())
5887330f729Sjoerg     return State;
5897330f729Sjoerg 
5907330f729Sjoerg   for (auto I : State->get<AllocatedData>()) {
5917330f729Sjoerg     SymbolRef Sym = I.first;
5927330f729Sjoerg     if (Escaped.count(Sym))
5937330f729Sjoerg       State = State->remove<AllocatedData>(Sym);
5947330f729Sjoerg 
5957330f729Sjoerg     // This checker is special. Most checkers in fact only track symbols of
5967330f729Sjoerg     // SymbolConjured type, eg. symbols returned from functions such as
5977330f729Sjoerg     // malloc(). This checker tracks symbols returned as out-parameters.
5987330f729Sjoerg     //
5997330f729Sjoerg     // When a function is evaluated conservatively, the out-parameter's pointee
6007330f729Sjoerg     // base region gets invalidated with a SymbolConjured. If the base region is
6017330f729Sjoerg     // larger than the region we're interested in, the value we're interested in
6027330f729Sjoerg     // would be SymbolDerived based on that SymbolConjured. However, such
6037330f729Sjoerg     // SymbolDerived will never be listed in the Escaped set when the base
6047330f729Sjoerg     // region is invalidated because ExprEngine doesn't know which symbols
6057330f729Sjoerg     // were derived from a given symbol, while there can be infinitely many
6067330f729Sjoerg     // valid symbols derived from any given symbol.
6077330f729Sjoerg     //
6087330f729Sjoerg     // Hence the extra boilerplate: remove the derived symbol when its parent
6097330f729Sjoerg     // symbol escapes.
6107330f729Sjoerg     //
6117330f729Sjoerg     if (const auto *SD = dyn_cast<SymbolDerived>(Sym)) {
6127330f729Sjoerg       SymbolRef ParentSym = SD->getParentSymbol();
6137330f729Sjoerg       if (Escaped.count(ParentSym))
6147330f729Sjoerg         State = State->remove<AllocatedData>(Sym);
6157330f729Sjoerg     }
6167330f729Sjoerg   }
6177330f729Sjoerg   return State;
6187330f729Sjoerg }
6197330f729Sjoerg 
6207330f729Sjoerg PathDiagnosticPieceRef
VisitNode(const ExplodedNode * N,BugReporterContext & BRC,PathSensitiveBugReport & BR)6217330f729Sjoerg MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
6227330f729Sjoerg     const ExplodedNode *N, BugReporterContext &BRC,
6237330f729Sjoerg     PathSensitiveBugReport &BR) {
6247330f729Sjoerg   const AllocationState *AS = N->getState()->get<AllocatedData>(Sym);
6257330f729Sjoerg   if (!AS)
6267330f729Sjoerg     return nullptr;
6277330f729Sjoerg   const AllocationState *ASPrev =
6287330f729Sjoerg       N->getFirstPred()->getState()->get<AllocatedData>(Sym);
6297330f729Sjoerg   if (ASPrev)
6307330f729Sjoerg     return nullptr;
6317330f729Sjoerg 
6327330f729Sjoerg   // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the
6337330f729Sjoerg   // allocation site.
6347330f729Sjoerg   const CallExpr *CE =
6357330f729Sjoerg       cast<CallExpr>(N->getLocation().castAs<StmtPoint>().getStmt());
6367330f729Sjoerg   const FunctionDecl *funDecl = CE->getDirectCallee();
6377330f729Sjoerg   assert(funDecl && "We do not support indirect function calls as of now.");
6387330f729Sjoerg   StringRef funName = funDecl->getName();
6397330f729Sjoerg 
6407330f729Sjoerg   // Get the expression of the corresponding argument.
6417330f729Sjoerg   unsigned Idx = getTrackedFunctionIndex(funName, true);
6427330f729Sjoerg   assert(Idx != InvalidIdx && "This should be a call to an allocator.");
6437330f729Sjoerg   const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param);
6447330f729Sjoerg   PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(),
6457330f729Sjoerg                              N->getLocationContext());
6467330f729Sjoerg   return std::make_shared<PathDiagnosticEventPiece>(Pos,
6477330f729Sjoerg                                                     "Data is allocated here.");
6487330f729Sjoerg }
6497330f729Sjoerg 
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const6507330f729Sjoerg void MacOSKeychainAPIChecker::printState(raw_ostream &Out,
6517330f729Sjoerg                                          ProgramStateRef State,
6527330f729Sjoerg                                          const char *NL,
6537330f729Sjoerg                                          const char *Sep) const {
6547330f729Sjoerg 
6557330f729Sjoerg   AllocatedDataTy AMap = State->get<AllocatedData>();
6567330f729Sjoerg 
6577330f729Sjoerg   if (!AMap.isEmpty()) {
6587330f729Sjoerg     Out << Sep << "KeychainAPIChecker :" << NL;
6597330f729Sjoerg     for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
6607330f729Sjoerg       I.getKey()->dumpToStream(Out);
6617330f729Sjoerg     }
6627330f729Sjoerg   }
6637330f729Sjoerg }
6647330f729Sjoerg 
6657330f729Sjoerg 
registerMacOSKeychainAPIChecker(CheckerManager & mgr)6667330f729Sjoerg void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
6677330f729Sjoerg   mgr.registerChecker<MacOSKeychainAPIChecker>();
6687330f729Sjoerg }
6697330f729Sjoerg 
shouldRegisterMacOSKeychainAPIChecker(const CheckerManager & mgr)670*e038c9c4Sjoerg bool ento::shouldRegisterMacOSKeychainAPIChecker(const CheckerManager &mgr) {
6717330f729Sjoerg   return true;
6727330f729Sjoerg }
673