[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:
parent
218da62091
commit
8707cd1d1b
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue