void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { if (!DS->isSingleDecl()) return; const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); if (!VD) return; const VariableArrayType *VLA = C.getASTContext().getAsVariableArrayType(VD->getType()); if (!VLA) return; // FIXME: Handle multi-dimensional VLAs. const Expr* SE = VLA->getSizeExpr(); const GRState *state = C.getState(); SVal sizeV = state->getSVal(SE); if (sizeV.isUndef()) { // Generate an error node. ExplodedNode *N = C.GenerateSink(); if (!N) return; if (!BT_undef) BT_undef = new BuiltinBug("Declared variable-length array (VLA) uses a " "garbage value as its size"); EnhancedBugReport *report = new EnhancedBugReport(*BT_undef, BT_undef->getName(), N); report->addRange(SE->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); C.EmitReport(report); return; } // Check if the size is zero. DefinedOrUnknownSVal sizeD = cast<DefinedOrUnknownSVal>(sizeV); const GRState *stateNotZero, *stateZero; llvm::tie(stateNotZero, stateZero) = state->Assume(sizeD); if (stateZero && !stateNotZero) { ExplodedNode* N = C.GenerateSink(stateZero); if (!BT_zero) BT_zero = new BuiltinBug("Declared variable-length array (VLA) has zero " "size"); EnhancedBugReport *report = new EnhancedBugReport(*BT_zero, BT_zero->getName(), N); report->addRange(SE->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); C.EmitReport(report); return; } // From this point on, assume that the size is not zero. C.addTransition(stateNotZero); }
void ObjCAtSyncChecker::PreVisitObjCAtSynchronizedStmt(CheckerContext &C, const ObjCAtSynchronizedStmt *S) { const Expr *Ex = S->getSynchExpr(); const GRState *state = C.getState(); SVal V = state->getSVal(Ex); // Uninitialized value used for the mutex? if (isa<UndefinedVal>(V)) { if (ExplodedNode *N = C.generateSink()) { if (!BT_undef) BT_undef = new BuiltinBug("Uninitialized value used as mutex " "for @synchronized"); EnhancedBugReport *report = new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); C.EmitReport(report); } return; } if (V.isUnknown()) return; // Check for null mutexes. const GRState *notNullState, *nullState; llvm::tie(notNullState, nullState) = state->assume(cast<DefinedSVal>(V)); if (nullState) { if (!notNullState) { // Generate an error node. This isn't a sink since // a null mutex just means no synchronization occurs. if (ExplodedNode *N = C.generateNode(nullState)) { if (!BT_null) BT_null = new BuiltinBug("Nil value used as mutex for @synchronized() " "(no synchronization will occur)"); EnhancedBugReport *report = new EnhancedBugReport(*BT_null, BT_null->getDescription(), N); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); C.EmitReport(report); return; } } // Don't add a transition for 'nullState'. If the value is // under-constrained to be null or non-null, assume it is non-null // afterwards. } if (notNullState) C.addTransition(notNullState); }
void UndefResultChecker::checkPostStmt(const BinaryOperator *B, CheckerContext &C) const { const GRState *state = C.getState(); if (state->getSVal(B).isUndef()) { // Generate an error node. ExplodedNode *N = C.generateSink(); if (!N) return; if (!BT) BT.reset(new BuiltinBug("Result of operation is garbage or undefined")); llvm::SmallString<256> sbuf; llvm::raw_svector_ostream OS(sbuf); const Expr *Ex = NULL; bool isLeft = true; if (state->getSVal(B->getLHS()).isUndef()) { Ex = B->getLHS()->IgnoreParenCasts(); isLeft = true; } else if (state->getSVal(B->getRHS()).isUndef()) { Ex = B->getRHS()->IgnoreParenCasts(); isLeft = false; } if (Ex) { OS << "The " << (isLeft ? "left" : "right") << " operand of '" << BinaryOperator::getOpcodeStr(B->getOpcode()) << "' is a garbage value"; } else { // Neither operand was undefined, but the result is undefined. OS << "The result of the '" << BinaryOperator::getOpcodeStr(B->getOpcode()) << "' expression is undefined"; } EnhancedBugReport *report = new EnhancedBugReport(*BT, OS.str(), N); if (Ex) { report->addRange(Ex->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); } else report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, B); C.EmitReport(report); } }
void ReturnUndefChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS) { const Expr *RetE = RS->getRetValue(); if (!RetE) return; if (!C.getState()->getSVal(RetE).isUndef()) return; ExplodedNode *N = C.GenerateSink(); if (!N) return; if (!BT) BT = new BuiltinBug("Garbage return value", "Undefined or garbage value returned to caller"); EnhancedBugReport *report = new EnhancedBugReport(*BT, BT->getDescription(), N); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, RetE); C.EmitReport(report); }
void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, BranchNodeBuilder &Builder, ExprEngine &Eng) const { const GRState *state = Builder.getState(); SVal X = state->getSVal(Condition); if (X.isUndef()) { ExplodedNode *N = Builder.generateNode(state, true); if (N) { N->markAsSink(); if (!BT) BT.reset( new BuiltinBug("Branch condition evaluates to a garbage value")); // What's going on here: we want to highlight the subexpression of the // condition that is the most likely source of the "uninitialized // branch condition." We do a recursive walk of the condition's // subexpressions and roughly look for the most nested subexpression // that binds to Undefined. We then highlight that expression's range. BlockEdge B = cast<BlockEdge>(N->getLocation()); const Expr* Ex = cast<Expr>(B.getSrc()->getTerminatorCondition()); assert (Ex && "Block must have a terminator."); // Get the predecessor node and check if is a PostStmt with the Stmt // being the terminator condition. We want to inspect the state // of that node instead because it will contain main information about // the subexpressions. assert (!N->pred_empty()); // Note: any predecessor will do. They should have identical state, // since all the BlockEdge did was act as an error sink since the value // had to already be undefined. ExplodedNode *PrevN = *N->pred_begin(); ProgramPoint P = PrevN->getLocation(); const GRState* St = N->getState(); if (PostStmt* PS = dyn_cast<PostStmt>(&P)) if (PS->getStmt() == Ex) St = PrevN->getState(); FindUndefExpr FindIt(Eng.getStateManager(), St); Ex = FindIt.FindExpr(Ex); // Emit the bug report. EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getDescription(),N); R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); R->addRange(Ex->getSourceRange()); Eng.getBugReporter().EmitReport(R); } Builder.markInfeasible(true); Builder.markInfeasible(false); } }
void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, CheckerContext &C) const { if (!val.isUndef()) return; ExplodedNode *N = C.generateSink(); if (!N) return; const char *str = "Assigned value is garbage or undefined"; if (!BT) BT.reset(new BuiltinBug(str)); // Generate a report for this bug. const Expr *ex = 0; const Stmt *StoreE = C.getStmt(); while (StoreE) { if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) { if (B->isCompoundAssignmentOp()) { const GRState *state = C.getState(); if (state->getSVal(B->getLHS()).isUndef()) { str = "The left expression of the compound assignment is an " "uninitialized value. The computed value will also be garbage"; ex = B->getLHS(); break; } } ex = B->getRHS(); break; } if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) { const VarDecl* VD = dyn_cast<VarDecl>(DS->getSingleDecl()); ex = VD->getInit(); } break; } EnhancedBugReport *R = new EnhancedBugReport(*BT, str, N); if (ex) { R->addRange(ex->getSourceRange()); R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, ex); } C.EmitReport(R); }
void UndefinedArraySubscriptChecker::PreVisitArraySubscriptExpr(CheckerContext &C, const ArraySubscriptExpr *A) { if (C.getState()->getSVal(A->getIdx()).isUndef()) { if (ExplodedNode *N = C.GenerateSink()) { if (!BT) BT = new BuiltinBug("Array subscript is undefined"); // Generate a report for this bug. EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); R->addRange(A->getIdx()->getSourceRange()); R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, A->getIdx()); C.EmitReport(R); } } }
void DivZeroChecker::PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B) { BinaryOperator::Opcode Op = B->getOpcode(); if (Op != BO_Div && Op != BO_Rem && Op != BO_DivAssign && Op != BO_RemAssign) return; if (!B->getRHS()->getType()->isIntegerType() || !B->getRHS()->getType()->isScalarType()) return; SVal Denom = C.getState()->getSVal(B->getRHS()); const DefinedSVal *DV = dyn_cast<DefinedSVal>(&Denom); // Divide-by-undefined handled in the generic checking for uses of // undefined values. if (!DV) return; // Check for divide by zero. ConstraintManager &CM = C.getConstraintManager(); const GRState *stateNotZero, *stateZero; llvm::tie(stateNotZero, stateZero) = CM.assumeDual(C.getState(), *DV); if (stateZero && !stateNotZero) { if (ExplodedNode *N = C.generateSink(stateZero)) { if (!BT) BT = new BuiltinBug("Division by zero"); EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getDescription(), N); R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, bugreporter::GetDenomExpr(N)); C.EmitReport(R); } return; } // If we get here, then the denom should not be zero. We abandon the implicit // zero denom case for now. C.addTransition(stateNotZero); }
// FIXME: Eventually this should be rolled into the MallocChecker, but this // check is more basic and is valuable for widespread use. void UnixAPIChecker::CheckMallocZero(CheckerContext &C, const CallExpr *CE) const { // Sanity check that malloc takes one argument. if (CE->getNumArgs() != 1) return; // Check if the allocation size is 0. const GRState *state = C.getState(); SVal argVal = state->getSVal(CE->getArg(0)); if (argVal.isUnknownOrUndef()) return; const GRState *trueState, *falseState; llvm::tie(trueState, falseState) = state->assume(cast<DefinedSVal>(argVal)); // Is the value perfectly constrained to zero? if (falseState && !trueState) { ExplodedNode *N = C.generateSink(falseState); if (!N) return; // FIXME: Add reference to CERT advisory, and/or C99 standard in bug // output. LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes"); EnhancedBugReport *report = new EnhancedBugReport(*BT_mallocZero, "Call to 'malloc' has an allocation" " size of 0 bytes", N); report->addRange(CE->getArg(0)->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, CE->getArg(0)); C.EmitReport(report); return; } // Assume the the value is non-zero going forward. assert(trueState); if (trueState != state) { C.addTransition(trueState); } }
void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C, const CallExpr* CE) { // If the CallExpr doesn't have exactly 1 argument just give up checking. if (CE->getNumArgs() != 1) return; // Get the function declaration of the callee. const GRState* state = C.getState(); SVal X = state->getSVal(CE->getCallee()); const FunctionDecl* FD = X.getAsFunctionDecl(); if (!FD) return; if (!BT) { ASTContext &Ctx = C.getASTContext(); Retain = &Ctx.Idents.get("CFRetain"); Release = &Ctx.Idents.get("CFRelease"); BT = new APIMisuse("null passed to CFRetain/CFRelease"); } // Check if we called CFRetain/CFRelease. const IdentifierInfo *FuncII = FD->getIdentifier(); if (!(FuncII == Retain || FuncII == Release)) return; // FIXME: The rest of this just checks that the argument is non-null. // It should probably be refactored and combined with AttrNonNullChecker. // Get the argument's value. const Expr *Arg = CE->getArg(0); SVal ArgVal = state->getSVal(Arg); DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal); if (!DefArgVal) return; // Get a NULL value. SValBuilder &svalBuilder = C.getSValBuilder(); DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType())); // Make an expression asserting that they're equal. DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); // Are they equal? const GRState *stateTrue, *stateFalse; llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); if (stateTrue && !stateFalse) { ExplodedNode *N = C.generateSink(stateTrue); if (!N) return; const char *description = (FuncII == Retain) ? "Null pointer argument in call to CFRetain" : "Null pointer argument in call to CFRelease"; EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N); report->addRange(Arg->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg); C.EmitReport(report); return; } // From here on, we know the argument is non-null. C.addTransition(stateFalse); }
void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { if (!DS->isSingleDecl()) return; const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); if (!VD) return; ASTContext &Ctx = C.getASTContext(); const VariableArrayType *VLA = Ctx.getAsVariableArrayType(VD->getType()); if (!VLA) return; // FIXME: Handle multi-dimensional VLAs. const Expr* SE = VLA->getSizeExpr(); const GRState *state = C.getState(); SVal sizeV = state->getSVal(SE); if (sizeV.isUndef()) { // Generate an error node. ExplodedNode *N = C.generateSink(); if (!N) return; if (!BT_undef) BT_undef = new BuiltinBug("Declared variable-length array (VLA) uses a " "garbage value as its size"); EnhancedBugReport *report = new EnhancedBugReport(*BT_undef, BT_undef->getName(), N); report->addRange(SE->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); C.EmitReport(report); return; } // See if the size value is known. It can't be undefined because we would have // warned about that already. if (sizeV.isUnknown()) return; // Check if the size is zero. DefinedSVal sizeD = cast<DefinedSVal>(sizeV); const GRState *stateNotZero, *stateZero; llvm::tie(stateNotZero, stateZero) = state->assume(sizeD); if (stateZero && !stateNotZero) { ExplodedNode* N = C.generateSink(stateZero); if (!BT_zero) BT_zero = new BuiltinBug("Declared variable-length array (VLA) has zero " "size"); EnhancedBugReport *report = new EnhancedBugReport(*BT_zero, BT_zero->getName(), N); report->addRange(SE->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); C.EmitReport(report); return; } // From this point on, assume that the size is not zero. state = stateNotZero; // VLASizeChecker is responsible for defining the extent of the array being // declared. We do this by multiplying the array length by the element size, // then matching that with the array region's extent symbol. // Convert the array length to size_t. SValBuilder &svalBuilder = C.getSValBuilder(); QualType SizeTy = Ctx.getSizeType(); NonLoc ArrayLength = cast<NonLoc>(svalBuilder.evalCast(sizeD, SizeTy, SE->getType())); // Get the element size. CharUnits EleSize = Ctx.getTypeSizeInChars(VLA->getElementType()); SVal EleSizeVal = svalBuilder.makeIntVal(EleSize.getQuantity(), SizeTy); // Multiply the array length by the element size. SVal ArraySizeVal = svalBuilder.evalBinOpNN(state, BO_Mul, ArrayLength, cast<NonLoc>(EleSizeVal), SizeTy); // Finally, assume that the array's extent matches the given size. const LocationContext *LC = C.getPredecessor()->getLocationContext(); DefinedOrUnknownSVal Extent = state->getRegion(VD, LC)->getExtent(svalBuilder); DefinedOrUnknownSVal ArraySize = cast<DefinedOrUnknownSVal>(ArraySizeVal); DefinedOrUnknownSVal sizeIsKnown = svalBuilder.evalEQ(state, Extent, ArraySize); state = state->assume(sizeIsKnown, true); // Assume should not fail at this point. assert(state); // Remember our assumptions! C.addTransition(state); }
void IdempotentOperationChecker::VisitEndAnalysis(ExplodedGraph &G, BugReporter &BR, ExprEngine &Eng) { BugType *BT = new BugType("Idempotent operation", "Dead code"); // Iterate over the hash to see if we have any paths with definite // idempotent operations. for (AssumptionMap::const_iterator i = hash.begin(); i != hash.end(); ++i) { // Unpack the hash contents const BinaryOperatorData &Data = i->second; const Assumption &A = Data.assumption; AnalysisContext *AC = Data.analysisContext; const ExplodedNodeSet &ES = Data.explodedNodes; const BinaryOperator *B = i->first; if (A == Impossible) continue; // If the analyzer did not finish, check to see if we can still emit this // warning if (Eng.hasWorkRemaining()) { const CFGStmtMap *CBM = CFGStmtMap::Build(AC->getCFG(), &AC->getParentMap()); // If we can trace back if (!PathWasCompletelyAnalyzed(AC->getCFG(), CBM->getBlock(B), CBM, Eng.getCoreEngine())) continue; delete CBM; } // Select the error message and SourceRanges to report. llvm::SmallString<128> buf; llvm::raw_svector_ostream os(buf); bool LHSRelevant = false, RHSRelevant = false; switch (A) { case Equal: LHSRelevant = true; RHSRelevant = true; if (B->getOpcode() == BO_Assign) os << "Assigned value is always the same as the existing value"; else os << "Both operands to '" << B->getOpcodeStr() << "' always have the same value"; break; case LHSis1: LHSRelevant = true; os << "The left operand to '" << B->getOpcodeStr() << "' is always 1"; break; case RHSis1: RHSRelevant = true; os << "The right operand to '" << B->getOpcodeStr() << "' is always 1"; break; case LHSis0: LHSRelevant = true; os << "The left operand to '" << B->getOpcodeStr() << "' is always 0"; break; case RHSis0: RHSRelevant = true; os << "The right operand to '" << B->getOpcodeStr() << "' is always 0"; break; case Possible: llvm_unreachable("Operation was never marked with an assumption"); case Impossible: llvm_unreachable(0); } // Add a report for each ExplodedNode for (ExplodedNodeSet::iterator I = ES.begin(), E = ES.end(); I != E; ++I) { EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), *I); // Add source ranges and visitor hooks if (LHSRelevant) { const Expr *LHS = i->first->getLHS(); report->addRange(LHS->getSourceRange()); report->addVisitorCreator(bugreporter::registerVarDeclsLastStore, LHS); } if (RHSRelevant) { const Expr *RHS = i->first->getRHS(); report->addRange(i->first->getRHS()->getSourceRange()); report->addVisitorCreator(bugreporter::registerVarDeclsLastStore, RHS); } BR.EmitReport(report); } } }
void AttrNonNullChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { const GRState *state = C.getState(); const GRState *originalState = state; // Check if the callee has a 'nonnull' attribute. SVal X = state->getSVal(CE->getCallee()); const FunctionDecl* FD = X.getAsFunctionDecl(); if (!FD) return; const NonNullAttr* Att = FD->getAttr<NonNullAttr>(); if (!Att) return; // Iterate through the arguments of CE and check them for null. unsigned idx = 0; for (CallExpr::const_arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E; ++I, ++idx) { if (!Att->isNonNull(idx)) continue; const SVal &V = state->getSVal(*I); const DefinedSVal *DV = dyn_cast<DefinedSVal>(&V); if (!DV) continue; ConstraintManager &CM = C.getConstraintManager(); const GRState *stateNotNull, *stateNull; llvm::tie(stateNotNull, stateNull) = CM.AssumeDual(state, *DV); if (stateNull && !stateNotNull) { // Generate an error node. Check for a null node in case // we cache out. if (ExplodedNode *errorNode = C.GenerateNode(CE, stateNull, true)) { // Lazily allocate the BugType object if it hasn't already been // created. Ownership is transferred to the BugReporter object once // the BugReport is passed to 'EmitWarning'. if (!BT) BT = new BugType("Argument with 'nonnull' attribute passed null", "API"); EnhancedBugReport *R = new EnhancedBugReport(*BT, "Null pointer passed as an argument to a " "'nonnull' parameter", errorNode); // Highlight the range of the argument that was null. const Expr *arg = *I; R->addRange(arg->getSourceRange()); R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, arg); // Emit the bug report. C.EmitReport(R); } // Always return. Either we cached out or we just emitted an error. return; } // If a pointer value passed the check we should assume that it is // indeed not null from this point forward. assert(stateNotNull); state = stateNotNull; } // If we reach here all of the arguments passed the nonnull check. // If 'state' has been updated generated a new node. if (state != originalState) C.addTransition(C.GenerateNode(CE, state)); }
void DereferenceChecker::VisitLocation(CheckerContext &C, const Stmt *S, SVal l) { // Check for dereference of an undefined value. if (l.isUndef()) { if (ExplodedNode *N = C.GenerateSink()) { if (!BT_undef) BT_undef = new BuiltinBug("Dereference of undefined pointer value"); EnhancedBugReport *report = new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, bugreporter::GetDerefExpr(N)); C.EmitReport(report); } return; } DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l); // Check for null dereferences. if (!isa<Loc>(location)) return; const GRState *state = C.getState(); const GRState *notNullState, *nullState; llvm::tie(notNullState, nullState) = state->Assume(location); // The explicit NULL case. if (nullState) { if (!notNullState) { // Generate an error node. ExplodedNode *N = C.GenerateSink(nullState); 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 = new BuiltinBug("Dereference of null pointer"); llvm::SmallString<100> buf; llvm::SmallVector<SourceRange, 2> Ranges; switch (S->getStmtClass()) { case Stmt::UnaryOperatorClass: { const UnaryOperator *U = cast<UnaryOperator>(S); const Expr *SU = U->getSubExpr()->IgnoreParens(); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(SU)) { if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { llvm::raw_svector_ostream os(buf); os << "Dereference of null pointer (loaded from variable '" << VD->getName() << "')"; Ranges.push_back(DR->getSourceRange()); } } break; } case Stmt::MemberExprClass: { const MemberExpr *M = cast<MemberExpr>(S); if (M->isArrow()) if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(M->getBase()->IgnoreParenCasts())) { if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { llvm::raw_svector_ostream os(buf); os << "Field access results in a dereference of a null pointer " "(loaded from variable '" << VD->getName() << "')"; Ranges.push_back(M->getBase()->getSourceRange()); } } break; } default: break; } EnhancedBugReport *report = new EnhancedBugReport(*BT_null, buf.empty() ? BT_null->getDescription():buf.str(), N); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, bugreporter::GetDerefExpr(N)); for (llvm::SmallVectorImpl<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) report->addRange(*I); C.EmitReport(report); return; } else { // Otherwise, we have the case where the location could either be // null or not-null. Record the error node as an "implicit" null // dereference. if (ExplodedNode *N = C.GenerateSink(nullState)) ImplicitNullDerefNodes.push_back(N); } } // From this point forward, we know that the location is not null. C.addTransition(notNullState); }