1c154f7bcSAnna Zaks //===-- BlockInCriticalSectionChecker.cpp -----------------------*- C++ -*-===//
2c154f7bcSAnna Zaks //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c154f7bcSAnna Zaks //
7c154f7bcSAnna Zaks //===----------------------------------------------------------------------===//
8c154f7bcSAnna Zaks //
9c154f7bcSAnna Zaks // Defines a checker for blocks in critical sections. This checker should find
10c154f7bcSAnna Zaks // the calls to blocking functions (for example: sleep, getc, fgets, read,
11c154f7bcSAnna Zaks // recv etc.) inside a critical section. When sleep(x) is called while a mutex
12c154f7bcSAnna Zaks // is held, other threades cannot lock the same mutex. This might take some
13c154f7bcSAnna Zaks // time, leading to bad performance or even deadlock.
14c154f7bcSAnna Zaks //
15c154f7bcSAnna Zaks //===----------------------------------------------------------------------===//
16c154f7bcSAnna Zaks
1776a21502SKristof Umann #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18c154f7bcSAnna Zaks #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19c154f7bcSAnna Zaks #include "clang/StaticAnalyzer/Core/Checker.h"
200b9d3a6eSBalazs Benics #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
21c154f7bcSAnna Zaks #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
22c154f7bcSAnna Zaks #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
23c154f7bcSAnna Zaks
24c154f7bcSAnna Zaks using namespace clang;
25c154f7bcSAnna Zaks using namespace ento;
26c154f7bcSAnna Zaks
27c154f7bcSAnna Zaks namespace {
28c154f7bcSAnna Zaks
299a8c8bf6SGabor Horvath class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
309a8c8bf6SGabor Horvath
319a8c8bf6SGabor Horvath mutable IdentifierInfo *IILockGuard, *IIUniqueLock;
32c154f7bcSAnna Zaks
33829c6bc0SGabor Horvath CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
34829c6bc0SGabor Horvath PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
35829c6bc0SGabor Horvath MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
36c154f7bcSAnna Zaks
379a8c8bf6SGabor Horvath StringRef ClassLockGuard, ClassUniqueLock;
389a8c8bf6SGabor Horvath
399a8c8bf6SGabor Horvath mutable bool IdentifierInfoInitialized;
409a8c8bf6SGabor Horvath
41c154f7bcSAnna Zaks std::unique_ptr<BugType> BlockInCritSectionBugType;
42c154f7bcSAnna Zaks
439a8c8bf6SGabor Horvath void initIdentifierInfo(ASTContext &Ctx) const;
449a8c8bf6SGabor Horvath
45c154f7bcSAnna Zaks void reportBlockInCritSection(SymbolRef FileDescSym,
46c154f7bcSAnna Zaks const CallEvent &call,
47c154f7bcSAnna Zaks CheckerContext &C) const;
48c154f7bcSAnna Zaks
49c154f7bcSAnna Zaks public:
50c154f7bcSAnna Zaks BlockInCriticalSectionChecker();
51c154f7bcSAnna Zaks
52829c6bc0SGabor Horvath bool isBlockingFunction(const CallEvent &Call) const;
53829c6bc0SGabor Horvath bool isLockFunction(const CallEvent &Call) const;
54829c6bc0SGabor Horvath bool isUnlockFunction(const CallEvent &Call) const;
55829c6bc0SGabor Horvath
56c154f7bcSAnna Zaks /// Process unlock.
57c154f7bcSAnna Zaks /// Process lock.
58c154f7bcSAnna Zaks /// Process blocking functions (sleep, getc, fgets, read, recv)
59c154f7bcSAnna Zaks void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
60c154f7bcSAnna Zaks };
61c154f7bcSAnna Zaks
62c154f7bcSAnna Zaks } // end anonymous namespace
63c154f7bcSAnna Zaks
REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter,unsigned)64c154f7bcSAnna Zaks REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
65c154f7bcSAnna Zaks
66c154f7bcSAnna Zaks BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
679a8c8bf6SGabor Horvath : IILockGuard(nullptr), IIUniqueLock(nullptr),
689a8c8bf6SGabor Horvath LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
69829c6bc0SGabor Horvath FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"),
70829c6bc0SGabor Horvath PthreadLockFn("pthread_mutex_lock"),
71829c6bc0SGabor Horvath PthreadTryLockFn("pthread_mutex_trylock"),
72829c6bc0SGabor Horvath PthreadUnlockFn("pthread_mutex_unlock"),
73829c6bc0SGabor Horvath MtxLock("mtx_lock"),
74829c6bc0SGabor Horvath MtxTimedLock("mtx_timedlock"),
75829c6bc0SGabor Horvath MtxTryLock("mtx_trylock"),
769a8c8bf6SGabor Horvath MtxUnlock("mtx_unlock"),
779a8c8bf6SGabor Horvath ClassLockGuard("lock_guard"),
789a8c8bf6SGabor Horvath ClassUniqueLock("unique_lock"),
799a8c8bf6SGabor Horvath IdentifierInfoInitialized(false) {
80c154f7bcSAnna Zaks // Initialize the bug type.
81c154f7bcSAnna Zaks BlockInCritSectionBugType.reset(
82c154f7bcSAnna Zaks new BugType(this, "Call to blocking function in critical section",
83c154f7bcSAnna Zaks "Blocking Error"));
84c154f7bcSAnna Zaks }
85c154f7bcSAnna Zaks
initIdentifierInfo(ASTContext & Ctx) const869a8c8bf6SGabor Horvath void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const {
879a8c8bf6SGabor Horvath if (!IdentifierInfoInitialized) {
889a8c8bf6SGabor Horvath /* In case of checking C code, or when the corresponding headers are not
899a8c8bf6SGabor Horvath * included, we might end up query the identifier table every time when this
909a8c8bf6SGabor Horvath * function is called instead of early returning it. To avoid this, a bool
919a8c8bf6SGabor Horvath * variable (IdentifierInfoInitialized) is used and the function will be run
929a8c8bf6SGabor Horvath * only once. */
939a8c8bf6SGabor Horvath IILockGuard = &Ctx.Idents.get(ClassLockGuard);
949a8c8bf6SGabor Horvath IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock);
959a8c8bf6SGabor Horvath IdentifierInfoInitialized = true;
969a8c8bf6SGabor Horvath }
979a8c8bf6SGabor Horvath }
989a8c8bf6SGabor Horvath
isBlockingFunction(const CallEvent & Call) const99829c6bc0SGabor Horvath bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
100*f18da190SBalazs Benics return matchesAny(Call, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn);
101829c6bc0SGabor Horvath }
102829c6bc0SGabor Horvath
isLockFunction(const CallEvent & Call) const103829c6bc0SGabor Horvath bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const {
1049a8c8bf6SGabor Horvath if (const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
1059a8c8bf6SGabor Horvath auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier();
1069a8c8bf6SGabor Horvath if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
1079a8c8bf6SGabor Horvath return true;
1089a8c8bf6SGabor Horvath }
1099a8c8bf6SGabor Horvath
110*f18da190SBalazs Benics return matchesAny(Call, LockFn, PthreadLockFn, PthreadTryLockFn, MtxLock,
111*f18da190SBalazs Benics MtxTimedLock, MtxTryLock);
112829c6bc0SGabor Horvath }
113829c6bc0SGabor Horvath
isUnlockFunction(const CallEvent & Call) const114829c6bc0SGabor Horvath bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
1159a8c8bf6SGabor Horvath if (const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
1162c775709SSimon Pilgrim const auto *DRecordDecl = cast<CXXRecordDecl>(Dtor->getDecl()->getParent());
1179a8c8bf6SGabor Horvath auto IdentifierInfo = DRecordDecl->getIdentifier();
1189a8c8bf6SGabor Horvath if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
1199a8c8bf6SGabor Horvath return true;
1209a8c8bf6SGabor Horvath }
1219a8c8bf6SGabor Horvath
122*f18da190SBalazs Benics return matchesAny(Call, UnlockFn, PthreadUnlockFn, MtxUnlock);
123829c6bc0SGabor Horvath }
124829c6bc0SGabor Horvath
checkPostCall(const CallEvent & Call,CheckerContext & C) const125c154f7bcSAnna Zaks void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
126c154f7bcSAnna Zaks CheckerContext &C) const {
1279a8c8bf6SGabor Horvath initIdentifierInfo(C.getASTContext());
1289a8c8bf6SGabor Horvath
129829c6bc0SGabor Horvath if (!isBlockingFunction(Call)
130829c6bc0SGabor Horvath && !isLockFunction(Call)
131829c6bc0SGabor Horvath && !isUnlockFunction(Call))
132c154f7bcSAnna Zaks return;
133c154f7bcSAnna Zaks
134c154f7bcSAnna Zaks ProgramStateRef State = C.getState();
135c154f7bcSAnna Zaks unsigned mutexCount = State->get<MutexCounter>();
136829c6bc0SGabor Horvath if (isUnlockFunction(Call) && mutexCount > 0) {
137c154f7bcSAnna Zaks State = State->set<MutexCounter>(--mutexCount);
138c154f7bcSAnna Zaks C.addTransition(State);
139829c6bc0SGabor Horvath } else if (isLockFunction(Call)) {
140c154f7bcSAnna Zaks State = State->set<MutexCounter>(++mutexCount);
141c154f7bcSAnna Zaks C.addTransition(State);
142c154f7bcSAnna Zaks } else if (mutexCount > 0) {
143c154f7bcSAnna Zaks SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol();
144c154f7bcSAnna Zaks reportBlockInCritSection(BlockDesc, Call, C);
145c154f7bcSAnna Zaks }
146c154f7bcSAnna Zaks }
147c154f7bcSAnna Zaks
reportBlockInCritSection(SymbolRef BlockDescSym,const CallEvent & Call,CheckerContext & C) const148c154f7bcSAnna Zaks void BlockInCriticalSectionChecker::reportBlockInCritSection(
149c154f7bcSAnna Zaks SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
150c154f7bcSAnna Zaks ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
151c154f7bcSAnna Zaks if (!ErrNode)
152c154f7bcSAnna Zaks return;
153c154f7bcSAnna Zaks
154829c6bc0SGabor Horvath std::string msg;
155829c6bc0SGabor Horvath llvm::raw_string_ostream os(msg);
156829c6bc0SGabor Horvath os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName()
157829c6bc0SGabor Horvath << "' inside of critical section";
1582f169e7cSArtem Dergachev auto R = std::make_unique<PathSensitiveBugReport>(*BlockInCritSectionBugType,
1592f169e7cSArtem Dergachev os.str(), ErrNode);
160c154f7bcSAnna Zaks R->addRange(Call.getSourceRange());
161c154f7bcSAnna Zaks R->markInteresting(BlockDescSym);
162c154f7bcSAnna Zaks C.emitReport(std::move(R));
163c154f7bcSAnna Zaks }
164c154f7bcSAnna Zaks
registerBlockInCriticalSectionChecker(CheckerManager & mgr)165c154f7bcSAnna Zaks void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
166c154f7bcSAnna Zaks mgr.registerChecker<BlockInCriticalSectionChecker>();
167c154f7bcSAnna Zaks }
168058a7a45SKristof Umann
shouldRegisterBlockInCriticalSectionChecker(const CheckerManager & mgr)169bda3dd0dSKirstóf Umann bool ento::shouldRegisterBlockInCriticalSectionChecker(const CheckerManager &mgr) {
170058a7a45SKristof Umann return true;
171058a7a45SKristof Umann }
172