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