MacOSKeychainAPIChecker.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  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/Checker.h"
  16. #include "clang/StaticAnalyzer/Core/CheckerManager.h"
  17. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.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. using namespace clang;
  23. using namespace ento;
  24. namespace {
  25. class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
  26. check::PreStmt<ReturnStmt>,
  27. check::PostStmt<CallExpr>,
  28. check::EndPath,
  29. check::DeadSymbols> {
  30. mutable OwningPtr<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 checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
  52. void checkPostStmt(const CallExpr *S, CheckerContext &C) const;
  53. void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
  54. void checkEndPath(CheckerContext &C) const;
  55. private:
  56. typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
  57. typedef llvm::SmallVector<AllocationPair, 2> AllocationPairVec;
  58. enum APIKind {
  59. /// Denotes functions tracked by this checker.
  60. ValidAPI = 0,
  61. /// The functions commonly/mistakenly used in place of the given API.
  62. ErrorAPI = 1,
  63. /// The functions which may allocate the data. These are tracked to reduce
  64. /// the false alarm rate.
  65. PossibleAPI = 2
  66. };
  67. /// Stores the information about the allocator and deallocator functions -
  68. /// these are the functions the checker is tracking.
  69. struct ADFunctionInfo {
  70. const char* Name;
  71. unsigned int Param;
  72. unsigned int DeallocatorIdx;
  73. APIKind Kind;
  74. };
  75. static const unsigned InvalidIdx = 100000;
  76. static const unsigned FunctionsToTrackSize = 8;
  77. static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize];
  78. /// The value, which represents no error return value for allocator functions.
  79. static const unsigned NoErr = 0;
  80. /// Given the function name, returns the index of the allocator/deallocator
  81. /// function.
  82. static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator);
  83. inline void initBugType() const {
  84. if (!BT)
  85. BT.reset(new BugType("Improper use of SecKeychain API", "Mac OS API"));
  86. }
  87. void generateDeallocatorMismatchReport(const AllocationPair &AP,
  88. const Expr *ArgExpr,
  89. CheckerContext &C) const;
  90. /// Find the allocation site for Sym on the path leading to the node N.
  91. const Stmt *getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
  92. CheckerContext &C) const;
  93. BugReport *generateAllocatedDataNotReleasedReport(const AllocationPair &AP,
  94. ExplodedNode *N,
  95. CheckerContext &C) const;
  96. /// Check if RetSym evaluates to an error value in the current state.
  97. bool definitelyReturnedError(SymbolRef RetSym,
  98. ProgramStateRef State,
  99. SValBuilder &Builder,
  100. bool noError = false) const;
  101. /// Check if RetSym evaluates to a NoErr value in the current state.
  102. bool definitelyDidnotReturnError(SymbolRef RetSym,
  103. ProgramStateRef State,
  104. SValBuilder &Builder) const {
  105. return definitelyReturnedError(RetSym, State, Builder, true);
  106. }
  107. /// Mark an AllocationPair interesting for diagnostic reporting.
  108. void markInteresting(BugReport *R, 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
  116. : public BugReporterVisitorImpl<SecKeychainBugVisitor> {
  117. protected:
  118. // The allocated region symbol tracked by the main analysis.
  119. SymbolRef Sym;
  120. public:
  121. SecKeychainBugVisitor(SymbolRef S) : Sym(S) {}
  122. virtual ~SecKeychainBugVisitor() {}
  123. void Profile(llvm::FoldingSetNodeID &ID) const {
  124. static int X = 0;
  125. ID.AddPointer(&X);
  126. ID.AddPointer(Sym);
  127. }
  128. PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
  129. const ExplodedNode *PrevN,
  130. BugReporterContext &BRC,
  131. BugReport &BR);
  132. };
  133. };
  134. }
  135. /// ProgramState traits to store the currently allocated (and not yet freed)
  136. /// symbols. This is a map from the allocated content symbol to the
  137. /// corresponding AllocationState.
  138. typedef llvm::ImmutableMap<SymbolRef,
  139. MacOSKeychainAPIChecker::AllocationState> AllocatedSetTy;
  140. namespace { struct AllocatedData {}; }
  141. namespace clang { namespace ento {
  142. template<> struct ProgramStateTrait<AllocatedData>
  143. : public ProgramStatePartialTrait<AllocatedSetTy > {
  144. static void *GDMIndex() { static int index = 0; return &index; }
  145. };
  146. }}
  147. static bool isEnclosingFunctionParam(const Expr *E) {
  148. E = E->IgnoreParenCasts();
  149. if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
  150. const ValueDecl *VD = DRE->getDecl();
  151. if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
  152. return true;
  153. }
  154. return false;
  155. }
  156. const MacOSKeychainAPIChecker::ADFunctionInfo
  157. MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
  158. {"SecKeychainItemCopyContent", 4, 3, ValidAPI}, // 0
  159. {"SecKeychainFindGenericPassword", 6, 3, ValidAPI}, // 1
  160. {"SecKeychainFindInternetPassword", 13, 3, ValidAPI}, // 2
  161. {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI}, // 3
  162. {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI}, // 4
  163. {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI}, // 5
  164. {"free", 0, InvalidIdx, ErrorAPI}, // 6
  165. {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI}, // 7
  166. };
  167. unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
  168. bool IsAllocator) {
  169. for (unsigned I = 0; I < FunctionsToTrackSize; ++I) {
  170. ADFunctionInfo FI = FunctionsToTrack[I];
  171. if (FI.Name != Name)
  172. continue;
  173. // Make sure the function is of the right type (allocator vs deallocator).
  174. if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
  175. return InvalidIdx;
  176. if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
  177. return InvalidIdx;
  178. return I;
  179. }
  180. // The function is not tracked.
  181. return InvalidIdx;
  182. }
  183. static bool isBadDeallocationArgument(const MemRegion *Arg) {
  184. if (!Arg)
  185. return false;
  186. if (isa<AllocaRegion>(Arg) ||
  187. isa<BlockDataRegion>(Arg) ||
  188. isa<TypedRegion>(Arg)) {
  189. return true;
  190. }
  191. return false;
  192. }
  193. /// Given the address expression, retrieve the value it's pointing to. Assume
  194. /// that value is itself an address, and return the corresponding symbol.
  195. static SymbolRef getAsPointeeSymbol(const Expr *Expr,
  196. CheckerContext &C) {
  197. ProgramStateRef State = C.getState();
  198. SVal ArgV = State->getSVal(Expr, C.getLocationContext());
  199. if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&ArgV)) {
  200. StoreManager& SM = C.getStoreManager();
  201. SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol();
  202. if (sym)
  203. return sym;
  204. }
  205. return 0;
  206. }
  207. // When checking for error code, we need to consider the following cases:
  208. // 1) noErr / [0]
  209. // 2) someErr / [1, inf]
  210. // 3) unknown
  211. // If noError, returns true iff (1).
  212. // If !noError, returns true iff (2).
  213. bool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym,
  214. ProgramStateRef State,
  215. SValBuilder &Builder,
  216. bool noError) const {
  217. DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal(NoErr,
  218. Builder.getSymbolManager().getType(RetSym));
  219. DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal,
  220. nonloc::SymbolVal(RetSym));
  221. ProgramStateRef ErrState = State->assume(NoErr, noError);
  222. if (ErrState == State) {
  223. return true;
  224. }
  225. return false;
  226. }
  227. // Report deallocator mismatch. Remove the region from tracking - reporting a
  228. // missing free error after this one is redundant.
  229. void MacOSKeychainAPIChecker::
  230. generateDeallocatorMismatchReport(const AllocationPair &AP,
  231. const Expr *ArgExpr,
  232. CheckerContext &C) const {
  233. ProgramStateRef State = C.getState();
  234. State = State->remove<AllocatedData>(AP.first);
  235. ExplodedNode *N = C.addTransition(State);
  236. if (!N)
  237. return;
  238. initBugType();
  239. SmallString<80> sbuf;
  240. llvm::raw_svector_ostream os(sbuf);
  241. unsigned int PDeallocIdx =
  242. FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
  243. os << "Deallocator doesn't match the allocator: '"
  244. << FunctionsToTrack[PDeallocIdx].Name << "' should be used.";
  245. BugReport *Report = new BugReport(*BT, os.str(), N);
  246. Report->addVisitor(new SecKeychainBugVisitor(AP.first));
  247. Report->addRange(ArgExpr->getSourceRange());
  248. markInteresting(Report, AP);
  249. C.EmitReport(Report);
  250. }
  251. void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
  252. CheckerContext &C) const {
  253. unsigned idx = InvalidIdx;
  254. ProgramStateRef State = C.getState();
  255. const FunctionDecl *FD = C.getCalleeDecl(CE);
  256. if (!FD || FD->getKind() != Decl::Function)
  257. return;
  258. StringRef funName = C.getCalleeName(FD);
  259. if (funName.empty())
  260. return;
  261. // If it is a call to an allocator function, it could be a double allocation.
  262. idx = getTrackedFunctionIndex(funName, true);
  263. if (idx != InvalidIdx) {
  264. const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
  265. if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C))
  266. if (const AllocationState *AS = State->get<AllocatedData>(V)) {
  267. if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) {
  268. // Remove the value from the state. The new symbol will be added for
  269. // tracking when the second allocator is processed in checkPostStmt().
  270. State = State->remove<AllocatedData>(V);
  271. ExplodedNode *N = C.addTransition(State);
  272. if (!N)
  273. return;
  274. initBugType();
  275. SmallString<128> sbuf;
  276. llvm::raw_svector_ostream os(sbuf);
  277. unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
  278. os << "Allocated data should be released before another call to "
  279. << "the allocator: missing a call to '"
  280. << FunctionsToTrack[DIdx].Name
  281. << "'.";
  282. BugReport *Report = new BugReport(*BT, os.str(), N);
  283. Report->addVisitor(new SecKeychainBugVisitor(V));
  284. Report->addRange(ArgExpr->getSourceRange());
  285. Report->markInteresting(AS->Region);
  286. C.EmitReport(Report);
  287. }
  288. }
  289. return;
  290. }
  291. // Is it a call to one of deallocator functions?
  292. idx = getTrackedFunctionIndex(funName, false);
  293. if (idx == InvalidIdx)
  294. return;
  295. // Check the argument to the deallocator.
  296. const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
  297. SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext());
  298. // Undef is reported by another checker.
  299. if (ArgSVal.isUndef())
  300. return;
  301. SymbolRef ArgSM = ArgSVal.getAsLocSymbol();
  302. // If the argument is coming from the heap, globals, or unknown, do not
  303. // report it.
  304. bool RegionArgIsBad = false;
  305. if (!ArgSM) {
  306. if (!isBadDeallocationArgument(ArgSVal.getAsRegion()))
  307. return;
  308. RegionArgIsBad = true;
  309. }
  310. // Is the argument to the call being tracked?
  311. const AllocationState *AS = State->get<AllocatedData>(ArgSM);
  312. if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) {
  313. return;
  314. }
  315. // If trying to free data which has not been allocated yet, report as a bug.
  316. // TODO: We might want a more precise diagnostic for double free
  317. // (that would involve tracking all the freed symbols in the checker state).
  318. if (!AS || RegionArgIsBad) {
  319. // It is possible that this is a false positive - the argument might
  320. // have entered as an enclosing function parameter.
  321. if (isEnclosingFunctionParam(ArgExpr))
  322. return;
  323. ExplodedNode *N = C.addTransition(State);
  324. if (!N)
  325. return;
  326. initBugType();
  327. BugReport *Report = new BugReport(*BT,
  328. "Trying to free data which has not been allocated.", N);
  329. Report->addRange(ArgExpr->getSourceRange());
  330. if (AS)
  331. Report->markInteresting(AS->Region);
  332. C.EmitReport(Report);
  333. return;
  334. }
  335. // Process functions which might deallocate.
  336. if (FunctionsToTrack[idx].Kind == PossibleAPI) {
  337. if (funName == "CFStringCreateWithBytesNoCopy") {
  338. const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts();
  339. // NULL ~ default deallocator, so warn.
  340. if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(),
  341. Expr::NPC_ValueDependentIsNotNull)) {
  342. const AllocationPair AP = std::make_pair(ArgSM, AS);
  343. generateDeallocatorMismatchReport(AP, ArgExpr, C);
  344. return;
  345. }
  346. // One of the default allocators, so warn.
  347. if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
  348. StringRef DeallocatorName = DE->getFoundDecl()->getName();
  349. if (DeallocatorName == "kCFAllocatorDefault" ||
  350. DeallocatorName == "kCFAllocatorSystemDefault" ||
  351. DeallocatorName == "kCFAllocatorMalloc") {
  352. const AllocationPair AP = std::make_pair(ArgSM, AS);
  353. generateDeallocatorMismatchReport(AP, ArgExpr, C);
  354. return;
  355. }
  356. // If kCFAllocatorNull, which does not deallocate, we still have to
  357. // find the deallocator. Otherwise, assume that the user had written a
  358. // custom deallocator which does the right thing.
  359. if (DE->getFoundDecl()->getName() != "kCFAllocatorNull") {
  360. State = State->remove<AllocatedData>(ArgSM);
  361. C.addTransition(State);
  362. return;
  363. }
  364. }
  365. }
  366. return;
  367. }
  368. // The call is deallocating a value we previously allocated, so remove it
  369. // from the next state.
  370. State = State->remove<AllocatedData>(ArgSM);
  371. // Check if the proper deallocator is used.
  372. unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
  373. if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) {
  374. const AllocationPair AP = std::make_pair(ArgSM, AS);
  375. generateDeallocatorMismatchReport(AP, ArgExpr, C);
  376. return;
  377. }
  378. // If the buffer can be null and the return status can be an error,
  379. // report a bad call to free.
  380. if (State->assume(cast<DefinedSVal>(ArgSVal), false) &&
  381. !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) {
  382. ExplodedNode *N = C.addTransition(State);
  383. if (!N)
  384. return;
  385. initBugType();
  386. BugReport *Report = new BugReport(*BT,
  387. "Only call free if a valid (non-NULL) buffer was returned.", N);
  388. Report->addVisitor(new SecKeychainBugVisitor(ArgSM));
  389. Report->addRange(ArgExpr->getSourceRange());
  390. Report->markInteresting(AS->Region);
  391. C.EmitReport(Report);
  392. return;
  393. }
  394. C.addTransition(State);
  395. }
  396. void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
  397. CheckerContext &C) const {
  398. ProgramStateRef State = C.getState();
  399. const FunctionDecl *FD = C.getCalleeDecl(CE);
  400. if (!FD || FD->getKind() != Decl::Function)
  401. return;
  402. StringRef funName = C.getCalleeName(FD);
  403. // If a value has been allocated, add it to the set for tracking.
  404. unsigned idx = getTrackedFunctionIndex(funName, true);
  405. if (idx == InvalidIdx)
  406. return;
  407. const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
  408. // If the argument entered as an enclosing function parameter, skip it to
  409. // avoid false positives.
  410. if (isEnclosingFunctionParam(ArgExpr) &&
  411. C.getLocationContext()->getParent() == 0)
  412. return;
  413. if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) {
  414. // If the argument points to something that's not a symbolic region, it
  415. // can be:
  416. // - unknown (cannot reason about it)
  417. // - undefined (already reported by other checker)
  418. // - constant (null - should not be tracked,
  419. // other constant will generate a compiler warning)
  420. // - goto (should be reported by other checker)
  421. // The call return value symbol should stay alive for as long as the
  422. // allocated value symbol, since our diagnostics depend on the value
  423. // returned by the call. Ex: Data should only be freed if noErr was
  424. // returned during allocation.)
  425. SymbolRef RetStatusSymbol =
  426. State->getSVal(CE, C.getLocationContext()).getAsSymbol();
  427. C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol);
  428. // Track the allocated value in the checker state.
  429. State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx,
  430. RetStatusSymbol));
  431. assert(State);
  432. C.addTransition(State);
  433. }
  434. }
  435. void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S,
  436. CheckerContext &C) const {
  437. const Expr *retExpr = S->getRetValue();
  438. if (!retExpr)
  439. return;
  440. // If inside inlined call, skip it.
  441. const LocationContext *LC = C.getLocationContext();
  442. if (LC->getParent() != 0)
  443. return;
  444. // Check if the value is escaping through the return.
  445. ProgramStateRef state = C.getState();
  446. SymbolRef sym = state->getSVal(retExpr, LC).getAsLocSymbol();
  447. if (!sym)
  448. return;
  449. state = state->remove<AllocatedData>(sym);
  450. // Proceed from the new state.
  451. C.addTransition(state);
  452. }
  453. // TODO: This logic is the same as in Malloc checker.
  454. const Stmt *
  455. MacOSKeychainAPIChecker::getAllocationSite(const ExplodedNode *N,
  456. SymbolRef Sym,
  457. CheckerContext &C) const {
  458. const LocationContext *LeakContext = N->getLocationContext();
  459. // Walk the ExplodedGraph backwards and find the first node that referred to
  460. // the tracked symbol.
  461. const ExplodedNode *AllocNode = N;
  462. while (N) {
  463. if (!N->getState()->get<AllocatedData>(Sym))
  464. break;
  465. // Allocation node, is the last node in the current context in which the
  466. // symbol was tracked.
  467. if (N->getLocationContext() == LeakContext)
  468. AllocNode = N;
  469. N = N->pred_empty() ? NULL : *(N->pred_begin());
  470. }
  471. ProgramPoint P = AllocNode->getLocation();
  472. if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&P))
  473. return Exit->getCalleeContext()->getCallSite();
  474. if (clang::PostStmt *PS = dyn_cast<clang::PostStmt>(&P))
  475. return PS->getStmt();
  476. return 0;
  477. }
  478. BugReport *MacOSKeychainAPIChecker::
  479. generateAllocatedDataNotReleasedReport(const AllocationPair &AP,
  480. ExplodedNode *N,
  481. CheckerContext &C) const {
  482. const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
  483. initBugType();
  484. SmallString<70> sbuf;
  485. llvm::raw_svector_ostream os(sbuf);
  486. os << "Allocated data is not released: missing a call to '"
  487. << FunctionsToTrack[FI.DeallocatorIdx].Name << "'.";
  488. // Most bug reports are cached at the location where they occurred.
  489. // With leaks, we want to unique them by the location where they were
  490. // allocated, and only report a single path.
  491. PathDiagnosticLocation LocUsedForUniqueing;
  492. if (const Stmt *AllocStmt = getAllocationSite(N, AP.first, C))
  493. LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt,
  494. C.getSourceManager(), N->getLocationContext());
  495. BugReport *Report = new BugReport(*BT, os.str(), N, LocUsedForUniqueing);
  496. Report->addVisitor(new SecKeychainBugVisitor(AP.first));
  497. markInteresting(Report, AP);
  498. return Report;
  499. }
  500. void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
  501. CheckerContext &C) const {
  502. ProgramStateRef State = C.getState();
  503. AllocatedSetTy ASet = State->get<AllocatedData>();
  504. if (ASet.isEmpty())
  505. return;
  506. bool Changed = false;
  507. AllocationPairVec Errors;
  508. for (AllocatedSetTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) {
  509. if (SR.isLive(I->first))
  510. continue;
  511. Changed = true;
  512. State = State->remove<AllocatedData>(I->first);
  513. // If the allocated symbol is null or if the allocation call might have
  514. // returned an error, do not report.
  515. if (State->getConstraintManager().isNull(State, I->first).isTrue() ||
  516. definitelyReturnedError(I->second.Region, State, C.getSValBuilder()))
  517. continue;
  518. Errors.push_back(std::make_pair(I->first, &I->second));
  519. }
  520. if (!Changed) {
  521. // Generate the new, cleaned up state.
  522. C.addTransition(State);
  523. return;
  524. }
  525. static SimpleProgramPointTag Tag("MacOSKeychainAPIChecker : DeadSymbolsLeak");
  526. ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag);
  527. // Generate the error reports.
  528. for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end();
  529. I != E; ++I) {
  530. C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N, C));
  531. }
  532. // Generate the new, cleaned up state.
  533. C.addTransition(State, N);
  534. }
  535. // TODO: Remove this after we ensure that checkDeadSymbols are always called.
  536. void MacOSKeychainAPIChecker::checkEndPath(CheckerContext &C) const {
  537. ProgramStateRef state = C.getState();
  538. // If inside inlined call, skip it.
  539. if (C.getLocationContext()->getParent() != 0)
  540. return;
  541. AllocatedSetTy AS = state->get<AllocatedData>();
  542. if (AS.isEmpty())
  543. return;
  544. // Anything which has been allocated but not freed (nor escaped) will be
  545. // found here, so report it.
  546. bool Changed = false;
  547. AllocationPairVec Errors;
  548. for (AllocatedSetTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) {
  549. Changed = true;
  550. state = state->remove<AllocatedData>(I->first);
  551. // If the allocated symbol is null or if error code was returned at
  552. // allocation, do not report.
  553. if (state->getConstraintManager().isNull(state, I.getKey()).isTrue() ||
  554. definitelyReturnedError(I->second.Region, state,
  555. C.getSValBuilder())) {
  556. continue;
  557. }
  558. Errors.push_back(std::make_pair(I->first, &I->second));
  559. }
  560. // If no change, do not generate a new state.
  561. if (!Changed) {
  562. C.addTransition(state);
  563. return;
  564. }
  565. static SimpleProgramPointTag Tag("MacOSKeychainAPIChecker : EndPathLeak");
  566. ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag);
  567. // Generate the error reports.
  568. for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end();
  569. I != E; ++I) {
  570. C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N, C));
  571. }
  572. C.addTransition(state, N);
  573. }
  574. PathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
  575. const ExplodedNode *N,
  576. const ExplodedNode *PrevN,
  577. BugReporterContext &BRC,
  578. BugReport &BR) {
  579. const AllocationState *AS = N->getState()->get<AllocatedData>(Sym);
  580. if (!AS)
  581. return 0;
  582. const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym);
  583. if (ASPrev)
  584. return 0;
  585. // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the
  586. // allocation site.
  587. const CallExpr *CE = cast<CallExpr>(cast<StmtPoint>(N->getLocation())
  588. .getStmt());
  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. // Get the expression of the corresponding argument.
  593. unsigned Idx = getTrackedFunctionIndex(funName, true);
  594. assert(Idx != InvalidIdx && "This should be a call to an allocator.");
  595. const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param);
  596. PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(),
  597. N->getLocationContext());
  598. return new PathDiagnosticEventPiece(Pos, "Data is allocated here.");
  599. }
  600. void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
  601. mgr.registerChecker<MacOSKeychainAPIChecker>();
  602. }