Created
December 13, 2016 23:22
-
-
Save karkhaz/82324c5a40fc25ea12ebd0a4d0c207cd 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
//===-- ProblemInCriticalSectionChecker.cpp ---------------------*- C++ -*-===// | |
// | |
// The LLVM Compiler Infrastructure | |
// | |
// This file is distributed under the University of Illinois Open Source | |
// License. See LICENSE.TXT for details. | |
// | |
//===----------------------------------------------------------------------===// | |
// | |
// This file defines a set of checkers that check for calling a function that is | |
// not supposed to be called from inside of a critical region. A 'critical | |
// region' is a program path that starts with a call to one of a set of | |
// functions (e.g. one of "lock", "acquire", etc) and ends with call to one of | |
// another set of functions (e.g. one of "unlock", "release", etc.) | |
// | |
//===----------------------------------------------------------------------===// | |
#include "ClangSACheckers.h" | |
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" | |
#include "clang/StaticAnalyzer/Core/Checker.h" | |
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" | |
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" | |
using namespace clang; | |
using namespace ento; | |
namespace { | |
/// \brief Base class for checks for calling prohibited functions from inside | |
/// a critical region | |
class ProblemInCriticalSectionChecker : public Checker<check::PostCall> { | |
protected: | |
/// Names of the functions that mark the entry into a critical region | |
std::vector<CallDescription> EntryFuns; | |
/// Names of the functions that mark the exit from a critical region | |
std::vector<CallDescription> ExitFuns; | |
/// Names of the functions that must not be called from a critical region | |
std::vector<CallDescription> ProhibitedCalls; | |
StringRef ReportString; | |
std::unique_ptr<BugType> ProblemInCritSectionBugType; | |
void reportProblemInCritSection(SymbolRef FileDescSym, | |
const CallEvent &call, | |
CheckerContext &C) const; | |
public: | |
/// \brief Specifying critical region delimiters and prohibited functions | |
/// | |
/// \param Entry List of functions that signify the entry of a critical region | |
/// \param Exit List of functions that signify the exit from a critical region | |
/// \param Prohibited List of functions that must not be called from inside a | |
/// critical region | |
/// \param ReportString A description of the problem, containing a single | |
/// string format token '%s' that will be replaced by the problematic | |
/// function call | |
ProblemInCriticalSectionChecker(std::vector<CallDescription> Entry, | |
std::vector<CallDescription> Exit, | |
std::vector<CallDescription> Prohibited, | |
StringRef ReportString) | |
: EntryFuns(Entry), ExitFuns(Exit), ProhibitedCalls(Prohibited), | |
ReportString(ReportString) {}; | |
void checkPostCall(const CallEvent &Call, CheckerContext &C) const; | |
}; | |
/// Defines a checker for blocks in critical sections. This checker should find | |
/// calls to blocking functions (for example: sleep, getc, fgets, read, recv | |
/// etc.) inside a critical section. When sleep(x) is called while a mutex is | |
/// held, other threads cannot lock the same mutex. This might take some time, | |
/// leading to bad performance or even deadlock. | |
class BlockInCriticalSectionChecker : public ProblemInCriticalSectionChecker { | |
public: | |
BlockInCriticalSectionChecker(); | |
}; | |
/// Magenta-specific checker for acquiring a mutex inside an interrupt context. | |
class MagentaInterruptContextChecker : public ProblemInCriticalSectionChecker { | |
public: | |
MagentaInterruptContextChecker(); | |
}; | |
} // end anonymous namespace | |
REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned) | |
void ProblemInCriticalSectionChecker::checkPostCall(const CallEvent &Call, | |
CheckerContext &C) const { | |
ProgramStateRef State = C.getState(); | |
unsigned mutexCount = State->get<MutexCounter>(); | |
for (CallDescription EntryFun : EntryFuns) | |
if (Call.isCalled(EntryFun)) { | |
State = State->set<MutexCounter>(++mutexCount); | |
C.addTransition(State); | |
return; | |
} | |
for (CallDescription ExitFun : ExitFuns) | |
if (Call.isCalled(ExitFun) && mutexCount > 0) { | |
State = State->set<MutexCounter>(--mutexCount); | |
C.addTransition(State); | |
return; | |
} | |
for (CallDescription ProhibitedCall : ProhibitedCalls) | |
if (Call.isCalled(ProhibitedCall) && mutexCount > 0) { | |
SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol(); | |
reportProblemInCritSection(BlockDesc, Call, C); | |
return; | |
} | |
} | |
void ProblemInCriticalSectionChecker::reportProblemInCritSection( | |
SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const { | |
ExplodedNode *ErrNode = C.generateNonFatalErrorNode(); | |
if (!ErrNode) | |
return; | |
auto R = llvm::make_unique<BugReport>(*ProblemInCritSectionBugType, | |
ReportString, ErrNode); | |
R->addRange(Call.getSourceRange()); | |
R->markInteresting(BlockDescSym); | |
C.emitReport(std::move(R)); | |
} | |
// ============= Implementations of checker subclasses ========================= | |
BlockInCriticalSectionChecker::BlockInCriticalSectionChecker() | |
: ProblemInCriticalSectionChecker({CallDescription("lock")}, | |
{CallDescription("unlock")}, | |
{CallDescription("sleep"), | |
CallDescription("getc"), | |
CallDescription("fgets"), | |
CallDescription("read"), | |
CallDescription("recv")}, | |
"A blocking function %s is called inside" | |
" a critical section") { | |
ProblemInCritSectionBugType.reset( | |
new BugType(this, "Call to blocking function in critical section", | |
"Blocking Error")); | |
} | |
void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) { | |
mgr.registerChecker<BlockInCriticalSectionChecker>(); | |
} | |
MagentaInterruptContextChecker::MagentaInterruptContextChecker() | |
: ProblemInCriticalSectionChecker( | |
{CallDescription("x86_exception_handler")}, | |
{CallDescription("thread_preempt"), | |
CallDescription("panic"), | |
CallDescription("_panic")}, | |
{CallDescription("mutex_acquire"), | |
CallDescription("mutex_acquire_timeout"), | |
CallDescription("mutex_acquire_timeout_internal")}, | |
"Mutex (%s) acquired inside interrupt context") { | |
ProblemInCritSectionBugType.reset( | |
new BugType(this, "Mutex acquisition during interrupt context", | |
"Magenta mutex error")); | |
} | |
void ento::registerMagentaInterruptContextChecker(CheckerManager &mgr) { | |
mgr.registerChecker<MagentaInterruptContextChecker>(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment