r337463 - [analyzer] Add support for more basic_string API in

Reka Kovacs via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 19 08:10:06 PDT 2018


Author: rkovacs
Date: Thu Jul 19 08:10:06 2018
New Revision: 337463

URL: http://llvm.org/viewvc/llvm-project?rev=337463&view=rev
Log:
[analyzer] Add support for more basic_string API in
DanglingInternalBufferChecker.

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.

Differential Revision: https://reviews.llvm.org/D49360

Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
    cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
    cfe/trunk/test/Analysis/dangling-internal-buffer.cpp

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp?rev=337463&r1=337462&r2=337463&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp Thu Jul 19 08:10:06 2018
@@ -32,8 +32,8 @@ REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap
 // This is a trick to gain access to PtrSet's Factory.
 namespace clang {
 namespace ento {
-template<> struct ProgramStateTrait<PtrSet>
-  : public ProgramStatePartialTrait<PtrSet> {
+template <>
+struct ProgramStateTrait<PtrSet> : public ProgramStatePartialTrait<PtrSet> {
   static void *GDMIndex() {
     static int Index = 0;
     return &Index;
@@ -46,7 +46,10 @@ namespace {
 
 class DanglingInternalBufferChecker
     : public Checker<check::DeadSymbols, check::PostCall> {
-  CallDescription CStrFn, DataFn;
+
+  CallDescription AppendFn, AssignFn, ClearFn, CStrFn, DataFn, EraseFn,
+      InsertFn, PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn,
+      ShrinkToFitFn, SwapFn;
 
 public:
   class DanglingBufferBRVisitor : public BugReporterVisitor {
@@ -81,7 +84,17 @@ public:
     }
   };
 
-  DanglingInternalBufferChecker() : CStrFn("c_str"), DataFn("data") {}
+  DanglingInternalBufferChecker()
+      : AppendFn("append"), AssignFn("assign"), ClearFn("clear"),
+        CStrFn("c_str"), DataFn("data"), EraseFn("erase"), InsertFn("insert"),
+        PopBackFn("pop_back"), PushBackFn("push_back"), ReplaceFn("replace"),
+        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 &Call) const;
 
   /// Record the connection between the symbol returned by c_str() and the
   /// corresponding string object region in the ProgramState. Mark the symbol
@@ -94,6 +107,37 @@ public:
 
 } // 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 argument. For example, as an argument to
+// non-member functions swap(), operator>>(), and getline(), or as an argument
+// to basic_string::swap().
+//
+// -- Calling non-const member functions, except operator[], at, front, back,
+// begin, rbegin, end, and rend."
+//
+bool DanglingInternalBufferChecker::mayInvalidateBuffer(
+    const CallEvent &Call) const {
+  if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) {
+    OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator();
+    if (Opc == OO_Equal || Opc == OO_PlusEqual)
+      return true;
+    return false;
+  }
+  return (isa<CXXDestructorCall>(Call) || Call.isCalled(AppendFn) ||
+          Call.isCalled(AssignFn) || Call.isCalled(ClearFn) ||
+          Call.isCalled(EraseFn) || Call.isCalled(InsertFn) ||
+          Call.isCalled(PopBackFn) || Call.isCalled(PushBackFn) ||
+          Call.isCalled(ReplaceFn) || Call.isCalled(ReserveFn) ||
+          Call.isCalled(ResizeFn) || Call.isCalled(ShrinkToFitFn) ||
+          Call.isCalled(SwapFn));
+}
+
 void DanglingInternalBufferChecker::checkPostCall(const CallEvent &Call,
                                                   CheckerContext &C) const {
   const auto *ICall = dyn_cast<CXXInstanceCall>(&Call);
@@ -127,7 +171,7 @@ void DanglingInternalBufferChecker::chec
     return;
   }
 
-  if (isa<CXXDestructorCall>(ICall)) {
+  if (mayInvalidateBuffer(Call)) {
     if (const PtrSet *PS = State->get<RawPtrMap>(ObjRegion)) {
       // Mark all pointer symbols associated with the deleted object released.
       const Expr *Origin = Call.getOriginExpr();
@@ -161,8 +205,8 @@ void DanglingInternalBufferChecker::chec
           CleanedUpSet = F.remove(CleanedUpSet, Symbol);
       }
       State = CleanedUpSet.isEmpty()
-              ? State->remove<RawPtrMap>(Entry.first)
-              : State->set<RawPtrMap>(Entry.first, CleanedUpSet);
+                  ? State->remove<RawPtrMap>(Entry.first)
+                  : State->set<RawPtrMap>(Entry.first, CleanedUpSet);
     }
   }
   C.addTransition(State);
@@ -183,7 +227,7 @@ DanglingInternalBufferChecker::DanglingB
 
   SmallString<256> Buf;
   llvm::raw_svector_ostream OS(Buf);
-  OS << "Pointer to dangling buffer was obtained here";
+  OS << "Dangling inner pointer obtained here";
   PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
                              N->getLocationContext());
   return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true,

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp?rev=337463&r1=337462&r2=337463&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Thu Jul 19 08:10:06 2018
@@ -2908,7 +2908,7 @@ std::shared_ptr<PathDiagnosticPiece> Mal
                                                   "Returned allocated memory");
     } else if (isReleased(RS, RSPrev, S)) {
       const auto Family = RS->getAllocationFamily();
-      switch(Family) {
+      switch (Family) {
         case AF_Alloca:
         case AF_Malloc:
         case AF_CXXNew:
@@ -2916,9 +2916,25 @@ std::shared_ptr<PathDiagnosticPiece> Mal
         case AF_IfNameIndex:
           Msg = "Memory is released";
           break;
-        case AF_InternalBuffer:
-          Msg = "Internal buffer is released because the object was destroyed";
+        case AF_InternalBuffer: {
+          SmallString<256> Buf;
+          llvm::raw_svector_ostream OS(Buf);
+          OS << "Inner pointer invalidated by call to ";
+          if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) {
+            OS << "destructor";
+          } else {
+            OS << "'";
+            const Stmt *S = RS->getStmt();
+            if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) {
+              OS << MemCallE->getMethodDecl()->getNameAsString();
+            } else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) {
+              OS << OpCallE->getDirectCallee()->getNameAsString();
+            }
+            OS << "'";
+          }
+          Msg = OS.str().data();
           break;
+        }
         case AF_None:
           llvm_unreachable("Unhandled allocation family!");
       }

Modified: cfe/trunk/test/Analysis/dangling-internal-buffer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/dangling-internal-buffer.cpp?rev=337463&r1=337462&r2=337463&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/dangling-internal-buffer.cpp (original)
+++ cfe/trunk/test/Analysis/dangling-internal-buffer.cpp Thu Jul 19 08:10:06 2018
@@ -2,13 +2,35 @@
 
 namespace std {
 
-template< typename CharT >
+typedef int size_type;
+
+template <typename CharT>
 class basic_string {
 public:
+  basic_string();
+  basic_string(const CharT *s);
+
   ~basic_string();
+  void clear();
+
+  basic_string &operator=(const basic_string &str);
+  basic_string &operator+=(const basic_string &str);
+
   const CharT *c_str() const;
   const CharT *data() const;
   CharT *data();
+
+  basic_string &append(size_type count, CharT ch);
+  basic_string &assign(size_type count, CharT ch);
+  basic_string &erase(size_type index, size_type count);
+  basic_string &insert(size_type index, size_type count, CharT ch);
+  basic_string &replace(size_type pos, size_type count, const basic_string &str);
+  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 &other);
 };
 
 typedef basic_string<char> string;
@@ -23,73 +45,70 @@ void consume(const wchar_t *) {}
 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 at -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 at -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 at -1 {{Use of memory after it is freed}}
+  if (cond) {
+    // expected-note at -1 {{Assuming 'cond' is not equal to 0}}
+    // expected-note at -2 {{Taking true branch}}
+    // expected-note at -3 {{Assuming 'cond' is 0}}
+    // expected-note at -4 {{Taking false branch}}
+    consume(c); // expected-warning {{Use of memory after it is freed}}
+    // expected-note at -1 {{Use of memory after it is freed}}
+  } else {
+    consume(d); // expected-warning {{Use of memory after it is freed}}
+    // expected-note at -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 at -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 at -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 at -1 {{Use of memory after it is freed}}
+    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 at -1 {{Inner pointer invalidated by call to destructor}}
+  std::wstring s;
+  const wchar_t *c2 = s.c_str();
+  if (cond) {
+    // expected-note at -1 {{Assuming 'cond' is not equal to 0}}
+    // expected-note at -2 {{Taking true branch}}
+    // expected-note at -3 {{Assuming 'cond' is 0}}
+    // expected-note at -4 {{Taking false branch}}
+    consume(c); // expected-warning {{Use of memory after it is freed}}
+    // expected-note at -1 {{Use of memory after it is freed}}
+  } else {
+    consume(d); // expected-warning {{Use of memory after it is freed}}
+    // expected-note at -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}}
+    c16 = s16.c_str(); // expected-note {{Dangling inner pointer obtained here}}
+  }                    // expected-note {{Inner pointer invalidated by call to destructor}}
   std::u16string s16;
   const char16_t *c16_2 = s16.c_str();
   consume(c16); // expected-warning {{Use of memory after it is freed}}
@@ -100,8 +119,8 @@ void deref_after_scope_char32_t_data() {
   const char32_t *c32;
   {
     std::u32string s32;
-    c32 = s32.data(); // expected-note {{Pointer to dangling buffer was obtained here}}
-  } // expected-note {{Internal buffer is released because the object was destroyed}}
+    c32 = s32.data(); // expected-note {{Dangling inner pointer obtained here}}
+  }                   // expected-note {{Inner pointer invalidated by call to destructor}}
   std::u32string s32;
   const char32_t *c32_2 = s32.data();
   consume(c32); // expected-warning {{Use of memory after it is freed}}
@@ -112,12 +131,12 @@ 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}}
+    c1 = s1.c_str(); // expected-note {{Dangling inner pointer obtained here}}
+    d1 = s1.data();  // expected-note {{Dangling inner pointer 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 at -1 {{Internal buffer is released because the object was destroyed}}
+  }                 // expected-note {{Inner pointer invalidated by call to destructor}}
+  // expected-note at -1 {{Inner pointer invalidated by call to destructor}}
   std::string s2;
   const char *c2 = s2.c_str();
   if (cond) {
@@ -129,23 +148,144 @@ void multiple_symbols(bool cond) {
     // expected-note at -1 {{Use of memory after it is freed}}
   } else {
     consume(d1); // expected-warning {{Use of memory after it is freed}}
-  } // expected-note at -1 {{Use of memory after it is freed}}
+  }              // expected-note at -1 {{Use of memory after it is freed}}
 }
 
-void deref_after_scope_cstr_ok() {
+void deref_after_equals() {
+  const char *c;
+  std::string s = "hello";
+  c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
+  s = "world";   // expected-note {{Inner pointer invalidated by call to 'operator='}}
+  consume(c);    // expected-warning {{Use of memory after it is freed}}
+  // expected-note at -1 {{Use of memory after it is freed}}
+}
+
+void deref_after_plus_equals() {
+  const char *c;
+  std::string s = "hello";
+  c = s.data();  // expected-note {{Dangling inner pointer obtained here}}
+  s += " world"; // expected-note {{Inner pointer invalidated by call to 'operator+='}}
+  consume(c);    // expected-warning {{Use of memory after it is freed}}
+  // expected-note at -1 {{Use of memory after it is freed}}
+}
+
+void deref_after_clear() {
   const char *c;
   std::string s;
-  {
-    c = s.c_str();
-  }
-  consume(c); // no-warning
+  c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
+  s.clear();     // expected-note {{Inner pointer invalidated by call to 'clear'}}
+  consume(c);    // expected-warning {{Use of memory after it is freed}}
+  // expected-note at -1 {{Use of memory after it is freed}}
 }
 
-void deref_after_scope_data_ok() {
+void deref_after_append() {
+  const char *c;
+  std::string s = "hello";
+  c = s.c_str();    // expected-note {{Dangling inner pointer obtained here}}
+  s.append(2, 'x'); // expected-note {{Inner pointer invalidated by call to 'append'}}
+  consume(c);       // expected-warning {{Use of memory after it is freed}}
+  // expected-note at -1 {{Use of memory after it is freed}}
+}
+
+void deref_after_assign() {
+  const char *c;
+  std::string s;
+  c = s.data();     // expected-note {{Dangling inner pointer obtained here}}
+  s.assign(4, 'a'); // expected-note {{Inner pointer invalidated by call to 'assign'}}
+  consume(c);       // expected-warning {{Use of memory after it is freed}}
+  // expected-note at -1 {{Use of memory after it is freed}}
+}
+
+void deref_after_erase() {
+  const char *c;
+  std::string s = "hello";
+  c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
+  s.erase(0, 2); // expected-note {{Inner pointer invalidated by call to 'erase'}}
+  consume(c);    // expected-warning {{Use of memory after it is freed}}
+  // expected-note at -1 {{Use of memory after it is freed}}
+}
+
+void deref_after_insert() {
+  const char *c;
+  std::string s = "ello";
+  c = s.c_str();       // expected-note {{Dangling inner pointer obtained here}}
+  s.insert(0, 1, 'h'); // expected-note {{Inner pointer invalidated by call to 'insert'}}
+  consume(c);          // expected-warning {{Use of memory after it is freed}}
+  // expected-note at -1 {{Use of memory after it is freed}}
+}
+
+void deref_after_replace() {
+  const char *c;
+  std::string s = "hello world";
+  c = s.c_str();             // expected-note {{Dangling inner pointer obtained here}}
+  s.replace(6, 5, "string"); // expected-note {{Inner pointer invalidated by call to 'replace'}}
+  consume(c);                // expected-warning {{Use of memory after it is freed}}
+  // expected-note at -1 {{Use of memory after it is freed}}
+}
+
+void deref_after_pop_back() {
+  const char *c;
+  std::string s;
+  c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
+  s.pop_back();  // expected-note {{Inner pointer invalidated by call to 'pop_back'}}
+  consume(c);    // expected-warning {{Use of memory after it is freed}}
+  // expected-note at -1 {{Use of memory after it is freed}}
+}
+
+void deref_after_push_back() {
   const char *c;
   std::string s;
+  c = s.data();     // expected-note {{Dangling inner pointer obtained here}}
+  s.push_back('c'); // expected-note {{Inner pointer invalidated by call to 'push_back'}}
+  consume(c);       // expected-warning {{Use of memory after it is freed}}
+  // expected-note at -1 {{Use of memory after it is freed}}
+}
+
+void deref_after_reserve() {
+  const char *c;
+  std::string s;
+  c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}}
+  s.reserve(5);  // expected-note {{Inner pointer invalidated by call to 'reserve'}}
+  consume(c);    // expected-warning {{Use of memory after it is freed}}
+  // expected-note at -1 {{Use of memory after it is freed}}
+}
+
+void deref_after_resize() {
+  const char *c;
+  std::string s;
+  c = s.data(); // expected-note {{Dangling inner pointer obtained here}}
+  s.resize(5);  // expected-note {{Inner pointer invalidated by call to 'resize'}}
+  consume(c);   // expected-warning {{Use of memory after it is freed}}
+  // expected-note at -1 {{Use of memory after it is freed}}
+}
+
+void deref_after_shrink_to_fit() {
+  const char *c;
+  std::string s;
+  c = s.data();      // expected-note {{Dangling inner pointer obtained here}}
+  s.shrink_to_fit(); // expected-note {{Inner pointer invalidated by call to 'shrink_to_fit'}}
+  consume(c);        // expected-warning {{Use of memory after it is freed}}
+  // expected-note at -1 {{Use of memory after it is freed}}
+}
+
+void deref_after_swap() {
+  const char *c;
+  std::string s1, s2;
+  c = s1.data(); // expected-note {{Dangling inner pointer obtained here}}
+  s1.swap(s2);   // expected-note {{Inner pointer invalidated by call to 'swap'}}
+  consume(c);    // expected-warning {{Use of memory after it is freed}}
+  // expected-note at -1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_ok(bool cond) {
+  const char *c, *d;
+  std::string s;
   {
-    c = s.data();
+    c = s.c_str();
+    d = s.data();
   }
-  consume(c); // no-warning
+  if (cond)
+    consume(c); // no-warning
+  else
+    consume(d); // no-warning
 }




More information about the cfe-commits mailing list