MacOSKeychainAPIChecker.cpp 23 KB

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