diff options
-rw-r--r-- | lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp | 97 | ||||
-rw-r--r-- | test/Analysis/initializer.cpp | 3 | ||||
-rw-r--r-- | test/Analysis/reference.cpp | 70 |
3 files changed, 141 insertions, 29 deletions
diff --git a/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp b/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp index 87e218505b..d1593419e8 100644 --- a/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp @@ -26,10 +26,16 @@ using namespace ento; namespace { class AttrNonNullChecker : public Checker< check::PreCall > { - mutable OwningPtr<BugType> BT; + mutable OwningPtr<BugType> BTAttrNonNull; + mutable OwningPtr<BugType> BTNullRefArg; public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + + BugReport *genReportNullAttrNonNull(const ExplodedNode *ErrorN, + const Expr *ArgE) const; + BugReport *genReportReferenceToNullPointer(const ExplodedNode *ErrorN, + const Expr *ArgE) const; }; } // end anonymous namespace @@ -40,26 +46,38 @@ void AttrNonNullChecker::checkPreCall(const CallEvent &Call, return; const NonNullAttr *Att = FD->getAttr<NonNullAttr>(); - if (!Att) - return; ProgramStateRef state = C.getState(); - // Iterate through the arguments of CE and check them for null. - for (unsigned idx = 0, count = Call.getNumArgs(); idx != count; ++idx) { - if (!Att->isNonNull(idx)) + CallEvent::param_type_iterator TyI = Call.param_type_begin(), + TyE = Call.param_type_end(); + + for (unsigned idx = 0, count = Call.getNumArgs(); idx != count; ++idx){ + + // Check if the parameter is a reference. We want to report when reference + // to a null pointer is passed as a paramter. + bool haveRefTypeParam = false; + if (TyI != TyE) { + haveRefTypeParam = (*TyI)->isReferenceType(); + TyI++; + } + + bool haveAttrNonNull = Att && Att->isNonNull(idx); + + if (!haveRefTypeParam && !haveAttrNonNull) continue; + // If the value is unknown or undefined, we can't perform this check. + const Expr *ArgE = Call.getArgExpr(idx); SVal V = Call.getArgSVal(idx); Optional<DefinedSVal> DV = V.getAs<DefinedSVal>(); - - // If the value is unknown or undefined, we can't perform this check. if (!DV) continue; - const Expr *ArgE = Call.getArgExpr(idx); - - if (!DV->getAs<Loc>()) { + // Process the case when the argument is not a location. + assert(!haveRefTypeParam || DV->getAs<Loc>()); + + if (haveAttrNonNull && !DV->getAs<Loc>()) { // If the argument is a union type, we want to handle a potential // transparent_union GCC extension. if (!ArgE) @@ -100,21 +118,15 @@ void AttrNonNullChecker::checkPreCall(const CallEvent &Call, // we cache out. if (ExplodedNode *errorNode = C.generateSink(stateNull)) { - // 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.reset(new BugType("Argument with 'nonnull' attribute passed null", - "API")); - - BugReport *R = - new BugReport(*BT, "Null pointer passed as an argument to a " - "'nonnull' parameter", errorNode); + BugReport *R = 0; + if (haveAttrNonNull) + R = genReportNullAttrNonNull(errorNode, ArgE); + else if (haveRefTypeParam) + R = genReportReferenceToNullPointer(errorNode, ArgE); // Highlight the range of the argument that was null. R->addRange(Call.getArgSourceRange(idx)); - if (ArgE) - bugreporter::trackNullOrUndefValue(errorNode, ArgE, *R); + // Emit the bug report. C.emitReport(R); } @@ -134,6 +146,45 @@ void AttrNonNullChecker::checkPreCall(const CallEvent &Call, C.addTransition(state); } +BugReport *AttrNonNullChecker::genReportNullAttrNonNull( + const ExplodedNode *ErrorNode, const Expr *ArgE) const { + // 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 (!BTAttrNonNull) + BTAttrNonNull.reset(new BugType( + "Argument with 'nonnull' attribute passed null", + "API")); + + BugReport *R = new BugReport(*BTAttrNonNull, + "Null pointer passed as an argument to a 'nonnull' parameter", + ErrorNode); + if (ArgE) + bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R); + + return R; +} + +BugReport *AttrNonNullChecker::genReportReferenceToNullPointer( + const ExplodedNode *ErrorNode, const Expr *ArgE) const { + if (!BTNullRefArg) + BTNullRefArg.reset(new BuiltinBug("Dereference of null pointer")); + + BugReport *R = new BugReport(*BTNullRefArg, + "Forming reference to null pointer", + ErrorNode); + if (ArgE) { + const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); + if (ArgEDeref == 0) + ArgEDeref = ArgE; + bugreporter::trackNullOrUndefValue(ErrorNode, + ArgEDeref, + *R); + } + return R; + +} + void ento::registerAttrNonNullChecker(CheckerManager &mgr) { mgr.registerChecker<AttrNonNullChecker>(); } diff --git a/test/Analysis/initializer.cpp b/test/Analysis/initializer.cpp index ab2eb90143..3f7802c56d 100644 --- a/test/Analysis/initializer.cpp +++ b/test/Analysis/initializer.cpp @@ -68,8 +68,7 @@ void testReferenceMember() { void testReferenceMember2() { int *p = 0; - // FIXME: We should warn here, since we're creating the reference here. - RefWrapper X(*p); // expected-warning@-12 {{Dereference of null pointer}} + RefWrapper X(*p); // expected-warning {{Forming reference to null pointer}} } diff --git a/test/Analysis/reference.cpp b/test/Analysis/reference.cpp index ed05720fe6..8dd0baf8c3 100644 --- a/test/Analysis/reference.cpp +++ b/test/Analysis/reference.cpp @@ -149,16 +149,78 @@ void testReturnReference() { clang_analyzer_eval(&refFromPointer() == 0); // expected-warning{{FALSE}} } +void intRefParam(int &r) { + ; +} + +void test(int *ptr) { + clang_analyzer_eval(ptr == 0); // expected-warning{{UNKNOWN}} + + extern void use(int &ref); + use(*ptr); + + clang_analyzer_eval(ptr == 0); // expected-warning{{FALSE}} +} + +void testIntRefParam() { + int i = 0; + intRefParam(i); // no-warning +} -// ------------------------------------ -// False negatives -// ------------------------------------ +int refParam(int &byteIndex) { + return byteIndex; +} + +void testRefParam(int *p) { + if (p) + ; + refParam(*p); // expected-warning {{Forming reference to null pointer}} +} + +int ptrRefParam(int *&byteIndex) { + return *byteIndex; // expected-warning {{Dereference of null pointer}} +} +void testRefParam2() { + int *p = 0; + int *&rp = p; + ptrRefParam(rp); +} + +int *maybeNull() { + extern bool coin(); + static int x; + return coin() ? &x : 0; +} + +void use(int &x) { + x = 1; // no-warning +} + +void testSuppression() { + use(*maybeNull()); +} namespace rdar11212286 { class B{}; B test() { B *x = 0; - return *x; // should warn here! + return *x; // expected-warning {{Forming reference to null pointer}} + } + + B testif(B *x) { + if (x) + ; + return *x; // expected-warning {{Forming reference to null pointer}} + } + + void idc(B *x) { + if (x) + ; + } + + B testidc(B *x) { + idc(x); + return *x; // no-warning } } |