martong created this revision.
martong added reviewers: Szelethus, NoQ, baloghadamsoftware, balazske, steakhal.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, gamesh411, 
dkrupp, donat.nagy, mikhail.ramalho, a.sidorin, rnkovacs, szepet, xazax.hun, 
whisperity.
Herald added a project: clang.
martong added parent revisions: D79423: [analyzer][NFC] 
StdLibraryFunctionsChecker: Add empty Signatures, D79425: [analyzer] 
StdLibraryFunctionsChecker: Add overload for adding the same summary for 
different names.

Some functions uses a platform dependent `ssize_t` (e.g. `read`). Currently, we
provide different summaries for the possible variants depending on the
canonical type of `ssize_t` (int, long, long long). In this patch we get rid of
this burden, thus making the administration of summaries easier. The newly
introduced `LazyRangeInt` is a union of a `RangeInt` and a function pointer. We
get the ranges lazily. By the time we need the ranges we already have a
concrete FunctionDecl set for the summary. When a function pointer is used
amongst the range values then we evaluate the function. This way we can get a
type dependent Max value and instead of providing 3 summaries to `read` we can
provide only one.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D79430

Files:
  clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
  clang/test/Analysis/std-c-library-functions-arg-constraints.c

Index: clang/test/Analysis/std-c-library-functions-arg-constraints.c
===================================================================
--- clang/test/Analysis/std-c-library-functions-arg-constraints.c
+++ clang/test/Analysis/std-c-library-functions-arg-constraints.c
@@ -122,3 +122,19 @@
   // bugpath-warning{{Function argument constraint is not satisfied}} \
   // bugpath-note{{Function argument constraint is not satisfied}}
 }
+
+int __lazy_range(int, int);
+void test_lazy_range(int a, int b) {
+  __lazy_range(a, b);
+  clang_analyzer_eval(a >= 0 && a <= 100 && b >= 0 && b <= __INT_MAX__); // \
+  // report-warning{{TRUE}} \
+  // bugpath-warning{{TRUE}} \
+  // bugpath-note{{TRUE}} \
+  // bugpath-note{{'a' is >= 0}} \
+  // bugpath-note{{'a' is <= 100}} \
+  // bugpath-note{{'b' is >= 0}} \
+  // bugpath-note{{'b' is <= __INT_MAX__}} \
+  // bugpath-note{{Left side of '&&' is true}} \
+  // bugpath-note{{Left side of '&&' is true}} \
+  // bugpath-note{{Left side of '&&' is true}}
+}
Index: clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -131,20 +131,59 @@
     virtual bool sanityCheck(const FunctionDecl *FD) const { return true; }
   };
 
+  using RangeFun = RangeInt(const FunctionDecl *, ArgNo, BasicValueFactory &);
+  using RangeFunPtr = RangeFun *;
+  class LazyRangeInt {
+    union {
+      RangeInt Int;
+      RangeFunPtr Fun;
+    } Storage;
+    enum { K_Int, K_Fun } Kind;
+
+  public:
+    LazyRangeInt(int Int) : Kind(K_Int) { Storage.Int = Int; }
+    LazyRangeInt(RangeFunPtr Fun) : Kind(K_Fun) { Storage.Fun = Fun; }
+    RangeInt eval(const FunctionDecl *FD, ArgNo ArgN,
+                  BasicValueFactory &BVF) const {
+      if (Kind == K_Int)
+        return Storage.Int;
+      return Storage.Fun(FD, ArgN, BVF);
+    }
+  };
+
+  static RangeInt Max(const FunctionDecl *FD, ArgNo ArgN,
+                      BasicValueFactory &BVF) {
+    return BVF.getMaxValue(getArgType(FD, ArgN)).getLimitedValue();
+  }
+
+  using LazyIntRangeVector = std::vector<std::pair<LazyRangeInt, LazyRangeInt>>;
+
   /// Given a range, should the argument stay inside or outside this range?
   enum RangeKind { OutOfRange, WithinRange };
 
   /// Encapsulates a single range on a single symbol within a branch.
   class RangeConstraint : public ValueConstraint {
     RangeKind Kind;      // Kind of range definition.
-    IntRangeVector Args; // Polymorphic arguments.
+    mutable IntRangeVector Ranges;
+    LazyIntRangeVector LazyRanges;
 
   public:
-    RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Args)
-        : ValueConstraint(ArgN), Kind(Kind), Args(Args) {}
-
-    const IntRangeVector &getRanges() const {
-      return Args;
+    RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Ranges)
+        : ValueConstraint(ArgN), Kind(Kind), Ranges(Ranges) {}
+
+    RangeConstraint(ArgNo ArgN, RangeKind Kind,
+                    const LazyIntRangeVector &Ranges)
+        : ValueConstraint(ArgN), Kind(Kind), LazyRanges(Ranges) {}
+
+    const IntRangeVector &getRanges(const FunctionDecl *FD,
+                                    BasicValueFactory &BVF) const {
+      if (Ranges.size())
+        return Ranges;
+      for (const std::pair<LazyRangeInt, LazyRangeInt> &LazyR : LazyRanges) {
+        Ranges.push_back({LazyR.first.eval(FD, ArgN, BVF),
+                          LazyR.second.eval(FD, ArgN, BVF)});
+      }
+      return Ranges;
     }
 
   private:
@@ -331,6 +370,7 @@
     }
 
     void setFunctionDecl(const FunctionDecl *FD) { this->FD = FD; }
+    const FunctionDecl *getFunctionDecl() const { return FD; }
 
     InvalidationKind getInvalidationKd() const { return InvalidationKd; }
     const Cases &getCaseConstraints() const { return CaseConstraints; }
@@ -425,7 +465,7 @@
   SVal V = getArgSVal(Call, getArgNo());
 
   if (auto N = V.getAs<NonLoc>()) {
-    const IntRangeVector &R = getRanges();
+    const IntRangeVector &R = getRanges(Summary.getFunctionDecl(), BVF);
     size_t E = R.size();
     for (size_t I = 0; I != E; ++I) {
       const llvm::APSInt &Min = BVF.getValue(R[I].first, T);
@@ -461,7 +501,7 @@
   // Then we assume that the value is not in [-inf, A - 1],
   // then not in [D + 1, +inf], then not in [B + 1, C - 1]
   if (auto N = V.getAs<NonLoc>()) {
-    const IntRangeVector &R = getRanges();
+    const IntRangeVector &R = getRanges(Summary.getFunctionDecl(), BVF);
     size_t E = R.size();
 
     const llvm::APSInt &MinusInf = BVF.getMinValue(T);
@@ -682,22 +722,11 @@
   // or long long, so three summary variants would be enough).
   // Of course, function variants are also useful for C++ overloads.
   const QualType IntTy = ACtx.IntTy;
-  const QualType LongTy = ACtx.LongTy;
-  const QualType LongLongTy = ACtx.LongLongTy;
-  const QualType SizeTy = ACtx.getSizeType();
   const QualType VoidPtrTy = ACtx.VoidPtrTy; // void *
-  const QualType VoidPtrRestrictTy =
-      ACtx.getRestrictType(VoidPtrTy); // void *restrict
-  const QualType ConstVoidPtrTy =
-      ACtx.getPointerType(ACtx.VoidTy.withConst()); // const void *
   const QualType ConstCharPtrTy =
       ACtx.getPointerType(ACtx.CharTy.withConst()); // const char *
-  const QualType ConstVoidPtrRestrictTy =
-      ACtx.getRestrictType(ConstVoidPtrTy); // const void *restrict
 
   const RangeInt IntMax = BVF.getMaxValue(IntTy).getLimitedValue();
-  const RangeInt LongMax = BVF.getMaxValue(LongTy).getLimitedValue();
-  const RangeInt LongLongMax = BVF.getMaxValue(LongLongTy).getLimitedValue();
 
   // Set UCharRangeMax to min of int or uchar maximum value.
   // The C standard states that the arguments of functions like isalpha must
@@ -789,22 +818,22 @@
 
   // Below are helpers functions to create the summaries.
   auto ArgumentCondition = [](ArgNo ArgN, RangeKind Kind,
-                              IntRangeVector Ranges) {
+                              LazyIntRangeVector Ranges) {
     return std::make_shared<RangeConstraint>(ArgN, Kind, Ranges);
   };
   struct {
-    auto operator()(RangeKind Kind, IntRangeVector Ranges) {
+    auto operator()(RangeKind Kind, LazyIntRangeVector Ranges) {
       return std::make_shared<RangeConstraint>(Ret, Kind, Ranges);
     }
     auto operator()(BinaryOperator::Opcode Op, ArgNo OtherArgN) {
       return std::make_shared<ComparisonConstraint>(Ret, Op, OtherArgN);
     }
   } ReturnValueCondition;
-  auto Range = [](RangeInt b, RangeInt e) {
-    return IntRangeVector{std::pair<RangeInt, RangeInt>{b, e}};
+  auto Range = [](LazyRangeInt b, LazyRangeInt e) {
+    return LazyIntRangeVector{std::pair<LazyRangeInt, LazyRangeInt>{b, e}};
   };
-  auto SingleValue = [](RangeInt v) {
-    return IntRangeVector{std::pair<RangeInt, RangeInt>{v, v}};
+  auto SingleValue = [](LazyRangeInt v) {
+    return LazyIntRangeVector{std::pair<LazyRangeInt, LazyRangeInt>{v, v}};
   };
   auto LessThanOrEq = BO_LE;
   auto NotNull = [&](ArgNo ArgN) {
@@ -812,40 +841,6 @@
   };
 
   using RetType = QualType;
-  // Templates for summaries that are reused by many functions.
-  auto Getc = [&]() {
-    return Summary(ArgTypes{Irrelevant}, RetType{IntTy}, NoEvalCall)
-        .Case({ReturnValueCondition(WithinRange,
-                                    {{EOFv, EOFv}, {0, UCharRangeMax}})});
-  };
-  auto Read = [&](RetType R, RangeInt Max) {
-    return Summary(ArgTypes{Irrelevant, Irrelevant, SizeTy}, RetType{R},
-                   NoEvalCall)
-        .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
-               ReturnValueCondition(WithinRange, Range(-1, Max))});
-  };
-  auto Fread = [&]() {
-    return Summary(ArgTypes{VoidPtrRestrictTy, Irrelevant, SizeTy, Irrelevant},
-                   RetType{SizeTy}, NoEvalCall)
-        .Case({
-            ReturnValueCondition(LessThanOrEq, ArgNo(2)),
-        })
-        .ArgConstraint(NotNull(ArgNo(0)));
-  };
-  auto Fwrite = [&]() {
-    return Summary(
-               ArgTypes{ConstVoidPtrRestrictTy, Irrelevant, SizeTy, Irrelevant},
-               RetType{SizeTy}, NoEvalCall)
-        .Case({
-            ReturnValueCondition(LessThanOrEq, ArgNo(2)),
-        })
-        .ArgConstraint(NotNull(ArgNo(0)));
-  };
-  auto Getline = [&](RetType R, RangeInt Max) {
-    return Summary(ArgTypes{Irrelevant, Irrelevant, Irrelevant}, RetType{R},
-                   NoEvalCall)
-        .Case({ReturnValueCondition(WithinRange, {{-1, -1}, {1, Max}})});
-  };
 
   // The isascii() family of functions.
   // The behavior is undefined if the value of the argument is not
@@ -982,29 +977,34 @@
                  ReturnValueCondition(WithinRange, SingleValue(0))}));
 
   // The getc() family of functions that returns either a char or an EOF.
-  addToFunctionSummaryMap("getc", Getc());
-  addToFunctionSummaryMap("fgetc", Getc());
+  addToFunctionSummaryMap(
+      {"getc", "fgetc"},
+      Summary(ArgTypes{Irrelevant}, RetType{IntTy}, NoEvalCall)
+          .Case({ReturnValueCondition(WithinRange,
+                                      {{EOFv, EOFv}, {0, UCharRangeMax}})}));
   addToFunctionSummaryMap(
       "getchar", Summary(ArgTypes{}, RetType{IntTy}, NoEvalCall)
                      .Case({ReturnValueCondition(
                          WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})}));
 
   // read()-like functions that never return more than buffer size.
-  // We are not sure how ssize_t is defined on every platform, so we
-  // provide three variants that should cover common cases.
-  addToFunctionSummaryMap("read", {Read(IntTy, IntMax), Read(LongTy, LongMax),
-                                   Read(LongLongTy, LongLongMax)});
-  addToFunctionSummaryMap("write", {Read(IntTy, IntMax), Read(LongTy, LongMax),
-                                    Read(LongLongTy, LongLongMax)});
-  addToFunctionSummaryMap("fread", Fread());
-  addToFunctionSummaryMap("fwrite", Fwrite());
+  addToFunctionSummaryMap(
+      {"read", "write"},
+      Summary(NoEvalCall)
+          .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
+                 ReturnValueCondition(WithinRange, Range(-1, Max))}));
+  addToFunctionSummaryMap({"fread", "fwrite"},
+                          Summary(NoEvalCall)
+                              .Case({
+                                  ReturnValueCondition(LessThanOrEq, ArgNo(2)),
+                              })
+                              .ArgConstraint(NotNull(ArgNo(0))));
+
   // getline()-like functions either fail or read at least the delimiter.
-  addToFunctionSummaryMap("getline",
-                          {Getline(IntTy, IntMax), Getline(LongTy, LongMax),
-                           Getline(LongLongTy, LongLongMax)});
-  addToFunctionSummaryMap("getdelim",
-                          {Getline(IntTy, IntMax), Getline(LongTy, LongMax),
-                           Getline(LongLongTy, LongLongMax)});
+  addToFunctionSummaryMap(
+      {"getline", "getdelim"},
+      Summary(NoEvalCall)
+          .Case({ReturnValueCondition(WithinRange, {{-1, -1}, {1, Max}})}));
 
   // Functions for testing.
   if (ChecksEnabled[CK_StdCLibraryFunctionsTesterChecker]) {
@@ -1027,6 +1027,11 @@
                                     RetType{IntTy}, EvalCallAsPure)
                                 .ArgConstraint(NotNull(ArgNo(0)))
                                 .ArgConstraint(NotNull(ArgNo(1))));
+    addToFunctionSummaryMap(
+        "__lazy_range",
+        Summary(ArgTypes{IntTy, IntTy}, RetType{IntTy}, EvalCallAsPure)
+            .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, 100)))
+            .ArgConstraint(ArgumentCondition(1, WithinRange, Range(0, Max))));
   }
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to