MacOSKeychainAPIChecker.cpp revision 065a4055f796c545cdcc89a490be2d3288426d57
1868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)//==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==// 22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The LLVM Compiler Infrastructure 42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 5868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// This file is distributed under the University of Illinois Open Source 62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// License. See LICENSE.TXT for details. 72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//===----------------------------------------------------------------------===// 9868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// This checker flags misuses of KeyChainAPI. In particular, the password data 102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// allocated/returned by SecKeychainItemCopyContent, 11eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// SecKeychainFindGenericPassword, SecKeychainFindInternetPassword functions has 12eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// to be freed using a call to SecKeychainItemFreeContent. 137d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)//===----------------------------------------------------------------------===// 142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ClangSACheckers.h" 162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "clang/StaticAnalyzer/Core/Checker.h" 172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "clang/StaticAnalyzer/Core/CheckerManager.h" 187dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using namespace clang; 242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using namespace ento; 252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace { 27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, 28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) check::PreStmt<ReturnStmt>, 29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) check::PostStmt<CallExpr>, 30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) check::EndPath, 31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) check::DeadSymbols> { 32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) mutable llvm::OwningPtr<BugType> BT; 33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)public: 35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) /// AllocationState is a part of the checker specific state together with the 36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) /// MemRegion corresponding to the allocated data. 37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) struct AllocationState { 38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) /// The index of the allocator function. 39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) unsigned int AllocatorIdx; 40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) SymbolRef Region; 41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) : 432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) AllocatorIdx(Idx), 442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Region(R) {} 452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool operator==(const AllocationState &X) const { 472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return (AllocatorIdx == X.AllocatorIdx && 482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Region == X.Region); 492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) void Profile(llvm::FoldingSetNodeID &ID) const { 522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ID.AddInteger(AllocatorIdx); 532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ID.AddPointer(Region); 542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }; 562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) void checkPreStmt(const CallExpr *S, CheckerContext &C) const; 582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; 592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) void checkPostStmt(const CallExpr *S, CheckerContext &C) const; 602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const; 622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)private: 642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) typedef std::pair<SymbolRef, const AllocationState*> AllocationPair; 652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) typedef llvm::SmallVector<AllocationPair, 2> AllocationPairVec; 662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) enum APIKind { 682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) /// Denotes functions tracked by this checker. 692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ValidAPI = 0, 702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) /// The functions commonly/mistakenly used in place of the given API. 712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ErrorAPI = 1, 722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) /// The functions which may allocate the data. These are tracked to reduce 732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) /// the false alarm rate. 742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) PossibleAPI = 2 752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }; 762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) /// Stores the information about the allocator and deallocator functions - 772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) /// these are the functions the checker is tracking. 782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) struct ADFunctionInfo { 792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const char* Name; 802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) unsigned int Param; 812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) unsigned int DeallocatorIdx; 822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) APIKind Kind; 83d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) }; 84d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) static const unsigned InvalidIdx = 100000; 85d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) static const unsigned FunctionsToTrackSize = 8; 86d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize]; 87d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) /// The value, which represents no error return value for allocator functions. 88d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) static const unsigned NoErr = 0; 89d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 90d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) /// Given the function name, returns the index of the allocator/deallocator 91d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) /// function. 92d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator); 93d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 94d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) inline void initBugType() const { 95d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) if (!BT) 96d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) BT.reset(new BugType("Improper use of SecKeychain API", "Mac OS API")); 97d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) } 982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) void generateDeallocatorMismatchReport(const AllocationPair &AP, 1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const Expr *ArgExpr, 101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CheckerContext &C) const; 1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) BugReport *generateAllocatedDataNotReleasedReport(const AllocationPair &AP, 104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) ExplodedNode *N) const; 1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) /// Check if RetSym evaluates to an error value in the current state. 1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool definitelyReturnedError(SymbolRef RetSym, 1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const ProgramState *State, 1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) SValBuilder &Builder, 1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool noError = false) const; 1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) /// Check if RetSym evaluates to a NoErr value in the current state. 1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool definitelyDidnotReturnError(SymbolRef RetSym, 1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const ProgramState *State, 1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) SValBuilder &Builder) const { 1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return definitelyReturnedError(RetSym, State, Builder, true); 1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) /// The bug visitor which allows us to print extra diagnostics along the 1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) /// BugReport path. For example, showing the allocation site of the leaked 1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) /// region. 1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) class SecKeychainBugVisitor : public BugReporterVisitor { 1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) protected: 1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // The allocated region symbol tracked by the main analysis. 1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) SymbolRef Sym; 1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public: 1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) SecKeychainBugVisitor(SymbolRef S) : Sym(S) {} 1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) virtual ~SecKeychainBugVisitor() {} 1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) void Profile(llvm::FoldingSetNodeID &ID) const { 1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) static int X = 0; 1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ID.AddPointer(&X); 1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ID.AddPointer(Sym); 1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 136c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) PathDiagnosticPiece *VisitNode(const ExplodedNode *N, 1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const ExplodedNode *PrevN, 1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) BugReporterContext &BRC, 1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) BugReport &BR); 1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }; 1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}; 1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/// ProgramState traits to store the currently allocated (and not yet freed) 1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/// symbols. This is a map from the allocated content symbol to the 1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/// corresponding AllocationState. 1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)typedef llvm::ImmutableMap<SymbolRef, 1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) MacOSKeychainAPIChecker::AllocationState> AllocatedSetTy; 1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace { struct AllocatedData {}; } 1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace clang { namespace ento { 1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)template<> struct ProgramStateTrait<AllocatedData> 1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) : public ProgramStatePartialTrait<AllocatedSetTy > { 1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) static void *GDMIndex() { static int index = 0; return &index; } 1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}; 1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}} 1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)static bool isEnclosingFunctionParam(const Expr *E) { 1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) E = E->IgnoreParenCasts(); 1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { 1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const ValueDecl *VD = DRE->getDecl(); 1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD)) 1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return true; 1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 169c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const MacOSKeychainAPIChecker::ADFunctionInfo 1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = { 1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) {"SecKeychainItemCopyContent", 4, 3, ValidAPI}, // 0 1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) {"SecKeychainFindGenericPassword", 6, 3, ValidAPI}, // 1 173c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) {"SecKeychainFindInternetPassword", 13, 3, ValidAPI}, // 2 1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI}, // 3 1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI}, // 4 1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI}, // 5 1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) {"free", 0, InvalidIdx, ErrorAPI}, // 6 1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI}, // 7 1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}; 1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name, 1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool IsAllocator) { 1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) for (unsigned I = 0; I < FunctionsToTrackSize; ++I) { 1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ADFunctionInfo FI = FunctionsToTrack[I]; 1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (FI.Name != Name) 1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) continue; 1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Make sure the function is of the right type (allocator vs deallocator). 1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx)) 189c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return InvalidIdx; 1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx)) 1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return InvalidIdx; 1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return I; 1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // The function is not tracked. 1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return InvalidIdx; 1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)static SymbolRef getSymbolForRegion(CheckerContext &C, 2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const MemRegion *R) { 2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Implicit casts (ex: void* -> char*) can turn Symbolic region into element 2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // region, if that is the case, get the underlining region. 2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) R = R->StripCasts(); 2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!isa<SymbolicRegion>(R)) { 2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return 0; 2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return cast<SymbolicRegion>(R)->getSymbol(); 2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)static bool isBadDeallocationArgument(const MemRegion *Arg) { 2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (isa<AllocaRegion>(Arg) || 2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) isa<BlockDataRegion>(Arg) || 2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) isa<TypedRegion>(Arg)) { 2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return true; 2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/// Given the address expression, retrieve the value it's pointing to. Assume 2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/// that value is itself an address, and return the corresponding symbol. 2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)static SymbolRef getAsPointeeSymbol(const Expr *Expr, 2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CheckerContext &C) { 2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const ProgramState *State = C.getState(); 2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) SVal ArgV = State->getSVal(Expr); 2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&ArgV)) { 2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) StoreManager& SM = C.getStoreManager(); 2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const MemRegion *V = SM.Retrieve(State->getStore(), *X).getAsRegion(); 2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (V) 2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return getSymbolForRegion(C, V); 2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return 0; 2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// When checking for error code, we need to consider the following cases: 2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 1) noErr / [0] 2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 2) someErr / [1, inf] 2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 3) unknown 2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// If noError, returns true iff (1). 2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// If !noError, returns true iff (2). 240c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym, 241c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const ProgramState *State, 242c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) SValBuilder &Builder, 243c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) bool noError) const { 244c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal(NoErr, 245c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) Builder.getSymbolManager().getType(RetSym)); 246c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal, 247c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) nonloc::SymbolVal(RetSym)); 248c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const ProgramState *ErrState = State->assume(NoErr, noError); 249c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (ErrState == State) { 250c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return true; 251c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 252c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 25390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return false; 25490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 25590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 25690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Report deallocator mismatch. Remove the region from tracking - reporting a 25790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// missing free error after this one is redundant. 25890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void MacOSKeychainAPIChecker:: 25990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) generateDeallocatorMismatchReport(const AllocationPair &AP, 260c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const Expr *ArgExpr, 2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CheckerContext &C) const { 2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const ProgramState *State = C.getState(); 2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) State = State->remove<AllocatedData>(AP.first); 2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ExplodedNode *N = C.generateNode(State); 2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!N) 2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) initBugType(); 269c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) llvm::SmallString<80> sbuf; 2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) llvm::raw_svector_ostream os(sbuf); 2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) unsigned int PDeallocIdx = 2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx; 2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 274c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) os << "Deallocator doesn't match the allocator: '" 2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) << FunctionsToTrack[PDeallocIdx].Name << "' should be used."; 2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) BugReport *Report = new BugReport(*BT, os.str(), N); 2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Report->addVisitor(new SecKeychainBugVisitor(AP.first)); 278c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) Report->addRange(ArgExpr->getSourceRange()); 279c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) C.EmitReport(Report); 280d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)} 281d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 282c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, 283c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CheckerContext &C) const { 2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const ProgramState *State = C.getState(); 2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const Expr *Callee = CE->getCallee(); 2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) SVal L = State->getSVal(Callee); 2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) unsigned idx = InvalidIdx; 2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const FunctionDecl *funDecl = L.getAsFunctionDecl(); 2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!funDecl) 2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) IdentifierInfo *funI = funDecl->getIdentifier(); 2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!funI) 2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) StringRef funName = funI->getName(); 2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // If it is a call to an allocator function, it could be a double allocation. 2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) idx = getTrackedFunctionIndex(funName, true); 2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (idx != InvalidIdx) { 3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) 3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (const AllocationState *AS = State->get<AllocatedData>(V)) { 303eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) { 3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Remove the value from the state. The new symbol will be added for 3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // tracking when the second allocator is processed in checkPostStmt(). 3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) State = State->remove<AllocatedData>(V); 3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ExplodedNode *N = C.generateNode(State); 3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!N) 3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) initBugType(); 3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) llvm::SmallString<128> sbuf; 3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) llvm::raw_svector_ostream os(sbuf); 3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 314c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) os << "Allocated data should be released before another call to " 3153240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch << "the allocator: missing a call to '" 316c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) << FunctionsToTrack[DIdx].Name 3173240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch << "'."; 318c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) BugReport *Report = new BugReport(*BT, os.str(), N); 3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Report->addVisitor(new SecKeychainBugVisitor(V)); 320bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch Report->addRange(ArgExpr->getSourceRange()); 3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) C.EmitReport(Report); 3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 32590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Is it a call to one of deallocator functions? 3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) idx = getTrackedFunctionIndex(funName, false); 3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (idx == InvalidIdx) 3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Check the argument to the deallocator. 333d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 334d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) SVal ArgSVal = State->getSVal(ArgExpr); 335d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Undef is reported by another checker. 3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (ArgSVal.isUndef()) 3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 340c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const MemRegion *Arg = ArgSVal.getAsRegion(); 3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!Arg) 3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) SymbolRef ArgSM = getSymbolForRegion(C, Arg); 3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool RegionArgIsBad = ArgSM ? false : isBadDeallocationArgument(Arg); 3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // If the argument is coming from the heap, globals, or unknown, do not 3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // report it. 348c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (!ArgSM && !RegionArgIsBad) 349c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return; 3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Is the argument to the call being tracked? 3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const AllocationState *AS = State->get<AllocatedData>(ArgSM); 3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) { 354c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return; 3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // If trying to free data which has not been allocated yet, report as a bug. 3572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // TODO: We might want a more precise diagnostic for double free 3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // (that would involve tracking all the freed symbols in the checker state). 3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!AS || RegionArgIsBad) { 360c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // It is possible that this is a false positive - the argument might 3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // have entered as an enclosing function parameter. 3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (isEnclosingFunctionParam(ArgExpr)) 363c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return; 364c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 365c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) ExplodedNode *N = C.generateNode(State); 366c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (!N) 367c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return; 3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) initBugType(); 3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) BugReport *Report = new BugReport(*BT, 3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) "Trying to free data which has not been allocated.", N); 371c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) Report->addRange(ArgExpr->getSourceRange()); 372c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) C.EmitReport(Report); 3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Process functions which might deallocate. 3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (FunctionsToTrack[idx].Kind == PossibleAPI) { 3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (funName == "CFStringCreateWithBytesNoCopy") { 380c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); 381c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // NULL ~ default deallocator, so warn. 382c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), 383c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) Expr::NPC_ValueDependentIsNotNull)) { 384c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const AllocationPair AP = std::make_pair(ArgSM, AS); 385c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) generateDeallocatorMismatchReport(AP, ArgExpr, C); 386c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return; 387c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 388c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // One of the default allocators, so warn. 389c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { 390c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) StringRef DeallocatorName = DE->getFoundDecl()->getName(); 3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (DeallocatorName == "kCFAllocatorDefault" || 3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DeallocatorName == "kCFAllocatorSystemDefault" || 3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DeallocatorName == "kCFAllocatorMalloc") { 3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const AllocationPair AP = std::make_pair(ArgSM, AS); 3952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) generateDeallocatorMismatchReport(AP, ArgExpr, C); 3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 3972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // If kCFAllocatorNull, which does not deallocate, we still have to 3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // find the deallocator. Otherwise, assume that the user had written a 4002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // custom deallocator which does the right thing. 4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (DE->getFoundDecl()->getName() != "kCFAllocatorNull") { 4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) State = State->remove<AllocatedData>(ArgSM); 4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) C.addTransition(State); 4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 4092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // The call is deallocating a value we previously allocated, so remove it 4122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // from the next state. 4132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) State = State->remove<AllocatedData>(ArgSM); 4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Check if the proper deallocator is used. 4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { 4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const AllocationPair AP = std::make_pair(ArgSM, AS); 4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) generateDeallocatorMismatchReport(AP, ArgExpr, C); 4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 4212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // If the return status is undefined or is error, report a bad call to free. 4242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { 4252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ExplodedNode *N = C.generateNode(State); 4262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!N) 4272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 4282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) initBugType(); 4292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) BugReport *Report = new BugReport(*BT, 4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) "Call to free data when error was returned during allocation.", N); 4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Report->addVisitor(new SecKeychainBugVisitor(ArgSM)); 4322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Report->addRange(ArgExpr->getSourceRange()); 4332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) C.EmitReport(Report); 4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 4352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) C.addTransition(State); 4382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, 4412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CheckerContext &C) const { 4422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const ProgramState *State = C.getState(); 4432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const Expr *Callee = CE->getCallee(); 4442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) SVal L = State->getSVal(Callee); 4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const FunctionDecl *funDecl = L.getAsFunctionDecl(); 4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!funDecl) 4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 4492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) IdentifierInfo *funI = funDecl->getIdentifier(); 4502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!funI) 4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 4522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) StringRef funName = funI->getName(); 4532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // If a value has been allocated, add it to the set for tracking. 4552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) unsigned idx = getTrackedFunctionIndex(funName, true); 4562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (idx == InvalidIdx) 4572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 4582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // If the argument entered as an enclosing function parameter, skip it to 4612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // avoid false positives. 4622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (isEnclosingFunctionParam(ArgExpr)) 4632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 4642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) { 466c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // If the argument points to something that's not a symbolic region, it 467c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // can be: 468c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // - unknown (cannot reason about it) 469c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // - undefined (already reported by other checker) 470c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // - constant (null - should not be tracked, 471c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // other constant will generate a compiler warning) 4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // - goto (should be reported by other checker) 4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // The call return value symbol should stay alive for as long as the 4752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // allocated value symbol, since our diagnostics depend on the value 4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // returned by the call. Ex: Data should only be freed if noErr was 4772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // returned during allocation.) 4782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) SymbolRef RetStatusSymbol = State->getSVal(CE).getAsSymbol(); 4792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol); 4802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Track the allocated value in the checker state. 4822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx, 4832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) RetStatusSymbol)); 4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) assert(State); 4852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) C.addTransition(State); 4862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 4882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S, 4902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CheckerContext &C) const { 4912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const Expr *retExpr = S->getRetValue(); 4922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!retExpr) 4932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Check if the value is escaping through the return. 4962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const ProgramState *state = C.getState(); 4972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const MemRegion *V = state->getSVal(retExpr).getAsRegion(); 4982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!V) 4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) state = state->remove<AllocatedData>(getSymbolForRegion(C, V)); 5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 5022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Proceed from the new state. 5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) C.addTransition(state); 5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 5062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)BugReport *MacOSKeychainAPIChecker:: 5072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) generateAllocatedDataNotReleasedReport(const AllocationPair &AP, 5082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ExplodedNode *N) const { 5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; 5102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) initBugType(); 5112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) llvm::SmallString<70> sbuf; 5122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) llvm::raw_svector_ostream os(sbuf); 5132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 5142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) os << "Allocated data is not released: missing a call to '" 5152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) << FunctionsToTrack[FI.DeallocatorIdx].Name << "'."; 5162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) BugReport *Report = new BugReport(*BT, os.str(), N); 5172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Report->addVisitor(new SecKeychainBugVisitor(AP.first)); 518c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) Report->addRange(SourceRange()); 519c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return Report; 520c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)} 521c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 522c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, 523c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CheckerContext &C) const { 5242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const ProgramState *State = C.getState(); 5252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) AllocatedSetTy ASet = State->get<AllocatedData>(); 5262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (ASet.isEmpty()) 5272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 5282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 5292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool Changed = false; 5302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) AllocationPairVec Errors; 5312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) for (AllocatedSetTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) { 532 if (SR.isLive(I->first)) 533 continue; 534 535 Changed = true; 536 State = State->remove<AllocatedData>(I->first); 537 // If the allocated symbol is null or if the allocation call might have 538 // returned an error, do not report. 539 if (State->getSymVal(I->first) || 540 definitelyReturnedError(I->second.Region, State, C.getSValBuilder())) 541 continue; 542 Errors.push_back(std::make_pair(I->first, &I->second)); 543 } 544 if (!Changed) 545 return; 546 547 // Generate the new, cleaned up state. 548 ExplodedNode *N = C.generateNode(State); 549 if (!N) 550 return; 551 552 // Generate the error reports. 553 for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); 554 I != E; ++I) { 555 C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N)); 556 } 557} 558 559// TODO: Remove this after we ensure that checkDeadSymbols are always called. 560void MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B, 561 ExprEngine &Eng) const { 562 const ProgramState *state = B.getState(); 563 AllocatedSetTy AS = state->get<AllocatedData>(); 564 if (AS.isEmpty()) 565 return; 566 567 // Anything which has been allocated but not freed (nor escaped) will be 568 // found here, so report it. 569 bool Changed = false; 570 AllocationPairVec Errors; 571 for (AllocatedSetTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) { 572 Changed = true; 573 state = state->remove<AllocatedData>(I->first); 574 // If the allocated symbol is null or if error code was returned at 575 // allocation, do not report. 576 if (state->getSymVal(I.getKey()) || 577 definitelyReturnedError(I->second.Region, state, 578 Eng.getSValBuilder())) { 579 continue; 580 } 581 Errors.push_back(std::make_pair(I->first, &I->second)); 582 } 583 584 // If no change, do not generate a new state. 585 if (!Changed) 586 return; 587 588 ExplodedNode *N = B.generateNode(state); 589 if (!N) 590 return; 591 592 // Generate the error reports. 593 for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); 594 I != E; ++I) { 595 Eng.getBugReporter().EmitReport( 596 generateAllocatedDataNotReleasedReport(*I, N)); 597 } 598} 599 600 601PathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( 602 const ExplodedNode *N, 603 const ExplodedNode *PrevN, 604 BugReporterContext &BRC, 605 BugReport &BR) { 606 const AllocationState *AS = N->getState()->get<AllocatedData>(Sym); 607 if (!AS) 608 return 0; 609 const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym); 610 if (ASPrev) 611 return 0; 612 613 // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the 614 // allocation site. 615 const CallExpr *CE = cast<CallExpr>(cast<StmtPoint>(N->getLocation()) 616 .getStmt()); 617 const FunctionDecl *funDecl = CE->getDirectCallee(); 618 assert(funDecl && "We do not support indirect function calls as of now."); 619 StringRef funName = funDecl->getName(); 620 621 // Get the expression of the corresponding argument. 622 unsigned Idx = getTrackedFunctionIndex(funName, true); 623 assert(Idx != InvalidIdx && "This should be a call to an allocator."); 624 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param); 625 PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager()); 626 return new PathDiagnosticEventPiece(Pos, "Data is allocated here."); 627} 628 629void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) { 630 mgr.registerChecker<MacOSKeychainAPIChecker>(); 631} 632