From: Reka Kovacs Date: Sat, 7 Jul 2018 17:22:45 +0000 (+0000) Subject: [analyzer] Highlight container object destruction in MallocChecker. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9f82dc375b2501b5b21a8a741fccea649d897495;p=clang [analyzer] Highlight container object destruction in MallocChecker. Extend MallocBugVisitor to place a note at the point where objects with AF_InternalBuffer allocation family are destroyed. Differential Revision: https://reviews.llvm.org/D48521 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@336489 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 1fa2e3b53e..c8c7a5a573 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -480,8 +480,13 @@ private: 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, @@ -2850,8 +2855,17 @@ static bool isReferenceCountingPointerDestructor(const CXXDestructorDecl *DD) { std::shared_ptr MallocChecker::MallocBugVisitor::VisitNode( const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { + + 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(); @@ -2876,14 +2890,6 @@ std::shared_ptr MallocChecker::MallocBugVisitor::VisitNode( } } - 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,7 +2902,22 @@ std::shared_ptr MallocChecker::MallocBugVisitor::VisitNode( 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"); @@ -2967,8 +2988,19 @@ std::shared_ptr MallocChecker::MallocBugVisitor::VisitNode( assert(StackHint); // Generate the extra diagnostic. - PathDiagnosticLocation Pos(S, BRC.getSourceManager(), - N->getLocationContext()); + PathDiagnosticLocation Pos; + if (!S) { + assert(RS->getAllocationFamily() == AF_InternalBuffer); + auto PostImplCall = N->getLocation().getAs(); + if (!PostImplCall) + return nullptr; + Pos = PathDiagnosticLocation(PostImplCall->getLocation(), + BRC.getSourceManager()); + } else { + Pos = PathDiagnosticLocation(S, BRC.getSourceManager(), + N->getLocationContext()); + } + return std::make_shared(Pos, Msg, true, StackHint); } diff --git a/test/Analysis/dangling-internal-buffer.cpp b/test/Analysis/dangling-internal-buffer.cpp index 791bb96047..cd7ebb17c6 100644 --- a/test/Analysis/dangling-internal-buffer.cpp +++ b/test/Analysis/dangling-internal-buffer.cpp @@ -26,7 +26,7 @@ void deref_after_scope_char() { { 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 @@ void deref_after_scope_wchar_t() { { 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 @@ void deref_after_scope_char16_t() { { 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 @@ void deref_after_scope_char32_t() { { 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}} }