1226586Sdim//==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==// 2226586Sdim// 3226586Sdim// The LLVM Compiler Infrastructure 4226586Sdim// 5226586Sdim// This file is distributed under the University of Illinois Open Source 6226586Sdim// License. See LICENSE.TXT for details. 7226586Sdim// 8226586Sdim//===----------------------------------------------------------------------===// 9226586Sdim// This checker flags misuses of KeyChainAPI. In particular, the password data 10226586Sdim// allocated/returned by SecKeychainItemCopyContent, 11226586Sdim// SecKeychainFindGenericPassword, SecKeychainFindInternetPassword functions has 12226586Sdim// to be freed using a call to SecKeychainItemFreeContent. 13226586Sdim//===----------------------------------------------------------------------===// 14226586Sdim 15226586Sdim#include "ClangSACheckers.h" 16249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 17226586Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 18226586Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h" 19226586Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 20226586Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 21226586Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 22234353Sdim#include "llvm/ADT/SmallString.h" 23249423Sdim#include "llvm/Support/raw_ostream.h" 24226586Sdim 25226586Sdimusing namespace clang; 26226586Sdimusing namespace ento; 27226586Sdim 28226586Sdimnamespace { 29226586Sdimclass MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, 30226586Sdim check::PostStmt<CallExpr>, 31226586Sdim check::DeadSymbols> { 32234353Sdim mutable OwningPtr<BugType> BT; 33226586Sdim 34226586Sdimpublic: 35226586Sdim /// AllocationState is a part of the checker specific state together with the 36226586Sdim /// MemRegion corresponding to the allocated data. 37226586Sdim struct AllocationState { 38226586Sdim /// The index of the allocator function. 39226586Sdim unsigned int AllocatorIdx; 40226586Sdim SymbolRef Region; 41226586Sdim 42226586Sdim AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) : 43226586Sdim AllocatorIdx(Idx), 44226586Sdim Region(R) {} 45226586Sdim 46226586Sdim bool operator==(const AllocationState &X) const { 47226586Sdim return (AllocatorIdx == X.AllocatorIdx && 48226586Sdim Region == X.Region); 49226586Sdim } 50226586Sdim 51226586Sdim void Profile(llvm::FoldingSetNodeID &ID) const { 52226586Sdim ID.AddInteger(AllocatorIdx); 53226586Sdim ID.AddPointer(Region); 54226586Sdim } 55226586Sdim }; 56226586Sdim 57226586Sdim void checkPreStmt(const CallExpr *S, CheckerContext &C) const; 58226586Sdim void checkPostStmt(const CallExpr *S, CheckerContext &C) const; 59226586Sdim void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 60226586Sdim 61226586Sdimprivate: 62226586Sdim typedef std::pair<SymbolRef, const AllocationState*> AllocationPair; 63249423Sdim typedef SmallVector<AllocationPair, 2> AllocationPairVec; 64226586Sdim 65226586Sdim enum APIKind { 66226586Sdim /// Denotes functions tracked by this checker. 67226586Sdim ValidAPI = 0, 68226586Sdim /// The functions commonly/mistakenly used in place of the given API. 69226586Sdim ErrorAPI = 1, 70226586Sdim /// The functions which may allocate the data. These are tracked to reduce 71226586Sdim /// the false alarm rate. 72226586Sdim PossibleAPI = 2 73226586Sdim }; 74226586Sdim /// Stores the information about the allocator and deallocator functions - 75226586Sdim /// these are the functions the checker is tracking. 76226586Sdim struct ADFunctionInfo { 77226586Sdim const char* Name; 78226586Sdim unsigned int Param; 79226586Sdim unsigned int DeallocatorIdx; 80226586Sdim APIKind Kind; 81226586Sdim }; 82226586Sdim static const unsigned InvalidIdx = 100000; 83226586Sdim static const unsigned FunctionsToTrackSize = 8; 84226586Sdim static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize]; 85226586Sdim /// The value, which represents no error return value for allocator functions. 86226586Sdim static const unsigned NoErr = 0; 87226586Sdim 88226586Sdim /// Given the function name, returns the index of the allocator/deallocator 89226586Sdim /// function. 90226586Sdim static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator); 91226586Sdim 92226586Sdim inline void initBugType() const { 93226586Sdim if (!BT) 94249423Sdim BT.reset(new BugType("Improper use of SecKeychain API", 95249423Sdim "API Misuse (Apple)")); 96226586Sdim } 97226586Sdim 98226586Sdim void generateDeallocatorMismatchReport(const AllocationPair &AP, 99226586Sdim const Expr *ArgExpr, 100226586Sdim CheckerContext &C) const; 101226586Sdim 102234353Sdim /// Find the allocation site for Sym on the path leading to the node N. 103249423Sdim const ExplodedNode *getAllocationNode(const ExplodedNode *N, SymbolRef Sym, 104249423Sdim CheckerContext &C) const; 105234353Sdim 106226586Sdim BugReport *generateAllocatedDataNotReleasedReport(const AllocationPair &AP, 107234353Sdim ExplodedNode *N, 108234353Sdim CheckerContext &C) const; 109226586Sdim 110226586Sdim /// Check if RetSym evaluates to an error value in the current state. 111226586Sdim bool definitelyReturnedError(SymbolRef RetSym, 112234353Sdim ProgramStateRef State, 113226586Sdim SValBuilder &Builder, 114226586Sdim bool noError = false) const; 115226586Sdim 116226586Sdim /// Check if RetSym evaluates to a NoErr value in the current state. 117226586Sdim bool definitelyDidnotReturnError(SymbolRef RetSym, 118234353Sdim ProgramStateRef State, 119226586Sdim SValBuilder &Builder) const { 120226586Sdim return definitelyReturnedError(RetSym, State, Builder, true); 121226586Sdim } 122234353Sdim 123234353Sdim /// Mark an AllocationPair interesting for diagnostic reporting. 124234353Sdim void markInteresting(BugReport *R, const AllocationPair &AP) const { 125234353Sdim R->markInteresting(AP.first); 126234353Sdim R->markInteresting(AP.second->Region); 127234353Sdim } 128226586Sdim 129226586Sdim /// The bug visitor which allows us to print extra diagnostics along the 130226586Sdim /// BugReport path. For example, showing the allocation site of the leaked 131226586Sdim /// region. 132234353Sdim class SecKeychainBugVisitor 133234353Sdim : public BugReporterVisitorImpl<SecKeychainBugVisitor> { 134226586Sdim protected: 135226586Sdim // The allocated region symbol tracked by the main analysis. 136226586Sdim SymbolRef Sym; 137226586Sdim 138226586Sdim public: 139226586Sdim SecKeychainBugVisitor(SymbolRef S) : Sym(S) {} 140226586Sdim virtual ~SecKeychainBugVisitor() {} 141226586Sdim 142226586Sdim void Profile(llvm::FoldingSetNodeID &ID) const { 143226586Sdim static int X = 0; 144226586Sdim ID.AddPointer(&X); 145226586Sdim ID.AddPointer(Sym); 146226586Sdim } 147226586Sdim 148226586Sdim PathDiagnosticPiece *VisitNode(const ExplodedNode *N, 149226586Sdim const ExplodedNode *PrevN, 150226586Sdim BugReporterContext &BRC, 151226586Sdim BugReport &BR); 152226586Sdim }; 153226586Sdim}; 154226586Sdim} 155226586Sdim 156226586Sdim/// ProgramState traits to store the currently allocated (and not yet freed) 157226586Sdim/// symbols. This is a map from the allocated content symbol to the 158226586Sdim/// corresponding AllocationState. 159243830SdimREGISTER_MAP_WITH_PROGRAMSTATE(AllocatedData, 160243830Sdim SymbolRef, 161243830Sdim MacOSKeychainAPIChecker::AllocationState) 162226586Sdim 163226586Sdimstatic bool isEnclosingFunctionParam(const Expr *E) { 164226586Sdim E = E->IgnoreParenCasts(); 165226586Sdim if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { 166226586Sdim const ValueDecl *VD = DRE->getDecl(); 167226586Sdim if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD)) 168226586Sdim return true; 169226586Sdim } 170226586Sdim return false; 171226586Sdim} 172226586Sdim 173226586Sdimconst MacOSKeychainAPIChecker::ADFunctionInfo 174226586Sdim MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = { 175226586Sdim {"SecKeychainItemCopyContent", 4, 3, ValidAPI}, // 0 176226586Sdim {"SecKeychainFindGenericPassword", 6, 3, ValidAPI}, // 1 177226586Sdim {"SecKeychainFindInternetPassword", 13, 3, ValidAPI}, // 2 178226586Sdim {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI}, // 3 179226586Sdim {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI}, // 4 180226586Sdim {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI}, // 5 181226586Sdim {"free", 0, InvalidIdx, ErrorAPI}, // 6 182226586Sdim {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI}, // 7 183226586Sdim}; 184226586Sdim 185226586Sdimunsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name, 186226586Sdim bool IsAllocator) { 187226586Sdim for (unsigned I = 0; I < FunctionsToTrackSize; ++I) { 188226586Sdim ADFunctionInfo FI = FunctionsToTrack[I]; 189226586Sdim if (FI.Name != Name) 190226586Sdim continue; 191226586Sdim // Make sure the function is of the right type (allocator vs deallocator). 192226586Sdim if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx)) 193226586Sdim return InvalidIdx; 194226586Sdim if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx)) 195226586Sdim return InvalidIdx; 196226586Sdim 197226586Sdim return I; 198226586Sdim } 199226586Sdim // The function is not tracked. 200226586Sdim return InvalidIdx; 201226586Sdim} 202226586Sdim 203226586Sdimstatic bool isBadDeallocationArgument(const MemRegion *Arg) { 204234353Sdim if (!Arg) 205234353Sdim return false; 206226586Sdim if (isa<AllocaRegion>(Arg) || 207226586Sdim isa<BlockDataRegion>(Arg) || 208226586Sdim isa<TypedRegion>(Arg)) { 209226586Sdim return true; 210226586Sdim } 211226586Sdim return false; 212226586Sdim} 213234353Sdim 214226586Sdim/// Given the address expression, retrieve the value it's pointing to. Assume 215226586Sdim/// that value is itself an address, and return the corresponding symbol. 216226586Sdimstatic SymbolRef getAsPointeeSymbol(const Expr *Expr, 217226586Sdim CheckerContext &C) { 218234353Sdim ProgramStateRef State = C.getState(); 219234353Sdim SVal ArgV = State->getSVal(Expr, C.getLocationContext()); 220226586Sdim 221249423Sdim if (Optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) { 222226586Sdim StoreManager& SM = C.getStoreManager(); 223234353Sdim SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol(); 224234353Sdim if (sym) 225234353Sdim return sym; 226226586Sdim } 227226586Sdim return 0; 228226586Sdim} 229226586Sdim 230226586Sdim// When checking for error code, we need to consider the following cases: 231226586Sdim// 1) noErr / [0] 232226586Sdim// 2) someErr / [1, inf] 233226586Sdim// 3) unknown 234226586Sdim// If noError, returns true iff (1). 235226586Sdim// If !noError, returns true iff (2). 236226586Sdimbool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym, 237234353Sdim ProgramStateRef State, 238226586Sdim SValBuilder &Builder, 239226586Sdim bool noError) const { 240226586Sdim DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal(NoErr, 241226586Sdim Builder.getSymbolManager().getType(RetSym)); 242226586Sdim DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal, 243226586Sdim nonloc::SymbolVal(RetSym)); 244234353Sdim ProgramStateRef ErrState = State->assume(NoErr, noError); 245226586Sdim if (ErrState == State) { 246226586Sdim return true; 247226586Sdim } 248226586Sdim 249226586Sdim return false; 250226586Sdim} 251226586Sdim 252226586Sdim// Report deallocator mismatch. Remove the region from tracking - reporting a 253226586Sdim// missing free error after this one is redundant. 254226586Sdimvoid MacOSKeychainAPIChecker:: 255226586Sdim generateDeallocatorMismatchReport(const AllocationPair &AP, 256226586Sdim const Expr *ArgExpr, 257226586Sdim CheckerContext &C) const { 258234353Sdim ProgramStateRef State = C.getState(); 259226586Sdim State = State->remove<AllocatedData>(AP.first); 260234353Sdim ExplodedNode *N = C.addTransition(State); 261226586Sdim 262226586Sdim if (!N) 263226586Sdim return; 264226586Sdim initBugType(); 265234353Sdim SmallString<80> sbuf; 266226586Sdim llvm::raw_svector_ostream os(sbuf); 267226586Sdim unsigned int PDeallocIdx = 268226586Sdim FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx; 269226586Sdim 270226586Sdim os << "Deallocator doesn't match the allocator: '" 271226586Sdim << FunctionsToTrack[PDeallocIdx].Name << "' should be used."; 272226586Sdim BugReport *Report = new BugReport(*BT, os.str(), N); 273226586Sdim Report->addVisitor(new SecKeychainBugVisitor(AP.first)); 274226586Sdim Report->addRange(ArgExpr->getSourceRange()); 275234353Sdim markInteresting(Report, AP); 276243830Sdim C.emitReport(Report); 277226586Sdim} 278226586Sdim 279226586Sdimvoid MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, 280226586Sdim CheckerContext &C) const { 281226586Sdim unsigned idx = InvalidIdx; 282234353Sdim ProgramStateRef State = C.getState(); 283226586Sdim 284239462Sdim const FunctionDecl *FD = C.getCalleeDecl(CE); 285239462Sdim if (!FD || FD->getKind() != Decl::Function) 286239462Sdim return; 287239462Sdim 288239462Sdim StringRef funName = C.getCalleeName(FD); 289234353Sdim if (funName.empty()) 290226586Sdim return; 291226586Sdim 292226586Sdim // If it is a call to an allocator function, it could be a double allocation. 293226586Sdim idx = getTrackedFunctionIndex(funName, true); 294226586Sdim if (idx != InvalidIdx) { 295226586Sdim const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 296226586Sdim if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) 297226586Sdim if (const AllocationState *AS = State->get<AllocatedData>(V)) { 298226586Sdim if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) { 299226586Sdim // Remove the value from the state. The new symbol will be added for 300226586Sdim // tracking when the second allocator is processed in checkPostStmt(). 301226586Sdim State = State->remove<AllocatedData>(V); 302234353Sdim ExplodedNode *N = C.addTransition(State); 303226586Sdim if (!N) 304226586Sdim return; 305226586Sdim initBugType(); 306234353Sdim SmallString<128> sbuf; 307226586Sdim llvm::raw_svector_ostream os(sbuf); 308226586Sdim unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 309226586Sdim os << "Allocated data should be released before another call to " 310226586Sdim << "the allocator: missing a call to '" 311226586Sdim << FunctionsToTrack[DIdx].Name 312226586Sdim << "'."; 313226586Sdim BugReport *Report = new BugReport(*BT, os.str(), N); 314226586Sdim Report->addVisitor(new SecKeychainBugVisitor(V)); 315226586Sdim Report->addRange(ArgExpr->getSourceRange()); 316234353Sdim Report->markInteresting(AS->Region); 317243830Sdim C.emitReport(Report); 318226586Sdim } 319226586Sdim } 320226586Sdim return; 321226586Sdim } 322226586Sdim 323226586Sdim // Is it a call to one of deallocator functions? 324226586Sdim idx = getTrackedFunctionIndex(funName, false); 325226586Sdim if (idx == InvalidIdx) 326226586Sdim return; 327226586Sdim 328226586Sdim // Check the argument to the deallocator. 329226586Sdim const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 330234353Sdim SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext()); 331226586Sdim 332226586Sdim // Undef is reported by another checker. 333226586Sdim if (ArgSVal.isUndef()) 334226586Sdim return; 335226586Sdim 336234353Sdim SymbolRef ArgSM = ArgSVal.getAsLocSymbol(); 337226586Sdim 338226586Sdim // If the argument is coming from the heap, globals, or unknown, do not 339226586Sdim // report it. 340234353Sdim bool RegionArgIsBad = false; 341234353Sdim if (!ArgSM) { 342234353Sdim if (!isBadDeallocationArgument(ArgSVal.getAsRegion())) 343234353Sdim return; 344234353Sdim RegionArgIsBad = true; 345234353Sdim } 346226586Sdim 347226586Sdim // Is the argument to the call being tracked? 348226586Sdim const AllocationState *AS = State->get<AllocatedData>(ArgSM); 349226586Sdim if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) { 350226586Sdim return; 351226586Sdim } 352226586Sdim // If trying to free data which has not been allocated yet, report as a bug. 353226586Sdim // TODO: We might want a more precise diagnostic for double free 354226586Sdim // (that would involve tracking all the freed symbols in the checker state). 355226586Sdim if (!AS || RegionArgIsBad) { 356226586Sdim // It is possible that this is a false positive - the argument might 357226586Sdim // have entered as an enclosing function parameter. 358226586Sdim if (isEnclosingFunctionParam(ArgExpr)) 359226586Sdim return; 360226586Sdim 361234353Sdim ExplodedNode *N = C.addTransition(State); 362226586Sdim if (!N) 363226586Sdim return; 364226586Sdim initBugType(); 365226586Sdim BugReport *Report = new BugReport(*BT, 366226586Sdim "Trying to free data which has not been allocated.", N); 367226586Sdim Report->addRange(ArgExpr->getSourceRange()); 368234353Sdim if (AS) 369234353Sdim Report->markInteresting(AS->Region); 370243830Sdim C.emitReport(Report); 371226586Sdim return; 372226586Sdim } 373226586Sdim 374226586Sdim // Process functions which might deallocate. 375226586Sdim if (FunctionsToTrack[idx].Kind == PossibleAPI) { 376226586Sdim 377226586Sdim if (funName == "CFStringCreateWithBytesNoCopy") { 378226586Sdim const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); 379226586Sdim // NULL ~ default deallocator, so warn. 380226586Sdim if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), 381226586Sdim Expr::NPC_ValueDependentIsNotNull)) { 382226586Sdim const AllocationPair AP = std::make_pair(ArgSM, AS); 383226586Sdim generateDeallocatorMismatchReport(AP, ArgExpr, C); 384226586Sdim return; 385226586Sdim } 386226586Sdim // One of the default allocators, so warn. 387226586Sdim if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { 388226586Sdim StringRef DeallocatorName = DE->getFoundDecl()->getName(); 389226586Sdim if (DeallocatorName == "kCFAllocatorDefault" || 390226586Sdim DeallocatorName == "kCFAllocatorSystemDefault" || 391226586Sdim DeallocatorName == "kCFAllocatorMalloc") { 392226586Sdim const AllocationPair AP = std::make_pair(ArgSM, AS); 393226586Sdim generateDeallocatorMismatchReport(AP, ArgExpr, C); 394226586Sdim return; 395226586Sdim } 396226586Sdim // If kCFAllocatorNull, which does not deallocate, we still have to 397249423Sdim // find the deallocator. 398249423Sdim if (DE->getFoundDecl()->getName() == "kCFAllocatorNull") 399226586Sdim return; 400226586Sdim } 401249423Sdim // In all other cases, assume the user supplied a correct deallocator 402249423Sdim // that will free memory so stop tracking. 403249423Sdim State = State->remove<AllocatedData>(ArgSM); 404249423Sdim C.addTransition(State); 405249423Sdim return; 406226586Sdim } 407249423Sdim 408249423Sdim llvm_unreachable("We know of no other possible APIs."); 409226586Sdim } 410226586Sdim 411226586Sdim // The call is deallocating a value we previously allocated, so remove it 412226586Sdim // from the next state. 413226586Sdim State = State->remove<AllocatedData>(ArgSM); 414226586Sdim 415226586Sdim // Check if the proper deallocator is used. 416226586Sdim unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 417226586Sdim if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { 418226586Sdim const AllocationPair AP = std::make_pair(ArgSM, AS); 419226586Sdim generateDeallocatorMismatchReport(AP, ArgExpr, C); 420226586Sdim return; 421226586Sdim } 422226586Sdim 423234353Sdim // If the buffer can be null and the return status can be an error, 424234353Sdim // report a bad call to free. 425249423Sdim if (State->assume(ArgSVal.castAs<DefinedSVal>(), false) && 426234353Sdim !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { 427234353Sdim ExplodedNode *N = C.addTransition(State); 428226586Sdim if (!N) 429226586Sdim return; 430226586Sdim initBugType(); 431226586Sdim BugReport *Report = new BugReport(*BT, 432234353Sdim "Only call free if a valid (non-NULL) buffer was returned.", N); 433226586Sdim Report->addVisitor(new SecKeychainBugVisitor(ArgSM)); 434226586Sdim Report->addRange(ArgExpr->getSourceRange()); 435234353Sdim Report->markInteresting(AS->Region); 436243830Sdim C.emitReport(Report); 437226586Sdim return; 438226586Sdim } 439226586Sdim 440226586Sdim C.addTransition(State); 441226586Sdim} 442226586Sdim 443226586Sdimvoid MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, 444226586Sdim CheckerContext &C) const { 445234353Sdim ProgramStateRef State = C.getState(); 446239462Sdim const FunctionDecl *FD = C.getCalleeDecl(CE); 447239462Sdim if (!FD || FD->getKind() != Decl::Function) 448239462Sdim return; 449226586Sdim 450239462Sdim StringRef funName = C.getCalleeName(FD); 451239462Sdim 452226586Sdim // If a value has been allocated, add it to the set for tracking. 453226586Sdim unsigned idx = getTrackedFunctionIndex(funName, true); 454226586Sdim if (idx == InvalidIdx) 455226586Sdim return; 456226586Sdim 457226586Sdim const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 458226586Sdim // If the argument entered as an enclosing function parameter, skip it to 459226586Sdim // avoid false positives. 460234353Sdim if (isEnclosingFunctionParam(ArgExpr) && 461234353Sdim C.getLocationContext()->getParent() == 0) 462226586Sdim return; 463226586Sdim 464226586Sdim if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) { 465226586Sdim // If the argument points to something that's not a symbolic region, it 466226586Sdim // can be: 467226586Sdim // - unknown (cannot reason about it) 468226586Sdim // - undefined (already reported by other checker) 469226586Sdim // - constant (null - should not be tracked, 470226586Sdim // other constant will generate a compiler warning) 471226586Sdim // - goto (should be reported by other checker) 472226586Sdim 473226586Sdim // The call return value symbol should stay alive for as long as the 474226586Sdim // allocated value symbol, since our diagnostics depend on the value 475226586Sdim // returned by the call. Ex: Data should only be freed if noErr was 476226586Sdim // returned during allocation.) 477234353Sdim SymbolRef RetStatusSymbol = 478234353Sdim State->getSVal(CE, C.getLocationContext()).getAsSymbol(); 479226586Sdim C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol); 480226586Sdim 481226586Sdim // Track the allocated value in the checker state. 482226586Sdim State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx, 483226586Sdim RetStatusSymbol)); 484226586Sdim assert(State); 485226586Sdim C.addTransition(State); 486226586Sdim } 487226586Sdim} 488226586Sdim 489234353Sdim// TODO: This logic is the same as in Malloc checker. 490249423Sdimconst ExplodedNode * 491249423SdimMacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N, 492234353Sdim SymbolRef Sym, 493234353Sdim CheckerContext &C) const { 494234353Sdim const LocationContext *LeakContext = N->getLocationContext(); 495234353Sdim // Walk the ExplodedGraph backwards and find the first node that referred to 496234353Sdim // the tracked symbol. 497234353Sdim const ExplodedNode *AllocNode = N; 498234353Sdim 499234353Sdim while (N) { 500234353Sdim if (!N->getState()->get<AllocatedData>(Sym)) 501234353Sdim break; 502234353Sdim // Allocation node, is the last node in the current context in which the 503234353Sdim // symbol was tracked. 504234353Sdim if (N->getLocationContext() == LeakContext) 505234353Sdim AllocNode = N; 506234353Sdim N = N->pred_empty() ? NULL : *(N->pred_begin()); 507234353Sdim } 508234353Sdim 509249423Sdim return AllocNode; 510234353Sdim} 511234353Sdim 512226586SdimBugReport *MacOSKeychainAPIChecker:: 513226586Sdim generateAllocatedDataNotReleasedReport(const AllocationPair &AP, 514234353Sdim ExplodedNode *N, 515234353Sdim CheckerContext &C) const { 516226586Sdim const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; 517226586Sdim initBugType(); 518234353Sdim SmallString<70> sbuf; 519226586Sdim llvm::raw_svector_ostream os(sbuf); 520226586Sdim os << "Allocated data is not released: missing a call to '" 521226586Sdim << FunctionsToTrack[FI.DeallocatorIdx].Name << "'."; 522234353Sdim 523234353Sdim // Most bug reports are cached at the location where they occurred. 524234353Sdim // With leaks, we want to unique them by the location where they were 525234353Sdim // allocated, and only report a single path. 526234353Sdim PathDiagnosticLocation LocUsedForUniqueing; 527249423Sdim const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C); 528249423Sdim const Stmt *AllocStmt = 0; 529249423Sdim ProgramPoint P = AllocNode->getLocation(); 530249423Sdim if (Optional<CallExitEnd> Exit = P.getAs<CallExitEnd>()) 531249423Sdim AllocStmt = Exit->getCalleeContext()->getCallSite(); 532249423Sdim else if (Optional<clang::PostStmt> PS = P.getAs<clang::PostStmt>()) 533249423Sdim AllocStmt = PS->getStmt(); 534249423Sdim 535249423Sdim if (AllocStmt) 536234353Sdim LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt, 537249423Sdim C.getSourceManager(), 538249423Sdim AllocNode->getLocationContext()); 539234353Sdim 540249423Sdim BugReport *Report = new BugReport(*BT, os.str(), N, LocUsedForUniqueing, 541249423Sdim AllocNode->getLocationContext()->getDecl()); 542249423Sdim 543226586Sdim Report->addVisitor(new SecKeychainBugVisitor(AP.first)); 544234353Sdim markInteresting(Report, AP); 545226586Sdim return Report; 546226586Sdim} 547226586Sdim 548226586Sdimvoid MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, 549226586Sdim CheckerContext &C) const { 550234353Sdim ProgramStateRef State = C.getState(); 551243830Sdim AllocatedDataTy ASet = State->get<AllocatedData>(); 552226586Sdim if (ASet.isEmpty()) 553226586Sdim return; 554226586Sdim 555226586Sdim bool Changed = false; 556226586Sdim AllocationPairVec Errors; 557243830Sdim for (AllocatedDataTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) { 558226586Sdim if (SR.isLive(I->first)) 559226586Sdim continue; 560226586Sdim 561226586Sdim Changed = true; 562226586Sdim State = State->remove<AllocatedData>(I->first); 563226586Sdim // If the allocated symbol is null or if the allocation call might have 564226586Sdim // returned an error, do not report. 565243830Sdim ConstraintManager &CMgr = State->getConstraintManager(); 566243830Sdim ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey()); 567243830Sdim if (AllocFailed.isConstrainedTrue() || 568226586Sdim definitelyReturnedError(I->second.Region, State, C.getSValBuilder())) 569226586Sdim continue; 570226586Sdim Errors.push_back(std::make_pair(I->first, &I->second)); 571226586Sdim } 572234353Sdim if (!Changed) { 573234353Sdim // Generate the new, cleaned up state. 574234353Sdim C.addTransition(State); 575226586Sdim return; 576234353Sdim } 577226586Sdim 578234353Sdim static SimpleProgramPointTag Tag("MacOSKeychainAPIChecker : DeadSymbolsLeak"); 579234353Sdim ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag); 580226586Sdim 581226586Sdim // Generate the error reports. 582226586Sdim for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); 583226586Sdim I != E; ++I) { 584243830Sdim C.emitReport(generateAllocatedDataNotReleasedReport(*I, N, C)); 585226586Sdim } 586234353Sdim 587234353Sdim // Generate the new, cleaned up state. 588234353Sdim C.addTransition(State, N); 589226586Sdim} 590226586Sdim 591234353Sdim 592226586SdimPathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( 593226586Sdim const ExplodedNode *N, 594226586Sdim const ExplodedNode *PrevN, 595226586Sdim BugReporterContext &BRC, 596226586Sdim BugReport &BR) { 597226586Sdim const AllocationState *AS = N->getState()->get<AllocatedData>(Sym); 598226586Sdim if (!AS) 599226586Sdim return 0; 600226586Sdim const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym); 601226586Sdim if (ASPrev) 602226586Sdim return 0; 603226586Sdim 604226586Sdim // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the 605226586Sdim // allocation site. 606249423Sdim const CallExpr *CE = 607249423Sdim cast<CallExpr>(N->getLocation().castAs<StmtPoint>().getStmt()); 608226586Sdim const FunctionDecl *funDecl = CE->getDirectCallee(); 609226586Sdim assert(funDecl && "We do not support indirect function calls as of now."); 610226586Sdim StringRef funName = funDecl->getName(); 611226586Sdim 612226586Sdim // Get the expression of the corresponding argument. 613226586Sdim unsigned Idx = getTrackedFunctionIndex(funName, true); 614226586Sdim assert(Idx != InvalidIdx && "This should be a call to an allocator."); 615226586Sdim const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param); 616226586Sdim PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(), 617226586Sdim N->getLocationContext()); 618226586Sdim return new PathDiagnosticEventPiece(Pos, "Data is allocated here."); 619226586Sdim} 620226586Sdim 621226586Sdimvoid ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) { 622226586Sdim mgr.registerChecker<MacOSKeychainAPIChecker>(); 623226586Sdim} 624