| Anna Zaks | 08be9b9 | 2011-08-04 17:28:06 +0000 | [diff] [blame] | 1 | //==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==// | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 2 | // | 
|  | 3 | //                     The LLVM Compiler Infrastructure | 
|  | 4 | // | 
|  | 5 | // This file is distributed under the University of Illinois Open Source | 
|  | 6 | // License. See LICENSE.TXT for details. | 
|  | 7 | // | 
|  | 8 | //===----------------------------------------------------------------------===// | 
|  | 9 | // This checker flags misuses of KeyChainAPI. In particular, the password data | 
|  | 10 | // allocated/returned by SecKeychainItemCopyContent, | 
|  | 11 | // SecKeychainFindGenericPassword, SecKeychainFindInternetPassword functions has | 
|  | 12 | // to be freed using a call to SecKeychainItemFreeContent. | 
|  | 13 | //===----------------------------------------------------------------------===// | 
|  | 14 |  | 
|  | 15 | #include "ClangSACheckers.h" | 
| Chandler Carruth | 3a02247 | 2012-12-04 09:13:33 +0000 | [diff] [blame] | 16 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 17 | #include "clang/StaticAnalyzer/Core/Checker.h" | 
|  | 18 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" | 
|  | 19 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" | 
| Ted Kremenek | 001fd5b | 2011-08-15 22:09:50 +0000 | [diff] [blame] | 20 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" | 
|  | 21 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" | 
| Benjamin Kramer | 4903802 | 2012-02-04 13:45:25 +0000 | [diff] [blame] | 22 | #include "llvm/ADT/SmallString.h" | 
| Benjamin Kramer | 444a130 | 2012-12-01 17:12:56 +0000 | [diff] [blame] | 23 | #include "llvm/Support/raw_ostream.h" | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 24 |  | 
|  | 25 | using namespace clang; | 
|  | 26 | using namespace ento; | 
|  | 27 |  | 
|  | 28 | namespace { | 
|  | 29 | class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 30 | check::PostStmt<CallExpr>, | 
| Anna Zaks | 14b1af5 | 2017-01-13 00:50:41 +0000 | [diff] [blame] | 31 | check::DeadSymbols, | 
|  | 32 | eval::Assume> { | 
| Ahmed Charles | b898432 | 2014-03-07 20:03:18 +0000 | [diff] [blame] | 33 | mutable std::unique_ptr<BugType> BT; | 
| Anna Zaks | 388c18e | 2011-08-04 00:26:57 +0000 | [diff] [blame] | 34 |  | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 35 | public: | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 36 | /// AllocationState is a part of the checker specific state together with the | 
|  | 37 | /// MemRegion corresponding to the allocated data. | 
|  | 38 | struct AllocationState { | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 39 | /// The index of the allocator function. | 
|  | 40 | unsigned int AllocatorIdx; | 
| Anna Zaks | 5be8a4d | 2011-08-25 00:59:06 +0000 | [diff] [blame] | 41 | SymbolRef Region; | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 42 |  | 
|  | 43 | AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) : | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 44 | AllocatorIdx(Idx), | 
| Anna Zaks | 5be8a4d | 2011-08-25 00:59:06 +0000 | [diff] [blame] | 45 | Region(R) {} | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 46 |  | 
|  | 47 | bool operator==(const AllocationState &X) const { | 
| Anna Zaks | 5be8a4d | 2011-08-25 00:59:06 +0000 | [diff] [blame] | 48 | return (AllocatorIdx == X.AllocatorIdx && | 
|  | 49 | Region == X.Region); | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 50 | } | 
| Anna Zaks | 5be8a4d | 2011-08-25 00:59:06 +0000 | [diff] [blame] | 51 |  | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 52 | void Profile(llvm::FoldingSetNodeID &ID) const { | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 53 | ID.AddInteger(AllocatorIdx); | 
| Anna Zaks | 5be8a4d | 2011-08-25 00:59:06 +0000 | [diff] [blame] | 54 | ID.AddPointer(Region); | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 55 | } | 
|  | 56 | }; | 
|  | 57 |  | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 58 | void checkPreStmt(const CallExpr *S, CheckerContext &C) const; | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 59 | void checkPostStmt(const CallExpr *S, CheckerContext &C) const; | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 60 | void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; | 
| Anna Zaks | 14b1af5 | 2017-01-13 00:50:41 +0000 | [diff] [blame] | 61 | ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond, | 
|  | 62 | bool Assumption) const; | 
|  | 63 | void printState(raw_ostream &Out, ProgramStateRef State, | 
|  | 64 | const char *NL, const char *Sep) const; | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 65 |  | 
|  | 66 | private: | 
| Anna Zaks | f880cff | 2011-08-24 21:58:55 +0000 | [diff] [blame] | 67 | typedef std::pair<SymbolRef, const AllocationState*> AllocationPair; | 
| Dmitri Gribenko | f857950 | 2013-01-12 19:30:44 +0000 | [diff] [blame] | 68 | typedef SmallVector<AllocationPair, 2> AllocationPairVec; | 
| Anna Zaks | be46089 | 2011-08-24 20:52:46 +0000 | [diff] [blame] | 69 |  | 
|  | 70 | enum APIKind { | 
| Anna Zaks | 0897a23 | 2011-08-24 00:06:27 +0000 | [diff] [blame] | 71 | /// Denotes functions tracked by this checker. | 
|  | 72 | ValidAPI = 0, | 
|  | 73 | /// The functions commonly/mistakenly used in place of the given API. | 
|  | 74 | ErrorAPI = 1, | 
|  | 75 | /// The functions which may allocate the data. These are tracked to reduce | 
|  | 76 | /// the false alarm rate. | 
|  | 77 | PossibleAPI = 2 | 
|  | 78 | }; | 
| Anna Zaks | 08be9b9 | 2011-08-04 17:28:06 +0000 | [diff] [blame] | 79 | /// Stores the information about the allocator and deallocator functions - | 
|  | 80 | /// these are the functions the checker is tracking. | 
|  | 81 | struct ADFunctionInfo { | 
|  | 82 | const char* Name; | 
|  | 83 | unsigned int Param; | 
|  | 84 | unsigned int DeallocatorIdx; | 
| Anna Zaks | 0897a23 | 2011-08-24 00:06:27 +0000 | [diff] [blame] | 85 | APIKind Kind; | 
| Anna Zaks | 08be9b9 | 2011-08-04 17:28:06 +0000 | [diff] [blame] | 86 | }; | 
|  | 87 | static const unsigned InvalidIdx = 100000; | 
| Anna Zaks | 0897a23 | 2011-08-24 00:06:27 +0000 | [diff] [blame] | 88 | static const unsigned FunctionsToTrackSize = 8; | 
| Anna Zaks | 08be9b9 | 2011-08-04 17:28:06 +0000 | [diff] [blame] | 89 | static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize]; | 
| Anna Zaks | 177ecfa | 2011-08-05 23:52:45 +0000 | [diff] [blame] | 90 | /// The value, which represents no error return value for allocator functions. | 
|  | 91 | static const unsigned NoErr = 0; | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 92 |  | 
| Anna Zaks | 08be9b9 | 2011-08-04 17:28:06 +0000 | [diff] [blame] | 93 | /// Given the function name, returns the index of the allocator/deallocator | 
|  | 94 | /// function. | 
| Anna Zaks | be46089 | 2011-08-24 20:52:46 +0000 | [diff] [blame] | 95 | static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator); | 
| Anna Zaks | 388c18e | 2011-08-04 00:26:57 +0000 | [diff] [blame] | 96 |  | 
|  | 97 | inline void initBugType() const { | 
|  | 98 | if (!BT) | 
| Alexander Kornienko | 4aca9b1 | 2014-02-11 21:49:21 +0000 | [diff] [blame] | 99 | BT.reset(new BugType(this, "Improper use of SecKeychain API", | 
| Anna Zaks | 8ef07e5 | 2013-04-03 19:28:22 +0000 | [diff] [blame] | 100 | "API Misuse (Apple)")); | 
| Anna Zaks | 388c18e | 2011-08-04 00:26:57 +0000 | [diff] [blame] | 101 | } | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 102 |  | 
| Anna Zaks | 85913db | 2011-08-25 00:32:42 +0000 | [diff] [blame] | 103 | void generateDeallocatorMismatchReport(const AllocationPair &AP, | 
| Anna Zaks | bb16701 | 2011-08-23 23:47:36 +0000 | [diff] [blame] | 104 | const Expr *ArgExpr, | 
| Anna Zaks | 85913db | 2011-08-25 00:32:42 +0000 | [diff] [blame] | 105 | CheckerContext &C) const; | 
| Anna Zaks | bb16701 | 2011-08-23 23:47:36 +0000 | [diff] [blame] | 106 |  | 
| Anna Zaks | 4b062cb | 2012-02-23 22:53:29 +0000 | [diff] [blame] | 107 | /// Find the allocation site for Sym on the path leading to the node N. | 
| Anna Zaks | a043d0c | 2013-01-08 00:25:29 +0000 | [diff] [blame] | 108 | const ExplodedNode *getAllocationNode(const ExplodedNode *N, SymbolRef Sym, | 
|  | 109 | CheckerContext &C) const; | 
| Anna Zaks | 4b062cb | 2012-02-23 22:53:29 +0000 | [diff] [blame] | 110 |  | 
| Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 111 | std::unique_ptr<BugReport> generateAllocatedDataNotReleasedReport( | 
|  | 112 | const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const; | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 113 |  | 
| Ted Kremenek | 1e809b4 | 2012-03-09 01:13:14 +0000 | [diff] [blame] | 114 | /// Mark an AllocationPair interesting for diagnostic reporting. | 
|  | 115 | void markInteresting(BugReport *R, const AllocationPair &AP) const { | 
|  | 116 | R->markInteresting(AP.first); | 
|  | 117 | R->markInteresting(AP.second->Region); | 
|  | 118 | } | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 119 |  | 
| Anna Zaks | be46089 | 2011-08-24 20:52:46 +0000 | [diff] [blame] | 120 | /// The bug visitor which allows us to print extra diagnostics along the | 
|  | 121 | /// BugReport path. For example, showing the allocation site of the leaked | 
|  | 122 | /// region. | 
| George Karpenkov | 70ec1dd | 2018-06-26 21:12:08 +0000 | [diff] [blame] | 123 | class SecKeychainBugVisitor : public BugReporterVisitor { | 
| Anna Zaks | be46089 | 2011-08-24 20:52:46 +0000 | [diff] [blame] | 124 | protected: | 
|  | 125 | // The allocated region symbol tracked by the main analysis. | 
|  | 126 | SymbolRef Sym; | 
|  | 127 |  | 
|  | 128 | public: | 
|  | 129 | SecKeychainBugVisitor(SymbolRef S) : Sym(S) {} | 
| Anna Zaks | be46089 | 2011-08-24 20:52:46 +0000 | [diff] [blame] | 130 |  | 
| Craig Topper | fb6b25b | 2014-03-15 04:29:04 +0000 | [diff] [blame] | 131 | void Profile(llvm::FoldingSetNodeID &ID) const override { | 
| Anna Zaks | be46089 | 2011-08-24 20:52:46 +0000 | [diff] [blame] | 132 | static int X = 0; | 
|  | 133 | ID.AddPointer(&X); | 
|  | 134 | ID.AddPointer(Sym); | 
|  | 135 | } | 
|  | 136 |  | 
| David Blaikie | 0a0c275 | 2017-01-05 17:26:53 +0000 | [diff] [blame] | 137 | std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, | 
|  | 138 | const ExplodedNode *PrevN, | 
|  | 139 | BugReporterContext &BRC, | 
|  | 140 | BugReport &BR) override; | 
| Anna Zaks | be46089 | 2011-08-24 20:52:46 +0000 | [diff] [blame] | 141 | }; | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 142 | }; | 
| Alexander Kornienko | ab9db51 | 2015-06-22 23:07:51 +0000 | [diff] [blame] | 143 | } | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 144 |  | 
| Anna Zaks | 5443a64 | 2011-08-15 23:23:15 +0000 | [diff] [blame] | 145 | /// ProgramState traits to store the currently allocated (and not yet freed) | 
|  | 146 | /// symbols. This is a map from the allocated content symbol to the | 
|  | 147 | /// corresponding AllocationState. | 
| Jordan Rose | 0c153cb | 2012-11-02 01:54:06 +0000 | [diff] [blame] | 148 | REGISTER_MAP_WITH_PROGRAMSTATE(AllocatedData, | 
|  | 149 | SymbolRef, | 
|  | 150 | MacOSKeychainAPIChecker::AllocationState) | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 151 |  | 
| Anna Zaks | 388c18e | 2011-08-04 00:26:57 +0000 | [diff] [blame] | 152 | static bool isEnclosingFunctionParam(const Expr *E) { | 
|  | 153 | E = E->IgnoreParenCasts(); | 
|  | 154 | if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { | 
|  | 155 | const ValueDecl *VD = DRE->getDecl(); | 
|  | 156 | if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD)) | 
|  | 157 | return true; | 
|  | 158 | } | 
|  | 159 | return false; | 
|  | 160 | } | 
|  | 161 |  | 
| Anna Zaks | 08be9b9 | 2011-08-04 17:28:06 +0000 | [diff] [blame] | 162 | const MacOSKeychainAPIChecker::ADFunctionInfo | 
|  | 163 | MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = { | 
| Anna Zaks | 0897a23 | 2011-08-24 00:06:27 +0000 | [diff] [blame] | 164 | {"SecKeychainItemCopyContent", 4, 3, ValidAPI},                       // 0 | 
|  | 165 | {"SecKeychainFindGenericPassword", 6, 3, ValidAPI},                   // 1 | 
|  | 166 | {"SecKeychainFindInternetPassword", 13, 3, ValidAPI},                 // 2 | 
|  | 167 | {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI},              // 3 | 
|  | 168 | {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI},             // 4 | 
|  | 169 | {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI},    // 5 | 
|  | 170 | {"free", 0, InvalidIdx, ErrorAPI},                                    // 6 | 
|  | 171 | {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI},        // 7 | 
| Anna Zaks | 08be9b9 | 2011-08-04 17:28:06 +0000 | [diff] [blame] | 172 | }; | 
|  | 173 |  | 
|  | 174 | unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name, | 
| Anna Zaks | be46089 | 2011-08-24 20:52:46 +0000 | [diff] [blame] | 175 | bool IsAllocator) { | 
| Anna Zaks | 08be9b9 | 2011-08-04 17:28:06 +0000 | [diff] [blame] | 176 | for (unsigned I = 0; I < FunctionsToTrackSize; ++I) { | 
|  | 177 | ADFunctionInfo FI = FunctionsToTrack[I]; | 
|  | 178 | if (FI.Name != Name) | 
|  | 179 | continue; | 
|  | 180 | // Make sure the function is of the right type (allocator vs deallocator). | 
|  | 181 | if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx)) | 
|  | 182 | return InvalidIdx; | 
|  | 183 | if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx)) | 
|  | 184 | return InvalidIdx; | 
|  | 185 |  | 
|  | 186 | return I; | 
|  | 187 | } | 
|  | 188 | // The function is not tracked. | 
|  | 189 | return InvalidIdx; | 
|  | 190 | } | 
|  | 191 |  | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 192 | static bool isBadDeallocationArgument(const MemRegion *Arg) { | 
| Jordy Rose | f80b2cc | 2012-03-11 00:08:24 +0000 | [diff] [blame] | 193 | if (!Arg) | 
|  | 194 | return false; | 
| Alexander Kornienko | 9c10490 | 2015-12-28 13:06:58 +0000 | [diff] [blame] | 195 | return isa<AllocaRegion>(Arg) || isa<BlockDataRegion>(Arg) || | 
|  | 196 | isa<TypedRegion>(Arg); | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 197 | } | 
| Jordy Rose | f80b2cc | 2012-03-11 00:08:24 +0000 | [diff] [blame] | 198 |  | 
| Anna Zaks | c52bed1 | 2011-08-05 00:37:00 +0000 | [diff] [blame] | 199 | /// Given the address expression, retrieve the value it's pointing to. Assume | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 200 | /// that value is itself an address, and return the corresponding symbol. | 
|  | 201 | static SymbolRef getAsPointeeSymbol(const Expr *Expr, | 
|  | 202 | CheckerContext &C) { | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 203 | ProgramStateRef State = C.getState(); | 
| George Karpenkov | d703ec9 | 2018-01-17 20:27:29 +0000 | [diff] [blame] | 204 | SVal ArgV = C.getSVal(Expr); | 
| Anna Zaks | 177ecfa | 2011-08-05 23:52:45 +0000 | [diff] [blame] | 205 |  | 
| David Blaikie | 05785d1 | 2013-02-20 22:23:23 +0000 | [diff] [blame] | 206 | if (Optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) { | 
| Anna Zaks | c52bed1 | 2011-08-05 00:37:00 +0000 | [diff] [blame] | 207 | StoreManager& SM = C.getStoreManager(); | 
| Jordy Rose | f80b2cc | 2012-03-11 00:08:24 +0000 | [diff] [blame] | 208 | SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol(); | 
|  | 209 | if (sym) | 
|  | 210 | return sym; | 
| Anna Zaks | c52bed1 | 2011-08-05 00:37:00 +0000 | [diff] [blame] | 211 | } | 
| Craig Topper | 0dbb783 | 2014-05-27 02:45:47 +0000 | [diff] [blame] | 212 | return nullptr; | 
| Anna Zaks | c52bed1 | 2011-08-05 00:37:00 +0000 | [diff] [blame] | 213 | } | 
|  | 214 |  | 
| Anna Zaks | bb16701 | 2011-08-23 23:47:36 +0000 | [diff] [blame] | 215 | // Report deallocator mismatch. Remove the region from tracking - reporting a | 
|  | 216 | // missing free error after this one is redundant. | 
|  | 217 | void MacOSKeychainAPIChecker:: | 
| Anna Zaks | 85913db | 2011-08-25 00:32:42 +0000 | [diff] [blame] | 218 | generateDeallocatorMismatchReport(const AllocationPair &AP, | 
| Anna Zaks | bb16701 | 2011-08-23 23:47:36 +0000 | [diff] [blame] | 219 | const Expr *ArgExpr, | 
| Anna Zaks | 85913db | 2011-08-25 00:32:42 +0000 | [diff] [blame] | 220 | CheckerContext &C) const { | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 221 | ProgramStateRef State = C.getState(); | 
| Anna Zaks | 85913db | 2011-08-25 00:32:42 +0000 | [diff] [blame] | 222 | State = State->remove<AllocatedData>(AP.first); | 
| Devin Coughlin | e39bd40 | 2015-09-16 22:03:05 +0000 | [diff] [blame] | 223 | ExplodedNode *N = C.generateNonFatalErrorNode(State); | 
| Anna Zaks | bb16701 | 2011-08-23 23:47:36 +0000 | [diff] [blame] | 224 |  | 
|  | 225 | if (!N) | 
|  | 226 | return; | 
|  | 227 | initBugType(); | 
| Dylan Noblesmith | 2c1dd27 | 2012-02-05 02:13:05 +0000 | [diff] [blame] | 228 | SmallString<80> sbuf; | 
| Anna Zaks | bb16701 | 2011-08-23 23:47:36 +0000 | [diff] [blame] | 229 | llvm::raw_svector_ostream os(sbuf); | 
| Anna Zaks | 85913db | 2011-08-25 00:32:42 +0000 | [diff] [blame] | 230 | unsigned int PDeallocIdx = | 
|  | 231 | FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx; | 
| Anna Zaks | bb16701 | 2011-08-23 23:47:36 +0000 | [diff] [blame] | 232 |  | 
|  | 233 | os << "Deallocator doesn't match the allocator: '" | 
|  | 234 | << FunctionsToTrack[PDeallocIdx].Name << "' should be used."; | 
| Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 235 | auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); | 
| David Blaikie | 91e7902 | 2014-09-04 23:54:33 +0000 | [diff] [blame] | 236 | Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first)); | 
| Anna Zaks | bb16701 | 2011-08-23 23:47:36 +0000 | [diff] [blame] | 237 | Report->addRange(ArgExpr->getSourceRange()); | 
| Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 238 | markInteresting(Report.get(), AP); | 
|  | 239 | C.emitReport(std::move(Report)); | 
| Anna Zaks | bb16701 | 2011-08-23 23:47:36 +0000 | [diff] [blame] | 240 | } | 
|  | 241 |  | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 242 | void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, | 
|  | 243 | CheckerContext &C) const { | 
| Anna Zaks | c52bed1 | 2011-08-05 00:37:00 +0000 | [diff] [blame] | 244 | unsigned idx = InvalidIdx; | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 245 | ProgramStateRef State = C.getState(); | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 246 |  | 
| Jordan Rose | 6cd16c5 | 2012-07-10 23:13:01 +0000 | [diff] [blame] | 247 | const FunctionDecl *FD = C.getCalleeDecl(CE); | 
|  | 248 | if (!FD || FD->getKind() != Decl::Function) | 
|  | 249 | return; | 
| Ted Kremenek | 3a0678e | 2015-09-08 03:50:52 +0000 | [diff] [blame] | 250 |  | 
| Jordan Rose | 6cd16c5 | 2012-07-10 23:13:01 +0000 | [diff] [blame] | 251 | StringRef funName = C.getCalleeName(FD); | 
| Anna Zaks | c6aa531 | 2011-12-01 05:57:37 +0000 | [diff] [blame] | 252 | if (funName.empty()) | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 253 | return; | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 254 |  | 
| Anna Zaks | c52bed1 | 2011-08-05 00:37:00 +0000 | [diff] [blame] | 255 | // If it is a call to an allocator function, it could be a double allocation. | 
|  | 256 | idx = getTrackedFunctionIndex(funName, true); | 
|  | 257 | if (idx != InvalidIdx) { | 
| Anna Zaks | 33f0632 | 2015-02-05 01:02:56 +0000 | [diff] [blame] | 258 | unsigned paramIdx = FunctionsToTrack[idx].Param; | 
|  | 259 | if (CE->getNumArgs() <= paramIdx) | 
|  | 260 | return; | 
|  | 261 |  | 
|  | 262 | const Expr *ArgExpr = CE->getArg(paramIdx); | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 263 | if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) | 
| Anna Zaks | c52bed1 | 2011-08-05 00:37:00 +0000 | [diff] [blame] | 264 | if (const AllocationState *AS = State->get<AllocatedData>(V)) { | 
| Anna Zaks | 14b1af5 | 2017-01-13 00:50:41 +0000 | [diff] [blame] | 265 | // Remove the value from the state. The new symbol will be added for | 
|  | 266 | // tracking when the second allocator is processed in checkPostStmt(). | 
|  | 267 | State = State->remove<AllocatedData>(V); | 
|  | 268 | ExplodedNode *N = C.generateNonFatalErrorNode(State); | 
|  | 269 | if (!N) | 
|  | 270 | return; | 
|  | 271 | initBugType(); | 
|  | 272 | SmallString<128> sbuf; | 
|  | 273 | llvm::raw_svector_ostream os(sbuf); | 
|  | 274 | unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; | 
|  | 275 | os << "Allocated data should be released before another call to " | 
|  | 276 | << "the allocator: missing a call to '" | 
|  | 277 | << FunctionsToTrack[DIdx].Name | 
|  | 278 | << "'."; | 
|  | 279 | auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); | 
|  | 280 | Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V)); | 
|  | 281 | Report->addRange(ArgExpr->getSourceRange()); | 
|  | 282 | Report->markInteresting(AS->Region); | 
|  | 283 | C.emitReport(std::move(Report)); | 
| Anna Zaks | c52bed1 | 2011-08-05 00:37:00 +0000 | [diff] [blame] | 284 | } | 
|  | 285 | return; | 
|  | 286 | } | 
|  | 287 |  | 
|  | 288 | // Is it a call to one of deallocator functions? | 
|  | 289 | idx = getTrackedFunctionIndex(funName, false); | 
| Anna Zaks | 08be9b9 | 2011-08-04 17:28:06 +0000 | [diff] [blame] | 290 | if (idx == InvalidIdx) | 
| Anna Zaks | 4aa34a53 | 2011-08-04 00:31:38 +0000 | [diff] [blame] | 291 | return; | 
|  | 292 |  | 
| Anna Zaks | 33f0632 | 2015-02-05 01:02:56 +0000 | [diff] [blame] | 293 | unsigned paramIdx = FunctionsToTrack[idx].Param; | 
|  | 294 | if (CE->getNumArgs() <= paramIdx) | 
|  | 295 | return; | 
|  | 296 |  | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 297 | // Check the argument to the deallocator. | 
| Anna Zaks | 33f0632 | 2015-02-05 01:02:56 +0000 | [diff] [blame] | 298 | const Expr *ArgExpr = CE->getArg(paramIdx); | 
| George Karpenkov | d703ec9 | 2018-01-17 20:27:29 +0000 | [diff] [blame] | 299 | SVal ArgSVal = C.getSVal(ArgExpr); | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 300 |  | 
|  | 301 | // Undef is reported by another checker. | 
|  | 302 | if (ArgSVal.isUndef()) | 
|  | 303 | return; | 
|  | 304 |  | 
| Jordy Rose | f80b2cc | 2012-03-11 00:08:24 +0000 | [diff] [blame] | 305 | SymbolRef ArgSM = ArgSVal.getAsLocSymbol(); | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 306 |  | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 307 | // If the argument is coming from the heap, globals, or unknown, do not | 
|  | 308 | // report it. | 
| Jordy Rose | f80b2cc | 2012-03-11 00:08:24 +0000 | [diff] [blame] | 309 | bool RegionArgIsBad = false; | 
|  | 310 | if (!ArgSM) { | 
|  | 311 | if (!isBadDeallocationArgument(ArgSVal.getAsRegion())) | 
|  | 312 | return; | 
|  | 313 | RegionArgIsBad = true; | 
|  | 314 | } | 
| Anna Zaks | 4aa34a53 | 2011-08-04 00:31:38 +0000 | [diff] [blame] | 315 |  | 
| Anna Zaks | 0897a23 | 2011-08-24 00:06:27 +0000 | [diff] [blame] | 316 | // Is the argument to the call being tracked? | 
|  | 317 | const AllocationState *AS = State->get<AllocatedData>(ArgSM); | 
| Anna Zaks | 14b1af5 | 2017-01-13 00:50:41 +0000 | [diff] [blame] | 318 | if (!AS) | 
| Anna Zaks | 0897a23 | 2011-08-24 00:06:27 +0000 | [diff] [blame] | 319 | return; | 
| Anna Zaks | 14b1af5 | 2017-01-13 00:50:41 +0000 | [diff] [blame] | 320 |  | 
|  | 321 | // TODO: We might want to report double free here. | 
| Anna Zaks | 5443a64 | 2011-08-15 23:23:15 +0000 | [diff] [blame] | 322 | // (that would involve tracking all the freed symbols in the checker state). | 
| Anna Zaks | 14b1af5 | 2017-01-13 00:50:41 +0000 | [diff] [blame] | 323 | if (RegionArgIsBad) { | 
| Anna Zaks | 4aa34a53 | 2011-08-04 00:31:38 +0000 | [diff] [blame] | 324 | // It is possible that this is a false positive - the argument might | 
|  | 325 | // have entered as an enclosing function parameter. | 
|  | 326 | if (isEnclosingFunctionParam(ArgExpr)) | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 327 | return; | 
| Anna Zaks | 388c18e | 2011-08-04 00:26:57 +0000 | [diff] [blame] | 328 |  | 
| Devin Coughlin | e39bd40 | 2015-09-16 22:03:05 +0000 | [diff] [blame] | 329 | ExplodedNode *N = C.generateNonFatalErrorNode(State); | 
| Anna Zaks | 4aa34a53 | 2011-08-04 00:31:38 +0000 | [diff] [blame] | 330 | if (!N) | 
|  | 331 | return; | 
|  | 332 | initBugType(); | 
| Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 333 | auto Report = llvm::make_unique<BugReport>( | 
|  | 334 | *BT, "Trying to free data which has not been allocated.", N); | 
| Anna Zaks | 4aa34a53 | 2011-08-04 00:31:38 +0000 | [diff] [blame] | 335 | Report->addRange(ArgExpr->getSourceRange()); | 
| Ted Kremenek | 1e809b4 | 2012-03-09 01:13:14 +0000 | [diff] [blame] | 336 | if (AS) | 
|  | 337 | Report->markInteresting(AS->Region); | 
| Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 338 | C.emitReport(std::move(Report)); | 
| Anna Zaks | 08be9b9 | 2011-08-04 17:28:06 +0000 | [diff] [blame] | 339 | return; | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 340 | } | 
| Anna Zaks | 4aa34a53 | 2011-08-04 00:31:38 +0000 | [diff] [blame] | 341 |  | 
| Anna Zaks | 0897a23 | 2011-08-24 00:06:27 +0000 | [diff] [blame] | 342 | // Process functions which might deallocate. | 
|  | 343 | if (FunctionsToTrack[idx].Kind == PossibleAPI) { | 
|  | 344 |  | 
|  | 345 | if (funName == "CFStringCreateWithBytesNoCopy") { | 
|  | 346 | const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); | 
|  | 347 | // NULL ~ default deallocator, so warn. | 
|  | 348 | if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), | 
|  | 349 | Expr::NPC_ValueDependentIsNotNull)) { | 
| Anna Zaks | 85913db | 2011-08-25 00:32:42 +0000 | [diff] [blame] | 350 | const AllocationPair AP = std::make_pair(ArgSM, AS); | 
|  | 351 | generateDeallocatorMismatchReport(AP, ArgExpr, C); | 
| Anna Zaks | 0897a23 | 2011-08-24 00:06:27 +0000 | [diff] [blame] | 352 | return; | 
|  | 353 | } | 
|  | 354 | // One of the default allocators, so warn. | 
|  | 355 | if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { | 
|  | 356 | StringRef DeallocatorName = DE->getFoundDecl()->getName(); | 
|  | 357 | if (DeallocatorName == "kCFAllocatorDefault" || | 
|  | 358 | DeallocatorName == "kCFAllocatorSystemDefault" || | 
|  | 359 | DeallocatorName == "kCFAllocatorMalloc") { | 
| Anna Zaks | 85913db | 2011-08-25 00:32:42 +0000 | [diff] [blame] | 360 | const AllocationPair AP = std::make_pair(ArgSM, AS); | 
|  | 361 | generateDeallocatorMismatchReport(AP, ArgExpr, C); | 
| Anna Zaks | 0897a23 | 2011-08-24 00:06:27 +0000 | [diff] [blame] | 362 | return; | 
|  | 363 | } | 
|  | 364 | // If kCFAllocatorNull, which does not deallocate, we still have to | 
| Anna Zaks | 030e65d | 2013-01-07 19:13:00 +0000 | [diff] [blame] | 365 | // find the deallocator. | 
|  | 366 | if (DE->getFoundDecl()->getName() == "kCFAllocatorNull") | 
| Anna Zaks | 0897a23 | 2011-08-24 00:06:27 +0000 | [diff] [blame] | 367 | return; | 
| Anna Zaks | 0897a23 | 2011-08-24 00:06:27 +0000 | [diff] [blame] | 368 | } | 
| Anna Zaks | 030e65d | 2013-01-07 19:13:00 +0000 | [diff] [blame] | 369 | // In all other cases, assume the user supplied a correct deallocator | 
|  | 370 | // that will free memory so stop tracking. | 
|  | 371 | State = State->remove<AllocatedData>(ArgSM); | 
|  | 372 | C.addTransition(State); | 
|  | 373 | return; | 
| Anna Zaks | 0897a23 | 2011-08-24 00:06:27 +0000 | [diff] [blame] | 374 | } | 
| Anna Zaks | 030e65d | 2013-01-07 19:13:00 +0000 | [diff] [blame] | 375 |  | 
|  | 376 | llvm_unreachable("We know of no other possible APIs."); | 
| Anna Zaks | 0897a23 | 2011-08-24 00:06:27 +0000 | [diff] [blame] | 377 | } | 
|  | 378 |  | 
| Anna Zaks | 5443a64 | 2011-08-15 23:23:15 +0000 | [diff] [blame] | 379 | // The call is deallocating a value we previously allocated, so remove it | 
|  | 380 | // from the next state. | 
|  | 381 | State = State->remove<AllocatedData>(ArgSM); | 
|  | 382 |  | 
| Anna Zaks | bb16701 | 2011-08-23 23:47:36 +0000 | [diff] [blame] | 383 | // Check if the proper deallocator is used. | 
| Anna Zaks | c686177 | 2011-08-04 21:53:01 +0000 | [diff] [blame] | 384 | unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; | 
| Anna Zaks | 0897a23 | 2011-08-24 00:06:27 +0000 | [diff] [blame] | 385 | if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { | 
| Anna Zaks | 85913db | 2011-08-25 00:32:42 +0000 | [diff] [blame] | 386 | const AllocationPair AP = std::make_pair(ArgSM, AS); | 
|  | 387 | generateDeallocatorMismatchReport(AP, ArgExpr, C); | 
| Anna Zaks | c686177 | 2011-08-04 21:53:01 +0000 | [diff] [blame] | 388 | return; | 
|  | 389 | } | 
|  | 390 |  | 
| Anna Zaks | da4c8d6 | 2011-10-26 21:06:34 +0000 | [diff] [blame] | 391 | C.addTransition(State); | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 392 | } | 
|  | 393 |  | 
|  | 394 | void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, | 
|  | 395 | CheckerContext &C) const { | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 396 | ProgramStateRef State = C.getState(); | 
| Jordan Rose | 6cd16c5 | 2012-07-10 23:13:01 +0000 | [diff] [blame] | 397 | const FunctionDecl *FD = C.getCalleeDecl(CE); | 
|  | 398 | if (!FD || FD->getKind() != Decl::Function) | 
|  | 399 | return; | 
|  | 400 |  | 
|  | 401 | StringRef funName = C.getCalleeName(FD); | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 402 |  | 
|  | 403 | // If a value has been allocated, add it to the set for tracking. | 
| Anna Zaks | 08be9b9 | 2011-08-04 17:28:06 +0000 | [diff] [blame] | 404 | unsigned idx = getTrackedFunctionIndex(funName, true); | 
|  | 405 | if (idx == InvalidIdx) | 
| Anna Zaks | 4aa34a53 | 2011-08-04 00:31:38 +0000 | [diff] [blame] | 406 | return; | 
| Anna Zaks | 388c18e | 2011-08-04 00:26:57 +0000 | [diff] [blame] | 407 |  | 
| Anna Zaks | 08be9b9 | 2011-08-04 17:28:06 +0000 | [diff] [blame] | 408 | const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); | 
| Anna Zaks | 59d741f | 2011-08-12 22:47:22 +0000 | [diff] [blame] | 409 | // If the argument entered as an enclosing function parameter, skip it to | 
|  | 410 | // avoid false positives. | 
| Anna Zaks | 19a6667 | 2012-02-21 00:00:44 +0000 | [diff] [blame] | 411 | if (isEnclosingFunctionParam(ArgExpr) && | 
| Craig Topper | 0dbb783 | 2014-05-27 02:45:47 +0000 | [diff] [blame] | 412 | C.getLocationContext()->getParent() == nullptr) | 
| Anna Zaks | 59d741f | 2011-08-12 22:47:22 +0000 | [diff] [blame] | 413 | return; | 
|  | 414 |  | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 415 | if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) { | 
|  | 416 | // If the argument points to something that's not a symbolic region, it | 
|  | 417 | // can be: | 
| Anna Zaks | 4aa34a53 | 2011-08-04 00:31:38 +0000 | [diff] [blame] | 418 | //  - unknown (cannot reason about it) | 
|  | 419 | //  - undefined (already reported by other checker) | 
| Anna Zaks | 08be9b9 | 2011-08-04 17:28:06 +0000 | [diff] [blame] | 420 | //  - constant (null - should not be tracked, | 
|  | 421 | //              other constant will generate a compiler warning) | 
| Anna Zaks | 4aa34a53 | 2011-08-04 00:31:38 +0000 | [diff] [blame] | 422 | //  - goto (should be reported by other checker) | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 423 |  | 
|  | 424 | // The call return value symbol should stay alive for as long as the | 
|  | 425 | // allocated value symbol, since our diagnostics depend on the value | 
|  | 426 | // returned by the call. Ex: Data should only be freed if noErr was | 
|  | 427 | // returned during allocation.) | 
| George Karpenkov | d703ec9 | 2018-01-17 20:27:29 +0000 | [diff] [blame] | 428 | SymbolRef RetStatusSymbol = C.getSVal(CE).getAsSymbol(); | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 429 | C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol); | 
|  | 430 |  | 
|  | 431 | // Track the allocated value in the checker state. | 
|  | 432 | State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx, | 
| Anna Zaks | c94894f | 2011-08-12 21:14:26 +0000 | [diff] [blame] | 433 | RetStatusSymbol)); | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 434 | assert(State); | 
| Anna Zaks | da4c8d6 | 2011-10-26 21:06:34 +0000 | [diff] [blame] | 435 | C.addTransition(State); | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 436 | } | 
|  | 437 | } | 
|  | 438 |  | 
| Anna Zaks | 6ca4fd5 | 2012-02-28 03:07:06 +0000 | [diff] [blame] | 439 | // TODO: This logic is the same as in Malloc checker. | 
| Anna Zaks | a043d0c | 2013-01-08 00:25:29 +0000 | [diff] [blame] | 440 | const ExplodedNode * | 
|  | 441 | MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N, | 
| Anna Zaks | 4b062cb | 2012-02-23 22:53:29 +0000 | [diff] [blame] | 442 | SymbolRef Sym, | 
|  | 443 | CheckerContext &C) const { | 
| Anna Zaks | 6ca4fd5 | 2012-02-28 03:07:06 +0000 | [diff] [blame] | 444 | const LocationContext *LeakContext = N->getLocationContext(); | 
| Anna Zaks | 4b062cb | 2012-02-23 22:53:29 +0000 | [diff] [blame] | 445 | // Walk the ExplodedGraph backwards and find the first node that referred to | 
|  | 446 | // the tracked symbol. | 
|  | 447 | const ExplodedNode *AllocNode = N; | 
|  | 448 |  | 
|  | 449 | while (N) { | 
|  | 450 | if (!N->getState()->get<AllocatedData>(Sym)) | 
|  | 451 | break; | 
| Anna Zaks | 486a0ff | 2015-02-05 01:02:53 +0000 | [diff] [blame] | 452 | // Allocation node, is the last node in the current or parent context in | 
|  | 453 | // which the symbol was tracked. | 
|  | 454 | const LocationContext *NContext = N->getLocationContext(); | 
|  | 455 | if (NContext == LeakContext || | 
|  | 456 | NContext->isParentOf(LeakContext)) | 
| Anna Zaks | 6ca4fd5 | 2012-02-28 03:07:06 +0000 | [diff] [blame] | 457 | AllocNode = N; | 
| Craig Topper | 0dbb783 | 2014-05-27 02:45:47 +0000 | [diff] [blame] | 458 | N = N->pred_empty() ? nullptr : *(N->pred_begin()); | 
| Anna Zaks | 4b062cb | 2012-02-23 22:53:29 +0000 | [diff] [blame] | 459 | } | 
|  | 460 |  | 
| Anna Zaks | a043d0c | 2013-01-08 00:25:29 +0000 | [diff] [blame] | 461 | return AllocNode; | 
| Anna Zaks | 4b062cb | 2012-02-23 22:53:29 +0000 | [diff] [blame] | 462 | } | 
|  | 463 |  | 
| Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 464 | std::unique_ptr<BugReport> | 
|  | 465 | MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport( | 
|  | 466 | const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const { | 
| Anna Zaks | f880cff | 2011-08-24 21:58:55 +0000 | [diff] [blame] | 467 | const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 468 | initBugType(); | 
| Dylan Noblesmith | 2c1dd27 | 2012-02-05 02:13:05 +0000 | [diff] [blame] | 469 | SmallString<70> sbuf; | 
| Anna Zaks | 29f9b7a | 2011-08-15 18:42:00 +0000 | [diff] [blame] | 470 | llvm::raw_svector_ostream os(sbuf); | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 471 | os << "Allocated data is not released: missing a call to '" | 
|  | 472 | << FunctionsToTrack[FI.DeallocatorIdx].Name << "'."; | 
| Anna Zaks | 4b062cb | 2012-02-23 22:53:29 +0000 | [diff] [blame] | 473 |  | 
|  | 474 | // Most bug reports are cached at the location where they occurred. | 
|  | 475 | // With leaks, we want to unique them by the location where they were | 
|  | 476 | // allocated, and only report a single path. | 
| Anna Zaks | 6ca4fd5 | 2012-02-28 03:07:06 +0000 | [diff] [blame] | 477 | PathDiagnosticLocation LocUsedForUniqueing; | 
| Anna Zaks | a043d0c | 2013-01-08 00:25:29 +0000 | [diff] [blame] | 478 | const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C); | 
| Gabor Horvath | 6ee4f90 | 2016-08-18 07:54:50 +0000 | [diff] [blame] | 479 | const Stmt *AllocStmt = PathDiagnosticLocation::getStmt(AllocNode); | 
| Anna Zaks | 4b062cb | 2012-02-23 22:53:29 +0000 | [diff] [blame] | 480 |  | 
| Anna Zaks | a043d0c | 2013-01-08 00:25:29 +0000 | [diff] [blame] | 481 | if (AllocStmt) | 
|  | 482 | LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt, | 
|  | 483 | C.getSourceManager(), | 
|  | 484 | AllocNode->getLocationContext()); | 
|  | 485 |  | 
| Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 486 | auto Report = | 
|  | 487 | llvm::make_unique<BugReport>(*BT, os.str(), N, LocUsedForUniqueing, | 
|  | 488 | AllocNode->getLocationContext()->getDecl()); | 
| Anna Zaks | a043d0c | 2013-01-08 00:25:29 +0000 | [diff] [blame] | 489 |  | 
| David Blaikie | 91e7902 | 2014-09-04 23:54:33 +0000 | [diff] [blame] | 490 | Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first)); | 
| Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 491 | markInteresting(Report.get(), AP); | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 492 | return Report; | 
|  | 493 | } | 
|  | 494 |  | 
| Anna Zaks | 14b1af5 | 2017-01-13 00:50:41 +0000 | [diff] [blame] | 495 | /// If the return symbol is assumed to be error, remove the allocated info | 
|  | 496 | /// from consideration. | 
|  | 497 | ProgramStateRef MacOSKeychainAPIChecker::evalAssume(ProgramStateRef State, | 
|  | 498 | SVal Cond, | 
|  | 499 | bool Assumption) const { | 
|  | 500 | AllocatedDataTy AMap = State->get<AllocatedData>(); | 
|  | 501 | if (AMap.isEmpty()) | 
|  | 502 | return State; | 
|  | 503 |  | 
|  | 504 | auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr()); | 
|  | 505 | if (!CondBSE) | 
|  | 506 | return State; | 
|  | 507 | BinaryOperator::Opcode OpCode = CondBSE->getOpcode(); | 
|  | 508 | if (OpCode != BO_EQ && OpCode != BO_NE) | 
|  | 509 | return State; | 
|  | 510 |  | 
|  | 511 | // Match for a restricted set of patterns for cmparison of error codes. | 
|  | 512 | // Note, the comparisons of type '0 == st' are transformed into SymIntExpr. | 
|  | 513 | SymbolRef ReturnSymbol = nullptr; | 
|  | 514 | if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) { | 
|  | 515 | const llvm::APInt &RHS = SIE->getRHS(); | 
|  | 516 | bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) || | 
|  | 517 | (OpCode == BO_NE && RHS == NoErr); | 
|  | 518 | if (!Assumption) | 
|  | 519 | ErrorIsReturned = !ErrorIsReturned; | 
|  | 520 | if (ErrorIsReturned) | 
|  | 521 | ReturnSymbol = SIE->getLHS(); | 
|  | 522 | } | 
|  | 523 |  | 
|  | 524 | if (ReturnSymbol) | 
|  | 525 | for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { | 
|  | 526 | if (ReturnSymbol == I->second.Region) | 
|  | 527 | State = State->remove<AllocatedData>(I->first); | 
|  | 528 | } | 
|  | 529 |  | 
|  | 530 | return State; | 
|  | 531 | } | 
|  | 532 |  | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 533 | void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, | 
|  | 534 | CheckerContext &C) const { | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 535 | ProgramStateRef State = C.getState(); | 
| Anna Zaks | 14b1af5 | 2017-01-13 00:50:41 +0000 | [diff] [blame] | 536 | AllocatedDataTy AMap = State->get<AllocatedData>(); | 
|  | 537 | if (AMap.isEmpty()) | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 538 | return; | 
|  | 539 |  | 
|  | 540 | bool Changed = false; | 
| Anna Zaks | be46089 | 2011-08-24 20:52:46 +0000 | [diff] [blame] | 541 | AllocationPairVec Errors; | 
| Anna Zaks | 14b1af5 | 2017-01-13 00:50:41 +0000 | [diff] [blame] | 542 | for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { | 
|  | 543 | if (!SR.isDead(I->first)) | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 544 | continue; | 
|  | 545 |  | 
|  | 546 | Changed = true; | 
|  | 547 | State = State->remove<AllocatedData>(I->first); | 
| Anna Zaks | 14b1af5 | 2017-01-13 00:50:41 +0000 | [diff] [blame] | 548 | // If the allocated symbol is null do not report. | 
| Jordan Rose | 14fe9f3 | 2012-11-01 00:18:27 +0000 | [diff] [blame] | 549 | ConstraintManager &CMgr = State->getConstraintManager(); | 
|  | 550 | ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey()); | 
| Anna Zaks | 14b1af5 | 2017-01-13 00:50:41 +0000 | [diff] [blame] | 551 | if (AllocFailed.isConstrainedTrue()) | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 552 | continue; | 
| Anna Zaks | f880cff | 2011-08-24 21:58:55 +0000 | [diff] [blame] | 553 | Errors.push_back(std::make_pair(I->first, &I->second)); | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 554 | } | 
| Anna Zaks | 4b062cb | 2012-02-23 22:53:29 +0000 | [diff] [blame] | 555 | if (!Changed) { | 
|  | 556 | // Generate the new, cleaned up state. | 
|  | 557 | C.addTransition(State); | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 558 | return; | 
| Anna Zaks | 4b062cb | 2012-02-23 22:53:29 +0000 | [diff] [blame] | 559 | } | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 560 |  | 
| Anton Yartsev | 6a61922 | 2014-02-17 18:25:34 +0000 | [diff] [blame] | 561 | static CheckerProgramPointTag Tag(this, "DeadSymbolsLeak"); | 
| Devin Coughlin | e39bd40 | 2015-09-16 22:03:05 +0000 | [diff] [blame] | 562 | ExplodedNode *N = C.generateNonFatalErrorNode(C.getState(), &Tag); | 
|  | 563 | if (!N) | 
|  | 564 | return; | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 565 |  | 
|  | 566 | // Generate the error reports. | 
| Benjamin Kramer | 72e6431 | 2015-09-24 14:48:49 +0000 | [diff] [blame] | 567 | for (const auto &P : Errors) | 
| Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 568 | C.emitReport(generateAllocatedDataNotReleasedReport(P, N, C)); | 
| Anna Zaks | 4b062cb | 2012-02-23 22:53:29 +0000 | [diff] [blame] | 569 |  | 
|  | 570 | // Generate the new, cleaned up state. | 
|  | 571 | C.addTransition(State, N); | 
| Anna Zaks | fdd0aca | 2011-08-12 21:56:43 +0000 | [diff] [blame] | 572 | } | 
|  | 573 |  | 
| David Blaikie | 0a0c275 | 2017-01-05 17:26:53 +0000 | [diff] [blame] | 574 | std::shared_ptr<PathDiagnosticPiece> | 
|  | 575 | MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( | 
|  | 576 | const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, | 
|  | 577 | BugReport &BR) { | 
| Anna Zaks | be46089 | 2011-08-24 20:52:46 +0000 | [diff] [blame] | 578 | const AllocationState *AS = N->getState()->get<AllocatedData>(Sym); | 
|  | 579 | if (!AS) | 
| Craig Topper | 0dbb783 | 2014-05-27 02:45:47 +0000 | [diff] [blame] | 580 | return nullptr; | 
| Anna Zaks | be46089 | 2011-08-24 20:52:46 +0000 | [diff] [blame] | 581 | const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym); | 
|  | 582 | if (ASPrev) | 
| Craig Topper | 0dbb783 | 2014-05-27 02:45:47 +0000 | [diff] [blame] | 583 | return nullptr; | 
| Anna Zaks | be46089 | 2011-08-24 20:52:46 +0000 | [diff] [blame] | 584 |  | 
|  | 585 | // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the | 
|  | 586 | // allocation site. | 
| David Blaikie | 87396b9 | 2013-02-21 22:23:56 +0000 | [diff] [blame] | 587 | const CallExpr *CE = | 
|  | 588 | cast<CallExpr>(N->getLocation().castAs<StmtPoint>().getStmt()); | 
| Anna Zaks | be46089 | 2011-08-24 20:52:46 +0000 | [diff] [blame] | 589 | const FunctionDecl *funDecl = CE->getDirectCallee(); | 
|  | 590 | assert(funDecl && "We do not support indirect function calls as of now."); | 
|  | 591 | StringRef funName = funDecl->getName(); | 
|  | 592 |  | 
|  | 593 | // Get the expression of the corresponding argument. | 
|  | 594 | unsigned Idx = getTrackedFunctionIndex(funName, true); | 
|  | 595 | assert(Idx != InvalidIdx && "This should be a call to an allocator."); | 
|  | 596 | const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param); | 
| Anna Zaks | 3a769bd | 2011-09-15 01:08:34 +0000 | [diff] [blame] | 597 | PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(), | 
|  | 598 | N->getLocationContext()); | 
| David Blaikie | 0a0c275 | 2017-01-05 17:26:53 +0000 | [diff] [blame] | 599 | return std::make_shared<PathDiagnosticEventPiece>(Pos, | 
|  | 600 | "Data is allocated here."); | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 601 | } | 
|  | 602 |  | 
| Anna Zaks | 14b1af5 | 2017-01-13 00:50:41 +0000 | [diff] [blame] | 603 | void MacOSKeychainAPIChecker::printState(raw_ostream &Out, | 
|  | 604 | ProgramStateRef State, | 
|  | 605 | const char *NL, | 
|  | 606 | const char *Sep) const { | 
|  | 607 |  | 
|  | 608 | AllocatedDataTy AMap = State->get<AllocatedData>(); | 
|  | 609 |  | 
|  | 610 | if (!AMap.isEmpty()) { | 
|  | 611 | Out << Sep << "KeychainAPIChecker :" << NL; | 
|  | 612 | for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { | 
|  | 613 | I.getKey()->dumpToStream(Out); | 
|  | 614 | } | 
|  | 615 | } | 
|  | 616 | } | 
|  | 617 |  | 
|  | 618 |  | 
| Anna Zaks | 15f496c | 2011-08-01 22:40:01 +0000 | [diff] [blame] | 619 | void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) { | 
|  | 620 | mgr.registerChecker<MacOSKeychainAPIChecker>(); | 
|  | 621 | } |