clang API Documentation
00001 //==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // This checker flags misuses of KeyChainAPI. In particular, the password data 00010 // allocated/returned by SecKeychainItemCopyContent, 00011 // SecKeychainFindGenericPassword, SecKeychainFindInternetPassword functions has 00012 // to be freed using a call to SecKeychainItemFreeContent. 00013 //===----------------------------------------------------------------------===// 00014 00015 #include "ClangSACheckers.h" 00016 #include "clang/StaticAnalyzer/Core/Checker.h" 00017 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 00018 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 00019 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 00020 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 00021 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 00022 #include "llvm/ADT/SmallString.h" 00023 00024 using namespace clang; 00025 using namespace ento; 00026 00027 namespace { 00028 class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, 00029 check::PreStmt<ReturnStmt>, 00030 check::PostStmt<CallExpr>, 00031 check::EndPath, 00032 check::DeadSymbols> { 00033 mutable OwningPtr<BugType> BT; 00034 00035 public: 00036 /// AllocationState is a part of the checker specific state together with the 00037 /// MemRegion corresponding to the allocated data. 00038 struct AllocationState { 00039 /// The index of the allocator function. 00040 unsigned int AllocatorIdx; 00041 SymbolRef Region; 00042 00043 AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) : 00044 AllocatorIdx(Idx), 00045 Region(R) {} 00046 00047 bool operator==(const AllocationState &X) const { 00048 return (AllocatorIdx == X.AllocatorIdx && 00049 Region == X.Region); 00050 } 00051 00052 void Profile(llvm::FoldingSetNodeID &ID) const { 00053 ID.AddInteger(AllocatorIdx); 00054 ID.AddPointer(Region); 00055 } 00056 }; 00057 00058 void checkPreStmt(const CallExpr *S, CheckerContext &C) const; 00059 void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; 00060 void checkPostStmt(const CallExpr *S, CheckerContext &C) const; 00061 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 00062 void checkEndPath(CheckerContext &C) const; 00063 00064 private: 00065 typedef std::pair<SymbolRef, const AllocationState*> AllocationPair; 00066 typedef llvm::SmallVector<AllocationPair, 2> AllocationPairVec; 00067 00068 enum APIKind { 00069 /// Denotes functions tracked by this checker. 00070 ValidAPI = 0, 00071 /// The functions commonly/mistakenly used in place of the given API. 00072 ErrorAPI = 1, 00073 /// The functions which may allocate the data. These are tracked to reduce 00074 /// the false alarm rate. 00075 PossibleAPI = 2 00076 }; 00077 /// Stores the information about the allocator and deallocator functions - 00078 /// these are the functions the checker is tracking. 00079 struct ADFunctionInfo { 00080 const char* Name; 00081 unsigned int Param; 00082 unsigned int DeallocatorIdx; 00083 APIKind Kind; 00084 }; 00085 static const unsigned InvalidIdx = 100000; 00086 static const unsigned FunctionsToTrackSize = 8; 00087 static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize]; 00088 /// The value, which represents no error return value for allocator functions. 00089 static const unsigned NoErr = 0; 00090 00091 /// Given the function name, returns the index of the allocator/deallocator 00092 /// function. 00093 static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator); 00094 00095 inline void initBugType() const { 00096 if (!BT) 00097 BT.reset(new BugType("Improper use of SecKeychain API", "Mac OS API")); 00098 } 00099 00100 void generateDeallocatorMismatchReport(const AllocationPair &AP, 00101 const Expr *ArgExpr, 00102 CheckerContext &C) const; 00103 00104 /// Find the allocation site for Sym on the path leading to the node N. 00105 const Stmt *getAllocationSite(const ExplodedNode *N, SymbolRef Sym, 00106 CheckerContext &C) const; 00107 00108 BugReport *generateAllocatedDataNotReleasedReport(const AllocationPair &AP, 00109 ExplodedNode *N, 00110 CheckerContext &C) const; 00111 00112 /// Check if RetSym evaluates to an error value in the current state. 00113 bool definitelyReturnedError(SymbolRef RetSym, 00114 ProgramStateRef State, 00115 SValBuilder &Builder, 00116 bool noError = false) const; 00117 00118 /// Check if RetSym evaluates to a NoErr value in the current state. 00119 bool definitelyDidnotReturnError(SymbolRef RetSym, 00120 ProgramStateRef State, 00121 SValBuilder &Builder) const { 00122 return definitelyReturnedError(RetSym, State, Builder, true); 00123 } 00124 00125 /// Mark an AllocationPair interesting for diagnostic reporting. 00126 void markInteresting(BugReport *R, const AllocationPair &AP) const { 00127 R->markInteresting(AP.first); 00128 R->markInteresting(AP.second->Region); 00129 } 00130 00131 /// The bug visitor which allows us to print extra diagnostics along the 00132 /// BugReport path. For example, showing the allocation site of the leaked 00133 /// region. 00134 class SecKeychainBugVisitor 00135 : public BugReporterVisitorImpl<SecKeychainBugVisitor> { 00136 protected: 00137 // The allocated region symbol tracked by the main analysis. 00138 SymbolRef Sym; 00139 00140 public: 00141 SecKeychainBugVisitor(SymbolRef S) : Sym(S) {} 00142 virtual ~SecKeychainBugVisitor() {} 00143 00144 void Profile(llvm::FoldingSetNodeID &ID) const { 00145 static int X = 0; 00146 ID.AddPointer(&X); 00147 ID.AddPointer(Sym); 00148 } 00149 00150 PathDiagnosticPiece *VisitNode(const ExplodedNode *N, 00151 const ExplodedNode *PrevN, 00152 BugReporterContext &BRC, 00153 BugReport &BR); 00154 }; 00155 }; 00156 } 00157 00158 /// ProgramState traits to store the currently allocated (and not yet freed) 00159 /// symbols. This is a map from the allocated content symbol to the 00160 /// corresponding AllocationState. 00161 typedef llvm::ImmutableMap<SymbolRef, 00162 MacOSKeychainAPIChecker::AllocationState> AllocatedSetTy; 00163 00164 namespace { struct AllocatedData {}; } 00165 namespace clang { namespace ento { 00166 template<> struct ProgramStateTrait<AllocatedData> 00167 : public ProgramStatePartialTrait<AllocatedSetTy > { 00168 static void *GDMIndex() { static int index = 0; return &index; } 00169 }; 00170 }} 00171 00172 static bool isEnclosingFunctionParam(const Expr *E) { 00173 E = E->IgnoreParenCasts(); 00174 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { 00175 const ValueDecl *VD = DRE->getDecl(); 00176 if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD)) 00177 return true; 00178 } 00179 return false; 00180 } 00181 00182 const MacOSKeychainAPIChecker::ADFunctionInfo 00183 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = { 00184 {"SecKeychainItemCopyContent", 4, 3, ValidAPI}, // 0 00185 {"SecKeychainFindGenericPassword", 6, 3, ValidAPI}, // 1 00186 {"SecKeychainFindInternetPassword", 13, 3, ValidAPI}, // 2 00187 {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI}, // 3 00188 {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI}, // 4 00189 {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI}, // 5 00190 {"free", 0, InvalidIdx, ErrorAPI}, // 6 00191 {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI}, // 7 00192 }; 00193 00194 unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name, 00195 bool IsAllocator) { 00196 for (unsigned I = 0; I < FunctionsToTrackSize; ++I) { 00197 ADFunctionInfo FI = FunctionsToTrack[I]; 00198 if (FI.Name != Name) 00199 continue; 00200 // Make sure the function is of the right type (allocator vs deallocator). 00201 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx)) 00202 return InvalidIdx; 00203 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx)) 00204 return InvalidIdx; 00205 00206 return I; 00207 } 00208 // The function is not tracked. 00209 return InvalidIdx; 00210 } 00211 00212 static bool isBadDeallocationArgument(const MemRegion *Arg) { 00213 if (!Arg) 00214 return false; 00215 if (isa<AllocaRegion>(Arg) || 00216 isa<BlockDataRegion>(Arg) || 00217 isa<TypedRegion>(Arg)) { 00218 return true; 00219 } 00220 return false; 00221 } 00222 00223 /// Given the address expression, retrieve the value it's pointing to. Assume 00224 /// that value is itself an address, and return the corresponding symbol. 00225 static SymbolRef getAsPointeeSymbol(const Expr *Expr, 00226 CheckerContext &C) { 00227 ProgramStateRef State = C.getState(); 00228 SVal ArgV = State->getSVal(Expr, C.getLocationContext()); 00229 00230 if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&ArgV)) { 00231 StoreManager& SM = C.getStoreManager(); 00232 SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol(); 00233 if (sym) 00234 return sym; 00235 } 00236 return 0; 00237 } 00238 00239 // When checking for error code, we need to consider the following cases: 00240 // 1) noErr / [0] 00241 // 2) someErr / [1, inf] 00242 // 3) unknown 00243 // If noError, returns true iff (1). 00244 // If !noError, returns true iff (2). 00245 bool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym, 00246 ProgramStateRef State, 00247 SValBuilder &Builder, 00248 bool noError) const { 00249 DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal(NoErr, 00250 Builder.getSymbolManager().getType(RetSym)); 00251 DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal, 00252 nonloc::SymbolVal(RetSym)); 00253 ProgramStateRef ErrState = State->assume(NoErr, noError); 00254 if (ErrState == State) { 00255 return true; 00256 } 00257 00258 return false; 00259 } 00260 00261 // Report deallocator mismatch. Remove the region from tracking - reporting a 00262 // missing free error after this one is redundant. 00263 void MacOSKeychainAPIChecker:: 00264 generateDeallocatorMismatchReport(const AllocationPair &AP, 00265 const Expr *ArgExpr, 00266 CheckerContext &C) const { 00267 ProgramStateRef State = C.getState(); 00268 State = State->remove<AllocatedData>(AP.first); 00269 ExplodedNode *N = C.addTransition(State); 00270 00271 if (!N) 00272 return; 00273 initBugType(); 00274 SmallString<80> sbuf; 00275 llvm::raw_svector_ostream os(sbuf); 00276 unsigned int PDeallocIdx = 00277 FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx; 00278 00279 os << "Deallocator doesn't match the allocator: '" 00280 << FunctionsToTrack[PDeallocIdx].Name << "' should be used."; 00281 BugReport *Report = new BugReport(*BT, os.str(), N); 00282 Report->addVisitor(new SecKeychainBugVisitor(AP.first)); 00283 Report->addRange(ArgExpr->getSourceRange()); 00284 markInteresting(Report, AP); 00285 C.EmitReport(Report); 00286 } 00287 00288 void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, 00289 CheckerContext &C) const { 00290 unsigned idx = InvalidIdx; 00291 ProgramStateRef State = C.getState(); 00292 00293 StringRef funName = C.getCalleeName(CE); 00294 if (funName.empty()) 00295 return; 00296 00297 // If it is a call to an allocator function, it could be a double allocation. 00298 idx = getTrackedFunctionIndex(funName, true); 00299 if (idx != InvalidIdx) { 00300 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 00301 if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) 00302 if (const AllocationState *AS = State->get<AllocatedData>(V)) { 00303 if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) { 00304 // Remove the value from the state. The new symbol will be added for 00305 // tracking when the second allocator is processed in checkPostStmt(). 00306 State = State->remove<AllocatedData>(V); 00307 ExplodedNode *N = C.addTransition(State); 00308 if (!N) 00309 return; 00310 initBugType(); 00311 SmallString<128> sbuf; 00312 llvm::raw_svector_ostream os(sbuf); 00313 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 00314 os << "Allocated data should be released before another call to " 00315 << "the allocator: missing a call to '" 00316 << FunctionsToTrack[DIdx].Name 00317 << "'."; 00318 BugReport *Report = new BugReport(*BT, os.str(), N); 00319 Report->addVisitor(new SecKeychainBugVisitor(V)); 00320 Report->addRange(ArgExpr->getSourceRange()); 00321 Report->markInteresting(AS->Region); 00322 C.EmitReport(Report); 00323 } 00324 } 00325 return; 00326 } 00327 00328 // Is it a call to one of deallocator functions? 00329 idx = getTrackedFunctionIndex(funName, false); 00330 if (idx == InvalidIdx) 00331 return; 00332 00333 // Check the argument to the deallocator. 00334 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 00335 SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext()); 00336 00337 // Undef is reported by another checker. 00338 if (ArgSVal.isUndef()) 00339 return; 00340 00341 SymbolRef ArgSM = ArgSVal.getAsLocSymbol(); 00342 00343 // If the argument is coming from the heap, globals, or unknown, do not 00344 // report it. 00345 bool RegionArgIsBad = false; 00346 if (!ArgSM) { 00347 if (!isBadDeallocationArgument(ArgSVal.getAsRegion())) 00348 return; 00349 RegionArgIsBad = true; 00350 } 00351 00352 // Is the argument to the call being tracked? 00353 const AllocationState *AS = State->get<AllocatedData>(ArgSM); 00354 if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) { 00355 return; 00356 } 00357 // If trying to free data which has not been allocated yet, report as a bug. 00358 // TODO: We might want a more precise diagnostic for double free 00359 // (that would involve tracking all the freed symbols in the checker state). 00360 if (!AS || RegionArgIsBad) { 00361 // It is possible that this is a false positive - the argument might 00362 // have entered as an enclosing function parameter. 00363 if (isEnclosingFunctionParam(ArgExpr)) 00364 return; 00365 00366 ExplodedNode *N = C.addTransition(State); 00367 if (!N) 00368 return; 00369 initBugType(); 00370 BugReport *Report = new BugReport(*BT, 00371 "Trying to free data which has not been allocated.", N); 00372 Report->addRange(ArgExpr->getSourceRange()); 00373 if (AS) 00374 Report->markInteresting(AS->Region); 00375 C.EmitReport(Report); 00376 return; 00377 } 00378 00379 // Process functions which might deallocate. 00380 if (FunctionsToTrack[idx].Kind == PossibleAPI) { 00381 00382 if (funName == "CFStringCreateWithBytesNoCopy") { 00383 const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); 00384 // NULL ~ default deallocator, so warn. 00385 if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), 00386 Expr::NPC_ValueDependentIsNotNull)) { 00387 const AllocationPair AP = std::make_pair(ArgSM, AS); 00388 generateDeallocatorMismatchReport(AP, ArgExpr, C); 00389 return; 00390 } 00391 // One of the default allocators, so warn. 00392 if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { 00393 StringRef DeallocatorName = DE->getFoundDecl()->getName(); 00394 if (DeallocatorName == "kCFAllocatorDefault" || 00395 DeallocatorName == "kCFAllocatorSystemDefault" || 00396 DeallocatorName == "kCFAllocatorMalloc") { 00397 const AllocationPair AP = std::make_pair(ArgSM, AS); 00398 generateDeallocatorMismatchReport(AP, ArgExpr, C); 00399 return; 00400 } 00401 // If kCFAllocatorNull, which does not deallocate, we still have to 00402 // find the deallocator. Otherwise, assume that the user had written a 00403 // custom deallocator which does the right thing. 00404 if (DE->getFoundDecl()->getName() != "kCFAllocatorNull") { 00405 State = State->remove<AllocatedData>(ArgSM); 00406 C.addTransition(State); 00407 return; 00408 } 00409 } 00410 } 00411 return; 00412 } 00413 00414 // The call is deallocating a value we previously allocated, so remove it 00415 // from the next state. 00416 State = State->remove<AllocatedData>(ArgSM); 00417 00418 // Check if the proper deallocator is used. 00419 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; 00420 if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { 00421 const AllocationPair AP = std::make_pair(ArgSM, AS); 00422 generateDeallocatorMismatchReport(AP, ArgExpr, C); 00423 return; 00424 } 00425 00426 // If the buffer can be null and the return status can be an error, 00427 // report a bad call to free. 00428 if (State->assume(cast<DefinedSVal>(ArgSVal), false) && 00429 !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { 00430 ExplodedNode *N = C.addTransition(State); 00431 if (!N) 00432 return; 00433 initBugType(); 00434 BugReport *Report = new BugReport(*BT, 00435 "Only call free if a valid (non-NULL) buffer was returned.", N); 00436 Report->addVisitor(new SecKeychainBugVisitor(ArgSM)); 00437 Report->addRange(ArgExpr->getSourceRange()); 00438 Report->markInteresting(AS->Region); 00439 C.EmitReport(Report); 00440 return; 00441 } 00442 00443 C.addTransition(State); 00444 } 00445 00446 void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, 00447 CheckerContext &C) const { 00448 ProgramStateRef State = C.getState(); 00449 StringRef funName = C.getCalleeName(CE); 00450 00451 // If a value has been allocated, add it to the set for tracking. 00452 unsigned idx = getTrackedFunctionIndex(funName, true); 00453 if (idx == InvalidIdx) 00454 return; 00455 00456 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); 00457 // If the argument entered as an enclosing function parameter, skip it to 00458 // avoid false positives. 00459 if (isEnclosingFunctionParam(ArgExpr) && 00460 C.getLocationContext()->getParent() == 0) 00461 return; 00462 00463 if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) { 00464 // If the argument points to something that's not a symbolic region, it 00465 // can be: 00466 // - unknown (cannot reason about it) 00467 // - undefined (already reported by other checker) 00468 // - constant (null - should not be tracked, 00469 // other constant will generate a compiler warning) 00470 // - goto (should be reported by other checker) 00471 00472 // The call return value symbol should stay alive for as long as the 00473 // allocated value symbol, since our diagnostics depend on the value 00474 // returned by the call. Ex: Data should only be freed if noErr was 00475 // returned during allocation.) 00476 SymbolRef RetStatusSymbol = 00477 State->getSVal(CE, C.getLocationContext()).getAsSymbol(); 00478 C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol); 00479 00480 // Track the allocated value in the checker state. 00481 State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx, 00482 RetStatusSymbol)); 00483 assert(State); 00484 C.addTransition(State); 00485 } 00486 } 00487 00488 void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S, 00489 CheckerContext &C) const { 00490 const Expr *retExpr = S->getRetValue(); 00491 if (!retExpr) 00492 return; 00493 00494 // If inside inlined call, skip it. 00495 const LocationContext *LC = C.getLocationContext(); 00496 if (LC->getParent() != 0) 00497 return; 00498 00499 // Check if the value is escaping through the return. 00500 ProgramStateRef state = C.getState(); 00501 SymbolRef sym = state->getSVal(retExpr, LC).getAsLocSymbol(); 00502 if (!sym) 00503 return; 00504 state = state->remove<AllocatedData>(sym); 00505 00506 // Proceed from the new state. 00507 C.addTransition(state); 00508 } 00509 00510 // TODO: This logic is the same as in Malloc checker. 00511 const Stmt * 00512 MacOSKeychainAPIChecker::getAllocationSite(const ExplodedNode *N, 00513 SymbolRef Sym, 00514 CheckerContext &C) const { 00515 const LocationContext *LeakContext = N->getLocationContext(); 00516 // Walk the ExplodedGraph backwards and find the first node that referred to 00517 // the tracked symbol. 00518 const ExplodedNode *AllocNode = N; 00519 00520 while (N) { 00521 if (!N->getState()->get<AllocatedData>(Sym)) 00522 break; 00523 // Allocation node, is the last node in the current context in which the 00524 // symbol was tracked. 00525 if (N->getLocationContext() == LeakContext) 00526 AllocNode = N; 00527 N = N->pred_empty() ? NULL : *(N->pred_begin()); 00528 } 00529 00530 ProgramPoint P = AllocNode->getLocation(); 00531 if (!isa<StmtPoint>(P)) 00532 return 0; 00533 return cast<clang::PostStmt>(P).getStmt(); 00534 } 00535 00536 BugReport *MacOSKeychainAPIChecker:: 00537 generateAllocatedDataNotReleasedReport(const AllocationPair &AP, 00538 ExplodedNode *N, 00539 CheckerContext &C) const { 00540 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; 00541 initBugType(); 00542 SmallString<70> sbuf; 00543 llvm::raw_svector_ostream os(sbuf); 00544 os << "Allocated data is not released: missing a call to '" 00545 << FunctionsToTrack[FI.DeallocatorIdx].Name << "'."; 00546 00547 // Most bug reports are cached at the location where they occurred. 00548 // With leaks, we want to unique them by the location where they were 00549 // allocated, and only report a single path. 00550 PathDiagnosticLocation LocUsedForUniqueing; 00551 if (const Stmt *AllocStmt = getAllocationSite(N, AP.first, C)) 00552 LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt, 00553 C.getSourceManager(), N->getLocationContext()); 00554 00555 BugReport *Report = new BugReport(*BT, os.str(), N, LocUsedForUniqueing); 00556 Report->addVisitor(new SecKeychainBugVisitor(AP.first)); 00557 markInteresting(Report, AP); 00558 return Report; 00559 } 00560 00561 void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, 00562 CheckerContext &C) const { 00563 ProgramStateRef State = C.getState(); 00564 AllocatedSetTy ASet = State->get<AllocatedData>(); 00565 if (ASet.isEmpty()) 00566 return; 00567 00568 bool Changed = false; 00569 AllocationPairVec Errors; 00570 for (AllocatedSetTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) { 00571 if (SR.isLive(I->first)) 00572 continue; 00573 00574 Changed = true; 00575 State = State->remove<AllocatedData>(I->first); 00576 // If the allocated symbol is null or if the allocation call might have 00577 // returned an error, do not report. 00578 if (State->getSymVal(I->first) || 00579 definitelyReturnedError(I->second.Region, State, C.getSValBuilder())) 00580 continue; 00581 Errors.push_back(std::make_pair(I->first, &I->second)); 00582 } 00583 if (!Changed) { 00584 // Generate the new, cleaned up state. 00585 C.addTransition(State); 00586 return; 00587 } 00588 00589 static SimpleProgramPointTag Tag("MacOSKeychainAPIChecker : DeadSymbolsLeak"); 00590 ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag); 00591 00592 // Generate the error reports. 00593 for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); 00594 I != E; ++I) { 00595 C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N, C)); 00596 } 00597 00598 // Generate the new, cleaned up state. 00599 C.addTransition(State, N); 00600 } 00601 00602 // TODO: Remove this after we ensure that checkDeadSymbols are always called. 00603 void MacOSKeychainAPIChecker::checkEndPath(CheckerContext &C) const { 00604 ProgramStateRef state = C.getState(); 00605 00606 // If inside inlined call, skip it. 00607 if (C.getLocationContext()->getParent() != 0) 00608 return; 00609 00610 AllocatedSetTy AS = state->get<AllocatedData>(); 00611 if (AS.isEmpty()) 00612 return; 00613 00614 // Anything which has been allocated but not freed (nor escaped) will be 00615 // found here, so report it. 00616 bool Changed = false; 00617 AllocationPairVec Errors; 00618 for (AllocatedSetTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) { 00619 Changed = true; 00620 state = state->remove<AllocatedData>(I->first); 00621 // If the allocated symbol is null or if error code was returned at 00622 // allocation, do not report. 00623 if (state->getSymVal(I.getKey()) || 00624 definitelyReturnedError(I->second.Region, state, 00625 C.getSValBuilder())) { 00626 continue; 00627 } 00628 Errors.push_back(std::make_pair(I->first, &I->second)); 00629 } 00630 00631 // If no change, do not generate a new state. 00632 if (!Changed) { 00633 C.addTransition(state); 00634 return; 00635 } 00636 00637 static SimpleProgramPointTag Tag("MacOSKeychainAPIChecker : EndPathLeak"); 00638 ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag); 00639 00640 // Generate the error reports. 00641 for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); 00642 I != E; ++I) { 00643 C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N, C)); 00644 } 00645 00646 C.addTransition(state, N); 00647 } 00648 00649 00650 PathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( 00651 const ExplodedNode *N, 00652 const ExplodedNode *PrevN, 00653 BugReporterContext &BRC, 00654 BugReport &BR) { 00655 const AllocationState *AS = N->getState()->get<AllocatedData>(Sym); 00656 if (!AS) 00657 return 0; 00658 const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym); 00659 if (ASPrev) 00660 return 0; 00661 00662 // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the 00663 // allocation site. 00664 const CallExpr *CE = cast<CallExpr>(cast<StmtPoint>(N->getLocation()) 00665 .getStmt()); 00666 const FunctionDecl *funDecl = CE->getDirectCallee(); 00667 assert(funDecl && "We do not support indirect function calls as of now."); 00668 StringRef funName = funDecl->getName(); 00669 00670 // Get the expression of the corresponding argument. 00671 unsigned Idx = getTrackedFunctionIndex(funName, true); 00672 assert(Idx != InvalidIdx && "This should be a call to an allocator."); 00673 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param); 00674 PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(), 00675 N->getLocationContext()); 00676 return new PathDiagnosticEventPiece(Pos, "Data is allocated here."); 00677 } 00678 00679 void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) { 00680 mgr.registerChecker<MacOSKeychainAPIChecker>(); 00681 }