|
@@ -26,13 +26,16 @@
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
#include "ClangSACheckers.h"
|
|
|
-#include "llvm/Support/Path.h"
|
|
|
+
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
|
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
|
|
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
|
|
|
|
|
|
+#include "llvm/ADT/StringExtras.h"
|
|
|
+#include "llvm/Support/Path.h"
|
|
|
+
|
|
|
using namespace clang;
|
|
|
using namespace ento;
|
|
|
|
|
@@ -89,18 +92,6 @@ enum class ErrorKind : int {
|
|
|
NullablePassedToNonnull
|
|
|
};
|
|
|
|
|
|
-const char *const ErrorMessages[] = {
|
|
|
- "Null is assigned to a pointer which is expected to have non-null value",
|
|
|
- "Null passed to a callee that requires a non-null argument",
|
|
|
- "Null is returned from a function that is expected to return a non-null "
|
|
|
- "value",
|
|
|
- "Nullable pointer is assigned to a pointer which is expected to have "
|
|
|
- "non-null value",
|
|
|
- "Nullable pointer is returned from a function that is expected to return a "
|
|
|
- "non-null value",
|
|
|
- "Nullable pointer is dereferenced",
|
|
|
- "Nullable pointer is passed to a callee that requires a non-null argument"};
|
|
|
-
|
|
|
class NullabilityChecker
|
|
|
: public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>,
|
|
|
check::PostCall, check::PostStmt<ExplicitCastExpr>,
|
|
@@ -169,17 +160,19 @@ private:
|
|
|
///
|
|
|
/// When \p SuppressPath is set to true, no more bugs will be reported on this
|
|
|
/// path by this checker.
|
|
|
- void reportBugIfPreconditionHolds(ErrorKind Error, ExplodedNode *N,
|
|
|
- const MemRegion *Region, CheckerContext &C,
|
|
|
+ void reportBugIfPreconditionHolds(StringRef Msg, ErrorKind Error,
|
|
|
+ ExplodedNode *N, const MemRegion *Region,
|
|
|
+ CheckerContext &C,
|
|
|
const Stmt *ValueExpr = nullptr,
|
|
|
bool SuppressPath = false) const;
|
|
|
|
|
|
- void reportBug(ErrorKind Error, ExplodedNode *N, const MemRegion *Region,
|
|
|
- BugReporter &BR, const Stmt *ValueExpr = nullptr) const {
|
|
|
+ void reportBug(StringRef Msg, ErrorKind Error, ExplodedNode *N,
|
|
|
+ const MemRegion *Region, BugReporter &BR,
|
|
|
+ const Stmt *ValueExpr = nullptr) const {
|
|
|
if (!BT)
|
|
|
BT.reset(new BugType(this, "Nullability", "Memory error"));
|
|
|
- const char *Msg = ErrorMessages[static_cast<int>(Error)];
|
|
|
- std::unique_ptr<BugReport> R(new BugReport(*BT, Msg, N));
|
|
|
+
|
|
|
+ auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
|
|
|
if (Region) {
|
|
|
R->markInteresting(Region);
|
|
|
R->addVisitor(llvm::make_unique<NullabilityBugVisitor>(Region));
|
|
@@ -384,7 +377,7 @@ static bool checkPreconditionViolation(ProgramStateRef State, ExplodedNode *N,
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
-void NullabilityChecker::reportBugIfPreconditionHolds(
|
|
|
+void NullabilityChecker::reportBugIfPreconditionHolds(StringRef Msg,
|
|
|
ErrorKind Error, ExplodedNode *N, const MemRegion *Region,
|
|
|
CheckerContext &C, const Stmt *ValueExpr, bool SuppressPath) const {
|
|
|
ProgramStateRef OriginalState = N->getState();
|
|
@@ -396,7 +389,7 @@ void NullabilityChecker::reportBugIfPreconditionHolds(
|
|
|
N = C.addTransition(OriginalState, N);
|
|
|
}
|
|
|
|
|
|
- reportBug(Error, N, Region, C.getBugReporter(), ValueExpr);
|
|
|
+ reportBug(Msg, Error, N, Region, C.getBugReporter(), ValueExpr);
|
|
|
}
|
|
|
|
|
|
/// Cleaning up the program state.
|
|
@@ -450,9 +443,13 @@ void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const {
|
|
|
// Do not suppress errors on defensive code paths, because dereferencing
|
|
|
// a nullable pointer is always an error.
|
|
|
if (Event.IsDirectDereference)
|
|
|
- reportBug(ErrorKind::NullableDereferenced, Event.SinkNode, Region, BR);
|
|
|
- else
|
|
|
- reportBug(ErrorKind::NullablePassedToNonnull, Event.SinkNode, Region, BR);
|
|
|
+ reportBug("Nullable pointer is dereferenced",
|
|
|
+ ErrorKind::NullableDereferenced, Event.SinkNode, Region, BR);
|
|
|
+ else {
|
|
|
+ reportBug("Nullable pointer is passed to a callee that requires a "
|
|
|
+ "non-null", ErrorKind::NullablePassedToNonnull,
|
|
|
+ Event.SinkNode, Region, BR);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -537,7 +534,14 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
|
|
|
ExplodedNode *N = C.generateErrorNode(State, &Tag);
|
|
|
if (!N)
|
|
|
return;
|
|
|
- reportBugIfPreconditionHolds(ErrorKind::NilReturnedToNonnull, N, nullptr, C,
|
|
|
+
|
|
|
+ SmallString<256> SBuf;
|
|
|
+ llvm::raw_svector_ostream OS(SBuf);
|
|
|
+ OS << "Null is returned from a " << C.getDeclDescription(D) <<
|
|
|
+ " that is expected to return a non-null value";
|
|
|
+
|
|
|
+ reportBugIfPreconditionHolds(OS.str(),
|
|
|
+ ErrorKind::NilReturnedToNonnull, N, nullptr, C,
|
|
|
RetExpr);
|
|
|
return;
|
|
|
}
|
|
@@ -556,7 +560,14 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
|
|
|
RequiredNullability == Nullability::Nonnull) {
|
|
|
static CheckerProgramPointTag Tag(this, "NullableReturnedFromNonnull");
|
|
|
ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag);
|
|
|
- reportBugIfPreconditionHolds(ErrorKind::NullableReturnedToNonnull, N,
|
|
|
+
|
|
|
+ SmallString<256> SBuf;
|
|
|
+ llvm::raw_svector_ostream OS(SBuf);
|
|
|
+ OS << "Nullable pointer is returned from a " << C.getDeclDescription(D) <<
|
|
|
+ " that is expected to return a non-null value";
|
|
|
+
|
|
|
+ reportBugIfPreconditionHolds(OS.str(),
|
|
|
+ ErrorKind::NullableReturnedToNonnull, N,
|
|
|
Region, C);
|
|
|
}
|
|
|
return;
|
|
@@ -605,14 +616,21 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
|
|
|
Nullability ArgExprTypeLevelNullability =
|
|
|
getNullabilityAnnotation(ArgExpr->getType());
|
|
|
|
|
|
+ unsigned ParamIdx = Param->getFunctionScopeIndex() + 1;
|
|
|
+
|
|
|
if (Filter.CheckNullPassedToNonnull && Nullness == NullConstraint::IsNull &&
|
|
|
ArgExprTypeLevelNullability != Nullability::Nonnull &&
|
|
|
RequiredNullability == Nullability::Nonnull) {
|
|
|
ExplodedNode *N = C.generateErrorNode(State);
|
|
|
if (!N)
|
|
|
return;
|
|
|
- reportBugIfPreconditionHolds(ErrorKind::NilPassedToNonnull, N, nullptr, C,
|
|
|
- ArgExpr);
|
|
|
+ SmallString<256> SBuf;
|
|
|
+ llvm::raw_svector_ostream OS(SBuf);
|
|
|
+ OS << "Null passed to a callee that requires a non-null " << ParamIdx
|
|
|
+ << llvm::getOrdinalSuffix(ParamIdx) << " parameter";
|
|
|
+ reportBugIfPreconditionHolds(OS.str(), ErrorKind::NilPassedToNonnull, N,
|
|
|
+ nullptr, C,
|
|
|
+ ArgExpr, /*SuppressPath=*/false);
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -631,14 +649,20 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
|
|
|
if (Filter.CheckNullablePassedToNonnull &&
|
|
|
RequiredNullability == Nullability::Nonnull) {
|
|
|
ExplodedNode *N = C.addTransition(State);
|
|
|
- reportBugIfPreconditionHolds(ErrorKind::NullablePassedToNonnull, N,
|
|
|
+ SmallString<256> SBuf;
|
|
|
+ llvm::raw_svector_ostream OS(SBuf);
|
|
|
+ OS << "Nullable pointer is passed to a callee that requires a non-null "
|
|
|
+ << ParamIdx << llvm::getOrdinalSuffix(ParamIdx) << " parameter";
|
|
|
+ reportBugIfPreconditionHolds(OS.str(),
|
|
|
+ ErrorKind::NullablePassedToNonnull, N,
|
|
|
Region, C, ArgExpr, /*SuppressPath=*/true);
|
|
|
return;
|
|
|
}
|
|
|
if (Filter.CheckNullableDereferenced &&
|
|
|
Param->getType()->isReferenceType()) {
|
|
|
ExplodedNode *N = C.addTransition(State);
|
|
|
- reportBugIfPreconditionHolds(ErrorKind::NullableDereferenced, N, Region,
|
|
|
+ reportBugIfPreconditionHolds("Nullable pointer is dereferenced",
|
|
|
+ ErrorKind::NullableDereferenced, N, Region,
|
|
|
C, ArgExpr, /*SuppressPath=*/true);
|
|
|
return;
|
|
|
}
|
|
@@ -1007,7 +1031,9 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
|
|
|
if (!ValueExpr)
|
|
|
ValueExpr = S;
|
|
|
|
|
|
- reportBugIfPreconditionHolds(ErrorKind::NilAssignedToNonnull, N, nullptr, C,
|
|
|
+ reportBugIfPreconditionHolds("Null is assigned to a pointer which is "
|
|
|
+ "expected to have non-null value",
|
|
|
+ ErrorKind::NilAssignedToNonnull, N, nullptr, C,
|
|
|
ValueExpr);
|
|
|
return;
|
|
|
}
|
|
@@ -1029,7 +1055,9 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
|
|
|
LocNullability == Nullability::Nonnull) {
|
|
|
static CheckerProgramPointTag Tag(this, "NullablePassedToNonnull");
|
|
|
ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag);
|
|
|
- reportBugIfPreconditionHolds(ErrorKind::NullableAssignedToNonnull, N,
|
|
|
+ reportBugIfPreconditionHolds("Nullable pointer is assigned to a pointer "
|
|
|
+ "which is expected to have non-null value",
|
|
|
+ ErrorKind::NullableAssignedToNonnull, N,
|
|
|
ValueRegion, C);
|
|
|
}
|
|
|
return;
|