void SimpleStreamChecker::reportLeaks(ArrayRef<SymbolRef> LeakedStreams, CheckerContext &C, ExplodedNode *ErrNode) const { // Attach bug reports to the leak node. // TODO: Identify the leaked file descriptor. for (SymbolRef LeakedStream : LeakedStreams) { BugReport *R = new BugReport(*LeakBugType, "Opened file is never closed; potential resource leak", ErrNode); R->markInteresting(LeakedStream); C.emitReport(R); } }
void SimpleStreamChecker::reportLeaks(SymbolVector LeakedStreams, CheckerContext &C, ExplodedNode *ErrNode) const { // Attach bug reports to the leak node. // TODO: Identify the leaked file descriptor. for (SmallVectorImpl<SymbolRef>::iterator I = LeakedStreams.begin(), E = LeakedStreams.end(); I != E; ++I) { BugReport *R = new BugReport(*LeakBugType, "Opened file is never closed; potential resource leak", ErrNode); R->markInteresting(*I); C.emitReport(R); } }
void SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym, const CallEvent &Call, CheckerContext &C) const { // We reached a bug, stop exploring the path here by generating a sink. ExplodedNode *ErrNode = C.generateSink(); // If we've already reached this node on another path, return. if (!ErrNode) return; // Generate the report. BugReport *R = new BugReport(*DoubleCloseBugType, "Closing a previously closed file stream", ErrNode); R->addRange(Call.getSourceRange()); R->markInteresting(FileDescSym); C.emitReport(R); }
bool PHPZPPCheckerImpl::compareTypeWithSVal(const SVal val, const std::string &expectedType, CheckerContext &C) const { if (expectedType != getTypeForSVal(val).getAsString()) { BugReport *R = new BugReport( *InvalidTypeBugType, std::string("Arguments don't match the type expected by the format " "string (") + expectedType + std::string(" != ") + getTypeForSVal(val).getAsString() + std::string(")"), C.addTransition()); R->markInteresting(val); C.emitReport(R); return false; } return true; }
void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C, bool IsBind) const { // Generate an error node. ExplodedNode *N = C.generateSink(State); if (!N) return; // We know that 'location' cannot be non-null. This is what // we call an "explicit" null dereference. if (!BT_null) BT_null.reset(new BuiltinBug("Dereference of null pointer")); SmallString<100> buf; SmallVector<SourceRange, 2> Ranges; // Walk through lvalue casts to get the original expression // that syntactically caused the load. if (const Expr *expr = dyn_cast<Expr>(S)) S = expr->IgnoreParenLValueCasts(); const MemRegion *sourceR = 0; if (IsBind) { if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) { if (BO->isAssignmentOp()) S = BO->getRHS(); } else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) { assert(DS->isSingleDecl() && "We process decls one by one"); if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl())) if (const Expr *Init = VD->getAnyInitializer()) S = Init; } } switch (S->getStmtClass()) { case Stmt::ArraySubscriptExprClass: { llvm::raw_svector_ostream os(buf); os << "Array access"; const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); sourceR = AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), State.getPtr(), N->getLocationContext()); os << " results in a null pointer dereference"; break; } case Stmt::UnaryOperatorClass: { llvm::raw_svector_ostream os(buf); os << "Dereference of null pointer"; const UnaryOperator *U = cast<UnaryOperator>(S); sourceR = AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), State.getPtr(), N->getLocationContext(), true); break; } case Stmt::MemberExprClass: { const MemberExpr *M = cast<MemberExpr>(S); if (M->isArrow()) { llvm::raw_svector_ostream os(buf); os << "Access to field '" << M->getMemberNameInfo() << "' results in a dereference of a null pointer"; sourceR = AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), State.getPtr(), N->getLocationContext(), true); } break; } case Stmt::ObjCIvarRefExprClass: { const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(IV->getBase()->IgnoreParenCasts())) { if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { llvm::raw_svector_ostream os(buf); os << "Instance variable access (via '" << VD->getName() << "') results in a null pointer dereference"; } } Ranges.push_back(IV->getSourceRange()); break; } default: break; } BugReport *report = new BugReport(*BT_null, buf.empty() ? BT_null->getDescription() : buf.str(), N); bugreporter::addTrackNullOrUndefValueVisitor(N, bugreporter::GetDerefExpr(N), report); for (SmallVectorImpl<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) report->addRange(*I); if (sourceR) { report->markInteresting(sourceR); report->markInteresting(State->getRawSVal(loc::MemRegionVal(sourceR))); } C.EmitReport(report); }
void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { unsigned idx = InvalidIdx; ProgramStateRef State = C.getState(); const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD || FD->getKind() != Decl::Function) return; StringRef funName = C.getCalleeName(FD); if (funName.empty()) return; // If it is a call to an allocator function, it could be a double allocation. idx = getTrackedFunctionIndex(funName, true); if (idx != InvalidIdx) { const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) if (const AllocationState *AS = State->get<AllocatedData>(V)) { if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) { // Remove the value from the state. The new symbol will be added for // tracking when the second allocator is processed in checkPostStmt(). State = State->remove<AllocatedData>(V); ExplodedNode *N = C.addTransition(State); if (!N) return; initBugType(); SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; os << "Allocated data should be released before another call to " << "the allocator: missing a call to '" << FunctionsToTrack[DIdx].Name << "'."; BugReport *Report = new BugReport(*BT, os.str(), N); Report->addVisitor(new SecKeychainBugVisitor(V)); Report->addRange(ArgExpr->getSourceRange()); Report->markInteresting(AS->Region); C.emitReport(Report); } } return; } // Is it a call to one of deallocator functions? idx = getTrackedFunctionIndex(funName, false); if (idx == InvalidIdx) return; // Check the argument to the deallocator. const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext()); // Undef is reported by another checker. if (ArgSVal.isUndef()) return; SymbolRef ArgSM = ArgSVal.getAsLocSymbol(); // If the argument is coming from the heap, globals, or unknown, do not // report it. bool RegionArgIsBad = false; if (!ArgSM) { if (!isBadDeallocationArgument(ArgSVal.getAsRegion())) return; RegionArgIsBad = true; } // Is the argument to the call being tracked? const AllocationState *AS = State->get<AllocatedData>(ArgSM); if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) { return; } // If trying to free data which has not been allocated yet, report as a bug. // TODO: We might want a more precise diagnostic for double free // (that would involve tracking all the freed symbols in the checker state). if (!AS || RegionArgIsBad) { // It is possible that this is a false positive - the argument might // have entered as an enclosing function parameter. if (isEnclosingFunctionParam(ArgExpr)) return; ExplodedNode *N = C.addTransition(State); if (!N) return; initBugType(); BugReport *Report = new BugReport(*BT, "Trying to free data which has not been allocated.", N); Report->addRange(ArgExpr->getSourceRange()); if (AS) Report->markInteresting(AS->Region); C.emitReport(Report); return; } // Process functions which might deallocate. if (FunctionsToTrack[idx].Kind == PossibleAPI) { if (funName == "CFStringCreateWithBytesNoCopy") { const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); // NULL ~ default deallocator, so warn. if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), Expr::NPC_ValueDependentIsNotNull)) { const AllocationPair AP = std::make_pair(ArgSM, AS); generateDeallocatorMismatchReport(AP, ArgExpr, C); return; } // One of the default allocators, so warn. if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { StringRef DeallocatorName = DE->getFoundDecl()->getName(); if (DeallocatorName == "kCFAllocatorDefault" || DeallocatorName == "kCFAllocatorSystemDefault" || DeallocatorName == "kCFAllocatorMalloc") { const AllocationPair AP = std::make_pair(ArgSM, AS); generateDeallocatorMismatchReport(AP, ArgExpr, C); return; } // If kCFAllocatorNull, which does not deallocate, we still have to // find the deallocator. if (DE->getFoundDecl()->getName() == "kCFAllocatorNull") return; } // In all other cases, assume the user supplied a correct deallocator // that will free memory so stop tracking. State = State->remove<AllocatedData>(ArgSM); C.addTransition(State); return; } llvm_unreachable("We know of no other possible APIs."); } // The call is deallocating a value we previously allocated, so remove it // from the next state. State = State->remove<AllocatedData>(ArgSM); // Check if the proper deallocator is used. unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { const AllocationPair AP = std::make_pair(ArgSM, AS); generateDeallocatorMismatchReport(AP, ArgExpr, C); return; } // If the buffer can be null and the return status can be an error, // report a bad call to free. if (State->assume(ArgSVal.castAs<DefinedSVal>(), false) && !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { ExplodedNode *N = C.addTransition(State); if (!N) return; initBugType(); BugReport *Report = new BugReport(*BT, "Only call free if a valid (non-NULL) buffer was returned.", N); Report->addVisitor(new SecKeychainBugVisitor(ArgSM)); Report->addRange(ArgExpr->getSourceRange()); Report->markInteresting(AS->Region); C.emitReport(Report); return; } C.addTransition(State); }
bool bugreporter::trackNullOrUndefValue(const ExplodedNode *ErrorNode, const Stmt *S, BugReport &report, bool IsArg) { if (!S || !ErrorNode) return false; if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S)) S = OVE->getSourceExpr(); const ExplodedNode *N = ErrorNode; const Expr *Inner = 0; if (const Expr *Ex = dyn_cast<Expr>(S)) { Ex = Ex->IgnoreParenCasts(); if (ExplodedGraph::isInterestingLValueExpr(Ex) || CallEvent::isCallStmt(Ex)) Inner = Ex; } if (IsArg) { assert(N->getLocation().getAs<CallEnter>() && "Tracking arg but not at call"); } else { // Walk through nodes until we get one that matches the statement exactly. // Alternately, if we hit a known lvalue for the statement, we know we've // gone too far (though we can likely track the lvalue better anyway). do { const ProgramPoint &pp = N->getLocation(); if (Optional<PostStmt> ps = pp.getAs<PostStmt>()) { if (ps->getStmt() == S || ps->getStmt() == Inner) break; } else if (Optional<CallExitEnd> CEE = pp.getAs<CallExitEnd>()) { if (CEE->getCalleeContext()->getCallSite() == S || CEE->getCalleeContext()->getCallSite() == Inner) break; } N = N->getFirstPred(); } while (N); if (!N) return false; } ProgramStateRef state = N->getState(); // See if the expression we're interested refers to a variable. // If so, we can track both its contents and constraints on its value. if (Inner && ExplodedGraph::isInterestingLValueExpr(Inner)) { const MemRegion *R = 0; // First check if this is a DeclRefExpr for a C++ reference type. // For those, we want the location of the reference. if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Inner)) { if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { if (VD->getType()->isReferenceType()) { ProgramStateManager &StateMgr = state->getStateManager(); MemRegionManager &MRMgr = StateMgr.getRegionManager(); R = MRMgr.getVarRegion(VD, N->getLocationContext()); } } } // For all other cases, find the location by scouring the ExplodedGraph. if (!R) { // Find the ExplodedNode where the lvalue (the value of 'Ex') // was computed. We need this for getting the location value. const ExplodedNode *LVNode = N; while (LVNode) { if (Optional<PostStmt> P = LVNode->getLocation().getAs<PostStmt>()) { if (P->getStmt() == Inner) break; } LVNode = LVNode->getFirstPred(); } assert(LVNode && "Unable to find the lvalue node."); ProgramStateRef LVState = LVNode->getState(); R = LVState->getSVal(Inner, LVNode->getLocationContext()).getAsRegion(); } if (R) { // Mark both the variable region and its contents as interesting. SVal V = state->getRawSVal(loc::MemRegionVal(R)); // If the value matches the default for the variable region, that // might mean that it's been cleared out of the state. Fall back to // the full argument expression (with casts and such intact). if (IsArg) { bool UseArgValue = V.isUnknownOrUndef() || V.isZeroConstant(); if (!UseArgValue) { const SymbolRegionValue *SRV = dyn_cast_or_null<SymbolRegionValue>(V.getAsLocSymbol()); if (SRV) UseArgValue = (SRV->getRegion() == R); } if (UseArgValue) V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); } report.markInteresting(R); report.markInteresting(V); report.addVisitor(new UndefOrNullArgVisitor(R)); if (isa<SymbolicRegion>(R)) { TrackConstraintBRVisitor *VI = new TrackConstraintBRVisitor(loc::MemRegionVal(R), false); report.addVisitor(VI); } // If the contents are symbolic, find out when they became null. if (V.getAsLocSymbol()) { BugReporterVisitor *ConstraintTracker = new TrackConstraintBRVisitor(V.castAs<DefinedSVal>(), false); report.addVisitor(ConstraintTracker); // Add visitor, which will suppress inline defensive checks. if (ErrorNode->getState()->isNull(V).isConstrainedTrue()) { BugReporterVisitor *IDCSuppressor = new SuppressInlineDefensiveChecksVisitor(V.castAs<DefinedSVal>(), ErrorNode); report.addVisitor(IDCSuppressor); } } if (Optional<KnownSVal> KV = V.getAs<KnownSVal>()) report.addVisitor(new FindLastStoreBRVisitor(*KV, R)); return true; } } // If the expression is not an "lvalue expression", we can still // track the constraints on its contents. SVal V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); // If the value came from an inlined function call, we should at least make // sure that function isn't pruned in our output. if (const Expr *E = dyn_cast<Expr>(S)) S = E->IgnoreParenCasts(); ReturnVisitor::addVisitorIfNecessary(N, S, report); // Uncomment this to find cases where we aren't properly getting the // base value that was dereferenced. // assert(!V.isUnknownOrUndef()); // Is it a symbolic value? if (Optional<loc::MemRegionVal> L = V.getAs<loc::MemRegionVal>()) { // At this point we are dealing with the region's LValue. // However, if the rvalue is a symbolic region, we should track it as well. SVal RVal = state->getSVal(L->getRegion()); const MemRegion *RegionRVal = RVal.getAsRegion(); report.addVisitor(new UndefOrNullArgVisitor(L->getRegion())); if (RegionRVal && isa<SymbolicRegion>(RegionRVal)) { report.markInteresting(RegionRVal); report.addVisitor(new TrackConstraintBRVisitor( loc::MemRegionVal(RegionRVal), false)); } } return true; }
bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, const Stmt *S, BugReport &report, bool IsArg) { if (!S || !N) return false; if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S)) S = OVE->getSourceExpr(); if (IsArg) { assert(isa<CallEnter>(N->getLocation()) && "Tracking arg but not at call"); } else { // Walk through nodes until we get one that matches the statement exactly. do { const ProgramPoint &pp = N->getLocation(); if (const PostStmt *ps = dyn_cast<PostStmt>(&pp)) { if (ps->getStmt() == S) break; } else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&pp)) { if (CEE->getCalleeContext()->getCallSite() == S) break; } N = N->getFirstPred(); } while (N); if (!N) return false; } ProgramStateRef state = N->getState(); // See if the expression we're interested refers to a variable. // If so, we can track both its contents and constraints on its value. if (const Expr *Ex = dyn_cast<Expr>(S)) { // Strip off parens and casts. Note that this will never have issues with // C++ user-defined implicit conversions, because those have a constructor // or function call inside. Ex = Ex->IgnoreParenCasts(); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { // FIXME: Right now we only track VarDecls because it's non-trivial to // get a MemRegion for any other DeclRefExprs. <rdar://problem/12114812> if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { ProgramStateManager &StateMgr = state->getStateManager(); MemRegionManager &MRMgr = StateMgr.getRegionManager(); const VarRegion *R = MRMgr.getVarRegion(VD, N->getLocationContext()); // Mark both the variable region and its contents as interesting. SVal V = state->getRawSVal(loc::MemRegionVal(R)); // If the value matches the default for the variable region, that // might mean that it's been cleared out of the state. Fall back to // the full argument expression (with casts and such intact). if (IsArg) { bool UseArgValue = V.isUnknownOrUndef() || V.isZeroConstant(); if (!UseArgValue) { const SymbolRegionValue *SRV = dyn_cast_or_null<SymbolRegionValue>(V.getAsLocSymbol()); if (SRV) UseArgValue = (SRV->getRegion() == R); } if (UseArgValue) V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); } report.markInteresting(R); report.markInteresting(V); report.addVisitor(new UndefOrNullArgVisitor(R)); // If the contents are symbolic, find out when they became null. if (V.getAsLocSymbol()) { BugReporterVisitor *ConstraintTracker = new TrackConstraintBRVisitor(cast<DefinedSVal>(V), false); report.addVisitor(ConstraintTracker); } report.addVisitor(new FindLastStoreBRVisitor(V, R)); return true; } } } // If the expression does NOT refer to a variable, we can still track // constraints on its contents. SVal V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); // Uncomment this to find cases where we aren't properly getting the // base value that was dereferenced. // assert(!V.isUnknownOrUndef()); // Is it a symbolic value? if (loc::MemRegionVal *L = dyn_cast<loc::MemRegionVal>(&V)) { // At this point we are dealing with the region's LValue. // However, if the rvalue is a symbolic region, we should track it as well. SVal RVal = state->getSVal(L->getRegion()); const MemRegion *RegionRVal = RVal.getAsRegion(); report.addVisitor(new UndefOrNullArgVisitor(L->getRegion())); if (RegionRVal && isa<SymbolicRegion>(RegionRVal)) { report.markInteresting(RegionRVal); report.addVisitor(new TrackConstraintBRVisitor( loc::MemRegionVal(RegionRVal), false)); } } else { // Otherwise, if the value came from an inlined function call, // we should at least make sure that function isn't pruned in our output. if (const Expr *E = dyn_cast<Expr>(S)) S = E->IgnoreParenCasts(); ReturnVisitor::addVisitorIfNecessary(N, S, report); } return true; }
void PHPZPPCheckerImpl::checkPreCall(const CallEvent &Call, CheckerContext &C) const { initIdentifierInfo(C.getASTContext()); unsigned offset; if (!Call.isGlobalCFunction()) return; if (Call.getCalleeIdentifier() == IIzpp) { offset = 1; } else if (Call.getCalleeIdentifier() == IIzpp_ex) { offset = 2; } else if (Call.getCalleeIdentifier() == IIzpmp) { offset = 2; } else if (Call.getCalleeIdentifier() == IIzpmp_ex) { offset = 3; } else { return; } if (TSRMBuild) { ++offset; } if (Call.getNumArgs() <= offset) // Something is really weird - this should be caught by the compiler return; const StringLiteral *format_spec_sl = getCStringLiteral(Call.getArgSVal(offset)); if (!format_spec_sl) { // TODO need a good way to report this, even though this is no error std::cout << "Couldn't get format string looked at offset " << offset << std::endl; Call.dump(); return; } const StringRef format_spec = format_spec_sl->getBytes(); // Call.dump(); for (StringRef::const_iterator modifier = format_spec.begin(), last_mod = format_spec.end(); modifier != last_mod; ++modifier) { //std::cout << " I am checking for " << *modifier << std::endl; const PHPTypeRange range = map.equal_range(*modifier); if (range.first == range.second) { BugReport *R = new BugReport( *InvalidModifierBugType, std::string("Unknown modifier '") + *modifier + "'", C.addTransition()); C.emitReport(R); return; } for (PHPTypeMap::const_iterator type = range.first; type != range.second; ++type) { if (!type->second) { // Current modifier doesn't need an argument, these are special things // like |, ! or / continue; } ++offset; //std::cout << " I need a " << *type->second << " (" << offset << ")" << std::endl; if (Call.getNumArgs() <= offset) { BugReport *R = new BugReport(*WrongArgumentNumberBugType, "Too few arguments for format specified", C.addTransition()); C.emitReport(R); //std::cout << "!!!!I am missing args! " << Call.getNumArgs() << "<=" << offset << std::endl; return; } SVal val = Call.getArgSVal(offset); if (!compareTypeWithSVal(val, *type->second, C)) { // TODO: Move error reporting here? // Even if there is a type mismatch we can continue, most of the time // this should be a simple mistake by the user, in rare cases the user // missed an argument and will get many subsequent errors } } } if (Call.getNumArgs() > 1 + offset) { BugReport *R = new BugReport(*WrongArgumentNumberBugType, "Too many arguments for format specified", C.addTransition()); R->markInteresting(Call.getArgSVal(offset)); C.emitReport(R); } }
void bugreporter::trackNullOrUndefValue(const ExplodedNode *N, const Stmt *S, BugReport &report) { if (!S || !N) return; ProgramStateManager &StateMgr = N->getState()->getStateManager(); // Walk through nodes until we get one that matches the statement exactly. while (N) { const ProgramPoint &pp = N->getLocation(); if (const PostStmt *ps = dyn_cast<PostStmt>(&pp)) { if (ps->getStmt() == S) break; } else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&pp)) { if (CEE->getCalleeContext()->getCallSite() == S) break; } N = N->getFirstPred(); } if (!N) return; ProgramStateRef state = N->getState(); // See if the expression we're interested refers to a variable. // If so, we can track both its contents and constraints on its value. if (const Expr *Ex = dyn_cast<Expr>(S)) { // Strip off parens and casts. Note that this will never have issues with // C++ user-defined implicit conversions, because those have a constructor // or function call inside. Ex = Ex->IgnoreParenCasts(); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { // FIXME: Right now we only track VarDecls because it's non-trivial to // get a MemRegion for any other DeclRefExprs. <rdar://problem/12114812> if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { const VarRegion *R = StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); // Mark both the variable region and its contents as interesting. SVal V = state->getRawSVal(loc::MemRegionVal(R)); report.markInteresting(R); report.markInteresting(V); // If the contents are symbolic, find out when they became null. if (V.getAsLocSymbol()) { BugReporterVisitor *ConstraintTracker = new TrackConstraintBRVisitor(cast<loc::MemRegionVal>(V), false); report.addVisitor(ConstraintTracker); } report.addVisitor(new FindLastStoreBRVisitor(V, R)); return; } } } // If the expression does NOT refer to a variable, we can still track // constraints on its contents. SVal V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); // Uncomment this to find cases where we aren't properly getting the // base value that was dereferenced. // assert(!V.isUnknownOrUndef()); // Is it a symbolic value? if (loc::MemRegionVal *L = dyn_cast<loc::MemRegionVal>(&V)) { const MemRegion *Base = L->getRegion()->getBaseRegion(); if (isa<SymbolicRegion>(Base)) { report.markInteresting(Base); report.addVisitor(new TrackConstraintBRVisitor(loc::MemRegionVal(Base), false)); } } else { // Otherwise, if the value came from an inlined function call, // we should at least make sure that function isn't pruned in our output. ReturnVisitor::addVisitorIfNecessary(N, S, report); } }
std::shared_ptr<PathDiagnosticPiece> RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, const ExplodedNode *EndN, BugReport &BR) { // Tell the BugReporterContext to report cases when the tracked symbol is // assigned to different variables, etc. BR.markInteresting(Sym); // We are reporting a leak. Walk up the graph to get to the first node where // the symbol appeared, and also get the first VarDecl that tracked object // is stored to. AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym); const MemRegion* FirstBinding = AllocI.R; BR.markInteresting(AllocI.InterestingMethodContext); SourceManager& SM = BRC.getSourceManager(); // Compute an actual location for the leak. Sometimes a leak doesn't // occur at an actual statement (e.g., transition between blocks; end // of function) so we need to walk the graph and compute a real location. const ExplodedNode *LeakN = EndN; PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM); std::string sbuf; llvm::raw_string_ostream os(sbuf); os << "Object leaked: "; Optional<std::string> RegionDescription = describeRegion(FirstBinding); if (RegionDescription) { os << "object allocated and stored into '" << *RegionDescription << '\''; } else { os << "allocated object of type '" << getPrettyTypeName(Sym->getType()) << "'"; } // Get the retain count. const RefVal* RV = getRefBinding(EndN->getState(), Sym); assert(RV); if (RV->getKind() == RefVal::ErrorLeakReturned) { // FIXME: Per comments in rdar://6320065, "create" only applies to CF // objects. Only "copy", "alloc", "retain" and "new" transfer ownership // to the caller for NS objects. const Decl *D = &EndN->getCodeDecl(); os << (isa<ObjCMethodDecl>(D) ? " is returned from a method " : " is returned from a function "); if (D->hasAttr<CFReturnsNotRetainedAttr>()) { os << "that is annotated as CF_RETURNS_NOT_RETAINED"; } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) { os << "that is annotated as NS_RETURNS_NOT_RETAINED"; } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) { os << "that is annotated as OS_RETURNS_NOT_RETAINED"; } else { if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) { os << "managed by Automatic Reference Counting"; } else { os << "whose name ('" << MD->getSelector().getAsString() << "') does not start with " "'copy', 'mutableCopy', 'alloc' or 'new'." " This violates the naming convention rules" " given in the Memory Management Guide for Cocoa"; } } else { const FunctionDecl *FD = cast<FunctionDecl>(D); os << "whose name ('" << *FD << "') does not contain 'Copy' or 'Create'. This violates the naming" " convention rules given in the Memory Management Guide for Core" " Foundation"; } } } else { os << " is not referenced later in this execution path and has a retain " "count of +" << RV->getCount(); } return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); }
std::shared_ptr<PathDiagnosticPiece> RefCountReportVisitor::getEndPath(BugReporterContext &BRC, const ExplodedNode *EndN, BugReport &BR) { BR.markInteresting(Sym); return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR); }