Charusso updated this revision to Diff 319940.
Charusso marked 6 inline comments as done.
Charusso added a comment.

- Fix everything.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D69726/new/

https://reviews.llvm.org/D69726

Files:
  clang/docs/analyzer/developer-docs/DebugChecks.rst
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h
  clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
  clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
  clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
  clang/lib/StaticAnalyzer/Core/DynamicSize.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  clang/lib/StaticAnalyzer/Core/MemRegion.cpp
  clang/test/Analysis/explain-svals.cpp
  clang/test/Analysis/expr-inspection.cpp
  clang/test/Analysis/memory-model.cpp

Index: clang/test/Analysis/memory-model.cpp
===================================================================
--- /dev/null
+++ clang/test/Analysis/memory-model.cpp
@@ -0,0 +1,157 @@
+// RUN: %clang_analyze_cc1 -std=c++20 \
+// RUN:  -analyzer-checker=core,unix,cplusplus,debug.ExprInspection \
+// RUN:  -triple x86_64-unknown-linux-gnu \
+// RUN:  -verify %s
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+typedef __SIZE_TYPE__ size_t;
+void *malloc(size_t);
+void *alloca(size_t);
+void *realloc(void *ptr, size_t size);
+void *calloc(size_t number, size_t size);
+void free(void *);
+
+struct S {
+  int f;
+};
+
+void clang_analyzer_dump(int);
+void clang_analyzer_dump(const void *);
+void clang_analyzer_dumpExtent(int);
+void clang_analyzer_dumpExtent(const void *);
+void clang_analyzer_dumpElementCount(int);
+void clang_analyzer_dumpElementCount(const void *);
+
+int clang_analyzer_getExtent(void *);
+void clang_analyzer_eval(bool);
+
+void var_simple_ref() {
+  int a = 13;
+  clang_analyzer_dump(&a);             // expected-warning {{a}}
+  clang_analyzer_dumpExtent(&a);       // expected-warning {{4 S64b}}
+  clang_analyzer_dumpElementCount(&a); // expected-warning {{1 S64b}}
+}
+
+void var_simple_ptr(int *a) {
+  clang_analyzer_dump(a);             // expected-warning {{SymRegion{reg_$0<int * a>}}}
+  clang_analyzer_dumpExtent(a);       // expected-warning {{extent_$1{SymRegion{reg_$0<int * a>}}}}
+  clang_analyzer_dumpElementCount(a); // expected-warning {{(extent_$1{SymRegion{reg_$0<int * a>}}) / 4}}
+}
+
+void var_array() {
+  int a[] = {1, 2, 3};
+  clang_analyzer_dump(a);             // expected-warning {{Element{a,0 S64b,int}}}
+  clang_analyzer_dumpExtent(a);       // expected-warning {{12 S64b}}
+  clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}}
+}
+
+void string() {
+  clang_analyzer_dump("foo");             // expected-warning {{Element{"foo",0 S64b,char}}}
+  clang_analyzer_dumpExtent("foo");       // expected-warning {{4 S64b}}
+  clang_analyzer_dumpElementCount("foo"); // expected-warning {{4 S64b}}
+}
+
+void struct_simple_ptr(S *a) {
+  clang_analyzer_dump(a);             // expected-warning {{SymRegion{reg_$0<struct S * a>}}}
+  clang_analyzer_dumpExtent(a);       // expected-warning {{extent_$1{SymRegion{reg_$0<struct S * a>}}}}
+  clang_analyzer_dumpElementCount(a); // expected-warning {{(extent_$1{SymRegion{reg_$0<struct S * a>}}) / 4}}
+}
+
+void field_ref(S a) {
+  clang_analyzer_dump(&a.f);             // expected-warning {{a.f}}
+  clang_analyzer_dumpExtent(&a.f);       // expected-warning {{4 S64b}}
+  clang_analyzer_dumpElementCount(&a.f); // expected-warning {{1 S64b}}
+}
+
+void field_ptr(S *a) {
+  clang_analyzer_dump(&a->f);             // expected-warning {{SymRegion{reg_$0<struct S * a>}.f}}
+  clang_analyzer_dumpExtent(&a->f);       // expected-warning {{4 S64b}}
+  clang_analyzer_dumpElementCount(&a->f); // expected-warning {{1 S64b}}
+}
+
+void symbolic_array() {
+  int *a = new int[3];
+  clang_analyzer_dump(a);             // expected-warning {{Element{HeapSymRegion{conj}}
+  clang_analyzer_dumpExtent(a);       // expected-warning {{12 S64b}}
+  clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}}
+  delete[] a;
+}
+
+void symbolic_placement_new() {
+  char *buf = new char[sizeof(int) * 3];
+  int *a = new (buf) int(12);
+  clang_analyzer_dump(a);             // expected-warning {{Element{HeapSymRegion{conj}}
+  clang_analyzer_dumpExtent(a);       // expected-warning {{12 S64b}}
+  clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}}
+  delete[] buf;
+}
+
+void symbolic_malloc() {
+  int *a = (int *)malloc(12);
+  clang_analyzer_dump(a);             // expected-warning {{Element{HeapSymRegion{conj}}
+  clang_analyzer_dumpExtent(a);       // expected-warning {{12 U64b}}
+  clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}}
+  free(a);
+}
+
+void symbolic_alloca() {
+  int *a = (int *)alloca(12);
+  clang_analyzer_dump(a);             // expected-warning {{Element{HeapSymRegion{conj}}
+  clang_analyzer_dumpExtent(a);       // expected-warning {{12 U64b}}
+  clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}}
+}
+
+void symbolic_complex() {
+  int *a = (int *)malloc(4);
+  clang_analyzer_dumpExtent(a);       // expected-warning {{4 U64b}}
+  clang_analyzer_dumpElementCount(a); // expected-warning {{1 S64b}}
+
+  int *b = (int *)realloc(a, sizeof(int) * 2);
+  clang_analyzer_dumpExtent(b);       // expected-warning {{8 U64b}}
+  clang_analyzer_dumpElementCount(b); // expected-warning {{2 S64b}}
+  free(b);
+
+  int *c = (int *)calloc(3, 4);
+  clang_analyzer_dumpExtent(c);       // expected-warning {{12 U64b}}
+  clang_analyzer_dumpElementCount(c); // expected-warning {{3 S64b}}
+  free(c);
+}
+
+void signedness_equality() {
+  char *a = new char[sizeof(char) * 13];
+  char *b = (char *)malloc(13);
+
+  clang_analyzer_dump(clang_analyzer_getExtent(a)); // expected-warning {{13 S64b}}
+  clang_analyzer_dump(clang_analyzer_getExtent(b)); // expected-warning {{13 U64b}}
+  clang_analyzer_eval(clang_analyzer_getExtent(a) ==
+                      clang_analyzer_getExtent(b));
+  // expected-warning@-2 {{TRUE}}
+
+  delete[] a;
+  free(b);
+}
+
+void default_new_aligned() {
+  struct alignas(32) S {};
+
+  S *a = new S[10];
+
+  clang_analyzer_dump(a);             // expected-warning {{Element{HeapSymRegion{conj}}
+  clang_analyzer_dumpExtent(a);       // expected-warning {{320 S64b}}
+  clang_analyzer_dumpElementCount(a); // expected-warning {{10 S64b}}
+
+  delete[] a;
+}
+
+void *operator new[](std::size_t, std::align_val_t, bool hack) throw();
+
+void user_defined_new() {
+  int *a = new (std::align_val_t(32), true) int[10];
+
+  clang_analyzer_dump(a);             // expected-warning {{Element{SymRegion{conj}}
+  clang_analyzer_dumpExtent(a);       // expected-warning-re {{{{^extent_\$[0-9]\{SymRegion{conj}}}}
+  clang_analyzer_dumpElementCount(a); // expected-warning-re {{{{^\(extent_\$[0-9]\{SymRegion{conj.*\) / 4}}}}
+
+  operator delete[](a, std::align_val_t(32));
+}
Index: clang/test/Analysis/expr-inspection.cpp
===================================================================
--- clang/test/Analysis/expr-inspection.cpp
+++ clang/test/Analysis/expr-inspection.cpp
@@ -12,7 +12,7 @@
 
 void foo(int x, unsigned y) {
   clang_analyzer_denote(); // expected-warning{{clang_analyzer_denote() requires a symbol and a string literal}}
-  clang_analyzer_express(); // expected-warning{{clang_analyzer_express() requires a symbol}}
+  clang_analyzer_express(); // expected-warning{{Missing argument}}
 
   clang_analyzer_denote(x); // expected-warning{{clang_analyzer_denote() requires a symbol and a string literal}}
   clang_analyzer_express(x); // expected-warning{{Unable to express}}
Index: clang/test/Analysis/explain-svals.cpp
===================================================================
--- clang/test/Analysis/explain-svals.cpp
+++ clang/test/Analysis/explain-svals.cpp
@@ -54,7 +54,7 @@
   int *x = new int[ext];
   clang_analyzer_explain(x); // expected-warning-re{{{{^pointer to element of type 'int' with index 0 of heap segment that starts at symbol of type 'int \*' conjured at statement 'new int \[ext\]'$}}}}
   // Sic! What gets computed is the extent of the element-region.
-  clang_analyzer_explain(clang_analyzer_getExtent(x)); // expected-warning-re{{{{^signed 32-bit integer '4'$}}}}
+  clang_analyzer_explain(clang_analyzer_getExtent(x)); // expected-warning-re{{{{^\(argument 'ext'\) \* 4$}}}}
   delete[] x;
 }
 
Index: clang/lib/StaticAnalyzer/Core/MemRegion.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ clang/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -28,6 +28,7 @@
 #include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
@@ -729,13 +730,6 @@
 // MemRegionManager methods.
 //===----------------------------------------------------------------------===//
 
-static DefinedOrUnknownSVal getTypeSize(QualType Ty, ASTContext &Ctx,
-                                          SValBuilder &SVB) {
-  CharUnits Size = Ctx.getTypeSizeInChars(Ty);
-  QualType SizeTy = SVB.getArrayIndexType();
-  return SVB.makeIntVal(Size.getQuantity(), SizeTy);
-}
-
 DefinedOrUnknownSVal MemRegionManager::getStaticSize(const MemRegion *MR,
                                                      SValBuilder &SVB) const {
   const auto *SR = cast<SubRegion>(MR);
@@ -766,7 +760,7 @@
     if (Ty->isIncompleteType())
       return UnknownVal();
 
-    return getTypeSize(Ty, Ctx, SVB);
+    return getElementSize(Ty, SVB);
   }
   case MemRegion::FieldRegionKind: {
     // Force callers to deal with bitfields explicitly.
@@ -774,7 +768,7 @@
       return UnknownVal();
 
     QualType Ty = cast<TypedValueRegion>(SR)->getDesugaredValueType(Ctx);
-    DefinedOrUnknownSVal Size = getTypeSize(Ty, Ctx, SVB);
+    DefinedOrUnknownSVal Size = getElementSize(Ty, SVB);
 
     // A zero-length array at the end of a struct often stands for dynamically
     // allocated extra memory.
Index: clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -18,6 +18,7 @@
 #include "clang/Analysis/ConstructionContext.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/Statistic.h"
@@ -689,16 +690,30 @@
 
     // See if we need to conjure a heap pointer instead of
     // a regular unknown pointer.
-    bool IsHeapPointer = false;
-    if (const auto *CNE = dyn_cast<CXXNewExpr>(E))
-      if (CNE->getOperatorNew()->isReplaceableGlobalAllocationFunction()) {
-        // FIXME: Delegate this to evalCall in MallocChecker?
-        IsHeapPointer = true;
+    const auto *CNE = dyn_cast<CXXNewExpr>(E);
+    if (CNE && CNE->getOperatorNew()->isReplaceableGlobalAllocationFunction()) {
+      R = svalBuilder.getConjuredHeapSymbolVal(E, LCtx, Count);
+      const MemRegion *MR = R.getAsRegion()->StripCasts();
+
+      // Store the extent of the allocated object(s).
+      SVal ElementCount;
+      if (const Expr *SizeExpr = CNE->getArraySize().getValueOr(nullptr)) {
+        ElementCount = State->getSVal(SizeExpr, LCtx);
+      } else {
+        ElementCount = svalBuilder.makeIntVal(1, /*IsUnsigned=*/true);
       }
 
-    R = IsHeapPointer ? svalBuilder.getConjuredHeapSymbolVal(E, LCtx, Count)
-                      : svalBuilder.conjureSymbolVal(nullptr, E, LCtx, ResultTy,
-                                                     Count);
+      SVal ElementSize = getElementSize(CNE->getAllocatedType(), svalBuilder);
+
+      SVal Size =
+          svalBuilder.evalBinOp(State, BO_Mul, ElementCount, ElementSize,
+                                svalBuilder.getArrayIndexType());
+
+      State = setDynamicSize(State, MR, Size.castAs<DefinedOrUnknownSVal>(),
+                             svalBuilder);
+    } else {
+      R = svalBuilder.conjureSymbolVal(nullptr, E, LCtx, ResultTy, Count);
+    }
   }
   return State->BindExpr(E, LCtx, R);
 }
Index: clang/lib/StaticAnalyzer/Core/DynamicSize.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/DynamicSize.cpp
+++ clang/lib/StaticAnalyzer/Core/DynamicSize.cpp
@@ -19,32 +19,43 @@
 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
 
+REGISTER_MAP_WITH_PROGRAMSTATE(DynamicSizeMap, const clang::ento::MemRegion *,
+                               clang::ento::DefinedOrUnknownSVal)
+
 namespace clang {
 namespace ento {
 
 DefinedOrUnknownSVal getDynamicSize(ProgramStateRef State, const MemRegion *MR,
                                     SValBuilder &SVB) {
+  MR = MR->StripCasts();
+
+  if (const DefinedOrUnknownSVal *Size = State->get<DynamicSizeMap>(MR))
+    return *Size;
+
   return MR->getMemRegionManager().getStaticSize(MR, SVB);
 }
 
+DefinedOrUnknownSVal getElementSize(QualType Ty, SValBuilder &SVB) {
+  return SVB.makeIntVal(SVB.getContext().getTypeSizeInChars(Ty).getQuantity(),
+                        SVB.getArrayIndexType());
+}
+
 DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State,
                                             const MemRegion *MR,
                                             SValBuilder &SVB,
                                             QualType ElementTy) {
-  MemRegionManager &MemMgr = MR->getMemRegionManager();
-  ASTContext &Ctx = MemMgr.getContext();
+  MR = MR->StripCasts();
 
   DefinedOrUnknownSVal Size = getDynamicSize(State, MR, SVB);
-  SVal ElementSizeV = SVB.makeIntVal(
-      Ctx.getTypeSizeInChars(ElementTy).getQuantity(), SVB.getArrayIndexType());
+  SVal ElementSize = getElementSize(ElementTy, SVB);
 
-  SVal DivisionV =
-      SVB.evalBinOp(State, BO_Div, Size, ElementSizeV, SVB.getArrayIndexType());
+  SVal ElementCount =
+      SVB.evalBinOp(State, BO_Div, Size, ElementSize, SVB.getArrayIndexType());
 
-  return DivisionV.castAs<DefinedOrUnknownSVal>();
+  return ElementCount.castAs<DefinedOrUnknownSVal>();
 }
 
-SVal getDynamicSizeWithOffset(ProgramStateRef State, const SVal &BufV) {
+SVal getDynamicSizeWithOffset(ProgramStateRef State, SVal BufV) {
   SValBuilder &SvalBuilder = State->getStateManager().getSValBuilder();
   const MemRegion *MRegion = BufV.getAsRegion();
   if (!MRegion)
@@ -67,5 +78,15 @@
                                SvalBuilder.getArrayIndexType());
 }
 
+ProgramStateRef setDynamicSize(ProgramStateRef State, const MemRegion *MR,
+                               DefinedOrUnknownSVal Size, SValBuilder &SVB) {
+  MR = MR->StripCasts();
+
+  if (Size.isUnknown())
+    return State;
+
+  return State->set<DynamicSizeMap>(MR->StripCasts(), Size);
+}
+
 } // namespace ento
 } // namespace clang
Index: clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
@@ -285,21 +285,10 @@
     return;
   }
 
-  // VLASizeChecker is responsible for defining the extent of the array being
-  // declared. We do this by multiplying the array length by the element size,
-  // then matching that with the array region's extent symbol.
-
+  // VLASizeChecker is responsible for defining the extent of the array.
   if (VD) {
-    // Assume that the array's size matches the region size.
-    const LocationContext *LC = C.getLocationContext();
-    DefinedOrUnknownSVal DynSize =
-        getDynamicSize(State, State->getRegion(VD, LC), SVB);
-
-    DefinedOrUnknownSVal SizeIsKnown = SVB.evalEQ(State, DynSize, *ArraySizeNL);
-    State = State->assume(SizeIsKnown, true);
-
-    // Assume should not fail at this point.
-    assert(State);
+    State = setDynamicSize(State, State->getRegion(VD, C.getLocationContext()),
+                           ArraySize.castAs<DefinedOrUnknownSVal>(), SVB);
   }
 
   // Remember our assumptions!
Index: clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -509,10 +509,6 @@
                                       ProgramStateRef State,
                                       AllocationFamily Family);
 
-  LLVM_NODISCARD
-  static ProgramStateRef addExtentSize(CheckerContext &C, const CXXNewExpr *NE,
-                                       ProgramStateRef State, SVal Target);
-
   // Check if this malloc() for special flags. At present that means M_ZERO or
   // __GFP_ZERO (in which case, treat it like calloc).
   LLVM_NODISCARD
@@ -1424,7 +1420,6 @@
   // existing binding.
   SVal Target = Call.getObjectUnderConstruction();
   State = MallocUpdateRefState(C, NE, State, Family, Target);
-  State = addExtentSize(C, NE, State, Target);
   State = ProcessZeroAllocCheck(Call, 0, State, Target);
   return State;
 }
@@ -1439,52 +1434,6 @@
   }
 }
 
-// Sets the extent value of the MemRegion allocated by
-// new expression NE to its size in Bytes.
-//
-ProgramStateRef MallocChecker::addExtentSize(CheckerContext &C,
-                                             const CXXNewExpr *NE,
-                                             ProgramStateRef State,
-                                             SVal Target) {
-  if (!State)
-    return nullptr;
-  SValBuilder &svalBuilder = C.getSValBuilder();
-  SVal ElementCount;
-  const SubRegion *Region;
-  if (NE->isArray()) {
-    const Expr *SizeExpr = *NE->getArraySize();
-    ElementCount = C.getSVal(SizeExpr);
-    // Store the extent size for the (symbolic)region
-    // containing the elements.
-    Region = Target.getAsRegion()
-                 ->castAs<SubRegion>()
-                 ->StripCasts()
-                 ->castAs<SubRegion>();
-  } else {
-    ElementCount = svalBuilder.makeIntVal(1, true);
-    Region = Target.getAsRegion()->castAs<SubRegion>();
-  }
-
-  // Set the region's extent equal to the Size in Bytes.
-  QualType ElementType = NE->getAllocatedType();
-  ASTContext &AstContext = C.getASTContext();
-  CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType);
-
-  if (ElementCount.getAs<NonLoc>()) {
-    DefinedOrUnknownSVal DynSize = getDynamicSize(State, Region, svalBuilder);
-
-    // size in Bytes = ElementCount*TypeSize
-    SVal SizeInBytes = svalBuilder.evalBinOpNN(
-        State, BO_Mul, ElementCount.castAs<NonLoc>(),
-        svalBuilder.makeArrayIndex(TypeSize.getQuantity()),
-        svalBuilder.getArrayIndexType());
-    DefinedOrUnknownSVal DynSizeMatchesSize = svalBuilder.evalEQ(
-        State, DynSize, SizeInBytes.castAs<DefinedOrUnknownSVal>());
-    State = State->assume(DynSizeMatchesSize, true);
-  }
-  return State;
-}
-
 static bool isKnownDeallocObjCMethodName(const ObjCMethodCall &Call) {
   // If the first selector piece is one of the names below, assume that the
   // object takes ownership of the memory, promising to eventually deallocate it
@@ -1588,21 +1537,9 @@
   // Fill the region with the initialization value.
   State = State->bindDefaultInitial(RetVal, Init, LCtx);
 
-  // Set the region's extent equal to the Size parameter.
-  const SymbolicRegion *R =
-      dyn_cast_or_null<SymbolicRegion>(RetVal.getAsRegion());
-  if (!R)
-    return nullptr;
-  if (Optional<DefinedOrUnknownSVal> DefinedSize =
-          Size.getAs<DefinedOrUnknownSVal>()) {
-    DefinedOrUnknownSVal DynSize = getDynamicSize(State, R, svalBuilder);
-
-    DefinedOrUnknownSVal DynSizeMatchesSize =
-        svalBuilder.evalEQ(State, DynSize, *DefinedSize);
-
-    State = State->assume(DynSizeMatchesSize, true);
-    assert(State);
-  }
+  // Set the region's extent.
+  State = setDynamicSize(State, RetVal.getAsRegion(),
+                         Size.castAs<DefinedOrUnknownSVal>(), svalBuilder);
 
   return MallocUpdateRefState(C, CE, State, Family);
 }
Index: clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
@@ -22,8 +22,8 @@
 using namespace ento;
 
 namespace {
-class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols,
-                                             check::EndAnalysis> {
+class ExprInspectionChecker
+    : public Checker<eval::Call, check::DeadSymbols, check::EndAnalysis> {
   mutable std::unique_ptr<BugType> BT;
 
   // These stats are per-analysis, not per-branch, hence they shouldn't
@@ -44,6 +44,8 @@
   void analyzerExplain(const CallExpr *CE, CheckerContext &C) const;
   void analyzerPrintState(const CallExpr *CE, CheckerContext &C) const;
   void analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const;
+  void analyzerDumpExtent(const CallExpr *CE, CheckerContext &C) const;
+  void analyzerDumpElementCount(const CallExpr *CE, CheckerContext &C) const;
   void analyzerHashDump(const CallExpr *CE, CheckerContext &C) const;
   void analyzerDenote(const CallExpr *CE, CheckerContext &C) const;
   void analyzerExpress(const CallExpr *CE, CheckerContext &C) const;
@@ -55,17 +57,19 @@
   // Optional parameter `ExprVal` for expression value to be marked interesting.
   ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C,
                           Optional<SVal> ExprVal = None) const;
-  ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR,
-                          ExplodedNode *N,
+  ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, ExplodedNode *N,
                           Optional<SVal> ExprVal = None) const;
 
+  const Expr *getArgExpr(const CallExpr *CE, CheckerContext &C) const;
+  const MemRegion *getArgRegion(const CallExpr *CE, CheckerContext &C) const;
+
 public:
   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
   void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
                         ExprEngine &Eng) const;
 };
-}
+} // namespace
 
 REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef)
 REGISTER_MAP_WITH_PROGRAMSTATE(DenotedSymbols, SymbolRef, const StringLiteral *)
@@ -90,6 +94,10 @@
                 &ExprInspectionChecker::analyzerWarnOnDeadSymbol)
           .StartsWith("clang_analyzer_explain",
                       &ExprInspectionChecker::analyzerExplain)
+          .Case("clang_analyzer_dumpExtent",
+                &ExprInspectionChecker::analyzerDumpExtent)
+          .Case("clang_analyzer_dumpElementCount",
+                &ExprInspectionChecker::analyzerDumpElementCount)
           .StartsWith("clang_analyzer_dump",
                       &ExprInspectionChecker::analyzerDump)
           .Case("clang_analyzer_getExtent",
@@ -131,7 +139,7 @@
 
   ProgramStateRef StTrue, StFalse;
   std::tie(StTrue, StFalse) =
-    State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>());
+      State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>());
 
   if (StTrue) {
     if (StFalse)
@@ -155,8 +163,7 @@
 }
 
 ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
-                                               BugReporter &BR,
-                                               ExplodedNode *N,
+                                               BugReporter &BR, ExplodedNode *N,
                                                Optional<SVal> ExprVal) const {
   if (!N)
     return nullptr;
@@ -172,6 +179,30 @@
   return N;
 }
 
+const Expr *ExprInspectionChecker::getArgExpr(const CallExpr *CE,
+                                              CheckerContext &C) const {
+  if (CE->getNumArgs() == 0) {
+    reportBug("Missing argument", C);
+    return nullptr;
+  }
+  return CE->getArg(0);
+}
+
+const MemRegion *ExprInspectionChecker::getArgRegion(const CallExpr *CE,
+                                                     CheckerContext &C) const {
+  const Expr *Arg = getArgExpr(CE, C);
+  if (!Arg)
+    return nullptr;
+
+  const MemRegion *MR = C.getSVal(Arg).getAsRegion();
+  if (!MR) {
+    reportBug("Cannot obtain the region", C);
+    return nullptr;
+  }
+
+  return MR;
+}
+
 void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
                                          CheckerContext &C) const {
   const LocationContext *LC = C.getPredecessor()->getLocationContext();
@@ -215,24 +246,22 @@
 
 void ExprInspectionChecker::analyzerExplain(const CallExpr *CE,
                                             CheckerContext &C) const {
-  if (CE->getNumArgs() == 0) {
-    reportBug("Missing argument for explaining", C);
+  const Expr *Arg = getArgExpr(CE, C);
+  if (!Arg)
     return;
-  }
 
-  SVal V = C.getSVal(CE->getArg(0));
+  SVal V = C.getSVal(Arg);
   SValExplainer Ex(C.getASTContext());
   reportBug(Ex.Visit(V), C);
 }
 
 void ExprInspectionChecker::analyzerDump(const CallExpr *CE,
                                          CheckerContext &C) const {
-  if (CE->getNumArgs() == 0) {
-    reportBug("Missing argument for dumping", C);
+  const Expr *Arg = getArgExpr(CE, C);
+  if (!Arg)
     return;
-  }
 
-  SVal V = C.getSVal(CE->getArg(0));
+  SVal V = C.getSVal(Arg);
 
   llvm::SmallString<32> Str;
   llvm::raw_svector_ostream OS(Str);
@@ -242,16 +271,9 @@
 
 void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE,
                                               CheckerContext &C) const {
-  if (CE->getNumArgs() == 0) {
-    reportBug("Missing region for obtaining extent", C);
+  const MemRegion *MR = getArgRegion(CE, C);
+  if (!MR)
     return;
-  }
-
-  auto MR = dyn_cast_or_null<SubRegion>(C.getSVal(CE->getArg(0)).getAsRegion());
-  if (!MR) {
-    reportBug("Obtaining extent of a non-region", C);
-    return;
-  }
 
   ProgramStateRef State = C.getState();
   DefinedOrUnknownSVal Size = getDynamicSize(State, MR, C.getSValBuilder());
@@ -260,6 +282,46 @@
   C.addTransition(State);
 }
 
+void ExprInspectionChecker::analyzerDumpExtent(const CallExpr *CE,
+                                               CheckerContext &C) const {
+  const MemRegion *MR = getArgRegion(CE, C);
+  if (!MR)
+    return;
+
+  DefinedOrUnknownSVal Size =
+      getDynamicSize(C.getState(), MR, C.getSValBuilder());
+
+  SmallString<128> Msg;
+  llvm::raw_svector_ostream Out(Msg);
+  Out << Size;
+  reportBug(Out.str(), C);
+}
+
+void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE,
+                                                     CheckerContext &C) const {
+  const MemRegion *MR = getArgRegion(CE, C);
+  if (!MR)
+    return;
+
+  QualType ElementTy;
+  if (const auto *TVR = MR->getAs<TypedValueRegion>()) {
+    ElementTy = TVR->getValueType();
+  } else {
+    ElementTy =
+        MR->castAs<SymbolicRegion>()->getSymbol()->getType()->getPointeeType();
+  }
+
+  assert(!ElementTy->isPointerType());
+
+  DefinedOrUnknownSVal ElementCount =
+      getDynamicElementCount(C.getState(), MR, C.getSValBuilder(), ElementTy);
+
+  SmallString<128> Msg;
+  llvm::raw_svector_ostream Out(Msg);
+  Out << ElementCount;
+  reportBug(Out.str(), C);
+}
+
 void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE,
                                                CheckerContext &C) const {
   C.getState()->dump();
@@ -267,9 +329,11 @@
 
 void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE,
                                                      CheckerContext &C) const {
-  if (CE->getNumArgs() == 0)
+  const Expr *Arg = getArgExpr(CE, C);
+  if (!Arg)
     return;
-  SVal Val = C.getSVal(CE->getArg(0));
+
+  SVal Val = C.getSVal(Arg);
   SymbolRef Sym = Val.getAsSymbol();
   if (!Sym)
     return;
@@ -306,7 +370,7 @@
 
 void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
                                              ExprEngine &Eng) const {
-  for (auto Item: ReachedStats) {
+  for (auto Item : ReachedStats) {
     unsigned NumTimesReached = Item.second.NumTimesReached;
     ExplodedNode *N = Item.second.ExampleNode;
 
@@ -373,9 +437,7 @@
     return None;
   }
 
-  Optional<std::string> VisitSymExpr(const SymExpr *S) {
-    return lookup(S);
-  }
+  Optional<std::string> VisitSymExpr(const SymExpr *S) { return lookup(S); }
 
   Optional<std::string> VisitSymIntExpr(const SymIntExpr *S) {
     if (Optional<std::string> Str = lookup(S))
@@ -394,7 +456,8 @@
     if (Optional<std::string> Str1 = Visit(S->getLHS()))
       if (Optional<std::string> Str2 = Visit(S->getRHS()))
         return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) +
-                " " + *Str2).str();
+                " " + *Str2)
+            .str();
     return None;
   }
 
@@ -410,10 +473,9 @@
 
 void ExprInspectionChecker::analyzerExpress(const CallExpr *CE,
                                             CheckerContext &C) const {
-  if (CE->getNumArgs() == 0) {
-    reportBug("clang_analyzer_express() requires a symbol", C);
+  const Expr *Arg = getArgExpr(CE, C);
+  if (!Arg)
     return;
-  }
 
   SVal ArgVal = C.getSVal(CE->getArg(0));
   SymbolRef Sym = ArgVal.getAsSymbol();
Index: clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
@@ -92,12 +92,8 @@
     if (Size.isUndef())
       return true; // Return true to model purity.
 
-    SValBuilder& svalBuilder = C.getSValBuilder();
-    DefinedOrUnknownSVal DynSize = getDynamicSize(state, R, svalBuilder);
-    DefinedOrUnknownSVal DynSizeMatchesSizeArg =
-        svalBuilder.evalEQ(state, DynSize, Size.castAs<DefinedOrUnknownSVal>());
-    state = state->assume(DynSizeMatchesSizeArg, true);
-    assert(state && "The region should not have any previous constraints");
+    state = setDynamicSize(state, R, Size.castAs<DefinedOrUnknownSVal>(),
+                           C.getSValBuilder());
 
     C.addTransition(state->BindExpr(CE, LCtx, loc::MemRegionVal(R)));
     return true;
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h
@@ -22,15 +22,21 @@
 namespace clang {
 namespace ento {
 
-/// Get the stored dynamic size for the region \p MR.
+/// \returns The stored dynamic size for the region \p MR.
 DefinedOrUnknownSVal getDynamicSize(ProgramStateRef State, const MemRegion *MR,
                                     SValBuilder &SVB);
 
-/// Get the stored element count of the region \p MR.
+/// \returns The element size of the type \p Ty.
+DefinedOrUnknownSVal getElementSize(QualType Ty, SValBuilder &SVB);
+
+/// \returns The stored element count of the region \p MR.
 DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State,
                                             const MemRegion *MR,
-                                            SValBuilder &SVB,
-                                            QualType ElementTy);
+                                            SValBuilder &SVB, QualType Ty);
+
+/// Set the dynamic size \p Size of the region \p MR.
+ProgramStateRef setDynamicSize(ProgramStateRef State, const MemRegion *MR,
+                               DefinedOrUnknownSVal Size, SValBuilder &SVB);
 
 /// Get the dynamic size for a symbolic value that represents a buffer. If
 /// there is an offsetting to the underlying buffer we consider that too.
@@ -45,7 +51,7 @@
 ///
 ///   char *bufptr;
 ///   (bufptr) // size is unknown
-SVal getDynamicSizeWithOffset(ProgramStateRef State, const SVal &BufV);
+SVal getDynamicSizeWithOffset(ProgramStateRef State, SVal BufV);
 
 } // namespace ento
 } // namespace clang
Index: clang/docs/analyzer/developer-docs/DebugChecks.rst
===================================================================
--- clang/docs/analyzer/developer-docs/DebugChecks.rst
+++ clang/docs/analyzer/developer-docs/DebugChecks.rst
@@ -297,6 +297,19 @@
       return n;
     }
 
+- ``clang_analyzer_dumpExtent(a single argument of any type)``
+- ``clang_analyzer_dumpElementCount(a single argument of any type)``
+
+  Dumps out the extent and the element count of the argument.
+
+  Example usage::
+
+    void array() {
+      int a[] = {1, 3};
+      clang_analyzer_dumpExtent(a);       // expected-warning {{8 S64b}}
+      clang_analyzer_dumpElementCount(a); // expected-warning {{2 S64b}}
+    }
+
 Statistics
 ==========
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to