[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

llvm-svn: 336489
This commit is contained in:
Reka Kovacs 2018-07-07 17:22:45 +00:00
parent 218da62091
commit 8707cd1d1b
2 changed files with 50 additions and 18 deletions

View File

@ -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<CallExpr>(Stmt) || isa<CXXDeleteExpr>(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<CallExpr>(Stmt) || isa<CXXDeleteExpr>(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<PathDiagnosticPiece> 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<RegionState>(Sym);
const RefState *RSPrev = statePrev->get<RegionState>(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<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(
}
}
ProgramStateRef state = N->getState();
ProgramStateRef statePrev = PrevN->getState();
const RefState *RS = state->get<RegionState>(Sym);
const RefState *RSPrev = statePrev->get<RegionState>(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<PathDiagnosticPiece> 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<PathDiagnosticPiece> 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<PostImplicitCall>();
if (!PostImplCall)
return nullptr;
Pos = PathDiagnosticLocation(PostImplCall->getLocation(),
BRC.getSourceManager());
} else {
Pos = PathDiagnosticLocation(S, BRC.getSourceManager(),
N->getLocationContext());
}
return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true, StackHint);
}

View File

@ -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}}
}