summaryrefslogtreecommitdiffstats
path: root/clang/lib/StaticAnalyzer
diff options
context:
space:
mode:
authorGeorge Karpenkov <ekarpenkov@apple.com>2018-02-23 23:26:56 +0000
committerGeorge Karpenkov <ekarpenkov@apple.com>2018-02-23 23:26:56 +0000
commite15451a9c04fa977a4911ca144904293e13e4326 (patch)
treebb2da3b51617da7fd5f163d2bf9f7feb84c0f106 /clang/lib/StaticAnalyzer
parent80e4ba24b9baa6d7312df70b681e613cc85a77f3 (diff)
downloadbcm5719-llvm-e15451a9c04fa977a4911ca144904293e13e4326.tar.gz
bcm5719-llvm-e15451a9c04fa977a4911ca144904293e13e4326.zip
[analyzer] mark returns of functions where the region passed as parameter was not initialized
In the wild, many cases of null pointer dereference, or uninitialized value read occur because the value was meant to be initialized by the inlined function, but did not, most often due to error condition in the inlined function. This change highlights the return branch taken by the inlined function, in order to help user understand the error report and see why the value was uninitialized. rdar://36287652 Differential Revision: https://reviews.llvm.org/D41848 llvm-svn: 325976
Diffstat (limited to 'clang/lib/StaticAnalyzer')
-rw-r--r--clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp272
1 files changed, 272 insertions, 0 deletions
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 7a0e4d41be7..618ddfe705f 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -184,6 +184,276 @@ static bool isFunctionMacroExpansion(SourceLocation Loc,
namespace {
+/// Put a diagnostic on return statement of all inlined functions
+/// for which the region of interest \p RegionOfInterest was passed into,
+/// but not written inside, and it has caused an undefined read or a null
+/// pointer dereference outside.
+class NoStoreFuncVisitor final
+ : public BugReporterVisitorImpl<NoStoreFuncVisitor> {
+
+ const SubRegion *RegionOfInterest;
+ static constexpr const char *DiagnosticsMsg =
+ "Returning without writing to '";
+ bool Initialized = false;
+
+ /// Frames writing into \c RegionOfInterest.
+ /// This visitor generates a note only if a function does not write into
+ /// a region of interest. This information is not immediately available
+ /// by looking at the node associated with the exit from the function
+ /// (usually the return statement). To avoid recomputing the same information
+ /// many times (going up the path for each node and checking whether the
+ /// region was written into) we instead pre-compute and store all
+ /// stack frames along the path which write into the region of interest
+ /// on the first \c VisitNode invocation.
+ llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingRegion;
+
+public:
+ NoStoreFuncVisitor(const SubRegion *R) : RegionOfInterest(R) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int Tag = 0;
+ ID.AddPointer(&Tag);
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR) override {
+ if (!Initialized) {
+ findModifyingFrames(N);
+ Initialized = true;
+ }
+
+ const LocationContext *Ctx = N->getLocationContext();
+ const StackFrameContext *SCtx = Ctx->getCurrentStackFrame();
+ ProgramStateRef State = N->getState();
+ auto CallExitLoc = N->getLocationAs<CallExitBegin>();
+
+ // No diagnostic if region was modified inside the frame.
+ if (!CallExitLoc || FramesModifyingRegion.count(SCtx))
+ return nullptr;
+
+ CallEventRef<> Call =
+ BRC.getStateManager().getCallEventManager().getCaller(SCtx, State);
+
+ const PrintingPolicy &PP = BRC.getASTContext().getPrintingPolicy();
+ const SourceManager &SM = BRC.getSourceManager();
+ if (auto *CCall = dyn_cast<CXXConstructorCall>(Call)) {
+ const MemRegion *ThisRegion = CCall->getCXXThisVal().getAsRegion();
+ if (RegionOfInterest->isSubRegionOf(ThisRegion) &&
+ !CCall->getDecl()->isImplicit())
+ return notModifiedInConstructorDiagnostics(Ctx, SM, PP, *CallExitLoc,
+ CCall, ThisRegion);
+ }
+
+ ArrayRef<ParmVarDecl *> parameters = getCallParameters(Call);
+ for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) {
+ const ParmVarDecl *PVD = parameters[I];
+ SVal S = Call->getArgSVal(I);
+ unsigned IndirectionLevel = 1;
+ QualType T = PVD->getType();
+ while (const MemRegion *R = S.getAsRegion()) {
+ if (RegionOfInterest->isSubRegionOf(R) &&
+ !isPointerToConst(PVD->getType()))
+ return notModifiedDiagnostics(
+ Ctx, SM, PP, *CallExitLoc, Call, PVD, R, IndirectionLevel);
+ QualType PT = T->getPointeeType();
+ if (PT.isNull() || PT->isVoidType()) break;
+ S = State->getSVal(R, PT);
+ T = PT;
+ IndirectionLevel++;
+ }
+ }
+
+ return nullptr;
+ }
+
+private:
+ /// Write to \c FramesModifyingRegion all stack frames along
+ /// the path which modify \c RegionOfInterest.
+ void findModifyingFrames(const ExplodedNode *N) {
+ ProgramStateRef LastReturnState;
+ do {
+ ProgramStateRef State = N->getState();
+ auto CallExitLoc = N->getLocationAs<CallExitBegin>();
+ if (CallExitLoc) {
+ LastReturnState = State;
+ }
+ if (LastReturnState &&
+ wasRegionOfInterestModifiedAt(N, LastReturnState)) {
+ const StackFrameContext *SCtx =
+ N->getLocationContext()->getCurrentStackFrame();
+ while (!SCtx->inTopFrame()) {
+ auto p = FramesModifyingRegion.insert(SCtx);
+ if (!p.second)
+ break; // Frame and all its parents already inserted.
+ SCtx = SCtx->getParent()->getCurrentStackFrame();
+ }
+ }
+
+ N = N->getFirstPred();
+ } while (N);
+ }
+
+ /// \return Whether \c RegionOfInterest was modified at \p N,
+ /// where \p ReturnState is a state associated with the return
+ /// from the current frame.
+ bool wasRegionOfInterestModifiedAt(const ExplodedNode *N,
+ ProgramStateRef ReturnState) {
+ SVal ValueAtReturn = ReturnState->getSVal(RegionOfInterest);
+
+ // Writing into region of interest.
+ if (auto PS = N->getLocationAs<PostStmt>())
+ if (auto *BO = PS->getStmtAs<BinaryOperator>())
+ if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf(
+ N->getSVal(BO->getLHS()).getAsRegion()))
+ return true;
+
+ // SVal after the state is possibly different.
+ SVal ValueAtN = N->getState()->getSVal(RegionOfInterest);
+ if (!ReturnState->areEqual(ValueAtN, ValueAtReturn).isConstrainedTrue() &&
+ (!ValueAtN.isUndef() || !ValueAtReturn.isUndef()))
+ return true;
+
+ return false;
+ }
+
+ /// Get parameters associated with runtime definition in order
+ /// to get the correct parameter name.
+ ArrayRef<ParmVarDecl *> getCallParameters(CallEventRef<> Call) {
+ if (isa<FunctionDecl>(Call->getDecl()))
+ return dyn_cast<FunctionDecl>(Call->getRuntimeDefinition().getDecl())
+ ->parameters();
+ return Call->parameters();
+ }
+
+ /// \return whether \p Ty points to a const type, or is a const reference.
+ bool isPointerToConst(QualType Ty) {
+ return !Ty->getPointeeType().isNull() &&
+ Ty->getPointeeType().getCanonicalType().isConstQualified();
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> notModifiedInConstructorDiagnostics(
+ const LocationContext *Ctx,
+ const SourceManager &SM,
+ const PrintingPolicy &PP,
+ CallExitBegin &CallExitLoc,
+ const CXXConstructorCall *Call,
+ const MemRegion *ArgRegion) {
+
+ SmallString<256> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ os << DiagnosticsMsg;
+ bool out = prettyPrintRegionName(
+ "this", "->", /*IsReference=*/true,
+ /*IndirectionLevel=*/1, ArgRegion, os, PP);
+
+ // Return nothing if we have failed to pretty-print.
+ if (!out)
+ return nullptr;
+
+ os << "'";
+ PathDiagnosticLocation L =
+ getPathDiagnosticLocation(nullptr, SM, Ctx, Call);
+ return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
+ }
+
+ /// \p IndirectionLevel How many times \c ArgRegion has to be dereferenced
+ /// before we get to the super region of \c RegionOfInterest
+ std::shared_ptr<PathDiagnosticPiece>
+ notModifiedDiagnostics(const LocationContext *Ctx,
+ const SourceManager &SM,
+ const PrintingPolicy &PP,
+ CallExitBegin &CallExitLoc,
+ CallEventRef<> Call,
+ const ParmVarDecl *PVD,
+ const MemRegion *ArgRegion,
+ unsigned IndirectionLevel) {
+
+ PathDiagnosticLocation L = getPathDiagnosticLocation(
+ CallExitLoc.getReturnStmt(), SM, Ctx, Call);
+ SmallString<256> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ os << DiagnosticsMsg;
+ bool IsReference = PVD->getType()->isReferenceType();
+ const char *Sep = IsReference && IndirectionLevel == 1 ? "." : "->";
+ bool Success = prettyPrintRegionName(
+ PVD->getQualifiedNameAsString().c_str(),
+ Sep, IsReference, IndirectionLevel, ArgRegion, os, PP);
+
+ // Print the parameter name if the pretty-printing has failed.
+ if (!Success)
+ PVD->printQualifiedName(os);
+ os << "'";
+ return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
+ }
+
+ /// \return a path diagnostic location for the optionally
+ /// present return statement \p RS.
+ PathDiagnosticLocation getPathDiagnosticLocation(const ReturnStmt *RS,
+ const SourceManager &SM,
+ const LocationContext *Ctx,
+ CallEventRef<> Call) {
+ if (RS)
+ return PathDiagnosticLocation::createBegin(RS, SM, Ctx);
+ return PathDiagnosticLocation(
+ Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM);
+ }
+
+ /// Pretty-print region \p ArgRegion starting from parent to \p os.
+ /// \return whether printing has succeeded
+ bool prettyPrintRegionName(const char *TopRegionName,
+ const char *Sep,
+ bool IsReference,
+ int IndirectionLevel,
+ const MemRegion *ArgRegion,
+ llvm::raw_svector_ostream &os,
+ const PrintingPolicy &PP) {
+ SmallVector<const MemRegion *, 5> Subregions;
+ const MemRegion *R = RegionOfInterest;
+ while (R != ArgRegion) {
+ if (!(isa<FieldRegion>(R) || isa<CXXBaseObjectRegion>(R)))
+ return false; // Pattern-matching failed.
+ Subregions.push_back(R);
+ R = dyn_cast<SubRegion>(R)->getSuperRegion();
+ }
+ bool IndirectReference = !Subregions.empty();
+
+ if (IndirectReference)
+ IndirectionLevel--; // Due to "->" symbol.
+
+ if (IsReference)
+ IndirectionLevel--; // Due to reference semantics.
+
+ bool ShouldSurround = IndirectReference && IndirectionLevel > 0;
+
+ if (ShouldSurround)
+ os << "(";
+ for (int i=0; i<IndirectionLevel; i++)
+ os << "*";
+ os << TopRegionName;
+ if (ShouldSurround)
+ os << ")";
+
+ for (auto I = Subregions.rbegin(), E = Subregions.rend(); I != E; ++I) {
+ if (auto *FR = dyn_cast<FieldRegion>(*I)) {
+ os << Sep;
+ FR->getDecl()->getDeclName().print(os, PP);
+ Sep = ".";
+ } else if (auto *CXXR = dyn_cast<CXXBaseObjectRegion>(*I)) {
+ continue; // Just keep going up to the base region.
+ } else {
+ llvm_unreachable("Previous check has missed an unexpected region");
+ }
+ }
+ return true;
+ }
+};
+
+} // namespace
+
+namespace {
+
class MacroNullReturnSuppressionVisitor final
: public BugReporterVisitorImpl<MacroNullReturnSuppressionVisitor> {
@@ -1215,6 +1485,8 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N,
if (R) {
// Mark both the variable region and its contents as interesting.
SVal V = LVState->getRawSVal(loc::MemRegionVal(R));
+ report.addVisitor(
+ llvm::make_unique<NoStoreFuncVisitor>(cast<SubRegion>(R)));
MacroNullReturnSuppressionVisitor::addMacroVisitorIfNecessary(
N, R, EnableNullFPSuppression, report, V);
OpenPOWER on IntegriCloud