-
-
Save zeroomega/2fcabf5979a0e42db1ccb8f30472d071 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//== TestHandleChecker.cpp -- test checker ----------------------*- C++ -*--==// | |
// | |
// The LLVM Compiler Infrastructure | |
// | |
// This file is distributed under the University of Illinois Open Source | |
// License. See LICENSE.TXT for details. | |
// | |
//===----------------------------------------------------------------------===// | |
// | |
// Just a simple handle leak checker | |
// | |
// | |
//===----------------------------------------------------------------------===// | |
#include "ClangSACheckers.h" | |
#include "clang/AST/Attr.h" | |
#include "clang/AST/ParentMap.h" | |
#include "clang/Basic/SourceManager.h" | |
#include "clang/Basic/TargetInfo.h" | |
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" | |
#include "clang/StaticAnalyzer/Core/Checker.h" | |
#include "clang/StaticAnalyzer/Core/CheckerManager.h" | |
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" | |
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" | |
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" | |
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" | |
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" | |
#include "llvm/ADT/STLExtras.h" | |
#include "llvm/ADT/SmallString.h" | |
#include "llvm/ADT/StringExtras.h" | |
#include <climits> | |
#include <utility> | |
#include <string> | |
#include <stdio.h> | |
using namespace clang; | |
using namespace ento; | |
namespace { | |
enum CallKind { | |
HANDLE_REQUEST, | |
HANDLE_RELEASE, | |
UNRELATED_CALL | |
}; | |
struct HandleState { | |
private: | |
enum Kind { | |
// Handle allocated | |
Allocated, | |
// Handle released | |
Released | |
} K; | |
HandleState(Kind k) : K(k) {} | |
public: | |
bool operator==(const HandleState &X) const { | |
return K == X.K; | |
} | |
bool isAllocated() const { return K == Allocated; } | |
bool isReleased() const { return K == Released; } | |
static HandleState getAllocated() { | |
return HandleState(Allocated); | |
} | |
static HandleState getReleased() { | |
return HandleState(Released); | |
} | |
void Profile(llvm::FoldingSetNodeID &ID) const { | |
ID.AddInteger(K); | |
} | |
void dump(raw_ostream &OS) const { | |
switch (static_cast<Kind>(K)) { | |
#define CASE(ID) case ID: OS << #ID; break; | |
CASE(Allocated) | |
CASE(Released) | |
} | |
} | |
LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); } | |
}; | |
class TestHandleChecker : public Checker< check::DeadSymbols, | |
check::PreCall, | |
check::PostCall > { | |
std::unique_ptr<BugType> LeakBugType; | |
std::unique_ptr<BugType> DoubleReleaseBugType; | |
std::unique_ptr<BugType> UseAfterFreeBugType; | |
public: | |
TestHandleChecker(); | |
// Checker callbacks | |
void checkPreCall(const CallEvent &Call, CheckerContext &C) const; | |
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; | |
void checkPostCall(const CallEvent &Call, CheckerContext &C) const; | |
// Bug report functions | |
void reportLeaks(ArrayRef<SymbolRef> LeakedHandles, | |
CheckerContext &C, ExplodedNode *ErrorNode) const; | |
void reportDoubleRelease(SymbolRef HandlerSym, | |
const CallEvent &Call, CheckerContext &C) const; | |
// Utility functions | |
CallKind CheckCallSignature(const CallEvent &Call) const; | |
// bool CheckSymbolDeathWithParents(SymbolReaper &SymReaper, | |
// SymbolRef sym, CheckerContext &C) const; | |
static void dumpSymbol(SymbolRef SymRef) { | |
if (SymRef) { | |
char buf[256]; | |
sprintf(buf, "%p", SymRef); | |
SymRef->dumpToStream(llvm::errs()); | |
llvm::errs()<<" Addr:"<<buf<<'\n'; | |
} | |
else { | |
llvm::errs()<<"SymbolRef is null\n"; | |
} | |
} | |
static bool isLeaked(SymbolRef SymHandler, | |
const HandleState &handleState, | |
bool isSymDead, | |
ProgramStateRef State) { | |
if (isSymDead && handleState.isAllocated()) { | |
// Handle's symbol is dead and is not in released state | |
return true; | |
} | |
return false; | |
} | |
}; | |
} // end anonymous namespace | |
REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState) | |
TestHandleChecker::TestHandleChecker() { | |
// Initialize the bug types. | |
LeakBugType.reset( | |
new BugType(this, "Handle Leak", "Handle Error")); | |
DoubleReleaseBugType.reset( | |
new BugType(this, "Handle Double Release", "Handle Error")); | |
UseAfterFreeBugType.reset( | |
new BugType(this, "Handle Use After Free", "Handle Error")); | |
} | |
CallKind TestHandleChecker::CheckCallSignature(const CallEvent &Call) | |
const { | |
if (const AnyFunctionCall *func_call = dyn_cast<AnyFunctionCall>(&Call)) { | |
const FunctionDecl *func_decl = func_call->getDecl(); | |
if (!func_decl) { | |
return UNRELATED_CALL; | |
} | |
DeclarationName DeclName = func_decl->getDeclName(); | |
if (!DeclName.isIdentifier()) { | |
return UNRELATED_CALL; | |
} | |
// Currently just use function name to identify target calls to simplify | |
// the test. In the future, strictly check the whole signature of the | |
// function | |
StringRef func_name = func_decl->getName(); | |
if (func_name.equals("request")) { | |
return HANDLE_REQUEST; | |
} | |
if (func_name.equals("release")) { | |
return HANDLE_RELEASE; | |
} | |
} | |
return UNRELATED_CALL; | |
} | |
void TestHandleChecker::checkPostCall(const CallEvent &Call, | |
CheckerContext &C) const { | |
ProgramStateRef State = C.getState(); | |
CallKind call_kind = CheckCallSignature(Call); | |
if (call_kind == HANDLE_REQUEST) { | |
// Retrieve symbolic value for allocated handler | |
const MemRegion* MemHandle1 = Call.getArgSVal(1).getAsRegion(); | |
const MemRegion* MemHandle2 = Call.getArgSVal(2).getAsRegion(); | |
if (!MemHandle1 || !MemHandle2) { | |
return; | |
} | |
SVal SValHandle1 = State->getSVal(MemHandle1); | |
SVal SValHandle2 = State->getSVal(MemHandle2); | |
SymbolRef SymHandle1 = SValHandle1.getAsSymbol(); | |
SymbolRef SymHandle2 = SValHandle2.getAsSymbol(); | |
if (!SymHandle1 || !SymHandle2) { | |
// There is at least one symbol that is null | |
return; | |
} | |
llvm::errs()<<"Requested Handles Dump: \n"; | |
dumpSymbol(SymHandle1); | |
dumpSymbol(SymHandle2); | |
llvm::errs()<<"Dump End\n\n"; | |
// Track symbol for handler | |
State = State->set<HStateMap>(SymHandle1, HandleState::getAllocated()); | |
State = State->set<HStateMap>(SymHandle2, HandleState::getAllocated()); | |
C.addTransition(State); | |
} | |
} | |
void TestHandleChecker::checkPreCall(const CallEvent &Call, | |
CheckerContext &C) const { | |
ProgramStateRef State = C.getState(); | |
CallKind call_kind = CheckCallSignature(Call); | |
if (call_kind == HANDLE_RELEASE) { | |
// Retrive symbolic value for handle | |
SymbolRef SymHandle = Call.getArgSVal(0).getAsSymbol(); | |
if (!SymHandle) { | |
return; | |
} | |
llvm::errs()<<"Released Handle Dump: \n"; | |
//Call.getArgSVal(0).dumpToStream(llvm::errs()); | |
llvm::errs()<<" "; | |
dumpSymbol(SymHandle); | |
llvm::errs()<<"Dump End\n\n"; | |
const HandleState * SymState = State->get<HStateMap>(SymHandle); | |
if (!SymState) { | |
return; | |
} | |
if (SymState->isReleased()) { | |
// Double release | |
llvm::errs()<<"Double release detected!\n"; | |
reportDoubleRelease(SymHandle, Call, C); | |
return; | |
} | |
State = State->set<HStateMap>(SymHandle, HandleState::getReleased()); | |
C.addTransition(State); | |
} | |
} | |
void TestHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper, | |
CheckerContext &C) const { | |
ProgramStateRef State = C.getState(); | |
if (SymReaper.hasDeadSymbols()) { | |
llvm::errs()<<"Dead Symbols detected\n"; | |
llvm::errs()<<"Dead Symbols dump\n"; | |
for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), | |
E = SymReaper.dead_end(); I != E; ++I) { | |
SymbolRef Sym = * I; | |
dumpSymbol(Sym); | |
} | |
llvm::errs()<<"Dump End\n\n"; | |
} else { | |
llvm::errs()<<"Dead Symbol not available\n\n"; | |
} | |
SmallVector<SymbolRef, 2> LeakVector; | |
HStateMapTy tracked_stream = State->get<HStateMap>(); | |
for (HStateMapTy::iterator I = tracked_stream.begin(), | |
E = tracked_stream.end(); I != E; ++I) { | |
SymbolRef SymHandle = I->first; | |
llvm::errs()<<"Test on SymbolRef: "; | |
dumpSymbol(SymHandle); | |
bool IsSymDead = SymReaper.isDead(SymHandle); | |
if (IsSymDead) { | |
llvm::errs()<<"It is dead\n"; | |
} else { | |
llvm::errs()<<"It is not dead\n"; | |
} | |
HandleState CurrentHandlerState = I->second; | |
if (CurrentHandlerState.isAllocated()) { | |
llvm::errs()<<"It is requested\n"; | |
} | |
if (CurrentHandlerState.isReleased()) { | |
llvm::errs()<<"It is released\n"; | |
} | |
if (isLeaked(SymHandle, CurrentHandlerState, IsSymDead, State)) { | |
llvm::errs()<<"Handler is leaking\n"; | |
LeakVector.push_back(SymHandle); | |
} | |
if (IsSymDead) { | |
State = State->remove<HStateMap>(SymHandle); | |
} | |
llvm::errs()<<"\n"; | |
} | |
if (!LeakVector.empty()) { | |
ExplodedNode *N = C.generateNonFatalErrorNode(State); | |
if (!N) { | |
return; | |
} | |
reportLeaks(LeakVector, C, N); | |
} | |
} | |
void TestHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles, | |
CheckerContext &C, | |
ExplodedNode *ErrorNode) const { | |
for (SymbolRef LeakedHandle : LeakedHandles) { | |
auto R = llvm::make_unique<BugReport>(*LeakBugType, | |
"Allocated handler is never released; potential resource leak", | |
ErrorNode); | |
llvm::errs()<<"Leaking handle: "; | |
dumpSymbol(LeakedHandle); | |
llvm::errs()<<"\n"; | |
R->markInteresting(LeakedHandle); | |
C.emitReport(std::move(R)); | |
} | |
} | |
void TestHandleChecker::reportDoubleRelease(SymbolRef HandlerSym, | |
const CallEvent &Call, | |
CheckerContext &C) const { | |
ExplodedNode *ErrNode = C.generateErrorNode(); | |
if (!ErrNode) { | |
return; | |
} | |
auto R = llvm::make_unique<BugReport>(*DoubleReleaseBugType, | |
"Releasing a previously released handler", ErrNode); | |
R->addRange(Call.getSourceRange()); | |
R->markInteresting(HandlerSym); | |
C.emitReport(std::move(R)); | |
} | |
void ento::registerTestHandleChecker(CheckerManager &mgr) { | |
mgr.registerChecker<TestHandleChecker>(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment