[PATCH] D31982: [analyzer] Improve suppression for inlined defensive checks when operator& is involved.

2017-04-24 Thread Phabricator via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL301224: [analyzer] Improve suppression for inlined defensive 
checks before operator &. (authored by dergachev).

Changed prior to commit:
  https://reviews.llvm.org/D31982?vs=95890=96446#toc

Repository:
  rL LLVM

https://reviews.llvm.org/D31982

Files:
  cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
  cfe/trunk/lib/StaticAnalyzer/Core/Store.cpp
  cfe/trunk/test/Analysis/inlining/inline-defensive-checks.c
  cfe/trunk/test/Analysis/inlining/inline-defensive-checks.cpp
  cfe/trunk/test/Analysis/null-deref-offsets.c
  cfe/trunk/test/Analysis/uninit-const.cpp

Index: cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
===
--- cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -961,6 +961,26 @@
   const Expr *Inner = nullptr;
   if (const Expr *Ex = dyn_cast(S)) {
 Ex = Ex->IgnoreParenCasts();
+
+// Performing operator `&' on an lvalue expression is essentially a no-op.
+// Then, if we are taking addresses of fields or elements, these are also
+// unlikely to matter.
+// FIXME: There's a hack in our Store implementation that always computes
+// field offsets around null pointers as if they are always equal to 0.
+// The idea here is to report accesses to fields as null dereferences
+// even though the pointer value that's being dereferenced is actually
+// the offset of the field rather than exactly 0.
+// See the FIXME in StoreManager's getLValueFieldOrIvar() method.
+// This code interacts heavily with this hack; otherwise the value
+// would not be null at all for most fields, so we'd be unable to track it.
+if (const auto *Op = dyn_cast(Ex))
+  if (Op->getOpcode() == UO_AddrOf && Op->getSubExpr()->isLValue()) {
+Ex = Op->getSubExpr()->IgnoreParenCasts();
+while (const auto *ME = dyn_cast(Ex)) {
+  Ex = ME->getBase()->IgnoreParenCasts();
+}
+  }
+
 if (ExplodedGraph::isInterestingLValueExpr(Ex) || CallEvent::isCallStmt(Ex))
   Inner = Ex;
   }
Index: cfe/trunk/lib/StaticAnalyzer/Core/Store.cpp
===
--- cfe/trunk/lib/StaticAnalyzer/Core/Store.cpp
+++ cfe/trunk/lib/StaticAnalyzer/Core/Store.cpp
@@ -404,9 +404,15 @@
 
   case loc::ConcreteIntKind:
 // While these seem funny, this can happen through casts.
-// FIXME: What we should return is the field offset.  For example,
-//  add the field offset to the integer value.  That way funny things
+// FIXME: What we should return is the field offset, not base. For example,
+//  add the field offset to the integer value.  That way things
 //  like this work properly:  &(((struct foo *) 0xa)->f)
+//  However, that's not easy to fix without reducing our abilities
+//  to catch null pointer dereference. Eg., ((struct foo *)0x0)->f = 7
+//  is a null dereference even though we're dereferencing offset of f
+//  rather than null. Coming up with an approach that computes offsets
+//  over null pointers properly while still being able to catch null
+//  dereferences might be worth it.
 return Base;
 
   default:
@@ -431,7 +437,7 @@
   // If the base is an unknown or undefined value, just return it back.
   // FIXME: For absolute pointer addresses, we just return that value back as
   //  well, although in reality we should return the offset added to that
-  //  value.
+  //  value. See also the similar FIXME in getLValueFieldOrIvar().
   if (Base.isUnknownOrUndef() || Base.getAs())
 return Base;
 
Index: cfe/trunk/test/Analysis/inlining/inline-defensive-checks.cpp
===
--- cfe/trunk/test/Analysis/inlining/inline-defensive-checks.cpp
+++ cfe/trunk/test/Analysis/inlining/inline-defensive-checks.cpp
@@ -70,4 +70,17 @@
 void test(int *p1, int *p2) {
   idc(p1);
 	Foo f(p1);
-}
\ No newline at end of file
+}
+
+struct Bar {
+  int x;
+};
+void idcBar(Bar *b) {
+  if (b)
+;
+}
+void testRefToField(Bar *b) {
+  idcBar(b);
+  int  = b->x; // no-warning
+  x = 5;
+}
Index: cfe/trunk/test/Analysis/inlining/inline-defensive-checks.c
===
--- cfe/trunk/test/Analysis/inlining/inline-defensive-checks.c
+++ cfe/trunk/test/Analysis/inlining/inline-defensive-checks.c
@@ -1,7 +1,7 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-config suppress-inlined-defensive-checks=true -verify %s
 
 // Perform inline defensive checks.
-void idc(int *p) {
+void idc(void *p) {
 	if (p)
 		;
 }
@@ -139,3 +139,42 @@
   int z = y;
   idcTriggerZeroValueThroughCall(z);
 }
+
+struct S {
+  int f1;
+  int f2;
+};
+
+void idcTrackZeroValueThroughUnaryPointerOperators(struct S *s) {
+  idc(s);
+  *(&(s->f1)) = 7; 

[PATCH] D31982: [analyzer] Improve suppression for inlined defensive checks when operator& is involved.

2017-04-20 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: lib/StaticAnalyzer/Core/Store.cpp:440
   //  well, although in reality we should return the offset added to that
-  //  value.
+  //  value. See also the similar FIXME in getLValueFieldOrIvar().
   if (Base.isUnknownOrUndef() || Base.getAs())

NoQ wrote:
> Note that this code doesn't really trigger; we return `UnknownVal()` 
> somewhere above, as shown on the newly added tests. I suspect we may be 
> missing valid null dereferences because of that; will have a look.
This is also addressed by  D32291.


https://reviews.llvm.org/D31982



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


[PATCH] D31982: [analyzer] Improve suppression for inlined defensive checks when operator& is involved.

2017-04-20 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp:977
+  Ex = Op->getSubExpr()->IgnoreParenCasts();
+  while (true) {
+if (const auto *ME = dyn_cast(Ex)) {

zaks.anna wrote:
> Why do we need the "while (true)"? Can we just use "dyn_cast(Ex)" 
> as the loop condition?
> 
> Take a look at the getDerefExpr(const Stmt *S) and see if that would be a 
> better place to add this code. Maybe not..
> 
Accidentally clicked "Done" and forgot about the `getDerefExpr()` part.

I put it into a follow-up patch though: D32291, because it's quite a cascade of 
changes already.


https://reviews.llvm.org/D31982



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


[PATCH] D31982: [analyzer] Improve suppression for inlined defensive checks when operator& is involved.

2017-04-20 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp:965
+
+// Performing operator `&' on an lvalue expression is essentially a no-op.
+// Then, if we are taking addresses of fields or elements, these are also

zaks.anna wrote:
> NoQ wrote:
> > alexshap wrote:
> > > "Address-of" operator can be overloaded, 
> > > just wondering - doest this code work correctly in that case ?
> > In this case we'd see a `CXXOperatorCallExpr` instead of `UnaryOperator` 
> > (all hail clang AST!).
> Adding a test case for that would be good.
Not sure. There are so many things that work differently in this scenario that 
i'm having troubles coming up with a test that tests exactly that and doesn't 
throw or not throw a warning for a dozen of other reasons. I'm even having 
troubles understanding what particular overload are we interested in. Did you 
have anything specific in mind?



Comment at: lib/StaticAnalyzer/Core/Store.cpp:440
   //  well, although in reality we should return the offset added to that
-  //  value.
+  //  value. See also the similar FIXME in getLValueFieldOrIvar().
   if (Base.isUnknownOrUndef() || Base.getAs())

Note that this code doesn't really trigger; we return `UnknownVal()` somewhere 
above, as shown on the newly added tests. I suspect we may be missing valid 
null dereferences because of that; will have a look.


https://reviews.llvm.org/D31982



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


[PATCH] D31982: [analyzer] Improve suppression for inlined defensive checks when operator& is involved.

2017-04-20 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 95890.
NoQ marked 5 inline comments as done.
NoQ added a comment.

Fix comments!


https://reviews.llvm.org/D31982

Files:
  lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
  lib/StaticAnalyzer/Core/Store.cpp
  test/Analysis/explain-svals.cpp
  test/Analysis/inlining/inline-defensive-checks.c
  test/Analysis/inlining/inline-defensive-checks.cpp
  test/Analysis/null-deref-offsets.c
  test/Analysis/uninit-const.cpp

Index: test/Analysis/uninit-const.cpp
===
--- test/Analysis/uninit-const.cpp
+++ test/Analysis/uninit-const.cpp
@@ -122,7 +122,7 @@
 }
 
 void f_uninit(void) {
-  int x;
+  int x;   // expected-note {{'x' declared without an initial value}}
   doStuff_uninit();  // expected-warning {{1st function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{1st function call argument is a pointer to uninitialized value}}
 }
Index: test/Analysis/null-deref-offsets.c
===
--- /dev/null
+++ test/Analysis/null-deref-offsets.c
@@ -0,0 +1,34 @@
+// RUN: %clang_analyze_cc1 -w -triple i386-apple-darwin10 -analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_eval(int);
+
+struct S {
+  int x, y;
+  int z[2];
+};
+
+void testOffsets(struct S *s) {
+  if (s != 0)
+return;
+
+  // FIXME: Here we are testing the hack that computes offsets to null pointers
+  // as 0 in order to find null dereferences of not-exactly-null pointers,
+  // such as &(s->y) below, which is equal to 4 rather than 0 in run-time.
+
+  // These are indeed null.
+  clang_analyzer_eval(s == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(&(s->x) == 0); // expected-warning{{TRUE}}
+
+  // FIXME: These should ideally be true.
+  clang_analyzer_eval(&(s->y) == 4); // expected-warning{{FALSE}}
+  clang_analyzer_eval(&(s->z[0]) == 8); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(&(s->z[1]) == 12); // expected-warning{{UNKNOWN}}
+
+  // FIXME: These should ideally be false.
+  clang_analyzer_eval(&(s->y) == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(&(s->z[0]) == 0); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(&(s->z[1]) == 0); // expected-warning{{UNKNOWN}}
+
+  // But this should still be a null dereference.
+  s->y = 5; // expected-warning{{Access to field 'y' results in a dereference of a null pointer (loaded from variable 's')}}
+}
Index: test/Analysis/inlining/inline-defensive-checks.cpp
===
--- test/Analysis/inlining/inline-defensive-checks.cpp
+++ test/Analysis/inlining/inline-defensive-checks.cpp
@@ -70,4 +70,17 @@
 void test(int *p1, int *p2) {
   idc(p1);
 	Foo f(p1);
-}
\ No newline at end of file
+}
+
+struct Bar {
+  int x;
+};
+void idcBar(Bar *b) {
+  if (b)
+;
+}
+void testRefToField(Bar *b) {
+  idcBar(b);
+  int  = b->x; // no-warning
+  x = 5;
+}
Index: test/Analysis/inlining/inline-defensive-checks.c
===
--- test/Analysis/inlining/inline-defensive-checks.c
+++ test/Analysis/inlining/inline-defensive-checks.c
@@ -1,7 +1,7 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-config suppress-inlined-defensive-checks=true -verify %s
 
 // Perform inline defensive checks.
-void idc(int *p) {
+void idc(void *p) {
 	if (p)
 		;
 }
@@ -139,3 +139,42 @@
   int z = y;
   idcTriggerZeroValueThroughCall(z);
 }
+
+struct S {
+  int f1;
+  int f2;
+};
+
+void idcTrackZeroValueThroughUnaryPointerOperators(struct S *s) {
+  idc(s);
+  *(&(s->f1)) = 7; // no-warning
+}
+
+void idcTrackZeroValueThroughUnaryPointerOperatorsWithOffset1(struct S *s) {
+  idc(s);
+  int *x = &(s->f2);
+  *x = 7; // no-warning
+}
+
+void idcTrackZeroValueThroughUnaryPointerOperatorsWithOffset2(struct S *s) {
+  idc(s);
+  int *x = &(s->f2) - 1;
+  // FIXME: Should not warn.
+  *x = 7; // expected-warning{{Dereference of null pointer}}
+}
+
+void idcTrackZeroValueThroughUnaryPointerOperatorsWithAssignment(struct S *s) {
+  idc(s);
+  int *x = &(s->f1);
+  *x = 7; // no-warning
+}
+
+
+struct S2 {
+  int a[1];
+};
+
+void idcTrackZeroValueThroughUnaryPointerOperatorsWithArrayField(struct S2 *s) {
+  idc(s);
+  *(&(s->a[0])) = 7; // no-warning
+}
Index: test/Analysis/explain-svals.cpp
===
--- test/Analysis/explain-svals.cpp
+++ test/Analysis/explain-svals.cpp
@@ -81,9 +81,10 @@
 
 namespace {
 class C {
+public:
   int x[10];
+  int y;
 
-public:
   void test_5(int i) {
 clang_analyzer_explain(this); // expected-warning-re^pointer to 'this' object$
 clang_analyzer_explain([i]); // expected-warning-re^pointer to element of type 'int' with index 'argument 'i'' of field 'x' of 'this' object$
Index: lib/StaticAnalyzer/Core/Store.cpp

[PATCH] D31982: [analyzer] Improve suppression for inlined defensive checks when operator& is involved.

2017-04-13 Thread Anna Zaks via Phabricator via cfe-commits
zaks.anna added a comment.

Thanks!!!




Comment at: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp:965
+
+// Performing operator `&' on an lvalue expression is essentially a no-op.
+// Then, if we are taking addresses of fields or elements, these are also

NoQ wrote:
> alexshap wrote:
> > "Address-of" operator can be overloaded, 
> > just wondering - doest this code work correctly in that case ?
> In this case we'd see a `CXXOperatorCallExpr` instead of `UnaryOperator` (all 
> hail clang AST!).
Adding a test case for that would be good.



Comment at: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp:968
+// unlikely to matter.
+// FIXME: Currently offsets of fields are computed incorrectly,
+// being always equal to 0. See the FIXME in StoreManager's

Incorrect implies that there is a better "correct" model and invites a fix. Do 
we know what better model would be? If so, we could add that to the comment. If 
not, I'd prefer explaining why it works this way (similarly to how you did in 
the comment below). Maybe adding an example of what does not work. And you 
could add a FIXME to say that it's worth investigating if there is a better way 
of handling it. (The majority of this info should probably go to Store.cpp)

Also, maybe it's just the choice of words here. "Incorrect" sounds like 
something that needs to be corrected. Whereas you could use something like "is 
modeled imprecisely with respect to what happens at execution time", which 
could still mean that this is how we do want to model it going forward.

It seems that the problem with modeling this way is demonstrated with a test 
case in explain-svals.cpp. So the benefits of the current modeling seem to be 
worth it.

Can we add a note along the path saying that "p" in "p->f" is null? This would 
address the user confusion with imprecise modeling.



Comment at: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp:977
+  Ex = Op->getSubExpr()->IgnoreParenCasts();
+  while (true) {
+if (const auto *ME = dyn_cast(Ex)) {

Why do we need the "while (true)"? Can we just use "dyn_cast(Ex)" 
as the loop condition?

Take a look at the getDerefExpr(const Stmt *S) and see if that would be a 
better place to add this code. Maybe not..




Comment at: lib/StaticAnalyzer/Core/Store.cpp:405
   case loc::ConcreteIntKind:
 // While these seem funny, this can happen through casts.
 // FIXME: What we should return is the field offset.  For example,

Could you rephrase this existing comment while you are here? Using word "funny" 
seems content-free and a bit strange. 





Comment at: lib/StaticAnalyzer/Core/Store.cpp:409
 //  like this work properly:  &(((struct foo *) 0xa)->f)
+//  However, that's not easy to fix without reducing our abilities
+//  to catch null pointer dereference. Eg., ((struct foo *)0x0)->f = 7

Thanks for adding the explanation!

Can you think of other cases where the same would apply? (Ex: array index)


https://reviews.llvm.org/D31982



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


[PATCH] D31982: [analyzer] Improve suppression for inlined defensive checks when operator& is involved.

2017-04-13 Thread Devin Coughlin via Phabricator via cfe-commits
dcoughlin accepted this revision.
dcoughlin added a comment.
This revision is now accepted and ready to land.

Yay! This is going to fix some really confusing false positives.LGTM with 
Gabor's comments addressed.

One note. I am not a big fan of using clang_analyzer_explain() in tests for 
functionality. It is great for debugging, but for testing it exposes too much 
of the implementation details of the region hierarchy and store. Exposing this 
kind of state is fine in tests specific to the store/region store, but in my 
opinion it should be another clang_analyzer_ entry point and should be limited 
to regions.

Similarly we should have a clang_analyzer_ entry point for testing symbols but 
that doesn't expose the guts of MemRegions. And (maybe?) another one for 
SymExprs.




Comment at: test/Analysis/explain-svals.cpp:103
+  C *c = 0;
+  // FIXME: we need to be explaining '40' rather than '0' here; not explainer 
bug.
+  clang_analyzer_explain(>y); // expected-warning-re^concrete memory 
address '0'$

It would be good to mention that this is a modeling bug and point to where it 
could be fixed. Otherwise the FIXME is not super helpful to future maintainers.




https://reviews.llvm.org/D31982



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


[PATCH] D31982: [analyzer] Improve suppression for inlined defensive checks when operator& is involved.

2017-04-13 Thread Gábor Horváth via Phabricator via cfe-commits
xazax.hun added inline comments.



Comment at: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp:975
+  if (Op->getOpcode() == UO_AddrOf)
+if (Op->getSubExpr()->isLValue()) {
+  Ex = Op->getSubExpr()->IgnoreParenCasts();

Maybe you could move this condition one level up with &&?



Comment at: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp:977
+  Ex = Op->getSubExpr()->IgnoreParenCasts();
+  while (true) {
+if (const auto *ME = dyn_cast(Ex)) {

Can't you writhe this like:

```
while (const auto *ME = dyn_cast(Ex)) {
  Ex = ME->getBase()->IgnoreParenCasts();
  }
```

?




https://reviews.llvm.org/D31982



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


[PATCH] D31982: [analyzer] Improve suppression for inlined defensive checks when operator& is involved.

2017-04-13 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 95082.
NoQ added a comment.

Remove accidentally added braces in unrelated code.


https://reviews.llvm.org/D31982

Files:
  lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
  lib/StaticAnalyzer/Core/Store.cpp
  test/Analysis/explain-svals.cpp
  test/Analysis/inlining/inline-defensive-checks.c
  test/Analysis/inlining/inline-defensive-checks.cpp
  test/Analysis/uninit-const.cpp

Index: test/Analysis/uninit-const.cpp
===
--- test/Analysis/uninit-const.cpp
+++ test/Analysis/uninit-const.cpp
@@ -122,7 +122,7 @@
 }
 
 void f_uninit(void) {
-  int x;
+  int x;   // expected-note {{'x' declared without an initial value}}
   doStuff_uninit();  // expected-warning {{1st function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{1st function call argument is a pointer to uninitialized value}}
 }
Index: test/Analysis/inlining/inline-defensive-checks.cpp
===
--- test/Analysis/inlining/inline-defensive-checks.cpp
+++ test/Analysis/inlining/inline-defensive-checks.cpp
@@ -70,4 +70,17 @@
 void test(int *p1, int *p2) {
   idc(p1);
 	Foo f(p1);
-}
\ No newline at end of file
+}
+
+struct Bar {
+  int x;
+};
+void idcBar(Bar *b) {
+  if (b)
+;
+}
+void testRefToField(Bar *b) {
+  idcBar(b);
+  int  = b->x; // no-warning
+  x = 5;
+}
Index: test/Analysis/inlining/inline-defensive-checks.c
===
--- test/Analysis/inlining/inline-defensive-checks.c
+++ test/Analysis/inlining/inline-defensive-checks.c
@@ -1,7 +1,7 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-config suppress-inlined-defensive-checks=true -verify %s
 
 // Perform inline defensive checks.
-void idc(int *p) {
+void idc(void *p) {
 	if (p)
 		;
 }
@@ -139,3 +139,42 @@
   int z = y;
   idcTriggerZeroValueThroughCall(z);
 }
+
+struct S {
+  int f1;
+  int f2;
+};
+
+void idcTrackZeroValueThroughUnaryPointerOperators(struct S *s) {
+  idc(s);
+  *(&(s->f1)) = 7; // no-warning
+}
+
+void idcTrackZeroValueThroughUnaryPointerOperatorsWithOffset1(struct S *s) {
+  idc(s);
+  int *x = &(s->f2);
+  *x = 7; // no-warning
+}
+
+void idcTrackZeroValueThroughUnaryPointerOperatorsWithOffset2(struct S *s) {
+  idc(s);
+  int *x = &(s->f2) - 1;
+  // FIXME: Should not warn.
+  *x = 7; // expected-warning{{Dereference of null pointer}}
+}
+
+void idcTrackZeroValueThroughUnaryPointerOperatorsWithAssignment(struct S *s) {
+  idc(s);
+  int *x = &(s->f1);
+  *x = 7; // no-warning
+}
+
+
+struct S2 {
+  int a[1];
+};
+
+void idcTrackZeroValueThroughUnaryPointerOperatorsWithArrayField(struct S2 *s) {
+  idc(s);
+  *(&(s->a[0])) = 7; // no-warning
+}
Index: test/Analysis/explain-svals.cpp
===
--- test/Analysis/explain-svals.cpp
+++ test/Analysis/explain-svals.cpp
@@ -81,9 +81,10 @@
 
 namespace {
 class C {
+public:
   int x[10];
+  int y;
 
-public:
   void test_5(int i) {
 clang_analyzer_explain(this); // expected-warning-re^pointer to 'this' object$
 clang_analyzer_explain([i]); // expected-warning-re^pointer to element of type 'int' with index 'argument 'i'' of field 'x' of 'this' object$
@@ -96,3 +97,9 @@
   clang_analyzer_explain(conjure_S()); // expected-warning-re^lazily frozen compound value of temporary object constructed at statement 'conjure_S\(\)'$
   clang_analyzer_explain(conjure_S().z); // expected-warning-re^value derived from \(symbol of type 'struct S' conjured at statement 'conjure_S\(\)'\) for field 'z' of temporary object constructed at statement 'conjure_S\(\)'$
 }
+
+void test_7() {
+  C *c = 0;
+  // FIXME: we need to be explaining '40' rather than '0' here; not explainer bug.
+  clang_analyzer_explain(>y); // expected-warning-re^concrete memory address '0'$
+}
Index: lib/StaticAnalyzer/Core/Store.cpp
===
--- lib/StaticAnalyzer/Core/Store.cpp
+++ lib/StaticAnalyzer/Core/Store.cpp
@@ -406,6 +406,10 @@
 // FIXME: What we should return is the field offset.  For example,
 //  add the field offset to the integer value.  That way funny things
 //  like this work properly:  &(((struct foo *) 0xa)->f)
+//  However, that's not easy to fix without reducing our abilities
+//  to catch null pointer dereference. Eg., ((struct foo *)0x0)->f = 7
+//  is a null dereference even though we're dereferencing offset of f
+//  rather than null.
 return Base;
 
   default:
Index: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
===
--- lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -961,6 +961,28 @@
   const Expr *Inner = nullptr;
   if (const Expr 

[PATCH] D31982: [analyzer] Improve suppression for inlined defensive checks when operator& is involved.

2017-04-12 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp:965
+
+// Performing operator `&' on an lvalue expression is essentially a no-op.
+// Then, if we are taking addresses of fields or elements, these are also

alexshap wrote:
> "Address-of" operator can be overloaded, 
> just wondering - doest this code work correctly in that case ?
In this case we'd see a `CXXOperatorCallExpr` instead of `UnaryOperator` (all 
hail clang AST!).


https://reviews.llvm.org/D31982



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


[PATCH] D31982: [analyzer] Improve suppression for inlined defensive checks when operator& is involved.

2017-04-12 Thread Alexander Shaposhnikov via Phabricator via cfe-commits
alexshap added inline comments.



Comment at: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp:965
+
+// Performing operator `&' on an lvalue expression is essentially a no-op.
+// Then, if we are taking addresses of fields or elements, these are also

"Address-of" operator can be overloaded, 
just wondering - doest this code work correctly in that case ?


https://reviews.llvm.org/D31982



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


[PATCH] D31982: [analyzer] Improve suppression for inlined defensive checks when operator& is involved.

2017-04-12 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.

In code

  void foo(int *p) {
if (p) {}
  }
  void bar(int *p) {
foo(p);
*p = 5;
  }

we suppress the null dereference warning in `*p = 5` because the state split 
within the inlined function is essentially irrelevant, as this is merely a 
defensive check that does not necessarily trigger in this caller context.

The suppression works by tracking the null pointer symbol in a bug report 
visitor and seeing if it was constrained to 0 inside a callee context. The fact 
that we have to rely on such manual suppressions is a downside of the 
inlining-based approach to interprocedural analysis.

The suppression gets confused when the null dereference becomes more complex, 
eg:

  struct S {
int x;
  }
  void foo(struct S *s) {
if (s) {}
  }
  void bar(struct S *s) {
foo(s);
*(&(s->x)) = 5;
  }

In this case `s` is `{reg_$0}`, and `reg_$0` is constrained to 
`[0, 0]`, similarly to the above. However, while computing `s->x` and then 
`&(s->x)`, the symbol is collapsed to a constant `0 (Loc)`, making it harder to 
track the symbol. The visitor is improved to handle this case.

While the fact that //offset of field `x` in struct `S` in our example is equal 
to 0// is purely accidental, with any other offset it'd still be a null 
dereference, worth suppressing. How does the analyzer handle the non-zero 
offset case and still see a null dereference? Simply and powerfully: it 
mis-computes the offset, incorrectly believing that all offsets are equal to 0 
(woohoo!). I add a test case to document this behavior; even though it's 
trivial to fix, the positive side effects of this bug are for now too useful to 
discard.


https://reviews.llvm.org/D31982

Files:
  lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
  lib/StaticAnalyzer/Core/Store.cpp
  test/Analysis/explain-svals.cpp
  test/Analysis/inlining/inline-defensive-checks.c
  test/Analysis/inlining/inline-defensive-checks.cpp
  test/Analysis/uninit-const.cpp

Index: test/Analysis/uninit-const.cpp
===
--- test/Analysis/uninit-const.cpp
+++ test/Analysis/uninit-const.cpp
@@ -122,7 +122,7 @@
 }
 
 void f_uninit(void) {
-  int x;
+  int x;   // expected-note {{'x' declared without an initial value}}
   doStuff_uninit();  // expected-warning {{1st function call argument is a pointer to uninitialized value}}
// expected-note@-1 {{1st function call argument is a pointer to uninitialized value}}
 }
Index: test/Analysis/inlining/inline-defensive-checks.cpp
===
--- test/Analysis/inlining/inline-defensive-checks.cpp
+++ test/Analysis/inlining/inline-defensive-checks.cpp
@@ -70,4 +70,17 @@
 void test(int *p1, int *p2) {
   idc(p1);
 	Foo f(p1);
-}
\ No newline at end of file
+}
+
+struct Bar {
+  int x;
+};
+void idcBar(Bar *b) {
+  if (b)
+;
+}
+void testRefToField(Bar *b) {
+  idcBar(b);
+  int  = b->x; // no-warning
+  x = 5;
+}
Index: test/Analysis/inlining/inline-defensive-checks.c
===
--- test/Analysis/inlining/inline-defensive-checks.c
+++ test/Analysis/inlining/inline-defensive-checks.c
@@ -1,7 +1,7 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-config suppress-inlined-defensive-checks=true -verify %s
 
 // Perform inline defensive checks.
-void idc(int *p) {
+void idc(void *p) {
 	if (p)
 		;
 }
@@ -139,3 +139,42 @@
   int z = y;
   idcTriggerZeroValueThroughCall(z);
 }
+
+struct S {
+  int f1;
+  int f2;
+};
+
+void idcTrackZeroValueThroughUnaryPointerOperators(struct S *s) {
+  idc(s);
+  *(&(s->f1)) = 7; // no-warning
+}
+
+void idcTrackZeroValueThroughUnaryPointerOperatorsWithOffset1(struct S *s) {
+  idc(s);
+  int *x = &(s->f2);
+  *x = 7; // no-warning
+}
+
+void idcTrackZeroValueThroughUnaryPointerOperatorsWithOffset2(struct S *s) {
+  idc(s);
+  int *x = &(s->f2) - 1;
+  // FIXME: Should not warn.
+  *x = 7; // expected-warning{{Dereference of null pointer}}
+}
+
+void idcTrackZeroValueThroughUnaryPointerOperatorsWithAssignment(struct S *s) {
+  idc(s);
+  int *x = &(s->f1);
+  *x = 7; // no-warning
+}
+
+
+struct S2 {
+  int a[1];
+};
+
+void idcTrackZeroValueThroughUnaryPointerOperatorsWithArrayField(struct S2 *s) {
+  idc(s);
+  *(&(s->a[0])) = 7; // no-warning
+}
Index: test/Analysis/explain-svals.cpp
===
--- test/Analysis/explain-svals.cpp
+++ test/Analysis/explain-svals.cpp
@@ -81,9 +81,10 @@
 
 namespace {
 class C {
+public:
   int x[10];
+  int y;
 
-public:
   void test_5(int i) {
 clang_analyzer_explain(this); // expected-warning-re^pointer to 'this' object$
 clang_analyzer_explain([i]); // expected-warning-re^pointer to element of type 'int' with index 'argument 'i'' of field 'x' of 'this' object$
@@ -96,3 +97,9 @@