[PATCH] D87785: [analyzer][StdLibraryFunctionsChecker] Fix a BufferSize constraint crash

2020-09-21 Thread Gabor Marton via Phabricator via cfe-commits
martong abandoned this revision.
martong added inline comments.



Comment at: clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp:694
   // execution continues on a code whose behaviour is undefined.
   assert(SuccessSt);
   NewState = SuccessSt;

martong wrote:
> This is where we crashed before this fix.
`assert(SuccessSt);` should not ever fail. Seems like the logic is not flawed 
in `negate` rather there is an issue in the underlying 
`RangeConstraintManager`: the analyzer goes on with an unfeasible path.

See the post-commit comments here: https://reviews.llvm.org/D82445


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D87785

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D87785: [analyzer][StdLibraryFunctionsChecker] Fix a BufferSize constraint crash

2020-09-17 Thread Gabor Marton via Phabricator via cfe-commits
martong added a comment.

Alright, I refactored the change a bit. A Constraint::assume works similarly to 
an engine DualAssume. Plus I added `isApplicable` to check if it is meaningful 
to call the assume at all.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D87785

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D87785: [analyzer][StdLibraryFunctionsChecker] Fix a BufferSize constraint crash

2020-09-17 Thread Gabor Marton via Phabricator via cfe-commits
martong updated this revision to Diff 292550.
martong added a comment.

- apply -> assume, Add isApplicable


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D87785

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

Index: clang/test/Analysis/std-c-library-functions.c
===
--- clang/test/Analysis/std-c-library-functions.c
+++ clang/test/Analysis/std-c-library-functions.c
@@ -126,6 +126,25 @@
   (void)fread(ptr, sz, nmem, fp); // expected-warning {{1st function call argument is an uninitialized value}}
 }
 
+static void test_fread_buffer_size_constraint_no_crash(FILE *f, size_t n) {
+  size_t rlen; /* how much to read */
+  size_t nr;   /* number of chars actually read */
+  char b[8192];
+  rlen = 8192;
+  do {
+char *p = b;
+if (rlen > n)
+  rlen = n; /* cannot read more than asked */
+nr = fread(p, sizeof(char), rlen, f);
+n -= nr; /* still have to read `n' chars */
+  } while (n > 0 && nr == rlen); /* until end of count or eof */
+}
+// Test that we do not crash here.
+void test_fread_buffer_size_constraint_no_crash_caller(FILE *f) {
+  /* read MAX_SIZE_T chars */
+  test_fread_buffer_size_constraint_no_crash(f, ~((size_t)0));
+}
+
 ssize_t getline(char **, size_t *, FILE *);
 void test_getline(FILE *fp) {
   char *line = 0;
Index: clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -104,14 +104,18 @@
   public:
 ValueConstraint(ArgNo ArgN) : ArgN(ArgN) {}
 virtual ~ValueConstraint() {}
-/// Apply the effects of the constraint on the given program state. If null
-/// is returned then the constraint is not feasible.
-virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent ,
-  const Summary ,
-  CheckerContext ) const = 0;
-virtual ValueConstraintPtr negate() const {
-  llvm_unreachable("Not implemented");
-};
+
+/// Returns true if we can do any assumption on the constraint, i.e. if it
+/// is meaningful to call an Engine `assume` function.
+virtual bool isApplicable(ProgramStateRef State, const CallEvent ,
+  const Summary ,
+  CheckerContext ) const = 0;
+
+/// Returns the program states for the cases when the constraint is
+/// satisfiable and non satisfiable.
+virtual std::pair
+assume(ProgramStateRef State, const CallEvent , const Summary ,
+   CheckerContext ) const = 0;
 
 // Check whether the constraint is malformed or not. It is malformed if the
 // specified argument has a mismatch with the given FunctionDecl (e.g. the
@@ -169,29 +173,25 @@
const Summary ) const;
 
   public:
-ProgramStateRef apply(ProgramStateRef State, const CallEvent ,
-  const Summary ,
-  CheckerContext ) const override {
-  switch (Kind) {
-  case OutOfRange:
-return applyAsOutOfRange(State, Call, Summary);
-  case WithinRange:
-return applyAsWithinRange(State, Call, Summary);
-  }
-  llvm_unreachable("Unknown range kind!");
+bool isApplicable(ProgramStateRef State, const CallEvent ,
+  const Summary ,
+  CheckerContext ) const override {
+  SVal V = getArgSVal(Call, getArgNo());
+  return V.getAs().hasValue();
 }
 
-ValueConstraintPtr negate() const override {
-  RangeConstraint Tmp(*this);
+std::pair
+assume(ProgramStateRef State, const CallEvent , const Summary ,
+   CheckerContext ) const override {
   switch (Kind) {
   case OutOfRange:
-Tmp.Kind = WithinRange;
-break;
+return {applyAsOutOfRange(State, Call, Summary),
+applyAsWithinRange(State, Call, Summary)};
   case WithinRange:
-Tmp.Kind = OutOfRange;
-break;
+return {applyAsWithinRange(State, Call, Summary),
+applyAsOutOfRange(State, Call, Summary)};
   }
-  return std::make_shared(Tmp);
+  llvm_unreachable("Unknown range kind!");
 }
 
 bool checkSpecificValidity(const FunctionDecl *FD) const override {
@@ -214,36 +214,37 @@
 : ValueConstraint(ArgN), Opcode(Opcode), OtherArgN(OtherArgN) {}
 ArgNo getOtherArgNo() const { return OtherArgN; }
 BinaryOperator::Opcode getOpcode() const { return Opcode; }
-ProgramStateRef apply(ProgramStateRef State, const CallEvent ,
-  const Summary ,
-  CheckerContext ) 

[PATCH] D87785: [analyzer][StdLibraryFunctionsChecker] Fix a BufferSize constraint crash

2020-09-16 Thread Balázs Benics via Phabricator via cfe-commits
steakhal added inline comments.



Comment at: 
clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp:219-226
   if (V.isUndef())
-return State;
+return {State, State};
 
   DefinedOrUnknownSVal L = V.castAs();
   if (!L.getAs())
-return State;
-
-  return State->assume(L, CannotBeNull);
-}
+return {State, State};
 

I suggest the same //simpler// version for the similar code segments as well.

By the same token, why do you return `{State, State}`?
Shouldn't you return `{State, nullptr}` there?
In general, one would not expect the **same** State being returned, IMO it's 
advised to avoid doing that.
Same applies for the other cases.



Comment at: 
clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp:299-300
 SvalBuilder.getContext().BoolTy);
   if (auto F = Feasible.getAs())
-return State->assume(*F, true);
+return State->assume(*F);
 

Why don't you `castAs`? That also has the corresponding assert inside.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D87785

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D87785: [analyzer][StdLibraryFunctionsChecker] Fix a BufferSize constraint crash

2020-09-16 Thread Gabor Marton via Phabricator via cfe-commits
martong added inline comments.



Comment at: clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp:694
   // execution continues on a code whose behaviour is undefined.
   assert(SuccessSt);
   NewState = SuccessSt;

This is where we crashed before this fix.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D87785

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D87785: [analyzer][StdLibraryFunctionsChecker] Fix a BufferSize constraint crash

2020-09-16 Thread Gabor Marton via Phabricator via cfe-commits
martong created this revision.
martong added reviewers: steakhal, balazske, Szelethus, NoQ.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, gamesh411, 
dkrupp, donat.nagy, mikhail.ramalho, a.sidorin, rnkovacs, szepet, 
baloghadamsoftware, xazax.hun, whisperity.
Herald added a project: clang.
martong requested review of this revision.

It could happen that both the satisfied and unsatisfied constraints gave
a NULL state. This happened probably because the negate() functionality
was not perfect. The solution is to return both states from the `assume`
calls where it makes sense.
This way the code becomes cleaner, there is no need anymore for the
controversial `negate()`.

Note that this causes a regression in our CTU builds.
http://codechecker-buildbot.eastus.cloudapp.azure.com:8080/job/ctu_pipeline_clang-master-monorepo/1720/
The error itself is not CTU related, however, the coverage and thus the path is 
different in that mode.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D87785

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

Index: clang/test/Analysis/std-c-library-functions.c
===
--- clang/test/Analysis/std-c-library-functions.c
+++ clang/test/Analysis/std-c-library-functions.c
@@ -126,6 +126,25 @@
   (void)fread(ptr, sz, nmem, fp); // expected-warning {{1st function call argument is an uninitialized value}}
 }
 
+static void test_fread_buffer_size_constraint_no_crash(FILE *f, size_t n) {
+  size_t rlen; /* how much to read */
+  size_t nr;   /* number of chars actually read */
+  char b[8192];
+  rlen = 8192;
+  do {
+char *p = b;
+if (rlen > n)
+  rlen = n; /* cannot read more than asked */
+nr = fread(p, sizeof(char), rlen, f);
+n -= nr; /* still have to read `n' chars */
+  } while (n > 0 && nr == rlen); /* until end of count or eof */
+}
+// Test that we do not crash here.
+void test_fread_buffer_size_constraint_no_crash_caller(FILE *f) {
+  /* read MAX_SIZE_T chars */
+  test_fread_buffer_size_constraint_no_crash(f, ~((size_t)0));
+}
+
 ssize_t getline(char **, size_t *, FILE *);
 void test_getline(FILE *fp) {
   char *line = 0;
Index: clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -104,14 +104,13 @@
   public:
 ValueConstraint(ArgNo ArgN) : ArgN(ArgN) {}
 virtual ~ValueConstraint() {}
-/// Apply the effects of the constraint on the given program state. If null
-/// is returned then the constraint is not feasible.
-virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent ,
-  const Summary ,
-  CheckerContext ) const = 0;
-virtual ValueConstraintPtr negate() const {
-  llvm_unreachable("Not implemented");
-};
+
+/// Apply the effects of the constraint on the given program state. Returns
+/// the program states for the cases when the constraint is satisfiable and
+/// non satisfiable.
+virtual std::pair
+apply(ProgramStateRef State, const CallEvent , const Summary ,
+  CheckerContext ) const = 0;
 
 // Check whether the constraint is malformed or not. It is malformed if the
 // specified argument has a mismatch with the given FunctionDecl (e.g. the
@@ -169,31 +168,20 @@
const Summary ) const;
 
   public:
-ProgramStateRef apply(ProgramStateRef State, const CallEvent ,
-  const Summary ,
-  CheckerContext ) const override {
+std::pair
+apply(ProgramStateRef State, const CallEvent , const Summary ,
+  CheckerContext ) const override {
   switch (Kind) {
   case OutOfRange:
-return applyAsOutOfRange(State, Call, Summary);
+return {applyAsOutOfRange(State, Call, Summary),
+applyAsWithinRange(State, Call, Summary)};
   case WithinRange:
-return applyAsWithinRange(State, Call, Summary);
+return {applyAsWithinRange(State, Call, Summary),
+applyAsOutOfRange(State, Call, Summary)};
   }
   llvm_unreachable("Unknown range kind!");
 }
 
-ValueConstraintPtr negate() const override {
-  RangeConstraint Tmp(*this);
-  switch (Kind) {
-  case OutOfRange:
-Tmp.Kind = WithinRange;
-break;
-  case WithinRange:
-Tmp.Kind = OutOfRange;
-break;
-  }
-  return std::make_shared(Tmp);
-}
-
 bool checkSpecificValidity(const FunctionDecl *FD) const override {
   const bool ValidArg =
   getArgType(FD, ArgN)->isIntegralType(FD->getASTContext());
@@ -214,36 +202,28 @@
 :