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; }
std::shared_ptr<PathDiagnosticPiece> RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { const SourceManager &SM = BRC.getSourceManager(); CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager(); if (auto CE = N->getLocationAs<CallExitBegin>()) if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr)) return PD; // FIXME: We will eventually need to handle non-statement-based events // (__attribute__((cleanup))). if (!N->getLocation().getAs<StmtPoint>()) return nullptr; // Check if the type state has changed. const ExplodedNode *PrevNode = N->getFirstPred(); ProgramStateRef PrevSt = PrevNode->getState(); ProgramStateRef CurrSt = N->getState(); const LocationContext *LCtx = N->getLocationContext(); const RefVal* CurrT = getRefBinding(CurrSt, Sym); if (!CurrT) return nullptr; const RefVal &CurrV = *CurrT; const RefVal *PrevT = getRefBinding(PrevSt, Sym); // Create a string buffer to constain all the useful things we want // to tell the user. std::string sbuf; llvm::raw_string_ostream os(sbuf); // This is the allocation site since the previous node had no bindings // for this symbol. if (!PrevT) { const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); if (isa<ObjCIvarRefExpr>(S) && isSynthesizedAccessor(LCtx->getStackFrame())) { S = LCtx->getStackFrame()->getCallSite(); } if (isa<ObjCArrayLiteral>(S)) { os << "NSArray literal is an object with a +0 retain count"; } else if (isa<ObjCDictionaryLiteral>(S)) { os << "NSDictionary literal is an object with a +0 retain count"; } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) { if (isNumericLiteralExpression(BL->getSubExpr())) os << "NSNumber literal is an object with a +0 retain count"; else { const ObjCInterfaceDecl *BoxClass = nullptr; if (const ObjCMethodDecl *Method = BL->getBoxingMethod()) BoxClass = Method->getClassInterface(); // We should always be able to find the boxing class interface, // but consider this future-proofing. if (BoxClass) { os << *BoxClass << " b"; } else { os << "B"; } os << "oxed expression produces an object with a +0 retain count"; } } else if (isa<ObjCIvarRefExpr>(S)) { os << "Object loaded from instance variable"; } else { generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os); } PathDiagnosticLocation Pos(S, SM, N->getLocationContext()); return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); } // Gather up the effects that were performed on the object at this // program point bool DeallocSent = false; if (N->getLocation().getTag() && N->getLocation().getTag()->getTagDescription().contains( RetainCountChecker::DeallocTagDescription)) { // We only have summaries attached to nodes after evaluating CallExpr and // ObjCMessageExprs. const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { // Iterate through the parameter expressions and see if the symbol // was ever passed as an argument. unsigned i = 0; for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) { // Retrieve the value of the argument. Is it the symbol // we are interested in? if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym) continue; // We have an argument. Get the effect! DeallocSent = true; } } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) { if (const Expr *receiver = ME->getInstanceReceiver()) { if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx) .getAsLocSymbol() == Sym) { // The symbol we are tracking is the receiver. DeallocSent = true; } } } } if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent)) return nullptr; if (os.str().empty()) return nullptr; // We have nothing to say! const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); PathDiagnosticLocation Pos(S, BRC.getSourceManager(), N->getLocationContext()); auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); // Add the range by scanning the children of the statement for any bindings // to Sym. for (const Stmt *Child : S->children()) if (const Expr *Exp = dyn_cast_or_null<Expr>(Child)) if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) { P->addRange(Exp->getSourceRange()); break; } return std::move(P); }
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); } }
static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, const LocationContext *LCtx, const RefVal &CurrV, SymbolRef &Sym, const Stmt *S, llvm::raw_string_ostream &os) { CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager(); if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { // Get the name of the callee (if it is available) // from the tracked SVal. SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx); const FunctionDecl *FD = X.getAsFunctionDecl(); // If failed, try to get it from AST. if (!FD) FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl()); if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) { os << "Call to method '" << MD->getQualifiedNameAsString() << '\''; } else if (FD) { os << "Call to function '" << FD->getQualifiedNameAsString() << '\''; } else { os << "function call"; } } else if (isa<CXXNewExpr>(S)) { os << "Operator 'new'"; } else { assert(isa<ObjCMessageExpr>(S)); CallEventRef<ObjCMethodCall> Call = Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx); switch (Call->getMessageKind()) { case OCM_Message: os << "Method"; break; case OCM_PropertyAccess: os << "Property"; break; case OCM_Subscript: os << "Subscript"; break; } } Optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx); auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE); // If index is not found, we assume that the symbol was returned. if (!Idx) { os << " returns "; } else { os << " writes "; } if (CurrV.getObjKind() == ObjKind::CF) { os << "a Core Foundation object of type '" << Sym->getType().getAsString() << "' with a "; } else if (CurrV.getObjKind() == ObjKind::OS) { os << "an OSObject of type '" << getPrettyTypeName(Sym->getType()) << "' with a "; } else if (CurrV.getObjKind() == ObjKind::Generalized) { os << "an object of type '" << Sym->getType().getAsString() << "' with a "; } else { assert(CurrV.getObjKind() == ObjKind::ObjC); QualType T = Sym->getType(); if (!isa<ObjCObjectPointerType>(T)) { os << "an Objective-C object with a "; } else { const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T); os << "an instance of " << PT->getPointeeType().getAsString() << " with a "; } } if (CurrV.isOwned()) { os << "+1 retain count"; } else { assert(CurrV.isNotOwned()); os << "+0 retain count"; } if (Idx) { os << " into an out parameter '"; const ParmVarDecl *PVD = (*CE)->parameters()[*Idx]; PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(), /*Qualified=*/false); os << "'"; QualType RT = (*CE)->getResultType(); if (!RT.isNull() && !RT->isVoidType()) { SVal RV = (*CE)->getReturnValue(); if (CurrSt->isNull(RV).isConstrainedTrue()) { os << " (assuming the call returns zero)"; } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) { os << " (assuming the call returns non-zero)"; } } } }