From: Gabor Horvath Date: Thu, 27 Aug 2015 18:49:07 +0000 (+0000) Subject: [Static Analyzer] Make NonNullParamChecker emit implicit null dereference events. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ecd7572ceae95193e6a58c83427069c4cddb3ade;p=clang [Static Analyzer] Make NonNullParamChecker emit implicit null dereference events. Differential Revision: http://reviews.llvm.org/D11433 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@246182 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/StaticAnalyzer/Core/Checker.h b/include/clang/StaticAnalyzer/Core/Checker.h index 099d76311f..6468553d98 100644 --- a/include/clang/StaticAnalyzer/Core/Checker.h +++ b/include/clang/StaticAnalyzer/Core/Checker.h @@ -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. diff --git a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp index cf9d9f7438..07be5a59e5 100644 --- a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -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); } } diff --git a/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp index 73f8087fd3..a7586c412a 100644 --- a/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp @@ -28,7 +28,7 @@ using namespace ento; namespace { class NonNullParamChecker - : public Checker< check::PreCall > { + : public Checker< check::PreCall, EventDispatcher > { mutable std::unique_ptr BTAttrNonNull; mutable std::unique_ptr 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 R; - if (haveAttrNonNull) - R = genReportNullAttrNonNull(errorNode, ArgE); - else if (haveRefTypeParam) - R = genReportReferenceToNullPointer(errorNode, ArgE); + std::unique_ptr 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; + // 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); + } } // If a pointer value passed the check we should assume that it is diff --git a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index f488b5f856..ceb437d12f 100644 --- a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -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); } } diff --git a/test/Analysis/nullability.mm b/test/Analysis/nullability.mm index 4b68e3da99..ec7ea3acad 100644 --- a/test/Analysis/nullability.mm +++ b/test/Analysis/nullability.mm @@ -50,6 +50,8 @@ int *_Nullable returnsNullableInt(); template 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);