Skip to content

Instantly share code, notes, and snippets.

@karkhaz
Created December 13, 2016 23:22
Show Gist options
  • Save karkhaz/82324c5a40fc25ea12ebd0a4d0c207cd to your computer and use it in GitHub Desktop.
Save karkhaz/82324c5a40fc25ea12ebd0a4d0c207cd to your computer and use it in GitHub Desktop.
//===-- 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