MacOSKeychainAPIChecker.cpp 25 KB

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