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