MacOSKeychainAPIChecker.cpp revision 226586
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" 16226586Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 17226586Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h" 18226586Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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" 22226586Sdim 23226586Sdimusing namespace clang; 24226586Sdimusing namespace ento; 25226586Sdim 26226586Sdimnamespace { 27226586Sdimclass MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, 28226586Sdim check::PreStmt<ReturnStmt>, 29226586Sdim check::PostStmt<CallExpr>, 30226586Sdim check::EndPath, 31226586Sdim check::DeadSymbols> { 32226586Sdim mutable llvm::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 checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; 59226586Sdim void checkPostStmt(const CallExpr *S, CheckerContext &C) const; 60226586Sdim void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 61226586Sdim void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const; 62226586Sdim 63226586Sdimprivate: 64226586Sdim typedef std::pair<SymbolRef, const AllocationState*> AllocationPair; 65226586Sdim typedef llvm::SmallVector<AllocationPair, 2> AllocationPairVec; 66226586Sdim 67226586Sdim enum APIKind { 68226586Sdim /// Denotes functions tracked by this checker. 69226586Sdim ValidAPI = 0, 70226586Sdim /// The functions commonly/mistakenly used in place of the given API. 71226586Sdim ErrorAPI = 1, 72226586Sdim /// The functions which may allocate the data. These are tracked to reduce 73226586Sdim /// the false alarm rate. 74226586Sdim PossibleAPI = 2 75226586Sdim }; 76226586Sdim /// Stores the information about the allocator and deallocator functions - 77226586Sdim /// these are the functions the checker is tracking. 78226586Sdim struct ADFunctionInfo { 79226586Sdim const char* Name; 80226586Sdim unsigned int Param; 81226586Sdim unsigned int DeallocatorIdx; 82226586Sdim APIKind Kind; 83226586Sdim }; 84226586Sdim static const unsigned InvalidIdx = 100000; 85226586Sdim static const unsigned FunctionsToTrackSize = 8; 86226586Sdim static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize]; 87226586Sdim /// The value, which represents no error return value for allocator functions. 88226586Sdim static const unsigned NoErr = 0; 89226586Sdim 90226586Sdim /// Given the function name, returns the index of the allocator/deallocator 91226586Sdim /// function. 92226586Sdim static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator); 93226586Sdim 94226586Sdim inline void initBugType() const { 95226586Sdim if (!BT) 96226586Sdim BT.reset(new BugType("Improper use of SecKeychain API", "Mac OS API")); 97226586Sdim } 98226586Sdim 99226586Sdim void generateDeallocatorMismatchReport(const AllocationPair &AP, 100226586Sdim const Expr *ArgExpr, 101226586Sdim CheckerContext &C) const; 102226586Sdim 103226586Sdim BugReport *generateAllocatedDataNotReleasedReport(const AllocationPair &AP, 104226586Sdim ExplodedNode *N) const; 105226586Sdim 106226586Sdim /// Check if RetSym evaluates to an error value in the current state. 107226586Sdim bool definitelyReturnedError(SymbolRef RetSym, 108226586Sdim const ProgramState *State, 109226586Sdim SValBuilder &Builder, 110226586Sdim bool noError = false) const; 111226586Sdim 112226586Sdim /// Check if RetSym evaluates to a NoErr value in the current state. 113226586Sdim bool definitelyDidnotReturnError(SymbolRef RetSym, 114226586Sdim const ProgramState *State, 115226586Sdim SValBuilder &Builder) const { 116226586Sdim return definitelyReturnedError(RetSym, State, Builder, true); 117226586Sdim } 118226586Sdim 119226586Sdim /// The bug visitor which allows us to print extra diagnostics along the 120226586Sdim /// BugReport path. For example, showing the allocation site of the leaked 121226586Sdim /// region. 122226586Sdim class SecKeychainBugVisitor : public BugReporterVisitor { 123226586Sdim protected: 124226586Sdim // The allocated region symbol tracked by the main analysis. 125226586Sdim SymbolRef Sym; 126226586Sdim 127226586Sdim public: 128226586Sdim SecKeychainBugVisitor(SymbolRef S) : Sym(S) {} 129226586Sdim virtual ~SecKeychainBugVisitor() {} 130226586Sdim 131226586Sdim void Profile(llvm::FoldingSetNodeID &ID) const { 132226586Sdim static int X = 0; 133226586Sdim ID.AddPointer(&X); 134226586Sdim ID.AddPointer(Sym); 135226586Sdim } 136226586Sdim 137226586Sdim PathDiagnosticPiece *VisitNode(const ExplodedNode *N, 138226586Sdim const ExplodedNode *PrevN, 139226586Sdim BugReporterContext &BRC, 140226586Sdim BugReport &BR); 141226586Sdim }; 142226586Sdim}; 143226586Sdim} 144226586Sdim 145226586Sdim/// ProgramState traits to store the currently allocated (and not yet freed) 146226586Sdim/// symbols. This is a map from the allocated content symbol to the 147226586Sdim/// corresponding AllocationState. 148226586Sdimtypedef llvm::ImmutableMap<SymbolRef, 149226586Sdim MacOSKeychainAPIChecker::AllocationState> AllocatedSetTy; 150226586Sdim 151226586Sdimnamespace { struct AllocatedData {}; } 152226586Sdimnamespace clang { namespace ento { 153226586Sdimtemplate<> struct ProgramStateTrait<AllocatedData> 154226586Sdim : public ProgramStatePartialTrait<AllocatedSetTy > { 155226586Sdim static void *GDMIndex() { static int index = 0; return &index; } 156226586Sdim}; 157226586Sdim}} 158226586Sdim 159226586Sdimstatic bool isEnclosingFunctionParam(const Expr *E) { 160226586Sdim E = E->IgnoreParenCasts(); 161226586Sdim if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { 162226586Sdim const ValueDecl *VD = DRE->getDecl(); 163226586Sdim if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD)) 164226586Sdim return true; 165226586Sdim } 166226586Sdim return false; 167226586Sdim} 168226586Sdim 169226586Sdimconst MacOSKeychainAPIChecker::ADFunctionInfo 170226586Sdim MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = { 171226586Sdim {"SecKeychainItemCopyContent", 4, 3, ValidAPI}, // 0 172226586Sdim {"SecKeychainFindGenericPassword", 6, 3, ValidAPI}, // 1 173226586Sdim {"SecKeychainFindInternetPassword", 13, 3, ValidAPI}, // 2 174226586Sdim {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI}, // 3 175226586Sdim {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI}, // 4 176226586Sdim {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI}, // 5 177226586Sdim {"free", 0, InvalidIdx, ErrorAPI}, // 6 178226586Sdim {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI}, // 7 179226586Sdim}; 180226586Sdim 181226586Sdimunsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name, 182226586Sdim bool IsAllocator) { 183226586Sdim for (unsigned I = 0; I < FunctionsToTrackSize; ++I) { 184226586Sdim ADFunctionInfo FI = FunctionsToTrack[I]; 185226586Sdim if (FI.Name != Name) 186226586Sdim continue; 187226586Sdim // Make sure the function is of the right type (allocator vs deallocator). 188226586Sdim if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx)) 189226586Sdim return InvalidIdx; 190226586Sdim if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx)) 191226586Sdim return InvalidIdx; 192226586Sdim 193226586Sdim return I; 194226586Sdim } 195226586Sdim // The function is not tracked. 196226586Sdim return InvalidIdx; 197226586Sdim} 198226586Sdim 199226586Sdimstatic SymbolRef getSymbolForRegion(CheckerContext &C, 200226586Sdim const MemRegion *R) { 201226586Sdim // Implicit casts (ex: void* -> char*) can turn Symbolic region into element 202226586Sdim // region, if that is the case, get the underlining region. 203226586Sdim R = R->StripCasts(); 204226586Sdim if (!isa<SymbolicRegion>(R)) { 205226586Sdim return 0; 206226586Sdim } 207226586Sdim return cast<SymbolicRegion>(R)->getSymbol(); 208226586Sdim} 209226586Sdim 210226586Sdimstatic bool isBadDeallocationArgument(const MemRegion *Arg) { 211226586Sdim if (isa<AllocaRegion>(Arg) || 212226586Sdim isa<BlockDataRegion>(Arg) || 213226586Sdim isa<TypedRegion>(Arg)) { 214226586Sdim return true; 215226586Sdim } 216226586Sdim return false; 217226586Sdim} 218226586Sdim/// Given the address expression, retrieve the value it's pointing to. Assume 219226586Sdim/// that value is itself an address, and return the corresponding symbol. 220226586Sdimstatic SymbolRef getAsPointeeSymbol(const Expr *Expr, 221226586Sdim CheckerContext &C) { 222226586Sdim const ProgramState *State = C.getState(); 223226586Sdim SVal ArgV = State->getSVal(Expr); 224226586Sdim 225226586Sdim if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&ArgV)) { 226226586Sdim StoreManager& SM = C.getStoreManager(); 227226586Sdim const MemRegion *V = SM.Retrieve(State->getStore(), *X).getAsRegion(); 228226586Sdim if (V) 229226586Sdim return getSymbolForRegion(C, V); 230226586Sdim } 231226586Sdim return 0; 232226586Sdim} 233226586Sdim 234226586Sdim// When checking for error code, we need to consider the following cases: 235226586Sdim// 1) noErr / [0] 236226586Sdim// 2) someErr / [1, inf] 237226586Sdim// 3) unknown 238226586Sdim// If noError, returns true iff (1). 239226586Sdim// If !noError, returns true iff (2). 240226586Sdimbool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym, 241226586Sdim const ProgramState *State, 242226586Sdim SValBuilder &Builder, 243226586Sdim bool noError) const { 244226586Sdim DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal(NoErr, 245226586Sdim Builder.getSymbolManager().getType(RetSym)); 246226586Sdim DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal, 247226586Sdim nonloc::SymbolVal(RetSym)); 248226586Sdim const ProgramState *ErrState = State->assume(NoErr, noError); 249226586Sdim if (ErrState == State) { 250226586Sdim return true; 251226586Sdim } 252226586Sdim 253226586Sdim return false; 254226586Sdim} 255226586Sdim 256226586Sdim// Report deallocator mismatch. Remove the region from tracking - reporting a 257226586Sdim// missing free error after this one is redundant. 258226586Sdimvoid MacOSKeychainAPIChecker:: 259226586Sdim generateDeallocatorMismatchReport(const AllocationPair &AP, 260226586Sdim const Expr *ArgExpr, 261226586Sdim CheckerContext &C) const { 262226586Sdim const ProgramState *State = C.getState(); 263226586Sdim State = State->remove<AllocatedData>(AP.first); 264226586Sdim ExplodedNode *N = C.generateNode(State); 265226586Sdim 266226586Sdim if (!N) 267226586Sdim return; 268226586Sdim initBugType(); 269226586Sdim llvm::SmallString<80> sbuf; 270226586Sdim llvm::raw_svector_ostream os(sbuf); 271226586Sdim unsigned int PDeallocIdx = 272226586Sdim FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx; 273226586Sdim 274226586Sdim os << "Deallocator doesn't match the allocator: '" 275226586Sdim << FunctionsToTrack[PDeallocIdx].Name << "' should be used."; 276226586Sdim BugReport *Report = new BugReport(*BT, os.str(), N); 277226586Sdim Report->addVisitor(new SecKeychainBugVisitor(AP.first)); 278226586Sdim Report->addRange(ArgExpr->getSourceRange()); 279226586Sdim C.EmitReport(Report); 280226586Sdim} 281226586Sdim 282226586Sdimvoid MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, 283226586Sdim CheckerContext &C) const { 284226586Sdim const ProgramState *State = C.getState(); 285226586Sdim const Expr *Callee = CE->getCallee(); 286226586Sdim SVal L = State->getSVal(Callee); 287226586Sdim unsigned idx = InvalidIdx; 288226586Sdim 289226586Sdim const FunctionDecl *funDecl = L.getAsFunctionDecl(); 290226586Sdim if (!funDecl) 291226586Sdim return; 292226586Sdim IdentifierInfo *funI = funDecl->getIdentifier(); 293226586Sdim if (!funI) 294226586Sdim return; 295226586Sdim StringRef funName = funI->getName(); 296226586Sdim 297226586Sdim // If it is a call to an allocator function, it could be a double allocation. 298226586Sdim idx = getTrackedFunctionIndex(funName, true); 299226586Sdim if (idx != InvalidIdx) { 300226586Sdim const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 301226586Sdim if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) 302226586Sdim if (const AllocationState *AS = State->get<AllocatedData>(V)) { 303226586Sdim if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) { 304226586Sdim // Remove the value from the state. The new symbol will be added for 305226586Sdim // tracking when the second allocator is processed in checkPostStmt(). 306226586Sdim State = State->remove<AllocatedData>(V); 307226586Sdim ExplodedNode *N = C.generateNode(State); 308226586Sdim if (!N) 309226586Sdim return; 310226586Sdim initBugType(); 311226586Sdim llvm::SmallString<128> sbuf; 312226586Sdim llvm::raw_svector_ostream os(sbuf); 313226586Sdim unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 314226586Sdim os << "Allocated data should be released before another call to " 315226586Sdim << "the allocator: missing a call to '" 316226586Sdim << FunctionsToTrack[DIdx].Name 317226586Sdim << "'."; 318226586Sdim BugReport *Report = new BugReport(*BT, os.str(), N); 319226586Sdim Report->addVisitor(new SecKeychainBugVisitor(V)); 320226586Sdim Report->addRange(ArgExpr->getSourceRange()); 321226586Sdim C.EmitReport(Report); 322226586Sdim } 323226586Sdim } 324226586Sdim return; 325226586Sdim } 326226586Sdim 327226586Sdim // Is it a call to one of deallocator functions? 328226586Sdim idx = getTrackedFunctionIndex(funName, false); 329226586Sdim if (idx == InvalidIdx) 330226586Sdim return; 331226586Sdim 332226586Sdim // Check the argument to the deallocator. 333226586Sdim const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 334226586Sdim SVal ArgSVal = State->getSVal(ArgExpr); 335226586Sdim 336226586Sdim // Undef is reported by another checker. 337226586Sdim if (ArgSVal.isUndef()) 338226586Sdim return; 339226586Sdim 340226586Sdim const MemRegion *Arg = ArgSVal.getAsRegion(); 341226586Sdim if (!Arg) 342226586Sdim return; 343226586Sdim 344226586Sdim SymbolRef ArgSM = getSymbolForRegion(C, Arg); 345226586Sdim bool RegionArgIsBad = ArgSM ? false : isBadDeallocationArgument(Arg); 346226586Sdim // If the argument is coming from the heap, globals, or unknown, do not 347226586Sdim // report it. 348226586Sdim if (!ArgSM && !RegionArgIsBad) 349226586Sdim return; 350226586Sdim 351226586Sdim // Is the argument to the call being tracked? 352226586Sdim const AllocationState *AS = State->get<AllocatedData>(ArgSM); 353226586Sdim if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) { 354226586Sdim return; 355226586Sdim } 356226586Sdim // If trying to free data which has not been allocated yet, report as a bug. 357226586Sdim // TODO: We might want a more precise diagnostic for double free 358226586Sdim // (that would involve tracking all the freed symbols in the checker state). 359226586Sdim if (!AS || RegionArgIsBad) { 360226586Sdim // It is possible that this is a false positive - the argument might 361226586Sdim // have entered as an enclosing function parameter. 362226586Sdim if (isEnclosingFunctionParam(ArgExpr)) 363226586Sdim return; 364226586Sdim 365226586Sdim ExplodedNode *N = C.generateNode(State); 366226586Sdim if (!N) 367226586Sdim return; 368226586Sdim initBugType(); 369226586Sdim BugReport *Report = new BugReport(*BT, 370226586Sdim "Trying to free data which has not been allocated.", N); 371226586Sdim Report->addRange(ArgExpr->getSourceRange()); 372226586Sdim C.EmitReport(Report); 373226586Sdim return; 374226586Sdim } 375226586Sdim 376226586Sdim // Process functions which might deallocate. 377226586Sdim if (FunctionsToTrack[idx].Kind == PossibleAPI) { 378226586Sdim 379226586Sdim if (funName == "CFStringCreateWithBytesNoCopy") { 380226586Sdim const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); 381226586Sdim // NULL ~ default deallocator, so warn. 382226586Sdim if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), 383226586Sdim Expr::NPC_ValueDependentIsNotNull)) { 384226586Sdim const AllocationPair AP = std::make_pair(ArgSM, AS); 385226586Sdim generateDeallocatorMismatchReport(AP, ArgExpr, C); 386226586Sdim return; 387226586Sdim } 388226586Sdim // One of the default allocators, so warn. 389226586Sdim if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { 390226586Sdim StringRef DeallocatorName = DE->getFoundDecl()->getName(); 391226586Sdim if (DeallocatorName == "kCFAllocatorDefault" || 392226586Sdim DeallocatorName == "kCFAllocatorSystemDefault" || 393226586Sdim DeallocatorName == "kCFAllocatorMalloc") { 394226586Sdim const AllocationPair AP = std::make_pair(ArgSM, AS); 395226586Sdim generateDeallocatorMismatchReport(AP, ArgExpr, C); 396226586Sdim return; 397226586Sdim } 398226586Sdim // If kCFAllocatorNull, which does not deallocate, we still have to 399226586Sdim // find the deallocator. Otherwise, assume that the user had written a 400226586Sdim // custom deallocator which does the right thing. 401226586Sdim if (DE->getFoundDecl()->getName() != "kCFAllocatorNull") { 402226586Sdim State = State->remove<AllocatedData>(ArgSM); 403226586Sdim C.addTransition(State); 404226586Sdim return; 405226586Sdim } 406226586Sdim } 407226586Sdim } 408226586Sdim return; 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 423226586Sdim // If the return status is undefined or is error, report a bad call to free. 424226586Sdim if (!definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { 425226586Sdim ExplodedNode *N = C.generateNode(State); 426226586Sdim if (!N) 427226586Sdim return; 428226586Sdim initBugType(); 429226586Sdim BugReport *Report = new BugReport(*BT, 430226586Sdim "Call to free data when error was returned during allocation.", N); 431226586Sdim Report->addVisitor(new SecKeychainBugVisitor(ArgSM)); 432226586Sdim Report->addRange(ArgExpr->getSourceRange()); 433226586Sdim C.EmitReport(Report); 434226586Sdim return; 435226586Sdim } 436226586Sdim 437226586Sdim C.addTransition(State); 438226586Sdim} 439226586Sdim 440226586Sdimvoid MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, 441226586Sdim CheckerContext &C) const { 442226586Sdim const ProgramState *State = C.getState(); 443226586Sdim const Expr *Callee = CE->getCallee(); 444226586Sdim SVal L = State->getSVal(Callee); 445226586Sdim 446226586Sdim const FunctionDecl *funDecl = L.getAsFunctionDecl(); 447226586Sdim if (!funDecl) 448226586Sdim return; 449226586Sdim IdentifierInfo *funI = funDecl->getIdentifier(); 450226586Sdim if (!funI) 451226586Sdim return; 452226586Sdim StringRef funName = funI->getName(); 453226586Sdim 454226586Sdim // If a value has been allocated, add it to the set for tracking. 455226586Sdim unsigned idx = getTrackedFunctionIndex(funName, true); 456226586Sdim if (idx == InvalidIdx) 457226586Sdim return; 458226586Sdim 459226586Sdim const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 460226586Sdim // If the argument entered as an enclosing function parameter, skip it to 461226586Sdim // avoid false positives. 462226586Sdim if (isEnclosingFunctionParam(ArgExpr)) 463226586Sdim return; 464226586Sdim 465226586Sdim if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) { 466226586Sdim // If the argument points to something that's not a symbolic region, it 467226586Sdim // can be: 468226586Sdim // - unknown (cannot reason about it) 469226586Sdim // - undefined (already reported by other checker) 470226586Sdim // - constant (null - should not be tracked, 471226586Sdim // other constant will generate a compiler warning) 472226586Sdim // - goto (should be reported by other checker) 473226586Sdim 474226586Sdim // The call return value symbol should stay alive for as long as the 475226586Sdim // allocated value symbol, since our diagnostics depend on the value 476226586Sdim // returned by the call. Ex: Data should only be freed if noErr was 477226586Sdim // returned during allocation.) 478226586Sdim SymbolRef RetStatusSymbol = State->getSVal(CE).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 489226586Sdimvoid MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S, 490226586Sdim CheckerContext &C) const { 491226586Sdim const Expr *retExpr = S->getRetValue(); 492226586Sdim if (!retExpr) 493226586Sdim return; 494226586Sdim 495226586Sdim // Check if the value is escaping through the return. 496226586Sdim const ProgramState *state = C.getState(); 497226586Sdim const MemRegion *V = state->getSVal(retExpr).getAsRegion(); 498226586Sdim if (!V) 499226586Sdim return; 500226586Sdim state = state->remove<AllocatedData>(getSymbolForRegion(C, V)); 501226586Sdim 502226586Sdim // Proceed from the new state. 503226586Sdim C.addTransition(state); 504226586Sdim} 505226586Sdim 506226586SdimBugReport *MacOSKeychainAPIChecker:: 507226586Sdim generateAllocatedDataNotReleasedReport(const AllocationPair &AP, 508226586Sdim ExplodedNode *N) const { 509226586Sdim const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; 510226586Sdim initBugType(); 511226586Sdim llvm::SmallString<70> sbuf; 512226586Sdim llvm::raw_svector_ostream os(sbuf); 513226586Sdim 514226586Sdim os << "Allocated data is not released: missing a call to '" 515226586Sdim << FunctionsToTrack[FI.DeallocatorIdx].Name << "'."; 516226586Sdim BugReport *Report = new BugReport(*BT, os.str(), N); 517226586Sdim Report->addVisitor(new SecKeychainBugVisitor(AP.first)); 518226586Sdim Report->addRange(SourceRange()); 519226586Sdim return Report; 520226586Sdim} 521226586Sdim 522226586Sdimvoid MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, 523226586Sdim CheckerContext &C) const { 524226586Sdim const ProgramState *State = C.getState(); 525226586Sdim AllocatedSetTy ASet = State->get<AllocatedData>(); 526226586Sdim if (ASet.isEmpty()) 527226586Sdim return; 528226586Sdim 529226586Sdim bool Changed = false; 530226586Sdim AllocationPairVec Errors; 531226586Sdim for (AllocatedSetTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) { 532226586Sdim if (SR.isLive(I->first)) 533226586Sdim continue; 534226586Sdim 535226586Sdim Changed = true; 536226586Sdim State = State->remove<AllocatedData>(I->first); 537226586Sdim // If the allocated symbol is null or if the allocation call might have 538226586Sdim // returned an error, do not report. 539226586Sdim if (State->getSymVal(I->first) || 540226586Sdim definitelyReturnedError(I->second.Region, State, C.getSValBuilder())) 541226586Sdim continue; 542226586Sdim Errors.push_back(std::make_pair(I->first, &I->second)); 543226586Sdim } 544226586Sdim if (!Changed) 545226586Sdim return; 546226586Sdim 547226586Sdim // Generate the new, cleaned up state. 548226586Sdim ExplodedNode *N = C.generateNode(State); 549226586Sdim if (!N) 550226586Sdim return; 551226586Sdim 552226586Sdim // Generate the error reports. 553226586Sdim for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); 554226586Sdim I != E; ++I) { 555226586Sdim C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N)); 556226586Sdim } 557226586Sdim} 558226586Sdim 559226586Sdim// TODO: Remove this after we ensure that checkDeadSymbols are always called. 560226586Sdimvoid MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B, 561226586Sdim ExprEngine &Eng) const { 562226586Sdim const ProgramState *state = B.getState(); 563226586Sdim AllocatedSetTy AS = state->get<AllocatedData>(); 564226586Sdim if (AS.isEmpty()) 565226586Sdim return; 566226586Sdim 567226586Sdim // Anything which has been allocated but not freed (nor escaped) will be 568226586Sdim // found here, so report it. 569226586Sdim bool Changed = false; 570226586Sdim AllocationPairVec Errors; 571226586Sdim for (AllocatedSetTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) { 572226586Sdim Changed = true; 573226586Sdim state = state->remove<AllocatedData>(I->first); 574226586Sdim // If the allocated symbol is null or if error code was returned at 575226586Sdim // allocation, do not report. 576226586Sdim if (state->getSymVal(I.getKey()) || 577226586Sdim definitelyReturnedError(I->second.Region, state, 578226586Sdim Eng.getSValBuilder())) { 579226586Sdim continue; 580226586Sdim } 581226586Sdim Errors.push_back(std::make_pair(I->first, &I->second)); 582226586Sdim } 583226586Sdim 584226586Sdim // If no change, do not generate a new state. 585226586Sdim if (!Changed) 586226586Sdim return; 587226586Sdim 588226586Sdim ExplodedNode *N = B.generateNode(state); 589226586Sdim if (!N) 590226586Sdim return; 591226586Sdim 592226586Sdim // Generate the error reports. 593226586Sdim for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); 594226586Sdim I != E; ++I) { 595226586Sdim Eng.getBugReporter().EmitReport( 596226586Sdim generateAllocatedDataNotReleasedReport(*I, N)); 597226586Sdim } 598226586Sdim} 599226586Sdim 600226586Sdim 601226586SdimPathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( 602226586Sdim const ExplodedNode *N, 603226586Sdim const ExplodedNode *PrevN, 604226586Sdim BugReporterContext &BRC, 605226586Sdim BugReport &BR) { 606226586Sdim const AllocationState *AS = N->getState()->get<AllocatedData>(Sym); 607226586Sdim if (!AS) 608226586Sdim return 0; 609226586Sdim const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym); 610226586Sdim if (ASPrev) 611226586Sdim return 0; 612226586Sdim 613226586Sdim // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the 614226586Sdim // allocation site. 615226586Sdim const CallExpr *CE = cast<CallExpr>(cast<StmtPoint>(N->getLocation()) 616226586Sdim .getStmt()); 617226586Sdim const FunctionDecl *funDecl = CE->getDirectCallee(); 618226586Sdim assert(funDecl && "We do not support indirect function calls as of now."); 619226586Sdim StringRef funName = funDecl->getName(); 620226586Sdim 621226586Sdim // Get the expression of the corresponding argument. 622226586Sdim unsigned Idx = getTrackedFunctionIndex(funName, true); 623226586Sdim assert(Idx != InvalidIdx && "This should be a call to an allocator."); 624226586Sdim const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param); 625226586Sdim PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(), 626226586Sdim N->getLocationContext()); 627226586Sdim return new PathDiagnosticEventPiece(Pos, "Data is allocated here."); 628226586Sdim} 629226586Sdim 630226586Sdimvoid ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) { 631226586Sdim mgr.registerChecker<MacOSKeychainAPIChecker>(); 632226586Sdim} 633