[PATCH] D60281: [analyzer] Add docs for cplusplus.InnerPointer

2019-08-14 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

In D60281#1630337 , @Szelethus wrote:

> I'll gladly add the finishing touches :)


So sorry for leaving this hanging!
Thanks Husi, you da best :)


Repository:
  rC Clang

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

https://reviews.llvm.org/D60281



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


[PATCH] D60281: [analyzer] Add docs for cplusplus.InnerPointer

2019-04-04 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, xazax.hun, Szelethus, dcoughlin, dkrupp.
Herald added subscribers: cfe-commits, Charusso, gamesh411, donat.nagy, 
mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, whisperity.
Herald added a project: clang.

Tried to pick two interesting examples from the tests.
This check has no options.


Repository:
  rC Clang

https://reviews.llvm.org/D60281

Files:
  docs/analyzer/checkers.rst


Index: docs/analyzer/checkers.rst
===
--- docs/analyzer/checkers.rst
+++ docs/analyzer/checkers.rst
@@ -216,10 +216,34 @@
 
 C++ Checkers.
 
-cplusplus.InnerPointer
-""
+cplusplus.InnerPointer (C++)
+
 Check for inner pointers of C++ containers used after re/deallocation.
 
+In its present state, the check detects incorrect uses of raw inner pointers of
+``std::string``s, by recognizing member functions that may re/deallocate the 
buffer
+before use. In the future, it would be great to add support for other STL and
+non-STL containers, and most notably, ``std::string_view``s.
+
+.. code-block:: cpp
+
+ void consume(const char *);
+
+ void test_deref_after_equals() {
+   std::string s = "llvm";
+   const char *c = s.data(); // note: pointer to inner buffer of 'std::string' 
obtained here
+   s = "clang"; // note: inner buffer of 'std::string' reallocated by call to 
'operator='
+   consume(c); // warn: inner pointer of container used after re/deallocation
+ }
+
+ const char *test_return_temp() {
+   int x;
+   return std::to_string(x).c_str(); // warn: inner pointer of container used 
after re/deallocation
+   // note: pointer to inner buffer of 'std::string' obtained here
+   // note: inner buffer of 'std::string' deallocated by call to destructor
+ }
+
+
 cplusplus.NewDelete (C++)
 "
 Check for double-free and use-after-free problems. Traces memory managed by 
new/delete.


Index: docs/analyzer/checkers.rst
===
--- docs/analyzer/checkers.rst
+++ docs/analyzer/checkers.rst
@@ -216,10 +216,34 @@
 
 C++ Checkers.
 
-cplusplus.InnerPointer
-""
+cplusplus.InnerPointer (C++)
+
 Check for inner pointers of C++ containers used after re/deallocation.
 
+In its present state, the check detects incorrect uses of raw inner pointers of
+``std::string``s, by recognizing member functions that may re/deallocate the buffer
+before use. In the future, it would be great to add support for other STL and
+non-STL containers, and most notably, ``std::string_view``s.
+
+.. code-block:: cpp
+
+ void consume(const char *);
+
+ void test_deref_after_equals() {
+   std::string s = "llvm";
+   const char *c = s.data(); // note: pointer to inner buffer of 'std::string' obtained here
+   s = "clang"; // note: inner buffer of 'std::string' reallocated by call to 'operator='
+   consume(c); // warn: inner pointer of container used after re/deallocation
+ }
+
+ const char *test_return_temp() {
+   int x;
+   return std::to_string(x).c_str(); // warn: inner pointer of container used after re/deallocation
+   // note: pointer to inner buffer of 'std::string' obtained here
+   // note: inner buffer of 'std::string' deallocated by call to destructor
+ }
+
+
 cplusplus.NewDelete (C++)
 "
 Check for double-free and use-after-free problems. Traces memory managed by new/delete.
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D59279: [Analyzer] Checker for non-determinism caused by iteration of unordered container of pointers

2019-03-26 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

In D59279#1438514 , @mgrang wrote:

> Yes, the reason we limit the checks only to //unordered// containers is to 
> reduce the false positive rate. Although, as you rightly pointed out that 
> //ordered// sets of pointers are as non-deterministic as //unordered// ones. 
> Once our checks have the capability to detect what happens inside the loop 
> maybe we can add //ordered// sets too. I will add this to the TODO. Thanks.


Great, thanks!


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

https://reviews.llvm.org/D59279



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


[PATCH] D59195: [analyzer] Remove the default value arg from getChecker*Option

2019-03-26 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added inline comments.



Comment at: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp:148-150
+  assert(Ret &&
+ "This option should be either 'true' or 'false', and should've been "
+ "validated by CheckerRegisrty!");

NoQ wrote:
> Even though `operator*()` would have asserted that there's a value, i really 
> appreciate the user-friendly assert message.
Sorry for the impertinence, but there's a typo: `CheckerRegisrty`.



Comment at: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp:171
+ "This option should be numeric, and should've been validated by "
+ "CheckerRegisrty!");
   (void)HasFailed;

And the same typo here.


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

https://reviews.llvm.org/D59195



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


[PATCH] D59279: [Analyzer] Checker for non-determinism caused by iteration of unordered container of pointers

2019-03-21 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

In D59279#1427017 , @mgrang wrote:

> Following are the assumptions/limitations of this patch:
>
>   1. The assumption is that iteration of ordered containers of pointers is 
> not non-deterministic.
>


Could you please explain which type of non-determinism we are addressing here? 
If our issue is that iteration order is not consistent across runs, then an 
unordered set of //integers// seems just as non-deterministic as an unordered 
set of //pointers//. On the other hand, if our issue is that pointer values 
vary between runs, then an //ordered// set of pointers seems just as 
non-deterministic as an //unordered// set of pointers. Are //unordered// sets 
of //pointers// distinguished because they lie in the intersection of these 
categories and thus avoid the most false positive cases? If so, for someone 
debugging non-deterministic behavior in their code, would it be useful to add a 
strict option that shows other cases too? If not, maybe we could document our 
reasons somewhere.


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

https://reviews.llvm.org/D59279



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


[PATCH] D53856: [analyzer] Put llvm.Conventions back in alpha

2018-11-02 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

In https://reviews.llvm.org/D53856#1280408, @Szelethus wrote:

> In https://reviews.llvm.org/D53856#1279887, @NoQ wrote:
>
> > This might be also covered by @rnkovacs's string buffer escape checker - 
> > either already or eventually, it'll become just yet another string view API 
> > that the checker supports.
>
>
> I thought about that too, adding some `StringRef` specific information to 
> that checker makes more sense then essentially duplicating the logic. 
> However, what @MTC mentioned about `ArrayRef` would be a neat addition 
> too, and maybe it isn't worth making `InnerPointerChecker` //that// general.
>
> @rnkovacs, any thoughts?


I agree, adding `StringRef` support to the buffer escape checker around the 
time we add `string_view` will be great. Also, we named it 
`InnerPointerChecker` to allow for covering more general structures in the 
future, so I guess `ArrayRef`s may also be added eventually. Unfortunately, I 
have some other tasks to finish before getting to these, so I think this patch 
is fine now as-is.


https://reviews.llvm.org/D53856



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


[PATCH] D53069: [analyzer][www] Update avaible_checks.html

2018-10-10 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added inline comments.



Comment at: www/analyzer/available_checks.html:376-393
+
+cplusplus.InnerPointer
+(C++)
+Check for inner pointers of C++ containers used after re/deallocation.
+
+
+

Szelethus wrote:
> @rnkovacs Is this a good description of your checker?
Hmm, how about:

```
void log(const char *str);

void test(int value) {
  const char *msg = std::to_string(value).c_str();
  // msg points to the buffer of a temporary that is now destroyed
  log(msg);  // warn: inner pointer of container used after re/deallocation
}
```

Most of the issues it found in real code looked like this.
Thanks a lot!


https://reviews.llvm.org/D53069



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


[PATCH] D51385: [analyzer] InnerPointerChecker: Fix a segfault.

2018-08-28 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

> Return value of `dyn_cast_or_null` should be checked before use. Otherwise we 
> may put a null pointer into the map as a key and eventually crash in 
> `checkDeadSymbols`.

Hm, so with the last `CallDescription` patch we removed some code here that 
essentially checked if the same region was null before this cast, which means 
two things: a) in the previous version it probably should have been a 
`dyn_cast` instead of `dyn_cast_or_null`, but now that makes it accidentally 
fine, and b) I should have thought about this when that code was removed.

> Reka: Why did we restrict ourselves to `TypedValueRegions` here? While we are 
> mostly interested in local string variables and temporaries, which would of 
> course be typed, i guess there's nothing that prevents us from checking that 
> we don't `delete` or mutate a string in a `SymbolicRegion` somewhere between 
> obtaining and using its inner pointer.

I think the reason is that previously `CallDescription`s didn't match fully 
qualified function names and the type was needed to see if the object was a 
`string`.


Repository:
  rC Clang

https://reviews.llvm.org/D51385



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


[PATCH] D48027: [analyzer] Improve `CallDescription` to handle c++ method.

2018-08-21 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs accepted this revision.
rnkovacs added a comment.

In https://reviews.llvm.org/D48027#1203944, @MTC wrote:

> However this approach has limit. Given the code below, we cannot distinguish 
> whether the `basic_string` is user-defined struct or namespace. That's means 
> when the user provide {"std", "basic_string", "append"}, we can only know the 
> qualified name of the call sequentially contains `std`, `basic_string`, 
> `append`. We don't know if these names come from `RecordDecl` or 
> `NamespaceDecl`.
>
>   namespace  std {
> namespace basic_string {
>   struct A {
> void append() {}
>   };
> }
>   }
>  
>   void foo() {
> std::basic_string::A a;
> a.append(); // Match
>   }
>
>
> @rnkovacs What do you think? Can this approach meet `InnerPointerChecker`'s 
> needs?


I guess it is highly unlikely for someone to write namespaces like that, and if 
they do, I think they deserve to have them treated as a `std::string`.
Thanks, Henry, this is great!


https://reviews.llvm.org/D48027



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


[PATCH] D49570: [analyzer] Improve warning messages and notes of DanglingInternalBufferChecker

2018-08-10 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs marked an inline comment as done.
rnkovacs added inline comments.



Comment at: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp:253
+  allocation_state::getContainerObjRegion(N->getState(), PtrToBuf);
+  const auto *TypedRegion = dyn_cast(ObjRegion);
+  QualType ObjTy = TypedRegion->getValueType();

NoQ wrote:
> `dyn_cast` may fail by returning a null pointer. This either needs to be 
> changed to `cast` or there needs to be a check for a null pointer before use. 
> I guess it should be a `cast` because you're only acting on typed regions in 
> the checker itself.
I hope that in a few more reviews I'll learn to use all of these properly.



Comment at: lib/StaticAnalyzer/Checkers/MallocChecker.cpp:2931-2932
+OS << "deallocated by call to destructor";
+StackHint = new StackHintGeneratorForSymbol(Sym,
+  "Returning; inner buffer was 
deallocated");
   } else {

NoQ wrote:
> Cool stuff!
:)



Comment at: test/Analysis/dangling-internal-buffer.cpp:63
 // expected-note@-4 {{Taking false branch}}
-consume(c); // expected-warning {{Use of memory after it is freed}}
-// expected-note@-1 {{Use of memory after it is freed}}
+consume(c); // expected-warning {{Deallocated pointer returned to the 
caller}}
+// expected-note@-1 {{Deallocated pointer returned to the caller}}

NoQ wrote:
> Mm, nono, there's no `return` statement here, so we shouldn't say that our 
> pointer is returned to the caller. Whether it should say "returned to the 
> caller" or not, should not depend on the allocation family, but on the kind 
> of "use" we encounter "after" "free".
I don't even know how this went so off, sorry.


https://reviews.llvm.org/D49570



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


[PATCH] D49570: [analyzer] Improve warning messages and notes of DanglingInternalBufferChecker

2018-08-10 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 160193.
rnkovacs marked 3 inline comments as done.
rnkovacs added a comment.

Address comments & rebase.


https://reviews.llvm.org/D49570

Files:
  lib/StaticAnalyzer/Checkers/AllocationState.h
  lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/inner-pointer.cpp

Index: test/Analysis/inner-pointer.cpp
===
--- test/Analysis/inner-pointer.cpp
+++ test/Analysis/inner-pointer.cpp
@@ -65,106 +65,106 @@
   const char *c, *d;
   {
 std::string s;
-c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
-d = s.data();  // expected-note {{Dangling inner pointer obtained here}}
-  }// expected-note {{Inner pointer invalidated by call to destructor}}
-  // expected-note@-1 {{Inner pointer invalidated by call to destructor}}
+c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
+d = s.data();  // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
+  }// expected-note {{Inner buffer of 'std::string' deallocated by call to destructor}}
+  // expected-note@-1 {{Inner buffer of 'std::string' deallocated by call to destructor}}
   std::string s;
   const char *c2 = s.c_str();
   if (cond) {
 // expected-note@-1 {{Assuming 'cond' is not equal to 0}}
 // expected-note@-2 {{Taking true branch}}
 // expected-note@-3 {{Assuming 'cond' is 0}}
 // expected-note@-4 {{Taking false branch}}
-consume(c); // expected-warning {{Use of memory after it is freed}}
-// expected-note@-1 {{Use of memory after it is freed}}
+consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}}
+// expected-note@-1 {{Inner pointer of container used after re/deallocation}}
   } else {
-consume(d); // expected-warning {{Use of memory after it is freed}}
-// expected-note@-1 {{Use of memory after it is freed}}
+consume(d); // expected-warning {{Inner pointer of container used after re/deallocation}}
+// expected-note@-1 {{Inner pointer of container used after re/deallocation}}
   }
 }
 
 void deref_after_scope_char_data_non_const() {
   char *c;
   {
 std::string s;
-c = s.data(); // expected-note {{Dangling inner pointer obtained here}}
-  }   // expected-note {{Inner pointer invalidated by call to destructor}}
+c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
+  }   // expected-note {{Inner buffer of 'std::string' deallocated by call to destructor}}
   std::string s;
   char *c2 = s.data();
-  consume(c); // expected-warning {{Use of memory after it is freed}}
-  // expected-note@-1 {{Use of memory after it is freed}}
+  consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}}
+  // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 }
 
 void deref_after_scope_wchar_t(bool cond) {
   const wchar_t *c, *d;
   {
 std::wstring s;
-c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
-d = s.data();  // expected-note {{Dangling inner pointer obtained here}}
-  }// expected-note {{Inner pointer invalidated by call to destructor}}
-  // expected-note@-1 {{Inner pointer invalidated by call to destructor}}
+c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::wstring' obtained here}}
+d = s.data();  // expected-note {{Pointer to inner buffer of 'std::wstring' obtained here}}
+  }// expected-note {{Inner buffer of 'std::wstring' deallocated by call to destructor}}
+  // expected-note@-1 {{Inner buffer of 'std::wstring' deallocated by call to destructor}}
   std::wstring s;
   const wchar_t *c2 = s.c_str();
   if (cond) {
 // expected-note@-1 {{Assuming 'cond' is not equal to 0}}
 // expected-note@-2 {{Taking true branch}}
 // expected-note@-3 {{Assuming 'cond' is 0}}
 // expected-note@-4 {{Taking false branch}}
-consume(c); // expected-warning {{Use of memory after it is freed}}
-// expected-note@-1 {{Use of memory after it is freed}}
+consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}}
+// expected-note@-1 {{Inner pointer of container used after re/deallocation}}
   } else {
-consume(d); // expected-warning {{Use of memory after it is freed}}
-// expected-note@-1 {{Use of memory after it is freed}}
+consume(d); // expected-warning {{Inner pointer of container used after re/deallocation}}
+// expected-note@-1 {{Inner pointer of container used after re/deallocation}}
   }
 }
 
 void deref_after_scope_char16_t_cstr() {
   const char16_t *c16;
   {
 std::u16string s16;
-c16 = s16.c_str(); // expected-note {{Dangling inner pointer obtained here}}
-  }// expected-note 

[PATCH] D50211: [analyzer] Fix displayed checker name for InnerPointerChecker

2018-08-06 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

In https://reviews.llvm.org/D50211#1190146, @NoQ wrote:

> Welcome to the club!


:D
Thanks, makes me feel better.


https://reviews.llvm.org/D50211



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


[PATCH] D50211: [analyzer] Fix displayed checker name for InnerPointerChecker

2018-08-06 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs closed this revision.
rnkovacs added a comment.

Committed in r339067, I just messed up the revision-closing line in the commit 
message.


https://reviews.llvm.org/D50211



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


[PATCH] D50211: [analyzer] Fix displayed checker name for InnerPointerChecker

2018-08-06 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 159244.
rnkovacs marked an inline comment as done.
rnkovacs added a comment.

Replace empty `Optional`s with `None`s.


https://reviews.llvm.org/D50211

Files:
  lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
  lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp

Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -194,6 +194,7 @@
 CK_NewDeleteChecker,
 CK_NewDeleteLeaksChecker,
 CK_MismatchedDeallocatorChecker,
+CK_InnerPointerChecker,
 CK_NumCheckKinds
   };
 
@@ -1662,22 +1663,24 @@
   case AF_IfNameIndex: {
 if (ChecksEnabled[CK_MallocChecker])
   return CK_MallocChecker;
-
-return Optional();
+return None;
   }
   case AF_CXXNew:
-  case AF_CXXNewArray:
-  // FIXME: Add new CheckKind for AF_InnerBuffer.
-  case AF_InnerBuffer: {
+  case AF_CXXNewArray: {
 if (IsALeakCheck) {
   if (ChecksEnabled[CK_NewDeleteLeaksChecker])
 return CK_NewDeleteLeaksChecker;
 }
 else {
   if (ChecksEnabled[CK_NewDeleteChecker])
 return CK_NewDeleteChecker;
 }
-return Optional();
+return None;
+  }
+  case AF_InnerBuffer: {
+if (ChecksEnabled[CK_InnerPointerChecker])
+  return CK_InnerPointerChecker;
+return None;
   }
   case AF_None: {
 llvm_unreachable("no family");
@@ -1980,7 +1983,8 @@
SymbolRef Sym) const {
 
   if (!ChecksEnabled[CK_MallocChecker] &&
-  !ChecksEnabled[CK_NewDeleteChecker])
+  !ChecksEnabled[CK_NewDeleteChecker] &&
+  !ChecksEnabled[CK_InnerPointerChecker])
 return;
 
   Optional CheckKind = getCheckIfTracked(C, Sym);
@@ -3109,6 +3113,18 @@
   }
 }
 
+// Intended to be used in InnerPointerChecker to register the part of
+// MallocChecker connected to it.
+void ento::registerInnerPointerCheckerAux(CheckerManager ) {
+registerCStringCheckerBasic(mgr);
+MallocChecker *checker = mgr.registerChecker();
+checker->IsOptimistic = mgr.getAnalyzerOptions().getBooleanOption(
+"Optimistic", false, checker);
+checker->ChecksEnabled[MallocChecker::CK_InnerPointerChecker] = true;
+checker->CheckNames[MallocChecker::CK_InnerPointerChecker] =
+mgr.getCurrentCheckName();
+}
+
 #define REGISTER_CHECKER(name) \
   void ento::register##name(CheckerManager ) { \
 registerCStringCheckerBasic(mgr);  \
Index: lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
===
--- lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
+++ lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
@@ -20,5 +20,8 @@
 /// Register the checker which evaluates CString API calls.
 void registerCStringCheckerBasic(CheckerManager );
 
+/// Register the part of MallocChecker connected to InnerPointerChecker.
+void registerInnerPointerCheckerAux(CheckerManager );
+
 }}
 #endif /* INTERCHECKERAPI_H_ */
Index: lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
+++ lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -15,6 +15,7 @@
 
 #include "AllocationState.h"
 #include "ClangSACheckers.h"
+#include "InterCheckerAPI.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
@@ -313,6 +314,6 @@
 } // end namespace clang
 
 void ento::registerInnerPointerChecker(CheckerManager ) {
-  registerNewDeleteChecker(Mgr);
+  registerInnerPointerCheckerAux(Mgr);
   Mgr.registerChecker();
 }
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50211: [analyzer] Fix displayed checker name for InnerPointerChecker

2018-08-03 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

In https://reviews.llvm.org/D50211#1186630, @NoQ wrote:

> I see, so that's how it's done!
>
> I also noticed that checker name was weird in exploded graph dumps, i.e. it 
> was showing regular new/delete stuff as if it was done by InnerPointer 
> checker. I'll check if this is fixed tomorrow.


This seemed interesting, so I tried out myself on the 
`NewDelete-path-notes.cpp` test file. I saw the following (using IP and ND 
abbreviations for InnerPointer and NewDelete respectively):

Before the patch,

- If only IP was turned on, I got ND's warnings with IP's name everywhere in 
the exploded graph.
- If only ND was turned on, I got the warnings under ND's tag.
- If both IP and ND were turned on, I got the warnings, and saw ND's name 
beside any allocation state change in the graph, but the bugs seemed to be 
tagged with IP's name in the end.

After this patch,

- If only IP is turned on, I get no warning but I see IP's tag in the graph on 
the last PurgeDeadSymbols node (I guess it's normal?).
- If only ND is turned on, I get the warnings with ND's tag everywhere.
- If both IP and ND are turned on, I get the warnings, and I see no trace of 
IP, only ND tags everywhere.

It was really messed up before, but I think I see an improvement there.


Repository:
  rC Clang

https://reviews.llvm.org/D50211



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


[PATCH] D50211: [analyzer] Fix displayed checker name for InnerPointerChecker

2018-08-02 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, xazax.hun, george.karpenkov.
Herald added subscribers: mikhail.ramalho, a.sidorin, dkrupp, szepet, 
baloghadamsoftware, whisperity.

For `InnerPointerChecker` to function properly, both the checker itself and 
`MallocChecker`'s capabilities that handle relevant use-after-free problems 
need to be turned on. So far, the latter part has been developed under the name 
of `MallocChecker`'s `NewDelete` sub-checker, often causing warnings to appear 
under that name. This patch defines a new `CheckKind` within `MallocChecker` 
for the inner pointer checking functionality, so that the correct name is 
displayed on warnings.

Tested on `clang-tidy`.


Repository:
  rC Clang

https://reviews.llvm.org/D50211

Files:
  lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
  lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp


Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -194,6 +194,7 @@
 CK_NewDeleteChecker,
 CK_NewDeleteLeaksChecker,
 CK_MismatchedDeallocatorChecker,
+CK_InnerPointerChecker,
 CK_NumCheckKinds
   };
 
@@ -1666,9 +1667,7 @@
 return Optional();
   }
   case AF_CXXNew:
-  case AF_CXXNewArray:
-  // FIXME: Add new CheckKind for AF_InnerBuffer.
-  case AF_InnerBuffer: {
+  case AF_CXXNewArray: {
 if (IsALeakCheck) {
   if (ChecksEnabled[CK_NewDeleteLeaksChecker])
 return CK_NewDeleteLeaksChecker;
@@ -1679,6 +1678,12 @@
 }
 return Optional();
   }
+  case AF_InnerBuffer: {
+if (ChecksEnabled[CK_InnerPointerChecker])
+  return CK_InnerPointerChecker;
+
+return Optional();
+  }
   case AF_None: {
 llvm_unreachable("no family");
   }
@@ -1980,7 +1985,8 @@
SymbolRef Sym) const {
 
   if (!ChecksEnabled[CK_MallocChecker] &&
-  !ChecksEnabled[CK_NewDeleteChecker])
+  !ChecksEnabled[CK_NewDeleteChecker] &&
+  !ChecksEnabled[CK_InnerPointerChecker])
 return;
 
   Optional CheckKind = getCheckIfTracked(C, Sym);
@@ -3109,6 +3115,18 @@
   }
 }
 
+// Intended to be used in InnerPointerChecker to register the part of
+// MallocChecker connected to it.
+void ento::registerInnerPointerCheckerAux(CheckerManager ) {
+registerCStringCheckerBasic(mgr);
+MallocChecker *checker = mgr.registerChecker();
+checker->IsOptimistic = mgr.getAnalyzerOptions().getBooleanOption(
+"Optimistic", false, checker);
+checker->ChecksEnabled[MallocChecker::CK_InnerPointerChecker] = true;
+checker->CheckNames[MallocChecker::CK_InnerPointerChecker] =
+mgr.getCurrentCheckName();
+}
+
 #define REGISTER_CHECKER(name) 
\
   void ento::register##name(CheckerManager ) { 
\
 registerCStringCheckerBasic(mgr);  
\
Index: lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
===
--- lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
+++ lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
@@ -20,5 +20,8 @@
 /// Register the checker which evaluates CString API calls.
 void registerCStringCheckerBasic(CheckerManager );
 
+/// Register the part of MallocChecker connected to InnerPointerChecker.
+void registerInnerPointerCheckerAux(CheckerManager );
+
 }}
 #endif /* INTERCHECKERAPI_H_ */
Index: lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
+++ lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -15,6 +15,7 @@
 
 #include "AllocationState.h"
 #include "ClangSACheckers.h"
+#include "InterCheckerAPI.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
@@ -316,6 +317,6 @@
 } // end namespace clang
 
 void ento::registerInnerPointerChecker(CheckerManager ) {
-  registerNewDeleteChecker(Mgr);
+  registerInnerPointerCheckerAux(Mgr);
   Mgr.registerChecker();
 }


Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -194,6 +194,7 @@
 CK_NewDeleteChecker,
 CK_NewDeleteLeaksChecker,
 CK_MismatchedDeallocatorChecker,
+CK_InnerPointerChecker,
 CK_NumCheckKinds
   };
 
@@ -1666,9 +1667,7 @@
 return Optional();
   }
   case AF_CXXNew:
-  case AF_CXXNewArray:
-  // FIXME: Add new CheckKind for AF_InnerBuffer.
-  case AF_InnerBuffer: {
+  case AF_CXXNewArray: {
 if (IsALeakCheck) {
   if 

[PATCH] D49361: [analyzer] Detect pointers escaped after return statement execution in MallocChecker

2018-08-01 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 158681.
rnkovacs marked an inline comment as done.
rnkovacs added a comment.

Add helper function to be used in both callbacks.


https://reviews.llvm.org/D49361

Files:
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/inner-pointer.cpp
  test/Analysis/malloc-free-after-return.cpp

Index: test/Analysis/malloc-free-after-return.cpp
===
--- /dev/null
+++ test/Analysis/malloc-free-after-return.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.NewDelete -verify %s
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+struct S {
+  S() : Data(new int) {}
+  ~S() { delete Data; }
+  int *getData() { return Data; }
+
+private:
+  int *Data;
+};
+
+int *freeAfterReturnTemp() {
+  return S().getData(); // expected-warning {{Use of memory after it is freed}}
+}
+
+int *freeAfterReturnLocal() {
+  S X;
+  return X.getData();
+} // expected-warning {{Use of memory after it is freed}}
Index: test/Analysis/inner-pointer.cpp
===
--- test/Analysis/inner-pointer.cpp
+++ test/Analysis/inner-pointer.cpp
@@ -361,3 +361,24 @@
   consume(c);// expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
+
+struct S {
+  std::string to_string() { return s; }
+private:
+  std::string s;
+};
+
+const char *escape_via_return_temp() {
+  S x;
+  return x.to_string().c_str(); // expected-note {{Dangling inner pointer obtained here}}
+  // expected-note@-1 {{Inner pointer invalidated by call to destructor}}
+  // expected-warning@-2 {{Use of memory after it is freed}}
+  // expected-note@-3 {{Use of memory after it is freed}}
+}
+
+const char *escape_via_return_local() {
+  std::string s;
+  return s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
+// expected-note@-1 {{Inner pointer invalidated by call to destructor}}
+} // expected-warning {{Use of memory after it is freed}}
+// expected-note@-1 {{Use of memory after it is freed}}
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -161,6 +161,7 @@
  check::PointerEscape,
  check::ConstPointerEscape,
  check::PreStmt,
+ check::EndFunction,
  check::PreCall,
  check::PostStmt,
  check::PostStmt,
@@ -217,6 +218,7 @@
   void checkPostStmt(const BlockExpr *BE, CheckerContext ) const;
   void checkDeadSymbols(SymbolReaper , CheckerContext ) const;
   void checkPreStmt(const ReturnStmt *S, CheckerContext ) const;
+  void checkEndFunction(const ReturnStmt *S, CheckerContext ) const;
   ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
 bool Assumption) const;
   void checkLocation(SVal l, bool isLoad, const Stmt *S,
@@ -353,7 +355,7 @@
   static ProgramStateRef CallocMem(CheckerContext , const CallExpr *CE,
ProgramStateRef State);
 
-  ///Check if the memory associated with this symbol was released.
+  /// Check if the memory associated with this symbol was released.
   bool isReleased(SymbolRef Sym, CheckerContext ) const;
 
   bool checkUseAfterFree(SymbolRef Sym, CheckerContext , const Stmt *S) const;
@@ -377,13 +379,16 @@
ProgramStateRef State,
SymbolRef ) const;
 
-  // Implementation of the checkPointerEscape callabcks.
+  // Implementation of the checkPointerEscape callbacks.
   ProgramStateRef checkPointerEscapeAux(ProgramStateRef State,
   const InvalidatedSymbols ,
   const CallEvent *Call,
   PointerEscapeKind Kind,
   bool(*CheckRefState)(const RefState*)) const;
 
+  // Implementation of the checkPreStmt and checkEndFunction callbacks.
+  void checkEscapeOnReturn(const ReturnStmt *S, CheckerContext ) const;
+
   ///@{
   /// Tells if a given family/call/symbol is tracked by the current checker.
   /// Sets CheckKind to the kind of the checker responsible for this
@@ -2451,7 +2456,24 @@
   }
 }
 
-void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext ) const {
+void MallocChecker::checkPreStmt(const ReturnStmt *S,
+ CheckerContext ) const {
+  checkEscapeOnReturn(S, C);
+}
+
+// In the CFG, automatic destructors come after the return statement.
+// This callback checks for returning memory that is freed by automatic
+// destructors, as those 

[PATCH] D49811: [analyzer] Obtain a ReturnStmt from a CFGAutomaticObjDtor

2018-08-01 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 158680.
rnkovacs added a comment.

In https://reviews.llvm.org/D49811#1175726, @NoQ wrote:

> I guess you could write a test with `debug.AnalysisOrder` (by making its 
> `checkEndFunction` callback (that you'll have to define) print different 
> things depending on the return statement), not sure if it's worth it; you can 
> also merge this commit with https://reviews.llvm.org/D49361 instead.


This is what I could come up with, what do you think?


https://reviews.llvm.org/D49811

Files:
  lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
  lib/StaticAnalyzer/Core/CoreEngine.cpp
  test/Analysis/end-function-return-stmt.cpp

Index: test/Analysis/end-function-return-stmt.cpp
===
--- /dev/null
+++ test/Analysis/end-function-return-stmt.cpp
@@ -0,0 +1,34 @@
+//RUN: %clang_analyze_cc1 -analyzer-checker=debug.AnalysisOrder -analyzer-config debug.AnalysisOrder:EndFunction=true %s 2>&1 | FileCheck %s
+
+// At the end of a function, we can only obtain a ReturnStmt if the last
+// CFGElement in the CFGBlock is either a CFGStmt or a CFGAutomaticObjDtor.
+
+void noReturnStmt() {}
+
+struct S {
+  S();
+  ~S();
+};
+
+int dtorAfterReturnStmt() {
+  S s;
+  return 0;
+}
+
+S endsWithReturnStmt() {
+  return S();
+}
+
+// endsWithReturnStmt()
+// CHECK:  EndFunction
+// CHECK-NEXT: ReturnStmt: yes
+// CHECK-NEXT: CFGElement: CFGStmt
+
+// dtorAfterReturnStmt()
+// CHECK:  EndFunction
+// CHECK-NEXT: ReturnStmt: yes
+// CHECK-NEXT: CFGElement: CFGAutomaticObjDtor
+
+// noReturnStmt()
+// CHECK:  EndFunction
+// CHECK-NEXT: ReturnStmt: no
Index: lib/StaticAnalyzer/Core/CoreEngine.cpp
===
--- lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -223,8 +223,12 @@
 // Get return statement..
 const ReturnStmt *RS = nullptr;
 if (!L.getSrc()->empty()) {
-  if (Optional LastStmt = L.getSrc()->back().getAs()) {
+  CFGElement LastElement = L.getSrc()->back();
+  if (Optional LastStmt = LastElement.getAs()) {
 RS = dyn_cast(LastStmt->getStmt());
+  } else if (Optional AutoDtor =
+ LastElement.getAs()) {
+RS = dyn_cast(AutoDtor->getTriggerStmt());
   }
 }
 
Index: lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
+++ lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
@@ -16,6 +16,7 @@
 
 #include "ClangSACheckers.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/Analysis/CFGStmtMap.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
@@ -37,6 +38,7 @@
  check::PostStmt,
  check::PreCall,
  check::PostCall,
+ check::EndFunction,
  check::NewAllocator,
  check::Bind,
  check::RegionChanges,
@@ -121,6 +123,23 @@
 }
   }
 
+  void checkEndFunction(const ReturnStmt *S, CheckerContext ) const {
+if (isCallbackEnabled(C, "EndFunction")) {
+  llvm::errs() << "EndFunction\nReturnStmt: " << (S ? "yes" : "no") << "\n";
+  if (!S)
+return;
+
+  llvm::errs() << "CFGElement: ";
+  CFGStmtMap *Map = C.getCurrentAnalysisDeclContext()->getCFGStmtMap();
+  CFGElement LastElement = Map->getBlock(S)->back();
+
+  if (LastElement.getAs())
+llvm::errs() << "CFGStmt\n";
+  else if (LastElement.getAs())
+llvm::errs() << "CFGAutomaticObjDtor\n";
+}
+  }
+
   void checkNewAllocator(const CXXNewExpr *CNE, SVal Target,
  CheckerContext ) const {
 if (isCallbackEnabled(C, "NewAllocator"))
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D49058: [analyzer] Move InnerPointerChecker out of alpha

2018-07-30 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 157987.
rnkovacs retitled this revision from "[analyzer] Move 
DanglingInternalBufferChecker out of alpha" to "[analyzer] Move 
InnerPointerChecker out of alpha".
rnkovacs added a comment.

Rebase.


https://reviews.llvm.org/D49058

Files:
  include/clang/StaticAnalyzer/Checkers/Checkers.td
  test/Analysis/inner-pointer.cpp


Index: test/Analysis/inner-pointer.cpp
===
--- test/Analysis/inner-pointer.cpp
+++ test/Analysis/inner-pointer.cpp
@@ -1,4 +1,4 @@
-//RUN: %clang_analyze_cc1 -analyzer-checker=alpha.cplusplus.InnerPointer %s 
-analyzer-output=text -verify
+//RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.InnerPointer %s 
-analyzer-output=text -verify
 
 namespace std {
 
Index: include/clang/StaticAnalyzer/Checkers/Checkers.td
===
--- include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -276,6 +276,10 @@
 
 let ParentPackage = Cplusplus in {
 
+def InnerPointerChecker : Checker<"InnerPointer">,
+  HelpText<"Check for inner pointers of C++ containers used after 
re/deallocation">,
+  DescFile<"InnerPointerChecker.cpp">;
+
 def NewDeleteChecker : Checker<"NewDelete">,
   HelpText<"Check for double-free and use-after-free problems. Traces memory 
managed by new/delete.">,
   DescFile<"MallocChecker.cpp">;
@@ -305,10 +309,6 @@
"destructor in their base class">,
   DescFile<"DeleteWithNonVirtualDtorChecker.cpp">;
 
-def InnerPointerChecker : Checker<"InnerPointer">,
-  HelpText<"Check for inner pointers of C++ containers used after 
re/deallocation">,
-  DescFile<"InnerPointerChecker.cpp">;
-
 def IteratorRangeChecker : Checker<"IteratorRange">,
   HelpText<"Check for iterators used outside their valid ranges">,
   DescFile<"IteratorChecker.cpp">;


Index: test/Analysis/inner-pointer.cpp
===
--- test/Analysis/inner-pointer.cpp
+++ test/Analysis/inner-pointer.cpp
@@ -1,4 +1,4 @@
-//RUN: %clang_analyze_cc1 -analyzer-checker=alpha.cplusplus.InnerPointer %s -analyzer-output=text -verify
+//RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.InnerPointer %s -analyzer-output=text -verify
 
 namespace std {
 
Index: include/clang/StaticAnalyzer/Checkers/Checkers.td
===
--- include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -276,6 +276,10 @@
 
 let ParentPackage = Cplusplus in {
 
+def InnerPointerChecker : Checker<"InnerPointer">,
+  HelpText<"Check for inner pointers of C++ containers used after re/deallocation">,
+  DescFile<"InnerPointerChecker.cpp">;
+
 def NewDeleteChecker : Checker<"NewDelete">,
   HelpText<"Check for double-free and use-after-free problems. Traces memory managed by new/delete.">,
   DescFile<"MallocChecker.cpp">;
@@ -305,10 +309,6 @@
"destructor in their base class">,
   DescFile<"DeleteWithNonVirtualDtorChecker.cpp">;
 
-def InnerPointerChecker : Checker<"InnerPointer">,
-  HelpText<"Check for inner pointers of C++ containers used after re/deallocation">,
-  DescFile<"InnerPointerChecker.cpp">;
-
 def IteratorRangeChecker : Checker<"IteratorRange">,
   HelpText<"Check for iterators used outside their valid ranges">,
   DescFile<"IteratorChecker.cpp">;
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D49361: [analyzer] Detect pointers escaped after return statement execution in MallocChecker

2018-07-30 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 157966.
rnkovacs marked an inline comment as done.
rnkovacs added a comment.

De-duplicate & add comment.


https://reviews.llvm.org/D49361

Files:
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/inner-pointer.cpp
  test/Analysis/malloc-free-after-return.cpp


Index: test/Analysis/malloc-free-after-return.cpp
===
--- /dev/null
+++ test/Analysis/malloc-free-after-return.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.NewDelete -verify %s
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+struct S {
+  S() : Data(new int) {}
+  ~S() { delete Data; }
+  int *getData() { return Data; }
+
+private:
+  int *Data;
+};
+
+int *freeAfterReturnTemp() {
+  return S().getData(); // expected-warning {{Use of memory after it is freed}}
+}
+
+int *freeAfterReturnLocal() {
+  S X;
+  return X.getData();
+} // expected-warning {{Use of memory after it is freed}}
Index: test/Analysis/inner-pointer.cpp
===
--- test/Analysis/inner-pointer.cpp
+++ test/Analysis/inner-pointer.cpp
@@ -361,3 +361,24 @@
   consume(c);// expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
+
+struct S {
+  std::string to_string() { return s; }
+private:
+  std::string s;
+};
+
+const char *escape_via_return_temp() {
+  S x;
+  return x.to_string().c_str(); // expected-note {{Dangling inner pointer 
obtained here}}
+  // expected-note@-1 {{Inner pointer invalidated by call to destructor}}
+  // expected-warning@-2 {{Use of memory after it is freed}}
+  // expected-note@-3 {{Use of memory after it is freed}}
+}
+
+const char *escape_via_return_local() {
+  std::string s;
+  return s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
+// expected-note@-1 {{Inner pointer invalidated by call to 
destructor}}
+} // expected-warning {{Use of memory after it is freed}}
+// expected-note@-1 {{Use of memory after it is freed}}
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -161,6 +161,7 @@
  check::PointerEscape,
  check::ConstPointerEscape,
  check::PreStmt,
+ check::EndFunction,
  check::PreCall,
  check::PostStmt,
  check::PostStmt,
@@ -217,6 +218,7 @@
   void checkPostStmt(const BlockExpr *BE, CheckerContext ) const;
   void checkDeadSymbols(SymbolReaper , CheckerContext ) const;
   void checkPreStmt(const ReturnStmt *S, CheckerContext ) const;
+  void checkEndFunction(const ReturnStmt *S, CheckerContext ) const;
   ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
 bool Assumption) const;
   void checkLocation(SVal l, bool isLoad, const Stmt *S,
@@ -2475,6 +2477,17 @@
 checkUseAfterFree(Sym, C, E);
 }
 
+// In the CFG, automatic destructors come after the return statement.
+// This callback checks for returning memory that is freed by automatic
+// destructors, as those cannot be reached from `checkPreStmt()`.
+void MallocChecker::checkEndFunction(const ReturnStmt *S,
+ CheckerContext ) const {
+  if (!S)
+return;
+
+  checkPreStmt(S, C);
+}
+
 // TODO: Blocks should be either inlined or should call invalidate regions
 // upon invocation. After that's in place, special casing here will not be
 // needed.


Index: test/Analysis/malloc-free-after-return.cpp
===
--- /dev/null
+++ test/Analysis/malloc-free-after-return.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.NewDelete -verify %s
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+struct S {
+  S() : Data(new int) {}
+  ~S() { delete Data; }
+  int *getData() { return Data; }
+
+private:
+  int *Data;
+};
+
+int *freeAfterReturnTemp() {
+  return S().getData(); // expected-warning {{Use of memory after it is freed}}
+}
+
+int *freeAfterReturnLocal() {
+  S X;
+  return X.getData();
+} // expected-warning {{Use of memory after it is freed}}
Index: test/Analysis/inner-pointer.cpp
===
--- test/Analysis/inner-pointer.cpp
+++ test/Analysis/inner-pointer.cpp
@@ -361,3 +361,24 @@
   consume(c);// expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
+
+struct S {
+  std::string to_string() { return s; }
+private:
+  std::string s;
+};
+
+const char 

[PATCH] D49656: [analyzer] Add support for more pointer invalidating functions in InnerPointerChecker

2018-07-27 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added inline comments.



Comment at: lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp:192
 
-  if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) {
-SVal RawPtr = Call.getReturnValue();
-if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) {
-  // Start tracking this raw pointer by adding it to the set of symbols
-  // associated with this container object in the program state map.
-  PtrSet::Factory  = State->getStateManager().get_context();
-  const PtrSet *SetPtr = State->get(ObjRegion);
-  PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet();
-  assert(C.wasInlined || !Set.contains(Sym));
-  Set = F.add(Set, Sym);
-  State = State->set(ObjRegion, Set);
-  C.addTransition(State);
+  SVal Arg = FC->getArgSVal(ArgI);
+  const auto *ArgRegion =

NoQ wrote:
> xazax.hun wrote:
> > While I cannot recall examples in the STL the number of arguments and 
> > parameters might differ. We might have ellipsis in the parameters and this 
> > way we may pass more arguments. Since we do not have the parameter type for 
> > this case, I think it is ok to not handle it. But it might be worth to have 
> > a comment. The other case, when we have default arguments. I do not really 
> > know how the analyzer behaves with default arguments but maybe it would be 
> > worth to add a test case to ensure we do not crash on that?
> The analyzer behaves pretty badly with default arguments; we don't really 
> model them unless they are like plain integer literals. But that's not an 
> issue here because a default parameter/argument is still both a parameter and 
> an argument (where the argument expression takes the form of 
> `CXXDefaultArgExpr`).
Hm, it seems that passing objects of non-trivial types like `std::string` to a 
variadic function is implementation defined, and for `clang 6.0`, this means 
that it does not compile (does compile with `gcc 8.1` though, [[ 
https://godbolt.org/g/C3MCtV | example ]]).

Added a test for the default parameter case.


https://reviews.llvm.org/D49656



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


[PATCH] D49656: [analyzer] Add support for more pointer invalidating functions in InnerPointerChecker

2018-07-27 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 157809.
rnkovacs marked an inline comment as done.

https://reviews.llvm.org/D49656

Files:
  lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/inner-pointer.cpp

Index: test/Analysis/inner-pointer.cpp
===
--- test/Analysis/inner-pointer.cpp
+++ test/Analysis/inner-pointer.cpp
@@ -38,13 +38,29 @@
 typedef basic_string u16string;
 typedef basic_string u32string;
 
+template 
+void func_ref(T );
+
+template 
+void func_const_ref(const T );
+
+template 
+void func_value(T a);
+
+string my_string = "default";
+void default_arg(int a = 42, string  = my_string);
+
 } // end namespace std
 
 void consume(const char *) {}
 void consume(const wchar_t *) {}
 void consume(const char16_t *) {}
 void consume(const char32_t *) {}
 
+//=--=//
+// `std::string` member functions //
+//=--=//
+
 void deref_after_scope_char(bool cond) {
   const char *c, *d;
   {
@@ -151,6 +167,19 @@
   }  // expected-note@-1 {{Use of memory after it is freed}}
 }
 
+void deref_after_scope_ok(bool cond) {
+  const char *c, *d;
+  std::string s;
+  {
+c = s.c_str();
+d = s.data();
+  }
+  if (cond)
+consume(c); // no-warning
+  else
+consume(d); // no-warning
+}
+
 void deref_after_equals() {
   const char *c;
   std::string s = "hello";
@@ -277,15 +306,58 @@
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
-void deref_after_scope_ok(bool cond) {
-  const char *c, *d;
+//=---=//
+// Other STL functions //
+//=---=//
+
+void STL_func_ref() {
+  const char *c;
   std::string s;
-  {
-c = s.c_str();
-d = s.data();
-  }
-  if (cond)
-consume(c); // no-warning
-  else
-consume(d); // no-warning
+  c = s.c_str();// expected-note {{Dangling inner pointer obtained here}}
+  std::func_ref(s); // expected-note {{Inner pointer invalidated by call to 'func_ref'}}
+  consume(c);   // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void STL_func_const_ref() {
+  const char *c;
+  std::string s;
+  c = s.c_str();
+  std::func_const_ref(s);
+  consume(c); // no-warning
+}
+
+void STL_func_value() {
+  const char *c;
+  std::string s;
+  c = s.c_str();
+  std::func_value(s);
+  consume(c); // no-warning
+}
+
+void func_ptr_known() {
+  const char *c;
+  std::string s;
+  void (*func_ptr)(std::string &) = std::func_ref;
+  c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
+  func_ptr(s);   // expected-note {{Inner pointer invalidated by call to 'func_ref'}}
+  consume(c);// expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void func_ptr_unknown(void (*func_ptr)(std::string &)) {
+  const char *c;
+  std::string s;
+  c = s.c_str();
+  func_ptr(s);
+  consume(c); // no-warning
+}
+
+void func_default_arg() {
+  const char *c;
+  std::string s;
+  c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
+  default_arg(3, s); // expected-note {{Inner pointer invalidated by call to 'default_arg'}}
+  consume(c);// expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
 }
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -2930,6 +2930,11 @@
   OS << MemCallE->getMethodDecl()->getNameAsString();
 } else if (const auto *OpCallE = dyn_cast(S)) {
   OS << OpCallE->getDirectCallee()->getNameAsString();
+} else if (const auto *CallE = dyn_cast(S)) {
+  auto  = BRC.getStateManager().getCallEventManager();
+  CallEventRef<> Call = CEMgr.getSimpleCall(CallE, state, CurrentLC);
+  const auto *D = dyn_cast_or_null(Call->getDecl());
+  OS << (D ? D->getNameAsString() : "unknown");
 }
 OS << "'";
   }
Index: lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
+++ lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -91,37 +91,53 @@
 ReserveFn("reserve"), ResizeFn("resize"),
 ShrinkToFitFn("shrink_to_fit"), SwapFn("swap") {}
 
-  /// Check whether the function called on the container object is a
-  /// member function that potentially invalidates pointers referring
-  /// to the objects's internal buffer.
-  bool mayInvalidateBuffer(const CallEvent ) const;
-
-  /// Record the connection between the symbol returned by c_str() and 

[PATCH] D49361: [analyzer] Detect pointers escaped after return statement execution in MallocChecker

2018-07-25 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 157375.
rnkovacs retitled this revision from "[analyzer][WIP] Detect pointers escaped 
after return statement execution in MallocChecker" to "[analyzer] Detect 
pointers escaped after return statement execution in MallocChecker".
rnkovacs edited the summary of this revision.
rnkovacs added a comment.

Updated to use the extended `checkEndFunction()` callback (committed in 
https://reviews.llvm.org/rL337215 - I forgot to add it as a dependency).

The first ones of the 2-2 tests added for `InnerPointerChecker` and 
`NewDeleteChecker` already worked before this patch, the second ones are the 
newly working ones. I just felt they are somewhat both relevant and maybe worth 
comparing.


https://reviews.llvm.org/D49361

Files:
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/inner-pointer.cpp
  test/Analysis/malloc-free-after-return.cpp


Index: test/Analysis/malloc-free-after-return.cpp
===
--- /dev/null
+++ test/Analysis/malloc-free-after-return.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.NewDelete 
-analyzer-output=text -verify %s
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+struct S {
+  S() : Data(new int) {}
+  ~S() { delete Data; }
+  int *getData() { return Data; }
+
+private:
+  int *Data;
+};
+
+int *freeAfterReturnTemp() {
+  return S().getData(); // expected-warning {{Use of memory after it is freed}}
+}
+
+int *freeAfterReturnLocal() {
+  S X;
+  return X.getData();
+} // expected-warning {{Use of memory after it is freed}}
Index: test/Analysis/inner-pointer.cpp
===
--- test/Analysis/inner-pointer.cpp
+++ test/Analysis/inner-pointer.cpp
@@ -277,6 +277,27 @@
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
+struct S {
+  std::string to_string() { return s; }
+private:
+  std::string s;
+};
+
+const char *escape_via_return_temp() {
+  S x;
+  return x.to_string().c_str(); // expected-note {{Dangling inner pointer 
obtained here}}
+  // expected-note@-1 {{Inner pointer invalidated by call to destructor}}
+  // expected-warning@-2 {{Use of memory after it is freed}}
+  // expected-note@-3 {{Use of memory after it is freed}}
+}
+
+const char *escape_via_return_local() {
+  std::string s;
+  return s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
+// expected-note@-1 {{Inner pointer invalidated by call to 
destructor}}
+} // expected-warning {{Use of memory after it is freed}}
+// expected-note@-1 {{Use of memory after it is freed}}
+
 void deref_after_scope_ok(bool cond) {
   const char *c, *d;
   std::string s;
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -161,6 +161,7 @@
  check::PointerEscape,
  check::ConstPointerEscape,
  check::PreStmt,
+ check::EndFunction,
  check::PreCall,
  check::PostStmt,
  check::PostStmt,
@@ -217,6 +218,7 @@
   void checkPostStmt(const BlockExpr *BE, CheckerContext ) const;
   void checkDeadSymbols(SymbolReaper , CheckerContext ) const;
   void checkPreStmt(const ReturnStmt *S, CheckerContext ) const;
+  void checkEndFunction(const ReturnStmt *S, CheckerContext ) const;
   ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
 bool Assumption) const;
   void checkLocation(SVal l, bool isLoad, const Stmt *S,
@@ -2475,6 +2477,20 @@
 checkUseAfterFree(Sym, C, E);
 }
 
+void MallocChecker::checkEndFunction(const ReturnStmt *S,
+ CheckerContext ) const {
+  if (!S)
+return;
+
+  const Expr *E = S->getRetValue();
+  if (!E)
+return;
+
+  SymbolRef Sym = C.getSVal(E).getAsSymbol();
+  if (Sym)
+checkUseAfterFree(Sym, C, E);
+}
+
 // TODO: Blocks should be either inlined or should call invalidate regions
 // upon invocation. After that's in place, special casing here will not be
 // needed.


Index: test/Analysis/malloc-free-after-return.cpp
===
--- /dev/null
+++ test/Analysis/malloc-free-after-return.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.NewDelete -analyzer-output=text -verify %s
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+struct S {
+  S() : Data(new int) {}
+  ~S() { delete Data; }
+  int *getData() { return Data; }
+
+private:
+  int *Data;
+};
+
+int *freeAfterReturnTemp() {
+  return S().getData(); // expected-warning {{Use of memory after it is freed}}
+}
+
+int 

[PATCH] D49811: [analyzer] Obtain a ReturnStmt from a CFGAutomaticObjDtor

2018-07-25 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

I'm not sure how to test this.
I'll need it in https://reviews.llvm.org/D49361 when I update it to use the 
changed `checkEndFunction()` callback, and that will kind of test this too.


Repository:
  rC Clang

https://reviews.llvm.org/D49811



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


[PATCH] D49811: [analyzer] Obtain a ReturnStmt from a CFGAutomaticObjDtor

2018-07-25 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, xazax.hun, george.karpenkov.
Herald added subscribers: mikhail.ramalho, a.sidorin, dkrupp, szepet, 
baloghadamsoftware, whisperity.

The `CoreEngine` only gives us a `ReturnStmt` if the last element in the 
`CFGBlock` is a `CFGStmt`, otherwise the `ReturnStmt` is `nullptr`.
This patch adds support for the case when the last element is a 
`CFGAutomaticObjDtor`, by returning its `TriggerStmt` as a `ReturnStmt`.


Repository:
  rC Clang

https://reviews.llvm.org/D49811

Files:
  lib/StaticAnalyzer/Core/CoreEngine.cpp


Index: lib/StaticAnalyzer/Core/CoreEngine.cpp
===
--- lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -223,8 +223,12 @@
 // Get return statement..
 const ReturnStmt *RS = nullptr;
 if (!L.getSrc()->empty()) {
-  if (Optional LastStmt = L.getSrc()->back().getAs()) {
+  CFGElement LastElement = L.getSrc()->back();
+  if (Optional LastStmt = LastElement.getAs()) {
 RS = dyn_cast(LastStmt->getStmt());
+  } else if (Optional AutoDtor =
+ LastElement.getAs()) {
+RS = dyn_cast(AutoDtor->getTriggerStmt());
   }
 }
 


Index: lib/StaticAnalyzer/Core/CoreEngine.cpp
===
--- lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -223,8 +223,12 @@
 // Get return statement..
 const ReturnStmt *RS = nullptr;
 if (!L.getSrc()->empty()) {
-  if (Optional LastStmt = L.getSrc()->back().getAs()) {
+  CFGElement LastElement = L.getSrc()->back();
+  if (Optional LastStmt = LastElement.getAs()) {
 RS = dyn_cast(LastStmt->getStmt());
+  } else if (Optional AutoDtor =
+ LastElement.getAs()) {
+RS = dyn_cast(AutoDtor->getTriggerStmt());
   }
 }
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D49656: [analyzer] Add support for more pointer invalidating functions in InnerPointerChecker

2018-07-25 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added inline comments.



Comment at: lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp:181
 
-  auto *TypeDecl = ObjRegion->getValueType()->getAsCXXRecordDecl();
-  if (TypeDecl->getName() != "basic_string")
-return;
+for (unsigned I = 0, E = FD->getNumParams(); I != E; ++I) {
+  QualType ParamTy = FD->getParamDecl(I)->getType();

xazax.hun wrote:
> Nit: maybe using `FunctionDecl::parameters` and range based for loop is 
> sligtly cleaner.
Given that I am manipulating indices below, I feel that actually the plain for 
loop is a bit simpler here, what do you think?


https://reviews.llvm.org/D49656



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


[PATCH] D49656: [analyzer] Add support for more pointer invalidating functions in InnerPointerChecker

2018-07-25 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 157286.
rnkovacs marked an inline comment as done.
rnkovacs added a comment.

Tiny bit more re-structuring.


https://reviews.llvm.org/D49656

Files:
  lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/inner-pointer.cpp

Index: test/Analysis/inner-pointer.cpp
===
--- test/Analysis/inner-pointer.cpp
+++ test/Analysis/inner-pointer.cpp
@@ -38,13 +38,26 @@
 typedef basic_string u16string;
 typedef basic_string u32string;
 
+template 
+void func_ref(T );
+
+template 
+void func_const_ref(const T );
+
+template 
+void func_value(T a);
+
 } // end namespace std
 
 void consume(const char *) {}
 void consume(const wchar_t *) {}
 void consume(const char16_t *) {}
 void consume(const char32_t *) {}
 
+//=--=//
+// `std::string` member functions //
+//=--=//
+
 void deref_after_scope_char(bool cond) {
   const char *c, *d;
   {
@@ -151,6 +164,19 @@
   }  // expected-note@-1 {{Use of memory after it is freed}}
 }
 
+void deref_after_scope_ok(bool cond) {
+  const char *c, *d;
+  std::string s;
+  {
+c = s.c_str();
+d = s.data();
+  }
+  if (cond)
+consume(c); // no-warning
+  else
+consume(d); // no-warning
+}
+
 void deref_after_equals() {
   const char *c;
   std::string s = "hello";
@@ -277,15 +303,49 @@
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
-void deref_after_scope_ok(bool cond) {
-  const char *c, *d;
+//=---=//
+// Other STL functions //
+//=---=//
+
+void STL_func_ref() {
+  const char *c;
   std::string s;
-  {
-c = s.c_str();
-d = s.data();
-  }
-  if (cond)
-consume(c); // no-warning
-  else
-consume(d); // no-warning
+  c = s.c_str();// expected-note {{Dangling inner pointer obtained here}}
+  std::func_ref(s); // expected-note {{Inner pointer invalidated by call to 'func_ref'}}
+  consume(c);   // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void STL_func_const_ref() {
+  const char *c;
+  std::string s;
+  c = s.c_str();
+  std::func_const_ref(s);
+  consume(c); // no-warning
+}
+
+void STL_func_value() {
+  const char *c;
+  std::string s;
+  c = s.c_str();
+  std::func_value(s);
+  consume(c); // no-warning
+}
+
+void func_ptr_known() {
+  const char *c;
+  std::string s;
+  void (*func_ptr)(std::string &) = std::func_ref;
+  c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
+  func_ptr(s);   // expected-note {{Inner pointer invalidated by call to 'func_ref'}}
+  consume(c);// expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void func_ptr_unknown(void (*func_ptr)(std::string &)) {
+  const char *c;
+  std::string s;
+  c = s.c_str();
+  func_ptr(s);
+  consume(c); // no-warning
 }
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -2930,6 +2930,11 @@
   OS << MemCallE->getMethodDecl()->getNameAsString();
 } else if (const auto *OpCallE = dyn_cast(S)) {
   OS << OpCallE->getDirectCallee()->getNameAsString();
+} else if (const auto *CallE = dyn_cast(S)) {
+  auto  = BRC.getStateManager().getCallEventManager();
+  CallEventRef<> Call = CEMgr.getSimpleCall(CallE, state, CurrentLC);
+  const auto *D = dyn_cast_or_null(Call->getDecl());
+  OS << (D ? D->getNameAsString() : "unknown");
 }
 OS << "'";
   }
Index: lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
+++ lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -91,37 +91,53 @@
 ReserveFn("reserve"), ResizeFn("resize"),
 ShrinkToFitFn("shrink_to_fit"), SwapFn("swap") {}
 
-  /// Check whether the function called on the container object is a
-  /// member function that potentially invalidates pointers referring
-  /// to the objects's internal buffer.
-  bool mayInvalidateBuffer(const CallEvent ) const;
-
-  /// Record the connection between the symbol returned by c_str() and the
-  /// corresponding string object region in the ProgramState. Mark the symbol
-  /// released if the string object is destroyed.
+  /// Check if the object of this member function call is a `basic_string`.
+  bool isCalledOnStringObject(const CXXInstanceCall *ICall) const;
+
+  /// Check whether the called member function potentially invalidates
+  /// pointers referring to the container object's 

[PATCH] D49656: [analyzer] Add support for more pointer invalidating functions in InnerPointerChecker

2018-07-25 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added inline comments.



Comment at: lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp:207-208
+
+for (unsigned I = 0, E = FD->getNumParams(); I != E; ++I) {
+  QualType ParamTy = FD->getParamDecl(I)->getType();
+  if (!ParamTy->isReferenceType() ||

NoQ wrote:
> NoQ wrote:
> > We need tests for operators here, due to the problem that i recently 
> > encountered in D49627: there's different numbering for arguments and 
> > parameters within in-class operators. Namely, this-argument is counted as 
> > an argument (shifting other argument indices by 1) but not as a parameter.
> (i.e., tests and most likely some actual code branch)
I did the coding part, but for the tests, I'm struggling to find an example of 
a member operator in the STL that takes a non-const string reference as an 
argument. Could you perhaps help me out here?



Comment at: lib/StaticAnalyzer/Checkers/MallocChecker.cpp:2934
+} else if (const auto *CallE = dyn_cast(S)) {
+  OS << CallE->getDirectCallee()->getNameAsString();
 }

NoQ wrote:
> rnkovacs wrote:
> > xazax.hun wrote:
> > > I think `getDirectCallee` might fail and return `nullptr`. One more 
> > > reason to test function pointers :)
> > You're right. Also, it needed a bit more effort to dig up the function 
> > pointer's name. Or should I go further and somehow find out the name of the 
> > function it points to?
> > Also, it needed a bit more effort to dig up the function pointer's name.
> 
> I think it should work out of the box; `Call.getDecl()` is smart enough to 
> show the right decl when a pointer to a concrete function is being called on 
> the current path.
That worked, thanks!



Comment at: test/Analysis/inner-pointer.cpp:41-42
 
+template< class T >
+void func_ref(T& a);
+

NoQ wrote:
> Without a definition for this thing, we won't be able to test much. I suggest 
> you to take a pointer to a function directly, i.e. define a `std::swap(x, 
> y)` somewhere and take a `::swap` directly and call it (hope 
> it actually works this way).
I think I am missing something. This is meant to test that the checker 
recognizes standard library function calls taking a non-const reference to a 
string as an argument. At L314, `func_ref` is called with a string argument, 
and the checker seems to do all the things it should after the current patch, 
emitting the new kind of note and such.

I can surely add the `std::swap(x, y)` definition too, but then the analyzer 
will see that the pointer was reallocated at the assignment inside the 
function, and place the note there. I think that would test the previous 
string-member-function patch more than this one.


https://reviews.llvm.org/D49656



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


[PATCH] D49656: [analyzer] Add support for more pointer invalidating functions in InnerPointerChecker

2018-07-25 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 157278.
rnkovacs marked 2 inline comments as done.
rnkovacs added a comment.

Fix note for function pointers & handle argument counting in member operator 
calls.
I also refactored the code a little, because after moving things from 
`checkPreCall` to `checkPostCall`, the structure was a bit confusing.


https://reviews.llvm.org/D49656

Files:
  lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/inner-pointer.cpp

Index: test/Analysis/inner-pointer.cpp
===
--- test/Analysis/inner-pointer.cpp
+++ test/Analysis/inner-pointer.cpp
@@ -38,13 +38,26 @@
 typedef basic_string u16string;
 typedef basic_string u32string;
 
+template 
+void func_ref(T );
+
+template 
+void func_const_ref(const T );
+
+template 
+void func_value(T a);
+
 } // end namespace std
 
 void consume(const char *) {}
 void consume(const wchar_t *) {}
 void consume(const char16_t *) {}
 void consume(const char32_t *) {}
 
+//=--=//
+// `std::string` member functions //
+//=--=//
+
 void deref_after_scope_char(bool cond) {
   const char *c, *d;
   {
@@ -151,6 +164,19 @@
   }  // expected-note@-1 {{Use of memory after it is freed}}
 }
 
+void deref_after_scope_ok(bool cond) {
+  const char *c, *d;
+  std::string s;
+  {
+c = s.c_str();
+d = s.data();
+  }
+  if (cond)
+consume(c); // no-warning
+  else
+consume(d); // no-warning
+}
+
 void deref_after_equals() {
   const char *c;
   std::string s = "hello";
@@ -277,15 +303,49 @@
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
-void deref_after_scope_ok(bool cond) {
-  const char *c, *d;
+//=---=//
+// Other STL functions //
+//=---=//
+
+void STL_func_ref() {
+  const char *c;
   std::string s;
-  {
-c = s.c_str();
-d = s.data();
-  }
-  if (cond)
-consume(c); // no-warning
-  else
-consume(d); // no-warning
+  c = s.c_str();// expected-note {{Dangling inner pointer obtained here}}
+  std::func_ref(s); // expected-note {{Inner pointer invalidated by call to 'func_ref'}}
+  consume(c);   // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void STL_func_const_ref() {
+  const char *c;
+  std::string s;
+  c = s.c_str();
+  std::func_const_ref(s);
+  consume(c); // no-warning
+}
+
+void STL_func_value() {
+  const char *c;
+  std::string s;
+  c = s.c_str();
+  std::func_value(s);
+  consume(c); // no-warning
+}
+
+void func_ptr_known() {
+  const char *c;
+  std::string s;
+  void (*func_ptr)(std::string &) = std::func_ref;
+  c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
+  func_ptr(s);   // expected-note {{Inner pointer invalidated by call to 'func_ref'}}
+  consume(c);// expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void func_ptr_unknown(void (*func_ptr)(std::string &)) {
+  const char *c;
+  std::string s;
+  c = s.c_str();
+  func_ptr(s);
+  consume(c); // no-warning
 }
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -2930,6 +2930,11 @@
   OS << MemCallE->getMethodDecl()->getNameAsString();
 } else if (const auto *OpCallE = dyn_cast(S)) {
   OS << OpCallE->getDirectCallee()->getNameAsString();
+} else if (const auto *CallE = dyn_cast(S)) {
+  auto  = BRC.getStateManager().getCallEventManager();
+  CallEventRef<> Call = CEMgr.getSimpleCall(CallE, state, CurrentLC);
+  const auto *D = dyn_cast_or_null(Call->getDecl());
+  OS << (D ? D->getNameAsString() : "unknown");
 }
 OS << "'";
   }
Index: lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
+++ lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -91,37 +91,53 @@
 ReserveFn("reserve"), ResizeFn("resize"),
 ShrinkToFitFn("shrink_to_fit"), SwapFn("swap") {}
 
-  /// Check whether the function called on the container object is a
-  /// member function that potentially invalidates pointers referring
-  /// to the objects's internal buffer.
-  bool mayInvalidateBuffer(const CallEvent ) const;
-
-  /// Record the connection between the symbol returned by c_str() and the
-  /// corresponding string object region in the ProgramState. Mark the symbol
-  /// released if the string object is destroyed.
+  /// Check if the object of this member function call is a `basic_string`.
+ 

[PATCH] D49656: [analyzer] Add support for more pointer invalidating functions in InnerPointerChecker

2018-07-23 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added inline comments.



Comment at: lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp:213
+  if (const auto *FC = dyn_cast()) {
+const FunctionDecl *FD = FC->getDecl();
+for (unsigned I = 0, E = FD->getNumParams(); I != E; ++I) {

xazax.hun wrote:
> I am not sure if we always have a `Decl` here, I am afraid this might return 
> null sometimes. Please add a test case with a function pointer (received as 
> an argument in a top level function).
Um, yes, I'd already seen a crash here on a project before I saw your comment. 
Thanks!



Comment at: lib/StaticAnalyzer/Checkers/MallocChecker.cpp:2934
+} else if (const auto *CallE = dyn_cast(S)) {
+  OS << CallE->getDirectCallee()->getNameAsString();
 }

xazax.hun wrote:
> I think `getDirectCallee` might fail and return `nullptr`. One more reason to 
> test function pointers :)
You're right. Also, it needed a bit more effort to dig up the function 
pointer's name. Or should I go further and somehow find out the name of the 
function it points to?


https://reviews.llvm.org/D49656



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


[PATCH] D49656: [analyzer] Add support for more pointer invalidating functions in InnerPointerChecker

2018-07-23 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 156938.
rnkovacs marked 11 inline comments as done.
rnkovacs added a comment.

Addressed comments & added two test cases for function pointers.


https://reviews.llvm.org/D49656

Files:
  lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/inner-pointer.cpp

Index: test/Analysis/inner-pointer.cpp
===
--- test/Analysis/inner-pointer.cpp
+++ test/Analysis/inner-pointer.cpp
@@ -38,6 +38,15 @@
 typedef basic_string u16string;
 typedef basic_string u32string;
 
+template< class T >
+void func_ref(T& a);
+
+template< class T >
+void func_const_ref(const T& a);
+
+template< class T >
+void func_value(T a);
+
 } // end namespace std
 
 void consume(const char *) {}
@@ -277,6 +286,49 @@
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
+void STL_func_ref() {
+  const char *c;
+  std::string s;
+  c = s.c_str();// expected-note {{Dangling inner pointer obtained here}}
+  std::func_ref(s); // expected-note {{Inner pointer invalidated by call to 'func_ref'}}
+  consume(c);// expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void STL_func_const_ref() {
+  const char *c;
+  std::string s;
+  c = s.c_str();
+  std::func_const_ref(s);
+  consume(c); // no-warning
+}
+
+void STL_func_value() {
+  const char *c;
+  std::string s;
+  c = s.c_str();
+  std::func_value(s);
+  consume(c); // no-warning
+}
+
+void func_ptr_known() {
+  const char *c;
+  std::string s;
+  void (*func_ptr)(std::string&) = std::func_ref;
+  c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
+  func_ptr(s);   // expected-note {{Inner pointer invalidated by call to 'func_ptr'}}
+  consume(c);// expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void func_ptr_unknown(void (*func_ptr)(std::string&)) {
+  const char *c;
+  std::string s;
+  c = s.c_str();
+  func_ptr(s);
+  consume(c); // no-warning
+}
+
 void deref_after_scope_ok(bool cond) {
   const char *c, *d;
   std::string s;
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -2930,6 +2930,9 @@
   OS << MemCallE->getMethodDecl()->getNameAsString();
 } else if (const auto *OpCallE = dyn_cast(S)) {
   OS << OpCallE->getDirectCallee()->getNameAsString();
+} else if (const auto *CallE = dyn_cast(S)) {
+  const auto *D = dyn_cast_or_null(CallE->getCalleeDecl());
+  OS << (D ? D->getNameAsString() : "unknown");
 }
 OS << "'";
   }
Index: lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
+++ lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -91,37 +91,29 @@
 ReserveFn("reserve"), ResizeFn("resize"),
 ShrinkToFitFn("shrink_to_fit"), SwapFn("swap") {}
 
-  /// Check whether the function called on the container object is a
-  /// member function that potentially invalidates pointers referring
-  /// to the objects's internal buffer.
-  bool mayInvalidateBuffer(const CallEvent ) const;
-
-  /// Record the connection between the symbol returned by c_str() and the
-  /// corresponding string object region in the ProgramState. Mark the symbol
-  /// released if the string object is destroyed.
+  /// Check whether the called member function potentially invalidates
+  /// pointers referring to the container object's inner buffer.
+  bool isInvalidatingMemberFunction(const CallEvent ) const;
+
+  /// Mark pointer symbols associated with the given memory region released
+  /// in the program state.
+  void markPtrSymbolsReleased(const CallEvent , ProgramStateRef State,
+  const MemRegion *ObjRegion,
+  CheckerContext ) const;
+
+  /// Record the connection between raw pointers referring to a container
+  /// object's inner buffer and the object's memory region in the program state.
+  /// Mark potentially invalidated pointers released.
   void checkPostCall(const CallEvent , CheckerContext ) const;
 
-  /// Clean up the ProgramState map.
+  /// Clean up the program state map.
   void checkDeadSymbols(SymbolReaper , CheckerContext ) const;
 };
 
 } // end anonymous namespace
 
-// [string.require]
-//
-// "References, pointers, and iterators referring to the elements of a
-// basic_string sequence may be invalidated by the following uses of that
-// basic_string object:
-//
-// -- TODO: As an argument to any standard library function taking a reference
-// to non-const basic_string as an 

[PATCH] D49656: [analyzer] Add support for more pointer invalidating functions in InnerPointerChecker

2018-07-22 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, xazax.hun, george.karpenkov.
Herald added subscribers: mikhail.ramalho, a.sidorin, dkrupp, szepet, 
baloghadamsoftware, whisperity.

According to the standard, pointers referring to the elements of a 
`basic_string` sequence may also be invalidated if they are used as an argument 
to any standard library function taking a reference to non-const `basic_string` 
as an argument. This patch makes InnerPointerChecker warn for these cases as 
well.


Repository:
  rC Clang

https://reviews.llvm.org/D49656

Files:
  lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/inner-pointer.cpp

Index: test/Analysis/inner-pointer.cpp
===
--- test/Analysis/inner-pointer.cpp
+++ test/Analysis/inner-pointer.cpp
@@ -38,6 +38,15 @@
 typedef basic_string u16string;
 typedef basic_string u32string;
 
+template< class T >
+void func_ref(T& a, T& b);
+
+template< class T >
+void func_const_ref(const T& a, const T& b);
+
+template< class T >
+void func_value(T a, T b);
+
 } // end namespace std
 
 void consume(const char *) {}
@@ -277,6 +286,31 @@
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
+void non_member_func_ref() {
+  const char *c;
+  std::string s1, s2;
+  c = s1.c_str();// expected-note {{Dangling inner pointer obtained here}}
+  std::func_ref(s1, s2); // expected-note {{Inner pointer invalidated by call to 'func_ref'}}
+  consume(c);// expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void non_member_func_const_ref() {
+  const char *c;
+  std::string s1, s2;
+  c = s1.c_str();
+  std::func_const_ref(s1, s2);
+  consume(c); // no-warning
+}
+
+void non_member_func_value() {
+  const char *c;
+  std::string s1, s2;
+  c = s1.c_str();
+  std::func_value(s1, s2);
+  consume(c); // no-warning
+}
+
 void deref_after_scope_ok(bool cond) {
   const char *c, *d;
   std::string s;
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -2930,6 +2930,8 @@
   OS << MemCallE->getMethodDecl()->getNameAsString();
 } else if (const auto *OpCallE = dyn_cast(S)) {
   OS << OpCallE->getDirectCallee()->getNameAsString();
+} else if (const auto *CallE = dyn_cast(S)) {
+  OS << CallE->getDirectCallee()->getNameAsString();
 }
 OS << "'";
   }
Index: lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
+++ lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -45,7 +45,7 @@
 namespace {
 
 class InnerPointerChecker
-: public Checker {
+: public Checker {
 
   CallDescription AppendFn, AssignFn, ClearFn, CStrFn, DataFn, EraseFn,
   InsertFn, PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn,
@@ -91,37 +91,33 @@
 ReserveFn("reserve"), ResizeFn("resize"),
 ShrinkToFitFn("shrink_to_fit"), SwapFn("swap") {}
 
-  /// Check whether the function called on the container object is a
-  /// member function that potentially invalidates pointers referring
-  /// to the objects's internal buffer.
-  bool mayInvalidateBuffer(const CallEvent ) const;
+  /// Check whether the called member function potentially invalidates
+  /// pointers referring to the container object's inner buffer.
+  bool isInvalidatingMemberFunction(const CallEvent ) const;
 
-  /// Record the connection between the symbol returned by c_str() and the
-  /// corresponding string object region in the ProgramState. Mark the symbol
-  /// released if the string object is destroyed.
+  /// Mark pointer symbols associated with the given memory region released
+  /// in the program state.
+  void markPtrSymbolsReleased(const CallEvent , ProgramStateRef State,
+  const MemRegion *ObjRegion,
+  CheckerContext ) const;
+
+  /// Record the connection between raw pointers referring to a container
+  /// object's inner buffer and the object's memory region in the program state.
+  /// Mark pointers potentially invalidated by member functions released.
   void checkPostCall(const CallEvent , CheckerContext ) const;
 
-  /// Clean up the ProgramState map.
+  /// Mark pointers potentially invalidated by functions taking the object
+  /// by non-const reference released.
+  void checkPreCall(const CallEvent , CheckerContext ) const;
+
+  /// Clean up the program state map.
   void checkDeadSymbols(SymbolReaper , CheckerContext ) const;
 };
 
 } // end anonymous namespace
 
-// [string.require]
-//
-// "References, pointers, and iterators referring to the 

[PATCH] D49570: [analyzer] Improve warning messages and notes of DanglingInternalBufferChecker

2018-07-19 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, dcoughlin, xazax.hun, george.karpenkov.
Herald added subscribers: mikhail.ramalho, a.sidorin, dkrupp, szepet, 
baloghadamsoftware, whisperity.

Following the discussion at https://reviews.llvm.org/D49360.
Added two more test cases that show "returning"-type-of notes as well.


Repository:
  rC Clang

https://reviews.llvm.org/D49570

Files:
  lib/StaticAnalyzer/Checkers/AllocationState.h
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -49,232 +49,264 @@
   const char *c, *d;
   {
 std::string s;
-c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
-d = s.data();  // expected-note {{Dangling inner pointer obtained here}}
-  }// expected-note {{Inner pointer invalidated by call to destructor}}
-  // expected-note@-1 {{Inner pointer invalidated by call to destructor}}
+c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}}
+d = s.data();  // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}}
+  }// expected-note {{Inner buffer deallocated by call to destructor}}
+  // expected-note@-1 {{Inner buffer deallocated by call to destructor}}
   std::string s;
   const char *c2 = s.c_str();
   if (cond) {
 // expected-note@-1 {{Assuming 'cond' is not equal to 0}}
 // expected-note@-2 {{Taking true branch}}
 // expected-note@-3 {{Assuming 'cond' is 0}}
 // expected-note@-4 {{Taking false branch}}
-consume(c); // expected-warning {{Use of memory after it is freed}}
-// expected-note@-1 {{Use of memory after it is freed}}
+consume(c); // expected-warning {{Deallocated pointer returned to the caller}}
+// expected-note@-1 {{Deallocated pointer returned to the caller}}
   } else {
-consume(d); // expected-warning {{Use of memory after it is freed}}
-// expected-note@-1 {{Use of memory after it is freed}}
+consume(d); // expected-warning {{Deallocated pointer returned to the caller}}
+// expected-note@-1 {{Deallocated pointer returned to the caller}}
   }
 }
 
 void deref_after_scope_char_data_non_const() {
   char *c;
   {
 std::string s;
-c = s.data(); // expected-note {{Dangling inner pointer obtained here}}
-  }   // expected-note {{Inner pointer invalidated by call to destructor}}
+c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}}
+  }   // expected-note {{Inner buffer deallocated by call to destructor}}
   std::string s;
   char *c2 = s.data();
-  consume(c); // expected-warning {{Use of memory after it is freed}}
-  // expected-note@-1 {{Use of memory after it is freed}}
+  consume(c); // expected-warning {{Deallocated pointer returned to the caller}}
+  // expected-note@-1 {{Deallocated pointer returned to the caller}}
 }
 
 void deref_after_scope_wchar_t(bool cond) {
   const wchar_t *c, *d;
   {
 std::wstring s;
-c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
-d = s.data();  // expected-note {{Dangling inner pointer obtained here}}
-  }// expected-note {{Inner pointer invalidated by call to destructor}}
-  // expected-note@-1 {{Inner pointer invalidated by call to destructor}}
+c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::wstring' object obtained here}}
+d = s.data();  // expected-note {{Pointer to inner buffer of 'std::wstring' object obtained here}}
+  }// expected-note {{Inner buffer deallocated by call to destructor}}
+  // expected-note@-1 {{Inner buffer deallocated by call to destructor}}
   std::wstring s;
   const wchar_t *c2 = s.c_str();
   if (cond) {
 // expected-note@-1 {{Assuming 'cond' is not equal to 0}}
 // expected-note@-2 {{Taking true branch}}
 // expected-note@-3 {{Assuming 'cond' is 0}}
 // expected-note@-4 {{Taking false branch}}
-consume(c); // expected-warning {{Use of memory after it is freed}}
-// expected-note@-1 {{Use of memory after it is freed}}
+consume(c); // expected-warning {{Deallocated pointer returned to the caller}}
+// expected-note@-1 {{Deallocated pointer returned to the caller}}
   } else {
-consume(d); // expected-warning {{Use of memory after it is freed}}
-// expected-note@-1 {{Use of memory after it is freed}}
+consume(d); // expected-warning {{Deallocated pointer returned to the caller}}
+// expected-note@-1 {{Deallocated pointer returned to the caller}}
   }
 }
 
 void deref_after_scope_char16_t_cstr() {
   const char16_t *c16;
   {
 std::u16string s16;
-c16 = 

[PATCH] D49568: [analyzer][WIP] Scan the program state map in the visitor only once in DanglingInternalBufferChecker

2018-07-19 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, xazax.hun, george.karpenkov.
Herald added subscribers: mikhail.ramalho, a.sidorin, dkrupp, szepet, 
baloghadamsoftware, whisperity.

In order to avoid scanning the map at each node in the bug reporter visitor, 
the `MemRegion` representing the container object associated with the tracked 
pointer symbol is looked up once in the visitor constructor. However, at the 
point where the visitor is constructed, we no longer find the appropriate 
`MemRegion` entry in the map, as it has already been cleaned up.
In this patch, I removed some clean-ups to allow for the look-up, but I suspect 
this is not the right approach.


Repository:
  rC Clang

https://reviews.llvm.org/D49568

Files:
  lib/StaticAnalyzer/Checkers/AllocationState.h
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp

Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -1994,9 +1994,10 @@
 R->addRange(Range);
 R->addVisitor(llvm::make_unique(Sym));
 
-const RefState *RS = C.getState()->get(Sym);
+ProgramStateRef State = C.getState();
+const RefState *RS = State->get(Sym);
 if (RS->getAllocationFamily() == AF_InternalBuffer)
-  R->addVisitor(allocation_state::getDanglingBufferBRVisitor(Sym));
+  R->addVisitor(allocation_state::getDanglingBufferBRVisitor(Sym, State));
 
 C.emitReport(std::move(R));
   }
Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -54,9 +54,17 @@
 public:
   class DanglingBufferBRVisitor : public BugReporterVisitor {
 SymbolRef PtrToBuf;
+const MemRegion *ObjRegion;
 
   public:
-DanglingBufferBRVisitor(SymbolRef Sym) : PtrToBuf(Sym) {}
+DanglingBufferBRVisitor(SymbolRef Sym, ProgramStateRef State)
+: PtrToBuf(Sym), ObjRegion(nullptr) {
+  RawPtrMapTy Map = State->get();
+  for (auto const Entry : Map) {
+if (Entry.second.contains(Sym))
+  ObjRegion = Entry.first;
+  }
+}
 
 static void *getTag() {
   static int Tag = 0;
@@ -72,15 +80,11 @@
BugReporterContext ,
BugReport ) override;
 
-// FIXME: Scan the map once in the visitor's constructor and do a direct
-// lookup by region.
-bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) {
-  RawPtrMapTy Map = State->get();
-  for (const auto Entry : Map) {
-if (Entry.second.contains(Sym))
-  return true;
-  }
-  return false;
+bool isSymbolTracked(ProgramStateRef State) {
+  const PtrSet *SymbolSet = State->get(ObjRegion);
+  if (!SymbolSet)
+return false;
+  return SymbolSet->contains(PtrToBuf);
 }
   };
 
@@ -180,7 +184,6 @@
 // `RefState` in MallocChecker's `RegionState` program state map.
 State = allocation_state::markReleased(State, Symbol, Origin);
   }
-  State = State->remove(ObjRegion);
   C.addTransition(State);
   return;
 }
@@ -193,11 +196,6 @@
   PtrSet::Factory  = State->getStateManager().get_context();
   RawPtrMapTy RPM = State->get();
   for (const auto Entry : RPM) {
-if (!SymReaper.isLiveRegion(Entry.first)) {
-  // Due to incomplete destructor support, some dead regions might
-  // remain in the program state map. Clean them up.
-  State = State->remove(Entry.first);
-}
 if (const PtrSet *OldSet = State->get(Entry.first)) {
   PtrSet CleanedUpSet = *OldSet;
   for (const auto Symbol : Entry.second) {
@@ -217,8 +215,7 @@
 const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext ,
 BugReport ) {
 
-  if (!isSymbolTracked(N->getState(), PtrToBuf) ||
-  isSymbolTracked(PrevN->getState(), PtrToBuf))
+  if (!isSymbolTracked(N->getState()) || isSymbolTracked(PrevN->getState()))
 return nullptr;
 
   const Stmt *S = PathDiagnosticLocation::getStmt(N);
@@ -238,9 +235,10 @@
 namespace ento {
 namespace allocation_state {
 
-std::unique_ptr getDanglingBufferBRVisitor(SymbolRef Sym) {
+std::unique_ptr
+getDanglingBufferBRVisitor(SymbolRef Sym, ProgramStateRef State) {
   return llvm::make_unique<
-  DanglingInternalBufferChecker::DanglingBufferBRVisitor>(Sym);
+  DanglingInternalBufferChecker::DanglingBufferBRVisitor>(Sym, State);
 }
 
 } // end namespace allocation_state
Index: lib/StaticAnalyzer/Checkers/AllocationState.h
===
--- lib/StaticAnalyzer/Checkers/AllocationState.h
+++ 

[PATCH] D49553: [analyzer] Rename DanglingInternalBufferChecker to InnerPointerChecker

2018-07-19 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, dcoughlin, xazax.hun, george.karpenkov.
Herald added subscribers: mikhail.ramalho, a.sidorin, dkrupp, szepet, 
baloghadamsoftware, whisperity, mgorny.

Also, the `AF_InternalBuffer` allocation family is renamed to `AF_InnerBuffer`.
I'm open to other suggestions.


Repository:
  rC Clang

https://reviews.llvm.org/D49553

Files:
  include/clang/StaticAnalyzer/Checkers/Checkers.td
  lib/StaticAnalyzer/Checkers/AllocationState.h
  lib/StaticAnalyzer/Checkers/CMakeLists.txt
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp
  test/Analysis/inner-pointer.cpp

Index: test/Analysis/inner-pointer.cpp
===
--- test/Analysis/inner-pointer.cpp
+++ test/Analysis/inner-pointer.cpp
@@ -1,4 +1,4 @@
-//RUN: %clang_analyze_cc1 -analyzer-checker=alpha.cplusplus.DanglingInternalBuffer %s -analyzer-output=text -verify
+//RUN: %clang_analyze_cc1 -analyzer-checker=alpha.cplusplus.InnerPointer %s -analyzer-output=text -verify
 
 namespace std {
 
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -47,7 +47,7 @@
   AF_CXXNewArray,
   AF_IfNameIndex,
   AF_Alloca,
-  AF_InternalBuffer
+  AF_InnerBuffer
 };
 
 class RefState {
@@ -485,7 +485,7 @@
 (!SPrev || !SPrev->isReleased());
   assert(!IsReleased ||
  (Stmt && (isa(Stmt) || isa(Stmt))) ||
- (!Stmt && S->getAllocationFamily() == AF_InternalBuffer));
+ (!Stmt && S->getAllocationFamily() == AF_InnerBuffer));
   return IsReleased;
 }
 
@@ -1473,7 +1473,7 @@
 case AF_CXXNew: os << "'new'"; return;
 case AF_CXXNewArray: os << "'new[]'"; return;
 case AF_IfNameIndex: os << "'if_nameindex()'"; return;
-case AF_InternalBuffer: os << "container-specific allocator"; return;
+case AF_InnerBuffer: os << "container-specific allocator"; return;
 case AF_Alloca:
 case AF_None: llvm_unreachable("not a deallocation expression");
   }
@@ -1486,7 +1486,7 @@
 case AF_CXXNew: os << "'delete'"; return;
 case AF_CXXNewArray: os << "'delete[]'"; return;
 case AF_IfNameIndex: os << "'if_freenameindex()'"; return;
-case AF_InternalBuffer: os << "container-specific deallocator"; return;
+case AF_InnerBuffer: os << "container-specific deallocator"; return;
 case AF_Alloca:
 case AF_None: llvm_unreachable("suspicious argument");
   }
@@ -1662,8 +1662,8 @@
   }
   case AF_CXXNew:
   case AF_CXXNewArray:
-  // FIXME: Add new CheckKind for AF_InternalBuffer.
-  case AF_InternalBuffer: {
+  // FIXME: Add new CheckKind for AF_InnerBuffer.
+  case AF_InnerBuffer: {
 if (IsALeakCheck) {
   if (ChecksEnabled[CK_NewDeleteLeaksChecker])
 return CK_NewDeleteLeaksChecker;
@@ -1995,8 +1995,8 @@
 R->addVisitor(llvm::make_unique(Sym));
 
 const RefState *RS = C.getState()->get(Sym);
-if (RS->getAllocationFamily() == AF_InternalBuffer)
-  R->addVisitor(allocation_state::getDanglingBufferBRVisitor(Sym));
+if (RS->getAllocationFamily() == AF_InnerBuffer)
+  R->addVisitor(allocation_state::getInnerPointerBRVisitor(Sym));
 
 C.emitReport(std::move(R));
   }
@@ -2870,7 +2870,7 @@
   const Stmt *S = PathDiagnosticLocation::getStmt(N);
   // When dealing with containers, we sometimes want to give a note
   // even if the statement is missing.
-  if (!S && (!RS || RS->getAllocationFamily() != AF_InternalBuffer))
+  if (!S && (!RS || RS->getAllocationFamily() != AF_InnerBuffer))
 return nullptr;
 
   const LocationContext *CurrentLC = N->getLocationContext();
@@ -2903,7 +2903,7 @@
   StackHintGeneratorForSymbol *StackHint = nullptr;
   SmallString<256> Buf;
   llvm::raw_svector_ostream OS(Buf);
-  
+
   if (Mode == Normal) {
 if (isAllocated(RS, RSPrev, S)) {
   Msg = "Memory is allocated";
@@ -2919,7 +2919,7 @@
 case AF_IfNameIndex:
   Msg = "Memory is released";
   break;
-case AF_InternalBuffer: {
+case AF_InnerBuffer: {
   OS << "Inner pointer invalidated by call to ";
   if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) {
 OS << "destructor";
@@ -3011,7 +3011,7 @@
   // Generate the extra diagnostic.
   PathDiagnosticLocation Pos;
   if (!S) {
-assert(RS->getAllocationFamily() == AF_InternalBuffer);
+assert(RS->getAllocationFamily() == AF_InnerBuffer);
 auto PostImplCall = N->getLocation().getAs();
 if (!PostImplCall)
   return nullptr;
@@ -3055,7 +3055,7 @@
 
 ProgramStateRef
 markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) {
-  AllocationFamily Family = 

[PATCH] D49058: [analyzer] Move DanglingInternalBufferChecker out of alpha

2018-07-18 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

Two more reports on Ceph that seem to be true positives (no other reports from 
this checker):

1. Here  (or if it does not work, the bug is on L130 
here ).
2. Here  (or L363 and L373 here 
).


https://reviews.llvm.org/D49058



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


[PATCH] D49360: [analyzer] Add support for more basic_string API in DanglingInternalBufferChecker

2018-07-17 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 155944.
rnkovacs marked 2 inline comments as done.
rnkovacs added a reviewer: dcoughlin.
rnkovacs added a comment.

Note messages updated.


https://reviews.llvm.org/D49360

Files:
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -2,13 +2,35 @@
 
 namespace std {
 
-template< typename CharT >
+typedef int size_type;
+
+template 
 class basic_string {
 public:
+  basic_string();
+  basic_string(const CharT *s);
+
   ~basic_string();
+  void clear();
+
+  basic_string =(const basic_string );
+  basic_string +=(const basic_string );
+
   const CharT *c_str() const;
   const CharT *data() const;
   CharT *data();
+
+  basic_string (size_type count, CharT ch);
+  basic_string (size_type count, CharT ch);
+  basic_string (size_type index, size_type count);
+  basic_string (size_type index, size_type count, CharT ch);
+  basic_string (size_type pos, size_type count, const basic_string );
+  void pop_back();
+  void push_back(CharT ch);
+  void reserve(size_type new_cap);
+  void resize(size_type count);
+  void shrink_to_fit();
+  void swap(basic_string );
 };
 
 typedef basic_string string;
@@ -23,73 +45,70 @@
 void consume(const char16_t *) {}
 void consume(const char32_t *) {}
 
-void deref_after_scope_char_cstr() {
-  const char *c;
+void deref_after_scope_char(bool cond) {
+  const char *c, *d;
   {
 std::string s;
-c = s.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
-  } // expected-note {{Internal buffer is released because the object was destroyed}}
+c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
+d = s.data();  // expected-note {{Dangling inner pointer obtained here}}
+  }// expected-note {{Inner pointer invalidated by call to destructor}}
+  // expected-note@-1 {{Inner pointer invalidated by call to destructor}}
   std::string s;
   const char *c2 = s.c_str();
-  consume(c); // expected-warning {{Use of memory after it is freed}}
-  // expected-note@-1 {{Use of memory after it is freed}}
-}
-
-void deref_after_scope_char_data() {
-  const char *c;
-  {
-std::string s;
-c = s.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
-  } // expected-note {{Internal buffer is released because the object was destroyed}}
-  std::string s;
-  const char *c2 = s.data();
-  consume(c); // expected-warning {{Use of memory after it is freed}}
-  // expected-note@-1 {{Use of memory after it is freed}}
+  if (cond) {
+// expected-note@-1 {{Assuming 'cond' is not equal to 0}}
+// expected-note@-2 {{Taking true branch}}
+// expected-note@-3 {{Assuming 'cond' is 0}}
+// expected-note@-4 {{Taking false branch}}
+consume(c); // expected-warning {{Use of memory after it is freed}}
+// expected-note@-1 {{Use of memory after it is freed}}
+  } else {
+consume(d); // expected-warning {{Use of memory after it is freed}}
+// expected-note@-1 {{Use of memory after it is freed}}
+  }
 }
 
 void deref_after_scope_char_data_non_const() {
   char *c;
   {
 std::string s;
-c = s.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
-  } // expected-note {{Internal buffer is released because the object was destroyed}}
+c = s.data(); // expected-note {{Dangling inner pointer obtained here}}
+  }   // expected-note {{Inner pointer invalidated by call to destructor}}
   std::string s;
   char *c2 = s.data();
   consume(c); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
-
-void deref_after_scope_wchar_t_cstr() {
-  const wchar_t *w;
+void deref_after_scope_wchar_t(bool cond) {
+  const wchar_t *c, *d;
   {
-std::wstring ws;
-w = ws.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
-  } // expected-note {{Internal buffer is released because the object was destroyed}}
-  std::wstring ws;
-  const wchar_t *w2 = ws.c_str();
-  consume(w); // expected-warning {{Use of memory after it is freed}}
-  // expected-note@-1 {{Use of memory after it is freed}}
-}
-
-void deref_after_scope_wchar_t_data() {
-  const wchar_t *w;
-  {
-std::wstring ws;
-w = ws.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
-  } // expected-note {{Internal buffer is released because the object was destroyed}}
-  std::wstring ws;
-  const wchar_t *w2 = ws.data();
-  consume(w); // expected-warning {{Use of memory after it is freed}}
-  // expected-note@-1 {{Use of memory after it is freed}}
+std::wstring s;
+c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
+

[PATCH] D49360: [analyzer] Add support for more basic_string API in DanglingInternalBufferChecker

2018-07-17 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added inline comments.



Comment at: test/Analysis/dangling-internal-buffer.cpp:175
   std::string s;
-  {
-c = s.c_str();
-  }
-  consume(c); // no-warning
+  c = s.c_str(); // expected-note {{Pointer to dangling buffer was obtained 
here}}
+  s.clear(); // expected-note {{Method call is allowed to invalidate the 
internal buffer}}

dcoughlin wrote:
> In other parts of clang we use the term "inner pointer" to mean a pointer 
> that will be invalidated if its containing object is destroyed 
> https://clang.llvm.org/docs/AutomaticReferenceCounting.html#interior-pointers.
>  There are existing attributes that use this term to specify that a method 
> returns an inner pointer.
> 
> I think it would be good to use the same terminology here. So the diagnostic 
> could be something like "Dangling inner pointer obtained here".
I feel like I should also retitle the checker to `DanglingInnerBuffer` to 
remain consistent. What do you think?



Comment at: test/Analysis/dangling-internal-buffer.cpp:176
+  c = s.c_str(); // expected-note {{Pointer to dangling buffer was obtained 
here}}
+  s.clear(); // expected-note {{Method call is allowed to invalidate the 
internal buffer}}
+  consume(c);// expected-warning {{Use of memory after it is freed}}

dcoughlin wrote:
> What do you think about explicitly mentioning the name of the method here 
> when we have it? This will make it more clear when there are multiple methods 
> on the same line.
> 
> I also think that instead of saying "is allowed to" (which raises the 
> question "by whom?") you could make it more direct.
> 
> How about:
> "Inner pointer invalidated by call to 'clear'"
> 
> or, for the destructor "Inner pointer invalidated by call to destructor"?
> 
> What do you think?
> 
> If you're worried about this wording being to strong, you could weaken it 
> with a "may be" to:
> 
> "Inner pointer may be invalidated by call to 'clear'"
> 
> 
I like these, thanks! I went with the stronger versions now, as they seem to 
fit better with the warnings themselves.


https://reviews.llvm.org/D49360



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


[PATCH] D49360: [analyzer] Add support for more basic_string API in DanglingInternalBufferChecker

2018-07-16 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

In https://reviews.llvm.org/D49360#1163113, @NoQ wrote:

> Also we rarely commit to adding a test for every single supported API 
> function; bonus points for that, but usually 2-3 functions from a series of 
> similar functions is enough :)


Um, okay, noted for next time :)


https://reviews.llvm.org/D49360



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


[PATCH] D49360: [analyzer] Add support for more basic_string API in DanglingInternalBufferChecker

2018-07-16 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 155770.
rnkovacs marked an inline comment as done.
rnkovacs edited the summary of this revision.
rnkovacs added a comment.

Added standard quote, marking the section about non-member functions that may 
also invalidate the buffer as a TODO.
Also changed the note message to that suggested by @NoQ (thanks!). All tests 
pass now.


https://reviews.llvm.org/D49360

Files:
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -2,13 +2,35 @@
 
 namespace std {
 
-template< typename CharT >
+typedef int size_type;
+
+template 
 class basic_string {
 public:
+  basic_string();
+  basic_string(const CharT *s);
+
   ~basic_string();
+  void clear();
+
+  basic_string =(const basic_string );
+  basic_string +=(const basic_string );
+
   const CharT *c_str() const;
   const CharT *data() const;
   CharT *data();
+
+  basic_string (size_type count, CharT ch);
+  basic_string (size_type count, CharT ch);
+  basic_string (size_type index, size_type count);
+  basic_string (size_type index, size_type count, CharT ch);
+  basic_string (size_type pos, size_type count, const basic_string );
+  void pop_back();
+  void push_back(CharT ch);
+  void reserve(size_type new_cap);
+  void resize(size_type count);
+  void shrink_to_fit();
+  void swap(basic_string );
 };
 
 typedef basic_string string;
@@ -23,73 +45,70 @@
 void consume(const char16_t *) {}
 void consume(const char32_t *) {}
 
-void deref_after_scope_char_cstr() {
-  const char *c;
+void deref_after_scope_char(bool cond) {
+  const char *c, *d;
   {
 std::string s;
 c = s.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
-  } // expected-note {{Internal buffer is released because the object was destroyed}}
+d = s.data();  // expected-note {{Pointer to dangling buffer was obtained here}}
+  }// expected-note {{Method call is allowed to invalidate the internal buffer}}
+  // expected-note@-1 {{Method call is allowed to invalidate the internal buffer}}
   std::string s;
   const char *c2 = s.c_str();
-  consume(c); // expected-warning {{Use of memory after it is freed}}
-  // expected-note@-1 {{Use of memory after it is freed}}
-}
-
-void deref_after_scope_char_data() {
-  const char *c;
-  {
-std::string s;
-c = s.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
-  } // expected-note {{Internal buffer is released because the object was destroyed}}
-  std::string s;
-  const char *c2 = s.data();
-  consume(c); // expected-warning {{Use of memory after it is freed}}
-  // expected-note@-1 {{Use of memory after it is freed}}
+  if (cond) {
+// expected-note@-1 {{Assuming 'cond' is not equal to 0}}
+// expected-note@-2 {{Taking true branch}}
+// expected-note@-3 {{Assuming 'cond' is 0}}
+// expected-note@-4 {{Taking false branch}}
+consume(c); // expected-warning {{Use of memory after it is freed}}
+// expected-note@-1 {{Use of memory after it is freed}}
+  } else {
+consume(d); // expected-warning {{Use of memory after it is freed}}
+// expected-note@-1 {{Use of memory after it is freed}}
+  }
 }
 
 void deref_after_scope_char_data_non_const() {
   char *c;
   {
 std::string s;
 c = s.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
-  } // expected-note {{Internal buffer is released because the object was destroyed}}
+  }   // expected-note {{Method call is allowed to invalidate the internal buffer}}
   std::string s;
   char *c2 = s.data();
   consume(c); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
-
-void deref_after_scope_wchar_t_cstr() {
-  const wchar_t *w;
+void deref_after_scope_wchar_t(bool cond) {
+  const wchar_t *c, *d;
   {
-std::wstring ws;
-w = ws.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
-  } // expected-note {{Internal buffer is released because the object was destroyed}}
-  std::wstring ws;
-  const wchar_t *w2 = ws.c_str();
-  consume(w); // expected-warning {{Use of memory after it is freed}}
-  // expected-note@-1 {{Use of memory after it is freed}}
-}
-
-void deref_after_scope_wchar_t_data() {
-  const wchar_t *w;
-  {
-std::wstring ws;
-w = ws.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
-  } // expected-note {{Internal buffer is released because the object was destroyed}}
-  std::wstring ws;
-  const wchar_t *w2 = ws.data();
-  consume(w); // expected-warning {{Use of memory after it is freed}}
-  // expected-note@-1 {{Use of memory after it is freed}}
+std::wstring s;
+c = 

[PATCH] D49387: [analyzer] Make checkEndFunction() give access to the return statement

2018-07-16 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, xazax.hun, george.karpenkov.
Herald added subscribers: mikhail.ramalho, a.sidorin, dkrupp, szepet, 
baloghadamsoftware, whisperity.

Repository:
  rC Clang

https://reviews.llvm.org/D49387

Files:
  include/clang/StaticAnalyzer/Core/Checker.h
  include/clang/StaticAnalyzer/Core/CheckerManager.h
  lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
  lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
  lib/StaticAnalyzer/Checkers/MisusedMovedObjectChecker.cpp
  lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
  lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
  lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
  lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
  lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp
  lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
  lib/StaticAnalyzer/Core/CheckerManager.cpp
  lib/StaticAnalyzer/Core/ExprEngine.cpp

Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2297,9 +2297,9 @@
 
 // Notify checkers.
 for (const auto I : AfterRemovedDead)
-  getCheckerManager().runCheckersForEndFunction(BC, Dst, I, *this);
+  getCheckerManager().runCheckersForEndFunction(BC, Dst, I, *this, RS);
   } else {
-getCheckerManager().runCheckersForEndFunction(BC, Dst, Pred, *this);
+getCheckerManager().runCheckersForEndFunction(BC, Dst, Pred, *this, RS);
   }
 
   Engine.enqueueEndOfFunction(Dst, RS);
Index: lib/StaticAnalyzer/Core/CheckerManager.cpp
===
--- lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -439,7 +439,8 @@
 void CheckerManager::runCheckersForEndFunction(NodeBuilderContext ,
ExplodedNodeSet ,
ExplodedNode *Pred,
-   ExprEngine ) {
+   ExprEngine ,
+   const ReturnStmt *RS) {
   // We define the builder outside of the loop bacause if at least one checkers
   // creates a sucsessor for Pred, we do not need to generate an
   // autotransition for it.
@@ -449,7 +450,7 @@
   Pred->getLocationContext(),
   checkFn.Checker);
 CheckerContext C(Bldr, Eng, Pred, L);
-checkFn(C);
+checkFn(RS, C);
   }
 }
 
Index: lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
+++ lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
@@ -48,7 +48,7 @@
   DefaultBool IsPureOnly;
 
   void checkBeginFunction(CheckerContext ) const;
-  void checkEndFunction(CheckerContext ) const;
+  void checkEndFunction(const ReturnStmt *RS, CheckerContext ) const;
   void checkPreCall(const CallEvent , CheckerContext ) const;
 
 private:
@@ -167,7 +167,8 @@
 }
 
 // The EndFunction callback when leave a constructor or a destructor.
-void VirtualCallChecker::checkEndFunction(CheckerContext ) const {
+void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,
+  CheckerContext ) const {
   registerCtorDtorCallInState(false, C);
 }
 
Index: lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp
+++ lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp
@@ -47,7 +47,7 @@
 
   UninitializedObjectChecker()
   : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {}
-  void checkEndFunction(CheckerContext ) const;
+  void checkEndFunction(const ReturnStmt *RS, CheckerContext ) const;
 };
 
 /// Represents a field chain. A field chain is a vector of fields where the
@@ -241,7 +241,7 @@
 //===--===//
 
 void UninitializedObjectChecker::checkEndFunction(
-CheckerContext ) const {
+const ReturnStmt *RS, CheckerContext ) const {
 
   const auto *CtorDecl = dyn_cast_or_null(
   Context.getLocationContext()->getDecl());
Index: lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
+++ lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
@@ -30,7 +30,7 @@
 public:
   void checkBranchCondition(const Stmt *Condition, CheckerContext ) const;
   void checkBeginFunction(CheckerContext ) const;
-  void checkEndFunction(CheckerContext ) const;
+  void checkEndFunction(const ReturnStmt *RS, CheckerContext ) const;
 };
 }
 
@@ -56,7 +56,8 @@
   llvm::outs() << 

[PATCH] D49058: [analyzer] Move DanglingInternalBufferChecker out of alpha

2018-07-16 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

In https://reviews.llvm.org/D49058#1159533, @george.karpenkov wrote:

> @rnkovacs Do you have evaluation statistics handy for this checker? How many 
> bugs it finds, on which projects? How many of those are real bugs?


In its present form, it does not produce many reports, so it might be worth 
waiting for patches like https://reviews.llvm.org/D49360 and 
https://reviews.llvm.org/D49361 that add more functionality.
A recent run on LibreOffice showed one report by this checker, and it seems 
like a real bug (in an external package called GPGME).
Can be seen here 
,
 or if it does not work, line 135 here 
 
(quite a one-liner).


https://reviews.llvm.org/D49058



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


[PATCH] D49361: [analyzer][WIP] Detect pointers escaped after return statement execution in MallocChecker

2018-07-15 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, xazax.hun, george.karpenkov.
Herald added subscribers: mikhail.ramalho, a.sidorin, dkrupp, szepet, 
baloghadamsoftware, whisperity.

Sometimes an object is destroyed right after the statement returning it is 
executed. This patch aims to make MallocChecker warn for these cases as well.

FIXME1: Using two traits might not be the best solution.
FIXME2: The warning is emitted at the end of the function, which might be 
confusing.


Repository:
  rC Clang

https://reviews.llvm.org/D49361

Files:
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp


Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -277,6 +277,13 @@
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
+const char *escape_via_return() {
+  std::string s;
+  return s.c_str(); // expected-note {{Pointer to dangling buffer was obtained 
here}}
+// expected-note@-1 {{Internal buffer is released because 
the object was destroyed}}
+} // expected-warning {{Use of memory after it is freed}}
+// expected-note@-1 {{Use of memory after it is freed}}
+
 void deref_after_scope_ok(bool cond) {
   const char *c, *d;
   std::string s;
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -161,6 +161,7 @@
  check::PointerEscape,
  check::ConstPointerEscape,
  check::PreStmt,
+ check::EndFunction,
  check::PreCall,
  check::PostStmt,
  check::PostStmt,
@@ -217,6 +218,7 @@
   void checkPostStmt(const BlockExpr *BE, CheckerContext ) const;
   void checkDeadSymbols(SymbolReaper , CheckerContext ) const;
   void checkPreStmt(const ReturnStmt *S, CheckerContext ) const;
+  void checkEndFunction(CheckerContext ) const;
   ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
 bool Assumption) const;
   void checkLocation(SVal l, bool isLoad, const Stmt *S,
@@ -556,12 +558,16 @@
 };
   };
 };
+
 } // end anonymous namespace
 
 REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState)
 REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair)
 REGISTER_SET_WITH_PROGRAMSTATE(ReallocSizeZeroSymbols, SymbolRef)
 
+REGISTER_TRAIT_WITH_PROGRAMSTATE(ReturnValue, const void *)
+REGISTER_TRAIT_WITH_PROGRAMSTATE(ReturnExpr, const void *)
+
 // A map from the freed symbol to the symbol representing the return value of
 // the free function.
 REGISTER_MAP_WITH_PROGRAMSTATE(FreeReturnValue, SymbolRef, SymbolRef)
@@ -2471,8 +2477,22 @@
   Sym = BMR->getSymbol();
 
   // Check if we are returning freed memory.
-  if (Sym)
+  if (Sym) {
+State = State->set(Sym);
+State = State->set(E);
+C.addTransition(State);
+  }
+}
+
+void MallocChecker::checkEndFunction(CheckerContext ) const {
+  ProgramStateRef State = C.getState();
+  if (SymbolRef Sym = static_cast(State->get())) {
+const Expr *E = static_cast(State->get());
 checkUseAfterFree(Sym, C, E);
+State = State->remove();
+State = State->remove();
+C.addTransition(State);
+  }
 }
 
 // TODO: Blocks should be either inlined or should call invalidate regions


Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -277,6 +277,13 @@
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
+const char *escape_via_return() {
+  std::string s;
+  return s.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
+// expected-note@-1 {{Internal buffer is released because the object was destroyed}}
+} // expected-warning {{Use of memory after it is freed}}
+// expected-note@-1 {{Use of memory after it is freed}}
+
 void deref_after_scope_ok(bool cond) {
   const char *c, *d;
   std::string s;
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -161,6 +161,7 @@
  check::PointerEscape,
  check::ConstPointerEscape,
  check::PreStmt,
+ check::EndFunction,
  check::PreCall,
 

[PATCH] D49360: [analyzer] Add support for more basic_string API in DanglingInternalBufferChecker

2018-07-15 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, xazax.hun, george.karpenkov.
Herald added subscribers: mikhail.ramalho, a.sidorin, dkrupp, szepet, 
baloghadamsoftware, whisperity.

A pointer referring to the elements of a `basic_string` may be invalidated by 
calling a non-const member function, except `operator[]`, `at`, `front`, 
`back`, `begin`, `rbegin`, `end`, and `rend`. The checker now warns if the 
pointer is used after such operations.

FIXME: warning messages.


Repository:
  rC Clang

https://reviews.llvm.org/D49360

Files:
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -2,13 +2,35 @@
 
 namespace std {
 
-template< typename CharT >
+typedef int size_type;
+
+template 
 class basic_string {
 public:
+  basic_string();
+  basic_string(const CharT *s);
+
   ~basic_string();
+  void clear();
+
+  basic_string =(const basic_string );
+  basic_string +=(const basic_string );
+
   const CharT *c_str() const;
   const CharT *data() const;
   CharT *data();
+
+  basic_string (size_type count, CharT ch);
+  basic_string (size_type count, CharT ch);
+  basic_string (size_type index, size_type count);
+  basic_string (size_type index, size_type count, CharT ch);
+  basic_string (size_type pos, size_type count, const basic_string );
+  void pop_back();
+  void push_back(CharT ch);
+  void reserve(size_type new_cap);
+  void resize(size_type count);
+  void shrink_to_fit();
+  void swap(basic_string );
 };
 
 typedef basic_string string;
@@ -23,73 +45,70 @@
 void consume(const char16_t *) {}
 void consume(const char32_t *) {}
 
-void deref_after_scope_char_cstr() {
-  const char *c;
+void deref_after_scope_char(bool cond) {
+  const char *c, *d;
   {
 std::string s;
 c = s.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
-  } // expected-note {{Internal buffer is released because the object was destroyed}}
+d = s.data();  // expected-note {{Pointer to dangling buffer was obtained here}}
+  }// expected-note {{Internal buffer is released because the object was destroyed}}
+  // expected-note@-1 {{Internal buffer is released because the object was destroyed}}
   std::string s;
   const char *c2 = s.c_str();
-  consume(c); // expected-warning {{Use of memory after it is freed}}
-  // expected-note@-1 {{Use of memory after it is freed}}
-}
-
-void deref_after_scope_char_data() {
-  const char *c;
-  {
-std::string s;
-c = s.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
-  } // expected-note {{Internal buffer is released because the object was destroyed}}
-  std::string s;
-  const char *c2 = s.data();
-  consume(c); // expected-warning {{Use of memory after it is freed}}
-  // expected-note@-1 {{Use of memory after it is freed}}
+  if (cond) {
+// expected-note@-1 {{Assuming 'cond' is not equal to 0}}
+// expected-note@-2 {{Taking true branch}}
+// expected-note@-3 {{Assuming 'cond' is 0}}
+// expected-note@-4 {{Taking false branch}}
+consume(c); // expected-warning {{Use of memory after it is freed}}
+// expected-note@-1 {{Use of memory after it is freed}}
+  } else {
+consume(d); // expected-warning {{Use of memory after it is freed}}
+// expected-note@-1 {{Use of memory after it is freed}}
+  }
 }
 
 void deref_after_scope_char_data_non_const() {
   char *c;
   {
 std::string s;
 c = s.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
-  } // expected-note {{Internal buffer is released because the object was destroyed}}
+  }   // expected-note {{Internal buffer is released because the object was destroyed}}
   std::string s;
   char *c2 = s.data();
   consume(c); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
-
-void deref_after_scope_wchar_t_cstr() {
-  const wchar_t *w;
+void deref_after_scope_wchar_t(bool cond) {
+  const wchar_t *c, *d;
   {
-std::wstring ws;
-w = ws.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
-  } // expected-note {{Internal buffer is released because the object was destroyed}}
-  std::wstring ws;
-  const wchar_t *w2 = ws.c_str();
-  consume(w); // expected-warning {{Use of memory after it is freed}}
-  // expected-note@-1 {{Use of memory after it is freed}}
-}
-
-void deref_after_scope_wchar_t_data() {
-  const wchar_t *w;
-  {
-std::wstring ws;
-w = ws.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
-  } // expected-note {{Internal buffer is released because the object was destroyed}}
-  std::wstring ws;
-  const wchar_t *w2 = ws.data();
-  consume(w); // expected-warning {{Use of memory 

[PATCH] D49058: [analyzer] Move DanglingInternalBufferChecker out of alpha

2018-07-10 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 154760.
rnkovacs added a comment.

Fix test run line.


https://reviews.llvm.org/D49058

Files:
  include/clang/StaticAnalyzer/Checkers/Checkers.td
  test/Analysis/dangling-internal-buffer.cpp


Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -1,4 +1,4 @@
-//RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.cplusplus.DanglingInternalBuffer %s 
-analyzer-output=text -verify
+//RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.DanglingInternalBuffer 
%s -analyzer-output=text -verify
 
 namespace std {
 
Index: include/clang/StaticAnalyzer/Checkers/Checkers.td
===
--- include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -276,6 +276,11 @@
 
 let ParentPackage = Cplusplus in {
 
+def DanglingInternalBufferChecker : Checker<"DanglingInternalBuffer">,
+  HelpText<"Reports the usage of internal raw pointers of C++ containers "
+   "after invalidation.">,
+  DescFile<"DanglingInternalBufferChecker.cpp">;
+
 def NewDeleteChecker : Checker<"NewDelete">,
   HelpText<"Check for double-free and use-after-free problems. Traces memory 
managed by new/delete.">,
   DescFile<"MallocChecker.cpp">;
@@ -300,11 +305,6 @@
 
 let ParentPackage = CplusplusAlpha in {
 
-def DanglingInternalBufferChecker : Checker<"DanglingInternalBuffer">,
-  HelpText<"Check for internal raw pointers of C++ standard library containers 
"
-   "used after deallocation">,
-  DescFile<"DanglingInternalBufferChecker.cpp">;
-
 def DeleteWithNonVirtualDtorChecker : Checker<"DeleteWithNonVirtualDtor">,
   HelpText<"Reports destructions of polymorphic objects with a non-virtual "
"destructor in their base class">,


Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -1,4 +1,4 @@
-//RUN: %clang_analyze_cc1 -analyzer-checker=alpha.cplusplus.DanglingInternalBuffer %s -analyzer-output=text -verify
+//RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.DanglingInternalBuffer %s -analyzer-output=text -verify
 
 namespace std {
 
Index: include/clang/StaticAnalyzer/Checkers/Checkers.td
===
--- include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -276,6 +276,11 @@
 
 let ParentPackage = Cplusplus in {
 
+def DanglingInternalBufferChecker : Checker<"DanglingInternalBuffer">,
+  HelpText<"Reports the usage of internal raw pointers of C++ containers "
+   "after invalidation.">,
+  DescFile<"DanglingInternalBufferChecker.cpp">;
+
 def NewDeleteChecker : Checker<"NewDelete">,
   HelpText<"Check for double-free and use-after-free problems. Traces memory managed by new/delete.">,
   DescFile<"MallocChecker.cpp">;
@@ -300,11 +305,6 @@
 
 let ParentPackage = CplusplusAlpha in {
 
-def DanglingInternalBufferChecker : Checker<"DanglingInternalBuffer">,
-  HelpText<"Check for internal raw pointers of C++ standard library containers "
-   "used after deallocation">,
-  DescFile<"DanglingInternalBufferChecker.cpp">;
-
 def DeleteWithNonVirtualDtorChecker : Checker<"DeleteWithNonVirtualDtor">,
   HelpText<"Reports destructions of polymorphic objects with a non-virtual "
"destructor in their base class">,
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D49057: [analyzer] Track multiple raw pointer symbols in DanglingInternalBufferChecker

2018-07-09 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 154556.
rnkovacs marked 5 inline comments as done.
rnkovacs added a comment.

Thanks very much for your review!


https://reviews.llvm.org/D49057

Files:
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -108,6 +108,30 @@
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
+void multiple_symbols(bool cond) {
+  const char *c1, *d1;
+  {
+std::string s1;
+c1 = s1.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
+d1 = s1.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
+const char *local = s1.c_str();
+consume(local); // no-warning
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
+  // expected-note@-1 {{Internal buffer is released because the object was destroyed}}
+  std::string s2;
+  const char *c2 = s2.c_str();
+  if (cond) {
+// expected-note@-1 {{Assuming 'cond' is not equal to 0}}
+// expected-note@-2 {{Taking true branch}}
+// expected-note@-3 {{Assuming 'cond' is 0}}
+// expected-note@-4 {{Taking false branch}}
+consume(c1); // expected-warning {{Use of memory after it is freed}}
+// expected-note@-1 {{Use of memory after it is freed}}
+  } else {
+consume(d1); // expected-warning {{Use of memory after it is freed}}
+  } // expected-note@-1 {{Use of memory after it is freed}}
+}
+
 void deref_after_scope_cstr_ok() {
   const char *c;
   std::string s;
Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -24,10 +24,23 @@
 using namespace clang;
 using namespace ento;
 
-// FIXME: member functions that return a pointer to the container's internal
-// buffer may be called on the object many times, so the object's memory
-// region should have a list of pointer symbols associated with it.
-REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, SymbolRef)
+using PtrSet = llvm::ImmutableSet;
+
+// Associate container objects with a set of raw pointer symbols.
+REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, PtrSet)
+
+// This is a trick to gain access to PtrSet's Factory.
+namespace clang {
+namespace ento {
+template<> struct ProgramStateTrait
+  : public ProgramStatePartialTrait {
+  static void *GDMIndex() {
+static int Index = 0;
+return 
+  }
+};
+} // end namespace ento
+} // end namespace clang
 
 namespace {
 
@@ -61,7 +74,7 @@
 bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) {
   RawPtrMapTy Map = State->get();
   for (const auto Entry : Map) {
-if (Entry.second == Sym)
+if (Entry.second.contains(Sym))
   return true;
   }
   return false;
@@ -88,32 +101,42 @@
 return;
 
   SVal Obj = ICall->getCXXThisVal();
-  const auto *TypedR = dyn_cast_or_null(Obj.getAsRegion());
-  if (!TypedR)
+  const auto *ObjRegion = dyn_cast_or_null(Obj.getAsRegion());
+  if (!ObjRegion)
 return;
 
-  auto *TypeDecl = TypedR->getValueType()->getAsCXXRecordDecl();
+  auto *TypeDecl = ObjRegion->getValueType()->getAsCXXRecordDecl();
   if (TypeDecl->getName() != "basic_string")
 return;
 
   ProgramStateRef State = C.getState();
 
   if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) {
 SVal RawPtr = Call.getReturnValue();
-if (!RawPtr.isUnknown()) {
-  State = State->set(TypedR, RawPtr.getAsSymbol());
+if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) {
+  // Start tracking this raw pointer by adding it to the set of symbols
+  // associated with this container object in the program state map.
+  PtrSet::Factory  = State->getStateManager().get_context();
+  const PtrSet *SetPtr = State->get(ObjRegion);
+  PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet();
+  assert(C.wasInlined || !Set.contains(Sym));
+  Set = F.add(Set, Sym);
+  State = State->set(ObjRegion, Set);
   C.addTransition(State);
 }
 return;
   }
 
   if (isa(ICall)) {
-if (State->contains(TypedR)) {
-  const SymbolRef *StrBufferPtr = State->get(TypedR);
-  // FIXME: What if Origin is null?
+if (const PtrSet *PS = State->get(ObjRegion)) {
+  // Mark all pointer symbols associated with the deleted object released.
   const Expr *Origin = Call.getOriginExpr();
-  State = allocation_state::markReleased(State, *StrBufferPtr, Origin);
-  State = State->remove(TypedR);
+  for (const auto Symbol : *PS) {
+// NOTE: `Origin` may be null, and will be stored so in the symbol's
+

[PATCH] D49057: [analyzer] Track multiple raw pointer symbols in DanglingInternalBufferChecker

2018-07-09 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added inline comments.



Comment at: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp:32-40
+namespace clang {
+namespace ento {
+template<> struct ProgramStateTrait
+  : public ProgramStatePartialTrait {
+  static void *GDMIndex() {
+static int Index = 0;
+return 

NoQ wrote:
> Please add a comment on how this template is useful. This trick is used by 
> some checkers, but it's a vry unobvious trick. We should probably add a 
> macro for that, i.e. `REGISTER_FACTORY_WITH_PROGRAMSTATE` or something like 
> that.
Should I do that then? Maybe in `CheckerContext.h`?



Comment at: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp:115
 SVal RawPtr = Call.getReturnValue();
 if (!RawPtr.isUnknown()) {
+  // Start tracking this raw pointer by adding it to the set of symbols

NoQ wrote:
> I'd much rather unwrap the symbol here, i.e. `if (SymbolRef Sym = 
> RawPtr.getAsSymbol())`. A lot of other cornercases may occur if the 
> implementation is accidentally inlined (`Undefined`, concrete regions). Also, 
> speculatively, `.getAsSymbol(/* search for symbolic base = */ true)` for the 
> same reason//*//.
> 
> If we didn't care about inlined implementations, the `Unknown` check would 
> have been redundant. So it should also be safe to straightforwardly ignore 
> inlined implementations by consulting `C.wasInlined`, then the presence of 
> the symbol can be asserted. But i'd rather speculatively care about inlined 
> implementations as long as it seems easy.
> 
> __
> //*// In fact your code relies on a very wonky implementation detail of our 
> `SVal` hierarchy: namely, pointer-type return values of conservatively 
> evaluated functions are always expressed as `{conj_$N type>}` and never as `{SymRegion{conj_$N}, 0 S32b, 
> pointee type}`. Currently nobody knows the rules under which zero element 
> regions are added in different parts of the analyzer, i.e. what is the 
> "canonical" representation of the symbolic pointer, though i made a few 
> attempts to speculate.
I wasn't aware of much of this. Thanks for the detailed explanation :)


https://reviews.llvm.org/D49057



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


[PATCH] D49058: [analyzer] Move DanglingInternalBufferChecker out of alpha

2018-07-08 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, xazax.hun, george.karpenkov.
Herald added subscribers: mikhail.ramalho, a.sidorin, dkrupp, szepet, 
baloghadamsoftware, whisperity.

Repository:
  rC Clang

https://reviews.llvm.org/D49058

Files:
  include/clang/StaticAnalyzer/Checkers/Checkers.td


Index: include/clang/StaticAnalyzer/Checkers/Checkers.td
===
--- include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -276,6 +276,11 @@
 
 let ParentPackage = Cplusplus in {
 
+def DanglingInternalBufferChecker : Checker<"DanglingInternalBuffer">,
+  HelpText<"Reports the usage of internal raw pointers of C++ containers "
+   "after invalidation.">,
+  DescFile<"DanglingInternalBufferChecker.cpp">;
+
 def NewDeleteChecker : Checker<"NewDelete">,
   HelpText<"Check for double-free and use-after-free problems. Traces memory 
managed by new/delete.">,
   DescFile<"MallocChecker.cpp">;
@@ -300,11 +305,6 @@
 
 let ParentPackage = CplusplusAlpha in {
 
-def DanglingInternalBufferChecker : Checker<"DanglingInternalBuffer">,
-  HelpText<"Check for internal raw pointers of C++ standard library containers 
"
-   "used after deallocation">,
-  DescFile<"DanglingInternalBufferChecker.cpp">;
-
 def DeleteWithNonVirtualDtorChecker : Checker<"DeleteWithNonVirtualDtor">,
   HelpText<"Reports destructions of polymorphic objects with a non-virtual "
"destructor in their base class">,


Index: include/clang/StaticAnalyzer/Checkers/Checkers.td
===
--- include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -276,6 +276,11 @@
 
 let ParentPackage = Cplusplus in {
 
+def DanglingInternalBufferChecker : Checker<"DanglingInternalBuffer">,
+  HelpText<"Reports the usage of internal raw pointers of C++ containers "
+   "after invalidation.">,
+  DescFile<"DanglingInternalBufferChecker.cpp">;
+
 def NewDeleteChecker : Checker<"NewDelete">,
   HelpText<"Check for double-free and use-after-free problems. Traces memory managed by new/delete.">,
   DescFile<"MallocChecker.cpp">;
@@ -300,11 +305,6 @@
 
 let ParentPackage = CplusplusAlpha in {
 
-def DanglingInternalBufferChecker : Checker<"DanglingInternalBuffer">,
-  HelpText<"Check for internal raw pointers of C++ standard library containers "
-   "used after deallocation">,
-  DescFile<"DanglingInternalBufferChecker.cpp">;
-
 def DeleteWithNonVirtualDtorChecker : Checker<"DeleteWithNonVirtualDtor">,
   HelpText<"Reports destructions of polymorphic objects with a non-virtual "
"destructor in their base class">,
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D49057: [analyzer] Track multiple raw pointer symbols in DanglingInternalBufferChecker

2018-07-08 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added inline comments.



Comment at: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp:121
+  if (State->contains(ObjRegion)) {
+NewSet = *State->get(ObjRegion);
+if (NewSet.contains(RawPtr.getAsSymbol()))

xazax.hun wrote:
> Oh, there is also a double lookup here, sorry I did not spot it in the 
> initial review.
Thanks for noticing. I should have seen it in the first place :)


https://reviews.llvm.org/D49057



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


[PATCH] D49057: [analyzer] Track multiple raw pointer symbols in DanglingInternalBufferChecker

2018-07-08 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 154520.
rnkovacs marked an inline comment as done.

https://reviews.llvm.org/D49057

Files:
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -108,6 +108,30 @@
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
+void multiple_symbols(bool cond) {
+  const char *c1, *d1;
+  {
+std::string s1;
+c1 = s1.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
+d1 = s1.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
+const char *local = s1.c_str();
+consume(local); // no-warning
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
+  // expected-note@-1 {{Internal buffer is released because the object was destroyed}}
+  std::string s2;
+  const char *c2 = s2.c_str();
+  if (cond) {
+// expected-note@-1 {{Assuming 'cond' is not equal to 0}}
+// expected-note@-2 {{Taking true branch}}
+// expected-note@-3 {{Assuming 'cond' is 0}}
+// expected-note@-4 {{Taking false branch}}
+consume(c1); // expected-warning {{Use of memory after it is freed}}
+// expected-note@-1 {{Use of memory after it is freed}}
+  } else {
+consume(d1); // expected-warning {{Use of memory after it is freed}}
+  } // expected-note@-1 {{Use of memory after it is freed}}
+}
+
 void deref_after_scope_cstr_ok() {
   const char *c;
   std::string s;
Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -24,10 +24,22 @@
 using namespace clang;
 using namespace ento;
 
-// FIXME: member functions that return a pointer to the container's internal
-// buffer may be called on the object many times, so the object's memory
-// region should have a list of pointer symbols associated with it.
-REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, SymbolRef)
+using PtrSet = llvm::ImmutableSet;
+
+// Associate container objects with a set of raw pointer symbols.
+REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, PtrSet)
+
+namespace clang {
+namespace ento {
+template<> struct ProgramStateTrait
+  : public ProgramStatePartialTrait {
+  static void *GDMIndex() {
+static int Index = 0;
+return 
+  }
+};
+} // end namespace ento
+} // end namespace clang
 
 namespace {
 
@@ -61,7 +73,7 @@
 bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) {
   RawPtrMapTy Map = State->get();
   for (const auto Entry : Map) {
-if (Entry.second == Sym)
+if (Entry.second.contains(Sym))
   return true;
   }
   return false;
@@ -88,32 +100,43 @@
 return;
 
   SVal Obj = ICall->getCXXThisVal();
-  const auto *TypedR = dyn_cast_or_null(Obj.getAsRegion());
-  if (!TypedR)
+  const auto *ObjRegion = dyn_cast_or_null(Obj.getAsRegion());
+  if (!ObjRegion)
 return;
 
-  auto *TypeDecl = TypedR->getValueType()->getAsCXXRecordDecl();
+  auto *TypeDecl = ObjRegion->getValueType()->getAsCXXRecordDecl();
   if (TypeDecl->getName() != "basic_string")
 return;
 
   ProgramStateRef State = C.getState();
 
   if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) {
 SVal RawPtr = Call.getReturnValue();
 if (!RawPtr.isUnknown()) {
-  State = State->set(TypedR, RawPtr.getAsSymbol());
+  // Start tracking this raw pointer by adding it to the set of symbols
+  // associated with this container object in the program state map.
+  PtrSet::Factory  = State->getStateManager().get_context();
+  PtrSet NewSet = F.getEmptySet();
+  if (const PtrSet *OldSet = State->get(ObjRegion)) {
+NewSet = *OldSet;
+if (NewSet.contains(RawPtr.getAsSymbol()))
+  return;
+  }
+  NewSet = F.add(NewSet, RawPtr.getAsSymbol());
+  assert(!NewSet.isEmpty());
+  State = State->set(ObjRegion, NewSet);
   C.addTransition(State);
 }
 return;
   }
 
   if (isa(ICall)) {
-if (State->contains(TypedR)) {
-  const SymbolRef *StrBufferPtr = State->get(TypedR);
-  // FIXME: What if Origin is null?
+if (const PtrSet *PS = State->get(ObjRegion)) {
+  // Mark all pointer symbols associated with the deleted object released.
   const Expr *Origin = Call.getOriginExpr();
-  State = allocation_state::markReleased(State, *StrBufferPtr, Origin);
-  State = State->remove(TypedR);
+  for (const auto Symbol : *PS)
+State = allocation_state::markReleased(State, Symbol, Origin);
+  State = State->remove(ObjRegion);
   C.addTransition(State);
   return;
 }
@@ -123,15 

[PATCH] D49057: [analyzer] Track multiple raw pointer symbols in DanglingInternalBufferChecker

2018-07-08 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added inline comments.



Comment at: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp:126
+  NewSet = F.add(NewSet, RawPtr.getAsSymbol());
+  if (!NewSet.isEmpty()) {
+State = State->set(ObjRegion, NewSet);

xazax.hun wrote:
> Is it possible here the set to be empty? We just added a new element to it 
> above. Maybe turn this into an assert or just omit this if it is impossible?
I'm not sure whether `add()` can fail. I turned it into an assert now and will 
see if it ever fails.


https://reviews.llvm.org/D49057



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


[PATCH] D49057: [analyzer] Track multiple raw pointer symbols in DanglingInternalBufferChecker

2018-07-08 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 154519.
rnkovacs marked 5 inline comments as done.
rnkovacs edited the summary of this revision.
rnkovacs added a comment.

Addressed comments.


https://reviews.llvm.org/D49057

Files:
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -108,6 +108,30 @@
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
+void multiple_symbols(bool cond) {
+  const char *c1, *d1;
+  {
+std::string s1;
+c1 = s1.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
+d1 = s1.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
+const char *local = s1.c_str();
+consume(local); // no-warning
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
+  // expected-note@-1 {{Internal buffer is released because the object was destroyed}}
+  std::string s2;
+  const char *c2 = s2.c_str();
+  if (cond) {
+// expected-note@-1 {{Assuming 'cond' is not equal to 0}}
+// expected-note@-2 {{Taking true branch}}
+// expected-note@-3 {{Assuming 'cond' is 0}}
+// expected-note@-4 {{Taking false branch}}
+consume(c1); // expected-warning {{Use of memory after it is freed}}
+// expected-note@-1 {{Use of memory after it is freed}}
+  } else {
+consume(d1); // expected-warning {{Use of memory after it is freed}}
+  } // expected-note@-1 {{Use of memory after it is freed}}
+}
+
 void deref_after_scope_cstr_ok() {
   const char *c;
   std::string s;
Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -24,10 +24,22 @@
 using namespace clang;
 using namespace ento;
 
-// FIXME: member functions that return a pointer to the container's internal
-// buffer may be called on the object many times, so the object's memory
-// region should have a list of pointer symbols associated with it.
-REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, SymbolRef)
+using PtrSet = llvm::ImmutableSet;
+
+// Associate container objects with a set of raw pointer symbols.
+REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, PtrSet)
+
+namespace clang {
+namespace ento {
+template<> struct ProgramStateTrait
+  : public ProgramStatePartialTrait {
+  static void *GDMIndex() {
+static int Index = 0;
+return 
+  }
+};
+} // end namespace ento
+} // end namespace clang
 
 namespace {
 
@@ -60,8 +72,8 @@
 // lookup by region.
 bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) {
   RawPtrMapTy Map = State->get();
-  for (const auto Entry : Map) {
-if (Entry.second == Sym)
+  for (const auto  : Map) {
+if (Entry.second.contains(Sym))
   return true;
   }
   return false;
@@ -88,32 +100,43 @@
 return;
 
   SVal Obj = ICall->getCXXThisVal();
-  const auto *TypedR = dyn_cast_or_null(Obj.getAsRegion());
-  if (!TypedR)
+  const auto *ObjRegion = dyn_cast_or_null(Obj.getAsRegion());
+  if (!ObjRegion)
 return;
 
-  auto *TypeDecl = TypedR->getValueType()->getAsCXXRecordDecl();
+  auto *TypeDecl = ObjRegion->getValueType()->getAsCXXRecordDecl();
   if (TypeDecl->getName() != "basic_string")
 return;
 
   ProgramStateRef State = C.getState();
 
   if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) {
 SVal RawPtr = Call.getReturnValue();
 if (!RawPtr.isUnknown()) {
-  State = State->set(TypedR, RawPtr.getAsSymbol());
+  // Start tracking this raw pointer by adding it to the set of symbols
+  // associated with this container object in the program state map.
+  PtrSet::Factory  = State->getStateManager().get_context();
+  PtrSet NewSet = F.getEmptySet();
+  if (State->contains(ObjRegion)) {
+NewSet = *State->get(ObjRegion);
+if (NewSet.contains(RawPtr.getAsSymbol()))
+  return;
+  }
+  NewSet = F.add(NewSet, RawPtr.getAsSymbol());
+  assert(!NewSet.isEmpty());
+  State = State->set(ObjRegion, NewSet);
   C.addTransition(State);
 }
 return;
   }
 
   if (isa(ICall)) {
-if (State->contains(TypedR)) {
-  const SymbolRef *StrBufferPtr = State->get(TypedR);
-  // FIXME: What if Origin is null?
+if (const PtrSet *PS = State->get(ObjRegion)) {
+  // Mark all pointer symbols associated with the deleted object released.
   const Expr *Origin = Call.getOriginExpr();
-  State = allocation_state::markReleased(State, *StrBufferPtr, Origin);
-  State = State->remove(TypedR);
+  for (const auto  : *PS)
+State = 

[PATCH] D49057: [analyzer] Track multiple raw pointer symbols in DanglingInternalBufferChecker

2018-07-08 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, xazax.hun, george.karpenkov.
Herald added subscribers: mikhail.ramalho, a.sidorin, dkrupp, szepet, 
baloghadamsoftware, whisperity.

Previously, the checker only tracked one raw pointer symbol for each container 
object. But member functions returning a pointer to the object's inner buffer 
may be called on the object several times.
These pointer symbols are now collected in a set inside the program state map 
and thus all of them is checked for use-after-free problems.


Repository:
  rC Clang

https://reviews.llvm.org/D49057

Files:
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -108,6 +108,28 @@
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
+void multiple_symbols(bool Cond) {
+  const char *c1, *d1;
+  {
+std::string s1;
+c1 = s1.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
+d1 = s1.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
+const char *local = s1.c_str();
+consume(local); // no-warning
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
+  // expected-note@-1 {{Internal buffer is released because the object was destroyed}}
+  std::string s2;
+  const char *c2 = s2.c_str();
+  if (Cond) {
+// expected-note@-1 {{Assuming 'Pred' is not equal to 0}} // expected-note@-1 {{Taking true branch}}
+// expected-note@-2 {{Assuming 'Pred' is 0}} // expected-note@-2 {{Taking false branch}}
+consume(c1); // expected-warning {{Use of memory after it is freed}}
+// expected-note@-1 {{Use of memory after it is freed}}
+  } else {
+consume(d1); // expected-warning {{Use of memory after it is freed}}
+  } // expected-note@-1 {{Use of memory after it is freed}}
+}
+
 void deref_after_scope_cstr_ok() {
   const char *c;
   std::string s;
Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -24,10 +24,22 @@
 using namespace clang;
 using namespace ento;
 
-// FIXME: member functions that return a pointer to the container's internal
-// buffer may be called on the object many times, so the object's memory
-// region should have a list of pointer symbols associated with it.
-REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, SymbolRef)
+typedef llvm::ImmutableSet PtrSet;
+
+// Associate container objects with a set of raw pointer symbols.
+REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, PtrSet)
+
+namespace clang {
+namespace ento {
+template<> struct ProgramStateTrait
+  : public ProgramStatePartialTrait {
+  static void *GDMIndex() {
+static int Index = 0;
+return 
+  }
+};
+} // end namespace ento
+} // end namespace clang
 
 namespace {
 
@@ -60,8 +72,8 @@
 // lookup by region.
 bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) {
   RawPtrMapTy Map = State->get();
-  for (const auto Entry : Map) {
-if (Entry.second == Sym)
+  for (const auto  : Map) {
+if (Entry.second.contains(Sym))
   return true;
   }
   return false;
@@ -88,32 +100,45 @@
 return;
 
   SVal Obj = ICall->getCXXThisVal();
-  const auto *TypedR = dyn_cast_or_null(Obj.getAsRegion());
-  if (!TypedR)
+  const auto *ObjRegion = dyn_cast_or_null(Obj.getAsRegion());
+  if (!ObjRegion)
 return;
 
-  auto *TypeDecl = TypedR->getValueType()->getAsCXXRecordDecl();
+  auto *TypeDecl = ObjRegion->getValueType()->getAsCXXRecordDecl();
   if (TypeDecl->getName() != "basic_string")
 return;
 
   ProgramStateRef State = C.getState();
 
   if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) {
 SVal RawPtr = Call.getReturnValue();
 if (!RawPtr.isUnknown()) {
-  State = State->set(TypedR, RawPtr.getAsSymbol());
-  C.addTransition(State);
+  // Start tracking this raw pointer by adding it to the set of symbols
+  // associated with this container object in the program state map.
+  PtrSet::Factory  = State->getStateManager().get_context();
+  PtrSet NewSet = F.getEmptySet();
+  if (State->contains(ObjRegion)) {
+NewSet = *State->get(ObjRegion);
+if (NewSet.contains(RawPtr.getAsSymbol()))
+  return;
+  }
+  NewSet = F.add(NewSet, RawPtr.getAsSymbol());
+  if (!NewSet.isEmpty()) {
+State = State->set(ObjRegion, NewSet);
+C.addTransition(State);
+  }
 }
 return;
   }
 
   if (isa(ICall)) {
-if (State->contains(TypedR)) {
-  const SymbolRef *StrBufferPtr = 

[PATCH] D48521: [analyzer] Highlight container object destruction in MallocChecker

2018-07-07 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

No crashes on Harfbuzz, ICU, Bitcoin, and LLVM. I'll commit.


https://reviews.llvm.org/D48521



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


[PATCH] D48532: [analyzer] Add support for std::basic_string::data() in DanglingInternalBufferChecker

2018-06-25 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added inline comments.



Comment at: test/Analysis/dangling-internal-buffer.cpp:10
   const CharT *c_str();
+  const CharT *data();
 };

xazax.hun wrote:
> Note that these methods are const according to the standard. Even if it does 
> not make any difference for this check we should follow the signatures. Also, 
> from c++17 there is a non-const overload of data. 
Oh, right, thanks. Fixed here and in D48522.


https://reviews.llvm.org/D48532



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


[PATCH] D48532: [analyzer] Add support for std::basic_string::data() in DanglingInternalBufferChecker

2018-06-25 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 152737.
rnkovacs marked an inline comment as done.

https://reviews.llvm.org/D48532

Files:
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -7,6 +7,8 @@
 public:
   ~basic_string();
   const CharT *c_str() const;
+  const CharT *data() const;
+  CharT *data();
 };
 
 typedef basic_string string;
@@ -21,63 +23,105 @@
 void consume(const char16_t *) {}
 void consume(const char32_t *) {}
 
-void deref_after_scope_char() {
+void deref_after_scope_char_cstr() {
   const char *c;
   {
 std::string s;
 c = s.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
+  std::string s;
+  const char *c2 = s.c_str();
   consume(c); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
-void deref_after_scope_char2() {
+void deref_after_scope_char_data() {
   const char *c;
   {
 std::string s;
-c = s.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
+c = s.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   std::string s;
-  const char *c2 = s.c_str();
+  const char *c2 = s.data();
+  consume(c); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_char_data_non_const() {
+  char *c;
+  {
+std::string s;
+c = s.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
+  std::string s;
+  char *c2 = s.data();
   consume(c); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
-void deref_after_scope_wchar_t() {
+
+void deref_after_scope_wchar_t_cstr() {
   const wchar_t *w;
   {
 std::wstring ws;
 w = ws.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
+  std::wstring ws;
+  const wchar_t *w2 = ws.c_str();
+  consume(w); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_wchar_t_data() {
+  const wchar_t *w;
+  {
+std::wstring ws;
+w = ws.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
+  std::wstring ws;
+  const wchar_t *w2 = ws.data();
   consume(w); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
-void deref_after_scope_char16_t() {
+void deref_after_scope_char16_t_cstr() {
   const char16_t *c16;
   {
 std::u16string s16;
 c16 = s16.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
+  std::u16string s16;
+  const char16_t *c16_2 = s16.c_str();
   consume(c16); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
-void deref_after_scope_char32_t() {
+void deref_after_scope_char32_t_data() {
   const char32_t *c32;
   {
 std::u32string s32;
-c32 = s32.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
+c32 = s32.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
+  std::u32string s32;
+  const char32_t *c32_2 = s32.data();
   consume(c32); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
-void deref_after_scope_ok() {
+void deref_after_scope_cstr_ok() {
   const char *c;
   std::string s;
   {
 c = s.c_str();
   }
   consume(c); // no-warning
 }
+
+void deref_after_scope_data_ok() {
+  const char *c;
+  std::string s;
+  {
+c = s.data();
+  }
+  consume(c); // no-warning
+}
Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -24,15 +24,16 @@
 using namespace clang;
 using namespace ento;
 
-// FIXME: c_str() may be called on a string object many times, so it should
-// have a list of symbols 

[PATCH] D48522: [analyzer] Highlight c_str() call in DanglingInternalBuffer checker

2018-06-25 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 152719.
rnkovacs added a comment.

Fixed the constness of `c_str()` in the test file.


https://reviews.llvm.org/D48522

Files:
  lib/StaticAnalyzer/Checkers/AllocationState.h
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -6,7 +6,7 @@
 class basic_string {
 public:
   ~basic_string();
-  const CharT *c_str();
+  const CharT *c_str() const;
 };
 
 typedef basic_string string;
@@ -25,17 +25,29 @@
   const char *c;
   {
 std::string s;
-c = s.c_str();
+c = s.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
+  consume(c); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_char2() {
+  const char *c;
+  {
+std::string s;
+c = s.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
+  std::string s;
+  const char *c2 = s.c_str();
   consume(c); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
 void deref_after_scope_wchar_t() {
   const wchar_t *w;
   {
 std::wstring ws;
-w = ws.c_str();
+w = ws.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(w); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
@@ -45,7 +57,7 @@
   const char16_t *c16;
   {
 std::u16string s16;
-c16 = s16.c_str();
+c16 = s16.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(c16); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
@@ -55,7 +67,7 @@
   const char32_t *c32;
   {
 std::u32string s32;
-c32 = s32.c_str();
+c32 = s32.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(c32); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -1994,6 +1994,11 @@
 R->markInteresting(Sym);
 R->addRange(Range);
 R->addVisitor(llvm::make_unique(Sym));
+
+const RefState *RS = C.getState()->get(Sym);
+if (RS->getAllocationFamily() == AF_InternalBuffer)
+  R->addVisitor(allocation_state::getDanglingBufferBRVisitor(Sym));
+
 C.emitReport(std::move(R));
   }
 }
Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -7,30 +7,67 @@
 //
 //===--===//
 //
-// This file defines a check that marks a raw pointer to a C++ standard library
-// container's inner buffer released when the object is destroyed. This
-// information can be used by MallocChecker to detect use-after-free problems.
+// This file defines a check that marks a raw pointer to a C++ container's
+// inner buffer released when the object is destroyed. This information can
+// be used by MallocChecker to detect use-after-free problems.
 //
 //===--===//
 
+#include "AllocationState.h"
 #include "ClangSACheckers.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "AllocationState.h"
 
 using namespace clang;
 using namespace ento;
 
+// FIXME: c_str() may be called on a string object many times, so it should
+// have a list of symbols associated with it.
+REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, SymbolRef)
+
 namespace {
 
-class DanglingInternalBufferChecker : public Checker {

[PATCH] D48532: [analyzer] Add support for std::basic_string::data() in DanglingInternalBufferChecker

2018-06-25 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, xazax.hun, george.karpenkov, dcoughlin.
Herald added subscribers: mikhail.ramalho, a.sidorin, dkrupp, szepet, 
baloghadamsoftware, whisperity.

+ Cleaned up test file a bit.


Repository:
  rC Clang

https://reviews.llvm.org/D48532

Files:
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -7,6 +7,7 @@
 public:
   ~basic_string();
   const CharT *c_str();
+  const CharT *data();
 };
 
 typedef basic_string string;
@@ -21,63 +22,92 @@
 void consume(const char16_t *) {}
 void consume(const char32_t *) {}
 
-void deref_after_scope_char() {
+void deref_after_scope_char_cstr() {
   const char *c;
   {
 std::string s;
 c = s.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
+  std::string s;
+  const char *c2 = s.c_str();
   consume(c); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
-void deref_after_scope_char2() {
+void deref_after_scope_char_data() {
   const char *c;
   {
 std::string s;
-c = s.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
+c = s.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   std::string s;
-  const char *c2 = s.c_str();
+  const char *c2 = s.data();
   consume(c); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
-void deref_after_scope_wchar_t() {
+void deref_after_scope_wchar_t_cstr() {
   const wchar_t *w;
   {
 std::wstring ws;
 w = ws.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
+  std::wstring ws;
+  const wchar_t *w2 = ws.c_str();
   consume(w); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
-void deref_after_scope_char16_t() {
+void deref_after_scope_wchar_t_data() {
+  const wchar_t *w;
+  {
+std::wstring ws;
+w = ws.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
+  std::wstring ws;
+  const wchar_t *w2 = ws.data();
+  consume(w); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_char16_t_cstr() {
   const char16_t *c16;
   {
 std::u16string s16;
 c16 = s16.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
+  std::u16string s16;
+  const char16_t *c16_2 = s16.c_str();
   consume(c16); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
-void deref_after_scope_char32_t() {
+void deref_after_scope_char32_t_data() {
   const char32_t *c32;
   {
 std::u32string s32;
-c32 = s32.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
+c32 = s32.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
+  std::u32string s32;
+  const char32_t *c32_2 = s32.data();
   consume(c32); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
-void deref_after_scope_ok() {
+void deref_after_scope_cstr_ok() {
   const char *c;
   std::string s;
   {
 c = s.c_str();
   }
   consume(c); // no-warning
 }
+
+void deref_after_scope_data_ok() {
+  const char *c;
+  std::string s;
+  {
+c = s.data();
+  }
+  consume(c); // no-warning
+}
Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -24,15 +24,16 @@
 using namespace clang;
 using namespace ento;
 
-// FIXME: c_str() may be called on a string object many times, so it should
-// have a list of symbols associated with it.
+// FIXME: member functions that return a pointer to the container's internal
+// buffer may be called on the object many times, so the object's memory
+// region should have a list of pointer symbols associated with it.
 REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, 

[PATCH] D48522: [analyzer] Highlight c_str() call in DanglingInternalBuffer checker

2018-06-25 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 152627.
rnkovacs marked an inline comment as done.
rnkovacs added a comment.

Fixed variable name inside the visitor.
I also clang-formatted the file, sorry for any line number shifting.


https://reviews.llvm.org/D48522

Files:
  lib/StaticAnalyzer/Checkers/AllocationState.h
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -25,17 +25,29 @@
   const char *c;
   {
 std::string s;
-c = s.c_str();
+c = s.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
+  consume(c); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_char2() {
+  const char *c;
+  {
+std::string s;
+c = s.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
+  std::string s;
+  const char *c2 = s.c_str();
   consume(c); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
 void deref_after_scope_wchar_t() {
   const wchar_t *w;
   {
 std::wstring ws;
-w = ws.c_str();
+w = ws.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(w); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
@@ -45,7 +57,7 @@
   const char16_t *c16;
   {
 std::u16string s16;
-c16 = s16.c_str();
+c16 = s16.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(c16); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
@@ -55,7 +67,7 @@
   const char32_t *c32;
   {
 std::u32string s32;
-c32 = s32.c_str();
+c32 = s32.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(c32); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -1994,6 +1994,11 @@
 R->markInteresting(Sym);
 R->addRange(Range);
 R->addVisitor(llvm::make_unique(Sym));
+
+const RefState *RS = C.getState()->get(Sym);
+if (RS->getAllocationFamily() == AF_InternalBuffer)
+  R->addVisitor(allocation_state::getDanglingBufferBRVisitor(Sym));
+
 C.emitReport(std::move(R));
   }
 }
Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -7,30 +7,67 @@
 //
 //===--===//
 //
-// This file defines a check that marks a raw pointer to a C++ standard library
-// container's inner buffer released when the object is destroyed. This
-// information can be used by MallocChecker to detect use-after-free problems.
+// This file defines a check that marks a raw pointer to a C++ container's
+// inner buffer released when the object is destroyed. This information can
+// be used by MallocChecker to detect use-after-free problems.
 //
 //===--===//
 
+#include "AllocationState.h"
 #include "ClangSACheckers.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "AllocationState.h"
 
 using namespace clang;
 using namespace ento;
 
+// FIXME: c_str() may be called on a string object many times, so it should
+// have a list of symbols associated with it.
+REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, SymbolRef)
+
 namespace {
 
-class DanglingInternalBufferChecker : public Checker {
+class DanglingInternalBufferChecker
+: public Checker {

[PATCH] D48522: [analyzer] Highlight c_str() call in DanglingInternalBuffer checker

2018-06-24 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 152616.
rnkovacs marked 4 inline comments as done.
rnkovacs added a comment.

Thanks! Addressed comments.


https://reviews.llvm.org/D48522

Files:
  lib/StaticAnalyzer/Checkers/AllocationState.h
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -25,17 +25,29 @@
   const char *c;
   {
 std::string s;
-c = s.c_str();
+c = s.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
+  consume(c); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_char2() {
+  const char *c;
+  {
+std::string s;
+c = s.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
+  std::string s;
+  const char *c2 = s.c_str();
   consume(c); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
 void deref_after_scope_wchar_t() {
   const wchar_t *w;
   {
 std::wstring ws;
-w = ws.c_str();
+w = ws.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(w); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
@@ -45,7 +57,7 @@
   const char16_t *c16;
   {
 std::u16string s16;
-c16 = s16.c_str();
+c16 = s16.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(c16); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
@@ -55,7 +67,7 @@
   const char32_t *c32;
   {
 std::u32string s32;
-c32 = s32.c_str();
+c32 = s32.c_str(); // expected-note {{Pointer to dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(c32); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -1994,6 +1994,11 @@
 R->markInteresting(Sym);
 R->addRange(Range);
 R->addVisitor(llvm::make_unique(Sym));
+
+const RefState *RS = C.getState()->get(Sym);
+if (RS->getAllocationFamily() == AF_InternalBuffer)
+  R->addVisitor(allocation_state::getDanglingBufferBRVisitor(Sym));
+
 C.emitReport(std::move(R));
   }
 }
Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -24,13 +24,52 @@
 using namespace clang;
 using namespace ento;
 
+// FIXME: c_str() may be called on a string object many times, so it should
+// have a list of symbols associated with it.
+REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, SymbolRef)
+
 namespace {
 
 class DanglingInternalBufferChecker : public Checker {
   CallDescription CStrFn;
 
 public:
+  class DanglingBufferBRVisitor
+  : public BugReporterVisitorImpl {
+// Tracked pointer to a buffer.
+SymbolRef Sym;
+
+  public:
+DanglingBufferBRVisitor(SymbolRef Sym) : Sym(Sym) {}
+
+static void *getTag() {
+  static int Tag = 0;
+  return 
+}
+
+void Profile(llvm::FoldingSetNodeID ) const override {
+  ID.AddPointer(getTag());
+}
+
+std::shared_ptr VisitNode(const ExplodedNode *N,
+   const ExplodedNode *PrevN,
+   BugReporterContext ,
+   BugReport ) override;
+
+// FIXME: Scan the map once in the visitor's constructor and do a direct
+// lookup by region.
+bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) {
+  RawPtrMapTy Map = State->get();
+  for (const auto Entry : Map) {
+if (Entry.second == Sym)
+  return true;
+  }
+  return false;
+}
+
+  };
+
   DanglingInternalBufferChecker() : CStrFn("c_str") {}
 
   /// Record the connection between the symbol returned 

[PATCH] D48521: [analyzer] Highlight container object destruction in MallocChecker

2018-06-24 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 152615.
rnkovacs marked 7 inline comments as done.
rnkovacs retitled this revision from "[analyzer] Highlight STL object 
destruction in MallocChecker" to "[analyzer] Highlight container object 
destruction in MallocChecker".
rnkovacs added a comment.

Thanks for the comments! 
I'll run this on some projects and see if any assertions fail.


https://reviews.llvm.org/D48521

Files:
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -26,7 +26,7 @@
   {
 std::string s;
 c = s.c_str();
-  }
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(c); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
@@ -36,7 +36,7 @@
   {
 std::wstring ws;
 w = ws.c_str();
-  }
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(w); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
@@ -46,7 +46,7 @@
   {
 std::u16string s16;
 c16 = s16.c_str();
-  }
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(c16); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
@@ -56,7 +56,7 @@
   {
 std::u32string s32;
 c32 = s32.c_str();
-  }
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(c32); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -481,8 +481,13 @@
 inline bool isReleased(const RefState *S, const RefState *SPrev,
const Stmt *Stmt) {
   // Did not track -> released. Other state (allocated) -> released.
-  return (Stmt && (isa(Stmt) || isa(Stmt)) &&
-  (S && S->isReleased()) && (!SPrev || !SPrev->isReleased()));
+  // The statement associated with the release might be missing.
+  bool IsReleased = (S && S->isReleased()) &&
+(!SPrev || !SPrev->isReleased());
+  assert(!IsReleased ||
+ (Stmt && (isa(Stmt) || isa(Stmt))) ||
+ (!Stmt && S->getAllocationFamily() == AF_InternalBuffer));
+  return IsReleased;
 }
 
 inline bool isRelinquished(const RefState *S, const RefState *SPrev,
@@ -2851,8 +2856,17 @@
 std::shared_ptr MallocChecker::MallocBugVisitor::VisitNode(
 const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext ,
 BugReport ) {
+
+  ProgramStateRef state = N->getState();
+  ProgramStateRef statePrev = PrevN->getState();
+
+  const RefState *RS = state->get(Sym);
+  const RefState *RSPrev = statePrev->get(Sym);
+
   const Stmt *S = PathDiagnosticLocation::getStmt(N);
-  if (!S)
+  // When dealing with containers, we sometimes want to give a note
+  // even if the statement is missing.
+  if (!S && (!RS || RS->getAllocationFamily() != AF_InternalBuffer))
 return nullptr;
 
   const LocationContext *CurrentLC = N->getLocationContext();
@@ -2877,14 +2891,6 @@
 }
   }
 
-  ProgramStateRef state = N->getState();
-  ProgramStateRef statePrev = PrevN->getState();
-
-  const RefState *RS = state->get(Sym);
-  const RefState *RSPrev = statePrev->get(Sym);
-  if (!RS)
-return nullptr;
-
   // FIXME: We will eventually need to handle non-statement-based events
   // (__attribute__((cleanup))).
 
@@ -2897,7 +2903,22 @@
   StackHint = new StackHintGeneratorForSymbol(Sym,
   "Returned allocated memory");
 } else if (isReleased(RS, RSPrev, S)) {
-  Msg = "Memory is released";
+  const auto Family = RS->getAllocationFamily();
+  switch(Family) {
+case AF_Alloca:
+case AF_Malloc:
+case AF_CXXNew:
+case AF_CXXNewArray:
+case AF_IfNameIndex:
+  Msg = "Memory is released";
+  break;
+case AF_InternalBuffer:
+  Msg = "Internal buffer is released because the object was destroyed";
+  break;
+case AF_None:
+default:
+  llvm_unreachable("Unhandled allocation family!");
+  }
   StackHint = new StackHintGeneratorForSymbol(Sym,
  "Returning; memory was released");
 
@@ -2968,8 +2989,19 @@
   assert(StackHint);
 
   // Generate the extra diagnostic.
-  PathDiagnosticLocation Pos(S, 

[PATCH] D48522: [analyzer] Highlight c_str() call in DanglingInternalBuffer checker

2018-06-23 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 152604.
rnkovacs marked an inline comment as done.
rnkovacs added a comment.

Um, sorry, I totally forgot about that. Added your case to the tests.


https://reviews.llvm.org/D48522

Files:
  lib/StaticAnalyzer/Checkers/AllocationState.h
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -25,17 +25,29 @@
   const char *c;
   {
 std::string s;
-c = s.c_str();
+c = s.c_str(); // expected-note {{Dangling buffer was obtained here}}
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
+  consume(c); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_char2() {
+  const char *c;
+  {
+std::string s;
+c = s.c_str(); // expected-note {{Dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
+  std::string s;
+  const char *c2 = s.c_str();
   consume(c); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
 
 void deref_after_scope_wchar_t() {
   const wchar_t *w;
   {
 std::wstring ws;
-w = ws.c_str();
+w = ws.c_str(); // expected-note {{Dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(w); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
@@ -45,7 +57,7 @@
   const char16_t *c16;
   {
 std::u16string s16;
-c16 = s16.c_str();
+c16 = s16.c_str(); // expected-note {{Dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(c16); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
@@ -55,7 +67,7 @@
   const char32_t *c32;
   {
 std::u32string s32;
-c32 = s32.c_str();
+c32 = s32.c_str(); // expected-note {{Dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(c32); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -1988,6 +1988,11 @@
 R->markInteresting(Sym);
 R->addRange(Range);
 R->addVisitor(llvm::make_unique(Sym));
+
+const RefState *RS = C.getState()->get(Sym);
+if (RS->getAllocationFamily() == AF_InternalBuffer)
+  R->addVisitor(allocation_state::getDanglingBufferBRVisitor(Sym));
+
 C.emitReport(std::move(R));
   }
 }
Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -24,13 +24,51 @@
 using namespace clang;
 using namespace ento;
 
+// FIXME: c_str() may be called on a string object many times, so it should
+// have a list of symbols associated with it.
+REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, SymbolRef)
+
 namespace {
 
 class DanglingInternalBufferChecker : public Checker {
   CallDescription CStrFn;
 
 public:
+  class DanglingBufferBRVisitor
+  : public BugReporterVisitorImpl {
+// Tracked pointer to a buffer.
+SymbolRef Sym;
+
+  public:
+DanglingBufferBRVisitor(SymbolRef Sym) : Sym(Sym) {}
+
+static void *getTag() {
+  static int Tag = 0;
+  return 
+}
+
+void Profile(llvm::FoldingSetNodeID ) const override {
+  ID.AddPointer(getTag());
+}
+
+std::shared_ptr VisitNode(const ExplodedNode *N,
+   const ExplodedNode *PrevN,
+   BugReporterContext ,
+   BugReport ) override;
+
+bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) {
+  bool Found = false;
+  RawPtrMapTy Map = State->get();
+  for (const auto Entry : Map) {
+if (Entry.second == Sym)
+  Found = true;
+  }
+  return Found;
+}
+
+  };
+
   DanglingInternalBufferChecker() : CStrFn("c_str") {}
 
   /// Record the connection between the symbol returned by c_str() and the
@@ -44,10 +82,6 @@
 
 } // end anonymous namespace
 
-// FIXME: c_str() 

[PATCH] D48522: [analyzer] Highlight c_str() call in DanglingInternalBuffer checker

2018-06-23 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, xazax.hun, george.karpenkov, dcoughlin.
Herald added subscribers: mikhail.ramalho, a.sidorin, dkrupp, szepet, 
baloghadamsoftware, whisperity.

Add a bug visitor to `DanglingInternalBuffer` checker that places a note at the 
point where the dangling pointer was obtained.
The visitor is handed over to `MallocChecker` and attached to the report there.


Repository:
  rC Clang

https://reviews.llvm.org/D48522

Files:
  lib/StaticAnalyzer/Checkers/AllocationState.h
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -25,7 +25,7 @@
   const char *c;
   {
 std::string s;
-c = s.c_str();
+c = s.c_str(); // expected-note {{Dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(c); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
@@ -35,7 +35,7 @@
   const wchar_t *w;
   {
 std::wstring ws;
-w = ws.c_str();
+w = ws.c_str(); // expected-note {{Dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(w); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
@@ -45,7 +45,7 @@
   const char16_t *c16;
   {
 std::u16string s16;
-c16 = s16.c_str();
+c16 = s16.c_str(); // expected-note {{Dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(c16); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
@@ -55,7 +55,7 @@
   const char32_t *c32;
   {
 std::u32string s32;
-c32 = s32.c_str();
+c32 = s32.c_str(); // expected-note {{Dangling buffer was obtained here}}
   } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(c32); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -1988,6 +1988,11 @@
 R->markInteresting(Sym);
 R->addRange(Range);
 R->addVisitor(llvm::make_unique(Sym));
+
+const RefState *RS = C.getState()->get(Sym);
+if (RS->getAllocationFamily() == AF_InternalBuffer)
+  R->addVisitor(allocation_state::getDanglingBufferBRVisitor());
+
 C.emitReport(std::move(R));
   }
 }
Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -31,6 +31,28 @@
   CallDescription CStrFn;
 
 public:
+  class DanglingBufferBRVisitor
+  : public BugReporterVisitorImpl {
+bool Satisfied;
+
+  public:
+DanglingBufferBRVisitor() : Satisfied(false) {}
+
+static void *getTag() {
+  static int Tag = 0;
+  return 
+}
+
+void Profile(llvm::FoldingSetNodeID ) const override {
+  ID.AddPointer(getTag());
+}
+
+std::shared_ptr VisitNode(const ExplodedNode *N,
+   const ExplodedNode *PrevN,
+   BugReporterContext ,
+   BugReport ) override;
+  };
+
   DanglingInternalBufferChecker() : CStrFn("c_str") {}
 
   /// Record the connection between the symbol returned by c_str() and the
@@ -103,6 +125,54 @@
   C.addTransition(State);
 }
 
+std::shared_ptr
+DanglingInternalBufferChecker::DanglingBufferBRVisitor::VisitNode(
+const ExplodedNode *N, const ExplodedNode *PrevN,
+BugReporterContext , BugReport ) {
+  if (Satisfied)
+return nullptr;
+
+  const Stmt *S = PathDiagnosticLocation::getStmt(N);
+  const auto *MemberCallExpr = dyn_cast_or_null(S);
+  if (!MemberCallExpr)
+return nullptr;
+
+  const Decl *D = MemberCallExpr->getCalleeDecl();
+  const auto *FD = dyn_cast_or_null(D);
+  if (!FD)
+return nullptr;
+
+  const IdentifierInfo *FunI = FD->getIdentifier();
+  if (!FunI)
+return nullptr;
+
+  if (!(FunI->getName() == "c_str"))
+return nullptr;
+
+  Satisfied = true;
+
+  SmallString<256> Buf;
+  llvm::raw_svector_ostream OS(Buf);
+  OS << "Dangling buffer was obtained here";
+  PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+

[PATCH] D48521: [analyzer] Highlight STL object destruction in MallocChecker

2018-06-23 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, xazax.hun, george.karpenkov, dcoughlin.
Herald added subscribers: mikhail.ramalho, a.sidorin, dkrupp, szepet, 
baloghadamsoftware, whisperity.

Extend `MallocBugVisitor` to place a note at the point where objects with 
`AF_InternalBuffer` allocation family are destroyed.


Repository:
  rC Clang

https://reviews.llvm.org/D48521

Files:
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- test/Analysis/dangling-internal-buffer.cpp
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -26,7 +26,7 @@
   {
 std::string s;
 c = s.c_str();
-  }
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(c); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
@@ -36,7 +36,7 @@
   {
 std::wstring ws;
 w = ws.c_str();
-  }
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(w); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
@@ -46,7 +46,7 @@
   {
 std::u16string s16;
 c16 = s16.c_str();
-  }
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(c16); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
@@ -56,7 +56,7 @@
   {
 std::u32string s32;
 c32 = s32.c_str();
-  }
+  } // expected-note {{Internal buffer is released because the object was destroyed}}
   consume(c32); // expected-warning {{Use of memory after it is freed}}
   // expected-note@-1 {{Use of memory after it is freed}}
 }
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -478,11 +478,10 @@
SPrev->isAllocatedOfSizeZero(;
 }
 
-inline bool isReleased(const RefState *S, const RefState *SPrev,
-   const Stmt *Stmt) {
+inline bool isReleased(const RefState *S, const RefState *SPrev) {
   // Did not track -> released. Other state (allocated) -> released.
-  return (Stmt && (isa(Stmt) || isa(Stmt)) &&
-  (S && S->isReleased()) && (!SPrev || !SPrev->isReleased()));
+  // The statement associated with the release might be missing.
+  return (S && S->isReleased()) && (!SPrev || !SPrev->isReleased());
 }
 
 inline bool isRelinquished(const RefState *S, const RefState *SPrev,
@@ -2851,8 +2850,17 @@
 std::shared_ptr MallocChecker::MallocBugVisitor::VisitNode(
 const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext ,
 BugReport ) {
+
+  ProgramStateRef state = N->getState();
+  ProgramStateRef statePrev = PrevN->getState();
+
+  const RefState *RS = state->get(Sym);
+  const RefState *RSPrev = statePrev->get(Sym);
+
   const Stmt *S = PathDiagnosticLocation::getStmt(N);
-  if (!S)
+  // When dealing with STL containers, we sometimes want to give a note
+  // even if the statement is missing.
+  if (!S && (!RS || RS->getAllocationFamily() != AF_InternalBuffer))
 return nullptr;
 
   const LocationContext *CurrentLC = N->getLocationContext();
@@ -2877,14 +2885,6 @@
 }
   }
 
-  ProgramStateRef state = N->getState();
-  ProgramStateRef statePrev = PrevN->getState();
-
-  const RefState *RS = state->get(Sym);
-  const RefState *RSPrev = statePrev->get(Sym);
-  if (!RS)
-return nullptr;
-
   // FIXME: We will eventually need to handle non-statement-based events
   // (__attribute__((cleanup))).
 
@@ -2896,8 +2896,23 @@
   Msg = "Memory is allocated";
   StackHint = new StackHintGeneratorForSymbol(Sym,
   "Returned allocated memory");
-} else if (isReleased(RS, RSPrev, S)) {
-  Msg = "Memory is released";
+} else if (isReleased(RS, RSPrev)) {
+  const auto Family = RS->getAllocationFamily();
+  switch(Family) {
+case AF_Alloca:
+case AF_Malloc:
+case AF_CXXNew:
+case AF_CXXNewArray:
+case AF_IfNameIndex:
+  Msg = "Memory is released";
+  break;
+case AF_InternalBuffer:
+  Msg = "Internal buffer is released because the object was destroyed";
+  break;
+case AF_None:
+default:
+  llvm_unreachable("unhandled allocation family");
+  }
   StackHint = new StackHintGeneratorForSymbol(Sym,
  "Returning; memory was released");
 
@@ -2968,8 +2983,18 @@
   assert(StackHint);
 
   // Generate the extra diagnostic.
-  PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
-   

[PATCH] D47416: [analyzer] Clean up the program state map of DanglingInternalBufferChecker

2018-06-09 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 150637.
rnkovacs marked 3 inline comments as done.
rnkovacs added a comment.

Addressed comments.


https://reviews.llvm.org/D47416

Files:
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp


Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -26,7 +26,8 @@
 
 namespace {
 
-class DanglingInternalBufferChecker : public Checker {
+class DanglingInternalBufferChecker : public Checker {
   CallDescription CStrFn;
 
 public:
@@ -36,6 +37,9 @@
   /// corresponding string object region in the ProgramState. Mark the symbol
   /// released if the string object is destroyed.
   void checkPostCall(const CallEvent , CheckerContext ) const;
+
+  /// Clean up the ProgramState map.
+  void checkDeadSymbols(SymbolReaper , CheckerContext ) const;
 };
 
 } // end anonymous namespace
@@ -76,12 +80,29 @@
   // FIXME: What if Origin is null?
   const Expr *Origin = Call.getOriginExpr();
   State = allocation_state::markReleased(State, *StrBufferPtr, Origin);
+  State = State->remove(TypedR);
   C.addTransition(State);
   return;
 }
   }
 }
 
+void DanglingInternalBufferChecker::checkDeadSymbols(SymbolReaper ,
+ CheckerContext ) const {
+  ProgramStateRef State = C.getState();
+  RawPtrMapTy RPM = State->get();
+  for (const auto Entry : RPM) {
+if (!SymReaper.isLive(Entry.second))
+  State = State->remove(Entry.first);
+if (!SymReaper.isLiveRegion(Entry.first)) {
+  // Due to incomplete destructor support, some dead regions might still
+  // remain in the program state map. Clean them up.
+  State = State->remove(Entry.first);
+}
+  }
+  C.addTransition(State);
+}
+
 void ento::registerDanglingInternalBufferChecker(CheckerManager ) {
   registerNewDeleteChecker(Mgr);
   Mgr.registerChecker();


Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -26,7 +26,8 @@
 
 namespace {
 
-class DanglingInternalBufferChecker : public Checker {
+class DanglingInternalBufferChecker : public Checker {
   CallDescription CStrFn;
 
 public:
@@ -36,6 +37,9 @@
   /// corresponding string object region in the ProgramState. Mark the symbol
   /// released if the string object is destroyed.
   void checkPostCall(const CallEvent , CheckerContext ) const;
+
+  /// Clean up the ProgramState map.
+  void checkDeadSymbols(SymbolReaper , CheckerContext ) const;
 };
 
 } // end anonymous namespace
@@ -76,12 +80,29 @@
   // FIXME: What if Origin is null?
   const Expr *Origin = Call.getOriginExpr();
   State = allocation_state::markReleased(State, *StrBufferPtr, Origin);
+  State = State->remove(TypedR);
   C.addTransition(State);
   return;
 }
   }
 }
 
+void DanglingInternalBufferChecker::checkDeadSymbols(SymbolReaper ,
+ CheckerContext ) const {
+  ProgramStateRef State = C.getState();
+  RawPtrMapTy RPM = State->get();
+  for (const auto Entry : RPM) {
+if (!SymReaper.isLive(Entry.second))
+  State = State->remove(Entry.first);
+if (!SymReaper.isLiveRegion(Entry.first)) {
+  // Due to incomplete destructor support, some dead regions might still
+  // remain in the program state map. Clean them up.
+  State = State->remove(Entry.first);
+}
+  }
+  C.addTransition(State);
+}
+
 void ento::registerDanglingInternalBufferChecker(CheckerManager ) {
   registerNewDeleteChecker(Mgr);
   Mgr.registerChecker();
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D47416: [analyzer] Clean up the program state map of DanglingInternalBufferChecker

2018-06-09 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 150625.
rnkovacs marked an inline comment as done.
rnkovacs edited the summary of this revision.
rnkovacs added a comment.
Herald added a subscriber: mikhail.ramalho.

Fixed naming and added an extra pass for regions left behind by incomplete 
destructors.


https://reviews.llvm.org/D47416

Files:
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp


Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -26,7 +26,8 @@
 
 namespace {
 
-class DanglingInternalBufferChecker : public Checker {
+class DanglingInternalBufferChecker : public Checker {
   CallDescription CStrFn;
 
 public:
@@ -36,6 +37,9 @@
   /// corresponding string object region in the ProgramState. Mark the symbol
   /// released if the string object is destroyed.
   void checkPostCall(const CallEvent , CheckerContext ) const;
+
+  /// Clean up the ProgramState map.
+  void checkDeadSymbols(SymbolReaper , CheckerContext ) const;
 };
 
 } // end anonymous namespace
@@ -76,12 +80,32 @@
   // FIXME: What if Origin is null?
   const Expr *Origin = Call.getOriginExpr();
   State = allocation_state::markReleased(State, *StrBufferPtr, Origin);
+  State = State->remove(TypedR);
   C.addTransition(State);
   return;
 }
   }
 }
 
+void DanglingInternalBufferChecker::checkDeadSymbols(SymbolReaper ,
+ CheckerContext ) const {
+  if (!SymReaper.hasDeadSymbols())
+return;
+
+  ProgramStateRef State = C.getState();
+  RawPtrMapTy RPM = State->get();
+  for (const auto Entry : RPM) {
+if (SymReaper.isDead(Entry.second))
+  State = State->remove(Entry.first);
+if (!SymReaper.isLiveRegion(Entry.first))
+  // Due to incomplete destructor support, some dead regions might still
+  // remain in the program state map. Clean them up.
+  State = State->remove(Entry.first);
+  }
+
+  C.addTransition(State);
+}
+
 void ento::registerDanglingInternalBufferChecker(CheckerManager ) {
   registerNewDeleteChecker(Mgr);
   Mgr.registerChecker();


Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -26,7 +26,8 @@
 
 namespace {
 
-class DanglingInternalBufferChecker : public Checker {
+class DanglingInternalBufferChecker : public Checker {
   CallDescription CStrFn;
 
 public:
@@ -36,6 +37,9 @@
   /// corresponding string object region in the ProgramState. Mark the symbol
   /// released if the string object is destroyed.
   void checkPostCall(const CallEvent , CheckerContext ) const;
+
+  /// Clean up the ProgramState map.
+  void checkDeadSymbols(SymbolReaper , CheckerContext ) const;
 };
 
 } // end anonymous namespace
@@ -76,12 +80,32 @@
   // FIXME: What if Origin is null?
   const Expr *Origin = Call.getOriginExpr();
   State = allocation_state::markReleased(State, *StrBufferPtr, Origin);
+  State = State->remove(TypedR);
   C.addTransition(State);
   return;
 }
   }
 }
 
+void DanglingInternalBufferChecker::checkDeadSymbols(SymbolReaper ,
+ CheckerContext ) const {
+  if (!SymReaper.hasDeadSymbols())
+return;
+
+  ProgramStateRef State = C.getState();
+  RawPtrMapTy RPM = State->get();
+  for (const auto Entry : RPM) {
+if (SymReaper.isDead(Entry.second))
+  State = State->remove(Entry.first);
+if (!SymReaper.isLiveRegion(Entry.first))
+  // Due to incomplete destructor support, some dead regions might still
+  // remain in the program state map. Clean them up.
+  State = State->remove(Entry.first);
+  }
+
+  C.addTransition(State);
+}
+
 void ento::registerDanglingInternalBufferChecker(CheckerManager ) {
   registerNewDeleteChecker(Mgr);
   Mgr.registerChecker();
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45517: [analyzer] False positive refutation with Z3

2018-05-31 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

In https://reviews.llvm.org/D45517#1117898, @mikhail.ramalho wrote:

> Just want to comment here and give thanks again for the first version of
>  the refutation code. It's being really helpful to develop the approach this
>  code as a base; things would definitely be slower if I had to start it from
>  scratch.


@mikhail.ramalho Thanks for this note, it's very nice of you :)
I'm glad if it saves a bit of time, but it's only a rough sketch, so please 
feel free to tailor it to your liking (and the reviewers' of course).


https://reviews.llvm.org/D45517



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


[PATCH] D45517: [analyzer] False positive refutation with Z3

2018-05-29 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added inline comments.



Comment at: lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp:1249
+bool Z3ConstraintManager::isModelFeasible() {
+  return Solver.check() != Z3_L_FALSE;
+}

george.karpenkov wrote:
> solver can also return "unknown", what happens then?
If it returns `Z3_L_UNDEF`, e.g. in case of a timeout, this assumes that the 
state was feasible because we couldn't prove the opposite. In that case the 
report won't be invalidated.



Comment at: lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp:1259
+return;
+  ConstraintRangeTy CR = OnlyPurged ? PrevCR : SuccCR;
+

george.karpenkov wrote:
> TBH I'm really confused here. Why does the method take two constraint ranges? 
> What's `OnlyPurged`? From reading the code it seems it's set by seeing 
> whether the program point only purges dead symbols, but at least a comment 
> should be added as to why this affects behavior.
The logic was:
  - add every constraint from the last node (first visited),
  - for other nodes on the path, only add those that disappear in the next step.

So `OnlyPurged` is meant to signal that we only want to add those symbols to 
the solver that are getting purged from the program state.



Comment at: lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp:1278
+  // a valid APSIntType.
+  if (RangeTy.isNull())
+continue;

george.karpenkov wrote:
> I'm really curious where does it happen and why.
I encountered some 1-bit `APSInt`s that wouldn't work together with any other 
integer-handling logic. As @ddcc mentioned, he has a fix for that in D35450 
(`Z3ConstraintManager::fixAPSInt()`).


https://reviews.llvm.org/D45517



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


[PATCH] D47135: [analyzer] A checker for dangling internal buffer pointers in C++

2018-05-28 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 148827.
rnkovacs added a comment.

Added a check for `UnknownVal` and two FIXMEs (one for the `OriginExpr` and one 
for the new `CheckKind`).


https://reviews.llvm.org/D47135

Files:
  include/clang/StaticAnalyzer/Checkers/Checkers.td
  lib/StaticAnalyzer/Checkers/AllocationState.h
  lib/StaticAnalyzer/Checkers/CMakeLists.txt
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- /dev/null
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -0,0 +1,71 @@
+//RUN: %clang_analyze_cc1 -analyzer-checker=alpha.cplusplus.DanglingInternalBuffer %s -analyzer-output=text -verify
+
+namespace std {
+
+template< typename CharT >
+class basic_string {
+public:
+  ~basic_string();
+  const CharT *c_str();
+};
+
+typedef basic_string string;
+typedef basic_string wstring;
+typedef basic_string u16string;
+typedef basic_string u32string;
+
+} // end namespace std
+
+void consume(const char *) {}
+void consume(const wchar_t *) {}
+void consume(const char16_t *) {}
+void consume(const char32_t *) {}
+
+void deref_after_scope_char() {
+  const char *c;
+  {
+std::string s;
+c = s.c_str();
+  }
+  consume(c); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_wchar_t() {
+  const wchar_t *w;
+  {
+std::wstring ws;
+w = ws.c_str();
+  }
+  consume(w); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_char16_t() {
+  const char16_t *c16;
+  {
+std::u16string s16;
+c16 = s16.c_str();
+  }
+  consume(c16); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_char32_t() {
+  const char32_t *c32;
+  {
+std::u32string s32;
+c32 = s32.c_str();
+  }
+  consume(c32); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_ok() {
+  const char *c;
+  std::string s;
+  {
+c = s.c_str();
+  }
+  consume(c); // no-warning
+}
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -30,6 +30,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
+#include "AllocationState.h"
 #include 
 #include 
 
@@ -45,7 +46,8 @@
   AF_CXXNew,
   AF_CXXNewArray,
   AF_IfNameIndex,
-  AF_Alloca
+  AF_Alloca,
+  AF_InternalBuffer
 };
 
 class RefState {
@@ -1467,6 +1469,7 @@
 case AF_CXXNew: os << "'new'"; return;
 case AF_CXXNewArray: os << "'new[]'"; return;
 case AF_IfNameIndex: os << "'if_nameindex()'"; return;
+case AF_InternalBuffer: os << "container-specific allocator"; return;
 case AF_Alloca:
 case AF_None: llvm_unreachable("not a deallocation expression");
   }
@@ -1479,6 +1482,7 @@
 case AF_CXXNew: os << "'delete'"; return;
 case AF_CXXNewArray: os << "'delete[]'"; return;
 case AF_IfNameIndex: os << "'if_freenameindex()'"; return;
+case AF_InternalBuffer: os << "container-specific deallocator"; return;
 case AF_Alloca:
 case AF_None: llvm_unreachable("suspicious argument");
   }
@@ -1653,7 +1657,9 @@
 return Optional();
   }
   case AF_CXXNew:
-  case AF_CXXNewArray: {
+  case AF_CXXNewArray:
+  // FIXME: Add new CheckKind for AF_InternalBuffer.
+  case AF_InternalBuffer: {
 if (IsALeakCheck) {
   if (ChecksEnabled[CK_NewDeleteLeaksChecker])
 return CK_NewDeleteLeaksChecker;
@@ -2991,6 +2997,20 @@
   }
 }
 
+namespace clang {
+namespace ento {
+namespace allocation_state {
+
+ProgramStateRef
+markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) {
+  AllocationFamily Family = AF_InternalBuffer;
+  return State->set(Sym, RefState::getReleased(Family, Origin));
+}
+
+} // end namespace allocation_state
+} // end namespace ento
+} // end namespace clang
+
 void ento::registerNewDeleteLeaksChecker(CheckerManager ) {
   registerCStringCheckerBasic(mgr);
   MallocChecker *checker = mgr.registerChecker();
Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- /dev/null
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -0,0 +1,88 @@
+//=== DanglingInternalBufferChecker.cpp ---*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//

[PATCH] D47417: [analyzer] Add missing state transition in IteratorChecker

2018-05-26 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, xazax.hun, george.karpenkov, baloghadamsoftware.
Herald added subscribers: a.sidorin, dkrupp, szepet, whisperity.

After cleaning up program state maps in `checkDeadSymbols()`, a transition 
should be added to generate the new state.


Repository:
  rC Clang

https://reviews.llvm.org/D47417

Files:
  lib/StaticAnalyzer/Checkers/IteratorChecker.cpp


Index: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
+++ lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
@@ -395,6 +395,8 @@
   State = State->remove(Comp.first);
 }
   }
+
+  C.addTransition(State);
 }
 
 ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond,


Index: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
+++ lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
@@ -395,6 +395,8 @@
   State = State->remove(Comp.first);
 }
   }
+
+  C.addTransition(State);
 }
 
 ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond,
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D47135: [analyzer] A checker for dangling internal buffer pointers in C++

2018-05-26 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added inline comments.



Comment at: lib/StaticAnalyzer/Checkers/MallocChecker.cpp:1661
+  case AF_CXXNewArray:
+  case AF_InternalBuffer: {
 if (IsALeakCheck) {

Is tying this new family to NewDeleteChecker reasonable? I did it because it 
was NewDelete that warned naturally before creating the new `AllocationFamily`. 
Should I introduce a new checker kind in MallocChecker for this purpose instead?


https://reviews.llvm.org/D47135



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


[PATCH] D47416: [analyzer] Clean up the program state map of DanglingInternalBufferChecker

2018-05-26 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, xazax.hun, george.karpenkov.
Herald added subscribers: a.sidorin, dkrupp, szepet, baloghadamsoftware, 
whisperity.

Symbols are cleaned up from the program state map when they go out of scope. 
(This will need to be done individually when the collection of multiple symbols 
for one region will be supported.)
Regions are cleaned up when the corresponding object is destroyed.


Repository:
  rC Clang

https://reviews.llvm.org/D47416

Files:
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp


Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -26,7 +26,8 @@
 
 namespace {
 
-class DanglingInternalBufferChecker : public Checker {
+class DanglingInternalBufferChecker : public Checker {
   CallDescription CStrFn;
 
 public:
@@ -36,6 +37,9 @@
   /// corresponding string object region in the ProgramState. Mark the symbol
   /// released if the string object is destroyed.
   void checkPostCall(const CallEvent , CheckerContext ) const;
+
+  /// Clean up the ProgramState map.
+  void checkDeadSymbols(SymbolReaper , CheckerContext ) const;
 };
 
 } // end anonymous namespace
@@ -73,12 +77,28 @@
   const SymbolRef *StrBufferPtr = State->get(TypedR);
   const Expr *Origin = Call.getOriginExpr();
   State = allocation_state::markReleased(State, *StrBufferPtr, Origin);
+  State = State->remove(TypedR);
   C.addTransition(State);
   return;
 }
   }
 }
 
+void DanglingInternalBufferChecker::checkDeadSymbols(SymbolReaper ,
+ CheckerContext ) const {
+  if (!SymReaper.hasDeadSymbols())
+return;
+
+  ProgramStateRef State = C.getState();
+  RawPtrMapTy RPM = State->get();
+  for (const auto Region : RPM) {
+if (SymReaper.isDead(Region.second))
+  State = State->remove(Region.first);
+  }
+
+  C.addTransition(State);
+}
+
 void ento::registerDanglingInternalBufferChecker(CheckerManager ) {
   registerNewDeleteChecker(Mgr);
   Mgr.registerChecker();


Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -26,7 +26,8 @@
 
 namespace {
 
-class DanglingInternalBufferChecker : public Checker {
+class DanglingInternalBufferChecker : public Checker {
   CallDescription CStrFn;
 
 public:
@@ -36,6 +37,9 @@
   /// corresponding string object region in the ProgramState. Mark the symbol
   /// released if the string object is destroyed.
   void checkPostCall(const CallEvent , CheckerContext ) const;
+
+  /// Clean up the ProgramState map.
+  void checkDeadSymbols(SymbolReaper , CheckerContext ) const;
 };
 
 } // end anonymous namespace
@@ -73,12 +77,28 @@
   const SymbolRef *StrBufferPtr = State->get(TypedR);
   const Expr *Origin = Call.getOriginExpr();
   State = allocation_state::markReleased(State, *StrBufferPtr, Origin);
+  State = State->remove(TypedR);
   C.addTransition(State);
   return;
 }
   }
 }
 
+void DanglingInternalBufferChecker::checkDeadSymbols(SymbolReaper ,
+ CheckerContext ) const {
+  if (!SymReaper.hasDeadSymbols())
+return;
+
+  ProgramStateRef State = C.getState();
+  RawPtrMapTy RPM = State->get();
+  for (const auto Region : RPM) {
+if (SymReaper.isDead(Region.second))
+  State = State->remove(Region.first);
+  }
+
+  C.addTransition(State);
+}
+
 void ento::registerDanglingInternalBufferChecker(CheckerManager ) {
   registerNewDeleteChecker(Mgr);
   Mgr.registerChecker();
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D47135: [analyzer] A checker for dangling internal buffer pointers in C++

2018-05-26 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 148732.
rnkovacs added a comment.

Address (most) comments.


https://reviews.llvm.org/D47135

Files:
  include/clang/StaticAnalyzer/Checkers/Checkers.td
  lib/StaticAnalyzer/Checkers/AllocationState.h
  lib/StaticAnalyzer/Checkers/CMakeLists.txt
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- /dev/null
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -0,0 +1,71 @@
+//RUN: %clang_analyze_cc1 -analyzer-checker=alpha.cplusplus.DanglingInternalBuffer %s -analyzer-output=text -verify
+
+namespace std {
+
+template< typename CharT >
+class basic_string {
+public:
+  ~basic_string();
+  const CharT *c_str();
+};
+
+typedef basic_string string;
+typedef basic_string wstring;
+typedef basic_string u16string;
+typedef basic_string u32string;
+
+} // end namespace std
+
+void consume(const char *) {}
+void consume(const wchar_t *) {}
+void consume(const char16_t *) {}
+void consume(const char32_t *) {}
+
+void deref_after_scope_char() {
+  const char *c;
+  {
+std::string s;
+c = s.c_str();
+  }
+  consume(c); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_wchar_t() {
+  const wchar_t *w;
+  {
+std::wstring ws;
+w = ws.c_str();
+  }
+  consume(w); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_char16_t() {
+  const char16_t *c16;
+  {
+std::u16string s16;
+c16 = s16.c_str();
+  }
+  consume(c16); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_char32_t() {
+  const char32_t *c32;
+  {
+std::u32string s32;
+c32 = s32.c_str();
+  }
+  consume(c32); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_ok() {
+  const char *c;
+  std::string s;
+  {
+c = s.c_str();
+  }
+  consume(c); // no-warning
+}
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -30,6 +30,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
+#include "AllocationState.h"
 #include 
 #include 
 
@@ -45,7 +46,8 @@
   AF_CXXNew,
   AF_CXXNewArray,
   AF_IfNameIndex,
-  AF_Alloca
+  AF_Alloca,
+  AF_InternalBuffer
 };
 
 class RefState {
@@ -1467,6 +1469,7 @@
 case AF_CXXNew: os << "'new'"; return;
 case AF_CXXNewArray: os << "'new[]'"; return;
 case AF_IfNameIndex: os << "'if_nameindex()'"; return;
+case AF_InternalBuffer: os << "container-specific allocator"; return;
 case AF_Alloca:
 case AF_None: llvm_unreachable("not a deallocation expression");
   }
@@ -1479,6 +1482,7 @@
 case AF_CXXNew: os << "'delete'"; return;
 case AF_CXXNewArray: os << "'delete[]'"; return;
 case AF_IfNameIndex: os << "'if_freenameindex()'"; return;
+case AF_InternalBuffer: os << "container-specific deallocator"; return;
 case AF_Alloca:
 case AF_None: llvm_unreachable("suspicious argument");
   }
@@ -1653,7 +1657,8 @@
 return Optional();
   }
   case AF_CXXNew:
-  case AF_CXXNewArray: {
+  case AF_CXXNewArray:
+  case AF_InternalBuffer: {
 if (IsALeakCheck) {
   if (ChecksEnabled[CK_NewDeleteLeaksChecker])
 return CK_NewDeleteLeaksChecker;
@@ -2991,6 +2996,20 @@
   }
 }
 
+namespace clang {
+namespace ento {
+namespace allocation_state {
+
+ProgramStateRef
+markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) {
+  AllocationFamily Family = AF_InternalBuffer;
+  return State->set(Sym, RefState::getReleased(Family, Origin));
+}
+
+} // end namespace allocation_state
+} // end namespace ento
+} // end namespace clang
+
 void ento::registerNewDeleteLeaksChecker(CheckerManager ) {
   registerCStringCheckerBasic(mgr);
   MallocChecker *checker = mgr.registerChecker();
Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- /dev/null
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -0,0 +1,85 @@
+//=== DanglingInternalBufferChecker.cpp ---*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+//
+// This file defines a check that marks a raw 

[PATCH] D47135: [analyzer] A checker for dangling internal buffer pointers in C++

2018-05-26 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 148727.
rnkovacs retitled this revision from "[analyzer][WIP] A checker for dangling 
string pointers in C++" to "[analyzer] A checker for dangling internal buffer 
pointers in C++".
rnkovacs edited the summary of this revision.
rnkovacs added a comment.

- All `basic_string` types are now supported.
- Mock tests added.
- New `AllocationFamily` `AF_InternalBuffer` introduced.
- NewDeleteChecker dependency added.


https://reviews.llvm.org/D47135

Files:
  include/clang/StaticAnalyzer/Checkers/Checkers.td
  lib/StaticAnalyzer/Checkers/AllocationState.h
  lib/StaticAnalyzer/Checkers/CMakeLists.txt
  lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  test/Analysis/dangling-internal-buffer.cpp

Index: test/Analysis/dangling-internal-buffer.cpp
===
--- /dev/null
+++ test/Analysis/dangling-internal-buffer.cpp
@@ -0,0 +1,62 @@
+//RUN: %clang_analyze_cc1 -analyzer-checker=alpha.cplusplus.DanglingInternalBuffer %s -verify
+
+namespace std {
+
+template< typename CharT >
+class basic_string {
+public:
+  ~basic_string();
+  const CharT *c_str();
+};
+
+typedef basic_string string;
+typedef basic_string wstring;
+typedef basic_string u16string;
+typedef basic_string u32string;
+
+} // end namespace std
+
+void consume(const char *) {}
+void consume(const wchar_t *) {}
+void consume(const char16_t *) {}
+void consume(const char32_t *) {}
+
+void deref_after_scope_char() {
+  const char *c;
+  {
+std::string s;
+c = s.c_str();
+  }
+  consume(c); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_wchar_t() {
+  const wchar_t *w;
+  {
+std::wstring ws;
+w = ws.c_str();
+  }
+  consume(w); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_char16_t() {
+  const char16_t *c16;
+  {
+std::u16string s16;
+c16 = s16.c_str();
+  }
+  consume(c16); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_char32_t() {
+  const char32_t *c32;
+  {
+std::u32string s32;
+c32 = s32.c_str();
+  }
+  consume(c32); // expected-warning {{Use of memory after it is freed}}
+  // expected-note@-1 {{Use of memory after it is freed}}
+}
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -30,6 +30,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
+#include "AllocationState.h"
 #include 
 #include 
 
@@ -45,7 +46,8 @@
   AF_CXXNew,
   AF_CXXNewArray,
   AF_IfNameIndex,
-  AF_Alloca
+  AF_Alloca,
+  AF_InternalBuffer
 };
 
 class RefState {
@@ -1467,6 +1469,7 @@
 case AF_CXXNew: os << "'new'"; return;
 case AF_CXXNewArray: os << "'new[]'"; return;
 case AF_IfNameIndex: os << "'if_nameindex()'"; return;
+case AF_InternalBuffer: os << "container-specific allocator"; return;
 case AF_Alloca:
 case AF_None: llvm_unreachable("not a deallocation expression");
   }
@@ -1479,6 +1482,7 @@
 case AF_CXXNew: os << "'delete'"; return;
 case AF_CXXNewArray: os << "'delete[]'"; return;
 case AF_IfNameIndex: os << "'if_freenameindex()'"; return;
+case AF_InternalBuffer: os << "container-specific deallocator"; return;
 case AF_Alloca:
 case AF_None: llvm_unreachable("suspicious argument");
   }
@@ -1653,7 +1657,8 @@
 return Optional();
   }
   case AF_CXXNew:
-  case AF_CXXNewArray: {
+  case AF_CXXNewArray:
+  case AF_InternalBuffer: {
 if (IsALeakCheck) {
   if (ChecksEnabled[CK_NewDeleteLeaksChecker])
 return CK_NewDeleteLeaksChecker;
@@ -2991,6 +2996,20 @@
   }
 }
 
+namespace clang {
+namespace ento {
+namespace allocation_state {
+
+ProgramStateRef
+markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) {
+  AllocationFamily Family = AF_InternalBuffer;
+  return State->set(Sym, RefState::getReleased(Family, Origin));
+}
+
+} // end namespace allocation_state
+} // end namespace ento
+} // end namespace clang
+
 void ento::registerNewDeleteLeaksChecker(CheckerManager ) {
   registerCStringCheckerBasic(mgr);
   MallocChecker *checker = mgr.registerChecker();
Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
===
--- /dev/null
+++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
@@ -0,0 +1,85 @@
+//=== DanglingInternalBufferChecker.cpp ---*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of 

[PATCH] D47135: [analyzer][WIP] A checker for dangling string pointers in C++

2018-05-22 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

Thanks for your comments!

It would be nice if we could reach a consensus on the naming issue before I 
update the patch. I was wondering, as we plan to support stuff like 
`std::vector::data()`, which is not a string, and `std::string_view`, which is 
not strictly a pointer, could we perhaps go with something like 
`DanglingInternalBufferHandle`? The `AllocationFamily` could similarly be 
`AF_InternalBuffer`. What do you think?


Repository:
  rC Clang

https://reviews.llvm.org/D47135



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


[PATCH] D47135: [analyzer][WIP] A checker for dangling string pointers in C++

2018-05-21 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

Adding a preliminary test file.

F6259981: tests.cpp 


Repository:
  rC Clang

https://reviews.llvm.org/D47135



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


[PATCH] D47135: [analyzer][WIP] A checker for dangling string pointers in C++

2018-05-21 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, xazax.hun, george.karpenkov.
Herald added subscribers: a.sidorin, dkrupp, szepet, baloghadamsoftware, 
whisperity, mgorny.

This check marks a raw pointer to a C++ string object's inner buffer "released"
when the object itself is destroyed. This information can be used by 
MallocChecker
to warn for use-after-free problems.

Test cases using mocks are to be added. Until then, the following file is
used for testing purposes: <>.


Repository:
  rC Clang

https://reviews.llvm.org/D47135

Files:
  include/clang/StaticAnalyzer/Checkers/Checkers.td
  lib/StaticAnalyzer/Checkers/CMakeLists.txt
  lib/StaticAnalyzer/Checkers/DanglingStringPointerChecker.cpp
  lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  lib/StaticAnalyzer/Checkers/RegionState.h

Index: lib/StaticAnalyzer/Checkers/RegionState.h
===
--- /dev/null
+++ lib/StaticAnalyzer/Checkers/RegionState.h
@@ -0,0 +1,28 @@
+//===--- RegionState.h - *- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_REGIONSTATE_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_REGIONSTATE_H
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+
+namespace clang {
+namespace ento {
+
+namespace region_state {
+
+ProgramStateRef markReleased(ProgramStateRef State, SymbolRef Sym,
+ const Expr *Origin);
+
+} // end namespace region_state
+
+} // end namespace ento
+} // end namespace clang
+
+#endif
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -30,6 +30,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
+#include "RegionState.h"
 #include 
 #include 
 
@@ -2991,6 +2992,21 @@
   }
 }
 
+namespace clang {
+namespace ento {
+namespace region_state {
+
+ProgramStateRef
+markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) {
+  // FIXME: This might not be the best allocation family for this purpose.
+  AllocationFamily Family = AF_CXXNew;
+  return State->set(Sym, RefState::getReleased(Family, Origin));
+}
+
+} // end namespace region_state
+} // end namespace ento
+} // end namespace clang
+
 void ento::registerNewDeleteLeaksChecker(CheckerManager ) {
   registerCStringCheckerBasic(mgr);
   MallocChecker *checker = mgr.registerChecker();
Index: lib/StaticAnalyzer/Checkers/DanglingStringPointerChecker.cpp
===
--- /dev/null
+++ lib/StaticAnalyzer/Checkers/DanglingStringPointerChecker.cpp
@@ -0,0 +1,85 @@
+//=== DanglingStringPointerChecker.cpp *- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+//
+// This file defines a check that marks a raw pointer to a C++ string object's
+// inner buffer released when the object is destroyed. This information can
+// then be used by MallocChecker to detect further use-after-free problems.
+//
+//===--===//
+
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "RegionState.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class DanglingStringPointerChecker : public Checker {
+  CallDescription CStrFn;
+
+public:
+  DanglingStringPointerChecker() : CStrFn("c_str") {}
+
+  /// Record the connection between the symbol returned by c_str() and the
+  /// corresponding string object region in the ProgramState. Mark the symbol
+  /// released if the string object is destroyed.
+  void checkPostCall(const CallEvent , CheckerContext ) const;
+};
+
+} // end anonymous namespace
+
+// FIXME: c_str() may be called on a string object many times, so it should
+// have a list of symbols associated with it.
+REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, SymbolRef)
+
+void DanglingStringPointerChecker::checkPostCall(const CallEvent ,
+ CheckerContext ) const {
+  const auto 

[PATCH] D45517: [analyzer] WIP: False positive refutation with Z3

2018-05-08 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added inline comments.



Comment at: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp:2342
+BugReport ) {
+  if (isInvalidated)
+return nullptr;

george.karpenkov wrote:
> Is this field actually necessary? Do we ever check the same bug report with 
> the same visitor multiple times?
I believe this function is called for each node on the bug path. I have a 
similar field to indicate the first visited node in the new version, but there 
may exist a better solution for that as well.



Comment at: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp:2351
+
+  if (!RefutationMgr.checkRangedStateConstraints(Succ->getState())) {
+const LocationContext *LC = Succ->getLocationContext();

george.karpenkov wrote:
> For the initial version I would just do all work in the visitor, but that's a 
> matter of taste.
I think that doing all the work in the visitor would need exposing even more of 
`Z3ConstraintManager`'s internals as of `RangedConstraintManager`. I tried to 
keep such changes minimal.



Comment at: lib/StaticAnalyzer/Core/ProgramState.cpp:86
+  ? CreateZ3ConstraintManager(*this, SubEng)
+  : nullptr;
 }

george.karpenkov wrote:
> Would then we crash on NPE if `getRefutationManager` is called? Getters 
> should preferably not cause crashes.
Um, currently yes, it will give a backend error if clang isn't built with Z3, 
but the option is on.


https://reviews.llvm.org/D45517



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


[PATCH] D45517: [analyzer] WIP: False positive refutation with Z3

2018-05-08 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 145762.
rnkovacs marked 4 inline comments as done.
rnkovacs edited the summary of this revision.
rnkovacs added a comment.

Expression chaining is fixed. The visitor now collects constraints that are 
about to disappear along the bug path and checks them once in the end.


https://reviews.llvm.org/D45517

Files:
  include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
  include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h
  include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h
  include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
  lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
  lib/StaticAnalyzer/Core/BugReporter.cpp
  lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
  lib/StaticAnalyzer/Core/ProgramState.cpp
  lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp

Index: lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
===
--- lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
+++ lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
@@ -7,6 +7,7 @@
 //
 //===--===//
 
+#include "RangedConstraintManager.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
@@ -915,6 +916,13 @@
   void print(ProgramStateRef St, raw_ostream , const char *nl,
  const char *sep) override;
 
+  void reset() override;
+
+  bool isModelFeasible() override;
+
+  void addRangeConstraints(ConstraintRangeTy PrevCR, ConstraintRangeTy SuccCR,
+   bool OnlyPurged) override;
+
   //===--===//
   // Implementation for interface from SimpleConstraintManager.
   //===--===//
@@ -1235,6 +1243,57 @@
   return State->set(CZ);
 }
 
+void Z3ConstraintManager::reset() { Solver.reset(); }
+
+bool Z3ConstraintManager::isModelFeasible() {
+  return Solver.check() != Z3_L_FALSE;
+}
+
+void Z3ConstraintManager::addRangeConstraints(ConstraintRangeTy PrevCR,
+  ConstraintRangeTy SuccCR,
+  bool OnlyPurged) {
+  if (OnlyPurged && PrevCR.isEmpty())
+return;
+  if (!OnlyPurged && SuccCR.isEmpty())
+return;
+  ConstraintRangeTy CR = OnlyPurged ? PrevCR : SuccCR;
+
+  for (ConstraintRangeTy::iterator I = CR.begin(), E = CR.end(); I != E; ++I) {
+SymbolRef Sym = I.getKey();
+
+if (OnlyPurged && SuccCR.contains(Sym))
+  continue;
+
+Z3Expr Constraints = Z3Expr::fromBoolean(false);
+
+for (const auto  : I.getData()) {
+  const llvm::APSInt  = Range.From();
+  const llvm::APSInt  = Range.To();
+
+  assert((getAPSIntType(From) == getAPSIntType(To)) &&
+ "Range values have different types!");
+  QualType RangeTy = getAPSIntType(From);
+  // Skip ranges whose endpoints cannot be converted to APSInts with
+  // a valid APSIntType.
+  if (RangeTy.isNull())
+continue;
+
+  QualType SymTy;
+  Z3Expr Exp = getZ3Expr(Sym, );
+  bool IsSignedTy = SymTy->isSignedIntegerOrEnumerationType();
+
+  Z3Expr FromExp = Z3Expr::fromAPSInt(From);
+  Z3Expr ToExp = Z3Expr::fromAPSInt(To);
+
+  Z3Expr LHS = getZ3BinExpr(Exp, SymTy, BO_GE, FromExp, RangeTy, nullptr);
+  Z3Expr RHS = getZ3BinExpr(Exp, SymTy, BO_LE, ToExp, RangeTy, nullptr);
+  Z3Expr SymRange = Z3Expr::fromBinOp(LHS, BO_LAnd, RHS, IsSignedTy);
+  Constraints = Z3Expr::fromBinOp(Constraints, BO_LOr, SymRange, IsSignedTy);
+}
+Solver.addConstraint(Constraints);
+  }
+}
+
 //===--===//
 // Internal implementation.
 //===--===//
Index: lib/StaticAnalyzer/Core/ProgramState.cpp
===
--- lib/StaticAnalyzer/Core/ProgramState.cpp
+++ lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -13,6 +13,8 @@
 
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
 #include "clang/Analysis/CFG.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
@@ -78,6 +80,10 @@
 CallEventMgr(new CallEventManager(alloc)), Alloc(alloc) {
   StoreMgr = (*CreateSMgr)(*this);
   ConstraintMgr = (*CreateCMgr)(*this, SubEng);
+  AnalyzerOptions  = SubEng->getAnalysisManager().getAnalyzerOptions();
+  RefutationMgr = Opts.shouldCrosscheckWithZ3()
+  ? CreateZ3ConstraintManager(*this, SubEng)
+ 

[PATCH] D45517: [analyzer] WIP: False positive refutation with Z3

2018-04-21 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

In https://reviews.llvm.org/D45517#1074057, @NoQ wrote:

> > The visitor currently checks states appearing as block edges in the 
> > exploded graph. The first idea was to filter states based on the shape of 
> > the exploded graph, by checking the number of successors of the parent 
> > node, but surprisingly, both `succ_size()` and `pred_size()` seemed to 
> > return 1 for each node in the graph (except for the root), even if there 
> > clearly were branchings in the code (and on the `.dot` picture). To my 
> > understanding, the exploded graph is fully constructed at the stage where 
> > visitors are run, so I must be missing something.
>
> Aha, yep, that's probably because visitors are operating on the "trimmed" 
> exploded graph. You can paint it via the `-trim-egraph` flag or by calling 
> `ViewGraph(1)` in the debugger.


Oh, thanks! That explains a lot.

> So, yeah, that's a good optimization that we're not invoking the solver on 
> every node. But i don't think we should focus on improving this optimization 
> further; instead, i think the next obvious step here is to implement it in 
> such a way that we only needed to call the solver //once// for every report. 
> We could simply collect all constraints from all states along the path and 
> put them into the solver all together. This will work because symbols are not 
> mutable and they don't reincarnate.

Won't collecting all constraints and solving a ~100ish equations at once take a 
long time? Maybe the timeout limit for Z3 will need to be slightly increased 
for refutation then.

> Apart from that, the patch seems to be going in the right direction. It 
> should be possible to split up the `RangeSet` refactoring into a different 
> review, for easier reviewing and better commit history.

Done in https://reviews.llvm.org/D45920.

I'll update this patch shortly.


https://reviews.llvm.org/D45517



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


[PATCH] D45517: [analyzer] WIP: False positive refutation with Z3

2018-04-21 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

In https://reviews.llvm.org/D45517#1074057, @NoQ wrote:

> > The visitor currently checks states appearing as block edges in the 
> > exploded graph. The first idea was to filter states based on the shape of 
> > the exploded graph, by checking the number of successors of the parent 
> > node, but surprisingly, both `succ_size()` and `pred_size()` seemed to 
> > return 1 for each node in the graph (except for the root), even if there 
> > clearly were branchings in the code (and on the `.dot` picture). To my 
> > understanding, the exploded graph is fully constructed at the stage where 
> > visitors are run, so I must be missing something.
>
> Aha, yep, that's probably because visitors are operating on the "trimmed" 
> exploded graph. You can paint it via the `-trim-egraph` flag or by calling 
> `ViewGraph(1)` in the debugger.


Oh, thanks! That explains a lot.

> So, yeah, that's a good optimization that we're not invoking the solver on 
> every node. But i don't think we should focus on improving this optimization 
> further; instead, i think the next obvious step here is to implement it in 
> such a way that we only needed to call the solver //once// for every report. 
> We could simply collect all constraints from all states along the path and 
> put them into the solver all together. This will work because symbols are not 
> mutable and they don't reincarnate.

Won't collecting all constraints and solving a ~100ish equations at once take a 
long time? Maybe the timeout limit for Z3 will need to be slightly increased 
for refutation then.

> Apart from that, the patch seems to be going in the right direction. It 
> should be possible to split up the `RangeSet` refactoring into a different 
> review, for easier reviewing and better commit history.

Done in https://reviews.llvm.org/D45920.

I'll update this patch shortly.


https://reviews.llvm.org/D45517



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


[PATCH] D45517: [analyzer] WIP: False positive refutation with Z3

2018-04-21 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 143440.

https://reviews.llvm.org/D45517

Files:
  include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
  include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h
  include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h
  include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
  lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
  lib/StaticAnalyzer/Core/BugReporter.cpp
  lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
  lib/StaticAnalyzer/Core/ProgramState.cpp
  lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp

Index: lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
===
--- lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
+++ lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
@@ -7,6 +7,7 @@
 //
 //===--===//
 
+#include "RangedConstraintManager.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
@@ -915,6 +916,8 @@
   void print(ProgramStateRef St, raw_ostream , const char *nl,
  const char *sep) override;
 
+  bool checkRangedStateConstraints(ProgramStateRef State) override;
+
   //===--===//
   // Implementation for interface from SimpleConstraintManager.
   //===--===//
@@ -1235,6 +1238,47 @@
   return State->set(CZ);
 }
 
+bool Z3ConstraintManager::checkRangedStateConstraints(ProgramStateRef State) {
+  Solver.reset();
+  ConstraintRangeTy CR = State->get();
+
+  for (ConstraintRangeTy::iterator I = CR.begin(), E = CR.end(); I != E; ++I) {
+SymbolRef Sym = I.getKey();
+
+for (const auto  : I.getData()) {
+  const llvm::APSInt  = Range.From();
+  const llvm::APSInt  = Range.To();
+
+  assert((getAPSIntType(From) == getAPSIntType(To)) &&
+ "Range values have different types!");
+  QualType RangeTy = getAPSIntType(From);
+  // Skip ranges whose endpoints cannot be converted to APSInts with
+  // a valid APSIntType.
+  if (RangeTy.isNull())
+continue;
+
+  QualType SymTy;
+  Z3Expr Exp = getZ3Expr(Sym, );
+  bool isSignedTy = SymTy->isSignedIntegerOrEnumerationType();
+
+  Z3Expr FromExp = Z3Expr::fromAPSInt(From);
+  Z3Expr ToExp = Z3Expr::fromAPSInt(To);
+
+  if (From == To) {
+Z3Expr Eq = getZ3BinExpr(Exp, SymTy, BO_EQ, FromExp, RangeTy, nullptr);
+Solver.addConstraint(Eq);
+  } else {
+Z3Expr LHS = getZ3BinExpr(Exp, SymTy, BO_GE, FromExp, RangeTy, nullptr);
+Z3Expr RHS = getZ3BinExpr(Exp, SymTy, BO_LE, ToExp, RangeTy, nullptr);
+Solver.addConstraint(Z3Expr::fromBinOp(LHS, BO_LOr, RHS, isSignedTy));
+  }
+}
+  }
+  // If Z3 timeouts, Z3_L_UNDEF is returned, and we assume that the state
+  // is feasible.
+  return Solver.check() != Z3_L_FALSE;
+}
+
 //===--===//
 // Internal implementation.
 //===--===//
Index: lib/StaticAnalyzer/Core/ProgramState.cpp
===
--- lib/StaticAnalyzer/Core/ProgramState.cpp
+++ lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -13,6 +13,8 @@
 
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
 #include "clang/Analysis/CFG.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
@@ -78,6 +80,10 @@
 CallEventMgr(new CallEventManager(alloc)), Alloc(alloc) {
   StoreMgr = (*CreateSMgr)(*this);
   ConstraintMgr = (*CreateCMgr)(*this, SubEng);
+  AnalyzerOptions  = SubEng->getAnalysisManager().getAnalyzerOptions();
+  RefutationMgr = Opts.shouldPostProcessBugReports()
+  ? CreateZ3ConstraintManager(*this, SubEng)
+  : nullptr;
 }
 
 
Index: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
===
--- lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -2333,3 +2333,26 @@
 
   return std::move(Piece);
 }
+
+std::shared_ptr
+FalsePositiveRefutationBRVisitor::VisitNode(const ExplodedNode *Succ,
+const ExplodedNode *Prev,
+BugReporterContext ,
+BugReport ) {
+  if (isInvalidated)
+return nullptr;
+
+  if (Succ->getLocation().getKind() != ProgramPoint::BlockEdgeKind)
+

[PATCH] D45920: [analyzer] Move RangeSet related declarations into the RangedConstraintManager header.

2018-04-21 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, george.karpenkov, dcoughlin.
Herald added subscribers: dkrupp, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun, whisperity.

I could also move `RangedConstraintManager.h` under `include/` if you agree as 
it seems slightly out of place under `lib/`.


Repository:
  rC Clang

https://reviews.llvm.org/D45920

Files:
  lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  lib/StaticAnalyzer/Core/RangedConstraintManager.h

Index: lib/StaticAnalyzer/Core/RangedConstraintManager.h
===
--- lib/StaticAnalyzer/Core/RangedConstraintManager.h
+++ lib/StaticAnalyzer/Core/RangedConstraintManager.h
@@ -15,12 +15,124 @@
 #define LLVM_CLANG_LIB_STATICANALYZER_CORE_RANGEDCONSTRAINTMANAGER_H
 
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h"
 
 namespace clang {
 
 namespace ento {
 
+/// A Range represents the closed range [from, to].  The caller must
+/// guarantee that from <= to.  Note that Range is immutable, so as not
+/// to subvert RangeSet's immutability.
+class Range : public std::pair {
+public:
+  Range(const llvm::APSInt , const llvm::APSInt )
+  : std::pair(, ) {
+assert(from <= to);
+  }
+  bool Includes(const llvm::APSInt ) const {
+return *first <= v && v <= *second;
+  }
+  const llvm::APSInt () const { return *first; }
+  const llvm::APSInt () const { return *second; }
+  const llvm::APSInt *getConcreteValue() const {
+return () == () ? () : nullptr;
+  }
+
+  void Profile(llvm::FoldingSetNodeID ) const {
+ID.AddPointer(());
+ID.AddPointer(());
+  }
+};
+
+class RangeTrait : public llvm::ImutContainerInfo {
+public:
+  // When comparing if one Range is less than another, we should compare
+  // the actual APSInt values instead of their pointers.  This keeps the order
+  // consistent (instead of comparing by pointer values) and can potentially
+  // be used to speed up some of the operations in RangeSet.
+  static inline bool isLess(key_type_ref lhs, key_type_ref rhs) {
+return *lhs.first < *rhs.first ||
+   (!(*rhs.first < *lhs.first) && *lhs.second < *rhs.second);
+  }
+};
+
+/// RangeSet contains a set of ranges. If the set is empty, then
+///  there the value of a symbol is overly constrained and there are no
+///  possible values for that symbol.
+class RangeSet {
+  typedef llvm::ImmutableSet PrimRangeSet;
+  PrimRangeSet ranges; // no need to make const, since it is an
+   // ImmutableSet - this allows default operator=
+   // to work.
+public:
+  typedef PrimRangeSet::Factory Factory;
+  typedef PrimRangeSet::iterator iterator;
+
+  RangeSet(PrimRangeSet RS) : ranges(RS) {}
+
+  /// Create a new set with all ranges of this set and RS.
+  /// Possible intersections are not checked here.
+  RangeSet addRange(Factory , const RangeSet ) {
+PrimRangeSet Ranges(RS.ranges);
+for (const auto  : ranges)
+  Ranges = F.add(Ranges, range);
+return RangeSet(Ranges);
+  }
+
+  iterator begin() const { return ranges.begin(); }
+  iterator end() const { return ranges.end(); }
+
+  bool isEmpty() const { return ranges.isEmpty(); }
+
+  /// Construct a new RangeSet representing '{ [from, to] }'.
+  RangeSet(Factory , const llvm::APSInt , const llvm::APSInt )
+  : ranges(F.add(F.getEmptySet(), Range(from, to))) {}
+
+  /// Profile - Generates a hash profile of this RangeSet for use
+  ///  by FoldingSet.
+  void Profile(llvm::FoldingSetNodeID ) const { ranges.Profile(ID); }
+
+  /// getConcreteValue - If a symbol is contrained to equal a specific integer
+  ///  constant then this method returns that value.  Otherwise, it returns
+  ///  NULL.
+  const llvm::APSInt *getConcreteValue() const {
+return ranges.isSingleton() ? ranges.begin()->getConcreteValue() : nullptr;
+  }
+
+private:
+  void IntersectInRange(BasicValueFactory , Factory ,
+const llvm::APSInt , const llvm::APSInt ,
+PrimRangeSet , PrimRangeSet::iterator ,
+PrimRangeSet::iterator ) const;
+
+  const llvm::APSInt () const;
+
+  bool pin(llvm::APSInt , llvm::APSInt ) const;
+
+public:
+  RangeSet Intersect(BasicValueFactory , Factory , llvm::APSInt Lower,
+ llvm::APSInt Upper) const;
+
+  void print(raw_ostream ) const;
+
+  bool operator==(const RangeSet ) const {
+return ranges == other.ranges;
+  }
+};
+
+
+class ConstraintRange {};
+using ConstraintRangeTy = llvm::ImmutableMap;
+
+template <>
+struct ProgramStateTrait
+  : public ProgramStatePartialTrait {
+  static void *GDMIndex() { static int Index; return  }
+};
+
+
 class RangedConstraintManager : public SimpleConstraintManager {
 public:
   

[PATCH] D45517: [analyzer] WIP: False positive refutation with Z3

2018-04-20 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 143287.
rnkovacs edited the summary of this revision.
rnkovacs added a comment.

Fixed logical operator in the 
`Z3ConstraintManager::checkRangedStateConstraints()` function.


https://reviews.llvm.org/D45517

Files:
  include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
  include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h
  include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h
  include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
  lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
  lib/StaticAnalyzer/Core/BugReporter.cpp
  lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
  lib/StaticAnalyzer/Core/ProgramState.cpp
  lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  lib/StaticAnalyzer/Core/RangedConstraintManager.h
  lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp

Index: lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
===
--- lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
+++ lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
@@ -7,6 +7,7 @@
 //
 //===--===//
 
+#include "RangedConstraintManager.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
@@ -915,6 +916,8 @@
   void print(ProgramStateRef St, raw_ostream , const char *nl,
  const char *sep) override;
 
+  bool checkRangedStateConstraints(ProgramStateRef State) override;
+
   //===--===//
   // Implementation for interface from SimpleConstraintManager.
   //===--===//
@@ -1235,6 +1238,47 @@
   return State->set(CZ);
 }
 
+bool Z3ConstraintManager::checkRangedStateConstraints(ProgramStateRef State) {
+  Solver.reset();
+  ConstraintRangeTy CR = State->get();
+
+  for (ConstraintRangeTy::iterator I = CR.begin(), E = CR.end(); I != E; ++I) {
+SymbolRef Sym = I.getKey();
+
+for (const auto  : I.getData()) {
+  const llvm::APSInt  = Range.From();
+  const llvm::APSInt  = Range.To();
+
+  assert((getAPSIntType(From) == getAPSIntType(To)) &&
+ "Range values have different types!");
+  QualType RangeTy = getAPSIntType(From);
+  // Skip ranges whose endpoints cannot be converted to APSInts with
+  // a valid APSIntType.
+  if (RangeTy.isNull())
+continue;
+
+  QualType SymTy;
+  Z3Expr Exp = getZ3Expr(Sym, );
+  bool isSignedTy = SymTy->isSignedIntegerOrEnumerationType();
+
+  Z3Expr FromExp = Z3Expr::fromAPSInt(From);
+  Z3Expr ToExp = Z3Expr::fromAPSInt(To);
+
+  if (From == To) {
+Z3Expr Eq = getZ3BinExpr(Exp, SymTy, BO_EQ, FromExp, RangeTy, nullptr);
+Solver.addConstraint(Eq);
+  } else {
+Z3Expr LHS = getZ3BinExpr(Exp, SymTy, BO_GE, FromExp, RangeTy, nullptr);
+Z3Expr RHS = getZ3BinExpr(Exp, SymTy, BO_LE, ToExp, RangeTy, nullptr);
+Solver.addConstraint(Z3Expr::fromBinOp(LHS, BO_LOr, RHS, isSignedTy));
+  }
+}
+  }
+  // If Z3 timeouts, Z3_L_UNDEF is returned, and we assume that the state
+  // is feasible.
+  return Solver.check() != Z3_L_FALSE;
+}
+
 //===--===//
 // Internal implementation.
 //===--===//
Index: lib/StaticAnalyzer/Core/RangedConstraintManager.h
===
--- lib/StaticAnalyzer/Core/RangedConstraintManager.h
+++ lib/StaticAnalyzer/Core/RangedConstraintManager.h
@@ -15,12 +15,124 @@
 #define LLVM_CLANG_LIB_STATICANALYZER_CORE_RANGEDCONSTRAINTMANAGER_H
 
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h"
 
 namespace clang {
 
 namespace ento {
 
+/// A Range represents the closed range [from, to].  The caller must
+/// guarantee that from <= to.  Note that Range is immutable, so as not
+/// to subvert RangeSet's immutability.
+class Range : public std::pair {
+public:
+  Range(const llvm::APSInt , const llvm::APSInt )
+  : std::pair(, ) {
+assert(from <= to);
+  }
+  bool Includes(const llvm::APSInt ) const {
+return *first <= v && v <= *second;
+  }
+  const llvm::APSInt () const { return *first; }
+  const llvm::APSInt () const { return *second; }
+  const llvm::APSInt *getConcreteValue() const {
+return () == () ? () : nullptr;
+  }
+
+  void Profile(llvm::FoldingSetNodeID ) const {
+ID.AddPointer(());
+ID.AddPointer(());
+  }
+};
+
+class RangeTrait : public llvm::ImutContainerInfo {
+public:
+  // When comparing if one Range is less than another, we should compare
+  // the 

[PATCH] D45517: [analyzer] WIP: False positive refutation with Z3

2018-04-11 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: george.karpenkov, NoQ, dcoughlin.
Herald added subscribers: a.sidorin, szepet, baloghadamsoftware, whisperity, 
fhahn.

This is a prototype of a bug reporter visitor that invalidates bug reports by 
re-checking constraints of certain states on the bug path using the Z3 
constraint manager backend. The functionality is available under the 
`postprocess-reports` analyzer config flag.

Results of analysis runs on a few open-source projects with this option turned 
on can be explored here .

This work is preliminary and any comments are appreciated. A few remarks:

- In order to work with constraints generated by the range-based constraint 
manager outside its own file, much of the `Range`, `RangeTrait` and `RangeSet` 
classes have been moved to the corresponding header file.

- The visitor currently checks states appearing as block edges in the exploded 
graph. The first idea was to filter states based on the shape of the exploded 
graph, by checking the number of successors of the parent node, but 
surprisingly, both `succ_size()` and `pred_size()` seemed to return 1 for each 
node in the graph (except for the root), even if there clearly were branchings 
in the code (and on the `.dot` picture). To my understanding, the exploded 
graph is fully constructed at the stage where visitors are run, so I must be 
missing something.

- 1-bit APSInts obtained from ranged constraints crashed when 
`isSignedIntegerOrEnumerationType()` was called on them inside 
`Z3ConstraintManager`'s methods. This issue is currently sidestepped, but they 
might be converted to a valid built-in type at some point.


Repository:
  rC Clang

https://reviews.llvm.org/D45517

Files:
  include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
  include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h
  include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h
  include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
  lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
  lib/StaticAnalyzer/Core/BugReporter.cpp
  lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
  lib/StaticAnalyzer/Core/ProgramState.cpp
  lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  lib/StaticAnalyzer/Core/RangedConstraintManager.h
  lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp

Index: lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
===
--- lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
+++ lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
@@ -7,6 +7,7 @@
 //
 //===--===//
 
+#include "RangedConstraintManager.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
@@ -915,6 +916,8 @@
   void print(ProgramStateRef St, raw_ostream , const char *nl,
  const char *sep) override;
 
+  bool checkRangedStateConstraints(ProgramStateRef State) override;
+
   //===--===//
   // Implementation for interface from SimpleConstraintManager.
   //===--===//
@@ -1235,6 +1238,47 @@
   return State->set(CZ);
 }
 
+bool Z3ConstraintManager::checkRangedStateConstraints(ProgramStateRef State) {
+  Solver.reset();
+  ConstraintRangeTy CR = State->get();
+
+  for (ConstraintRangeTy::iterator I = CR.begin(), E = CR.end(); I != E; ++I) {
+SymbolRef Sym = I.getKey();
+
+for (const auto  : I.getData()) {
+  const llvm::APSInt  = Range.From();
+  const llvm::APSInt  = Range.To();
+
+  assert((getAPSIntType(From) == getAPSIntType(To)) &&
+ "Range values have different types!");
+  QualType RangeTy = getAPSIntType(From);
+  // Skip ranges whose endpoints cannot be converted to APSInts with
+  // a valid APSIntType.
+  if (RangeTy.isNull())
+continue;
+
+  QualType SymTy;
+  Z3Expr Exp = getZ3Expr(Sym, );
+  bool isSignedTy = SymTy->isSignedIntegerOrEnumerationType();
+
+  Z3Expr FromExp = Z3Expr::fromAPSInt(From);
+  Z3Expr ToExp = Z3Expr::fromAPSInt(To);
+
+  if (From == To) {
+Z3Expr Eq = getZ3BinExpr(Exp, SymTy, BO_EQ, FromExp, RangeTy, nullptr);
+Solver.addConstraint(Eq);
+  } else {
+Z3Expr LHS = getZ3BinExpr(Exp, SymTy, BO_GE, FromExp, RangeTy, nullptr);
+Z3Expr RHS = getZ3BinExpr(Exp, SymTy, BO_LE, ToExp, RangeTy, nullptr);
+Solver.addConstraint(Z3Expr::fromBinOp(LHS, BO_LAnd, RHS, isSignedTy));
+  }
+}
+  }
+  // If Z3 timeouts, Z3_L_UNDEF is returned, and we assume that the state
+  // is feasible.
+  return Solver.check() != Z3_L_FALSE;
+}
+
 //===--===//
 // Internal implementation.
 

[PATCH] D41816: [analyzer] Model and check unrepresentable left shifts

2018-01-15 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added inline comments.



Comment at: lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp:150
+SB.getKnownValue(state, C.getSVal(B->getRHS()));
+if ((unsigned) RHS->getZExtValue() > LHS->countLeadingZeros()) {
+  OS << "The result of the left shift is undefined due to shifting \'"

dcoughlin wrote:
> This inner 'if' looks fishy to me because if the 'else' branch is ever taken 
> then OS will be empty.
> 
> If the else branch can't be taken then you should turn it into an assert(). 
> If it can be taken, then you should make sure that the fall through goes 
> through the "default" else case at the bottom. One way to do this is to pull 
> out the "is representable logic" into a helper function and call that in the 
> containing 'else if'.
> 
> Something like:
> 
> ```
> if (B->getOpcode() == BinaryOperatorKind::BO_Shl && 
> isLeftShiftResultRepresentable(LHS, RHS)) {
>   OS << "The result of the left shift ..."
> }
> ```
I overlooked this issue, thanks for pointing out. I pulled the logic out into a 
helper function.


https://reviews.llvm.org/D41816



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


[PATCH] D41816: [analyzer] Model and check unrepresentable left shifts

2018-01-15 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 129905.
rnkovacs marked an inline comment as done.

https://reviews.llvm.org/D41816

Files:
  lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
  lib/StaticAnalyzer/Core/BasicValueFactory.cpp
  test/Analysis/bitwise-ops.c


Index: test/Analysis/bitwise-ops.c
===
--- test/Analysis/bitwise-ops.c
+++ test/Analysis/bitwise-ops.c
@@ -51,3 +51,9 @@
   }
   return 0;
 }
+
+int testUnrepresentableLeftShift(int a) {
+  if (a == 8)
+return a << 30; // expected-warning{{The result of the left shift is 
undefined due to shifting '8' by '30', which is unrepresentable in the unsigned 
version of the return type 'int'}}
+  return 0;
+}
Index: lib/StaticAnalyzer/Core/BasicValueFactory.cpp
===
--- lib/StaticAnalyzer/Core/BasicValueFactory.cpp
+++ lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -224,7 +224,6 @@
   // FIXME: This logic should probably go higher up, where we can
   // test these conditions symbolically.
 
-  // FIXME: Expand these checks to include all undefined behavior.
   if (V1.isSigned() && V1.isNegative())
 return nullptr;
 
@@ -236,16 +235,17 @@
   if (Amt >= V1.getBitWidth())
 return nullptr;
 
+  if (V1.isSigned() && Amt > V1.countLeadingZeros())
+  return nullptr;
+
   return ( V1.operator<<( (unsigned) Amt ));
 }
 
 case BO_Shr: {
 
   // FIXME: This logic should probably go higher up, where we can
   // test these conditions symbolically.
 
-  // FIXME: Expand these checks to include all undefined behavior.
-
   if (V2.isSigned() && V2.isNegative())
 return nullptr;
 
Index: lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
+++ lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
@@ -64,6 +64,15 @@
   B->getRHS(), C.getASTContext().getIntWidth(B->getLHS()->getType()));
 }
 
+static bool isLeftShiftResultUnrepresentable(const BinaryOperator *B,
+ CheckerContext ) {
+  SValBuilder  = C.getSValBuilder();
+  ProgramStateRef State = C.getState();
+  const llvm::APSInt *LHS = SB.getKnownValue(State, C.getSVal(B->getLHS()));
+  const llvm::APSInt *RHS = SB.getKnownValue(State, C.getSVal(B->getRHS()));
+  return (unsigned)RHS->getZExtValue() > LHS->countLeadingZeros();
+}
+
 void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
CheckerContext ) const {
   ProgramStateRef state = C.getState();
@@ -141,6 +150,18 @@
  C.isNegative(B->getLHS())) {
 OS << "The result of the left shift is undefined because the left "
   "operand is negative";
+  } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
+ isLeftShiftResultUnrepresentable(B, C)) {
+SValBuilder  = C.getSValBuilder();
+const llvm::APSInt *LHS =
+SB.getKnownValue(state, C.getSVal(B->getLHS()));
+const llvm::APSInt *RHS =
+SB.getKnownValue(state, C.getSVal(B->getRHS()));
+OS << "The result of the left shift is undefined due to shifting \'"
+   << LHS->getSExtValue() << "\' by \'" << RHS->getZExtValue()
+   << "\', which is unrepresentable in the unsigned version of "
+   << "the return type \'" << B->getLHS()->getType().getAsString()
+   << "\'";
   } else {
 OS << "The result of the '"
<< BinaryOperator::getOpcodeStr(B->getOpcode())


Index: test/Analysis/bitwise-ops.c
===
--- test/Analysis/bitwise-ops.c
+++ test/Analysis/bitwise-ops.c
@@ -51,3 +51,9 @@
   }
   return 0;
 }
+
+int testUnrepresentableLeftShift(int a) {
+  if (a == 8)
+return a << 30; // expected-warning{{The result of the left shift is undefined due to shifting '8' by '30', which is unrepresentable in the unsigned version of the return type 'int'}}
+  return 0;
+}
Index: lib/StaticAnalyzer/Core/BasicValueFactory.cpp
===
--- lib/StaticAnalyzer/Core/BasicValueFactory.cpp
+++ lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -224,7 +224,6 @@
   // FIXME: This logic should probably go higher up, where we can
   // test these conditions symbolically.
 
-  // FIXME: Expand these checks to include all undefined behavior.
   if (V1.isSigned() && V1.isNegative())
 return nullptr;
 
@@ -236,16 +235,17 @@
   if (Amt >= V1.getBitWidth())
 return nullptr;
 
+  if (V1.isSigned() && Amt > V1.countLeadingZeros())
+  return nullptr;
+
   return ( V1.operator<<( (unsigned) Amt ));
 }
 
 case BO_Shr: {
 
   // FIXME: This logic should probably go higher up, where we can
   

[PATCH] D41816: [analyzer] Model and check unrepresentable left shifts

2018-01-11 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 129448.
rnkovacs added a comment.

I extended the warning message to include more information. What do you think?


https://reviews.llvm.org/D41816

Files:
  lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
  lib/StaticAnalyzer/Core/BasicValueFactory.cpp
  test/Analysis/bitwise-ops.c


Index: test/Analysis/bitwise-ops.c
===
--- test/Analysis/bitwise-ops.c
+++ test/Analysis/bitwise-ops.c
@@ -51,3 +51,9 @@
   }
   return 0;
 }
+
+int testUnrepresentableLeftShift(int a) {
+  if (a == 8)
+return a << 30; // expected-warning{{The result of the left shift is 
undefined due to shifting '8' by '30', which is unrepresentable in the unsigned 
version of the return type 'int'}}
+  return 0;
+}
Index: lib/StaticAnalyzer/Core/BasicValueFactory.cpp
===
--- lib/StaticAnalyzer/Core/BasicValueFactory.cpp
+++ lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -224,7 +224,6 @@
   // FIXME: This logic should probably go higher up, where we can
   // test these conditions symbolically.
 
-  // FIXME: Expand these checks to include all undefined behavior.
   if (V1.isSigned() && V1.isNegative())
 return nullptr;
 
@@ -236,16 +235,17 @@
   if (Amt >= V1.getBitWidth())
 return nullptr;
 
+  if (V1.isSigned() && Amt > V1.countLeadingZeros())
+return nullptr;
+
   return ( V1.operator<<( (unsigned) Amt ));
 }
 
 case BO_Shr: {
 
   // FIXME: This logic should probably go higher up, where we can
   // test these conditions symbolically.
 
-  // FIXME: Expand these checks to include all undefined behavior.
-
   if (V2.isSigned() && V2.isNegative())
 return nullptr;
 
Index: lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
+++ lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
@@ -141,6 +141,19 @@
  C.isNegative(B->getLHS())) {
 OS << "The result of the left shift is undefined because the left "
   "operand is negative";
+  } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl) {
+SValBuilder  = C.getSValBuilder();
+const llvm::APSInt *LHS =
+SB.getKnownValue(state, C.getSVal(B->getLHS()));
+const llvm::APSInt *RHS =
+SB.getKnownValue(state, C.getSVal(B->getRHS()));
+if ((unsigned) RHS->getZExtValue() > LHS->countLeadingZeros()) {
+  OS << "The result of the left shift is undefined due to shifting \'"
+ << LHS->getSExtValue() << "\' by \'" << RHS->getZExtValue()
+ << "\', which is unrepresentable in the unsigned version of "
+ << "the return type \'" << B->getLHS()->getType().getAsString()
+ << "\'";
+}
   } else {
 OS << "The result of the '"
<< BinaryOperator::getOpcodeStr(B->getOpcode())


Index: test/Analysis/bitwise-ops.c
===
--- test/Analysis/bitwise-ops.c
+++ test/Analysis/bitwise-ops.c
@@ -51,3 +51,9 @@
   }
   return 0;
 }
+
+int testUnrepresentableLeftShift(int a) {
+  if (a == 8)
+return a << 30; // expected-warning{{The result of the left shift is undefined due to shifting '8' by '30', which is unrepresentable in the unsigned version of the return type 'int'}}
+  return 0;
+}
Index: lib/StaticAnalyzer/Core/BasicValueFactory.cpp
===
--- lib/StaticAnalyzer/Core/BasicValueFactory.cpp
+++ lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -224,7 +224,6 @@
   // FIXME: This logic should probably go higher up, where we can
   // test these conditions symbolically.
 
-  // FIXME: Expand these checks to include all undefined behavior.
   if (V1.isSigned() && V1.isNegative())
 return nullptr;
 
@@ -236,16 +235,17 @@
   if (Amt >= V1.getBitWidth())
 return nullptr;
 
+  if (V1.isSigned() && Amt > V1.countLeadingZeros())
+return nullptr;
+
   return ( V1.operator<<( (unsigned) Amt ));
 }
 
 case BO_Shr: {
 
   // FIXME: This logic should probably go higher up, where we can
   // test these conditions symbolically.
 
-  // FIXME: Expand these checks to include all undefined behavior.
-
   if (V2.isSigned() && V2.isNegative())
 return nullptr;
 
Index: lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
+++ lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
@@ -141,6 +141,19 @@
  C.isNegative(B->getLHS())) {
 OS << "The result of the left shift is undefined because the left "
   "operand is negative";
+  } else 

[PATCH] D41816: [analyzer] Model and check unrepresentable left shifts

2018-01-09 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

In https://reviews.llvm.org/D41816#970845, @xazax.hun wrote:

> Overall looks good to me, one comment inline. I think it is good to have 
> these checks to prevent the analyzer executing undefined behavior. Maybe this 
> would make it more feasible to run the analyzer with ubsan :)
>  In the future, it would be great to also look for these cases symbolically, 
> but I believe it is perfectly fine to have that in a separate patch.


I agree, but I thought that making these checks complete first might be a good 
idea.


https://reviews.llvm.org/D41816



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


[PATCH] D41816: [analyzer] Model and check unrepresentable left shifts

2018-01-08 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.
rnkovacs added reviewers: NoQ, dcoughlin, xazax.hun.
Herald added subscribers: a.sidorin, szepet, baloghadamsoftware, whisperity.

Left shifting a signed positive value is undefined if the result is not 
representable in the unsigned version of the return type.

The analyzer now returns an UndefVal in this case and UndefResultChecker is 
updated to warn about it.


https://reviews.llvm.org/D41816

Files:
  lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
  lib/StaticAnalyzer/Core/BasicValueFactory.cpp
  test/Analysis/bitwise-ops.c


Index: test/Analysis/bitwise-ops.c
===
--- test/Analysis/bitwise-ops.c
+++ test/Analysis/bitwise-ops.c
@@ -51,3 +51,9 @@
   }
   return 0;
 }
+
+int testUnrepresentableLeftShift(int a) {
+  if (a == 8)
+return a << 30; // expected-warning{{The result of the left shift is 
undefined because it is not representable in the return type}}
+  return 0;
+}
Index: lib/StaticAnalyzer/Core/BasicValueFactory.cpp
===
--- lib/StaticAnalyzer/Core/BasicValueFactory.cpp
+++ lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -224,7 +224,6 @@
   // FIXME: This logic should probably go higher up, where we can
   // test these conditions symbolically.
 
-  // FIXME: Expand these checks to include all undefined behavior.
   if (V1.isSigned() && V1.isNegative())
 return nullptr;
 
@@ -236,16 +235,17 @@
   if (Amt >= V1.getBitWidth())
 return nullptr;
 
+  if (V1.isSigned() && (unsigned) Amt > V1.countLeadingZeros())
+  return nullptr;
+
   return ( V1.operator<<( (unsigned) Amt ));
 }
 
 case BO_Shr: {
 
   // FIXME: This logic should probably go higher up, where we can
   // test these conditions symbolically.
 
-  // FIXME: Expand these checks to include all undefined behavior.
-
   if (V2.isSigned() && V2.isNegative())
 return nullptr;
 
Index: lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
+++ lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
@@ -141,6 +141,15 @@
  C.isNegative(B->getLHS())) {
 OS << "The result of the left shift is undefined because the left "
   "operand is negative";
+  } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl) {
+SValBuilder  = C.getSValBuilder();
+const llvm::APSInt *LHS =
+SB.getKnownValue(state, C.getSVal(B->getLHS()));
+const llvm::APSInt *RHS =
+SB.getKnownValue(state, C.getSVal(B->getRHS()));
+if ((unsigned) RHS->getZExtValue() > LHS->countLeadingZeros())
+  OS << "The result of the left shift is undefined because it is not "
+"representable in the return type";
   } else {
 OS << "The result of the '"
<< BinaryOperator::getOpcodeStr(B->getOpcode())


Index: test/Analysis/bitwise-ops.c
===
--- test/Analysis/bitwise-ops.c
+++ test/Analysis/bitwise-ops.c
@@ -51,3 +51,9 @@
   }
   return 0;
 }
+
+int testUnrepresentableLeftShift(int a) {
+  if (a == 8)
+return a << 30; // expected-warning{{The result of the left shift is undefined because it is not representable in the return type}}
+  return 0;
+}
Index: lib/StaticAnalyzer/Core/BasicValueFactory.cpp
===
--- lib/StaticAnalyzer/Core/BasicValueFactory.cpp
+++ lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -224,7 +224,6 @@
   // FIXME: This logic should probably go higher up, where we can
   // test these conditions symbolically.
 
-  // FIXME: Expand these checks to include all undefined behavior.
   if (V1.isSigned() && V1.isNegative())
 return nullptr;
 
@@ -236,16 +235,17 @@
   if (Amt >= V1.getBitWidth())
 return nullptr;
 
+  if (V1.isSigned() && (unsigned) Amt > V1.countLeadingZeros())
+  return nullptr;
+
   return ( V1.operator<<( (unsigned) Amt ));
 }
 
 case BO_Shr: {
 
   // FIXME: This logic should probably go higher up, where we can
   // test these conditions symbolically.
 
-  // FIXME: Expand these checks to include all undefined behavior.
-
   if (V2.isSigned() && V2.isNegative())
 return nullptr;
 
Index: lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
===
--- lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
+++ lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
@@ -141,6 +141,15 @@
  C.isNegative(B->getLHS())) {
 OS << "The result of the left shift is undefined because the left "
   "operand is negative";
+  } else if (B->getOpcode() == 

[PATCH] D35796: [analyzer] Delete with non-virtual destructor check

2017-09-22 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

In https://reviews.llvm.org/D35796#878200, @dcoughlin wrote:

> This looks good to me! Do you have commit access, or do you need someone to 
> commit it for you?


Thanks! I don't, so it would be nice if someone committed it for me.


https://reviews.llvm.org/D35796



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


[PATCH] D35796: [analyzer] Delete with non-virtual destructor check

2017-09-20 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 116060.
rnkovacs added a comment.

- Accidentally left-in comment removed.
- Checker file clang-formatted.


https://reviews.llvm.org/D35796

Files:
  include/clang/StaticAnalyzer/Checkers/Checkers.td
  lib/StaticAnalyzer/Checkers/CMakeLists.txt
  lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
  test/Analysis/DeleteWithNonVirtualDtor.cpp

Index: test/Analysis/DeleteWithNonVirtualDtor.cpp
===
--- /dev/null
+++ test/Analysis/DeleteWithNonVirtualDtor.cpp
@@ -0,0 +1,187 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.cplusplus.DeleteWithNonVirtualDtor -std=c++11 -verify -analyzer-output=text %s
+
+struct Virtual {
+  virtual ~Virtual() {}
+};
+
+struct VDerived : public Virtual {};
+
+struct NonVirtual {
+  ~NonVirtual() {}
+};
+
+struct NVDerived : public NonVirtual {};
+struct NVDoubleDerived : public NVDerived {};
+
+struct Base {
+  virtual void destroy() = 0;
+};
+
+class PrivateDtor final : public Base {
+public:
+  void destroy() { delete this; }
+private:
+  ~PrivateDtor() {}
+};
+
+struct ImplicitNV {
+  virtual void f();
+};
+
+struct ImplicitNVDerived : public ImplicitNV {};
+
+NVDerived *get();
+
+NonVirtual *create() {
+  NonVirtual *x = new NVDerived(); // expected-note{{Conversion from derived to base happened here}}
+  return x;
+}
+
+void sink(NonVirtual *x) {
+  delete x; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
+  // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
+}
+
+void sinkCast(NonVirtual *y) {
+  delete reinterpret_cast(y);
+}
+
+void sinkParamCast(NVDerived *z) {
+  delete z;
+}
+
+void singleDerived() {
+  NonVirtual *sd;
+  sd = new NVDerived(); // expected-note{{Conversion from derived to base happened here}}
+  delete sd; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
+  // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
+}
+
+void singleDerivedArr() {
+  NonVirtual *sda = new NVDerived[5]; // expected-note{{Conversion from derived to base happened here}}
+  delete[] sda; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
+  // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
+}
+
+void doubleDerived() {
+  NonVirtual *dd = new NVDoubleDerived(); // expected-note{{Conversion from derived to base happened here}}
+  delete (dd); // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
+  // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
+}
+
+void assignThroughFunction() {
+  NonVirtual *atf = get(); // expected-note{{Conversion from derived to base happened here}}
+  delete atf; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
+  // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
+}
+
+void assignThroughFunction2() {
+  NonVirtual *atf2;
+  atf2 = get(); // expected-note{{Conversion from derived to base happened here}}
+  delete atf2; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
+  // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
+}
+
+void createThroughFunction() {
+  NonVirtual *ctf = create(); // expected-note{{Calling 'create'}}
+  // expected-note@-1{{Returning from 'create'}}
+  delete ctf; // expected-warning {{Destruction of a polymorphic object with no virtual destructor}}
+  // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
+}
+
+void deleteThroughFunction() {
+  NonVirtual *dtf = new NVDerived(); // expected-note{{Conversion from derived to base happened here}}
+  sink(dtf); // expected-note{{Calling 'sink'}}
+}
+
+void singleCastCStyle() {
+  NVDerived *sccs = new NVDerived();
+  NonVirtual *sccs2 = (NonVirtual*)sccs; // expected-note{{Conversion from derived to base happened here}}
+  delete sccs2; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
+  // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
+}
+
+void doubleCastCStyle() {
+  NonVirtual *dccs = new NVDerived();
+  NVDerived *dccs2 = (NVDerived*)dccs;
+  dccs = (NonVirtual*)dccs2; // expected-note{{Conversion from derived to base happened here}}
+  delete dccs; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
+  // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
+}
+
+void singleCast() {
+  NVDerived *sc = new NVDerived();
+  NonVirtual *sc2 = reinterpret_cast(sc); // expected-note{{Conversion from derived to base happened here}}
+  delete sc2; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
+  // 

[PATCH] D35796: [analyzer] Delete with non-virtual destructor check

2017-09-14 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 115198.
rnkovacs marked an inline comment as done.
rnkovacs retitled this revision from "[analyzer] Misused polymorphic object 
checker" to "[analyzer] Delete with non-virtual destructor check".
rnkovacs edited the summary of this revision.
rnkovacs added a comment.

Sorry for the late reply. I did run it on a few open-source projects as well as 
LLVM/Clang and honestly it didn't find anything. As the test cases seem to work 
fine it might already be in a state ready to bring out of alpha.


https://reviews.llvm.org/D35796

Files:
  include/clang/StaticAnalyzer/Checkers/Checkers.td
  lib/StaticAnalyzer/Checkers/CMakeLists.txt
  lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
  test/Analysis/DeleteWithNonVirtualDtor.cpp

Index: test/Analysis/DeleteWithNonVirtualDtor.cpp
===
--- /dev/null
+++ test/Analysis/DeleteWithNonVirtualDtor.cpp
@@ -0,0 +1,187 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.cplusplus.DeleteWithNonVirtualDtor -std=c++11 -verify -analyzer-output=text %s
+
+struct Virtual {
+  virtual ~Virtual() {}
+};
+
+struct VDerived : public Virtual {};
+
+struct NonVirtual {
+  ~NonVirtual() {}
+};
+
+struct NVDerived : public NonVirtual {};
+struct NVDoubleDerived : public NVDerived {};
+
+struct Base {
+  virtual void destroy() = 0;
+};
+
+class PrivateDtor final : public Base {
+public:
+  void destroy() { delete this; }
+private:
+  ~PrivateDtor() {}
+};
+
+struct ImplicitNV {
+  virtual void f();
+};
+
+struct ImplicitNVDerived : public ImplicitNV {};
+
+NVDerived *get();
+
+NonVirtual *create() {
+  NonVirtual *x = new NVDerived(); // expected-note{{Conversion from derived to base happened here}}
+  return x;
+}
+
+void sink(NonVirtual *x) {
+  delete x; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
+  // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
+}
+
+void sinkCast(NonVirtual *y) {
+  delete reinterpret_cast(y);
+}
+
+void sinkParamCast(NVDerived *z) {
+  delete z;
+}
+
+void singleDerived() {
+  NonVirtual *sd;
+  sd = new NVDerived(); // expected-note{{Conversion from derived to base happened here}}
+  delete sd; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
+  // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
+}
+
+void singleDerivedArr() {
+  NonVirtual *sda = new NVDerived[5]; // expected-note{{Conversion from derived to base happened here}}
+  delete[] sda; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
+  // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
+}
+
+void doubleDerived() {
+  NonVirtual *dd = new NVDoubleDerived(); // expected-note{{Conversion from derived to base happened here}}
+  delete (dd); // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
+  // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
+}
+
+void assignThroughFunction() {
+  NonVirtual *atf = get(); // expected-note{{Conversion from derived to base happened here}}
+  delete atf; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
+  // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
+}
+
+void assignThroughFunction2() {
+  NonVirtual *atf2;
+  atf2 = get(); // expected-note{{Conversion from derived to base happened here}}
+  delete atf2; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
+  // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
+}
+
+void createThroughFunction() {
+  NonVirtual *ctf = create(); // expected-note{{Calling 'create'}}
+  // expected-note@-1{{Returning from 'create'}}
+  delete ctf; // expected-warning {{Destruction of a polymorphic object with no virtual destructor}}
+  // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
+}
+
+void deleteThroughFunction() {
+  NonVirtual *dtf = new NVDerived(); // expected-note{{Conversion from derived to base happened here}}
+  sink(dtf); // expected-note{{Calling 'sink'}}
+}
+
+void singleCastCStyle() {
+  NVDerived *sccs = new NVDerived();
+  NonVirtual *sccs2 = (NonVirtual*)sccs; // expected-note{{Conversion from derived to base happened here}}
+  delete sccs2; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
+  // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
+}
+
+void doubleCastCStyle() {
+  NonVirtual *dccs = new NVDerived();
+  NVDerived *dccs2 = (NVDerived*)dccs;
+  dccs = (NonVirtual*)dccs2; // expected-note{{Conversion from derived to base happened here}}
+  delete dccs; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
+  // 

[PATCH] D35796: [analyzer] Misused polymorphic object checker

2017-09-14 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs marked 3 inline comments as done.
rnkovacs added inline comments.



Comment at: include/clang/StaticAnalyzer/Checkers/Checkers.td:296
 
+def MisusedPolymorphicObjectChecker: Checker<"MisusedPolymorphicObject">,
+ HelpText<"Reports deletions of polymorphic objects with a non-virtual "

dcoughlin wrote:
> I think users would find it helpful if this had a more specific name. There 
> are a lot of ways to misuse polymorphic objects, and this checker won't check 
> all of them.
> 
> What do you think about "DeleteWithNonVirtualDestructor"?
I settled with the slightly abbreviated "DeleteWithNonVirtualDtor" version to 
shorten it a little.


https://reviews.llvm.org/D35796



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


[PATCH] D35932: [clang-tidy] Add integer division check

2017-08-10 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 110539.
rnkovacs marked 3 inline comments as done.
rnkovacs edited the summary of this revision.
rnkovacs added a comment.

Thanks for the comments. I improved the docs and truncated the messages in the 
test file.

We also had concerns about the nested `hasAncestor` matchers but thought that 
it might be worth a try. If this solution proves to cause too much of a 
performance burden I can rewrite it using RAVs.


https://reviews.llvm.org/D35932

Files:
  clang-tidy/bugprone/BugproneTidyModule.cpp
  clang-tidy/bugprone/CMakeLists.txt
  clang-tidy/bugprone/IntegerDivisionCheck.cpp
  clang-tidy/bugprone/IntegerDivisionCheck.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/bugprone-integer-division.rst
  docs/clang-tidy/checks/list.rst
  test/clang-tidy/bugprone-integer-division.cpp

Index: test/clang-tidy/bugprone-integer-division.cpp
===
--- /dev/null
+++ test/clang-tidy/bugprone-integer-division.cpp
@@ -0,0 +1,130 @@
+// RUN: %check_clang_tidy %s bugprone-integer-division %t
+
+// Functions expecting a floating-point parameter.
+void floatArg(float x) {}
+void doubleArg(double x) {}
+void longDoubleArg(long double x) {}
+
+// Functions expected to return a floating-point value.
+float singleDiv() {
+  int x = -5;
+  int y = 2;
+  return x/y;
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: result of integer division used in
+}
+
+double wrongOrder(int x, int y) {
+  return x/y/0.1;
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: result of integer division used in
+}
+
+long double rightOrder(int x, int y) {
+  return 0.1/x/y; // OK
+}
+
+// Typical mathematical functions.
+float sin(float);
+double acos(double);
+long double tanh(long double);
+
+namespace std {
+  using ::sin;
+}
+
+template 
+void intDivSin(T x) {
+  sin(x);
+}
+
+int intFunc(int);
+
+struct X {
+  int n;
+  void m() {
+sin(n / 3);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: result of integer division used in
+  }
+};
+
+void integerDivision() {
+  char a = 2;
+  short b = -5;
+  int c = 9784;
+  enum third { x, y, z=2 };
+  third d = z;
+  char e[] = {'a', 'b', 'c'};
+  char f = *(e + 1 / a);
+  bool g = 1;
+
+  sin(1 + c / (2 + 2));
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: result of integer division used in
+  sin(c / (1 + .5));
+  sin((c + .5) / 3);
+
+  sin(intFunc(3) / 5);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of integer division used in
+  acos(2 / intFunc(7));
+// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: result of integer division used in
+
+  floatArg(1 + 2 / 3);
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: result of integer division used in
+  sin(1 + 2 / 3);
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: result of integer division used in
+  intFunc(sin(1 + 2 / 3));
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: result of integer division used in
+
+  floatArg(1 + intFunc(1 + 2 / 3));
+  floatArg(1 + 3 * intFunc(a / b));
+
+  1 << (2 / 3);
+  1 << intFunc(2 / 3);
+
+#define M_SIN sin(a / b);
+  M_SIN
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: result of integer division used in
+
+  intDivSin(a / b);
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: result of integer division used in
+  intDivSin(c / d);
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: result of integer division used in
+  intDivSin(f / g);
+// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: result of integer division used in
+
+  floatArg(1 / 3);
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: result of integer division used in
+  doubleArg(a / b);
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: result of integer division used in
+  longDoubleArg(3 / d);
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: result of integer division used in
+  floatArg(a / b / 0.1);
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: result of integer division used in
+  doubleArg(1 / 3 / 0.1);
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: result of integer division used in
+  longDoubleArg(2 / 3 / 5);
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: result of integer division used in
+
+  std::sin(2 / 3);
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: result of integer division used in
+  ::acos(7 / d);
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: result of integer division used in
+  tanh(f / g);
+// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: result of integer division used in
+
+  floatArg(0.1 / a / b);
+  doubleArg(0.1 / 3 / 1);
+
+  singleDiv();
+  wrongOrder(a,b);
+  rightOrder(a,b);
+
+  sin(a / b);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of integer division used in
+  acos(f / d);
+// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: result of integer division used in
+  tanh(c / g);
+// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: result of integer division used in
+
+  sin(3.0 / a);
+  acos(b / 3.14);
+  tanh(3.14 / f / g);
+}
Index: docs/clang-tidy/checks/list.rst
===
--- docs/clang-tidy/checks/list.rst
+++ 

[PATCH] D36526: [Sema] Assign new flag -Wenum-compare-switch to switch-related parts of -Wenum-compare

2017-08-09 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.

This patch assigns switch-related parts of the `-Wenum-compare` diagnostic to a 
new flag `-Wenum-compare-switch`.

`-Wenum-compare-switch` is put into the same group as `-Wenum-compare` so that 
`-W(no-)enum-compare` affects both.


https://reviews.llvm.org/D36526

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/Sema/SemaStmt.cpp
  test/Sema/switch.c
  test/SemaCXX/warn-enum-compare.cpp


Index: test/SemaCXX/warn-enum-compare.cpp
===
--- test/SemaCXX/warn-enum-compare.cpp
+++ test/SemaCXX/warn-enum-compare.cpp
@@ -212,19 +212,19 @@
   switch (a) {
 case name1::F1: break;
 case name1::F3: break;
-case name2::B2: break; // expected-warning {{comparison of two values with 
different enumeration types ('name1::Foo' and 'name2::Baz')}}
+case name2::B2: break; // expected-warning {{comparison of two values with 
different enumeration types in switch statement ('name1::Foo' and 
'name2::Baz')}}
   }
 
   switch (x) {
 case FooB: break;
 case FooC: break;
-case BarD: break; // expected-warning {{comparison of two values with 
different enumeration types ('Foo' and 'Bar')}}
+case BarD: break; // expected-warning {{comparison of two values with 
different enumeration types in switch statement ('Foo' and 'Bar')}}
   }
 
   switch(getBar()) {
 case BarE: break;
 case BarF: break;
-case FooA: break; // expected-warning {{comparison of two values with 
different enumeration types ('Bar' and 'Foo')}}
+case FooA: break; // expected-warning {{comparison of two values with 
different enumeration types in switch statement ('Bar' and 'Foo')}}
   }
 
   switch(x) {
Index: test/Sema/switch.c
===
--- test/Sema/switch.c
+++ test/Sema/switch.c
@@ -372,7 +372,7 @@
   case EE1_b: break;
   case EE1_c: break; // no-warning
   case EE1_d: break; // expected-warning {{case value not in enumerated type 
'enum ExtendedEnum1'}}
-  // expected-warning@-1 {{comparison of two values with different enumeration 
types ('enum ExtendedEnum1' and 'const enum ExtendedEnum1_unrelated')}}
+  // expected-warning@-1 {{comparison of two values with different enumeration 
types in switch statement ('enum ExtendedEnum1' and 'const enum 
ExtendedEnum1_unrelated')}}
   }
 }
 
Index: lib/Sema/SemaStmt.cpp
===
--- lib/Sema/SemaStmt.cpp
+++ lib/Sema/SemaStmt.cpp
@@ -762,7 +762,7 @@
   if (S.Context.hasSameUnqualifiedType(CondType, CaseType))
 return;
 
-  S.Diag(Case->getExprLoc(), diag::warn_comparison_of_mixed_enum_types)
+  S.Diag(Case->getExprLoc(), diag::warn_comparison_of_mixed_enum_types_switch)
   << CondType << CaseType << Cond->getSourceRange()
   << Case->getSourceRange();
 }
Index: include/clang/Basic/DiagnosticSemaKinds.td
===
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -5916,7 +5916,11 @@
 def warn_comparison_of_mixed_enum_types : Warning<
   "comparison of two values with different enumeration types"
   "%diff{ ($ and $)|}0,1">,
-  InGroup>;
+  InGroup;
+def warn_comparison_of_mixed_enum_types_switch : Warning<
+  "comparison of two values with different enumeration types in switch 
statement"
+  "%diff{ ($ and $)|}0,1">,
+  InGroup;
 def warn_null_in_arithmetic_operation : Warning<
   "use of NULL in arithmetic operation">,
   InGroup;
Index: include/clang/Basic/DiagnosticGroups.td
===
--- include/clang/Basic/DiagnosticGroups.td
+++ include/clang/Basic/DiagnosticGroups.td
@@ -454,6 +454,8 @@
 def SwitchBool : DiagGroup<"switch-bool">;
 def SwitchEnum : DiagGroup<"switch-enum">;
 def Switch : DiagGroup<"switch">;
+def EnumCompareSwitch : DiagGroup<"enum-compare-switch">;
+def EnumCompare   : DiagGroup<"enum-compare", [EnumCompareSwitch]>;
 def ImplicitFallthroughPerFunction :
   DiagGroup<"implicit-fallthrough-per-function">;
 def ImplicitFallthrough  : DiagGroup<"implicit-fallthrough",


Index: test/SemaCXX/warn-enum-compare.cpp
===
--- test/SemaCXX/warn-enum-compare.cpp
+++ test/SemaCXX/warn-enum-compare.cpp
@@ -212,19 +212,19 @@
   switch (a) {
 case name1::F1: break;
 case name1::F3: break;
-case name2::B2: break; // expected-warning {{comparison of two values with different enumeration types ('name1::Foo' and 'name2::Baz')}}
+case name2::B2: break; // expected-warning {{comparison of two values with different enumeration types in switch statement ('name1::Foo' and 'name2::Baz')}}
   }
 
   switch (x) {
 case FooB: break;
 case FooC: break;
-case BarD: break; // expected-warning {{comparison of 

[PATCH] D36407: [Sema] Extend -Wenum-compare to handle mixed enum comparisons in switch statements

2017-08-08 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 110219.
rnkovacs marked 2 inline comments as done.
rnkovacs added a comment.

Uploaded the full diff and addressed comments. Added `const` qualifiers to 
`GetTypeBeforeIntegralPromotion()` function.


https://reviews.llvm.org/D36407

Files:
  lib/Sema/SemaStmt.cpp
  test/Sema/switch.c
  test/SemaCXX/warn-enum-compare.cpp


Index: test/SemaCXX/warn-enum-compare.cpp
===
--- test/SemaCXX/warn-enum-compare.cpp
+++ test/SemaCXX/warn-enum-compare.cpp
@@ -209,4 +209,21 @@
   while (getBar() > x); // expected-warning  {{comparison of two values with 
different enumeration types ('Bar' and 'Foo')}}
   while (getBar() < x); // expected-warning  {{comparison of two values with 
different enumeration types ('Bar' and 'Foo')}}
 
+  switch (a) {
+case name1::F1: break;
+case name1::F3: break;
+case name2::B2: break; // expected-warning {{comparison of two values with 
different enumeration types ('name1::Foo' and 'name2::Baz')}}
+  }
+
+  switch (x) {
+case FooB: break;
+case FooC: break;
+case BarD: break; // expected-warning {{comparison of two values with 
different enumeration types ('Foo' and 'Bar')}}
+  }
+
+  switch(getBar()) {
+case BarE: break;
+case BarF: break;
+case FooA: break; // expected-warning {{comparison of two values with 
different enumeration types ('Bar' and 'Foo')}}
+  }
 }
Index: test/Sema/switch.c
===
--- test/Sema/switch.c
+++ test/Sema/switch.c
@@ -372,6 +372,7 @@
   case EE1_b: break;
   case EE1_c: break; // no-warning
   case EE1_d: break; // expected-warning {{case value not in enumerated type 
'enum ExtendedEnum1'}}
+  // expected-warning@-1 {{comparison of two values with different enumeration 
types ('enum ExtendedEnum1' and 'const enum ExtendedEnum1_unrelated')}}
   }
 }
 
Index: lib/Sema/SemaStmt.cpp
===
--- lib/Sema/SemaStmt.cpp
+++ lib/Sema/SemaStmt.cpp
@@ -602,10 +602,10 @@
 
 /// GetTypeBeforeIntegralPromotion - Returns the pre-promotion type of
 /// potentially integral-promoted expression @p expr.
-static QualType GetTypeBeforeIntegralPromotion(Expr *) {
-  if (ExprWithCleanups *cleanups = dyn_cast(expr))
+static QualType GetTypeBeforeIntegralPromotion(const Expr *) {
+  if (const auto *cleanups = dyn_cast(expr))
 expr = cleanups->getSubExpr();
-  while (ImplicitCastExpr *impcast = dyn_cast(expr)) {
+  while (const auto *impcast = dyn_cast(expr)) {
 if (impcast->getCastKind() != CK_IntegralCast) break;
 expr = impcast->getSubExpr();
   }
@@ -743,6 +743,24 @@
   return true;
 }
 
+static void checkEnumTypesInSwitchStmt(Sema , const Expr *Cond,
+   const Expr *Case) {
+  QualType CondType = GetTypeBeforeIntegralPromotion(Cond);
+  QualType CaseType = Case->getType();
+
+  const EnumType *CondEnumType = CondType->getAs();
+  const EnumType *CaseEnumType = CaseType->getAs();
+  if (!CondEnumType || !CaseEnumType)
+return;
+
+  if (S.Context.hasSameUnqualifiedType(CondType, CaseType))
+return;
+
+  S.Diag(Case->getExprLoc(), diag::warn_comparison_of_mixed_enum_types)
+  << CondType << CaseType << Cond->getSourceRange()
+  << Case->getSourceRange();
+}
+
 StmtResult
 Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
 Stmt *BodyStmt) {
@@ -760,7 +778,7 @@
 
   QualType CondType = CondExpr->getType();
 
-  Expr *CondExprBeforePromotion = CondExpr;
+  const Expr *CondExprBeforePromotion = CondExpr;
   QualType CondTypeBeforePromotion =
   GetTypeBeforeIntegralPromotion(CondExprBeforePromotion);
 
@@ -843,6 +861,8 @@
 break;
   }
 
+  checkEnumTypesInSwitchStmt(*this, CondExpr, Lo);
+
   llvm::APSInt LoVal;
 
   if (getLangOpts().CPlusPlus11) {


Index: test/SemaCXX/warn-enum-compare.cpp
===
--- test/SemaCXX/warn-enum-compare.cpp
+++ test/SemaCXX/warn-enum-compare.cpp
@@ -209,4 +209,21 @@
   while (getBar() > x); // expected-warning  {{comparison of two values with different enumeration types ('Bar' and 'Foo')}}
   while (getBar() < x); // expected-warning  {{comparison of two values with different enumeration types ('Bar' and 'Foo')}}
 
+  switch (a) {
+case name1::F1: break;
+case name1::F3: break;
+case name2::B2: break; // expected-warning {{comparison of two values with different enumeration types ('name1::Foo' and 'name2::Baz')}}
+  }
+
+  switch (x) {
+case FooB: break;
+case FooC: break;
+case BarD: break; // expected-warning {{comparison of two values with different enumeration types ('Foo' and 'Bar')}}
+  }
+
+  switch(getBar()) {
+case BarE: break;
+case BarF: break;
+case FooA: break; // expected-warning {{comparison of two values with different enumeration types ('Bar' and 

[PATCH] D36407: [Sema] Extend -Wenum-compare to handle mixed enum comparisons in switch statements

2017-08-07 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs created this revision.

`-Wenum-compare` warns if two values with different enumeration types are 
compared in expressions with binary operators. This patch extends this 
diagnostic so that comparisons of mixed enumeration types are recognized in 
switch statements as well.

Example:

  enum MixedA { A1, A2, A3 };
  enum MixedB { B1, B2, B3 };
  
  void MixedEnums(MixedA a) {
switch (a) {
  case A1: break; // OK, same enum types.
  case B1: break; // Warn, different enum types.
}
  }


https://reviews.llvm.org/D36407

Files:
  lib/Sema/SemaStmt.cpp
  test/Sema/switch.c
  test/SemaCXX/warn-enum-compare.cpp


Index: test/SemaCXX/warn-enum-compare.cpp
===
--- test/SemaCXX/warn-enum-compare.cpp
+++ test/SemaCXX/warn-enum-compare.cpp
@@ -209,4 +209,21 @@
   while (getBar() > x); // expected-warning  {{comparison of two values with 
different enumeration types ('Bar' and 'Foo')}}
   while (getBar() < x); // expected-warning  {{comparison of two values with 
different enumeration types ('Bar' and 'Foo')}}
 
+  switch (a) {
+case name1::F1: break;
+case name1::F3: break;
+case name2::B2: break; // expected-warning {{comparison of two values with 
different enumeration types ('name1::Foo' and 'name2::Baz')}}
+  }
+
+  switch (x) {
+case FooB: break;
+case FooC: break;
+case BarD: break; // expected-warning {{comparison of two values with 
different enumeration types ('Foo' and 'Bar')}}
+  }
+
+  switch(getBar()) {
+case BarE: break;
+case BarF: break;
+case FooA: break; // expected-warning {{comparison of two values with 
different enumeration types ('Bar' and 'Foo')}}
+  }
 }
Index: test/Sema/switch.c
===
--- test/Sema/switch.c
+++ test/Sema/switch.c
@@ -372,6 +372,7 @@
   case EE1_b: break;
   case EE1_c: break; // no-warning
   case EE1_d: break; // expected-warning {{case value not in enumerated type 
'enum ExtendedEnum1'}}
+  // expected-warning@-1 {{comparison of two values with different enumeration 
types ('enum ExtendedEnum1' and 'const enum ExtendedEnum1_unrelated')}}
   }
 }
 
Index: lib/Sema/SemaStmt.cpp
===
--- lib/Sema/SemaStmt.cpp
+++ lib/Sema/SemaStmt.cpp
@@ -743,6 +743,24 @@
   return true;
 }
 
+static void checkEnumTypesInSwitchStmt(Sema , Expr *Cond, Expr *Case) {
+  QualType CondType = GetTypeBeforeIntegralPromotion(Cond);
+  QualType CaseType = Case->getType();
+
+  const EnumType *CondEnumType = CondType->getAs();
+  const EnumType *CaseEnumType = CaseType->getAs();
+  if (!CondEnumType || !CaseEnumType)
+return;
+
+  if (S.Context.hasSameUnqualifiedType(CondType, CaseType))
+return;
+
+  SourceLocation Loc = Case->getExprLoc();
+  S.Diag(Loc, diag::warn_comparison_of_mixed_enum_types)
+  << CondType << CaseType << Cond->getSourceRange()
+  << Case->getSourceRange();
+}
+
 StmtResult
 Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
 Stmt *BodyStmt) {
@@ -843,6 +861,8 @@
 break;
   }
 
+  checkEnumTypesInSwitchStmt(*this, CondExpr, Lo);
+
   llvm::APSInt LoVal;
 
   if (getLangOpts().CPlusPlus11) {


Index: test/SemaCXX/warn-enum-compare.cpp
===
--- test/SemaCXX/warn-enum-compare.cpp
+++ test/SemaCXX/warn-enum-compare.cpp
@@ -209,4 +209,21 @@
   while (getBar() > x); // expected-warning  {{comparison of two values with different enumeration types ('Bar' and 'Foo')}}
   while (getBar() < x); // expected-warning  {{comparison of two values with different enumeration types ('Bar' and 'Foo')}}
 
+  switch (a) {
+case name1::F1: break;
+case name1::F3: break;
+case name2::B2: break; // expected-warning {{comparison of two values with different enumeration types ('name1::Foo' and 'name2::Baz')}}
+  }
+
+  switch (x) {
+case FooB: break;
+case FooC: break;
+case BarD: break; // expected-warning {{comparison of two values with different enumeration types ('Foo' and 'Bar')}}
+  }
+
+  switch(getBar()) {
+case BarE: break;
+case BarF: break;
+case FooA: break; // expected-warning {{comparison of two values with different enumeration types ('Bar' and 'Foo')}}
+  }
 }
Index: test/Sema/switch.c
===
--- test/Sema/switch.c
+++ test/Sema/switch.c
@@ -372,6 +372,7 @@
   case EE1_b: break;
   case EE1_c: break; // no-warning
   case EE1_d: break; // expected-warning {{case value not in enumerated type 'enum ExtendedEnum1'}}
+  // expected-warning@-1 {{comparison of two values with different enumeration types ('enum ExtendedEnum1' and 'const enum ExtendedEnum1_unrelated')}}
   }
 }
 
Index: lib/Sema/SemaStmt.cpp
===
--- lib/Sema/SemaStmt.cpp
+++ 

[PATCH] D35932: [clang-tidy] Add integer division check

2017-08-03 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs updated this revision to Diff 109497.
rnkovacs edited the summary of this revision.
rnkovacs added a comment.

Uploaded a more thought-out version of the check with more cases covered and 
hopefully clearer docs. It produces no hits on LLVM


https://reviews.llvm.org/D35932

Files:
  clang-tidy/bugprone/BugproneTidyModule.cpp
  clang-tidy/bugprone/CMakeLists.txt
  clang-tidy/bugprone/IntegerDivisionCheck.cpp
  clang-tidy/bugprone/IntegerDivisionCheck.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/bugprone-integer-division.rst
  docs/clang-tidy/checks/list.rst
  test/clang-tidy/bugprone-integer-division.cpp

Index: test/clang-tidy/bugprone-integer-division.cpp
===
--- /dev/null
+++ test/clang-tidy/bugprone-integer-division.cpp
@@ -0,0 +1,150 @@
+// RUN: %check_clang_tidy %s bugprone-integer-division %t
+
+void floatArg(float x) {}
+void doubleArg(double x) {}
+void longDoubleArg(long double x) {}
+
+float floatReturn(unsigned x, int y, bool z) {
+  return (x * y) / z;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: integer division; possible precision loss [bugprone-integer-division]
+}
+
+double doubleReturn(int x, char y) {
+  return x / y - 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: integer division; possible precision loss [bugprone-integer-division]
+}
+
+long double longDoubleReturn(char x, unsigned y) {
+  return (1 + x / y) + 3;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: integer division; possible precision loss [bugprone-integer-division]
+}
+
+struct X {
+  int n;
+  void m() {
+floatArg(n / 3);
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: integer division; possible precision loss [bugprone-integer-division]
+  }
+};
+
+struct Y {
+  void f(){
+auto l = [] { floatArg(2 / 3); };
+// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: integer division; possible precision loss [bugprone-integer-division]
+  }
+};
+
+template 
+void arbitraryArg(T x) {
+  longDoubleArg(x);
+}
+
+void floatEnvironment() {
+  char a = 2;
+  int b = -5;
+  unsigned c = 9784;
+  enum third { x, y, z=2 };
+  third d = z;
+  char e[] = {'a', 'b', 'c'};
+  char f = *(e + 1 / a);
+  bool g = 1;
+
+  // Implicit cast to float: function argument.
+  floatArg(a / g - 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: integer division; possible precision loss [bugprone-integer-division]
+  doubleArg(2 + b / f);
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: integer division; possible precision loss [bugprone-integer-division]
+  longDoubleArg(c + (e[0] / d));
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: integer division; possible precision loss [bugprone-integer-division]
+
+  // Implicit cast to float: function return value.
+  long double q;
+  q = floatReturn(c, b, g);
+  q = doubleReturn(d, a);
+  q = longDoubleReturn(f, c);
+
+  // Explicit casts.
+  q = (float)(a / b + 2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: integer division; possible precision loss [bugprone-integer-division]
+  q = double((1 - c) / d);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: integer division; possible precision loss [bugprone-integer-division]
+  q = static_cast(2 + e[1] / g);
+  // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: integer division; possible precision loss [bugprone-integer-division]
+
+#define THIRD float(1 / 3);
+  THIRD
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: integer division; possible precision loss [bugprone-integer-division]
+
+  arbitraryArg(a / b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: integer division; possible precision loss [bugprone-integer-division]
+}
+
+int intArg(int x) { return x; }
+char charArg(char x) { return x; }
+bool boolArg(bool x) { return x; }
+
+int intReturn(int x, int y) {
+  return (x - 5) / y + 1;
+}
+
+char charReturn(int x, unsigned y) {
+  return 1 - x / (y * 4);
+}
+
+bool boolReturn(int x, char y) {
+  return (x / y + 1) * 5;
+}
+
+void integerCastInFloatEnvironment() {
+  unsigned k = 87;
+  int l = -7;
+  char m = 'q';
+  bool n = 0;
+
+  // We can assume that the following cases are intended.
+  // Function calls expecting integers:
+  floatArg(0.3 + intArg(6 * k / m));
+  doubleArg(charArg(n / l) * 9);
+  longDoubleArg(3 - boolArg(m / (k - 2)));
+
+  // Function calls returning integers:
+  double o;
+  o = intReturn(-2, 99);
+  o = charReturn(1, 7);
+  o = boolReturn(42, 'c');
+
+  // Explicit casts:
+  floatArg(0.3 + int(6 * k / m));
+  doubleArg((int)(n / l) * 9);
+  longDoubleArg(3 - static_cast(m / (k - 2)));
+
+  // Operators expecting integral types:
+  o = 1 << (2 / m);
+  o = 1 << intArg(4 + k / 64);
+  o = ~(k / 8 + 3);
+  o = (32 - k / 8) ^ 1;
+  o = ((k / 8 + 1) * 32) | 1;
+  o = (1 & (k / 8)) - 2;
+  o = ((k - 8) / 32) % m;
+
+  // Relational, logical, and conditional operators:
+  o = k / m <= 0;
+  o = (k * l - 5) / m != n;
+  o = !(l / m * 8 - 1);
+  o = n / m || l == -7;
+  o = n / m ? 1 : 0;
+  o = n / m ?: 1;
+
+  // Precision loss can still be 

[PATCH] D35932: [clang-tidy] Add integer division check

2017-07-28 Thread Reka Kovacs via Phabricator via cfe-commits
rnkovacs added a comment.

I run the check on LLVM-Clang, and got this one hit:

  
/home/reka/codechecker_dev_env/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp:1000:43:
 warning: integer division; possible precision loss [bugprone-integer-division]
SDValue TWOHW = DAG.getConstantFP(1 << (BW / 2), DL, Op.getValueType());
^

It warns because `getConstantFP()` expects a floating-point value as its first 
argument. Still seems to be a false positive. I might want to add an exception 
for bitshift operators.




Comment at: docs/clang-tidy/checks/bugprone-integer-division.rst:21-24
+  // Do not warn, there are signs of deliberateness.
+  sin(abs(3) / 5);
+  sin(1 + abs(1 + 7 / 2));
+  1 << 2 / 3;

alexfh wrote:
> I'd argue that the presence of `abs` here doesn't add any signal, since the 
> function has overloads both for integer and floating point types.
Well, I meant specifically the above declared function, but you're right, the 
naming was misleading, so I changed that.

To be honest, this check doesn't work for most of the std math functions 
involving template magic. Custom user-defined functions could be mainly 
targeted.


https://reviews.llvm.org/D35932



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


  1   2   >