diff --git a/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -30,11 +30,14 @@ : public Checker< check::Location, check::Bind, EventDispatcher > { + enum DerefKind { NullPointer, UndefinedPointerValue }; + BugType BT_Null{this, "Dereference of null pointer", categories::LogicError}; BugType BT_Undef{this, "Dereference of undefined pointer value", categories::LogicError}; - void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C) const; + void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S, + CheckerContext &C) const; public: void checkLocation(SVal location, bool isLoad, const Stmt* S, @@ -117,8 +120,24 @@ return false; } -void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, - CheckerContext &C) const { +void DereferenceChecker::reportBug(DerefKind K, ProgramStateRef State, + const Stmt *S, CheckerContext &C) const { + const BugType *BT = nullptr; + llvm::StringRef DerefStr1; + llvm::StringRef DerefStr2; + switch (K) { + case DerefKind::NullPointer: + BT = &BT_Null; + DerefStr1 = " results in a null pointer dereference"; + DerefStr2 = " results in a dereference of a null pointer"; + break; + case DerefKind::UndefinedPointerValue: + BT = &BT_Undef; + DerefStr1 = " results in an undefined pointer dereference"; + DerefStr2 = " results in a dereference of an undefined pointer value"; + break; + }; + // Generate an error node. ExplodedNode *N = C.generateErrorNode(State); if (!N) @@ -135,7 +154,7 @@ const ArraySubscriptExpr *AE = cast(S); AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), State.get(), N->getLocationContext()); - os << " results in a null pointer dereference"; + os << DerefStr1; break; } case Stmt::OMPArraySectionExprClass: { @@ -143,11 +162,11 @@ const OMPArraySectionExpr *AE = cast(S); AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), State.get(), N->getLocationContext()); - os << " results in a null pointer dereference"; + os << DerefStr1; break; } case Stmt::UnaryOperatorClass: { - os << "Dereference of null pointer"; + os << BT->getDescription(); const UnaryOperator *U = cast(S); AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), State.get(), N->getLocationContext(), true); @@ -156,8 +175,7 @@ case Stmt::MemberExprClass: { const MemberExpr *M = cast(S); if (M->isArrow() || isDeclRefExprToReference(M->getBase())) { - os << "Access to field '" << M->getMemberNameInfo() - << "' results in a dereference of a null pointer"; + os << "Access to field '" << M->getMemberNameInfo() << "'" << DerefStr2; AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), State.get(), N->getLocationContext(), true); } @@ -165,8 +183,7 @@ } case Stmt::ObjCIvarRefExprClass: { const ObjCIvarRefExpr *IV = cast(S); - os << "Access to instance variable '" << *IV->getDecl() - << "' results in a dereference of a null pointer"; + os << "Access to instance variable '" << *IV->getDecl() << "'" << DerefStr2; AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(), State.get(), N->getLocationContext(), true); break; @@ -176,7 +193,7 @@ } auto report = std::make_unique( - BT_Null, buf.empty() ? BT_Null.getDescription() : StringRef(buf), N); + *BT, buf.empty() ? BT->getDescription() : StringRef(buf), N); bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report); @@ -191,12 +208,9 @@ CheckerContext &C) const { // Check for dereference of an undefined value. if (l.isUndef()) { - if (ExplodedNode *N = C.generateErrorNode()) { - auto report = std::make_unique( - BT_Undef, BT_Undef.getDescription(), N); - bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report); - C.emitReport(std::move(report)); - } + const Expr *DerefExpr = getDereferenceExpr(S); + if (!suppressReport(DerefExpr)) + reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C); return; } @@ -217,7 +231,7 @@ // we call an "explicit" null dereference. const Expr *expr = getDereferenceExpr(S); if (!suppressReport(expr)) { - reportBug(nullState, expr, C); + reportBug(DerefKind::NullPointer, nullState, expr, C); return; } } @@ -259,7 +273,7 @@ if (!StNonNull) { const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true); if (!suppressReport(expr)) { - reportBug(StNull, expr, C); + reportBug(DerefKind::NullPointer, StNull, expr, C); return; } } diff --git a/clang/test/Analysis/invalid-deref.c b/clang/test/Analysis/invalid-deref.c new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/invalid-deref.c @@ -0,0 +1,32 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s + +typedef unsigned uintptr_t; + +void f1() { + int *p; + *p = 0; // expected-warning{{Dereference of undefined pointer value}} +} + +struct foo_struct { + int x; +}; + +int f2() { + struct foo_struct *p; + + return p->x++; // expected-warning{{Access to field 'x' results in a dereference of an undefined pointer value (loaded from variable 'p')}} +} + +int f3() { + char *x; + int i = 2; + + return x[i + 1]; // expected-warning{{Array access (from variable 'x') results in an undefined pointer dereference}} +} + +int f3_b() { + char *x; + int i = 2; + + return x[i + 1]++; // expected-warning{{Array access (from variable 'x') results in an undefined pointer dereference}} +} diff --git a/clang/test/Analysis/misc-ps-region-store.m b/clang/test/Analysis/misc-ps-region-store.m --- a/clang/test/Analysis/misc-ps-region-store.m +++ b/clang/test/Analysis/misc-ps-region-store.m @@ -1157,7 +1157,7 @@ struct list_pr8141 * pr8141 (void) { struct list_pr8141 *items; - for (;; items = ({ do { } while (0); items->tail; })) // expected-warning{{Dereference of undefined pointer value}} + for (;; items = ({ do { } while (0); items->tail; })) // expected-warning{{dereference of an undefined pointer value}} { } }