mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-25 23:00:15 +00:00
[Static Analyzer] Make NonNullParamChecker emit implicit null dereference events.
Differential Revision: http://reviews.llvm.org/D11433 llvm-svn: 246182
This commit is contained in:
parent
8251f978d3
commit
8d3ad6b617
@ -514,6 +514,10 @@ struct ImplicitNullDerefEvent {
|
||||
bool IsLoad;
|
||||
ExplodedNode *SinkNode;
|
||||
BugReporter *BR;
|
||||
// When true, the dereference is in the source code directly. When false, the
|
||||
// dereference might happen later (for example pointer passed to a parameter
|
||||
// that is marked with nonnull attribute.)
|
||||
bool IsDirectDereference;
|
||||
};
|
||||
|
||||
/// \brief A helper class which wraps a boolean value set to false by default.
|
||||
|
@ -220,7 +220,8 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
|
||||
// null or not-null. Record the error node as an "implicit" null
|
||||
// dereference.
|
||||
if (ExplodedNode *N = C.generateSink(nullState)) {
|
||||
ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() };
|
||||
ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),
|
||||
/*IsDirectDereference=*/false};
|
||||
dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
@ -257,8 +258,9 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
|
||||
// At this point the value could be either null or non-null.
|
||||
// Record this as an "implicit" null dereference.
|
||||
if (ExplodedNode *N = C.generateSink(StNull)) {
|
||||
ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N,
|
||||
&C.getBugReporter() };
|
||||
ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,
|
||||
&C.getBugReporter(),
|
||||
/*IsDirectDereference=*/false};
|
||||
dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ using namespace ento;
|
||||
|
||||
namespace {
|
||||
class NonNullParamChecker
|
||||
: public Checker< check::PreCall > {
|
||||
: public Checker< check::PreCall, EventDispatcher<ImplicitNullDerefEvent> > {
|
||||
mutable std::unique_ptr<BugType> BTAttrNonNull;
|
||||
mutable std::unique_ptr<BugType> BTNullRefArg;
|
||||
|
||||
@ -139,26 +139,34 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
|
||||
ProgramStateRef stateNotNull, stateNull;
|
||||
std::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.generateSink(stateNull)) {
|
||||
if (stateNull) {
|
||||
if (!stateNotNull) {
|
||||
// Generate an error node. Check for a null node in case
|
||||
// we cache out.
|
||||
if (ExplodedNode *errorNode = C.generateSink(stateNull)) {
|
||||
|
||||
std::unique_ptr<BugReport> R;
|
||||
if (haveAttrNonNull)
|
||||
R = genReportNullAttrNonNull(errorNode, ArgE);
|
||||
else if (haveRefTypeParam)
|
||||
R = genReportReferenceToNullPointer(errorNode, ArgE);
|
||||
std::unique_ptr<BugReport> R;
|
||||
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));
|
||||
// Highlight the range of the argument that was null.
|
||||
R->addRange(Call.getArgSourceRange(idx));
|
||||
|
||||
// Emit the bug report.
|
||||
C.emitReport(std::move(R));
|
||||
// Emit the bug report.
|
||||
C.emitReport(std::move(R));
|
||||
}
|
||||
|
||||
// Always return. Either we cached out or we just emitted an error.
|
||||
return;
|
||||
}
|
||||
if (ExplodedNode *N = C.generateSink(stateNull)) {
|
||||
ImplicitNullDerefEvent event = {
|
||||
V, false, N, &C.getBugReporter(),
|
||||
/*IsDirectDereference=*/haveRefTypeParam};
|
||||
dispatchEvent(event);
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -335,7 +335,10 @@ void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const {
|
||||
if (Filter.CheckNullableDereferenced &&
|
||||
TrackedNullability->getValue() == Nullability::Nullable) {
|
||||
BugReporter &BR = *Event.BR;
|
||||
reportBug(ErrorKind::NullableDereferenced, Event.SinkNode, Region, BR);
|
||||
if (Event.IsDirectDereference)
|
||||
reportBug(ErrorKind::NullableDereferenced, Event.SinkNode, Region, BR);
|
||||
else
|
||||
reportBug(ErrorKind::NullablePassedToNonnull, Event.SinkNode, Region, BR);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,8 @@ int *_Nullable returnsNullableInt();
|
||||
|
||||
template <typename T> T *eraseNullab(T *p) { return p; }
|
||||
|
||||
void takesAttrNonnull(Dummy *p) __attribute((nonnull(1)));
|
||||
|
||||
void testBasicRules() {
|
||||
Dummy *p = returnsNullable();
|
||||
int *ptr = returnsNullableInt();
|
||||
@ -73,10 +75,8 @@ void testBasicRules() {
|
||||
Dummy dd(d);
|
||||
break;
|
||||
}
|
||||
// Here the copy constructor is called, so a reference is initialized with the
|
||||
// value of p. No ImplicitNullDereference event will be dispatched for this
|
||||
// case. A followup patch is expected to fix this in NonNullParamChecker.
|
||||
default: { Dummy d = *p; } break; // No warning.
|
||||
case 5: takesAttrNonnull(p); break; // expected-warning {{Nullable pointer is passed to}}
|
||||
default: { Dummy d = *p; } break; // expected-warning {{Nullable pointer is dereferenced}}
|
||||
}
|
||||
if (p) {
|
||||
takesNonnull(p);
|
||||
|
Loading…
Reference in New Issue
Block a user