1 //===-- BlockInCriticalSectionChecker.cpp -----------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // Defines a checker for blocks in critical sections. This checker should find
11 // the calls to blocking functions (for example: sleep, getc, fgets, read,
12 // recv etc.) inside a critical section. When sleep(x) is called while a mutex
13 // is held, other threades cannot lock the same mutex. This might take some
14 // time, leading to bad performance or even deadlock.
16 //===----------------------------------------------------------------------===//
18 #include "ClangSACheckers.h"
19 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20 #include "clang/StaticAnalyzer/Core/Checker.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
24 using namespace clang;
29 class BlockInCriticalSectionChecker : public Checker<check::PostCall,
32 CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn;
34 std::unique_ptr<BugType> BlockInCritSectionBugType;
36 void reportBlockInCritSection(SymbolRef FileDescSym,
37 const CallEvent &call,
38 CheckerContext &C) const;
41 BlockInCriticalSectionChecker();
43 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
47 /// Process blocking functions (sleep, getc, fgets, read, recv)
48 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
52 } // end anonymous namespace
54 REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
56 BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
57 : LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
58 FgetsFn("fgets"), ReadFn("read"), RecvFn("recv") {
59 // Initialize the bug type.
60 BlockInCritSectionBugType.reset(
61 new BugType(this, "Call to blocking function in critical section",
65 void BlockInCriticalSectionChecker::checkPreCall(const CallEvent &Call,
66 CheckerContext &C) const {
69 void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
70 CheckerContext &C) const {
71 if (!Call.isCalled(LockFn)
72 && !Call.isCalled(SleepFn)
73 && !Call.isCalled(GetcFn)
74 && !Call.isCalled(FgetsFn)
75 && !Call.isCalled(ReadFn)
76 && !Call.isCalled(RecvFn)
77 && !Call.isCalled(UnlockFn))
80 ProgramStateRef State = C.getState();
81 unsigned mutexCount = State->get<MutexCounter>();
82 if (Call.isCalled(UnlockFn) && mutexCount > 0) {
83 State = State->set<MutexCounter>(--mutexCount);
84 C.addTransition(State);
85 } else if (Call.isCalled(LockFn)) {
86 State = State->set<MutexCounter>(++mutexCount);
87 C.addTransition(State);
88 } else if (mutexCount > 0) {
89 SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol();
90 reportBlockInCritSection(BlockDesc, Call, C);
94 void BlockInCriticalSectionChecker::reportBlockInCritSection(
95 SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
96 ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
100 auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType,
101 "A blocking function %s is called inside a critical section.", ErrNode);
102 R->addRange(Call.getSourceRange());
103 R->markInteresting(BlockDescSym);
104 C.emitReport(std::move(R));
107 void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
108 mgr.registerChecker<BlockInCriticalSectionChecker>();