[analyzer] Add path notes to FuchsiaHandleCheck.

Differential Revision: https://reviews.llvm.org/D70725
This commit is contained in:
Gabor Horvath 2019-11-26 09:17:30 -08:00
parent 226a014044
commit 59878ec809
3 changed files with 126 additions and 12 deletions

View File

@ -386,7 +386,7 @@ public:
/// to the user. This method allows to rest the location which should be used
/// for uniquing reports. For example, memory leaks checker, could set this to
/// the allocation site, rather then the location where the bug is reported.
PathSensitiveBugReport(BugType &bt, StringRef desc,
PathSensitiveBugReport(const BugType &bt, StringRef desc,
const ExplodedNode *errorNode,
PathDiagnosticLocation LocationToUnique,
const Decl *DeclToUnique)

View File

@ -199,6 +199,28 @@ public:
REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState)
static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym,
CheckerContext &Ctx) {
ProgramStateRef State = N->getState();
// When bug type is handle leak, exploded node N does not have state info for
// leaking handle. Get the predecessor of N instead.
if (!State->get<HStateMap>(Sym))
N = N->getFirstPred();
const ExplodedNode *Pred = N;
while (N) {
State = N->getState();
if (!State->get<HStateMap>(Sym)) {
const HandleState *HState = Pred->getState()->get<HStateMap>(Sym);
if (HState && (HState->isAllocated() || HState->maybeAllocated()))
return N;
}
Pred = N;
N = N->getFirstPred();
}
return nullptr;
}
/// Returns the symbols extracted from the argument or null if it cannot be
/// found.
SymbolRef getFuchsiaHandleSymbol(QualType QT, SVal Arg, ProgramStateRef State) {
@ -282,6 +304,7 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
ProgramStateRef State = C.getState();
std::vector<std::function<std::string(BugReport & BR)>> Notes;
SymbolRef ResultSymbol = nullptr;
if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>())
if (TypeDefTy->getDecl()->getName() == ErrorTypeName)
@ -310,14 +333,45 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
if (HState && HState->isReleased()) {
reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C);
return;
} else {
Notes.push_back([Handle](BugReport &BR) {
auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
return "Handle released here.";
} else
return "";
});
State = State->set<HStateMap>(Handle, HandleState::getReleased());
}
} else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {
Notes.push_back([Handle](BugReport &BR) {
auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
return "Handle allocated here.";
} else
return "";
});
State = State->set<HStateMap>(
Handle, HandleState::getMaybeAllocated(ResultSymbol));
}
}
C.addTransition(State);
const NoteTag *T = nullptr;
if (!Notes.empty()) {
T = C.getNoteTag(
[this, Notes{std::move(Notes)}](BugReport &BR) -> std::string {
if (&BR.getBugType() != &UseAfterReleaseBugType &&
&BR.getBugType() != &LeakBugType &&
&BR.getBugType() != &DoubleReleaseBugType)
return "";
for (auto &Note : Notes) {
std::string Text = Note(BR);
if (!Text.empty())
return Text;
}
return "";
});
}
C.addTransition(State, T);
}
void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,
@ -353,6 +407,7 @@ void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,
ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State,
SVal Cond,
bool Assumption) const {
// TODO: add notes about successes/fails for APIs.
ConstraintManager &Cmr = State->getConstraintManager();
HStateMapTy TrackedHandles = State->get<HStateMap>();
for (auto &CurItem : TrackedHandles) {
@ -453,7 +508,22 @@ void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode,
if (!ErrorNode)
return;
auto R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode);
std::unique_ptr<PathSensitiveBugReport> R;
if (Type.isSuppressOnSink()) {
const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C);
if (AcquireNode) {
PathDiagnosticLocation LocUsedForUniqueing =
PathDiagnosticLocation::createBegin(
AcquireNode->getStmtForDiagnostics(), C.getSourceManager(),
AcquireNode->getLocationContext());
R = std::make_unique<PathSensitiveBugReport>(
Type, Msg, ErrorNode, LocUsedForUniqueing,
AcquireNode->getLocationContext()->getDecl());
}
}
if (!R)
R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode);
if (Range)
R->addRange(*Range);
R->markInteresting(Sym);

View File

@ -1,4 +1,5 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core,fuchsia.HandleChecker -verify %s
// RUN: %clang_analyze_cc1 -analyzer-checker=core,fuchsia.HandleChecker -analyzer-output=text \
// RUN: -verify %s
typedef __typeof__(sizeof(int)) size_t;
typedef int zx_status_t;
@ -116,33 +117,76 @@ void checkNoLeak06() {
void checkLeak01(int tag) {
zx_handle_t sa, sb;
if (zx_channel_create(0, &sa, &sb))
return;
if (zx_channel_create(0, &sa, &sb)) // expected-note {{Handle allocated here}}
return; // expected-note@-1 {{Assuming the condition is false}}
// expected-note@-2 {{Taking false branch}}
use1(&sa);
if (tag)
if (tag) // expected-note {{Assuming 'tag' is 0}}
zx_handle_close(sa);
// expected-note@-2 {{Taking false branch}}
use2(sb); // expected-warning {{Potential leak of handle}}
// expected-note@-1 {{Potential leak of handle}}
zx_handle_close(sb);
}
void checkReportLeakOnOnePath(int tag) {
zx_handle_t sa, sb;
if (zx_channel_create(0, &sa, &sb)) // expected-note {{Handle allocated here}}
return; // expected-note@-1 {{Assuming the condition is false}}
// expected-note@-2 {{Taking false branch}}
zx_handle_close(sb);
switch(tag) { // expected-note {{Control jumps to the 'default' case at line}}
case 0:
use2(sa);
return;
case 1:
use2(sa);
return;
case 2:
use2(sa);
return;
case 3:
use2(sa);
return;
case 4:
use2(sa);
return;
default:
use2(sa);
return; // expected-warning {{Potential leak of handle}}
// expected-note@-1 {{Potential leak of handle}}
}
}
void checkDoubleRelease01(int tag) {
zx_handle_t sa, sb;
zx_channel_create(0, &sa, &sb);
if (tag)
zx_handle_close(sa);
// expected-note@-1 {{Handle allocated here}}
if (tag) // expected-note {{Assuming 'tag' is not equal to 0}}
zx_handle_close(sa); // expected-note {{Handle released here}}
// expected-note@-2 {{Taking true branch}}
zx_handle_close(sa); // expected-warning {{Releasing a previously released handle}}
// expected-note@-1 {{Releasing a previously released handle}}
zx_handle_close(sb);
}
void checkUseAfterFree01(int tag) {
zx_handle_t sa, sb;
zx_channel_create(0, &sa, &sb);
// expected-note@-1 {{Handle allocated here}}
// expected-note@-2 {{Handle allocated here}}
// expected-note@+2 {{Taking true branch}}
// expected-note@+1 {{Taking false branch}}
if (tag) {
zx_handle_close(sa);
// expected-note@-1 {{Assuming 'tag' is not equal to 0}}
zx_handle_close(sa); // expected-note {{Handle released here}}
use1(&sa); // expected-warning {{Using a previously released handle}}
// expected-note@-1 {{Using a previously released handle}}
}
zx_handle_close(sb);
// expected-note@-6 {{Assuming 'tag' is 0}}
zx_handle_close(sb); // expected-note {{Handle released here}}
use2(sb); // expected-warning {{Using a previously released handle}}
// expected-note@-1 {{Using a previously released handle}}
}
void checkMemberOperatorIndices() {