r325976 - [analyzer] mark returns of functions where the region passed as parameter was not initialized

George Karpenkov via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 23 15:26:56 PST 2018


Author: george.karpenkov
Date: Fri Feb 23 15:26:56 2018
New Revision: 325976

URL: http://llvm.org/viewvc/llvm-project?rev=325976&view=rev
Log:
[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

Added:
    cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.c
    cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.cpp
    cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.m
Modified:
    cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
    cfe/trunk/test/Analysis/diagnostics/undef-value-param.c
    cfe/trunk/test/Analysis/diagnostics/undef-value-param.m

Modified: cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp?rev=325976&r1=325975&r2=325976&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Fri Feb 23 15:26:56 2018
@@ -184,6 +184,276 @@ static bool isFunctionMacroExpansion(Sou
 
 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(
     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);

Added: cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.c?rev=325976&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.c (added)
+++ cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.c Fri Feb 23 15:26:56 2018
@@ -0,0 +1,226 @@
+// RUN: %clang_analyze_cc1 -x c -analyzer-checker=core -analyzer-output=text -verify %s
+
+typedef __typeof(sizeof(int)) size_t;
+void *memset(void *__s, int __c, size_t __n);
+
+int initializer1(int *p, int x) {
+  if (x) { // expected-note{{Taking false branch}}
+    *p = 1;
+    return 0;
+  } else {
+    return 1; // expected-note {{Returning without writing to '*p'}}
+  }
+}
+
+int param_not_initialized_by_func() {
+  int p; // expected-note {{'p' declared without an initial value}}
+  int out = initializer1(&p, 0); // expected-note{{Calling 'initializer1'}}
+                                 // expected-note at -1{{Returning from 'initializer1'}}
+  return p; // expected-note{{Undefined or garbage value returned to caller}}
+            // expected-warning at -1{{Undefined or garbage value returned to caller}}
+}
+
+int param_initialized_properly() {
+  int p;
+  int out = initializer1(&p, 1);
+  return p; //no-warning
+}
+
+static int global;
+
+int initializer2(int **p, int x) {
+  if (x) { // expected-note{{Taking false branch}}
+    *p = &global;
+    return 0;
+  } else {
+    return 1; // expected-note {{Returning without writing to '*p'}}
+  }
+}
+
+int param_not_written_into_by_func() {
+  int *p = 0;                    // expected-note{{'p' initialized to a null pointer value}}
+  int out = initializer2(&p, 0); // expected-note{{Calling 'initializer2'}}
+                                 // expected-note at -1{{Returning from 'initializer2'}}
+  return *p;                     // expected-warning{{Dereference of null pointer (loaded from variable 'p')}}
+                                 // expected-note at -1{{Dereference of null pointer (loaded from variable 'p')}}
+}
+
+void initializer3(int *p, int param) {
+  if (param) // expected-note{{Taking false branch}}
+    *p = 0;
+} // expected-note{{Returning without writing to '*p'}}
+
+int param_written_into_by_void_func() {
+  int p;               // expected-note{{'p' declared without an initial value}}
+  initializer3(&p, 0); // expected-note{{Calling 'initializer3'}}
+                       // expected-note at -1{{Returning from 'initializer3'}}
+  return p;            // expected-warning{{Undefined or garbage value returned to caller}}
+                       // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
+
+void initializer4(int *p, int param) {
+  if (param) // expected-note{{Taking false branch}}
+    *p = 0;
+} // expected-note{{Returning without writing to '*p'}}
+
+void initializer5(int *p, int param) {
+  if (!param) // expected-note{{Taking false branch}}
+    *p = 0;
+} // expected-note{{Returning without writing to '*p'}}
+
+int multi_init_tries_func() {
+  int p;               // expected-note{{'p' declared without an initial value}}
+  initializer4(&p, 0); // expected-note{{Calling 'initializer4'}}
+                       // expected-note at -1{{Returning from 'initializer4'}}
+  initializer5(&p, 1); // expected-note{{Calling 'initializer5'}}
+                       // expected-note at -1{{Returning from 'initializer5'}}
+  return p;            // expected-warning{{Undefined or garbage value returned to caller}}
+                       // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
+
+int initializer6(const int *p) {
+  return 0;
+}
+
+int no_msg_on_const() {
+  int p; // expected-note{{'p' declared without an initial value}}
+  initializer6(&p);
+  return p; // expected-warning{{Undefined or garbage value returned to caller}}
+            // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
+
+typedef struct {
+  int x;
+} S;
+
+int initializer7(S *s, int param) {
+  if (param) { // expected-note{{Taking false branch}}
+    s->x = 0;
+    return 0;
+  }
+  return 1; // expected-note{{Returning without writing to 's->x'}}
+}
+
+int initialize_struct_field() {
+  S local;
+  initializer7(&local, 0); // expected-note{{Calling 'initializer7'}}
+                           // expected-note at -1{{Returning from 'initializer7'}}
+  return local.x;          // expected-warning{{Undefined or garbage value returned to caller}}
+                           // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
+
+void nullwriter(int **p) {
+  *p = 0; // expected-note{{Null pointer value stored to 'p'}}
+} // no extra note
+
+int usage() {
+  int x = 0;
+  int *p = &x;
+  nullwriter(&p); // expected-note{{Calling 'nullwriter'}}
+                  // expected-note at -1{{Returning from 'nullwriter'}}
+  return *p;      // expected-warning{{Dereference of null pointer (loaded from variable 'p')}}
+                  // expected-note at -1{{Dereference of null pointer (loaded from variable 'p')}}
+}
+
+typedef struct {
+  int x;
+  int y;
+} A;
+
+void partial_initializer(A *a) {
+  a->x = 0;
+} // expected-note{{Returning without writing to 'a->y'}}
+
+int use_partial_initializer() {
+  A a;
+  partial_initializer(&a); // expected-note{{Calling 'partial_initializer'}}
+                           // expected-note at -1{{Returning from 'partial_initializer'}}
+  return a.y;              // expected-warning{{Undefined or garbage value returned to caller}}
+                           // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
+
+typedef struct {
+  int x;
+  int y;
+} B;
+
+typedef struct {
+  B b;
+} C;
+
+void partial_nested_initializer(C *c) {
+  c->b.x = 0;
+} // expected-note{{Returning without writing to 'c->b.y'}}
+
+int use_partial_nested_initializer() {
+  B localB;
+  C localC;
+  localC.b = localB;
+  partial_nested_initializer(&localC); // expected-note{{Calling 'partial_nested_initializer'}}
+                                       // expected-note at -1{{Returning from 'partial_nested_initializer'}}
+  return localC.b.y;                   // expected-warning{{Undefined or garbage value returned to caller}}
+                                       // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
+
+void test_subregion_assignment(C* c) {
+  B b;
+  c->b = b;
+}
+
+int use_subregion_assignment() {
+  C c;
+  test_subregion_assignment(&c); // expected-note{{Calling 'test_subregion_assignment'}}
+                                 // expected-note at -1{{Returning from 'test_subregion_assignment'}}
+  return c.b.x; // expected-warning{{Undefined or garbage value returned to caller}}
+                // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
+
+int confusing_signature(int *);
+int confusing_signature(int *p) {
+  return 0; // expected-note{{Returning without writing to '*p'}}
+}
+
+int use_confusing_signature() {
+  int a; // expected-note {{'a' declared without an initial value}}
+  confusing_signature(&a); // expected-note{{Calling 'confusing_signature'}}
+                           // expected-note at -1{{Returning from 'confusing_signature'}}
+  return a; // expected-note{{Undefined or garbage value returned to caller}}
+            // expected-warning at -1{{Undefined or garbage value returned to caller}}
+}
+
+int coin();
+
+int multiindirection(int **p) {
+  if (coin()) // expected-note{{Assuming the condition is true}}
+              // expected-note at -1{{Taking true branch}}
+    return 1; // expected-note{{Returning without writing to '**p'}}
+  *(*p) = 0;
+  return 0;
+}
+
+int usemultiindirection() {
+  int a; // expected-note {{'a' declared without an initial value}}
+  int *b = &a;
+  multiindirection(&b); // expected-note{{Calling 'multiindirection'}}
+                        // expected-note at -1{{Returning from 'multiindirection'}}
+  return a; // expected-note{{Undefined or garbage value returned to caller}}
+            // expected-warning at -1{{Undefined or garbage value returned to caller}}
+}
+
+int indirectingstruct(S** s) {
+  if (coin()) // expected-note{{Assuming the condition is true}}
+              // expected-note at -1{{Taking true branch}}
+    return 1; // expected-note{{Returning without writing to '(*s)->x'}}
+
+  (*s)->x = 0;
+  return 0;
+}
+
+int useindirectingstruct() {
+  S s;
+  S* p = &s;
+  indirectingstruct(&p); //expected-note{{Calling 'indirectingstruct'}}
+                         //expected-note at -1{{Returning from 'indirectingstruct'}}
+  return s.x; // expected-warning{{Undefined or garbage value returned to caller}}
+              // expected-note at -1{{Undefined or garbage value returned to caller}}
+}

Added: cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.cpp?rev=325976&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.cpp (added)
+++ cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.cpp Fri Feb 23 15:26:56 2018
@@ -0,0 +1,147 @@
+// RUN: %clang_analyze_cc1 -x c++ -analyzer-checker=core -analyzer-output=text -verify %s
+
+int initializer1(int &p, int x) {
+  if (x) { // expected-note{{Taking false branch}}
+    p = 1;
+    return 0;
+  } else {
+    return 1; // expected-note {{Returning without writing to 'p'}}
+  }
+}
+
+int param_not_initialized_by_func() {
+  int p;                        // expected-note {{'p' declared without an initial value}}
+  int out = initializer1(p, 0); // expected-note{{Calling 'initializer1'}}
+                                // expected-note at -1{{Returning from 'initializer1'}}
+  return p;                     // expected-note{{Undefined or garbage value returned to caller}}
+                                // expected-warning at -1{{Undefined or garbage value returned to caller}}
+}
+
+struct S {
+  int initialize(int *p, int param) {
+    if (param) { //expected-note{{Taking false branch}}
+      *p = 1;
+      return 1;
+    }
+    return 0; // expected-note{{Returning without writing to '*p'}}
+  }
+};
+
+int use(S *s) {
+  int p;                //expected-note{{'p' declared without an initial value}}
+  s->initialize(&p, 0); //expected-note{{Calling 'S::initialize'}}
+                        //expected-note at -1{{Returning from 'S::initialize'}}
+  return p;             // expected-warning{{Undefined or garbage value returned to caller}}
+                        // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
+
+int initializer2(const int &p) {
+  return 0;
+}
+
+int no_msg_const_ref() {
+  int p; //expected-note{{'p' declared without an initial value}}
+  initializer2(p);
+  return p; // expected-warning{{Undefined or garbage value returned to caller}}
+            // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
+
+void nested() {}
+void init_in_nested_func(int **x) {
+  *x = 0; // expected-note{{Null pointer value stored to 'y'}}
+  nested();
+} // no-note
+
+int call_init_nested() {
+  int x = 0;
+  int *y = &x;
+  init_in_nested_func(&y); // expected-note{{Calling 'init_in_nested_func'}}
+                           // expected-note at -1{{Returning from 'init_in_nested_func'}}
+  return *y;               //expected-warning{{Dereference of null pointer (loaded from variable 'y')}}
+                           //expected-note at -1{{Dereference of null pointer (loaded from variable 'y')}}
+}
+
+struct A {
+  int x;
+  int y;
+};
+
+void partial_init_by_reference(A &a) {
+  a.x = 0;
+} // expected-note {{Returning without writing to 'a.y'}}
+
+int use_partial_init_by_reference() {
+  A a;
+  partial_init_by_reference(a); // expected-note{{Calling 'partial_init_by_reference'}}
+                                // expected-note at -1{{Returning from 'partial_init_by_reference'}}
+  return a.y;                   // expected-warning{{Undefined or garbage value returned to caller}}
+                                // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
+
+struct B : A {
+};
+
+void partially_init_inherited_struct(B *b) {
+  b->x = 0;
+} // expected-note{{Returning without writing to 'b->y'}}
+
+int use_partially_init_inherited_struct() {
+  B b;
+  partially_init_inherited_struct(&b); // expected-note{{Calling 'partially_init_inherited_struct'}}
+                                       // expected-note at -1{{Returning from 'partially_init_inherited_struct'}}
+  return b.y;                          // expected-warning{{Undefined or garbage value returned to caller}}
+                                       // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
+
+struct C {
+  int x;
+  int y;
+  C(int pX, int pY) : x(pX) {} // expected-note{{Returning without writing to 'this->y'}}
+};
+
+int use_constructor() {
+  C c(0, 0); // expected-note{{Calling constructor for 'C'}}
+             // expected-note at -1{{Returning from constructor for 'C'}}
+  return c.y; // expected-note{{Undefined or garbage value returned to caller}}
+              // expected-warning at -1{{Undefined or garbage value returned to caller}}
+}
+
+struct D {
+  void initialize(int *);
+};
+
+void D::initialize(int *p) {
+
+} // expected-note{{Returning without writing to '*p'}}
+
+int use_d_initializer(D* d) {
+  int p; // expected-note {{'p' declared without an initial value}}
+  d->initialize(&p); // expected-note{{Calling 'D::initialize'}}
+                     // expected-note at -1{{Returning from 'D::initialize'}}
+  return p;                     // expected-note{{Undefined or garbage value returned to caller}}
+                                // expected-warning at -1{{Undefined or garbage value returned to caller}}
+}
+
+int coin();
+
+struct S2 {
+  int x;
+};
+
+int pointerreference(S2* &s) {
+  if (coin()) // expected-note{{Assuming the condition is true}}
+              // expected-note at -1{{Taking true branch}}
+    return 1; // expected-note{{Returning without writing to 's->x'}}
+
+  s->x = 0;
+  return 0;
+}
+
+int usepointerreference() {
+  S2 s;
+  S2* p = &s;
+  pointerreference(p); //expected-note{{Calling 'pointerreference'}}
+                         //expected-note at -1{{Returning from 'pointerreference'}}
+  return s.x; // expected-warning{{Undefined or garbage value returned to caller}}
+              // expected-note at -1{{Undefined or garbage value returned to caller}}
+}

Added: cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.m?rev=325976&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.m (added)
+++ cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.m Fri Feb 23 15:26:56 2018
@@ -0,0 +1,46 @@
+// RUN: %clang_analyze_cc1 -x objective-c -analyzer-checker=core -analyzer-output=text -Wno-objc-root-class -fblocks -verify %s
+
+ at interface I
+- (int)initVar:(int *)var param:(int)param;
+ at end
+
+ at implementation I
+- (int)initVar:(int *)var param:(int)param {
+  if (param) { // expected-note{{Taking false branch}}
+    *var = 1;
+    return 0;
+  }
+  return 1; // expected-note{{Returning without writing to '*var'}}
+}
+ at end
+
+int foo(I *i) {
+  int x;                            //expected-note{{'x' declared without an initial value}}
+  int out = [i initVar:&x param:0]; //expected-note{{Calling 'initVar:param:'}}
+                                    //expected-note at -1{{Returning from 'initVar:param:'}}
+  if (out)                          // expected-note{{Taking true branch}}
+    return x;                       //expected-warning{{Undefined or garbage value returned to caller}}
+                                    //expected-note at -1{{Undefined or garbage value returned to caller}}
+  return 0;
+}
+
+int initializer1(int *p, int x) {
+  if (x) { // expected-note{{Taking false branch}}
+    *p = 1;
+    return 0;
+  } else {
+    return 1; // expected-note {{Returning without writing to '*p'}}
+  }
+}
+
+int initFromBlock() {
+  __block int z;
+  ^{                     // expected-note {{Calling anonymous block}}
+    int p;               // expected-note{{'p' declared without an initial value}}
+    initializer1(&p, 0); // expected-note{{Calling 'initializer1'}}
+                         // expected-note at -1{{Returning from 'initializer1'}}
+    z = p;               // expected-warning{{Assigned value is garbage or undefined}}
+                         // expected-note at -1{{Assigned value is garbage or undefined}}
+  }();
+  return z;
+}

Modified: cfe/trunk/test/Analysis/diagnostics/undef-value-param.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/diagnostics/undef-value-param.c?rev=325976&r1=325975&r2=325976&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/diagnostics/undef-value-param.c (original)
+++ cfe/trunk/test/Analysis/diagnostics/undef-value-param.c Fri Feb 23 15:26:56 2018
@@ -12,7 +12,7 @@ void foo(int c, int *x) {
     if (c)
            //expected-note at -1{{Assuming 'c' is not equal to 0}}
            //expected-note at -2{{Taking true branch}}
-        return;
+           return; // expected-note{{Returning without writing to '*x'}}
     *x = 5;
 }
 
@@ -51,7 +51,7 @@ void initStruct(int x, struct WithFields
   if (x <= 0) //expected-note {{Taking true branch}}
               //expected-note at -1 {{Assuming 'x' is <= 0}}
 
-    return;
+    return; //expected-note{{Returning without writing to 'X->f1'}}
   X->f1 = getValidPtr();
 }
 double testPassingParentRegionStruct(int x) {
@@ -293,12 +293,12 @@ double testPassingParentRegionStruct(int
 // CHECK-NEXT:          <array>
 // CHECK-NEXT:           <dict>
 // CHECK-NEXT:            <key>line</key><integer>15</integer>
-// CHECK-NEXT:            <key>col</key><integer>9</integer>
+// CHECK-NEXT:            <key>col</key><integer>12</integer>
 // CHECK-NEXT:            <key>file</key><integer>0</integer>
 // CHECK-NEXT:           </dict>
 // CHECK-NEXT:           <dict>
 // CHECK-NEXT:            <key>line</key><integer>15</integer>
-// CHECK-NEXT:            <key>col</key><integer>14</integer>
+// CHECK-NEXT:            <key>col</key><integer>17</integer>
 // CHECK-NEXT:            <key>file</key><integer>0</integer>
 // CHECK-NEXT:           </dict>
 // CHECK-NEXT:          </array>
@@ -309,6 +309,20 @@ double testPassingParentRegionStruct(int
 // CHECK-NEXT:      <key>kind</key><string>event</string>
 // CHECK-NEXT:      <key>location</key>
 // CHECK-NEXT:      <dict>
+// CHECK-NEXT:       <key>line</key><integer>15</integer>
+// CHECK-NEXT:       <key>col</key><integer>12</integer>
+// CHECK-NEXT:       <key>file</key><integer>0</integer>
+// CHECK-NEXT:      </dict>
+// CHECK-NEXT:      <key>depth</key><integer>1</integer>
+// CHECK-NEXT:      <key>extended_message</key>
+// CHECK-NEXT:      <string>Returning without writing to '*x'</string>
+// CHECK-NEXT:      <key>message</key>
+// CHECK-NEXT:      <string>Returning without writing to '*x'</string>
+// CHECK-NEXT:     </dict>
+// CHECK-NEXT:     <dict>
+// CHECK-NEXT:      <key>kind</key><string>event</string>
+// CHECK-NEXT:      <key>location</key>
+// CHECK-NEXT:      <dict>
 // CHECK-NEXT:       <key>line</key><integer>22</integer>
 // CHECK-NEXT:       <key>col</key><integer>5</integer>
 // CHECK-NEXT:       <key>file</key><integer>0</integer>
@@ -1044,6 +1058,20 @@ double testPassingParentRegionStruct(int
 // CHECK-NEXT:     </dict>
 // CHECK-NEXT:     <dict>
 // CHECK-NEXT:      <key>kind</key><string>event</string>
+// CHECK-NEXT:      <key>location</key>
+// CHECK-NEXT:      <dict>
+// CHECK-NEXT:       <key>line</key><integer>54</integer>
+// CHECK-NEXT:       <key>col</key><integer>5</integer>
+// CHECK-NEXT:       <key>file</key><integer>0</integer>
+// CHECK-NEXT:      </dict>
+// CHECK-NEXT:      <key>depth</key><integer>1</integer>
+// CHECK-NEXT:      <key>extended_message</key>
+// CHECK-NEXT:      <string>Returning without writing to 'X->f1'</string>
+// CHECK-NEXT:      <key>message</key>
+// CHECK-NEXT:      <string>Returning without writing to 'X->f1'</string>
+// CHECK-NEXT:     </dict>
+// CHECK-NEXT:     <dict>
+// CHECK-NEXT:      <key>kind</key><string>event</string>
 // CHECK-NEXT:      <key>location</key>
 // CHECK-NEXT:      <dict>
 // CHECK-NEXT:       <key>line</key><integer>60</integer>

Modified: cfe/trunk/test/Analysis/diagnostics/undef-value-param.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/diagnostics/undef-value-param.m?rev=325976&r1=325975&r2=325976&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/diagnostics/undef-value-param.m (original)
+++ cfe/trunk/test/Analysis/diagnostics/undef-value-param.m Fri Feb 23 15:26:56 2018
@@ -69,7 +69,7 @@ static void CreateRefUndef(SCDynamicStor
              //expected-note at -1{{Assuming 'err' is not equal to 0}}
              //expected-note at -2{{Taking true branch}}
     CFRelease(ref);
-    return;
+    return; // expected-note{{Returning without writing to '*storeRef'}}
   }
   *storeRef = ref;
 }
@@ -831,6 +831,54 @@ static void CreateRefUndef(SCDynamicStor
 // CHECK-NEXT:       </array>
 // CHECK-NEXT:     </dict>
 // CHECK-NEXT:     <dict>
+// CHECK-NEXT:      <key>kind</key><string>control</string>
+// CHECK-NEXT:      <key>edges</key>
+// CHECK-NEXT:       <array>
+// CHECK-NEXT:        <dict>
+// CHECK-NEXT:         <key>start</key>
+// CHECK-NEXT:          <array>
+// CHECK-NEXT:           <dict>
+// CHECK-NEXT:            <key>line</key><integer>71</integer>
+// CHECK-NEXT:            <key>col</key><integer>5</integer>
+// CHECK-NEXT:            <key>file</key><integer>0</integer>
+// CHECK-NEXT:           </dict>
+// CHECK-NEXT:           <dict>
+// CHECK-NEXT:            <key>line</key><integer>71</integer>
+// CHECK-NEXT:            <key>col</key><integer>13</integer>
+// CHECK-NEXT:            <key>file</key><integer>0</integer>
+// CHECK-NEXT:           </dict>
+// CHECK-NEXT:          </array>
+// CHECK-NEXT:         <key>end</key>
+// CHECK-NEXT:          <array>
+// CHECK-NEXT:           <dict>
+// CHECK-NEXT:            <key>line</key><integer>72</integer>
+// CHECK-NEXT:            <key>col</key><integer>5</integer>
+// CHECK-NEXT:            <key>file</key><integer>0</integer>
+// CHECK-NEXT:           </dict>
+// CHECK-NEXT:           <dict>
+// CHECK-NEXT:            <key>line</key><integer>72</integer>
+// CHECK-NEXT:            <key>col</key><integer>10</integer>
+// CHECK-NEXT:            <key>file</key><integer>0</integer>
+// CHECK-NEXT:           </dict>
+// CHECK-NEXT:          </array>
+// CHECK-NEXT:        </dict>
+// CHECK-NEXT:       </array>
+// CHECK-NEXT:     </dict>
+// CHECK-NEXT:     <dict>
+// CHECK-NEXT:      <key>kind</key><string>event</string>
+// CHECK-NEXT:      <key>location</key>
+// CHECK-NEXT:      <dict>
+// CHECK-NEXT:       <key>line</key><integer>72</integer>
+// CHECK-NEXT:       <key>col</key><integer>5</integer>
+// CHECK-NEXT:       <key>file</key><integer>0</integer>
+// CHECK-NEXT:      </dict>
+// CHECK-NEXT:      <key>depth</key><integer>1</integer>
+// CHECK-NEXT:      <key>extended_message</key>
+// CHECK-NEXT:      <string>Returning without writing to '*storeRef'</string>
+// CHECK-NEXT:      <key>message</key>
+// CHECK-NEXT:      <string>Returning without writing to '*storeRef'</string>
+// CHECK-NEXT:     </dict>
+// CHECK-NEXT:     <dict>
 // CHECK-NEXT:      <key>kind</key><string>event</string>
 // CHECK-NEXT:      <key>location</key>
 // CHECK-NEXT:      <dict>




More information about the cfe-commits mailing list