forked from OSchip/llvm-project
[analyzer] Extend block in critical section check with C11 and Pthread APIs.
Patch by Zoltan Daniel Torok! Differential Revision: https://reviews.llvm.org/D29567 llvm-svn: 297461
This commit is contained in:
parent
e9313ba2de
commit
829c6bc04a
|
@ -29,7 +29,9 @@ namespace {
|
||||||
class BlockInCriticalSectionChecker : public Checker<check::PostCall,
|
class BlockInCriticalSectionChecker : public Checker<check::PostCall,
|
||||||
check::PreCall> {
|
check::PreCall> {
|
||||||
|
|
||||||
CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn;
|
CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
|
||||||
|
PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
|
||||||
|
MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
|
||||||
|
|
||||||
std::unique_ptr<BugType> BlockInCritSectionBugType;
|
std::unique_ptr<BugType> BlockInCritSectionBugType;
|
||||||
|
|
||||||
|
@ -40,6 +42,10 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall,
|
||||||
public:
|
public:
|
||||||
BlockInCriticalSectionChecker();
|
BlockInCriticalSectionChecker();
|
||||||
|
|
||||||
|
bool isBlockingFunction(const CallEvent &Call) const;
|
||||||
|
bool isLockFunction(const CallEvent &Call) const;
|
||||||
|
bool isUnlockFunction(const CallEvent &Call) const;
|
||||||
|
|
||||||
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
|
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
|
||||||
|
|
||||||
/// Process unlock.
|
/// Process unlock.
|
||||||
|
@ -55,34 +61,69 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
|
||||||
|
|
||||||
BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
|
BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
|
||||||
: LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
|
: LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
|
||||||
FgetsFn("fgets"), ReadFn("read"), RecvFn("recv") {
|
FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"),
|
||||||
|
PthreadLockFn("pthread_mutex_lock"),
|
||||||
|
PthreadTryLockFn("pthread_mutex_trylock"),
|
||||||
|
PthreadUnlockFn("pthread_mutex_unlock"),
|
||||||
|
MtxLock("mtx_lock"),
|
||||||
|
MtxTimedLock("mtx_timedlock"),
|
||||||
|
MtxTryLock("mtx_trylock"),
|
||||||
|
MtxUnlock("mtx_unlock") {
|
||||||
// Initialize the bug type.
|
// Initialize the bug type.
|
||||||
BlockInCritSectionBugType.reset(
|
BlockInCritSectionBugType.reset(
|
||||||
new BugType(this, "Call to blocking function in critical section",
|
new BugType(this, "Call to blocking function in critical section",
|
||||||
"Blocking Error"));
|
"Blocking Error"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
|
||||||
|
if (Call.isCalled(SleepFn)
|
||||||
|
|| Call.isCalled(GetcFn)
|
||||||
|
|| Call.isCalled(FgetsFn)
|
||||||
|
|| Call.isCalled(ReadFn)
|
||||||
|
|| Call.isCalled(RecvFn)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const {
|
||||||
|
if (Call.isCalled(LockFn)
|
||||||
|
|| Call.isCalled(PthreadLockFn)
|
||||||
|
|| Call.isCalled(PthreadTryLockFn)
|
||||||
|
|| Call.isCalled(MtxLock)
|
||||||
|
|| Call.isCalled(MtxTimedLock)
|
||||||
|
|| Call.isCalled(MtxTryLock)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
|
||||||
|
if (Call.isCalled(UnlockFn)
|
||||||
|
|| Call.isCalled(PthreadUnlockFn)
|
||||||
|
|| Call.isCalled(MtxUnlock)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void BlockInCriticalSectionChecker::checkPreCall(const CallEvent &Call,
|
void BlockInCriticalSectionChecker::checkPreCall(const CallEvent &Call,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
|
void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
if (!Call.isCalled(LockFn)
|
if (!isBlockingFunction(Call)
|
||||||
&& !Call.isCalled(SleepFn)
|
&& !isLockFunction(Call)
|
||||||
&& !Call.isCalled(GetcFn)
|
&& !isUnlockFunction(Call))
|
||||||
&& !Call.isCalled(FgetsFn)
|
|
||||||
&& !Call.isCalled(ReadFn)
|
|
||||||
&& !Call.isCalled(RecvFn)
|
|
||||||
&& !Call.isCalled(UnlockFn))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ProgramStateRef State = C.getState();
|
ProgramStateRef State = C.getState();
|
||||||
unsigned mutexCount = State->get<MutexCounter>();
|
unsigned mutexCount = State->get<MutexCounter>();
|
||||||
if (Call.isCalled(UnlockFn) && mutexCount > 0) {
|
if (isUnlockFunction(Call) && mutexCount > 0) {
|
||||||
State = State->set<MutexCounter>(--mutexCount);
|
State = State->set<MutexCounter>(--mutexCount);
|
||||||
C.addTransition(State);
|
C.addTransition(State);
|
||||||
} else if (Call.isCalled(LockFn)) {
|
} else if (isLockFunction(Call)) {
|
||||||
State = State->set<MutexCounter>(++mutexCount);
|
State = State->set<MutexCounter>(++mutexCount);
|
||||||
C.addTransition(State);
|
C.addTransition(State);
|
||||||
} else if (mutexCount > 0) {
|
} else if (mutexCount > 0) {
|
||||||
|
@ -97,8 +138,11 @@ void BlockInCriticalSectionChecker::reportBlockInCritSection(
|
||||||
if (!ErrNode)
|
if (!ErrNode)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType,
|
std::string msg;
|
||||||
"A blocking function %s is called inside a critical section.", ErrNode);
|
llvm::raw_string_ostream os(msg);
|
||||||
|
os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName()
|
||||||
|
<< "' inside of critical section";
|
||||||
|
auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, os.str(), ErrNode);
|
||||||
R->addRange(Call.getSourceRange());
|
R->addRange(Call.getSourceRange());
|
||||||
R->markInteresting(BlockDescSym);
|
R->markInteresting(BlockDescSym);
|
||||||
C.emitReport(std::move(R));
|
C.emitReport(std::move(R));
|
||||||
|
|
|
@ -9,29 +9,91 @@ struct mutex {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void testBlockInCriticalSection() {
|
void getc() {}
|
||||||
|
void fgets() {}
|
||||||
|
void read() {}
|
||||||
|
void recv() {}
|
||||||
|
|
||||||
|
void pthread_mutex_lock() {}
|
||||||
|
void pthread_mutex_trylock() {}
|
||||||
|
void pthread_mutex_unlock() {}
|
||||||
|
|
||||||
|
void mtx_lock() {}
|
||||||
|
void mtx_timedlock() {}
|
||||||
|
void mtx_trylock() {}
|
||||||
|
void mtx_unlock() {}
|
||||||
|
|
||||||
|
void testBlockInCriticalSectionWithStdMutex() {
|
||||||
std::mutex m;
|
std::mutex m;
|
||||||
m.lock();
|
m.lock();
|
||||||
sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}}
|
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
||||||
|
getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
|
||||||
|
fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
|
||||||
|
read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
|
||||||
|
recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
|
||||||
m.unlock();
|
m.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void testBlockInCriticalSectionWithPthreadMutex() {
|
||||||
|
pthread_mutex_lock();
|
||||||
|
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
||||||
|
getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
|
||||||
|
fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
|
||||||
|
read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
|
||||||
|
recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
|
||||||
|
pthread_mutex_unlock();
|
||||||
|
|
||||||
|
pthread_mutex_trylock();
|
||||||
|
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
||||||
|
getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
|
||||||
|
fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
|
||||||
|
read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
|
||||||
|
recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
|
||||||
|
pthread_mutex_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void testBlockInCriticalSectionC11Locks() {
|
||||||
|
mtx_lock();
|
||||||
|
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
||||||
|
getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
|
||||||
|
fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
|
||||||
|
read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
|
||||||
|
recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
|
||||||
|
mtx_unlock();
|
||||||
|
|
||||||
|
mtx_timedlock();
|
||||||
|
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
||||||
|
getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
|
||||||
|
fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
|
||||||
|
read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
|
||||||
|
recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
|
||||||
|
mtx_unlock();
|
||||||
|
|
||||||
|
mtx_trylock();
|
||||||
|
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
||||||
|
getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
|
||||||
|
fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
|
||||||
|
read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
|
||||||
|
recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
|
||||||
|
mtx_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
void testBlockInCriticalSectionWithNestedMutexes() {
|
void testBlockInCriticalSectionWithNestedMutexes() {
|
||||||
std::mutex m, n, k;
|
std::mutex m, n, k;
|
||||||
m.lock();
|
m.lock();
|
||||||
n.lock();
|
n.lock();
|
||||||
k.lock();
|
k.lock();
|
||||||
sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}}
|
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
||||||
k.unlock();
|
k.unlock();
|
||||||
sleep(5); // expected-warning {{A blocking function %s is called inside a critical section}}
|
sleep(5); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
||||||
n.unlock();
|
n.unlock();
|
||||||
sleep(3); // expected-warning {{A blocking function %s is called inside a critical section}}
|
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
||||||
m.unlock();
|
m.unlock();
|
||||||
sleep(3); // no-warning
|
sleep(3); // no-warning
|
||||||
}
|
}
|
||||||
|
|
||||||
void f() {
|
void f() {
|
||||||
sleep(1000); // expected-warning {{A blocking function %s is called inside a critical section}}
|
sleep(1000); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
||||||
}
|
}
|
||||||
|
|
||||||
void testBlockInCriticalSectionInterProcedural() {
|
void testBlockInCriticalSectionInterProcedural() {
|
||||||
|
@ -46,5 +108,5 @@ void testBlockInCriticalSectionUnexpectedUnlock() {
|
||||||
m.unlock();
|
m.unlock();
|
||||||
sleep(1); // no-warning
|
sleep(1); // no-warning
|
||||||
m.lock();
|
m.lock();
|
||||||
sleep(1); // expected-warning {{A blocking function %s is called inside a critical section}}
|
sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
|
||||||
}
|
}
|
||||||
|
|
|
@ -910,6 +910,30 @@ void test(char *y) {
|
||||||
}
|
}
|
||||||
</pre></div></div></td></tr>
|
</pre></div></div></td></tr>
|
||||||
|
|
||||||
|
|
||||||
|
<tr><td><div class="namedescr expandable"><span class="name">
|
||||||
|
alpha.unix.cstring.BlockInCriticalSection</span><span class="lang">
|
||||||
|
(C)</span><div class="descr">
|
||||||
|
Check for calls to blocking functions inside a critical section; applies
|
||||||
|
to:<div class=functions>
|
||||||
|
lock, unlock<br>
|
||||||
|
sleep<br>
|
||||||
|
getc<br>
|
||||||
|
fgets<br>
|
||||||
|
read<br>
|
||||||
|
recv<br>
|
||||||
|
pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock<br>
|
||||||
|
mtx_lock, mtx_timedlock, mtx_trylock, mtx_unlock<br>
|
||||||
|
</div></div></div></td>
|
||||||
|
<td><div class="exampleContainer expandable">
|
||||||
|
<div class="example"><pre>
|
||||||
|
void testBlockInCriticalSection() {
|
||||||
|
std::mutex m;
|
||||||
|
m.lock();
|
||||||
|
sleep(3); // warn
|
||||||
|
m.unlock();
|
||||||
|
}
|
||||||
|
</pre></div></div></td></tr>
|
||||||
</tbody></table>
|
</tbody></table>
|
||||||
|
|
||||||
</div> <!-- page -->
|
</div> <!-- page -->
|
||||||
|
|
Loading…
Reference in New Issue