Bug Summary

File:tools/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
Warning:line 130, column 27
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name BlockInCriticalSectionChecker.cpp -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mthread-model posix -mframe-pointer=none -relaxed-aliasing -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -ffunction-sections -fdata-sections -resource-dir /usr/lib/llvm-10/lib/clang/10.0.0 -D CLANG_VENDOR="Debian " -D _DEBUG -D _GNU_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I /build/llvm-toolchain-snapshot-10~svn374710/build-llvm/tools/clang/lib/StaticAnalyzer/Checkers -I /build/llvm-toolchain-snapshot-10~svn374710/tools/clang/lib/StaticAnalyzer/Checkers -I /build/llvm-toolchain-snapshot-10~svn374710/tools/clang/include -I /build/llvm-toolchain-snapshot-10~svn374710/build-llvm/tools/clang/include -I /build/llvm-toolchain-snapshot-10~svn374710/build-llvm/include -I /build/llvm-toolchain-snapshot-10~svn374710/include -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/x86_64-linux-gnu/c++/6.3.0 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/x86_64-linux-gnu/c++/6.3.0 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-10/lib/clang/10.0.0/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-comment -std=c++14 -fdeprecated-macro -fdebug-compilation-dir /build/llvm-toolchain-snapshot-10~svn374710/build-llvm/tools/clang/lib/StaticAnalyzer/Checkers -fdebug-prefix-map=/build/llvm-toolchain-snapshot-10~svn374710=. -ferror-limit 19 -fmessage-length 0 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fobjc-runtime=gcc -fno-common -fdiagnostics-show-option -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -o /tmp/scan-build-2019-10-13-141012-12518-1 -x c++ /build/llvm-toolchain-snapshot-10~svn374710/tools/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
1//===-- BlockInCriticalSectionChecker.cpp -----------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Defines a checker for blocks in critical sections. This checker should find
10// the calls to blocking functions (for example: sleep, getc, fgets, read,
11// recv etc.) inside a critical section. When sleep(x) is called while a mutex
12// is held, other threades cannot lock the same mutex. This might take some
13// time, leading to bad performance or even deadlock.
14//
15//===----------------------------------------------------------------------===//
16
17#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19#include "clang/StaticAnalyzer/Core/Checker.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
21#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22
23using namespace clang;
24using namespace ento;
25
26namespace {
27
28class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
29
30 mutable IdentifierInfo *IILockGuard, *IIUniqueLock;
31
32 CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
33 PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
34 MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
35
36 StringRef ClassLockGuard, ClassUniqueLock;
37
38 mutable bool IdentifierInfoInitialized;
39
40 std::unique_ptr<BugType> BlockInCritSectionBugType;
41
42 void initIdentifierInfo(ASTContext &Ctx) const;
43
44 void reportBlockInCritSection(SymbolRef FileDescSym,
45 const CallEvent &call,
46 CheckerContext &C) const;
47
48public:
49 BlockInCriticalSectionChecker();
50
51 bool isBlockingFunction(const CallEvent &Call) const;
52 bool isLockFunction(const CallEvent &Call) const;
53 bool isUnlockFunction(const CallEvent &Call) const;
54
55 /// Process unlock.
56 /// Process lock.
57 /// Process blocking functions (sleep, getc, fgets, read, recv)
58 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
59};
60
61} // end anonymous namespace
62
63REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)namespace { class MutexCounter {}; using MutexCounterTy = unsigned
; } namespace clang { namespace ento { template <> struct
ProgramStateTrait<MutexCounter> : public ProgramStatePartialTrait
<MutexCounterTy> { static void *GDMIndex() { static int
Index; return &Index; } }; } }
64
65BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
66 : IILockGuard(nullptr), IIUniqueLock(nullptr),
67 LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
68 FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"),
69 PthreadLockFn("pthread_mutex_lock"),
70 PthreadTryLockFn("pthread_mutex_trylock"),
71 PthreadUnlockFn("pthread_mutex_unlock"),
72 MtxLock("mtx_lock"),
73 MtxTimedLock("mtx_timedlock"),
74 MtxTryLock("mtx_trylock"),
75 MtxUnlock("mtx_unlock"),
76 ClassLockGuard("lock_guard"),
77 ClassUniqueLock("unique_lock"),
78 IdentifierInfoInitialized(false) {
79 // Initialize the bug type.
80 BlockInCritSectionBugType.reset(
81 new BugType(this, "Call to blocking function in critical section",
82 "Blocking Error"));
83}
84
85void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const {
86 if (!IdentifierInfoInitialized) {
87 /* In case of checking C code, or when the corresponding headers are not
88 * included, we might end up query the identifier table every time when this
89 * function is called instead of early returning it. To avoid this, a bool
90 * variable (IdentifierInfoInitialized) is used and the function will be run
91 * only once. */
92 IILockGuard = &Ctx.Idents.get(ClassLockGuard);
93 IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock);
94 IdentifierInfoInitialized = true;
95 }
96}
97
98bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
99 if (Call.isCalled(SleepFn)
100 || Call.isCalled(GetcFn)
101 || Call.isCalled(FgetsFn)
102 || Call.isCalled(ReadFn)
103 || Call.isCalled(RecvFn)) {
104 return true;
105 }
106 return false;
107}
108
109bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const {
110 if (const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
111 auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier();
112 if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
113 return true;
114 }
115
116 if (Call.isCalled(LockFn)
117 || Call.isCalled(PthreadLockFn)
118 || Call.isCalled(PthreadTryLockFn)
119 || Call.isCalled(MtxLock)
120 || Call.isCalled(MtxTimedLock)
121 || Call.isCalled(MtxTryLock)) {
122 return true;
123 }
124 return false;
125}
126
127bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
128 if (const auto *Dtor
2.1
'Dtor' is non-null
= dyn_cast<CXXDestructorCall>(&Call)) {
2
Assuming the object is a 'CXXDestructorCall'
3
Taking true branch
129 const auto *DRecordDecl = dyn_cast<CXXRecordDecl>(Dtor->getDecl()->getParent());
4
Assuming the object is not a 'CXXRecordDecl'
5
'DRecordDecl' initialized to a null pointer value
130 auto IdentifierInfo = DRecordDecl->getIdentifier();
6
Called C++ object pointer is null
131 if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
132 return true;
133 }
134
135 if (Call.isCalled(UnlockFn)
136 || Call.isCalled(PthreadUnlockFn)
137 || Call.isCalled(MtxUnlock)) {
138 return true;
139 }
140 return false;
141}
142
143void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
144 CheckerContext &C) const {
145 initIdentifierInfo(C.getASTContext());
146
147 if (!isBlockingFunction(Call)
148 && !isLockFunction(Call)
149 && !isUnlockFunction(Call))
1
Calling 'BlockInCriticalSectionChecker::isUnlockFunction'
150 return;
151
152 ProgramStateRef State = C.getState();
153 unsigned mutexCount = State->get<MutexCounter>();
154 if (isUnlockFunction(Call) && mutexCount > 0) {
155 State = State->set<MutexCounter>(--mutexCount);
156 C.addTransition(State);
157 } else if (isLockFunction(Call)) {
158 State = State->set<MutexCounter>(++mutexCount);
159 C.addTransition(State);
160 } else if (mutexCount > 0) {
161 SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol();
162 reportBlockInCritSection(BlockDesc, Call, C);
163 }
164}
165
166void BlockInCriticalSectionChecker::reportBlockInCritSection(
167 SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
168 ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
169 if (!ErrNode)
170 return;
171
172 std::string msg;
173 llvm::raw_string_ostream os(msg);
174 os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName()
175 << "' inside of critical section";
176 auto R = std::make_unique<PathSensitiveBugReport>(*BlockInCritSectionBugType,
177 os.str(), ErrNode);
178 R->addRange(Call.getSourceRange());
179 R->markInteresting(BlockDescSym);
180 C.emitReport(std::move(R));
181}
182
183void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
184 mgr.registerChecker<BlockInCriticalSectionChecker>();
185}
186
187bool ento::shouldRegisterBlockInCriticalSectionChecker(const LangOptions &LO) {
188 return true;
189}