1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
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 | |
23 | using namespace clang; |
24 | using namespace ento; |
25 | |
26 | namespace { |
27 | |
28 | class 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 | |
48 | public: |
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 | |
56 | |
57 | |
58 | void checkPostCall(const CallEvent &Call, CheckerContext &C) const; |
59 | }; |
60 | |
61 | } |
62 | |
63 | REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned) |
64 | |
65 | BlockInCriticalSectionChecker::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 | |
80 | BlockInCritSectionBugType.reset( |
81 | new BugType(this, "Call to blocking function in critical section", |
82 | "Blocking Error")); |
83 | } |
84 | |
85 | void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const { |
86 | if (!IdentifierInfoInitialized) { |
87 | |
88 | |
89 | |
90 | |
91 | |
92 | IILockGuard = &Ctx.Idents.get(ClassLockGuard); |
93 | IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock); |
94 | IdentifierInfoInitialized = true; |
95 | } |
96 | } |
97 | |
98 | bool 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 | |
109 | bool 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 | |
127 | bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const { |
128 | if (const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) { |
129 | const auto *DRecordDecl = dyn_cast<CXXRecordDecl>(Dtor->getDecl()->getParent()); |
130 | auto IdentifierInfo = DRecordDecl->getIdentifier(); |
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 | |
143 | void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call, |
144 | CheckerContext &C) const { |
145 | initIdentifierInfo(C.getASTContext()); |
146 | |
147 | if (!isBlockingFunction(Call) |
148 | && !isLockFunction(Call) |
149 | && !isUnlockFunction(Call)) |
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 | |
166 | void 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 = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, os.str(), ErrNode); |
177 | R->addRange(Call.getSourceRange()); |
178 | R->markInteresting(BlockDescSym); |
179 | C.emitReport(std::move(R)); |
180 | } |
181 | |
182 | void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) { |
183 | mgr.registerChecker<BlockInCriticalSectionChecker>(); |
184 | } |
185 | |
186 | bool ento::shouldRegisterBlockInCriticalSectionChecker(const LangOptions &LO) { |
187 | return true; |
188 | } |
189 | |