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